MFC使用mscomm串口通信

xiaoxiao2021-02-28  37

使用VS2013

需要注册MSCOMM插件

1、添加串口变量

对话框上右键 插入Active X控件,选择 Micsrosft Commuunication Control,version 6.0 ,之后界面上有一个类似于电话的控件图标,把ID号改为 IDC_MSCOMM,右键这个控件 添加变量,命名为m_mscomm,(此时会新建mscomm的头文件和源文件),把DoDataExchange中的DDX_Control(pDX, IDC_MSCOMM, m_mscomm)这行删除掉。

2、初始化串口

[cpp] view plain copy print ? //创建模块串口  if (!m_mscomm.Create(nullptr, WS_VISIBLE | WS_CHILD, CRect(0, 0, 0, 0), this, IDC_MSCOMM))  {      MessageBox(_T("创建mscomm串口失败"));  }   //创建模块串口 if (!m_mscomm.Create(nullptr, WS_VISIBLE | WS_CHILD, CRect(0, 0, 0, 0), this, IDC_MSCOMM)) { MessageBox(_T("创建mscomm串口失败")); } 3、打开串口

[cpp] view plain copy print ? if (m_mscomm.get_PortOpen())//如果是打开的,则关闭  {      m_mscomm.put_PortOpen(FALSE);      GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]);      return;  }  m_mscomm.put__CommPort(m_ci.comm);//选择串口  m_mscomm.put_InBufferSize(1024);//接收缓冲区  m_mscomm.put_OutBufferSize(1024);//发送缓冲区  m_mscomm.put_InputLen(0);//设置当前接受去数据长度为0,表示全部读取  m_mscomm.put_InputMode(1);//以二进制方式读写数据  m_mscomm.put_RThreshold(1);//接收缓冲区有1个及以上字符时,将响应接收数据事件  CString setting;  setting.Format(_T("%s,%c,%s,%s"),      m_ci.baudrate, m_ci.parity[0], m_ci.data, m_ci.stop);  m_mscomm.put_Settings(setting);//波特率、校验位、数据位、停止位  try  {      m_mscomm.put_PortOpen(TRUE);      GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSON]);  }  catch (CException*)  {      m_mscomm.put_OutBufferCount(0);      CString errmsg;      errmsg.Format(_T("打开串口失败[串口信息:串口号%d,波特率%s,校验位%s,数据位%s,停止位%s]"),          m_ci.comm, m_ci.baudrate, m_ci.parity, m_ci.data, m_ci.stop);      //AddShowInfo(errmsg);  }   if (m_mscomm.get_PortOpen())//如果是打开的,则关闭 { m_mscomm.put_PortOpen(FALSE); GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]); return; } m_mscomm.put__CommPort(m_ci.comm);//选择串口 m_mscomm.put_InBufferSize(1024);//接收缓冲区 m_mscomm.put_OutBufferSize(1024);//发送缓冲区 m_mscomm.put_InputLen(0);//设置当前接受去数据长度为0,表示全部读取 m_mscomm.put_InputMode(1);//以二进制方式读写数据 m_mscomm.put_RThreshold(1);//接收缓冲区有1个及以上字符时,将响应接收数据事件 CString setting; setting.Format(_T("%s,%c,%s,%s"), m_ci.baudrate, m_ci.parity[0], m_ci.data, m_ci.stop); m_mscomm.put_Settings(setting);//波特率、校验位、数据位、停止位 try { m_mscomm.put_PortOpen(TRUE); GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSON]); } catch (CException*) { m_mscomm.put_OutBufferCount(0); CString errmsg; errmsg.Format(_T("打开串口失败[串口信息:串口号%d,波特率%s,校验位%s,数据位%s,停止位%s]"), m_ci.comm, m_ci.baudrate, m_ci.parity, m_ci.data, m_ci.stop); //AddShowInfo(errmsg); } 4、发送数据

//CByteArray    m_senddata;//发送的数据

[cpp] view plain copy print ? //发送数据  m_mscomm.put_Output(COleVariant(m_senddata));   //发送数据 m_mscomm.put_Output(COleVariant(m_senddata));5、接收数据  

头文件:

[cpp] view plain copy print ? DECLARE_EVENTSINK_MAP()  void OnCommMscomm();//串口接收处理   DECLARE_EVENTSINK_MAP() void OnCommMscomm();//串口接收处理源文件消息映射

[cpp] view plain copy print ? BEGIN_EVENTSINK_MAP(CKELONCommDlg, CDialogEx)      ON_EVENT(CKELONCommDlg, IDC_MSCOMM, 1, CKELONCommDlg::OnCommMscomm, VTS_NONE)  END_EVENTSINK_MAP()   BEGIN_EVENTSINK_MAP(CKELONCommDlg, CDialogEx) ON_EVENT(CKELONCommDlg, IDC_MSCOMM, 1, CKELONCommDlg::OnCommMscomm, VTS_NONE) END_EVENTSINK_MAP() OnCommMscomm里面的处理 [cpp] view plain copy print ? if (m_mscomm.get_CommEvent() != 2)  {      return;  }    //读缓冲区信息  unsigned char rcvdata[1024] = { 0 };//接收的数据  COleSafeArray safearray_inp = m_mscomm.get_Input();//读缓冲区消息  DWORD len = safearray_inp.GetOneDimSize();//获取有效数据长度  for (long j = 0; j < len; j++)//转化为unsigned char数组  {      safearray_inp.GetElement(&j, rcvdata + j);  }   if (m_mscomm.get_CommEvent() != 2) { return; } //读缓冲区信息 unsigned char rcvdata[1024] = { 0 };//接收的数据 COleSafeArray safearray_inp = m_mscomm.get_Input();//读缓冲区消息 DWORD len = safearray_inp.GetOneDimSize();//获取有效数据长度 for (long j = 0; j < len; j++)//转化为unsigned char数组 { safearray_inp.GetElement(&j, rcvdata + j); }6、完整程序

(不含组帧和解析帧)

头文件:

[cpp] view plain copy print ? #ifndef KELONCommDlg_h__  #define KELONCommDlg_h__      // KELONCommDlg.h : 头文件  //    #include "mscomm.h"  #include "DLGCommConfig.h"  #include "FrameHandle.h"  #include "GridCtrl\GridCtrl.h"      // CKELONCommDlg 对话框  class CKELONCommDlg : public CDialogEx  {  // 构造  public:      CKELONCommDlg(CWnd* pParent = NULL);    // 标准构造函数      ~CKELONCommDlg();    // 对话框数据      enum { IDD = IDD_KELONCOMM_DIALOG };        using comminfo = CDLGCommConfig::comminfo;        struct ShowInfo//显示信息的格式      {          const CString &info;//显示的信息          CString state;//显示状态,三种状态:发送、接收、无,有显示状态时一定会显示时间,一定要换行          bool breturnline;//是否换行          ShowInfo(const CString &info)              :info(info)          {              breturnline = false;          }      };    protected:      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持      // 实现  protected:      HICON m_hIcon;        // 生成的消息映射函数      virtual BOOL OnInitDialog();      afx_msg void OnSysCommand(UINT nID, LPARAM lParam);      afx_msg void OnPaint();      afx_msg HCURSOR OnQueryDragIcon();      DECLARE_MESSAGE_MAP()    private:      CMscomm m_mscomm;//串口控件      comminfo    m_ci;//串口信息      FrameHandle m_rcvdata;//接收的数据      CByteArray  m_senddata;//发送的数据        int         m_sendtimes;//发送次数      UINT        m_timesE, m_timesRetE;//多次发送事件句柄            CGridCtrl   m_datagrid;//显示接收数据的表格    public:      DECLARE_EVENTSINK_MAP()      void OnCommMscomm();//串口接收处理      afx_msg void OnTimer(UINT_PTR nIDEvent);      void SendData();//发送数据        afx_msg void OnBnClickedBtnOpencomm();      void GetConfigInfo(const CString &ifile);//读配置信息      void SetConfigInfo(const CString &ifile);//写配置信息      afx_msg void OnBnClickedBtnSenddata();      afx_msg void OnBnClickedBtnSetcomm();      void OnInitUI();//初始化界面      UIDataInfo GetUIDataInfo();//获取界面信息      //检测消息,消息合法返回真,并组织成unsigned char数组,消息不合法,存储错误消息      bool JudgeMsg(CString imsg,std::vector<unsigned char> &omsg,CString &errmsg);      afx_msg void OnBnClickedOk();      void AddShowInfo(const ShowInfo &si);//添加显示信息      afx_msg void OnBnClickedBtnClearshowinfo();      afx_msg void OnBnClickedBtnAbout();      void AddGridData(const std::vector<UIDataInfo> &uidi);//添加接收数据到表格上      CString parseMsg(const UIDataInfo & uidi);//解析消息内容      unsigned long BCDToDec(const unsigned char *bcd, int length);//BCD码      void copy_V2A(std::vector<unsigned char>::const_iterator begin, unsigned char *arr, int length);//拷贝:vector -> 数组      afx_msg void OnBnClickedBtnCleargrid();  };    #endif // KELONCommDlg_h__   #ifndef KELONCommDlg_h__ #define KELONCommDlg_h__ // KELONCommDlg.h : 头文件 // #include "mscomm.h" #include "DLGCommConfig.h" #include "FrameHandle.h" #include "GridCtrl\GridCtrl.h" // CKELONCommDlg 对话框 class CKELONCommDlg : public CDialogEx { // 构造 public: CKELONCommDlg(CWnd* pParent = NULL); // 标准构造函数 ~CKELONCommDlg(); // 对话框数据 enum { IDD = IDD_KELONCOMM_DIALOG }; using comminfo = CDLGCommConfig::comminfo; struct ShowInfo//显示信息的格式 { const CString &info;//显示的信息 CString state;//显示状态,三种状态:发送、接收、无,有显示状态时一定会显示时间,一定要换行 bool breturnline;//是否换行 ShowInfo(const CString &info) :info(info) { breturnline = false; } }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() private: CMscomm m_mscomm;//串口控件 comminfo m_ci;//串口信息 FrameHandle m_rcvdata;//接收的数据 CByteArray m_senddata;//发送的数据 int m_sendtimes;//发送次数 UINT m_timesE, m_timesRetE;//多次发送事件句柄 CGridCtrl m_datagrid;//显示接收数据的表格 public: DECLARE_EVENTSINK_MAP() void OnCommMscomm();//串口接收处理 afx_msg void OnTimer(UINT_PTR nIDEvent); void SendData();//发送数据 afx_msg void OnBnClickedBtnOpencomm(); void GetConfigInfo(const CString &ifile);//读配置信息 void SetConfigInfo(const CString &ifile);//写配置信息 afx_msg void OnBnClickedBtnSenddata(); afx_msg void OnBnClickedBtnSetcomm(); void OnInitUI();//初始化界面 UIDataInfo GetUIDataInfo();//获取界面信息 //检测消息,消息合法返回真,并组织成unsigned char数组,消息不合法,存储错误消息 bool JudgeMsg(CString imsg,std::vector<unsigned char> &omsg,CString &errmsg); afx_msg void OnBnClickedOk(); void AddShowInfo(const ShowInfo &si);//添加显示信息 afx_msg void OnBnClickedBtnClearshowinfo(); afx_msg void OnBnClickedBtnAbout(); void AddGridData(const std::vector<UIDataInfo> &uidi);//添加接收数据到表格上 CString parseMsg(const UIDataInfo & uidi);//解析消息内容 unsigned long BCDToDec(const unsigned char *bcd, int length);//BCD码 void copy_V2A(std::vector<unsigned char>::const_iterator begin, unsigned char *arr, int length);//拷贝:vector -> 数组 afx_msg void OnBnClickedBtnCleargrid(); }; #endif // KELONCommDlg_h__ 源文件:

[cpp] view plain copy print ? // KELONCommDlg.cpp : 实现文件  //    #include "stdafx.h"  #include "KELONComm.h"  #include "KELONCommDlg.h"  #include "afxdialogex.h"    #include <map>  #include <set>  #include <vector>  #include <chrono>  #include <algorithm>    //表格处理  #include "GridCtrl\CellRange.h"  #include "GridCtrl\GridCell.h"  #include "GridCtrl\GridCellBase.h"  #include "GridCtrl\GridDropTarget.h"  #include "GridCtrl\InPlaceEdit.h"  #include "GridCtrl\MemDC.h"  #include "GridCtrl\TitleTip.h"    #ifdef _DEBUG  #define new DEBUG_NEW  #endif    namespace  {      int g_timespan = 300;//多次发送的时间间隔,300ms      int g_sendtimes = 3;//最多重发这么多次        CString g_configfilepath;//配置路径        struct MsgCmd//消息命令      {          int cmdtype;//命令类型          int cmdsubtype;//命令子类型          int result;//操作结果          friend bool operator==(const MsgCmd &item1, const MsgCmd &item2)          {              return item1.cmdtype == item2.cmdtype                  && item1.cmdsubtype == item2.cmdsubtype                  && item1.result == item2.result;          }      };        std::map<CString,MsgCmd> g_msghead;//消息头:<描述,命令>        const CString g_CommState[2] = { _T("打开串口"), _T("关闭串口") };      //enum CommState Index :关闭状态时显示"打开串口",打开状态时显示"关闭串口"      enum g_eCSI{ CSOFF = 0, CSON };        const CString g_RSState[2] = { _T("receive:"), _T("send:") };      enum g_erssi{ RECV = 0, SEND };//enum reveive send state index  }    // 用于应用程序“关于”菜单项的 CAboutDlg 对话框    class CAboutDlg : public CDialogEx  {  public:      CAboutDlg();    // 对话框数据      enum { IDD = IDD_ABOUTBOX };        protected:      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持    // 实现  protected:      DECLARE_MESSAGE_MAP()  };    CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)  {  }    void CAboutDlg::DoDataExchange(CDataExchange* pDX)  {      CDialogEx::DoDataExchange(pDX);  }    BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)  END_MESSAGE_MAP()      // CKELONCommDlg 对话框        CKELONCommDlg::CKELONCommDlg(CWnd* pParent /*=NULL*/)      : CDialogEx(CKELONCommDlg::IDD, pParent)      , m_timesE(1001)  {      m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);  }    CKELONCommDlg::~CKELONCommDlg()  {      SetConfigInfo(g_configfilepath);  }    void CKELONCommDlg::DoDataExchange(CDataExchange* pDX)  {      CDialogEx::DoDataExchange(pDX);  }    BEGIN_MESSAGE_MAP(CKELONCommDlg, CDialogEx)      ON_WM_SYSCOMMAND()      ON_WM_PAINT()      ON_WM_QUERYDRAGICON()      ON_BN_CLICKED(IDC_Btn_OpenComm, &CKELONCommDlg::OnBnClickedBtnOpencomm)      ON_BN_CLICKED(IDC_Btn_SendData, &CKELONCommDlg::OnBnClickedBtnSenddata)      ON_WM_TIMER()      ON_BN_CLICKED(IDC_Btn_SetComm, &CKELONCommDlg::OnBnClickedBtnSetcomm)      ON_BN_CLICKED(IDOK, &CKELONCommDlg::OnBnClickedOk)      ON_BN_CLICKED(IDC_Btn_ClearShowInfo, &CKELONCommDlg::OnBnClickedBtnClearshowinfo)      ON_BN_CLICKED(IDC_Btn_About, &CKELONCommDlg::OnBnClickedBtnAbout)      ON_BN_CLICKED(IDC_Btn_ClearGrid, &CKELONCommDlg::OnBnClickedBtnCleargrid)  END_MESSAGE_MAP()    BEGIN_EVENTSINK_MAP(CKELONCommDlg, CDialogEx)      ON_EVENT(CKELONCommDlg, IDC_MSCOMM, 1, CKELONCommDlg::OnCommMscomm, VTS_NONE)  END_EVENTSINK_MAP()    // CKELONCommDlg 消息处理程序    BOOL CKELONCommDlg::OnInitDialog()  {      CDialogEx::OnInitDialog();        // 将“关于...”菜单项添加到系统菜单中。        // IDM_ABOUTBOX 必须在系统命令范围内。      ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);      ASSERT(IDM_ABOUTBOX < 0xF000);        CMenu* pSysMenu = GetSystemMenu(FALSE);      if (pSysMenu != NULL)      {          BOOL bNameValid;          CString strAboutMenu;          bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);          ASSERT(bNameValid);          if (!strAboutMenu.IsEmpty())          {              pSysMenu->AppendMenu(MF_SEPARATOR);              pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);          }      }        // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动      //  执行此操作      SetIcon(m_hIcon, TRUE);         // 设置大图标      SetIcon(m_hIcon, FALSE);        // 设置小图标        // TODO:  在此添加额外的初始化代码      //创建模块串口      if (!m_mscomm.Create(nullptr, WS_VISIBLE | WS_CHILD, CRect(0, 0, 0, 0), this, IDC_MSCOMM))      {          MessageBox(_T("创建mscomm串口失败"));      }        //设置串口状态      GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]);        //添加显示信息的滚动条      CEdit *pedit = (CEdit*)GetDlgItem(IDC_Edt_ShowInfo);      pedit->ShowScrollBar(SB_VERT, TRUE);//显示滚动条        //获取程序目录      CString dirpath;      GetModuleFileName(NULL, dirpath.GetBuffer(MAX_PATH), MAX_PATH);//获取程序的路径,注意GetBuffer里面的MAX_PATH      dirpath.ReleaseBuffer();//需要释放,不然的话szPath的长度为0,也就是GetBuffer要和ReleaseBuffer配对使用      dirpath = dirpath.Left(dirpath.ReverseFind('\\'));        //配置路径       g_configfilepath = dirpath + _T("\\configinfo.ini");      //获取串口信息      GetConfigInfo(g_configfilepath);        //初始化界面      OnInitUI();        return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE  }    void CKELONCommDlg::OnInitUI()  {      //初始化表格      CRect rc;      CWnd *pwnd = (this->GetDlgItem(IDC_STATIC_ShowGrid));      pwnd->GetWindowRect(&rc);      ScreenToClient(&rc);      m_datagrid.Create(rc, this, 100);        m_datagrid.DeleteAllItems();      m_datagrid.SetBkColor(RGB(192, 192, 192));      m_datagrid.SetColumnCount(5);      m_datagrid.SetRowCount(1);      m_datagrid.SetFixedColumnCount(5);      m_datagrid.SetFixedRowCount(1);        m_datagrid.SetItemText(0, 0, _T("正确/错误"));      m_datagrid.SetItemText(0, 1, _T("链路层信息"));      m_datagrid.SetItemText(0, 2, _T("网络层信息"));      m_datagrid.SetItemText(0, 3, _T("传输层信息"));      m_datagrid.SetItemText(0, 4, _T("应用层消息头"));        m_datagrid.AutoSizeColumns();      m_datagrid.ExpandColumnsToFit();        //      //初始化控件      CComboBox *pcombo;      CString text;        //链路层应答标识      pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_LinkRF);      for (int i = 0; i != 9; i++)      {          text.Format(_T("%d"), i);          pcombo->AddString(text);      }      pcombo->SetCurSel(0);        //网络层应答标识      pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_NWRF);      for (int i = 0; i != 9; i++)      {          text.Format(_T("%d"), i);          pcombo->AddString(text);      }      pcombo->SetCurSel(0);        //传输层应答标识      pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_TransPortRF);      for (int i = 0; i != 9; i++)      {          text.Format(_T("%d"), i);          pcombo->AddString(text);      }      pcombo->SetCurSel(0);        //应用层命令类型消息头      pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_AppMsgHead);      for (auto it = g_msghead.begin(); it != g_msghead.end(); ++it)      {          pcombo->AddString(it->first);      }      if (!g_msghead.empty())      {          pcombo->SetCurSel(0);      }  }    void CKELONCommDlg::OnSysCommand(UINT nID, LPARAM lParam)  {      if ((nID & 0xFFF0) == IDM_ABOUTBOX)      {          CAboutDlg dlgAbout;          dlgAbout.DoModal();      }      else      {          CDialogEx::OnSysCommand(nID, lParam);      }  }    // 如果向对话框添加最小化按钮,则需要下面的代码  //  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,  //  这将由框架自动完成。    void CKELONCommDlg::OnPaint()  {      if (IsIconic())      {          CPaintDC dc(this); // 用于绘制的设备上下文            SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);            // 使图标在工作区矩形中居中          int cxIcon = GetSystemMetrics(SM_CXICON);          int cyIcon = GetSystemMetrics(SM_CYICON);          CRect rect;          GetClientRect(&rect);          int x = (rect.Width() - cxIcon + 1) / 2;          int y = (rect.Height() - cyIcon + 1) / 2;            // 绘制图标          dc.DrawIcon(x, y, m_hIcon);      }      else      {          CDialogEx::OnPaint();      }  }    //当用户拖动最小化窗口时系统调用此函数取得光标  //显示。  HCURSOR CKELONCommDlg::OnQueryDragIcon()  {      return static_cast<HCURSOR>(m_hIcon);  }        void CKELONCommDlg::GetConfigInfo(const CString &ifile)  {      //串口信息      m_ci.comm = GetPrivateProfileInt(_T("comminfo"), _T("comm"), 1, ifile);      GetPrivateProfileString(_T("comminfo"), _T("baudrate"), _T("9600"),          m_ci.baudrate, 256, ifile);      GetPrivateProfileString(_T("comminfo"), _T("parity"), _T("even"),          m_ci.parity, 256, ifile);      GetPrivateProfileString(_T("comminfo"), _T("data"), _T("8"),          m_ci.data, 256, ifile);      GetPrivateProfileString(_T("comminfo"), _T("stop"), _T("1"),          m_ci.stop, 256, ifile);        //时间处理      //多次发送的时间间隔      g_timespan = GetPrivateProfileInt(_T("timeinfo"),          _T("timespan"), 300, ifile);      //最多重发几次 g_sendtimes      g_sendtimes = GetPrivateProfileInt(_T("timeinfo"),          _T("sendtimes"), 3, ifile);        using std::map;      using std::set;      //消息头处理      //std::map<CString,MsgCmd> g_msghead;//消息头:<描述,命令>      int msgcount = GetPrivateProfileInt(_T("msghead"), _T("msgcount"), 0, ifile);      for (int i = 0; i != msgcount; i++)      {          MsgCmd mc;          CString indexstr;          indexstr.Format(_T("msg%d"), i);          mc.cmdtype = GetPrivateProfileInt(indexstr, _T("cmdtype"), 0, ifile);          mc.cmdsubtype = GetPrivateProfileInt(indexstr, _T("cmdsubtype"), 0, ifile);          mc.result = GetPrivateProfileInt(indexstr, _T("result"), 0, ifile);            TCHAR szdescrip[1024];          GetPrivateProfileString(indexstr, _T("description"), _T(""),              szdescrip, sizeof(szdescrip), ifile);          CString descrip(szdescrip);            //插入到消息头map中:std::map<CString,MsgCmd> g_msghead;//消息头:<描述,命令>          //查找有没有这个消息头命令或消息描述          auto findit = std::find_if(g_msghead.begin(), g_msghead.end(),              [&mc, &descrip](const std::pair<CString, MsgCmd> &item)          {return item.second == mc || item.first == descrip; });          if (findit != g_msghead.end())//已存在这个描述或命令          {              CString errmsg;              errmsg.Format(_T("发现有重复的消息命令头或描述:\r\n\                               [cmdtype : %d, cmdsubtype : %d, result : %d, description : %s]\r\n\                               [cmdtype : %d, cmdsubtype : %d, result : %d, description : %s]"),                  findit->second.cmdtype, findit->second.cmdsubtype,                  findit->second.result, findit->first,                  mc.cmdtype, mc.cmdsubtype, mc.result, descrip                  );              MessageBox(errmsg);          }          else          {              g_msghead[descrip] = mc;          }      }  }    void CKELONCommDlg::SetConfigInfo(const CString &ifile)  {      //串口信息      CString commstr;      commstr.Format(_T("%d"), m_ci.comm);      WritePrivateProfileString(_T("comminfo"), _T("comm"), commstr, ifile);      WritePrivateProfileString(_T("comminfo"), _T("baudrate"), m_ci.baudrate, ifile);      WritePrivateProfileString(_T("comminfo"), _T("parity"), m_ci.parity, ifile);      WritePrivateProfileString(_T("comminfo"), _T("data"), m_ci.data, ifile);      WritePrivateProfileString(_T("comminfo"), _T("stop"), m_ci.stop, ifile);  }    void CKELONCommDlg::OnCommMscomm()  {      if (m_mscomm.get_CommEvent() != 2)      {          return;      }        //读缓冲区信息      unsigned char rcvdata[1024] = { 0 };//接收的数据      COleSafeArray safearray_inp = m_mscomm.get_Input();//读缓冲区消息      DWORD len = safearray_inp.GetOneDimSize();//获取有效数据长度      for (long j = 0; j < len; j++)//转化为unsigned char数组      {          safearray_inp.GetElement(&j, rcvdata + j);      }      CString Temp;      for (long i = 0; i < len; i++)      {          Temp.AppendFormat(_T("%.2X "), rcvdata[i]);      }      bool havenodata = m_rcvdata.GetDataLength() == 0;      ShowInfo si(Temp);      if (havenodata)//没有接收数据      {          si.state = g_RSState[g_erssi::RECV];      }       AddShowInfo(si);        //数据处理      auto appret = m_rcvdata.appdata(rcvdata, len);        //判断数据。。。      if (appret.first)      {          KillTimer(m_timesRetE);          //显示接收到的数据          AddGridData(appret.second);      }  }      void CKELONCommDlg::SendData()  {      //判断是否启动模块串口      CString ButtonText;      GetDlgItem(IDC_Btn_OpenComm)->GetWindowText(ButtonText);      if (ButtonText == g_CommState[g_eCSI::CSOFF])      {          CString errmsg = _T("没有打开串口");          MessageBox(errmsg);          return;      }            CString showinfo;      for (int i = 0; i != m_senddata.GetSize(); i++)      {          showinfo.AppendFormat(_T("%.2X "), m_senddata.GetAt(i));      }      ShowInfo si(showinfo);      si.state = g_RSState[g_erssi::SEND];      AddShowInfo(si);        //发送数据      m_mscomm.put_Output(COleVariant(m_senddata));        m_rcvdata.init();//初始化接收帧      m_sendtimes = 1;//第一次发送        //多次发送事件      m_timesRetE = SetTimer(m_timesE, g_timespan, NULL);  }    void CKELONCommDlg::OnTimer(UINT_PTR nIDEvent)  {      // TODO:  在此添加消息处理程序代码和/或调用默认值      KillTimer(nIDEvent);      if (nIDEvent == m_timesRetE)//多次发送事件      {          m_sendtimes++;          if (m_sendtimes <= g_sendtimes)//发送不满3次          {              size_t sendtimes = m_sendtimes;              SendData();              m_sendtimes = sendtimes;          }          else//发送了3次          {              //AddShowInfo(_T("刷新失败"));          }      }      CDialogEx::OnTimer(nIDEvent);  }    void CKELONCommDlg::OnBnClickedBtnOpencomm()  {      // TODO:  在此添加控件通知处理程序代码      if (m_mscomm.get_PortOpen())//如果是打开的,则关闭      {          m_mscomm.put_PortOpen(FALSE);          GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]);          return;      }      m_mscomm.put__CommPort(m_ci.comm);//选择串口      m_mscomm.put_InBufferSize(1024);//接收缓冲区      m_mscomm.put_OutBufferSize(1024);//发送缓冲区      m_mscomm.put_InputLen(0);//设置当前接受去数据长度为0,表示全部读取      m_mscomm.put_InputMode(1);//以二进制方式读写数据      m_mscomm.put_RThreshold(1);//接收缓冲区有1个及以上字符时,将响应接收数据事件      CString setting;      setting.Format(_T("%s,%c,%s,%s"),          m_ci.baudrate, m_ci.parity[0], m_ci.data, m_ci.stop);      m_mscomm.put_Settings(setting);//波特率、校验位、数据位、停止位      try      {          m_mscomm.put_PortOpen(TRUE);          GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSON]);      }      catch (CException*)      {          m_mscomm.put_OutBufferCount(0);          CString errmsg;          errmsg.Format(_T("打开串口失败[串口信息:串口号%d,波特率%s,校验位%s,数据位%s,停止位%s]"),              m_ci.comm, m_ci.baudrate, m_ci.parity, m_ci.data, m_ci.stop);          //AddShowInfo(errmsg);      }  }    void CKELONCommDlg::OnBnClickedBtnSenddata()  {      // TODO:  在此添加控件通知处理程序代码      UpdateData(TRUE);        //获取界面数据信息      UIDataInfo di = GetUIDataInfo();      if (!di.bOK)//获取界面信息失败      {          MessageBox(di.errmsg);          return;      }        //发送数据      //对需要发送的数据组帧      auto framedata = FrameHandle::createFrame(di);      if (!framedata.bok)//组帧失败      {          MessageBox(framedata.errmsg);          return;      }      else      {          m_senddata.RemoveAll();          m_senddata.SetSize(framedata.length);          for (int i = 0; i < framedata.length; i++)          {              m_senddata.SetAt(i, framedata.data[i]);          }            SendData();//发送数据      }  }      void CKELONCommDlg::OnBnClickedBtnSetcomm()  {      // TODO:  在此添加控件通知处理程序代码      CDLGCommConfig commconfigdlg(m_ci, NULL);      if (commconfigdlg.DoModal() == IDOK)      {          CString ButtonText;          GetDlgItem(IDC_Btn_OpenComm)->GetWindowText(ButtonText);          if (ButtonText == g_CommState[g_eCSI::CSON])          {              if (m_mscomm.get_PortOpen())              {                  m_mscomm.put_PortOpen(FALSE);              }              GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]);          }      }  }      UIDataInfo CKELONCommDlg::GetUIDataInfo()  {      UIDataInfo di;        //略            return di;  }      void CKELONCommDlg::OnBnClickedOk()  {      // TODO:  在此添加控件通知处理程序代码      OnBnClickedBtnSenddata();      //CDialogEx::OnOK();  }    void CKELONCommDlg::AddShowInfo(const ShowInfo &si)  {      CString info;      GetDlgItemText(IDC_Edt_ShowInfo, info);      int nLen = info.GetLength();      if (nLen > 0xffffffff)//以前显示的信息很多,清空      {          info.Empty();      }        if (!si.state.IsEmpty())//有显示状态      {          //获取时间          auto timepoint = std::chrono::system_clock::now();          time_t tm_t = std::chrono::system_clock::to_time_t(timepoint);          tm t;          localtime_s(&t, &tm_t);          char sztime[100];          strftime(sztime, sizeof(sztime), "%Y/%m/%d %H:%M:%S", &t);            if (!info.IsEmpty())//非空,添加两次换行          {              info.Append(_T("\r\n\r\n"));          }          info.AppendFormat(_T("%-10s %s\r\n%s"), si.state, CString(sztime), si.info);      }      else if (si.breturnline)//没有显示状态,需要换行      {          if (!info.IsEmpty())//非空,添加一次换行          {              info.Append(_T("\r\n"));          }          info.AppendFormat(_T("%s"), si.info);      }      else//不需要换行      {          info.AppendFormat(_T("%s"), si.info);      }      SetDlgItemText(IDC_Edt_ShowInfo, info);//收发框会闪烁        nLen = info.GetLength();      CEdit *pedit = (CEdit*)GetDlgItem(IDC_Edt_ShowInfo);      pedit->SetSel(nLen - 1, nLen - 1);//滚动到底部  }      void CKELONCommDlg::OnBnClickedBtnClearshowinfo()  {      // TODO:  在此添加控件通知处理程序代码      SetDlgItemText(IDC_Edt_ShowInfo, _T(""));  }    bool CKELONCommDlg::JudgeMsg(CString imsg,       std::vector<unsigned char> &omsg, CString &oerrmsg)  {      oerrmsg.Empty();      omsg.clear();        USES_CONVERSION;        //去除中间的空格      imsg.Remove(' ');        //判断输入的长度是否为偶数      if (imsg.GetLength() % 2)      {          imsg = _T("0") + imsg;      }      int CurrPos = 0;      for (; CurrPos < imsg.GetLength(); CurrPos++)      {          TCHAR currch = imsg.GetAt(CurrPos);          if ((currch >= '0' && currch <= '9')              || (currch >= 'a' && currch <= 'f')              || (currch >= 'A' && currch <= 'F'))          {          }          else          {              oerrmsg=_T("消息内容格式不对[应该为0-F]");              return false;          }      }        //转换为字节数组      CurrPos = 0;      int i = 0;      for (; CurrPos < imsg.GetLength(); CurrPos += 2)      {          CString tmpbytestr = imsg.Mid(CurrPos, 2);          int tmpbyte;          sscanf_s(W2A(tmpbytestr), "%x", &tmpbyte);          omsg.push_back(tmpbyte);      }        return true;  }      void CKELONCommDlg::OnBnClickedBtnAbout()  {      // TODO:  在此添加控件通知处理程序代码      CAboutDlg dlg;      dlg.DoModal();  }    void CKELONCommDlg::AddGridData(const std::vector<UIDataInfo> &uidi)  {      int rowcount = m_datagrid.GetRowCount();      m_datagrid.SetRowCount(rowcount + uidi.size());        for (int i = 0; i != uidi.size(); i++)      {          CString tmpstr(_T("√"));          if (!uidi[i].bOK)          {              tmpstr.Format(_T("× : %s"), uidi[i].errmsg);          }          m_datagrid.SetItemText(rowcount + i, 0, tmpstr);          tmpstr.Format(_T("%.2X / %.2X"), uidi[i].link.RF, uidi[i].link.flag); // 链路层信息          m_datagrid.SetItemText(rowcount + i, 1, tmpstr);          tmpstr.Format(_T("%.2X / %.2X / %.2X%.2X / %.2X%.2X"),  // 网络层信息              uidi[i].NW.RF, uidi[i].NW.flag,              uidi[i].NW.addr1[0], uidi[i].NW.addr1[1],              uidi[i].NW.addr2[0], uidi[i].NW.addr2[1]              );          m_datagrid.SetItemText(rowcount + i, 2, tmpstr);          tmpstr.Format(_T("%.2X / %.2X"), uidi[i].Transport.RF, uidi[i].Transport.flag); // 传输层信息          m_datagrid.SetItemText(rowcount + i, 3, tmpstr);          tmpstr.Format(_T("%.2X / %.2X / %.2X"),  // 应用层消息头              uidi[i].App.msghead[0], uidi[i].App.msghead[1], uidi[i].App.msghead[2]);          m_datagrid.SetItemText(rowcount + i, 4, tmpstr);            if (uidi[i].bOK)          {              const CString parsemsg = parseMsg(uidi[i]);              ShowInfo si(parsemsg);              si.breturnline = true;              AddShowInfo(si);          }      }        /*m_datagrid.AutoSizeColumns();     m_datagrid.ExpandColumnsToFit();*/      m_datagrid.EnsureVisible(rowcount, 0);//滚动到最后一行  }    CString CKELONCommDlg::parseMsg(const UIDataInfo & uidi)  {      using namespace std;      CString msg;        //略        return msg;  }    unsigned long CKELONCommDlg::BCDToDec(const unsigned char *bcd, int length)  {      int i, tmp;      unsigned long dec = 0;      for (i = 0; i != length; i++)      {          tmp = bcd[i];          tmp = ((bcd[i] >> 4) & 0x0F) * 10 + (bcd[i] & 0x0F);          dec = dec * 100 + tmp;      }      return dec;  }    void CKELONCommDlg::copy_V2A(std::vector<unsigned char>::const_iterator begin,      unsigned char *arr, int length)  {      int i = 0;      for (auto it = begin; it != begin + length; ++it,++i)      {          arr[i] = *it;      }  }      void CKELONCommDlg::OnBnClickedBtnCleargrid()  {      // TODO:  在此添加控件通知处理程序代码      m_datagrid.SetRowCount(1);        /*m_datagrid.AutoSizeColumns();     m_datagrid.ExpandColumnsToFit();*/  }   // KELONCommDlg.cpp : 实现文件 // #include "stdafx.h" #include "KELONComm.h" #include "KELONCommDlg.h" #include "afxdialogex.h" #include <map> #include <set> #include <vector> #include <chrono> #include <algorithm> //表格处理 #include "GridCtrl\CellRange.h" #include "GridCtrl\GridCell.h" #include "GridCtrl\GridCellBase.h" #include "GridCtrl\GridDropTarget.h" #include "GridCtrl\InPlaceEdit.h" #include "GridCtrl\MemDC.h" #include "GridCtrl\TitleTip.h" #ifdef _DEBUG #define new DEBUG_NEW #endif namespace { int g_timespan = 300;//多次发送的时间间隔,300ms int g_sendtimes = 3;//最多重发这么多次 CString g_configfilepath;//配置路径 struct MsgCmd//消息命令 { int cmdtype;//命令类型 int cmdsubtype;//命令子类型 int result;//操作结果 friend bool operator==(const MsgCmd &item1, const MsgCmd &item2) { return item1.cmdtype == item2.cmdtype && item1.cmdsubtype == item2.cmdsubtype && item1.result == item2.result; } }; std::map<CString,MsgCmd> g_msghead;//消息头:<描述,命令> const CString g_CommState[2] = { _T("打开串口"), _T("关闭串口") }; //enum CommState Index :关闭状态时显示"打开串口",打开状态时显示"关闭串口" enum g_eCSI{ CSOFF = 0, CSON }; const CString g_RSState[2] = { _T("receive:"), _T("send:") }; enum g_erssi{ RECV = 0, SEND };//enum reveive send state index } // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx { public: CAboutDlg(); // 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CKELONCommDlg 对话框 CKELONCommDlg::CKELONCommDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CKELONCommDlg::IDD, pParent) , m_timesE(1001) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } CKELONCommDlg::~CKELONCommDlg() { SetConfigInfo(g_configfilepath); } void CKELONCommDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CKELONCommDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_Btn_OpenComm, &CKELONCommDlg::OnBnClickedBtnOpencomm) ON_BN_CLICKED(IDC_Btn_SendData, &CKELONCommDlg::OnBnClickedBtnSenddata) ON_WM_TIMER() ON_BN_CLICKED(IDC_Btn_SetComm, &CKELONCommDlg::OnBnClickedBtnSetcomm) ON_BN_CLICKED(IDOK, &CKELONCommDlg::OnBnClickedOk) ON_BN_CLICKED(IDC_Btn_ClearShowInfo, &CKELONCommDlg::OnBnClickedBtnClearshowinfo) ON_BN_CLICKED(IDC_Btn_About, &CKELONCommDlg::OnBnClickedBtnAbout) ON_BN_CLICKED(IDC_Btn_ClearGrid, &CKELONCommDlg::OnBnClickedBtnCleargrid) END_MESSAGE_MAP() BEGIN_EVENTSINK_MAP(CKELONCommDlg, CDialogEx) ON_EVENT(CKELONCommDlg, IDC_MSCOMM, 1, CKELONCommDlg::OnCommMscomm, VTS_NONE) END_EVENTSINK_MAP() // CKELONCommDlg 消息处理程序 BOOL CKELONCommDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 //创建模块串口 if (!m_mscomm.Create(nullptr, WS_VISIBLE | WS_CHILD, CRect(0, 0, 0, 0), this, IDC_MSCOMM)) { MessageBox(_T("创建mscomm串口失败")); } //设置串口状态 GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]); //添加显示信息的滚动条 CEdit *pedit = (CEdit*)GetDlgItem(IDC_Edt_ShowInfo); pedit->ShowScrollBar(SB_VERT, TRUE);//显示滚动条 //获取程序目录 CString dirpath; GetModuleFileName(NULL, dirpath.GetBuffer(MAX_PATH), MAX_PATH);//获取程序的路径,注意GetBuffer里面的MAX_PATH dirpath.ReleaseBuffer();//需要释放,不然的话szPath的长度为0,也就是GetBuffer要和ReleaseBuffer配对使用 dirpath = dirpath.Left(dirpath.ReverseFind('\\')); //配置路径 g_configfilepath = dirpath + _T("\\configinfo.ini"); //获取串口信息 GetConfigInfo(g_configfilepath); //初始化界面 OnInitUI(); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CKELONCommDlg::OnInitUI() { //初始化表格 CRect rc; CWnd *pwnd = (this->GetDlgItem(IDC_STATIC_ShowGrid)); pwnd->GetWindowRect(&rc); ScreenToClient(&rc); m_datagrid.Create(rc, this, 100); m_datagrid.DeleteAllItems(); m_datagrid.SetBkColor(RGB(192, 192, 192)); m_datagrid.SetColumnCount(5); m_datagrid.SetRowCount(1); m_datagrid.SetFixedColumnCount(5); m_datagrid.SetFixedRowCount(1); m_datagrid.SetItemText(0, 0, _T("正确/错误")); m_datagrid.SetItemText(0, 1, _T("链路层信息")); m_datagrid.SetItemText(0, 2, _T("网络层信息")); m_datagrid.SetItemText(0, 3, _T("传输层信息")); m_datagrid.SetItemText(0, 4, _T("应用层消息头")); m_datagrid.AutoSizeColumns(); m_datagrid.ExpandColumnsToFit(); // //初始化控件 CComboBox *pcombo; CString text; //链路层应答标识 pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_LinkRF); for (int i = 0; i != 9; i++) { text.Format(_T("%d"), i); pcombo->AddString(text); } pcombo->SetCurSel(0); //网络层应答标识 pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_NWRF); for (int i = 0; i != 9; i++) { text.Format(_T("%d"), i); pcombo->AddString(text); } pcombo->SetCurSel(0); //传输层应答标识 pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_TransPortRF); for (int i = 0; i != 9; i++) { text.Format(_T("%d"), i); pcombo->AddString(text); } pcombo->SetCurSel(0); //应用层命令类型消息头 pcombo = (CComboBox*)GetDlgItem(IDC_COMBO_AppMsgHead); for (auto it = g_msghead.begin(); it != g_msghead.end(); ++it) { pcombo->AddString(it->first); } if (!g_msghead.empty()) { pcombo->SetCurSel(0); } } void CKELONCommDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CKELONCommDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CKELONCommDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CKELONCommDlg::GetConfigInfo(const CString &ifile) { //串口信息 m_ci.comm = GetPrivateProfileInt(_T("comminfo"), _T("comm"), 1, ifile); GetPrivateProfileString(_T("comminfo"), _T("baudrate"), _T("9600"), m_ci.baudrate, 256, ifile); GetPrivateProfileString(_T("comminfo"), _T("parity"), _T("even"), m_ci.parity, 256, ifile); GetPrivateProfileString(_T("comminfo"), _T("data"), _T("8"), m_ci.data, 256, ifile); GetPrivateProfileString(_T("comminfo"), _T("stop"), _T("1"), m_ci.stop, 256, ifile); //时间处理 //多次发送的时间间隔 g_timespan = GetPrivateProfileInt(_T("timeinfo"), _T("timespan"), 300, ifile); //最多重发几次 g_sendtimes g_sendtimes = GetPrivateProfileInt(_T("timeinfo"), _T("sendtimes"), 3, ifile); using std::map; using std::set; //消息头处理 //std::map<CString,MsgCmd> g_msghead;//消息头:<描述,命令> int msgcount = GetPrivateProfileInt(_T("msghead"), _T("msgcount"), 0, ifile); for (int i = 0; i != msgcount; i++) { MsgCmd mc; CString indexstr; indexstr.Format(_T("msg%d"), i); mc.cmdtype = GetPrivateProfileInt(indexstr, _T("cmdtype"), 0, ifile); mc.cmdsubtype = GetPrivateProfileInt(indexstr, _T("cmdsubtype"), 0, ifile); mc.result = GetPrivateProfileInt(indexstr, _T("result"), 0, ifile); TCHAR szdescrip[1024]; GetPrivateProfileString(indexstr, _T("description"), _T(""), szdescrip, sizeof(szdescrip), ifile); CString descrip(szdescrip); //插入到消息头map中:std::map<CString,MsgCmd> g_msghead;//消息头:<描述,命令> //查找有没有这个消息头命令或消息描述 auto findit = std::find_if(g_msghead.begin(), g_msghead.end(), [&mc, &descrip](const std::pair<CString, MsgCmd> &item) {return item.second == mc || item.first == descrip; }); if (findit != g_msghead.end())//已存在这个描述或命令 { CString errmsg; errmsg.Format(_T("发现有重复的消息命令头或描述:\r\n\ [cmdtype : %d, cmdsubtype : %d, result : %d, description : %s]\r\n\ [cmdtype : %d, cmdsubtype : %d, result : %d, description : %s]"), findit->second.cmdtype, findit->second.cmdsubtype, findit->second.result, findit->first, mc.cmdtype, mc.cmdsubtype, mc.result, descrip ); MessageBox(errmsg); } else { g_msghead[descrip] = mc; } } } void CKELONCommDlg::SetConfigInfo(const CString &ifile) { //串口信息 CString commstr; commstr.Format(_T("%d"), m_ci.comm); WritePrivateProfileString(_T("comminfo"), _T("comm"), commstr, ifile); WritePrivateProfileString(_T("comminfo"), _T("baudrate"), m_ci.baudrate, ifile); WritePrivateProfileString(_T("comminfo"), _T("parity"), m_ci.parity, ifile); WritePrivateProfileString(_T("comminfo"), _T("data"), m_ci.data, ifile); WritePrivateProfileString(_T("comminfo"), _T("stop"), m_ci.stop, ifile); } void CKELONCommDlg::OnCommMscomm() { if (m_mscomm.get_CommEvent() != 2) { return; } //读缓冲区信息 unsigned char rcvdata[1024] = { 0 };//接收的数据 COleSafeArray safearray_inp = m_mscomm.get_Input();//读缓冲区消息 DWORD len = safearray_inp.GetOneDimSize();//获取有效数据长度 for (long j = 0; j < len; j++)//转化为unsigned char数组 { safearray_inp.GetElement(&j, rcvdata + j); } CString Temp; for (long i = 0; i < len; i++) { Temp.AppendFormat(_T("%.2X "), rcvdata[i]); } bool havenodata = m_rcvdata.GetDataLength() == 0; ShowInfo si(Temp); if (havenodata)//没有接收数据 { si.state = g_RSState[g_erssi::RECV]; } AddShowInfo(si); //数据处理 auto appret = m_rcvdata.appdata(rcvdata, len); //判断数据。。。 if (appret.first) { KillTimer(m_timesRetE); //显示接收到的数据 AddGridData(appret.second); } } void CKELONCommDlg::SendData() { //判断是否启动模块串口 CString ButtonText; GetDlgItem(IDC_Btn_OpenComm)->GetWindowText(ButtonText); if (ButtonText == g_CommState[g_eCSI::CSOFF]) { CString errmsg = _T("没有打开串口"); MessageBox(errmsg); return; } CString showinfo; for (int i = 0; i != m_senddata.GetSize(); i++) { showinfo.AppendFormat(_T("%.2X "), m_senddata.GetAt(i)); } ShowInfo si(showinfo); si.state = g_RSState[g_erssi::SEND]; AddShowInfo(si); //发送数据 m_mscomm.put_Output(COleVariant(m_senddata)); m_rcvdata.init();//初始化接收帧 m_sendtimes = 1;//第一次发送 //多次发送事件 m_timesRetE = SetTimer(m_timesE, g_timespan, NULL); } void CKELONCommDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 KillTimer(nIDEvent); if (nIDEvent == m_timesRetE)//多次发送事件 { m_sendtimes++; if (m_sendtimes <= g_sendtimes)//发送不满3次 { size_t sendtimes = m_sendtimes; SendData(); m_sendtimes = sendtimes; } else//发送了3次 { //AddShowInfo(_T("刷新失败")); } } CDialogEx::OnTimer(nIDEvent); } void CKELONCommDlg::OnBnClickedBtnOpencomm() { // TODO: 在此添加控件通知处理程序代码 if (m_mscomm.get_PortOpen())//如果是打开的,则关闭 { m_mscomm.put_PortOpen(FALSE); GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]); return; } m_mscomm.put__CommPort(m_ci.comm);//选择串口 m_mscomm.put_InBufferSize(1024);//接收缓冲区 m_mscomm.put_OutBufferSize(1024);//发送缓冲区 m_mscomm.put_InputLen(0);//设置当前接受去数据长度为0,表示全部读取 m_mscomm.put_InputMode(1);//以二进制方式读写数据 m_mscomm.put_RThreshold(1);//接收缓冲区有1个及以上字符时,将响应接收数据事件 CString setting; setting.Format(_T("%s,%c,%s,%s"), m_ci.baudrate, m_ci.parity[0], m_ci.data, m_ci.stop); m_mscomm.put_Settings(setting);//波特率、校验位、数据位、停止位 try { m_mscomm.put_PortOpen(TRUE); GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSON]); } catch (CException*) { m_mscomm.put_OutBufferCount(0); CString errmsg; errmsg.Format(_T("打开串口失败[串口信息:串口号%d,波特率%s,校验位%s,数据位%s,停止位%s]"), m_ci.comm, m_ci.baudrate, m_ci.parity, m_ci.data, m_ci.stop); //AddShowInfo(errmsg); } } void CKELONCommDlg::OnBnClickedBtnSenddata() { // TODO: 在此添加控件通知处理程序代码 UpdateData(TRUE); //获取界面数据信息 UIDataInfo di = GetUIDataInfo(); if (!di.bOK)//获取界面信息失败 { MessageBox(di.errmsg); return; } //发送数据 //对需要发送的数据组帧 auto framedata = FrameHandle::createFrame(di); if (!framedata.bok)//组帧失败 { MessageBox(framedata.errmsg); return; } else { m_senddata.RemoveAll(); m_senddata.SetSize(framedata.length); for (int i = 0; i < framedata.length; i++) { m_senddata.SetAt(i, framedata.data[i]); } SendData();//发送数据 } } void CKELONCommDlg::OnBnClickedBtnSetcomm() { // TODO: 在此添加控件通知处理程序代码 CDLGCommConfig commconfigdlg(m_ci, NULL); if (commconfigdlg.DoModal() == IDOK) { CString ButtonText; GetDlgItem(IDC_Btn_OpenComm)->GetWindowText(ButtonText); if (ButtonText == g_CommState[g_eCSI::CSON]) { if (m_mscomm.get_PortOpen()) { m_mscomm.put_PortOpen(FALSE); } GetDlgItem(IDC_Btn_OpenComm)->SetWindowText(g_CommState[g_eCSI::CSOFF]); } } } UIDataInfo CKELONCommDlg::GetUIDataInfo() {     UIDataInfo di;     //略          return di; } void CKELONCommDlg::OnBnClickedOk() { // TODO: 在此添加控件通知处理程序代码 OnBnClickedBtnSenddata(); //CDialogEx::OnOK(); } void CKELONCommDlg::AddShowInfo(const ShowInfo &si) { CString info; GetDlgItemText(IDC_Edt_ShowInfo, info); int nLen = info.GetLength(); if (nLen > 0xffffffff)//以前显示的信息很多,清空 { info.Empty(); } if (!si.state.IsEmpty())//有显示状态 { //获取时间 auto timepoint = std::chrono::system_clock::now(); time_t tm_t = std::chrono::system_clock::to_time_t(timepoint); tm t; localtime_s(&t, &tm_t); char sztime[100]; strftime(sztime, sizeof(sztime), "%Y/%m/%d %H:%M:%S", &t); if (!info.IsEmpty())//非空,添加两次换行 { info.Append(_T("\r\n\r\n")); } info.AppendFormat(_T("%-10s %s\r\n%s"), si.state, CString(sztime), si.info); } else if (si.breturnline)//没有显示状态,需要换行 { if (!info.IsEmpty())//非空,添加一次换行 { info.Append(_T("\r\n")); } info.AppendFormat(_T("%s"), si.info); } else//不需要换行 { info.AppendFormat(_T("%s"), si.info); } SetDlgItemText(IDC_Edt_ShowInfo, info);//收发框会闪烁 nLen = info.GetLength(); CEdit *pedit = (CEdit*)GetDlgItem(IDC_Edt_ShowInfo); pedit->SetSel(nLen - 1, nLen - 1);//滚动到底部 } void CKELONCommDlg::OnBnClickedBtnClearshowinfo() { // TODO: 在此添加控件通知处理程序代码 SetDlgItemText(IDC_Edt_ShowInfo, _T("")); } bool CKELONCommDlg::JudgeMsg(CString imsg, std::vector<unsigned char> &omsg, CString &oerrmsg) { oerrmsg.Empty(); omsg.clear(); USES_CONVERSION; //去除中间的空格 imsg.Remove(' '); //判断输入的长度是否为偶数 if (imsg.GetLength() % 2) { imsg = _T("0") + imsg; } int CurrPos = 0; for (; CurrPos < imsg.GetLength(); CurrPos++) { TCHAR currch = imsg.GetAt(CurrPos); if ((currch >= '0' && currch <= '9') || (currch >= 'a' && currch <= 'f') || (currch >= 'A' && currch <= 'F')) { } else { oerrmsg=_T("消息内容格式不对[应该为0-F]"); return false; } } //转换为字节数组 CurrPos = 0; int i = 0; for (; CurrPos < imsg.GetLength(); CurrPos += 2) { CString tmpbytestr = imsg.Mid(CurrPos, 2); int tmpbyte; sscanf_s(W2A(tmpbytestr), "%x", &tmpbyte); omsg.push_back(tmpbyte); } return true; } void CKELONCommDlg::OnBnClickedBtnAbout() { // TODO: 在此添加控件通知处理程序代码 CAboutDlg dlg; dlg.DoModal(); } void CKELONCommDlg::AddGridData(const std::vector<UIDataInfo> &uidi) { int rowcount = m_datagrid.GetRowCount(); m_datagrid.SetRowCount(rowcount + uidi.size()); for (int i = 0; i != uidi.size(); i++) { CString tmpstr(_T("√")); if (!uidi[i].bOK) { tmpstr.Format(_T("× : %s"), uidi[i].errmsg); } m_datagrid.SetItemText(rowcount + i, 0, tmpstr); tmpstr.Format(_T("%.2X / %.2X"), uidi[i].link.RF, uidi[i].link.flag); // 链路层信息 m_datagrid.SetItemText(rowcount + i, 1, tmpstr); tmpstr.Format(_T("%.2X / %.2X / %.2X%.2X / %.2X%.2X"), // 网络层信息 uidi[i].NW.RF, uidi[i].NW.flag, uidi[i].NW.addr1[0], uidi[i].NW.addr1[1], uidi[i].NW.addr2[0], uidi[i].NW.addr2[1] ); m_datagrid.SetItemText(rowcount + i, 2, tmpstr); tmpstr.Format(_T("%.2X / %.2X"), uidi[i].Transport.RF, uidi[i].Transport.flag); // 传输层信息 m_datagrid.SetItemText(rowcount + i, 3, tmpstr); tmpstr.Format(_T("%.2X / %.2X / %.2X"), // 应用层消息头 uidi[i].App.msghead[0], uidi[i].App.msghead[1], uidi[i].App.msghead[2]); m_datagrid.SetItemText(rowcount + i, 4, tmpstr); if (uidi[i].bOK) { const CString parsemsg = parseMsg(uidi[i]); ShowInfo si(parsemsg); si.breturnline = true; AddShowInfo(si); } } /*m_datagrid.AutoSizeColumns(); m_datagrid.ExpandColumnsToFit();*/ m_datagrid.EnsureVisible(rowcount, 0);//滚动到最后一行 } CString CKELONCommDlg::parseMsg(const UIDataInfo & uidi) {     using namespace std;     CString msg;     //略     return msg; } unsigned long CKELONCommDlg::BCDToDec(const unsigned char *bcd, int length) { int i, tmp; unsigned long dec = 0; for (i = 0; i != length; i++) { tmp = bcd[i]; tmp = ((bcd[i] >> 4) & 0x0F) * 10 + (bcd[i] & 0x0F); dec = dec * 100 + tmp; } return dec; } void CKELONCommDlg::copy_V2A(std::vector<unsigned char>::const_iterator begin, unsigned char *arr, int length) { int i = 0; for (auto it = begin; it != begin + length; ++it,++i) { arr[i] = *it; } } void CKELONCommDlg::OnBnClickedBtnCleargrid() { // TODO: 在此添加控件通知处理程序代码 m_datagrid.SetRowCount(1); /*m_datagrid.AutoSizeColumns(); m_datagrid.ExpandColumnsToFit();*/ } 配置文件:

[cpp] view plain copy print ? //串口配置信息  //串口comm  //波特率baudrate  //校验位parity  //数据位data  //停止位stop    [comminfo]  comm=4  baudrate=9600  parity=even  data=8  stop=1    //时间信息  //timespan ms 重发数据  // 重复发送sendtimes次  [timeinfo]  timespan=500  sendtimes=1    //消息头配置  //每一个消息头有:命令类型、命令子类型、操作结果、描述信息  [msghead]  msgcount=18  [msg0]  cmdtype=7  cmdsubtype=1  result=0  description=查询设备版本号  //。。。。。。  [msg17]  cmdtype=103  cmdsubtype=1  result=0  description=读电量模块型号和厂家信息   //串口配置信息 //串口comm //波特率baudrate //校验位parity //数据位data //停止位stop [comminfo] comm=4 baudrate=9600 parity=even data=8 stop=1 //时间信息 //timespan ms 重发数据 // 重复发送sendtimes次 [timeinfo] timespan=500 sendtimes=1 //消息头配置 //每一个消息头有:命令类型、命令子类型、操作结果、描述信息 [msghead] msgcount=18 [msg0] cmdtype=7 cmdsubtype=1 result=0 description=查询设备版本号 //。。。。。。 [msg17] cmdtype=103 cmdsubtype=1 result=0 description=读电量模块型号和厂家信息

转载请注明原文地址: https://www.6miu.com/read-1600326.html

最新回复(0)