管道流量采集实验指导书

版权声明:本文为博主原创文章,转载请注明作者。 https://blog.csdn.net/a18792721831/article/details/80870613

管道流量采集实验指导书

      项目较大,是我毕业设计的成果之一,完成整个项目需要的时间大概为8小时(1天,非常熟悉MFC编程),24小时(3天,熟悉MFC编程),48小时(6天,较熟悉),2周(只懂得语法)

一、 实验目的

1.1 了解485现场总线;
1.2 了解Visual Studio 2015编译环境;
1.3 了解磁漩涡流量传感器测量原理;
1.4 掌握MODBUS协议
1.5 掌握C++中MFC类库编程;
1.6 掌握MFC程序打包;
1.7 掌握MySQL数据库;
1.8 掌握CRC校验码计算;
1.9 掌握串口编程。

二、 实验环境及设备

      Windows系统、MySQL数据库、Visual Studio 2015、水仓系统、KJ402-F1矿用本安型水文检测分站、GLC30/50矿用磁漩涡流量传感器、FD15直流稳压电源。

三、 设备简介

3.1 RS-485现场总线

      电子工业协会于 1983 年在 RS-422 工业总线标准的基础之上,制订并发布了 RS-485 总线工业标准。RS-485 工业总线标准能够有效支持多个分节点和通信距离远,并且对于信息的接收灵敏度较高等特性。在工业通信网络中,RS-485 总线一般主要用于与外部各种工业设备进行信息传输和数据交换,所具备的对于噪声的有效抑制能力、高效的数据传输速率与良好的数据传输的可靠性能以及可扩展的通信电缆的长度是其他的许多工业通信标准所无法比拟的。因此,RS-485 总线在诸多个领域得到了广泛的应用,比如在工业控制领域、交通的自动化控制领域和现场总线通信网络等。
      RS-485 总线标准规定了总线接口的电气特性标准即对于 2 个逻辑状态的定义:正电平在+2V~+6V 之间,表示一个逻辑状态;负电平在-2V~-6V 之间,则表示另一个逻辑状态;数字信号采用差分传输方式,能够有效减少噪声信号的干扰。

3.2 MODBUS协议

      MODBUS通信协议是在1978年由美国的可编程控制器供应商莫迪康公司进行制定的一种工业现场总线控制器的通信网络协议。由于MODBUS通信协议具有高度的自由开放性以及用户可以快速熟悉的掌握通信协议,使得变得简单且易学,从而使得它比很多其他受商业利益驱使的工业控制通信协议标准取得更快的发展,因此普遍受到大量的第三方产品设计厂商、各种各样的终端用户以及大量的系统芯片集成商的广泛支持,并且被应用于智能化仪表通信网络和现场总线控制技术等领域。
      MODBUS通信协议是可以认为是属于在应用层的工业控制技术的通信协议,在物理层方面将可以遵循RS-485总线标准或者光纤等信息传输线路,将会主要应用于主机和从机以及远程通信控制模块三者之间进行的网络信息传输。现在在世界各地已经有大量釆用MODBUS通信协议的工业现场总线技术和设备以及各种工业通信网络的控制模块应用于国内外的各种工业控制现场。
      通过MODBUS通信协议构建各种现场总线网络,某一个控制器可以与其他各种控制器之间进行相互通信或者控制器的数据信息经由一个通信网络与其它在网络中互连的通信设备之间可以实现通信。因此,MODBUS通信协议通过逐渐发展已经成为一种通用的工业通信标准。
      标准的MODBUS口是使用RS-232C兼容串行接口,它定义了连接口的针脚、电缆、信号位、传输波特率、奇偶校验等,控制器能直接或经由Modem组网。
控制器通信使用主/从技术,即仅主设备能初始化传输(查询),其它设备(从设备)根据主设备查询提供的数据做出响应。无论是主设备查询还是从设备响应,每个MODBUS帧都包括地址域、功能域、数据域、错误检测域。该协议分RTU和ASCII两种传输模式,相对于ASC1I模式,RTU模式表达相同的信息需要较少的位数,且在相同通讯速率下具有更大的数据流量,因此通常情况下,一般工业智能仪器仪表都是采用RTU模式。
      在RTU模式下,信息帧以3.5个字符的静态时间间隔为起始符和结束符,在信息帧格式中均用T1-T2-T3-T4表示。每两个字符之间发送或者接收的时间间隔不能超过1.5倍字符传输时间。如果两个字符时间问隔超过了3.5倍的字符传输时间,协议就认为一帧数据已经接收,新的一帧数据传输开始。地址域用来写入从设备的地址。功能域用来写入主设备要求从设备执行的功能代码,如03或06。数据域用来写入主设备告之从设备的一些额外的信息,如连续的寄存器的起始地址、寄存器的数量等。CRC校验是RTU模式下的错误校验方式。数据帧中的数据采用十六进制数。如表1所示。

表1 RTU模式下的信息帧格式 帧起始地| 址域功 | 能码域 | 数据域 | CRC校验域 | 结束 :—-| :——- |:——-|:———|:———–|:—— T1-T2-T3-T4|8bits|8bits|n * 8bits|16bits|T1-T2-T3-T4 T1-T2-T3-T4|1B|1B|nB|2B|T1-T2-T3-T4       根据MODBUS协议规定,不同功能码对应数据域格式不尽相同,常用的功能码有1、2、3、4、5、6、15和16,分别表示读线圈、读输入状态、读保持寄存器、读输入寄存器、写单个线圈、写单个输入状态、写多个线圈,写多个保持寄存器。

3.3 水仓系统

      水仓系统由大水仓、蓄水池、应急水泵、变频器水泵、放水阀门、明渠、暗渠、水舌组成;在水仓系统设置有管道流量传感器、明渠水位传感器、水舌温度水位传感器、水仓水位温度传感器、蓄水池水位温度传感器、管道压力温度传感器。水仓系统的工作方式:首先打开放水阀门,水从大水仓通过放水阀门流入明渠,经过水舌流入暗渠,然后汇集于蓄水池,最后通过变频器水泵把水从蓄水池抽入大水仓,完成整个循环。在使用过程中,需要注意蓄水池水位需要保持一定的水位,否则水泵中进入空气,无法工作;同时也要注意蓄水池中的水不能溢出。

3.4 KJ402-F1矿用本安型水文检测分站

      KJ402-F1 矿用本安型水文监测分站是一个数据监测控制分站,具有4 路模拟量采集和4 路数字量采集接口、1 路485 对上通讯接口、2 路对下通讯接口和一个液晶显示屏。液晶显示屏显示分站挂接的传感器采集到的各种数据。
      分站集电子技术、计算机技术与数据通信技术于一体,具有测量精度高、实时性强、运行稳定和自动化程度高等特点,其体积小、重量轻、安装方便,能够长期在无人值守的条件下自动对各类水文等进行监测与控制。

  • 分站具有与传输接口双向通讯及工作状态指示和显示功能。
  • 分站具有模拟量采集和数字量采集功能以及开关量控制功能。
  • 初始化参数通过上位机软件设置。
  • 分站具有自诊断和故障指示功能。
  • 分站具有外接备用电源

3.5 GLC30/50矿用磁漩涡流量传感器

      GLC30/50 矿用磁旋涡流量传感器(以下简称传感器)是采用最先进磁旋涡数字检测技术开发出的新型智能流量传感器。该传感器是将双检测抗震涡街传感器和电磁传感器技术结合为一体的流量测量仪表。传感器采用先进的单片机技术和微功耗高新技术,具有以下特点:耐腐蚀性能好、结构简单、使用寿命长、性能稳定,能识别流量与振动数据,消除所有干扰信号,具有良好的抗震,抗干扰能力,可耐 1g 振动(压电式耐振≤0.2g),使计量性能稳定、可靠;下限流量低,量程比大,测量准确、精度高。
      GLC30/50 矿用磁旋涡流量传感器是根据法拉第电磁感应原里研制的,在表壳底部放置一个磁钢产生强磁场,磁力线穿过管道,当介质流过传感器强磁场时,切割磁力线感应出脉动的磁动势,用电极拾取此电信号,在一定的流速范围内其频率正比于流量。
磁漩涡流量传感器原理图


图1磁漩涡流量传感器原理图
1—磁钢 2—电极 3—表壳 4—电极压紧螺丝
5—信号插头 6—显示仪 7—功能键 8—接地端子 9—信号输出接线口

3.6 FD15直流稳压电源

      FD15 专用直流稳压采用先进的补偿式技术设计制造,适应用于矿山、乡镇等地区电网电压波动较大的工作环境,能自动保持输出电压的稳定,以保证用电设备的正常运行。输入电压范围宽,输出电压稳定。具有过流保护,短路保护,过压保护,增强了电源的安全性与可靠性。

四、 实验原理

4.1 发送帧

发送帧解析图


图2发送帧解析图

  • 长度:长度是除去地址码的帧的长度,最后以16进制保存。
  • 属性:00表示查询单条数据,分站只需要回复一条数据即可,回复的数据由站号和通道号指定
  • 站号:指定回复的分站设备
  • 通道号:指定回复的传感器测量值
  • 时间BCD码:用4位二进制数来表示1位十进制数中的0~9这10个数码。是一种二进制的数字编码形式,用二进制编码的十进制代码。通俗的说就是把十进制数字看作16进制数字,比如10进制的18转换为16进制为12,但是使用BCD码16进制还是18。
  • CRC校验码:在CRC计算时只用8个数据位,起始位及停止位,如有奇偶校验位也包括奇偶校验位,都不参与CRC计算。
    CRC计算方法是:
    1、加载一值为0XFFFF的16位寄存器,此寄存器为CRC寄存器。
    2、把第一个8位二进制数据(即通讯信息帧的第一个字节)与16位的CRC寄存器的相异或,异或的结果仍存放于该CRC寄存器中。
    3、把CRC寄存器的内容右移一位,用0填补最高位,并检测移出位是0还是1。
    4、如果移出位为零,则重复第三步(再次右移一位);如果移出位为1,CRC寄存器与0XA001进行异或。
    5、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理。
    6、重复步骤2和5,进行通讯信息帧下一个字节的处理。
    7、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换
    8、最后得到的CRC寄存器内容即为:CRC校验码。

4.2 接收帧

接收帧解析图

图3接收帧解析图
  • 标志:FF表示高电平为1
  • 长度:长度是除去地址码的帧的长度,最后以16进制保存。
  • 属性:发送帧的属性码加0X80
  • 站号:回复的分站设备
  • 通道号:回复的传感器测量值
  • 数据1:3个字节
  • 数据2:3个字节
  • 数据3:3个字节
  • 随机位:两次查询的时间如果较短,那么回复的内容可能完全相同,此时回复数据帧的接收会
    把两个回复帧误认为是一个回复帧,这样就出现伪丢帧,所以,随机位解决这种情况,防止在短时间内回复的数据帧完全相同,而造成伪丢帧。

      数据有三个是因为分站设备需要适配所有的传感器,磁漩涡流量传感器只有一个数据,所以需要数据1即可,但是其他的传感器比如温度压力传感器需要2个数据,而明渠中的水位有三个数据(水位、泥位、温度),所以数据位有三个,如果无数据,默认用0来补全。

五、 实验步骤

5.1系统框架构建

首先创建一个基于对话框的空工程,不需要关于框和系统菜单。
这里写图片描述


图4工程预设图

VS2015会自动生成基于工程名字的App和Dlg文件。
这里写图片描述


图5工程自动生成文件图

接下来创建额外的7个对话框,对话框的名字和ID如表所示。

表2对话框和ID映射表

帧起始地 址域功
流量统计分析表 IDD_EXCEL
流量预测 IDD_FORECAST
历史流量数据 IDD_HISTORY
原理讲解 IDD_INTERPRET
查看参数 IDD_LOOKDATA
设置参数 IDD_SETDATA
协议测试 IDD_TEXT

为添加的对话框绑定各自的类,右键对话框->【添加类】->【输入类名】->【确定】。

这里写图片描述


图6创建的类

5.2外部插件导入

5.2.1串口插件导入

      系统中需要使用串口编程,所以需要使用到串口插件。首先在互联网上下载串口插件的ocx文件,然后把ocx文件放到系统盘的System32文件夹下;最后使用管理员身份打开CMD,在CMD中切换到System32文件夹下,运行命令:Regsvr32 串口插件的ocx名字.ocx。
      CMD会提示注册成功,此时可以在VS2015中【工具】->【选择工具箱】->【COM组件】->【浏览】->找到串口插件的ocx文件->【确定】。如果插件导入成功,可以在VS2015的工具箱中找到,或者可以在对话框中画出一个电话类似的图标。

5.2.2折线图插件导入

      系统中画折线图需要用到TeeChart的一个插件。首先在互联网上下载TeeChart插件的ocx文件,然后注册插件,并导入到VS2015中;导入成功后,VS2015的工具箱中不会显示插件,但是可以画出一个折线图的样子,此时导入成功。

5.3对话框设计

其中对话框的控件和属性如下:

5.3.1查看参数


表3查看参数控件表

类型 ID标识符 属性
对话框 IDD_LOOKDATA Caption:查看参数;Style:Popup;System Menu:false;
按钮 IDOK Caption:确定;
静态文本框 IDC_STATIC Align Text:Left;Caption:服务器IP地址:;
按钮 IDCANCEL Caption:取消;
组合框 IDC_STATIC Caption:数据库参数;
组合框 IDC_STATIC Caption:通信参数;
静态文本框 IDC_STATIC Align Text:Left;Caption:服务器端口号:;
静态文本框 IDC_STATIC Align Text:Left;Caption:登陆用户名:;
静态文本框 IDC_STATIC Align Text:Left;Caption:登陆密码:;
静态文本框 IDC_STATIC Align Text:Left;Caption:连接数据库名:;
静态文本框 IDC_STATIC Align Text:Left;Caption:COM端口:;
静态文本框 IDC_STATIC Align Text:Left;Caption:波特率:;
静态文本框 IDC_STATIC Align Text:Left;Caption:校验位:;
静态文本框 IDC_STATIC Align Text:Left;Caption:数据位:;
静态文本框 IDC_STATIC Align Text:Left;Caption:停止位:;
静态文本框 IDC_STATIC Align Text:Left;Caption:自动刷新时间:;
静态文本框 IDC_L_IPADD Align Text:Center;Caption:;
静态文本框 IDC_L_COM Align Text:Center;Caption:;
静态文本框 IDC_L_PASSWORD Align Text:Center;Caption:;
静态文本框 IDC_L_USERNAME Align Text:Center;Caption:;
静态文本框 IDC_L_DATABASE Align Text:Center;Caption:;
静态文本框 IDC_L_COMM Align Text:Center;Caption:;
静态文本框 IDC_L_BOTELV Align Text:Center;Caption:;
静态文本框 IDC_L_JIAOYAN Align Text:Center;Caption:;
静态文本框 IDC_L_SHUJU Align Text:Center;Caption:;
静态文本框 IDC_L_TINGZHI Align Text:Center;Caption:;
静态文本框 IDC_L_TIME Align Text:Center;Caption:;

这里写图片描述


图7查看参数效果图

图中红框就是有各自ID的静态文本框。

5.3.2历史流量


表4历史流量折线图控件表

类型 ID标识符 属性
对话框 IDD_HISTORY Caption:历史流量数据;Style:Popup;System Menu:false;
静态文本框 IDC_STATIC Align Text:Left;Caption:选择日期:;
时间 IDC_H_DATE Format:短日期;
折线图 IDC_H_TCHART
按钮 IDOK Caption:确定;
静态文本框 IDC_H_TITLE Align Text:Center;Caption:;
按钮 IDCANCEL Caption:取消;


表5历史流量折线图控件变量映射表

控件ID 类别 变量类型 变量名
IDC_DATAVIEW Value Cstring m_Dataview
IDC_H_DATE Control CDateTimeCtrl c_HDate
IDC_H_TCHART Control CTchart1 c_HTChart

折线图插件设置:
双击插件会弹出设置对话框:
这里写图片描述


图8折线图插件设置图

      其中General调节查看折线图时鼠标的样式,Axis设置坐标系,Titles设置折线图标题,第一行的Series菜单设置折线图连线的样式。
这里写图片描述


图9历史流量对话框效果图

5.3.3流量预测


表6流量预测控件表

类型 ID标识符 属性
对话框 IDD_FORECAST Caption:流量预测;Style:Popup;System Menu:false;
静态文本框 IDC_STATIC Align Text:Left;Caption:预测结果:;
输入框 IDC_F_RESULT Align Text:Left;
输入框 IDC_F_REASON Align Text:Left;
按钮 IDOK Caption:确定;
静态文本框 IDC_STATIC Align Text:Left;Caption:预测理由:;

这里写图片描述


图10流量预测对话框效果图

5.3.4设置参数


表7设置参数控件表

类型 ID标识符 属性
对话框 IDD_LOOKDATA Caption:查看参数;Style:Popup;System Menu:false;
按钮 IDOK Caption:确定;
静态文本框 IDC_STATIC Align Text:Left;Caption:服务器IP地址:;
按钮 IDCANCEL Caption:取消;
组合框 IDC_STATIC Caption:数据库参数;
组合框 IDC_STATIC Caption:通信参数;
静态文本框 IDC_STATIC Align Text:Left;Caption:服务器端口号:;
静态文本框 IDC_STATIC Align Text:Left;Caption:登陆用户名:;
静态文本框 IDC_STATIC Align Text:Left;Caption:登陆密码:;
静态文本框 IDC_STATIC Align Text:Left;Caption:连接数据库名:;
静态文本框 IDC_STATIC Align Text:Left;Caption:COM端口:;
静态文本框 IDC_STATIC Align Text:Left;Caption:波特率:;
静态文本框 IDC_STATIC Align Text:Left;Caption:校验位:;
静态文本框 IDC_STATIC Align Text:Left;Caption:数据位:;
静态文本框 IDC_STATIC Align Text:Left;Caption:停止位:;
静态文本框 IDC_STATIC Align Text:Left;Caption:自动刷新时间:;
IP地址输入框 IDC_S_IPADD
输入框 IDC_S_COM Align Text:Left;
输入框 IDC_S_USERNAME Align Text:Left;
输入框 IDC_S_PASSWORD Align Text:Left;Password:true;
输入框 IDC_S_DATABASE Align Text:Left;
下拉列表 IDC_S_COMM Type:Dropdown;
下拉列表 IDC_S_BOTELV Type:Dropdown;
下拉列表 IDC_S_JIAO Type:Dropdown;
下拉列表 IDC_S_SHUJU Type:Dropdown;
下拉列表 IDC_S_TING Type:Dropdown;
输入框 IDC_S_TIME Align Text:Left;
单选按钮 IDC_S_HOUR Caption:小时(h);
单选按钮 IDC_S_MINTE Caption:分钟(m);
单选按钮 IDC_S_SECEND Caption:秒(s);


表8设置参数控件变量映射表

控件ID 类别 变量类型 变量名
IDC_S_IPADD Value DWORD m_SetIpadd
IDC_S_COMM Control CComboBox c_Comm
IDC_S_BOTELV Control CComboBox c_Botelv
IDC_S_COM Value Cstring m_SetCom
IDC_S_USERNAME Value Cstring m_SetUsername
IDC_S_PASSWORD Value Cstring m_SetPassword
IDC_S_DATABASE Value Cstring m_SetDatabase
IDC_S_JIAO Control CComboBox c_Jiaoyan
IDC_S_SHUJU Control CComboBox c_Shuju
IDC_S_TING Control CComboBox c_Tingzhi
IDC_S_TIME Control CEdit c_Time
IDCANCEL Control CButton c_ButtonH

这里写图片描述


图11设置参数对话框效果图

5.3.5统计表


表9统计表控件表

类型 ID标识符 属性
对话框 IDD_EXCEL Caption:流量统计分析表;Style:Popup;System Menu:false;
静态文本框 IDC_E_TITLE Align Text:Center;
列表 IDC_E_LIST Alignment:Left;View:Report;
时间 IDC_E_DATE Format:短日期;
按钮 IDOK Caption:确定;
静态文本框 IDC_STATIC Align Text:Left;Caption:选择日期:;
按钮 IDCANCEL Caption:取消;


表10统计表控件变量映射表

控件ID 类别 变量类型 变量名
IDC_E_TITLE Value Cstring m_ETitle
IDC_E_LIST Control CListCtrl c_EList
IDC_E_DATE Control CDateTimeCtrl c_EDate

这里写图片描述


图12统计表对话框效果图

5.3.6协议测试


表11协议测试控件表

类型 ID标识符 属性
对话框 IDD_TEXT Caption:协议测试;Style:Popup;System Menu:false;
静态文本框 IDC_E_TITLE Align Text:Center;
输入框 IDC_T_INPUT Align Text:Left;
时间 IDC_T_DATA Format:短日期;
按钮 IDOK Caption:确定;
静态文本框 IDC_STATIC Align Text:Left;Caption:(必须以空格分割);
按钮 IDCANCEL Caption:取消;
静态文本框 IDC_STATIC Align Text:Left;Caption:请输入协议帧:;
静态文本框 IDC_STATIC Align Text:Left;Caption:测试结果:;
时间 IDC_T_TIME Format:时间;
按钮 IDC_BUTTON1 Caption:生成协议帧;


表12协议测试控件变量映射表

控件ID 类别 变量类型 变量名
IDC_DATAVIEW Value Cstring m_Dataview
IDC_T_DATA Control CDateTimeCtrl c_Data
IDC_T_TIME Control CDateTimeCtrl c_Time

这里写图片描述


图13协议测试对话框效果图

5.3.7原理讲解


表13协议测试控件表

类型 ID标识符 属性
对话框 IDD_INTERPRET Caption:原理讲解;Style:Popup;System Menu:false;
图片 IDC_I_PIC
按钮 IDC_I_LAST Caption:上一页;
按钮 IDC_I_NEXT Caption:下一页;


表14协议测试控件变量映射表

控件ID 类别 变量类型 变量名
IDC_I_LAST Control CButton m_InterpretLast
IDC_I_NEXT Control CButton m_InterpretNext

这里写图片描述


图14协议测试对话框效果图

5.3.8主对话框


表15主对话框控件表

类型 ID标识符 属性
对话框 IDD_DIA_DIALOG Caption:管道流量采集;Style:Popup;System Menu:true;
静态文本框 IDC_DATAVIEW Align Text:Center;
静态文本框 IDC_XIEYIZHEN Align Text:Left;
折线图 IDC_TC_DATA
串口 IDC_MSCOMM1


表16主对话框控件变量映射表

控件ID 类别 变量类型 变量名
IDC_DATAVIEW Value Cstring m_Dataview
IDC_TC_DATA Control CTchart1 c_TChartData
IDC_MSCOMM1 Control CMscomm1 c_Comm

这里写图片描述


图15主对话框效果图

5.4单例类

      在整个系统中,有些数据必须一直保持唯一,比如设置参数在系统运行的全部时间里必须唯一,否则就会出错,所以,需要为必须保持唯一性的数据使用单例类来调用,这样就可以保证唯一性。单例类是单例模式的实现,单例模式是说当一个类中只有一个实例化的对象时,这个类就是单例类。单例类的写法很简单,首先私有化类的构造方法,并且类中含有一个静态的本类的私有实例化对象,然后创建一个静态的公有方法来获取此实例,这样,每次获取的实例对象,都是类的私有化的对象。
      在系统中数据库、对话框、设置参数都需要唯一的调用,所以设置三个单例类来保存数据DataConfig.h:
      注意,所有的代码在理解的基础上自行输入,不要拷贝,否则会出错,其中的省略号自行按照前面的补充完整。

#pragma once
#include "Configdata.h"
class CDataConfig
{
public:
    static CDataConfig * GetCDataConfig() {
        static CDataConfig * m_CDataConfig;
        if (NULL == m_CDataConfig) {
            m_CDataConfig = new CDataConfig();
        }
        return m_CDataConfig;
    }
    ~CDataConfig();
private:
    CDataConfig();
    // 连接数据库服务器的IP地址
    CString m_IPAdress;
    //连接数据库服务器的端口号
    CString m_ComNum;
    // 连接数据库的用户名,默认为root
    CString m_Username;
    // 连接数据库的密码,默认为root
    CString m_Password;
    // 连接数据库的数据库名字
    CString m_Database;
    //COM端口
    CString m_Comm;
    //波特率
    CString m_Botelv;
    //校验码
    CString m_Jiaoyan;
    //数据位
    CString m_Shuju;
    //停止位
    CString m_Tingzhi;
    //时间
    CString m_Time;
public:
    // 获取数据库服务器的IP地址
    CString getIpAddress();
    // 获取数据库服务器端口号
    CString getComnum();
    // 获取连接的数据库名字
    CString getDatabase();
    // 获取连接数据库的密码
    CString getPassword();
    // 获取连接数据库的用户名
    CString getUsername();
    CString getComm();
    CString getBotelv();
    CString getJiaoyan();
    CString getShuju();
    CString getTingzhi();
    CString getTime();
    void setComm(CString m_Comm);
    void setBotelv(CString m_Botelv);
    void setJiaoyan(CString m_Jiaoyan);
    void setShuju(CString m_Shuju);
    void setTingzhi(CString m_Tingzhi);
    void setTime(CString m_Time);
    // 设置数据库服务器端口号
    void setComnum(CString m_ComNum);
    void setDatabase(CString m_Database);
    void setIpAddress(CString m_IpAddress);
    void setPassword(CString m_Password);
    void setUsername(CString m_Username);
};

DataConfig.cpp:

#include "stdafx.h"
#include "DataConfig.h"
CDataConfig::CDataConfig()
    : m_IPAdress(_T(""))
    , m_ComNum(_T(""))
    , m_Username(_T(""))
    , m_Password(_T(""))
    , m_Database(_T(""))
    , m_Comm(_T(""))
    , m_Botelv(_T(""))
    , m_Jiaoyan(_T(""))
    , m_Shuju(_T(""))
    , m_Tingzhi(_T(""))
    , m_Time(_T(""))
{
}
CDataConfig::~CDataConfig()
{
}
// 获取数据库服务器的IP地址
CString CDataConfig::getIpAddress()
{
    if (!this->m_IPAdress.IsEmpty()) {
        return this->m_IPAdress;
    }
    else
    {
        return NULL;
    }
}
// 获取数据库服务器端口号
CString CDataConfig::getComnum()
{
    if (!this->m_ComNum.IsEmpty()) {
        return this->m_ComNum;
    }
    else {
        return 0;
    }
}
// 获取连接的数据库名字
CString CDataConfig::getDatabase()
{
    if (!this->m_Database.IsEmpty()) {
        return this->m_Database;
    }
    else {
        return NULL;
    }
}
// 获取连接数据库的密码
CString CDataConfig::getPassword()
{
    if (!this->m_Password.IsEmpty()) {
        return this->m_Password;
    }
    else {
        return NULL;
    }
}
// 获取连接数据库的用户名
CString CDataConfig::getUsername()
{
    if (!this->m_Username.IsEmpty()) {
        return this->m_Username;
    }
    else {
        return NULL;
    }
}
CString CDataConfig::getComm() {
    if (!this->m_Comm.IsEmpty()) {
        return this->m_Comm;
    }
    else {
        return NULL;
    }
}
CString CDataConfig::getBotelv() {
    if (!this->m_Botelv.IsEmpty()) {
        return this->m_Botelv;
    }
    else {
        return NULL;
    }
}
CString CDataConfig::getJiaoyan() {
    if (!this->m_Jiaoyan.IsEmpty()) {
        return this->m_Jiaoyan;
    }
    else {
        return NULL;
    }
}
CString CDataConfig::getShuju() {
    if (!this->m_Shuju.IsEmpty()) {
        return this->m_Shuju;
    }
    else {
        return NULL;
    }
}
CString CDataConfig::getTingzhi() {
    if (!this->m_Tingzhi.IsEmpty()) {
        return this->m_Tingzhi;
    }
    else {
        return NULL;
    }
}
CString CDataConfig::getTime() {
    if (!this->m_Time.IsEmpty()) {
        return this->m_Time;
    }
    else {
        return NULL;
    }
}
void CDataConfig::setComnum(CString m_ConNum) {
    this->m_ComNum = m_ConNum;
}
void CDataConfig::setDatabase(CString m_Database) {
    this->m_Database = m_Database;
}
void CDataConfig::setIpAddress(CString m_IpAddress) {
    this->m_IPAdress = m_IpAddress;
}
void CDataConfig::setPassword(CString m_Password) {
    this->m_Password = m_Password;
}
void CDataConfig::setUsername(CString m_Username) {
    this->m_Username = m_Username;
}
void CDataConfig::setComm(CString m_Comm) {
    this->m_Comm = m_Comm;
}
void CDataConfig::setBotelv(CString m_Botelv) {
    this->m_Botelv = m_Botelv;
}
void CDataConfig::setJiaoyan(CString m_Jiaoyan) {
    this->m_Jiaoyan = m_Jiaoyan;
}
void CDataConfig::setShuju(CString m_Shuju) {
    this->m_Shuju = m_Shuju;
}
void CDataConfig::setTingzhi(CString  m_Tingzhi) {
    this->m_Tingzhi = m_Tingzhi;
}
void CDataConfig::setTime(CString m_Time) {
    this->m_Time = m_Time;
}

MysqlConnection.h

#pragma once
//头文件
#include "mysql.h"
#include "DataConfig.h"
#include "Configdata.h"
#include "TeeData.h"
class CMysqlConnection
{
private:
    CMysqlConnection();
    MYSQL m_mysql;
public:
    static CMysqlConnection * GetMysqlConn() {
        static CMysqlConnection * m_MysqlConn;
        if (NULL == m_MysqlConn) {
            m_MysqlConn = new CMysqlConnection();
        }
        return m_MysqlConn;
    }
    ~CMysqlConnection();
    BOOL setData(double m_FlowValue, CString time);
    CObArray * getData(SYSTEMTIME time);
    void ConnectionMysql();
};

MysqlConnection.cpp:

#include "stdafx.h"
#include "Single.h"
#include "MysqlConnection.h"
#include "管道流量采集Dlg.h"
CMysqlConnection::CMysqlConnection()
{
}
CMysqlConnection::~CMysqlConnection()
{
}
BOOL CMysqlConnection::setData(double m_FlowValue, CString time) {
    CString sql;
    CMysqlConnection * m_MysqlConnection = CMysqlConnection::GetMysqlConn();
    CString temp;
    temp.Format("%.3f", m_FlowValue);
    //INSERT INTO `data` (`time`, `value`) VALUES ('2018-5-4 12:51:52', '0.00');
    sql.Format("INSERT INTO `data` (`time`, `value`) VALUES ('%s', '%s');",time,temp);
    if (!mysql_query(&m_MysqlConnection->m_mysql, sql)) {
        return TRUE;
    }
    else {
        return FALSE;
    }
}
CObArray * CMysqlConnection::getData(SYSTEMTIME time) {
    CObArray * m_QueryData = new CObArray();
    CMysqlConnection * m_MysqlConnection = CMysqlConnection::GetMysqlConn();
    CString sql;
    MYSQL_RES * m_res;
    MYSQL_ROW m_row;
    sql.Format("SELECT * FROM `data` WHERE time BETWEEN '%2d-%d-%d' AND '%2d-%d-%d' ORDER BY time;",
        time.wYear, time.wMonth, time.wDay, time.wYear, time.wMonth, time.wDay + 1);
    mysql_query(&m_MysqlConnection->m_mysql, sql);
    m_res = mysql_store_result(&m_MysqlConnection->m_mysql);
    if (NULL != m_res) {
        if (0 != m_res->row_count) {
            while (m_row = mysql_fetch_row(m_res)) {
                m_QueryData->Add(new CTeeData(atof(m_row[1]), m_row[0]));
                //CString b = m_row[0];//时间
                //CString a = m_row[1];//值
            }
            return m_QueryData;
        }
        else
        {
            AfxMessageBox("数据库没有数据");
            return NULL;
        }
    }
    return NULL;
}
void CMysqlConnection::ConnectionMysql()
{
    CConfigdata * m_Configdata = new CConfigdata();
    m_Configdata->getConfigdata();
    CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
    mysql_init(&this->m_mysql);
    //localhost:服务器 root/123456为账号密码 managesystemdb为数据库名 3306为端口    
    //if (!mysql_real_connect(&m_sqlCon, "localhost", "root", "123456", "managesystemdb", 3306, NULL, 0))
    if (!mysql_real_connect(&this->m_mysql,
        m_DataConfig->getIpAddress(),
        m_DataConfig->getUsername(),
        m_DataConfig->getPassword(),
        m_DataConfig->getDatabase(),
        atoi(m_DataConfig->getComnum()),
        NULL,
        0)
        ) {
        AfxMessageBox(mysql_error(&this->m_mysql));
        CSingle * m_Single = CSingle::GetSingle();
        if (m_Single->m_dlgSetdata->DoModal() == IDOK) {
            SetTimer(AfxGetMainWnd()->GetSafeHwnd(), 1, atoi(m_DataConfig->getTime()), NULL);
            //AfxMessageBox("开启定时器");
        }
    }
    else {
        CString sql;
        AfxMessageBox("连接数据库成功");
        mysql_query(&this->m_mysql, "SET NAMES 'utf-8'");
        //如果数据表不存在则重新创建
        sql.Format("CREATE TABLE If Not Exists `data` (`time` datetime NOT NULL,`value` double(5, 2) DEFAULT NULL,PRIMARY KEY(`time`)) ENGINE = InnoDB DEFAULT CHARSET = utf8; ");
        mysql_query(&this->m_mysql, sql);
        SetTimer(AfxGetMainWnd()->GetSafeHwnd(), 1, atoi(m_DataConfig->getTime()), NULL);
        //AfxMessageBox("定时器开始");
    }
    //mysql_query(&m_sqlCon, "SET NAMES 'GB2312'");//解决从数据库中读取数据后汉字乱码显示的问题  
}

Single.h

#pragma once
//include
#include "DlgInterpret.h"
#include "DlgText.h"
#include "DlgForecast.h"
#include "DlgHistory.h"
#include "DlgExcel.h"
#include "DlgSetdata.h"
#include "DlgLookdata.h"
#include "Resource.h"
class CSingle
{
private:
    CSingle();
public:
    ~CSingle();
    // 获取单例
    static CSingle* GetSingle() {
        static CSingle* m_Single;
        if (NULL == m_Single) {
            m_Single = new CSingle();
        }
        return m_Single;
    }
    CDlgExcel * m_dlgExcel;
    CDlgForecast * m_dlgForecast;
    CDlgHistory * m_dlgHistory;
    CDlgInterpret * m_dlgInterpret;
    CDlgText * m_dlgText;
    CDlgSetdata * m_dlgSetdata;
    CDlgLookdata * m_dlgLookdata;
};

Single.cpp:

#include "stdafx.h"
#include "Single.h"
CSingle::CSingle()
{
    if (NULL == this->m_dlgExcel) {
        this->m_dlgExcel = new CDlgExcel();
    }
    if (NULL == this->m_dlgForecast) {
        this->m_dlgForecast = new CDlgForecast();
    }
    if (NULL == this->m_dlgHistory) {
        this->m_dlgHistory = new CDlgHistory();
    }
    if (NULL == this->m_dlgInterpret) {
        this->m_dlgInterpret = new CDlgInterpret();
    }
    if (NULL == this->m_dlgText) {
        this->m_dlgText = new CDlgText();
    }
    if (NULL == this->m_dlgSetdata) {
        this->m_dlgSetdata = new CDlgSetdata();
    }
    if (NULL == this->m_dlgLookdata) {
        this->m_dlgLookdata = new CDlgLookdata();
    }
}
CSingle::~CSingle()
{
}

5.5注册表读写

ConfigData.h

#pragma once
//头文件
#include "DataConfig.h"
#include "DlgSetdata.h"
class CConfigdata
{
public:
    CConfigdata();
    ~CConfigdata();
    // 保存数据到注册表
    void setConfigdata();
    // 读取注册表数据到单例
    void getConfigdata();
};

ConfigData.cpp:

#include "stdafx.h"
#include "Configdata.h"
#include "MysqlConnection.h"
CConfigdata::CConfigdata()
{
}
CConfigdata::~CConfigdata()
{
}
/*
从注册表读取数据库参数
包括:
IP:localhost
com:3306
username:root
password:root
database:flowdata
*/
// 保存数据到注册表
void CConfigdata::setConfigdata()
{
    CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
    HKEY hKey;
    if (!RegOpenKeyEx(HKEY_CURRENT_USER,
        "SOFTWARE\\Flowtest", NULL, KEY_READ, &hKey)) {
        //如果在SOFTWARE下没有Folwtest文件夹那么就需要创建文件夹
        RegCreateKey(HKEY_CURRENT_USER, "Software\\Flowtest", &hKey);
        //AfxMessageBox("创建注册表项");
    }
    RegSetValue(hKey,"IPAddress",
        REG_SZ,m_DataConfig->getIpAddress(),
        strlen(m_DataConfig->getIpAddress()));
    RegSetValue(hKey, "COM",
        REG_SZ, m_DataConfig->getComnum(),
        strlen(m_DataConfig->getComnum()));
    RegSetValue(hKey, "Username",
        REG_SZ, m_DataConfig->getUsername(),
        strlen(m_DataConfig->getUsername()));
    RegSetValue(hKey, "Password",
        REG_SZ, m_DataConfig->getPassword(),
        strlen(m_DataConfig->getPassword()));
    RegSetValue(hKey, "Database",
        REG_SZ, m_DataConfig->getDatabase(),
        strlen(m_DataConfig->getDatabase()));
    RegSetValue(hKey, "Comm",
        REG_SZ, m_DataConfig->getComm(),
        strlen(m_DataConfig->getComm()));
    RegSetValue(hKey, "Botelv",
        REG_SZ, m_DataConfig->getBotelv(),
        strlen(m_DataConfig->getBotelv()));
    RegSetValue(hKey, "Jiaoyan",
        REG_SZ, m_DataConfig->getJiaoyan(),
        strlen(m_DataConfig->getJiaoyan()));
    RegSetValue(hKey, "Shuju",
        REG_SZ, m_DataConfig->getShuju(),
        strlen(m_DataConfig->getShuju()));
    RegSetValue(hKey, "Tingzhi",
        REG_SZ, m_DataConfig->getTingzhi(),
        strlen(m_DataConfig->getTingzhi()));
    RegSetValue(hKey, "Time",
        REG_SZ, m_DataConfig->getTime(),
        strlen(m_DataConfig->getTime()));
    AfxMessageBox("注册表写入成功");
}
// 读取注册表数据到单例
void CConfigdata::getConfigdata()
{
    CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
    HKEY hKey;
    char * temp;
    long a;
    if (ERROR_SUCCESS !=
        RegOpenKeyEx(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest", NULL, KEY_READ, &hKey)) {
        //如果注册表中没有数据,那么调用设置模态对话框提示输入
        //同时创建注册表
        CDlgSetdata m_dlgSetdata;
        if (m_dlgSetdata.DoModal() == IDOK) {
            this->setConfigdata();
            //AfxMessageBox("创建后从注册表读取到单例");
        }
    }
    else {
        //RegQueryValue(HKEY_CURRENT_USER,
        //"SOFTWARE\\ArwenSoft\\dataip", ipadd, &a);
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\IPAddress", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\IPAddress", temp, &a);
        m_DataConfig->setIpAddress(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\COM", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\COM", temp, &a);
        m_DataConfig->setComnum(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Username", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Username", temp, &a);
        m_DataConfig->setUsername(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Password", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Password", temp, &a);
        m_DataConfig->setPassword(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Database", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Database", temp, &a);
        m_DataConfig->setDatabase(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Comm", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Comm", temp, &a);
        m_DataConfig->setComm(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Botelv", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Botelv", temp, &a);
        m_DataConfig->setBotelv(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Jiaoyan", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Jiaoyan", temp, &a);
        m_DataConfig->setJiaoyan(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Shuju", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Shuju", temp, &a);
        m_DataConfig->setShuju(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Tingzhi", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Tingzhi", temp, &a);
        m_DataConfig->setTingzhi(temp);
        delete temp;
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Time", NULL, &a);
        temp = new char[a];
        RegQueryValue(HKEY_CURRENT_USER,
            "SOFTWARE\\Flowtest\\Time", temp, &a);
        m_DataConfig->setTime(temp);
        delete temp;
        AfxMessageBox("直接从注册表读取参数");
    }
}

5.6折线图、统计表、数据库bean类

      Bean类是对应于数据表中一行数据的类,一个bean类的对象就是表中存储的一行,在本系统中,数据处理,这是最小的单位
TeeData.h

#pragma once
class CTeeData : public CObject
{
public:
    CTeeData();
    ~CTeeData();
    double m_Viewvalue;
    CString m_ViewData;
    CTeeData(double m_Viewvalue,CString m_ViewData);
};

TeeData.cpp:

 
 

5.7对话框功能实现

5.7.1统计表

消息中重写OnCtlColor方法:

// TODO:  在此更改 DC 的任何特性
CFont m_SetFont;switch (pWnd->GetDlgCtrlID()){case IDC_E_TITLE:
m_SetFont.CreatePointFont(400,"楷体");pDC->SelectObject(&m_SetFont);
pDC->SetTextColor(RGB(0, 255, 0));pDC->SetBkColor(RGB(103, 103, 103));break;
default:break;}

重写OnInitDialog方法:

// TODO:  在此添加额外的初始化
SYSTEMTIME m_System;GetLocalTime(&m_System);
this->c_EList.InsertColumn(0, "时间", LVCFMT_CENTER, 200);
this->c_EList.InsertColumn(1, "流量", LVCFMT_CENTER, 100);
this->c_EList.InsertColumn(2, "时间", LVCFMT_CENTER, 200);
this->c_EList.InsertColumn(3, "流量", LVCFMT_CENTER, 100);
this->c_EList.InsertColumn(4, "时间", LVCFMT_CENTER, 200);
this->c_EList.InsertColumn(5, "流量", LVCFMT_CENTER, 100);
this->c_EList.InsertColumn(6, "时间", LVCFMT_CENTER, 200);
this->c_EList.InsertColumn(7, "流量", LVCFMT_CENTER, 100);
this->c_EList.InsertColumn(8, "时间", LVCFMT_CENTER, 200);
this->c_EList.InsertColumn(9, "流量", LVCFMT_CENTER, 100);setExcel(m_System);

增加参数写入到统计表控件中的方法setExcel:

void CDlgExcel::setExcel(SYSTEMTIME m_Systime)
{CMysqlConnection * m_MysqlConnection = CMysqlConnection::GetMysqlConn();
CObArray * m_ExcelData = new CObArray();double flowsum = 0.0;//总流量数
double max = 0.0;//最大值double min = 0.0;//最小值int nItem = 0;//行数
int sum = 0;//总记录数CTeeData * m_tmpTeedata;//一个数据元CString temp;
m_ExcelData = m_MysqlConnection->getData(m_Systime);if (m_ExcelData != NULL) {
this->c_EList.DeleteAllItems();
for (int i = 0; i < (m_ExcelData->GetSize() + 4) / 5; i++) {//添加一行
this->c_EList.InsertItem(nItem, "");for (int j = 0; j < 10; j++) {//添加一行中的一个元组数据
m_tmpTeedata = (CTeeData *)m_ExcelData->GetAt(sum);
flowsum += m_tmpTeedata->m_Viewvalue;
max=max>=m_tmpTeedata->m_Viewvalue?max:tmpTeedata->m_Viewvalue;
min=min<=m_tmpTeedata->m_Viewvalue?min:m_tmpTeedata->m_Viewvalue;
temp.Format("%.3f", m_tmpTeedata->m_Viewvalue);
this->c_EList.SetItemText(nItem, j, m_tmpTeedata->m_ViewData);//时间
this->c_EList.SetItemText(nItem, j + 1, temp);//值sum++;j++;
if (sum >= m_ExcelData->GetSize()) {    break;}}    nItem++;}//添加分析数据
this->c_EList.InsertItem(nItem, "");this->c_EList.SetItemText(nItem, 0, "最大值");
temp.Format("%.3f", max);this->c_EList.SetItemText(nItem, 1, temp);
this->c_EList.SetItemText(nItem, 2, "最小值");temp.Format("%.3f", min);
this->c_EList.SetItemText(nItem, 3, temp);this->c_EList.SetItemText(nItem, 4, "平均值");
temp.Format("%.6f", flowsum / m_ExcelData->GetSize());
this->c_EList.SetItemText(nItem, 5, temp);this->c_EList.SetItemText(nItem, 6, "总流量");
temp.Format("%.3f", flowsum);this->c_EList.SetItemText(nItem, 7, temp);
this->c_EList.SetItemText(nItem, 8, "总数据");temp.Format("%d", m_ExcelData->GetSize());
this->c_EList.SetItemText(nItem, 9, temp);
this->m_ETitle.Format("%2d-%d-%d的流量统计表", m_Systime.wYear, m_Systime.wMonth, m_Systime.wDay);GetDlgItem(IDC_E_TITLE)->SetWindowText(this->m_ETitle);}}

添加统计表中确定按钮的事件处理OnBnClickedEOk:

CTime m_ExcelTime;SYSTEMTIME m_tmpTime;this->c_EDate.GetTime(m_ExcelTime);
m_tmpTime.wYear = m_ExcelTime.GetYear();
m_tmpTime.wMonth = m_ExcelTime.GetMonth();
m_tmpTime.wDay = m_ExcelTime.GetDay();
m_tmpTime.wHour = m_ExcelTime.GetHour();
m_tmpTime.wMinute = m_ExcelTime.GetMinute();
m_tmpTime.wSecond = m_ExcelTime.GetSecond();setExcel(m_tmpTime);

DlgExcel.h:

#pragma once
#include "afxcmn.h"
#include "afxdtctl.h"
#include "MysqlConnection.h"
#include "TeeData.h"
// CDlgExcel 对话框
class CDlgExcel : public CDialogEx
{
    DECLARE_DYNAMIC(CDlgExcel)
public:
    CDlgExcel(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CDlgExcel();
// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_EXCEL };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    DECLARE_MESSAGE_MAP()
public:
    // 统计表标题
    CString m_ETitle;
    // 列表
    CListCtrl c_EList;
    // 日期
    CDateTimeCtrl c_EDate;
    afx_msg void OnBnClickedEOk();
    virtual BOOL OnInitDialog();
    void setExcel(SYSTEMTIME m_Systime);
    afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};

DlgExcel.cpp:

// DlgExcel.cpp : 实现文件
#include "stdafx.h"
#include "管道流量采集.h"
#include "DlgExcel.h"
#include "afxdialogex.h"
// CDlgExcel 对话框
IMPLEMENT_DYNAMIC(CDlgExcel, CDialogEx)
CDlgExcel::CDlgExcel(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_EXCEL, pParent)
    , m_ETitle(_T(""))
{
}
CDlgExcel::~CDlgExcel()
{
}
void CDlgExcel::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_E_TITLE, m_ETitle);
    DDX_Control(pDX, IDC_E_LIST, c_EList);
    DDX_Control(pDX, IDC_E_DATE, c_EDate);
}
BEGIN_MESSAGE_MAP(CDlgExcel, CDialogEx)
    ON_BN_CLICKED(IDOK, &CDlgExcel::OnBnClickedEOk)
    ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
// CDlgExcel 消息处理程序
void CDlgExcel::OnBnClickedEOk()
{
    // TODO: 在此添加控件通知处理程序代码
    /*
    统计表模块:
    在开始的时候查询当前时间的数据
    然后显示
    调用此方法知识获取到选择的日期
    另一个查询的函数
    接收需要查询的日期
    然后按照日期进行查询
    第一次打开的时候以当前的日期为查询日期
    查询到的数据在绘制函数中进行绘制
    表格只有两项
    时间     流量值

    最后手动添加
    最大值
    最小值
    总量
    平均值
    超过检测值的次数
    等等
    10列
    */
    CTime m_ExcelTime;
    SYSTEMTIME m_tmpTime;
    this->c_EDate.GetTime(m_ExcelTime);
    m_tmpTime.wYear = m_ExcelTime.GetYear();
    m_tmpTime.wMonth = m_ExcelTime.GetMonth();
    m_tmpTime.wDay = m_ExcelTime.GetDay();
    m_tmpTime.wHour = m_ExcelTime.GetHour();
    m_tmpTime.wMinute = m_ExcelTime.GetMinute();
    m_tmpTime.wSecond = m_ExcelTime.GetSecond();
    setExcel(m_tmpTime);
}
BOOL CDlgExcel::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // TODO:  在此添加额外的初始化
    SYSTEMTIME m_System;
    GetLocalTime(&m_System);
    this->c_EList.InsertColumn(0, "时间", LVCFMT_CENTER, 200);
    this->c_EList.InsertColumn(1, "流量", LVCFMT_CENTER, 100);
    this->c_EList.InsertColumn(2, "时间", LVCFMT_CENTER, 200);
    this->c_EList.InsertColumn(3, "流量", LVCFMT_CENTER, 100);
    this->c_EList.InsertColumn(4, "时间", LVCFMT_CENTER, 200);
    this->c_EList.InsertColumn(5, "流量", LVCFMT_CENTER, 100);
    this->c_EList.InsertColumn(6, "时间", LVCFMT_CENTER, 200);
    this->c_EList.InsertColumn(7, "流量", LVCFMT_CENTER, 100);
    this->c_EList.InsertColumn(8, "时间", LVCFMT_CENTER, 200);
    this->c_EList.InsertColumn(9, "流量", LVCFMT_CENTER, 100);
    setExcel(m_System);
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
}
void CDlgExcel::setExcel(SYSTEMTIME m_Systime)
{
    CMysqlConnection * m_MysqlConnection = CMysqlConnection::GetMysqlConn();
    CObArray * m_ExcelData = new CObArray();
    double flowsum = 0.0;//总流量数
    double max = 0.0;//最大值
    double min = 0.0;//最小值
    int nItem = 0;//行数
    int sum = 0;//总记录数
    CTeeData * m_tmpTeedata;//一个数据元
    CString temp;
    m_ExcelData = m_MysqlConnection->getData(m_Systime);
    if (m_ExcelData != NULL) {
        this->c_EList.DeleteAllItems();
        for (int i = 0; i < (m_ExcelData->GetSize() + 4) / 5; i++) {
            //添加一行
            this->c_EList.InsertItem(nItem, "");
            for (int j = 0; j < 10; j++) {
                //添加一行中的一个元组数据
                m_tmpTeedata = (CTeeData *)m_ExcelData->GetAt(sum);
                flowsum += m_tmpTeedata->m_Viewvalue;
                max = max >= m_tmpTeedata->m_Viewvalue ? max : m_tmpTeedata->m_Viewvalue;
                min = min <= m_tmpTeedata->m_Viewvalue ? min : m_tmpTeedata->m_Viewvalue;
                temp.Format("%.3f", m_tmpTeedata->m_Viewvalue);
                this->c_EList.SetItemText(nItem, j, m_tmpTeedata->m_ViewData);//时间
                this->c_EList.SetItemText(nItem, j + 1, temp);//值
                sum++;
                j++;
                if (sum >= m_ExcelData->GetSize()) {
                    break;
                }
            }
            nItem++;
        }
        //添加分析数据
        this->c_EList.InsertItem(nItem, "");
        this->c_EList.SetItemText(nItem, 0, "最大值");
        temp.Format("%.3f", max);
        this->c_EList.SetItemText(nItem, 1, temp);
        this->c_EList.SetItemText(nItem, 2, "最小值");
        temp.Format("%.3f", min);
        this->c_EList.SetItemText(nItem, 3, temp);
        this->c_EList.SetItemText(nItem, 4, "平均值");
        temp.Format("%.6f", flowsum / m_ExcelData->GetSize());
        this->c_EList.SetItemText(nItem, 5, temp);
        this->c_EList.SetItemText(nItem, 6, "总流量");
        temp.Format("%.3f", flowsum);
        this->c_EList.SetItemText(nItem, 7, temp);
        this->c_EList.SetItemText(nItem, 8, "总数据");
        temp.Format("%d", m_ExcelData->GetSize());
        this->c_EList.SetItemText(nItem, 9, temp);
        this->m_ETitle.Format("%2d-%d-%d的流量统计表", m_Systime.wYear, m_Systime.wMonth, m_Systime.wDay);
        GetDlgItem(IDC_E_TITLE)->SetWindowText(this->m_ETitle);
    }
}
HBRUSH CDlgExcel::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    // TODO:  在此更改 DC 的任何特性
    CFont m_SetFont;
    switch (pWnd->GetDlgCtrlID())
    {
    case IDC_E_TITLE:
        m_SetFont.CreatePointFont(400,"楷体");
        pDC->SelectObject(&m_SetFont);
        pDC->SetTextColor(RGB(0, 255, 0));
        pDC->SetBkColor(RGB(103, 103, 103));
        break;
    default:
        break;
    }
    // TODO:  如果默认的不是所需画笔,则返回另一个画笔
    return hbr;
}

5.7.2流量预测

添加流量预测保存按钮的事件处理方法OnBnClickedOk:

CString m_result;CString m_reason;char temp[255];CString t;CString strPath;CFile file;
GetDlgItemText(IDC_F_RESULT, m_result);GetDlgItemText(IDC_F_REASON, m_reason);
SHGetSpecialFolderPath(0, temp, CSIDL_DESKTOPDIRECTORY, 0);t.Format(temp);
t += "\\流量预测分析";m_result += "\r\n";m_result += m_reason;
CFileDialog saveDlg(FALSE,"txt",t,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, "文本文档(*.txt)", this);
if (saveDlg.DoModal() == IDOK) {strPath = saveDlg.GetPathName();
file.Open(strPath, CFile::modeCreate | CFile::modeWrite);//str为CString类型 
file.Write(m_result.GetBuffer(),m_result.GetLength()*sizeof(TCHAR));
file.Flush();file.Close();AfxMessageBox("保存成功");}
else {AfxMessageBox("没有传入路径");}

DlgForecast.h:

#pragma once
// CDlgForecast 对话框
class CDlgForecast : public CDialogEx
{
    DECLARE_DYNAMIC(CDlgForecast)
public:
    CDlgForecast(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CDlgForecast();
// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_FORECAST };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedOk();
};

DlgForecast.cpp:

#include "stdafx.h"
#include "管道流量采集.h"
#include "DlgForecast.h"
#include "afxdialogex.h"
// CDlgForecast 对话框
IMPLEMENT_DYNAMIC(CDlgForecast, CDialogEx)
CDlgForecast::CDlgForecast(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_FORECAST, pParent)
{
}
CDlgForecast::~CDlgForecast()
{
}
void CDlgForecast::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CDlgForecast, CDialogEx)
    ON_BN_CLICKED(IDOK, &CDlgForecast::OnBnClickedOk)
END_MESSAGE_MAP()
void CDlgForecast::OnBnClickedOk()
{
    // TODO: 在此添加控件通知处理程序代码
    CString m_result;
    CString m_reason;
    char temp[255];
    CString t;
    CString strPath;
    CFile file;
    GetDlgItemText(IDC_F_RESULT, m_result);
    GetDlgItemText(IDC_F_REASON, m_reason);
    SHGetSpecialFolderPath(0, temp, CSIDL_DESKTOPDIRECTORY, 0);
    t.Format(temp);
    t += "\\流量预测分析";
    m_result += "\r\n";
    m_result += m_reason;
    CFileDialog saveDlg(FALSE, "txt", t, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "文本文档(*.txt)", this);
    if (saveDlg.DoModal() == IDOK) {
        strPath = saveDlg.GetPathName();
        file.Open(strPath, CFile::modeCreate | CFile::modeWrite);
        file.Write(m_result.GetBuffer(), m_result.GetLength() * sizeof(TCHAR));//str为CString类型 
        file.Flush();
        file.Close();
        AfxMessageBox("保存成功");
    }
    else {
        AfxMessageBox("没有传入路径");
    }
    //CDialogEx::OnOK();
}

5.7.3历史流量折线图

重写OnCtlColor方法:

CFont m_SetFont;switch (pWnd->GetDlgCtrlID()){case IDC_H_TITLE:
m_SetFont.CreatePointFont(400, "楷体");pDC->SelectObject(&m_SetFont);
pDC->SetTextColor(RGB(0, 255, 0));pDC->SetBkColor(RGB(103, 103, 103));break;
default:break;}

重写OnInitDialog,使用当前日期作为查询条件并显示:

SYSTEMTIME m_Systime;GetLocalTime(&m_Systime);setTeeChart(m_Systime);

增加数据显示到折线图插件的方法:

void CDlgHistory::setTeeChart(SYSTEMTIME m_Systime)
{CObArray * m_Viewdatas = new CObArray();
CMysqlConnection * m_MysqlConnection = CMysqlConnection::GetMysqlConn();
CTeeData * m_OneData;CSeries viewdata = this->c_HTChart.Series(0);CString temp;
temp.Format("%2d-%d-%d的流量数据折线图", m_Systime.wYear, m_Systime.wMonth, m_Systime.wDay);
GetDlgItem(IDC_H_TITLE)->SetWindowText(temp);
m_Viewdatas =  m_MysqlConnection->getData(m_Systime);viewdata.Clear();
if (NULL != m_Viewdatas) {for (int i = 0; i < m_Viewdatas->GetSize(); i++) {
m_OneData = (CTeeData *)m_Viewdatas->GetAt(i);
viewdata.Add(m_OneData->m_Viewvalue,m_OneData->m_ViewData, NULL);}}}

添加确定按钮的事件处理方法OnBnClickedOk,当用户重写选择日期后确定,此方法就会被调用。

CTime m_tmpTime;SYSTEMTIME m_Datetime;this->c_HDate.GetTime(m_tmpTime);
m_Datetime.wYear = m_tmpTime.GetYear();m_Datetime.wMonth = m_tmpTime.GetMonth();
m_Datetime.wDay = m_tmpTime.GetDay();m_Datetime.wHour = m_tmpTime.GetHour();
m_Datetime.wMinute = m_tmpTime.GetMinute();
m_Datetime.wSecond = m_tmpTime.GetSecond();setTeeChart(m_Datetime);

DlgHistory.h:

#pragma once
#include "afxdtctl.h"
#include "tchart1.h"
#include "TeeData.h"
#include "CSeries.h"
// CDlgHistory 对话框
class CDlgHistory : public CDialogEx
{
    DECLARE_DYNAMIC(CDlgHistory)
public:
    CDlgHistory(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CDlgHistory();
// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_HISTORY };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    DECLARE_MESSAGE_MAP()
public:
    CDateTimeCtrl c_HDate;
    CTchart1 c_HTChart;
    virtual BOOL OnInitDialog();
    afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
    void setTeeChart(SYSTEMTIME m_Systime);
    afx_msg void OnBnClickedOk();
};

DlgHistory.cpp:

// DlgHistory.cpp : 实现文件
#include "stdafx.h"
#include "管道流量采集.h"
#include "DlgHistory.h"
#include "afxdialogex.h"
#include "MysqlConnection.h"
// CDlgHistory 对话框
IMPLEMENT_DYNAMIC(CDlgHistory, CDialogEx)
CDlgHistory::CDlgHistory(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_HISTORY, pParent)
{
}
CDlgHistory::~CDlgHistory()
{
}
void CDlgHistory::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_H_DATE, c_HDate);
    DDX_Control(pDX, IDC_H_TCHART, c_HTChart);
}
BEGIN_MESSAGE_MAP(CDlgHistory, CDialogEx)
    ON_WM_CTLCOLOR()
    ON_BN_CLICKED(IDOK, &CDlgHistory::OnBnClickedOk)
END_MESSAGE_MAP()
// CDlgHistory 消息处理程序
BOOL CDlgHistory::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // TODO:  在此添加额外的初始化
    SYSTEMTIME m_Systime;
    GetLocalTime(&m_Systime);
    setTeeChart(m_Systime);
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
}
HBRUSH CDlgHistory::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    // TODO:  在此更改 DC 的任何特性
    CFont m_SetFont;
    switch (pWnd->GetDlgCtrlID())
    {
    case IDC_H_TITLE:
        m_SetFont.CreatePointFont(400, "楷体");
        pDC->SelectObject(&m_SetFont);
        pDC->SetTextColor(RGB(0, 255, 0));
        pDC->SetBkColor(RGB(103, 103, 103));
        break;
    default:
        break;
    }
    // TODO:  如果默认的不是所需画笔,则返回另一个画笔
    return hbr;
}
void CDlgHistory::setTeeChart(SYSTEMTIME m_Systime)
{
    CObArray * m_Viewdatas = new CObArray();
    CMysqlConnection * m_MysqlConnection = CMysqlConnection::GetMysqlConn();
    CTeeData * m_OneData;
    CSeries viewdata = this->c_HTChart.Series(0);
    CString temp;
    temp.Format("%2d-%d-%d的流量数据折线图",m_Systime.wYear,m_Systime.wMonth,m_Systime.wDay);
    GetDlgItem(IDC_H_TITLE)->SetWindowText(temp);
    m_Viewdatas =  m_MysqlConnection->getData(m_Systime);
    viewdata.Clear();
    if (NULL != m_Viewdatas) {
        for (int i = 0; i < m_Viewdatas->GetSize(); i++) {
            m_OneData = (CTeeData *)m_Viewdatas->GetAt(i);
            viewdata.Add(m_OneData->m_Viewvalue, m_OneData->m_ViewData, NULL);
        }
    }   
}
void CDlgHistory::OnBnClickedOk()
{
    // TODO: 在此添加控件通知处理程序代码
    CTime m_tmpTime;
    SYSTEMTIME m_Datetime;
    this->c_HDate.GetTime(m_tmpTime);
    m_Datetime.wYear = m_tmpTime.GetYear();
    m_Datetime.wMonth = m_tmpTime.GetMonth();
    m_Datetime.wDay = m_tmpTime.GetDay();
    m_Datetime.wHour = m_tmpTime.GetHour();
    m_Datetime.wMinute = m_tmpTime.GetMinute();
    m_Datetime.wSecond = m_tmpTime.GetSecond();
    setTeeChart(m_Datetime);
}

5.7.4原理讲解

重写OnInitDialog方法:

this->m_BmpId.Add(IDB_BITMAP1);this->m_BmpId.Add(IDB_BITMAP2);
this->m_BmpId.Add(IDB_BITMAP3);this->m_BmpId.Add(IDB_BITMAP4);
this->m_BmpId.Add(IDB_BITMAP5);this->m_BmpId.Add(IDB_BITMAP6);

重写OnPaint方法:

//如果是第一张图片,设置上一页按钮不可用
if (0 == this->m_BmpFlag) {m_InterpretLast.EnableWindow(0);}
else if (0 != this->m_BmpFlag && !m_InterpretLast.IsWindowEnabled()) {
m_InterpretLast.EnableWindow(1);}
//如果是最后一张图片,设置下一页按钮不可用
if (this->m_BmpFlag == this->m_BmpId.GetSize() - 1) {m_InterpretNext.EnableWindow(0);}
else if (this->m_BmpFlag != this->m_BmpId.GetSize() - 1 &&
!m_InterpretNext.IsWindowEnabled()) {m_InterpretNext.EnableWindow(1);}
ShowPic(this->m_BmpId.GetAt(this->m_BmpFlag));

增加图片自适应方法ShowPic:

CBitmap bitmap; CStatic * p;BITMAP bmpInfo;
CDC dcMemory;   CDC * pDC;  CBitmap * pOldBitmap;CRect rect;int nX, nY;
//加载指定位图资源 Bmp图片ID  
bitmap.LoadBitmap(m_PicID);//获取对话框上的句柄 图片控件ID  
p = (CStatic *)GetDlgItem(IDC_I_PIC);//设置静态控件窗口风格为位图居中显示   
p->ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);//将图片设置到Picture控件上  
p->SetBitmap(bitmap);bitmap.GetBitmap(&bmpInfo);
pDC = GetDlgItem(IDC_I_PIC)->GetDC();dcMemory.CreateCompatibleDC(pDC);
pOldBitmap = dcMemory.SelectObject(&bitmap);p->GetClientRect(&rect);
nX = rect.left + (rect.Width() - bmpInfo.bmWidth) / 2;
nY = rect.top + (rect.Height() - bmpInfo.bmHeight) / 2;
pDC->SetStretchBltMode(COLORONCOLOR);
pDC->StretchBlt(0, 0, rect.Width(), rect.Height(),&dcMemory, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, SRCCOPY);dcMemory.SelectObject(pOldBitmap);ReleaseDC(pDC);

增加上一页按钮的事件处理方法OnBnClickedILast:

this->m_BmpFlag--;if (this->m_BmpFlag < 0) {this->m_BmpFlag = 0;}
CDlgInterpret::OnPaint();

增加下一页按钮的事件处理方法OnBnClickedINext

this->m_BmpFlag++;  if (this->m_BmpFlag >= this->m_BmpId.GetSize()) {
this->m_BmpFlag = this->m_BmpId.GetSize() - 1;} CDlgInterpret::OnPaint();

DlgInterpret.h:

#pragma once
#include "afxwin.h"
// CDlgInterpret 对话框
class CDlgInterpret : public CDialogEx
{
    DECLARE_DYNAMIC(CDlgInterpret)
public:
    CDlgInterpret(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CDlgInterpret();
// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_INTERPRET };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    DECLARE_MESSAGE_MAP()
public:
    void ShowPic(int m_PicID);
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    CButton m_InterpretLast;
    CButton m_InterpretNext;
    CArray<int, int> m_BmpId;
    int m_BmpFlag;
    afx_msg void OnBnClickedINext();
    afx_msg void OnBnClickedILast();
};

DlgInterpret.cpp:

// DlgInterpret.cpp : 实现文件
#include "stdafx.h"
#include "管道流量采集.h"
#include "DlgInterpret.h"
#include "afxdialogex.h"
// CDlgInterpret 对话框
IMPLEMENT_DYNAMIC(CDlgInterpret, CDialogEx)
CDlgInterpret::CDlgInterpret(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_INTERPRET, pParent)
    , m_BmpFlag(0)
{
}
CDlgInterpret::~CDlgInterpret()
{
}
void CDlgInterpret::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_I_LAST, m_InterpretLast);
    DDX_Control(pDX, IDC_I_NEXT, m_InterpretNext);
}
BEGIN_MESSAGE_MAP(CDlgInterpret, CDialogEx)
    ON_WM_PAINT()
    ON_BN_CLICKED(IDC_I_NEXT, &CDlgInterpret::OnBnClickedINext)
    ON_BN_CLICKED(IDC_I_LAST, &CDlgInterpret::OnBnClickedILast)
END_MESSAGE_MAP()
// CDlgInterpret 消息处理程序
void CDlgInterpret::ShowPic(int m_PicID)
{
    CBitmap bitmap;
    CStatic * p;
    BITMAP bmpInfo;
    CDC dcMemory;
    CDC * pDC;
    CBitmap * pOldBitmap;
    CRect rect;
    int nX, nY;
    //加载指定位图资源 Bmp图片ID  
    bitmap.LoadBitmap(m_PicID);
    //获取对话框上的句柄 图片控件ID  
    p = (CStatic *)GetDlgItem(IDC_I_PIC);
    //设置静态控件窗口风格为位图居中显示   
    p->ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);
    //将图片设置到Picture控件上  
    p->SetBitmap(bitmap);
    bitmap.GetBitmap(&bmpInfo);
    pDC = GetDlgItem(IDC_I_PIC)->GetDC();
    dcMemory.CreateCompatibleDC(pDC);
    pOldBitmap = dcMemory.SelectObject(&bitmap);
    p->GetClientRect(&rect);
    nX = rect.left + (rect.Width() - bmpInfo.bmWidth) / 2;
    nY = rect.top + (rect.Height() - bmpInfo.bmHeight) / 2;
    pDC->SetStretchBltMode(COLORONCOLOR);
    pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), 
        &dcMemory, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, SRCCOPY);
    dcMemory.SelectObject(pOldBitmap);
    ReleaseDC(pDC);
}
BOOL CDlgInterpret::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // TODO:  在此添加额外的初始化
    this->m_BmpId.Add(IDB_BITMAP1);
    this->m_BmpId.Add(IDB_BITMAP2);
    this->m_BmpId.Add(IDB_BITMAP3);
    this->m_BmpId.Add(IDB_BITMAP4);
    this->m_BmpId.Add(IDB_BITMAP5);
    this->m_BmpId.Add(IDB_BITMAP6);
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
}
void CDlgInterpret::OnPaint()
{
    CPaintDC dc(this); // device context for painting
                       // TODO: 在此处添加消息处理程序代码
                       // 不为绘图消息调用 CDialogEx::OnPaint()
    //如果是第一张图片,设置上一页按钮不可用
    if (0 == this->m_BmpFlag) {
        m_InterpretLast.EnableWindow(0);
    }
    else if (0 != this->m_BmpFlag && !m_InterpretLast.IsWindowEnabled()) {
        m_InterpretLast.EnableWindow(1);
    }
    //如果是最后一张图片,设置下一页按钮不可用
    if (this->m_BmpFlag == this->m_BmpId.GetSize() - 1) {
        m_InterpretNext.EnableWindow(0);
    }
    else if (this->m_BmpFlag != this->m_BmpId.GetSize() - 1 &&
        !m_InterpretNext.IsWindowEnabled()) {
        m_InterpretNext.EnableWindow(1);
    }
    ShowPic(this->m_BmpId.GetAt(this->m_BmpFlag));
}
void CDlgInterpret::OnBnClickedINext()
{
    // TODO: 在此添加控件通知处理程序代码
    this->m_BmpFlag++;
    if (this->m_BmpFlag >= this->m_BmpId.GetSize()) {
        this->m_BmpFlag = this->m_BmpId.GetSize() - 1;
    }
    CDlgInterpret::OnPaint();
}
void CDlgInterpret::OnBnClickedILast()
{
    // TODO: 在此添加控件通知处理程序代码
    this->m_BmpFlag--;
    if (this->m_BmpFlag < 0) {
        this->m_BmpFlag = 0;
    }
    CDlgInterpret::OnPaint();
}

5.7.5查看参数

重写OnInitDialog方法:

CString temp;CConfigdata * m_Configdata = new CConfigdata();
m_Configdata->getConfigdata();
CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
SetDlgItemText(IDC_L_IPADD,m_DataConfig->getIpAddress());
SetDlgItemText(IDC_L_COM, m_DataConfig->getComnum());
SetDlgItemText(IDC_L_USERNAME, m_DataConfig->getUsername());
SetDlgItemText(IDC_L_PASSWORD, m_DataConfig->getPassword());
SetDlgItemText(IDC_L_DATABASE, m_DataConfig->getDatabase());temp = "COM";
temp += m_DataConfig->getComm();SetDlgItemText(IDC_L_COMM, temp);
SetDlgItemText(IDC_L_BOTELV, m_DataConfig->getBotelv());
SetDlgItemText(IDC_L_JIAOYAN, m_DataConfig->getJiaoyan());
SetDlgItemText(IDC_L_SHUJU, m_DataConfig->getShuju());
SetDlgItemText(IDC_L_TINGZHI, m_DataConfig->getTingzhi());
temp = m_DataConfig->getTime();long tt = atol(temp);tt /= 1000;
if (tt <= 60) {temp.Format("%d", tt);temp += "秒";}if (tt > 60 && tt <= 3600) {
temp.Format("%d", tt / 60);temp += "分钟";}if (tt > 3600) {   temp.Format("%d", (tt / 60) / 60);
temp += "小时";}SetDlgItemText(IDC_L_TIME, temp);UpdateData(FALSE);

DlgLookdata.h:

#pragma once
#include "DataConfig.h"
#include "Configdata.h"
// CDlgLookdata 对话框
class CDlgLookdata : public CDialogEx
{
    DECLARE_DYNAMIC(CDlgLookdata)
public:
    CDlgLookdata(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CDlgLookdata();
// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_LOOKDATA };
#endif
protected:
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
};

DlgLookdata.cpp:

// DlgLookdata.cpp : 实现文件
#include "stdafx.h"
#include "管道流量采集.h"
#include "DlgLookdata.h"
#include "afxdialogex.h"
// CDlgLookdata 对话框
IMPLEMENT_DYNAMIC(CDlgLookdata, CDialogEx)
CDlgLookdata::CDlgLookdata(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_LOOKDATA, pParent)
{
}
CDlgLookdata::~CDlgLookdata()
{
}
BEGIN_MESSAGE_MAP(CDlgLookdata, CDialogEx)
END_MESSAGE_MAP()
// CDlgLookdata 消息处理程序
BOOL CDlgLookdata::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // TODO:  在此添加额外的初始化
    CString temp;
    CConfigdata * m_Configdata = new CConfigdata();
    m_Configdata->getConfigdata();
    CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
    SetDlgItemText(IDC_L_IPADD,m_DataConfig->getIpAddress());
    SetDlgItemText(IDC_L_COM, m_DataConfig->getComnum());
    SetDlgItemText(IDC_L_USERNAME, m_DataConfig->getUsername());
    SetDlgItemText(IDC_L_PASSWORD, m_DataConfig->getPassword());
    SetDlgItemText(IDC_L_DATABASE, m_DataConfig->getDatabase());
    temp = "COM";
    temp += m_DataConfig->getComm();
    SetDlgItemText(IDC_L_COMM, temp);
    SetDlgItemText(IDC_L_BOTELV, m_DataConfig->getBotelv());
    SetDlgItemText(IDC_L_JIAOYAN, m_DataConfig->getJiaoyan());
    SetDlgItemText(IDC_L_SHUJU, m_DataConfig->getShuju());
    SetDlgItemText(IDC_L_TINGZHI, m_DataConfig->getTingzhi());
    temp = m_DataConfig->getTime();
    long tt = atol(temp);
    tt /= 1000;
    if (tt <= 60) {
        temp.Format("%d", tt);
        temp += "秒";
    }
    if (tt > 60 && tt <= 3600) {
        temp.Format("%d", tt / 60);
        temp += "分钟";
    }
    if (tt > 3600) {
        temp.Format("%d", (tt / 60) / 60);
        temp += "小时";
    }
    SetDlgItemText(IDC_L_TIME, temp);
    UpdateData(FALSE);
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
}

5.7.6设置参数

重写OnInitDialog方法,设置参数的默认参数:

//初始化下拉选择框
SetDlgItemText(IDC_S_IPADD, "127.0.0.1");SetDlgItemText(IDC_S_COM, "3305");
SetDlgItemText(IDC_S_USERNAME, "root"); SetDlgItemText(IDC_S_PASSWORD, "root");
SetDlgItemText(IDC_S_DATABASE, "flowdata"); this->c_Comm.ResetContent();
this->c_Comm.AddString("COM1");
…….
this->c_Comm.AddString("COM8");this->c_Comm.SetCurSel(2);//默认为COM3
this->c_Botelv.ResetContent();this->c_Botelv.AddString("300");
……
this->c_Botelv.AddString("38400");this->c_Botelv.SetCurSel(2);//默认1200
this->c_Jiaoyan.ResetContent();this->c_Jiaoyan.AddString("无");
this->c_Jiaoyan.AddString("奇校验");this->c_Jiaoyan.AddString("偶校验");
this->c_Jiaoyan.SetCurSel(0);//默认无校验this->c_Shuju.ResetContent();
this->c_Shuju.AddString("8");this->c_Shuju.AddString("7");this->c_Shuju.AddString("6");
this->c_Shuju.SetCurSel(0);//默认8个数据位this->c_Tingzhi.ResetContent();
this->c_Tingzhi.AddString("1");this->c_Tingzhi.AddString("2");
this->c_Tingzhi.SetCurSel(0);//默认1个停止位SetDlgItemText(IDC_S_TIME, "30");
CButton * radioS = (CButton *)GetDlgItem(IDC_S_SECEND);radioS->SetCheck(1);

重写PreTranslateMessage方法,此方法可以让用户通过Tab键来选择下一个:

if ((pMsg->message == WM_KEYDOWN) && (VK_TAB == (int)pMsg->wParam)) {
CWnd *mwnd = GetFocus();if (NULL != mwnd) {
if (mwnd == GetDlgItem(IDC_S_IPADD)) {GetDlgItem(IDC_S_COM)->SetFocus();
return TRUE;}else if (mwnd == GetDlgItem(IDC_S_COM)) {
GetDlgItem(IDC_S_USERNAME)->SetFocus();return TRUE;}
…….}}

增加确定按钮的事件处理方法OnBnClickedOk:

CString temp;CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
GetDlgItemText(IDC_S_COM, temp);    m_DataConfig->setComnum(temp);
GetDlgItemText(IDC_S_IPADD, temp);m_DataConfig->setIpAddress(temp);
GetDlgItemText(IDC_S_USERNAME, this->m_SetUsername);
m_DataConfig->setUsername(this->m_SetUsername);
GetDlgItemText(IDC_S_PASSWORD, this->m_SetPassword);
m_DataConfig->setPassword(this->m_SetPassword);
GetDlgItemText(IDC_S_DATABASE, this->m_SetDatabase);
m_DataConfig->setDatabase(this->m_SetDatabase);
//没有COM0
switch (this->c_Comm.GetCurSel()) {case 0:temp = "1";break;case 1:temp = "2";break;
case 2:temp = "3";break;case 3:temp = "4";break;case 4:temp = "5";break;
case 5:temp = "6";break;case 6:temp = "7";break;case 7:temp = "8";break;    default:break;}
m_DataConfig->setComm(temp);GetDlgItemText(IDC_S_BOTELV, temp);
m_DataConfig->setBotelv(temp);switch (this->c_Jiaoyan.GetCurSel())
{case 0:temp = "n";break;   case 1:temp = "1";break;case 2:temp = "2";break;
default:temp = "n";break;}m_DataConfig->setJiaoyan(temp);
GetDlgItemText(IDC_S_SHUJU, temp);m_DataConfig->setShuju(temp);
GetDlgItemText(IDC_S_TING, temp);   m_DataConfig->setTingzhi(temp);
GetDlgItemText(IDC_S_TIME, temp);   long tt;tt = atol(temp);CButton * pTemp = NULL;
pTemp = (CButton * )GetDlgItem(IDC_S_HOUR);if (pTemp->GetCheck()) {
tt = tt * 60 * 60 * 1000;}pTemp = (CButton *)GetDlgItem(IDC_S_MINTE);
if (pTemp->GetCheck()) {tt = tt * 60 * 1000;}
pTemp = (CButton *)GetDlgItem(IDC_S_SECEND);if (pTemp->GetCheck()) {
tt = tt * 1000;}temp.Format("%d", tt);m_DataConfig->setTime(temp);
CConfigdata m_Configdata;m_Configdata.setConfigdata();
SetTimer(1, atoi(m_DataConfig->getTime()), NULL);CDialogEx::OnOK();

DlgSetdata.h:

#pragma once
//头文件
#include "afxwin.h"
#include "Configdata.h"
// CDlgSetdata 对话框
class CDlgSetdata : public CDialogEx
{
    DECLARE_DYNAMIC(CDlgSetdata)
    CDlgSetdata(CWnd* pParent = NULL);   // 标准构造函数
public:
    virtual ~CDlgSetdata();
// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_SETDATA };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    DECLARE_MESSAGE_MAP()
public:
    // IP地址
    DWORD m_SetIpadd;
    // 端口号
    CString m_SetCom;
    // 用户名
    CString m_SetUsername;
    // 密码
    CString m_SetPassword;
    // 数据库名
    CString m_SetDatabase;
    afx_msg void OnBnClickedOk();
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    virtual BOOL OnInitDialog();
    CComboBox c_Comm;
    CComboBox c_Botelv;
    CComboBox c_Jiaoyan;
    CComboBox c_Shuju;
    CComboBox c_Tingzhi;
    CEdit c_Time;
    CButton c_ButtonH;
};

DlgSetdata.cpp:

// DlgSetdata.cpp : 实现文件
#include "stdafx.h"
#include "管道流量采集.h"
#include "DlgSetdata.h"
#include "afxdialogex.h"
// CDlgSetdata 对话框
IMPLEMENT_DYNAMIC(CDlgSetdata, CDialogEx)
CDlgSetdata::CDlgSetdata(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_SETDATA, pParent)
    , m_SetIpadd(0)
    , m_SetUsername(_T(""))
    , m_SetPassword(_T(""))
    , m_SetDatabase(_T(""))
{
}
CDlgSetdata::~CDlgSetdata()
{
}
void CDlgSetdata::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_IPAddress(pDX, IDC_S_IPADD, m_SetIpadd);
    DDX_Text(pDX, IDC_S_COM, m_SetCom);
    DDX_Text(pDX, IDC_S_USERNAME, m_SetUsername);
    DDX_Text(pDX, IDC_S_PASSWORD, m_SetPassword);
    DDX_Text(pDX, IDC_S_DATABASE, m_SetDatabase);
    DDX_Control(pDX, IDC_S_COMM, c_Comm);
    DDX_Control(pDX, IDC_S_BOTELV, c_Botelv);
    DDX_Control(pDX, IDC_S_JIAO, c_Jiaoyan);
    DDX_Control(pDX, IDC_S_SHUJU, c_Shuju);
    DDX_Control(pDX, IDC_S_TING, c_Tingzhi);
    DDX_Control(pDX, IDC_S_TIME, c_Time);
    DDX_Control(pDX, IDCANCEL, c_ButtonH);
}
BEGIN_MESSAGE_MAP(CDlgSetdata, CDialogEx)
    ON_BN_CLICKED(IDOK, &CDlgSetdata::OnBnClickedOk)
END_MESSAGE_MAP()
// CDlgSetdata 消息处理程序
void CDlgSetdata::OnBnClickedOk()
{
    // TODO: 在此添加控件通知处理程序代码
    CString temp;
    CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
    GetDlgItemText(IDC_S_COM, temp);
    m_DataConfig->setComnum(temp);
    GetDlgItemText(IDC_S_IPADD, temp);
    m_DataConfig->setIpAddress(temp);
    GetDlgItemText(IDC_S_USERNAME, this->m_SetUsername);
    m_DataConfig->setUsername(this->m_SetUsername);
    GetDlgItemText(IDC_S_PASSWORD, this->m_SetPassword);
    m_DataConfig->setPassword(this->m_SetPassword);
    GetDlgItemText(IDC_S_DATABASE, this->m_SetDatabase);
    m_DataConfig->setDatabase(this->m_SetDatabase);
    switch (this->c_Comm.GetCurSel()) {
    case 0:
        temp = "1";
        break;
    case 1:
        temp = "2";
        break;
    case 2:
        temp = "3";
        break;
    case 3:
        temp = "4";
        break;
    case 4:
        temp = "5";
        break;
    case 5:
        temp = "6";
        break;
    case 6:
        temp = "7";
        break;
    case 7:
        temp = "8";
        break;
    default:
        break;
    }
    m_DataConfig->setComm(temp);
    GetDlgItemText(IDC_S_BOTELV, temp);
    m_DataConfig->setBotelv(temp);
    switch (this->c_Jiaoyan.GetCurSel())
    {
    case 0:
        temp = "n";
        break;
    case 1:
        temp = "1";
        break;
    case 2:
        temp = "2";
        break;
    default:
        temp = "n";
        break;
    }
    m_DataConfig->setJiaoyan(temp);
    GetDlgItemText(IDC_S_SHUJU, temp);
    m_DataConfig->setShuju(temp);
    GetDlgItemText(IDC_S_TING, temp);
    m_DataConfig->setTingzhi(temp);
    GetDlgItemText(IDC_S_TIME, temp);
    long tt;
    tt = atol(temp);
    CButton * pTemp = NULL;
    pTemp = (CButton * )GetDlgItem(IDC_S_HOUR);
    if (pTemp->GetCheck()) {
        tt = tt * 60 * 60 * 1000;
    }
    pTemp = (CButton *)GetDlgItem(IDC_S_MINTE);
    if (pTemp->GetCheck()) {
        tt = tt * 60 * 1000;
    }
    pTemp = (CButton *)GetDlgItem(IDC_S_SECEND);
    if (pTemp->GetCheck()) {
        tt = tt * 1000;
    }
    temp.Format("%d", tt);
    m_DataConfig->setTime(temp);
    CConfigdata m_Configdata;
    m_Configdata.setConfigdata();
    SetTimer(1, atoi(m_DataConfig->getTime()), NULL);
    //AfxMessageBox("定时器开始");
    CDialogEx::OnOK();
}
BOOL CDlgSetdata::PreTranslateMessage(MSG* pMsg)
{
    // TODO: 在此添加专用代码和/或调用基类
    if ((pMsg->message == WM_KEYDOWN) && (VK_TAB == (int)pMsg->wParam)) {
        CWnd *mwnd = GetFocus();
        if (NULL != mwnd) {
            if (mwnd == GetDlgItem(IDC_S_IPADD)) {
                GetDlgItem(IDC_S_COM)->SetFocus();
                return TRUE;
            }
            else if (mwnd == GetDlgItem(IDC_S_COM)) {
                GetDlgItem(IDC_S_USERNAME)->SetFocus();
                return TRUE;
            }
            else if (mwnd == GetDlgItem(IDC_S_USERNAME)) {
                GetDlgItem(IDC_S_PASSWORD)->SetFocus();
                return TRUE;
            }
            else if (mwnd == GetDlgItem(IDC_S_PASSWORD)) {
                GetDlgItem(IDC_S_DATABASE)->SetFocus();
                return TRUE;
            }
            else if (mwnd == GetDlgItem(IDC_S_DATABASE)) {
                GetDlgItem(IDOK)->SetFocus();
                return TRUE;
            }
            else if (mwnd == GetDlgItem(IDOK)) {
                GetDlgItem(IDCANCEL)->SetFocus();
                return TRUE;
            }
            else if (mwnd == GetDlgItem(IDCANCEL)) {
                GetDlgItem(IDC_S_IPADD)->SetFocus();
                return TRUE;
            }
        }
    }
    return CDialogEx::PreTranslateMessage(pMsg);
}
BOOL CDlgSetdata::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // TODO:  在此添加额外的初始化
    //初始化下拉选择框
    SetDlgItemText(IDC_S_IPADD, "127.0.0.1");
    SetDlgItemText(IDC_S_COM, "3305");
    SetDlgItemText(IDC_S_USERNAME, "root");
    SetDlgItemText(IDC_S_PASSWORD, "root");
    SetDlgItemText(IDC_S_DATABASE, "flowdata");
    this->c_Comm.ResetContent();
    this->c_Comm.AddString("COM1");
    this->c_Comm.AddString("COM2");
    this->c_Comm.AddString("COM3");
    this->c_Comm.AddString("COM4");
    this->c_Comm.AddString("COM5");
    this->c_Comm.AddString("COM6");
    this->c_Comm.AddString("COM7");
    this->c_Comm.AddString("COM8");
    this->c_Comm.SetCurSel(2);//默认为COM3
    this->c_Botelv.ResetContent();
    this->c_Botelv.AddString("300");
    this->c_Botelv.AddString("600");
    this->c_Botelv.AddString("1200");
    this->c_Botelv.AddString("2400");
    this->c_Botelv.AddString("3600");
    this->c_Botelv.AddString("4800");
    this->c_Botelv.AddString("9600");
    this->c_Botelv.AddString("19200");
    this->c_Botelv.AddString("38400");
    this->c_Botelv.SetCurSel(2);//默认1200
    this->c_Jiaoyan.ResetContent();
    this->c_Jiaoyan.AddString("无");
    this->c_Jiaoyan.AddString("奇校验");
    this->c_Jiaoyan.AddString("偶校验");
    this->c_Jiaoyan.SetCurSel(0);//默认无校验
    this->c_Shuju.ResetContent();
    this->c_Shuju.AddString("8");
    this->c_Shuju.AddString("7");
    this->c_Shuju.AddString("6");
    this->c_Shuju.SetCurSel(0);//默认8个数据位
    this->c_Tingzhi.ResetContent();
    this->c_Tingzhi.AddString("1");
    this->c_Tingzhi.AddString("2");
    this->c_Tingzhi.SetCurSel(0);//默认1个停止位
    SetDlgItemText(IDC_S_TIME, "30");
    CButton * radioS = (CButton *)GetDlgItem(IDC_S_SECEND);
    radioS->SetCheck(1);
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
}

5.7.7协议测试

添加CRC校验码的计算方法,头文件:

#include <stdint.h>
uint16_t CRC16( uint8_t * pucFrame, uint16_t usLen );

实现文件:

#include "stdafx.h"#include "crc.h"
static const uint8_t aucCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40};
static const uint8_t aucCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
    0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
    0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
    0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
    0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
    0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
    0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
    0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
    0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
    0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
    0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
    0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
    0x41, 0x81, 0x80, 0x40};
uint16_t CRC16( uint8_t * pucFrame, uint16_t usLen )
{uint8_t ucCRCHi = 0xFF;uint8_t ucCRCLo = 0xFF;int iIndex;
while( usLen-- ){iIndex = ucCRCLo ^ *( pucFrame++ );
ucCRCLo = ( uint8_t )( ucCRCHi ^ aucCRCHi[iIndex] );ucCRCHi = aucCRCLo[iIndex];}
return ( uint16_t )( ucCRCLo << 8 | ucCRCHi );}

自动生成协议帧的按钮事件处理方法OnBnClickedButton1:

CString result;CTime tmpTime;SYSTEMTIME systim;uint8_t len;uint8_t prop;
uint8_t station;uint8_t pass;uint8_t timey;uint8_t timem;   uint8_t timed;
uint8_t timeh;uint8_t timemin;uint8_t times;uint16_t crc_result;CString temp;
len = 0X0C;prop = 0X00;station = 0X02;pass = 0X02;result = "自动生成的协议帧\n";
result += "EB 90 0C 00 02 02 ";this->c_Data.GetTime(tmpTime);
timey = tmpTime.GetYear()%100;  temp.Format("%02d", timey);result += temp;result += " ";
timey = strtol(temp, NULL, 16);
…….
uint8_t crc_mess[] = { len, prop, station, pass, timey, timem, timed, timeh, timemin, times };
crc_result = CRC16((uint8_t*)crc_mess, 10);temp.Format("%04X", crc_result);
result += temp;result.Insert(result.GetLength() - 2, " ");
SetDlgItemText(IDC_T_RESULT, result);

确定按钮的事件处理方法OnBnClickedOk:

CString m_input;    CString m_result = "测试结果:";GetDlgItemText(IDC_T_INPUT, m_input);
if (m_input.GetLength() < 41) {m_result += "帧总长度不够";}
else {uint16_t address;CString addrtemp;addrtemp += m_input.GetAt(0);
addrtemp += m_input.GetAt(1);addrtemp += m_input.GetAt(3);
addrtemp += m_input.GetAt(4);address = strtol(addrtemp, NULL, 16);if (0xEB90 != address) {
m_result += "\n前四位地址码为EB 90";}else {m_result += "\n地址码测试通过";}
uint8_t len;CString lentemp;lentemp += m_input.GetAt(6);
lentemp += m_input.GetAt(7);len = strtol(lentemp, NULL, 16);
if ((m_input.GetLength() - 17) / 2 != len) {m_result += "\t长度计算错误,正确的是:";
lentemp.Format("%02X", (m_input.GetLength() - 17) / 2);m_result += lentemp; }
else {m_result += "\t长度测试通过";}
uint8_t prop;   CString proptemp;proptemp += m_input.GetAt(9);
proptemp += m_input.GetAt(10);prop = strtol(proptemp, NULL, 16);
if (0 != prop) {m_result += "\n属性码错误,正确的为:00";}
else {m_result += "\n属性码测试通过";}uint8_t station;CString stationtemp;
stationtemp += m_input.GetAt(12);stationtemp += m_input.GetAt(13);
station = strtol(stationtemp, NULL, 16);
if (0x02 != station) {m_result += "\t站号错误,正确为:02";}
else {m_result += "\t站号测试通过";}
uint8_t pass;   CString passtemp;passtemp += m_input.GetAt(15);
passtemp += m_input.GetAt(16);pass = strtol(passtemp, NULL, 16);
if (0x02 != pass) {m_result += "\n通道号错误,正确为:02";}
else {m_result += "\n通道号测试通过";}
uint8_t timey;uint8_t timem;uint8_t timed;uint8_t timeh;uint8_t timemin;
uint8_t times;CString timetemp = "";timetemp += m_input.GetAt(18);
timetemp += m_input.GetAt(19);timey = strtol(timetemp, NULL, 16);
timetemp = "";timetemp += m_input.GetAt(21);
timetemp += m_input.GetAt(22);timem = strtol(timetemp, NULL, 16);
timetemp = "";timetemp += m_input.GetAt(24);timetemp += m_input.GetAt(25);
timed = strtol(timetemp, NULL, 16);timetemp = "";timetemp += m_input.GetAt(27);
timetemp += m_input.GetAt(28);timeh = strtol(timetemp, NULL, 16);timetemp = "";
timetemp += m_input.GetAt(30);timetemp += m_input.GetAt(31);
timemin = strtol(timetemp, NULL, 16);timetemp = "";
timetemp += m_input.GetAt(33);timetemp += m_input.GetAt(34);
times = strtol(timetemp, NULL, 16);uint16_t crc_y;timetemp = "";
timetemp += m_input.GetAt(36);timetemp += m_input.GetAt(37);
timetemp += m_input.GetAt(39);timetemp += m_input.GetAt(40);
crc_y = strtol(timetemp, NULL, 16);
uint8_t crc_mess[]={len,prop,station,pass, timey, timem, timed, timeh, timemin, times };
uint16_t crc_result = CRC16((uint8_t*)crc_mess, 10);
if (crc_result != crc_y) {CString t;m_result += "\t校验码错误,正确为:";
t.Format("%04X", crc_result);   t.Insert(t.GetLength() - 2, " ");m_result += t;}
else {m_result += "\t校验码测试通过";}}SetDlgItemText(IDC_T_RESULT, m_result);

DlgText.h

#pragma once
#include "afxdtctl.h"
// CDlgText 对话框
class CDlgText : public CDialogEx
{
    DECLARE_DYNAMIC(CDlgText)
public:
    CDlgText(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~CDlgText();
// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_TEXT };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedOk();
    afx_msg void OnBnClickedButton1();
    CDateTimeCtrl c_Data;
    CDateTimeCtrl c_Time;
};

DlgText.cpp:

// DlgText.cpp : 实现文件
#include "stdafx.h"
#include "管道流量采集.h"
#include "DlgText.h"
#include "afxdialogex.h"
#include "stdio.h"
#include "stdint.h"
#include "crc.h"
// CDlgText 对话框
IMPLEMENT_DYNAMIC(CDlgText, CDialogEx)
CDlgText::CDlgText(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_TEXT, pParent)
{
}
CDlgText::~CDlgText()
{
}
void CDlgText::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_T_DATA, c_Data);
    DDX_Control(pDX, IDC_T_TIME, c_Time);
}
BEGIN_MESSAGE_MAP(CDlgText, CDialogEx)
    ON_BN_CLICKED(IDOK, &CDlgText::OnBnClickedOk)
    ON_BN_CLICKED(IDC_BUTTON1, &CDlgText::OnBnClickedButton1)
END_MESSAGE_MAP()
// CDlgText 消息处理程序
void CDlgText::OnBnClickedOk()
{
    // TODO: 在此添加控件通知处理程序代码
    CString m_input;
    CString m_result = "测试结果:";
    GetDlgItemText(IDC_T_INPUT, m_input);
    if (m_input.GetLength() < 41) {
        m_result += "帧总长度不够";
    }
    else {
        uint16_t address;
        CString addrtemp;
        addrtemp += m_input.GetAt(0);
        addrtemp += m_input.GetAt(1);
        addrtemp += m_input.GetAt(3);
        addrtemp += m_input.GetAt(4);
        address = strtol(addrtemp, NULL, 16);
        if (0xEB90 != address) {
            m_result += "\n前四位地址码为EB 90";
        }
        else {
            m_result += "\n地址码测试通过";
        }
        uint8_t len;
        CString lentemp;
        lentemp += m_input.GetAt(6);
        lentemp += m_input.GetAt(7);
        len = strtol(lentemp, NULL, 16);
        if ((m_input.GetLength() - 17) / 2 != len) {
            m_result += "\t长度计算错误,正确的是:";
            lentemp.Format("%02X", (m_input.GetLength() - 17) / 2);
            m_result += lentemp;
        }
        else {
            m_result += "\t长度测试通过";
        }
        uint8_t prop;
        CString proptemp;
        proptemp += m_input.GetAt(9);
        proptemp += m_input.GetAt(10);
        prop = strtol(proptemp, NULL, 16);
        if (0 != prop) {
            m_result += "\n属性码错误,正确的为:00";
        }
        else {
            m_result += "\n属性码测试通过";
        }
        uint8_t station;
        CString stationtemp;
        stationtemp += m_input.GetAt(12);
        stationtemp += m_input.GetAt(13);
        station = strtol(stationtemp, NULL, 16);
        if (0x02 != station) {
            m_result += "\t站号错误,正确为:02";
        }
        else {
            m_result += "\t站号测试通过";
        }
        uint8_t pass;
        CString passtemp;
        passtemp += m_input.GetAt(15);
        passtemp += m_input.GetAt(16);
        pass = strtol(passtemp, NULL, 16);
        if (0x02 != pass) {
            m_result += "\n通道号错误,正确为:02";
        }
        else {
            m_result += "\n通道号测试通过";
        }
        uint8_t timey;
        uint8_t timem;
        uint8_t timed;
        uint8_t timeh;
        uint8_t timemin;
        uint8_t times;
        CString timetemp = "";
        timetemp += m_input.GetAt(18);
        timetemp += m_input.GetAt(19);
        timey = strtol(timetemp, NULL, 16);
        timetemp = "";
        timetemp += m_input.GetAt(21);
        timetemp += m_input.GetAt(22);
        timem = strtol(timetemp, NULL, 16);
        timetemp = "";
        timetemp += m_input.GetAt(24);
        timetemp += m_input.GetAt(25);
        timed = strtol(timetemp, NULL, 16);
        timetemp = "";
        timetemp += m_input.GetAt(27);
        timetemp += m_input.GetAt(28);
        timeh = strtol(timetemp, NULL, 16);
        timetemp = "";
        timetemp += m_input.GetAt(30);
        timetemp += m_input.GetAt(31);
        timemin = strtol(timetemp, NULL, 16);
        timetemp = "";
        timetemp += m_input.GetAt(33);
        timetemp += m_input.GetAt(34);
        times = strtol(timetemp, NULL, 16);
        uint16_t crc_y;
        timetemp = "";
        timetemp += m_input.GetAt(36);
        timetemp += m_input.GetAt(37);
        timetemp += m_input.GetAt(39);
        timetemp += m_input.GetAt(40);
        crc_y = strtol(timetemp, NULL, 16);
        //EB90 0C 00 02 02 180512162835 C1CF
        uint8_t crc_mess[] = { len, prop, station, pass, timey, timem, timed, timeh, timemin, times };
        uint16_t crc_result = CRC16((uint8_t*)crc_mess, 10);
        if (crc_result != crc_y) {
            CString t;
            m_result += "\t校验码错误,正确为:";
            t.Format("%04X", crc_result);
            t.Insert(t.GetLength() - 2, " ");
            m_result += t;
        }
        else {
            m_result += "\t校验码测试通过";
        }
    }
    SetDlgItemText(IDC_T_RESULT, m_result);
    //CDialogEx::OnOK();
}
void CDlgText::OnBnClickedButton1()
{
    // TODO: 在此添加控件通知处理程序代码
    CString result;
    CTime tmpTime;
    SYSTEMTIME systim;
    uint8_t len;
    uint8_t prop;
    uint8_t station;
    uint8_t pass;
    uint8_t timey;
    uint8_t timem;
    uint8_t timed;
    uint8_t timeh;
    uint8_t timemin;
    uint8_t times;
    uint16_t crc_result;
    CString temp;
    len = 0X0C;
    prop = 0X00;
    station = 0X02;
    pass = 0X02;
    result = "自动生成的协议帧\n";
    result += "EB 90 0C 00 02 02 ";
    this->c_Data.GetTime(tmpTime);
    timey = tmpTime.GetYear()%100;
    temp.Format("%02d", timey);
    result += temp;
    result += " ";
    timey = strtol(temp, NULL, 16);
    timem = tmpTime.GetMonth();
    temp.Format("%02d", timem);
    result += temp;
    result += " ";
    timem = strtol(temp, NULL, 16);
    timed = tmpTime.GetDay();
    temp.Format("%02d", timed);
    result += temp;
    result += " ";
    timed = strtol(temp, NULL, 16);
    this->c_Time.GetTime(tmpTime);
    timeh = tmpTime.GetHour();
    temp.Format("%02d", timeh);
    result += temp;
    result += " ";
    timeh = strtol(temp, NULL, 16);
    timemin = tmpTime.GetMinute();
    temp.Format("%02d", timemin);
    result += temp;
    result += " ";
    timemin = strtol(temp, NULL, 16);
    times = tmpTime.GetSecond();
    temp.Format("%02d", times);
    result += temp;
    result += " ";
    times = strtol(temp, NULL, 16);
    uint8_t crc_mess[] = { len, prop, station, pass, timey, timem, timed, timeh, timemin, times };
    crc_result = CRC16((uint8_t*)crc_mess, 10);
    temp.Format("%04X", crc_result);
    result += temp;
    result.Insert(result.GetLength() - 2, " ");
    SetDlgItemText(IDC_T_RESULT, result);
}

5.7.8主对话框(实时流量)

首先为主对话框添加菜单栏:
这里写图片描述


图16增加资源示意图
这里写图片描述

图17菜单分布图

所有的菜单都是一级菜单:
表17菜单ID映射表 菜单名|ID :——–|:—– 原理讲解(&I)|ID_INTERPRET 协议测试(&T)|ID_TEST 流量预测(&F)|ID_FORECAST 历史流量数据(&H)|ID_HISTORY 统计分析表(&E)|ID_EXCEL 设置参数(&S)|ID_SETDATA 查看参数(&L)|ID_LOOKDATA 添加菜单的快捷键,资源的添加方式和菜单的资源添加相同,只是资源格式为Accelerator。 ![这里写图片描述](https://img-blog.csdn.net/20180701125308716?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ExODc5MjcyMTgzMQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
图18快捷键键位图 把菜单和快捷键和主对话框绑定,重写OnInitDialog方法:
ShowWindow(SW_MAXIMIZE);    CMenu menu;menu.LoadMenu(IDR_MENU);
SetMenu(&menu);//添加快捷键//加载资源列表
m_Haccel = LoadAccelerators(theApp.m_hInstance, (LPCTSTR)IDR_ACCELERATOR);
CMysqlConnection * m_MysqlConnectin = CMysqlConnection::GetMysqlConn();
m_MysqlConnectin->ConnectionMysql();//设置串口参数setComm();//设置折线图数据
setTChartData();

为菜单添加事件处理方法,在主对话框属性中选择事件添加COMMAND处理方法:

void C管道流量采集Dlg::OnExcel()
{// TODO: 在此添加命令处理程序代码//统计分析表对话框
CSingle *single = CSingle::GetSingle();single->m_dlgExcel->DoModal();}

其余类似,确保所有的菜单都添加了事件处理方法。
重写OnCancel方法,在此方法中添加连接关闭的方法:

if (this->c_Comm.get_PortOpen()) {this->c_Comm.put_PortOpen(FALSE);
AfxMessageBox("串口关闭成功");}
else {AfxMessageBox("串口关闭失败,请重试");}

重写OnCtlColor方法:

CFont m_SetFont;switch (pWnd->GetDlgCtrlID())
{case IDC_STATIC_DATAVIEW:pDC->SetTextColor(RGB(255, 0, 0));break;
case IDC_STATIC_MS:pDC->SetTextColor(RGB(255, 0, 0));break;
case IDC_DATAVIEW:m_SetFont.CreatePointFont(400, _T("TimesNewRoman"));
pDC->SelectObject(&m_SetFont);pDC->SetTextColor(RGB(0, 255, 0));
pDC->SetBkColor(RGB(103, 103, 103));break;
case IDC_XIEYIZHEN:m_SetFont.CreatePointFont(200, _T("TimesNewRoman"));
pDC->SelectObject(&m_SetFont);pDC->SetTextColor(RGB(0, 0, 255));
pDC->SetBkColor(RGB(0, 255, 0));break;default:break;}

增加打开串口的方法setComm:

CDataConfig * m_DataConfig = CDataConfig::GetCDataConfig();
CString temp;temp += m_DataConfig->getBotelv(); temp += ",";
temp += m_DataConfig->getJiaoyan(); temp += ",";    temp += m_DataConfig->getShuju();
temp += ",";    temp += m_DataConfig->getTingzhi();
this->c_Comm.put_CommPort(atoi(m_DataConfig->getComm()));
this->c_Comm.put_InBufferSize(1024);this->c_Comm.put_OutBufferSize(1024);
this->c_Comm.put_InputLen(0);this->c_Comm.put_InputMode(1);
this->c_Comm.put_RThreshold(1);this->c_Comm.put_Settings(temp);
if (!this->c_Comm.get_PortOpen()) {this->c_Comm.put_PortOpen(TRUE);
AfxMessageBox("串口打开成功");this->c_Comm.put_InBufferCount(0);}
else {AfxMessageBox("串口打开失败,请重试");}

增加数据显示到实时流量折线图的方法setTChartData:

CTeeData * m_Onedata;CString temp;
CString tt;CSeries viewdata= CSeries)this->c_TChartData.Series(0);
tt = "实时流量:";temp.Format("%.3f", this->m_Dataview);
tt += temp;tt += "  m3/s";GetDlgItem(IDC_DATAVIEW)->SetWindowText(tt);
viewdata.Clear();   for (int i = 0; i < this->m_ViewDatas->GetSize(); i++) {
m_Onedata = (CTeeData *)this->m_ViewDatas->GetAt(i);
viewdata.Add(m_Onedata->m_Viewvalue, m_Onedata->m_ViewData, NULL);}

增加字符串转16进制数的方法String2Hex:

int C管道流量采集Dlg::String2Hex(CString str, CByteArray& senddata)
{int hexdata, lowhexdata;int hexdatalen = 0;int len = str.GetLength();senddata.SetSize(len / 2);
for (int i = 0; i<len;){char lstr, hstr = str[i];if (hstr == ' '){i++;continue;}i++;if (i >= len)break;
lstr = str[i];hexdata = ConvertHexChar(hstr);lowhexdata = ConvertHexChar(lstr);
if ((hexdata == 16) || (lowhexdata == 16))break;
else    hexdata = hexdata * 16 + lowhexdata;i++;senddata[hexdatalen] = (char)hexdata;
hexdatalen++;}senddata.SetSize(hexdatalen);return hexdatalen;}

增加串口数据帧格式化方法:

char C管道流量采集Dlg::ConvertHexChar(char ch)
{if ((ch >= '0') && (ch <= '9'))return ch - 0x30;else if ((ch >= 'A') && (ch <= 'F'))
return ch - 'A' + 10;else if ((ch >= 'a') && (ch <= 'f'))return ch - 'a' + 10;else return (-1);}

增加16进制转10进制的方法:

int C管道流量采集Dlg::HexToDem(CString str)
{int dem = 0;for (int i = 0; i<str.GetLength(); i++){dem = dem * 16;
if ((str[i] <= '9') && (str[i] >= '0'))        //0~9之间的字符dem += str[i] - '0';
else if ((str[i] <= 'F') && (str[i] >= 'A'))   //A~F之间的字符dem += str[i] - 'A' + 10;
else if ((str[i] <= 'f') && (str[i] >= 'a'))   //a~f之间的字符dem += str[i] - 'a' + 10;
else    return -1;                          //出错时返回-1}return dem;}

增加串口数据接收的事件处理方法OnCommMscomm1:

VARIANT variant_inp;COleSafeArray safearray_inp;LONG len, k;
BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.
CString strtemp = "";CString resultmess = "";BYTE bt;
if (this->c_Comm.get_CommEvent() == 2) //事件值为2表示接收缓冲区内有字符
{variant_inp = this->c_Comm.get_Input(); //读缓冲区
safearray_inp = variant_inp; //VARIANT型变量转换为ColeSafeArray型变量
len = safearray_inp.GetOneDimSize(); //得到有效数据长度for (k = 0; k<len; k++)
safearray_inp.GetElement(&k, rxdata + k);//转换为BYTE型数组
for (k = 0; k<len; k++) //将数组转换为Cstring型变量{bt = *(char*)(rxdata + k); //字符型
strtemp.Format("%02X", bt);resultmess += strtemp;   strtemp = "";}}
this->message += resultmess;    this->c_Comm.put_InBufferCount(0);
this->timerflag++;if(this->timerflag == 3)SetTimer(2, 300, NULL);

增加定时器时间到的处理方法,在属性的消息中重写WM_TIMER的处理方法OnTimer。

CString temp;
CMysqlConnection * m_MysqlConnection = CMysqlConnection::GetMysqlConn();
CString send;CString strtemp;CString tmp;SYSTEMTIME m_Systm;//添加发送数据
//在接收数据里处理//添加到数据库等等
CByteArray hexdata;uint8_t addressh;uint8_t addressl;uint8_t len;uint8_t prop;
uint8_t station;uint8_t pass;uint8_t timey;uint8_t timem;uint8_t timed;uint8_t timeh;
uint8_t timemin;uint8_t times;uint16_t crc_result;uint8_t crc_y[10];
CString xieyizhentemp = "";CString ttt = "";CString xieyizhen = "";switch (nIDEvent) {
case 1:temp = "";   send = "";GetLocalTime(&m_Systm);this->m_Datatime = m_Systm;
addressh = strtol("EB", NULL, 16);addressl = strtol("90", NULL, 16);
len = strtol("0C", NULL, 16);prop = strtol("00", NULL, 16);
station = strtol("02", NULL, 16);pass = strtol("02", NULL, 16);
tmp.Format("%d", m_Systm.wYear % 100);timey = strtol(tmp, NULL, 16);
tmp.Format("%d", m_Systm.wMonth);   timem = strtol(tmp, NULL, 16);
tmp.Format("%d", m_Systm.wDay);timed = strtol(tmp, NULL, 16);
tmp.Format("%d", m_Systm.wHour);timeh = strtol(tmp, NULL, 16);
tmp.Format("%d", m_Systm.wMinute);timemin = strtol(tmp, NULL, 16);
tmp.Format("%d", m_Systm.wSecond);times = strtol(tmp, NULL, 16);
crc_y[0] = len;crc_y[1] = prop;crc_y[2] = station;crc_y[3] = pass;crc_y[4] = timey;
crc_y[5] = timem;crc_y[6] = timed;crc_y[7] = timeh;crc_y[8] = timemin;
crc_y[9] = times;   crc_result = CRC16((uint8_t*)crc_y, 10);
temp.Format("%02X ", addressh);send += temp;temp.Format("%02X ", addressl);
send += temp;temp.Format("%02X ", len);send += temp;temp.Format("%02X ", prop);
send += temp;temp.Format("%02X ", station);send += temp;
temp.Format("%02X ", pass);send += temp;temp.Format("%02X ", timey);
send += temp;temp.Format("%02X ", timem);send += temp;
temp.Format("%02X ", timed);send += temp;temp.Format("%02X ", timeh);
send += temp;temp.Format("%02X ", timemin);send += temp;
temp.Format("%02X ", times);send += temp;temp.Format("%04X", crc_result);
send += temp;send.Insert(send.GetLength() - 2, " ");    xieyizhen = "发送:";
xieyizhen += send;SetDlgItemText(IDC_XIEYIZHEN, xieyizhen);
String2Hex(send, hexdata);this->c_Comm.put_Output(COleVariant(hexdata));break;
case 2:if (this->message.GetLength() == 38 && this->timerflag == 3 && KillTimer(2)) {
xieyizhentemp = this->message;for (int i = 2; i < xieyizhentemp.GetLength(); i += 3) {
xieyizhentemp.Insert(i, " ");}
GetDlgItemText(IDC_XIEYIZHEN, ttt);ttt += "\n返回:";ttt += xieyizhentemp;
SetDlgItemText(IDC_XIEYIZHEN, ttt);
this->timerflag = 0;strtemp += this->message.GetAt(14);
strtemp += this->message.GetAt(15);strtemp += this->message.GetAt(16);
strtemp += this->message.GetAt(17);strtemp += this->message.GetAt(18);
strtemp += this->message.GetAt(19);this->m_Dataview = HexToDem(strtemp) / 1000.0;
strtemp.Format("%d:%d:%d",this->m_Datatime.wHour,his->m_Datatime.wMinute,this->m_Datatime.wSecond);CTeeData * m_Onedata = new CTeeData(this->m_Dataview, strtemp);
this->m_ViewDatas->Add(m_Onedata);setTChartData();
strtemp.Format("%2d-%d-%d %d:%d:%d",this->m_Datatime.wYear,this->m_Datatime.wMonth,this->m_Datatime.wDay,this->m_Datatime.wHour,this->m_Datatime.wMinute,this->m_Datatime.wSecond);if (m_MysqlConnection->setData(this->m_Dataview, strtemp)) {
this->message = "";}else    {AfxMessageBox("加入数据库失败");return;}  }break;efault:break;}

对话框实现大小自适应,在自适应中控件的大小需要对应的改变。
首先增加自使用的头文件easysize.h:

#ifndef __EASYSIZE_H_#define __EASYSIZE_H_#define ES_BORDER 0xffffffff
#define ES_KEEPSIZE 0xfffffffe#define ES_HCENTER 0x00000001
#define ES_VCENTER 0x00000002#define DECLARE_EASYSIZE \void __ES__RepositionControls(BOOL bInit);\void __ES__CalcBottomRight(CWnd *pThis, BOOL bBottom, int &bottomright, int &topleft, UINT id, UINT br, int es_br, CRect &rect, int clientbottomright);#define INIT_EASYSIZE __ES__RepositionControls(TRUE); __ES__RepositionControls(FALSE)#define UPDATE_EASYSIZE if(GetWindow(GW_CHILD)!=NULL) __ES__RepositionControls(FALSE)#define EASYSIZE_MINSIZE(mx,my,s,r) if(r->right-r->left < mx) { if((s == WMSZ_BOTTOMLEFT)||(s == WMSZ_LEFT)||(s == WMSZ_TOPLEFT)) r->left = r->right-mx; else r->right = r->left+mx; } if(r->bottom-r->top < my) { if((s == WMSZ_TOP)||(s == WMSZ_TOPLEFT)||(s == WMSZ_TOPRIGHT)) r->top = r->bottom-my; else r->bottom = r->top+my; }#define BEGIN_EASYSIZE_MAP(class) \void class::__ES__CalcBottomRight(CWnd *pThis, BOOL bBottom, int &bottomright, int &topleft, UINT id, UINT br, int es_br, CRect &rect, int clientbottomright) {\if(br==ES_BORDER) bottomright = clientbottomright-es_br;\else if(br==ES_KEEPSIZE) bottomright = topleft+es_br;\else { CRect rect2;\pThis->GetDlgItem(br)->GetWindowRect(rect2); pThis->ScreenToClient(rect2);\bottomright = (bBottom?rect2.top:rect2.left) - es_br;}}\void class::__ES__RepositionControls(BOOL bInit) { CRect rect,rect2,client; GetClientRect(client);#define END_EASYSIZE_MAP Invalidate(); UpdateWindow(); }#define EASYSIZE(id,l,t,r,b,o) \static int id##_es_l, id##_es_t, id##_es_r, id##_es_b;\if(bInit) {\GetDlgItem(id)->GetWindowRect(rect); ScreenToClient(rect);\if(o & ES_HCENTER) id##_es_l = rect.Width()/2; else {\if(l==ES_BORDER) id##_es_l = rect.left; else if(l==ES_KEEPSIZE) id##_es_l = rect.Width(); else {\GetDlgItem(l)->GetWindowRect(rect2); ScreenToClient(rect2);\id##_es_l = rect.left-rect2.right;}}\if(o & ES_VCENTER) id##_es_t = rect.Height()/2; else {\if(t==ES_BORDER) id##_es_t = rect.top; else if(t==ES_KEEPSIZE) id##_es_t = rect.Height(); else {\GetDlgItem(t)->GetWindowRect(rect2); ScreenToClient(rect2);\id##_es_t = rect.top-rect2.bottom;}}\if(o & ES_HCENTER) id##_es_r = rect.Width(); else { if(r==ES_BORDER) id##_es_r = client.right-rect.right; else if(r==ES_KEEPSIZE) id##_es_r = rect.Width(); else {\GetDlgItem(r)->GetWindowRect(rect2); ScreenToClient(rect2);\id##_es_r = rect2.left-rect.right;}}\if(o & ES_VCENTER) id##_es_b = rect.Height(); else { if(b==ES_BORDER) id##_es_b = client.bottom-rect.bottom; else if(b==ES_KEEPSIZE) id##_es_b = rect.Height(); else {\GetDlgItem(b)->GetWindowRect(rect2); ScreenToClient(rect2);\id##_es_b = rect2.top-rect.bottom;}}\} else {\int left,top,right,bottom; BOOL bR = FALSE,bB = FALSE;\if(o & ES_HCENTER) { int _a,_b;\if(l==ES_BORDER) _a = client.left; else { GetDlgItem(l)->GetWindowRect(rect2); ScreenToClient(rect2); _a = rect2.right; }\if(r==ES_BORDER) _b = client.right; else { GetDlgItem(r)->GetWindowRect(rect2); ScreenToClient(rect2); _b = rect2.left; }\left = _a+((_b-_a)/2-id##_es_l); right = left + id##_es_r;} else {\if(l==ES_BORDER) left = id##_es_l;\else if(l==ES_KEEPSIZE) { __ES__CalcBottomRight(this,FALSE,right,left,id,r,id##_es_r,rect,client.right); left = right-id##_es_l;\} else { GetDlgItem(l)->GetWindowRect(rect2); ScreenToClient(rect2); left = rect2.right + id##_es_l; }\if(l != ES_KEEPSIZE) __ES__CalcBottomRight(this,FALSE,right,left,id,r,id##_es_r,rect,client.right);}\if(o & ES_VCENTER) { int _a,_b;\if(t==ES_BORDER) _a = client.top; else { GetDlgItem(t)->GetWindowRect(rect2); ScreenToClient(rect2); _a = rect2.bottom; }\
if(b==ES_BORDER) _b = client.bottom; else { GetDlgItem(b)->GetWindowRect(rect2); ScreenToClient(rect2); _b = rect2.top; }\top = _a+((_b-_a)/2-id##_es_t); bottom = top + id##_es_b;} else {\if(t==ES_BORDER) top = id##_es_t;\else if(t==ES_KEEPSIZE) { __ES__CalcBottomRight(this,TRUE,bottom,top,id,b,id##_es_b,rect,client.bottom); top = bottom-id##_es_t;\} else { GetDlgItem(t)->GetWindowRect(rect2); ScreenToClient(rect2); top = rect2.bottom + id##_es_t; }\if(t != ES_KEEPSIZE) __ES__CalcBottomRight(this,TRUE,bottom,top,id,b,id##_es_b,rect,client.bottom);}\GetDlgItem(id)->MoveWindow(left,top,right-left,bottom-top,FALSE);\}#endif //__EASYSIZE_H_

然后重写OnSize方法:

UPDATE_EASYSIZE;

在主对话框的cpp文件中最后部分添加控件映射:

BEGIN_EASYSIZE_MAP(C管道流量采集Dlg)
    EASYSIZE(IDC_TC_DATA, ES_BORDER, IDC_XIEYIZHEN, ES_BORDER, ES_BORDER, 0)
    EASYSIZE(IDC_XIEYIZHEN, ES_BORDER, IDC_DATAVIEW, ES_BORDER, IDC_TC_DATA,0)
    EASYSIZE(IDC_DATAVIEW, ES_BORDER, ES_BORDER, ES_BORDER, IDC_XIEYIZHEN, 0)
END_EASYSIZE_MAP

在初始化方法中添加INIT_EASYSIZE;,在类的声明中最开始添加DECLARE_EASYSIZE。
管道流量采集.h:

// 管道流量采集.h : PROJECT_NAME 应用程序的主头文件
#pragma once
#ifndef __AFXWIN_H__
    #error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif
#include "resource.h"       // 主符号
// C管道流量采集App: 
// 有关此类的实现,请参阅 管道流量采集.cpp
//
class C管道流量采集App : public CWinApp
{
public:
    C管道流量采集App();
// 重写
public:
    virtual BOOL InitInstance();
// 实现
    DECLARE_MESSAGE_MAP()
};
extern C管道流量采集App theApp;

管道流量采集.cpp:

// 管道流量采集.cpp : 定义应用程序的类行为。
#include "stdafx.h"
#include "管道流量采集.h"
#include "管道流量采集Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// C管道流量采集App
BEGIN_MESSAGE_MAP(C管道流量采集App, CWinApp)
    ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// C管道流量采集App 构造
C管道流量采集App::C管道流量采集App()
{
    // 支持重新启动管理器
    m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
    // TODO: 在此处添加构造代码,
    // 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 C管道流量采集App 对象
C管道流量采集App theApp;
// C管道流量采集App 初始化
BOOL C管道流量采集App::InitInstance()
{
    // 如果一个运行在 Windows XP 上的应用程序清单指定要
    // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
    //则需要 InitCommonControlsEx()。  否则,将无法创建窗口。
    INITCOMMONCONTROLSEX InitCtrls;
    InitCtrls.dwSize = sizeof(InitCtrls);
    // 将它设置为包括所有要在应用程序中使用的
    // 公共控件类。
    InitCtrls.dwICC = ICC_WIN95_CLASSES;
    InitCommonControlsEx(&InitCtrls);
    OleInitialize(NULL);
    AfxEnableControlContainer();
    CWinApp::InitInstance();
    AfxEnableControlContainer();
    // 创建 shell 管理器,以防对话框包含
    // 任何 shell 树视图控件或 shell 列表视图控件。
    CShellManager *pShellManager = new CShellManager;
    // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
    // 标准初始化
    // 如果未使用这些功能并希望减小
    // 最终可执行文件的大小,则应移除下列
    // 不需要的特定初始化例程
    // 更改用于存储设置的注册表项
    // TODO: 应适当修改该字符串,
    // 例如修改为公司或组织名
    SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
    C管道流量采集Dlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
        // TODO: 在此放置处理何时用
        //  “确定”来关闭对话框的代码
    }
    else if (nResponse == IDCANCEL)
    {
        // TODO: 在此放置处理何时用
        //  “取消”来关闭对话框的代码
    }
    else if (nResponse == -1)
    {
        TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
        TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
    }
    // 删除上面创建的 shell 管理器。
    if (pShellManager != NULL)
    {
        delete pShellManager;
    }
#ifndef _AFXDLL
    ControlBarCleanUp();
#endif
    // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
    //  而不是启动应用程序的消息泵。
    return FALSE;
}

5.8数据库额外操作

      首先在互联网上下载32位的数据库文件,如果本机系统是32的,那么可以不用下载,如果实在找不到下载的地址,可以到32位的数据库安装目录中拷贝文件:
这里写图片描述


图19数据库32连接文件图

【项目】->【属性】->【配置属性】->【常规属性】->【字符集】->【多字节字符集】
【配置属性】->【C/C++】->【常规】->【附加包含目录】->【C:\Program Files\MySQL\MySQL Server 5.7\include;%(AdditionalIncludeDirectories)】
【链接器】->【常规】->【附加库目录】->【C:\Program Files\MySQL\MySQL Server 5.7\lib;%(AdditionalLibraryDirectories)】
【链接器】->【输入】->【附加依赖项】->【libmysql32.lib;%(AdditionalDependencies)】
然后把libmysql32.dll重命名为libmysql.dll并且放到Debug目录下。

5.9常见错误

预编译无法使用或这打开**预编译文件:
【项目】->【属性】->【配置属性】->【C/C++】->【预编译头】->【预编译头】,如果原来是使用改成创建,原来是创建改成使用。
*.dll文件无法加载:
这里写图片描述


图20错误解决设置图

第一次编译时间会比较长,无法打开的dll文件会从互联网上下载,所以必须联网。

5.10替换标题栏图标

      MFC默认的标题栏图标替换首先需要找到自己需要替换的ico文件,然后类似导入图片一样导入到工程中,其ID为IDI_ICON1,并且把ico文件放到res文件夹下,然后打开resource.hz增加:

#define IDI_ICON1                       100

最后在主对话框的构造方法中添加:

m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);

5.11打包

      在前面的操作都没有问题,且debug调试全部通过后,即可进入这个操作。
首先切换到Release模式。
这里写图片描述


图21切换模式图

      然后重写设置第8部分的所有操作,操作成功后重新生成解决方案,当Release中调试通过后,到互联网上下载打包模板。
这里写图片描述


图22打包模板详细信息

      把打包模板安装好后,在工程的解决方案视图中右键整个解决方案->【添加】->【新建项目】。
这里写图片描述


图23创建安装包工程图

此时整个解决方案中有两个项目:
这里写图片描述


图24解决方案图

右键安装包项目->【Add】->【项目输出】->【主输出】,其次:
这里写图片描述


图25添加模块图

添加所有32位的文件。
右键安装包项目->【View】->【文件系统】,然后添加Release文件夹中除去exe文件的所有文件。
这里写图片描述


图26安装包文件系统图

      其中Pipe Line.ico是应用程序安装成功后快捷方式的图标,也需要导入;msiexec.exe是32位系统中卸载程序的,以及折线图和串口的ocx文件,这些文件都是安装目录中的文件。User’s Desktop是安装包安装成功后在用户桌面的快捷方式,首先创建一个主输出的快捷方式和一个卸载程序的快捷方式,然后重命名为自己需要的名字,设置主输出的icon为添加的ico图标;修改卸载程序的快捷方式的Arguments的属性值为/x {D1AB7D8F-BCD0-4F41-857B-549373B3EDE5},大括号中的数字在安装包的属性的ProductCode中的数值。同样在开始菜单中拷贝这两个文件。
右键整个解决方案->【批生成】。
这里写图片描述


图27批生成配置图

选中图中的两个勾,选择重新生成。如果一切顺利将会在输出窗口显示。
这里写图片描述


图28安装包生成成功图

此时,安装包文件在工程文件下安装包文件夹的Release文件夹下。
这里写图片描述


图29安装包位置图

六、 总结

      完成整个实验可以掌握RS-485原理、磁漩涡流量传感器、MODBUS通信等以及较强的MFC程序编程能力。
      在整个实验中,实验比较大,难点比较多,包括数据库的连接、注册表操作、外部插件的导入和使用、对话框菜单项添加、查询数据帧的组装和解析、定时器的使用、快捷键的添加、程序的打包等等。但是完成实验后的收获也是非常大的,是一个值得完成的实验项目。
      我的毕业设计是工程类型的,并不是学术研究型的,所以要求有论文,实验指导书,实例程序,软件使用说明书四部分。
      这只是其中的实验指导书,因为有硬件设备,如果没有硬件设备可以使用VSPD和串口调试助手来模拟。
      现在毕业设计已经完成了,包括论文查重,答辩等等。之前没有写出来一方面是没有时间来上传,我有pdf和word版本,但是上传到csdn还是需要很多编辑的,我不会直接上传这些格式的文章。另一方面实验指导书和论文中有一部分是意思大致相同,担心查重结果的影响。
      因为毕业设计中使用的技术都是很老的技术,所以毕业设计论文怎么写重复率都很高,我尽力的降重了十好几次,重复率还是在15左右,所以不敢在查重之前放出来。


      如果我的文章中有什么错误,毕竟很大,可能是我没有注意到的地方,请大家直接在评论区中指出,我看到后有时间尽力回复。

      源代码下载地址
https://download.csdn.net/download/a18792721831/10511739
      对话框控件资源文件下载地址
https://download.csdn.net/download/a18792721831/10511757
      easysize和crc文件下载地址
https://download.csdn.net/download/a18792721831/10511751
      对话框插件文件下载地址
https://download.csdn.net/download/a18792721831/10511759
      打包的卸载程序文件下载地址
https://download.csdn.net/download/a18792721831/10511754
打包卸载不建议下载,在32位windows操作系统可以找到,网上也有资源可以免费下载,但是csdn积分下载没有0分,分数不足的请勿下载哦。

猜你喜欢

转载自blog.csdn.net/a18792721831/article/details/80870613