534 lines
15 KiB

  1. #include "common.h"
  2. /*******************************************************************************************
  3.  * 功能     : 打开串口
  4. * port     : 串口号, 如("COM1")
  5. * baud_rate: 波特率
  6. * date_bits: 数据位(有效范围4~8)
  7. * stop_bit : 停止位
  8. * parity   : 奇偶校验。默认为无校验。NOPARITY 0; ODDPARITY 1;EVENPARITY 2
  9. ********************************************************************************************/
  10. HANDLE Init_COM(LPCTSTR port, int baud_rate, BYTE date_bits, BYTE stop_bit, BYTE parity)
  11. {
  12. HANDLE Handle_Com = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);//同步方式打开串口
  13. if (INVALID_HANDLE_VALUE == Handle_Com)
  14. {
  15. return INVALID_HANDLE_VALUE;
  16. }
  17. SetupComm(Handle_Com, 4096, 4096);//设置缓存
  18. DCB dcb;
  19. if (!GetCommState(Handle_Com, &dcb))
  20. {
  21. cout << "获取串口配置失败" << endl;
  22. }
  23. dcb.BaudRate = baud_rate; //波特率
  24. dcb.fBinary = TRUE; //二进制模式。必须为TRUE
  25. dcb.ByteSize = date_bits; //数据位。范围4-8
  26. if (stop_bit == 0)
  27. dcb.StopBits = ONESTOPBIT; //停止位
  28. if (stop_bit == 1)
  29. dcb.StopBits = ONE5STOPBITS; //停止位
  30. if (stop_bit == 2)
  31. dcb.StopBits = TWOSTOPBITS; //停止位
  32. if (parity == NOPARITY)
  33. {
  34. dcb.fParity = FALSE; //奇偶校验关闭
  35. dcb.Parity = parity; //校验模式
  36. }
  37. else
  38. {
  39. dcb.fParity = TRUE; //奇偶校验开启
  40. dcb.Parity = parity; //校验模式
  41. }
  42. if (!SetCommState(Handle_Com, &dcb))
  43. return INVALID_HANDLE_VALUE;
  44. PurgeComm(Handle_Com, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT);//清除缓存
  45. //设置串口读写时间
  46. COMMTIMEOUTS CommTimeOuts;
  47. GetCommTimeouts(Handle_Com, &CommTimeOuts);
  48. CommTimeOuts.ReadIntervalTimeout = 5;
  49. CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
  50. CommTimeOuts.ReadTotalTimeoutConstant = 0;
  51. CommTimeOuts.WriteTotalTimeoutMultiplier = 10;
  52. CommTimeOuts.WriteTotalTimeoutConstant = 1000;
  53. if (!SetCommTimeouts(Handle_Com, &CommTimeOuts)) {
  54. return INVALID_HANDLE_VALUE;
  55. }
  56. return Handle_Com;
  57. }
  58. /*********************************************************************************************
  59. * 功能     :  发送响应数据
  60. * 描述    : 向串口写入数据
  61. * 返回值 : true 发送成功 false 发送失败
  62. * m_hComm : 串口句柄
  63. * data : 要写入的数据
  64. * len : 写入数据的长度
  65. **********************************************************************************************/
  66. bool SendData(HANDLE m_hcomm, char* data, int len)
  67. {
  68. if (m_hcomm == INVALID_HANDLE_VALUE)
  69. return FALSE;
  70. PurgeComm(m_hcomm, PURGE_RXCLEAR | PURGE_TXCLEAR);
  71. DWORD dwWrite = 0;
  72. int count = 0;
  73. while (!WriteFile(m_hcomm, data, len, &dwWrite, NULL))
  74. {
  75. count++;
  76. if (count > 4)
  77. return false;
  78. }
  79. return true;
  80. }
  81. bool Abnormal_Connection(HANDLE *handle_com)
  82. {
  83. printf("设备端口异常,请检查设备连接状态\n");
  84. printf("**************************** Press Enter To Contioun ****************************\n");
  85. getchar();
  86. system("cls");
  87. printf("是否重新连接端口: 1 重新连接 0 关闭本软件\n");
  88. int flage = 0;
  89. do
  90. {
  91. cin >> flage;
  92. cin.clear();
  93. cin.sync();
  94. } while (!(flage == 0 || flage == 1));
  95. if (flage == 0)
  96. {
  97. return false;
  98. }
  99. CloseHandle(*handle_com);
  100. *handle_com = Input_Parameter();
  101. return true;
  102. }
  103. bool Recv_date(HANDLE *handle_com, UINT8 *Requst_Message, DWORD *read_len)
  104. {
  105. PurgeComm(*handle_com, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT);//清除缓存
  106. BOOL read_flage = ReadFile(*handle_com, Requst_Message, 300, &(*read_len), NULL); //阻塞等待接收请求报文
  107. if (read_flage && (*read_len > 0))
  108. {
  109. return true;
  110. }
  111. if (!Abnormal_Connection(handle_com))
  112. return false;
  113. return true;
  114. }
  115. /*********************************************************************************************
  116. * 功能     :  检查端口名称
  117. * 描述    : 判断端口名称是否正确
  118. * 输入 : Comm 端口名
  119. * 返回值 : true 端口名正确
  120. * false 端口名错误
  121. **********************************************************************************************/
  122. bool Check_Input_COMM(string comm)
  123. {
  124. string compare1 = "com";
  125. string compare2 = "COM";
  126. for (unsigned int i = 0; i < 3; i++)
  127. {
  128. if (comm[i] != compare1[i] && comm[i] != compare2[i])
  129. return false;
  130. }
  131. if (comm[3] == 0x30)
  132. return false;
  133. for (unsigned int i = 4; i < comm.length(); i++)
  134. {
  135. if (comm[i] >= 0x30 && comm[i] <= 0x39)
  136. continue;
  137. }
  138. return true;
  139. }
  140. /*********************************************************************************************
  141. * 功能     :  输入端口名称
  142. * 描述    : 获取串口通信的端口名称
  143. * 输入 : 无
  144. * 返回值 : COMM 端口名
  145. **********************************************************************************************/
  146. string Input_COMM(void)
  147. {
  148. int flage = 1;
  149. string comm;
  150. do
  151. {
  152. if (flage == 1)
  153. {
  154. printf("请输入端口名称:");
  155. flage = 0;
  156. }
  157. else
  158. printf("请重新输入端口名称:");
  159. cin >> comm;
  160. cin.clear();
  161. cin.sync();
  162. } while (!Check_Input_COMM(comm));
  163. return comm;
  164. }
  165. /*********************************************************************************************
  166. * 功能     :  检查波特率
  167. * 描述    : 检查串口通信的波特率
  168. * 输入 : Baud_Rate 波特率
  169. * 返回值 : true 波特率正确
  170. * false 波特率错误
  171. **********************************************************************************************/
  172. bool Check_Input_Baud_Rate(unsigned int baud_rate)
  173. {
  174. unsigned int compare_baud_rate[9] = {300,600,1200,2400,4800,9600,14400,19200,38400};
  175. for (int i = 0; i < 9; i++)
  176. {
  177. if (baud_rate == compare_baud_rate[i])
  178. return true;
  179. }
  180. return false;
  181. }
  182. /*********************************************************************************************
  183. * 功能     :  输入波特率
  184. * 描述    : 获取串口通信的波特率
  185. * 输入 : 无
  186. * 返回值 : Baud_Rate 波特率
  187. **********************************************************************************************/
  188. unsigned int Input_Baud_Rate(void)
  189. {
  190. unsigned int baud_rate;
  191. int flage = 1;
  192. printf("支持的波特率有:300,600,1200,2400,4800,9600,14400,19200,38400 \n");
  193. do
  194. {
  195. if (flage == 1)
  196. {
  197. printf("请输入串口波特率:");
  198. flage = 0;
  199. }
  200. else
  201. printf("请重新输入串口波特率:");
  202. cin >> baud_rate;
  203. cin.clear();
  204. cin.sync();
  205. } while (!Check_Input_Baud_Rate(baud_rate));
  206. return baud_rate;
  207. }
  208. /*********************************************************************************************
  209. * 功能     :  输入数据位
  210. * 描述    : 获取串口通信的数据位个数
  211. * 输入 : 无
  212. * 返回值 : Date_Bits 数据位
  213. **********************************************************************************************/
  214. BYTE Input_Date_Bits(void)
  215. {
  216. BYTE date_bits;
  217. int flage = 1;
  218. unsigned int data_bits_temp = 0;
  219. printf("支持的数据位有:5,6,7,8 \n");
  220. do
  221. {
  222. if (flage == 1)
  223. {
  224. printf("请输入有效数据位:");
  225. flage = 0;
  226. }
  227. else
  228. printf("请重新输入有效数据位:");
  229. cin >> data_bits_temp;
  230. cin.clear();
  231. cin.sync();
  232. } while (!(data_bits_temp == 5 || data_bits_temp == 6 || data_bits_temp == 7 || data_bits_temp == 8));
  233. date_bits = (BYTE)data_bits_temp;
  234. return date_bits;
  235. }
  236. /*********************************************************************************************
  237. * 功能     :  检查停止位
  238. * 描述    : 检测停止位是否和数据位相符
  239. * 输入 : Date_Bits 数据位
  240. * Stop_Bits 停止位
  241. * 返回值 : true 停止位正确
  242. * false 停止位错误
  243. **********************************************************************************************/
  244. bool Check_Input_Stop_Bits(BYTE date_bits, BYTE stop_bits)
  245. {
  246. if (stop_bits == 0 || stop_bits == 1 || stop_bits == 2)
  247. {
  248. if (stop_bits == 1 && (date_bits == 6 || date_bits == 7 || date_bits == 8))
  249. return false;
  250. if (stop_bits == 2 && date_bits == 5)
  251. return false;
  252. }
  253. else
  254. return false;
  255. return true;
  256. }
  257. /*********************************************************************************************
  258. * 功能     :  输入停止位
  259. * 描述    : 获取串口通信的停止位
  260. * 输入 : Date_Bits 数据位
  261. * Stop_Bits= 0,1,2对应的是1bit,1.5bits,2bits.
  262. * Date_Bits=6,7,8时 Stop_Bits不能为1
  263. * Date_Bits=5时 Stop_Bits不能为2
  264. * 返回值 : Stop_Bits 数据位
  265. **********************************************************************************************/
  266. BYTE Input_Stop_Bits(BYTE date_bits)
  267. {
  268. BYTE stop_bits;
  269. int flage = 1;
  270. unsigned int temp = 0;
  271. printf("支持的停止位有:0,1,2 \n");
  272. do
  273. {
  274. if (flage == 1)
  275. {
  276. printf("请输入停止位:");
  277. flage = 0;
  278. }
  279. else
  280. printf("请重新输入停止位:");
  281. cin >> temp;
  282. cin.clear();
  283. cin.sync();
  284. } while (!Check_Input_Stop_Bits(date_bits, (BYTE)temp));
  285. stop_bits = (BYTE)temp;
  286. return stop_bits;
  287. }
  288. /*********************************************************************************************
  289. * 功能     :  选择校验位
  290. * 描述    : 获取串口通信的校验位
  291. * 输入 : 无
  292. * 返回值 : Parity 校验位
  293. **********************************************************************************************/
  294. BYTE Input_Parity(void)
  295. {
  296. BYTE parity;
  297. int flage = 1;
  298. unsigned int temp = 0;
  299. printf("支持的校验位有:0- 无校验 1- 奇校验 2- 偶校验 \n");
  300. do
  301. {
  302. if (flage == 1)
  303. {
  304. printf("请输入校验位:");
  305. flage = 0;
  306. }
  307. else
  308. printf("请重新输入校验位:");
  309. cin >> temp;
  310. cin.clear();
  311. cin.sync();
  312. } while (!(temp == 1 || temp == 0 || temp == 2));
  313. parity = (BYTE)temp;
  314. return parity;
  315. }
  316. /*********************************************************************************************
  317. * 功能    : 计算写入数据的字节数
  318. * 描述   : 通过对应的功能码和操作数量计算对应的数据字节数
  319. * 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量
  320. * 输出 : Read_date_number 写入数据的字节数
  321. *********************************************************************************************/
  322. unsigned int Count_Read_date_number(int function_code, unsigned int operations_number)
  323. {
  324. unsigned int read_date_number = 0;
  325. if (function_code == 0x01 || function_code == 0x0F) //读线圈
  326. {
  327. read_date_number = operations_number / 8;
  328. if (operations_number % 8)
  329. read_date_number++;
  330. }
  331. if (function_code == 0x03 || function_code == 0x10)//读寄存器
  332. read_date_number = operations_number * 2;
  333. return read_date_number;
  334. }
  335. /*********************************************************************************************
  336. * 功能     :  计算CRC校验
  337. * 描述    : 获取Modbus—CRC-16的校验数据
  338. * 输入 : *Data 计算校验数据 CRC_Len 数据长度
  339. * 返回值 : Ret_CRC_date CRC校验结果
  340. **********************************************************************************************/
  341. UINT16 CRC_16(UINT8 *data, unsigned int crc_len)
  342. {
  343. UINT16 crc_date = 0XFFFF;//16位crc寄存器预置
  344. UINT16 temp;
  345. unsigned int i = 0, j = 0;
  346. for (i = 0; i < crc_len; i++)
  347. {
  348. temp = *data & 0X00FF;//将八位数据与CRC寄存器亦或
  349. data++;
  350. crc_date ^= temp;
  351. for (j = 0; j < 8; j++)
  352. {
  353. if (crc_date & 0X0001)//判断右移出的是不是1,如果是1则与多项式进行异或。
  354. {
  355. crc_date >>= 1;
  356. crc_date ^= 0XA001;
  357. }
  358. else
  359. {
  360. crc_date >>= 1;
  361. }
  362. }
  363. }
  364. UINT16 ret_crc_date = crc_date >> 8;
  365. ret_crc_date = ret_crc_date | crc_date << 8;
  366. return ret_crc_date;
  367. }
  368. /*********************************************************************************************
  369. * 功能    : 日志记录
  370. * 描述   : 记录每次通信的请求和响应报文
  371. * 输入 : *Message 消息帧存放的数组 flage = 1 请求报文 flage = 其他 响应报文
  372. * Message_len 报文数据长度
  373. * 输出 : 无
  374. *********************************************************************************************/
  375. void Log_Note(UINT8 *Message, int flage, int message_len)
  376. {
  377. FILE *fp = NULL;
  378. time_t timep;
  379. struct tm *p;
  380. time(&timep);
  381. p = gmtime(&timep);
  382. string recv_str = to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday)
  383. + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Recv:";
  384. string send_str = to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday)
  385. + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Send:";
  386. /* 打开文件用于读写 */
  387. if ((fp = fopen("Modbus_TCP_log.txt", "a+")) == NULL)
  388. {
  389. printf("打开文件失败");
  390. return;
  391. }
  392. if (flage == 1)
  393. {
  394. fwrite(send_str.c_str(), send_str.length(), 1, fp);
  395. for (int i = 0; i < message_len; i++)
  396. {
  397. fprintf(fp, "%02X ", Message[i]);
  398. }
  399. fprintf(fp, "\n");
  400. }
  401. else
  402. {
  403. fwrite(recv_str.c_str(), recv_str.length(), 1, fp);
  404. for (int i = 0; i < message_len; i++)
  405. {
  406. fprintf(fp, "%02X ", Message[i]);
  407. }
  408. fprintf(fp, "\n");
  409. }
  410. fclose(fp);
  411. fp = NULL;
  412. }
  413. bool cmp(string s1, string s2)
  414. {
  415. if (atoi(s1.substr(3).c_str()) < atoi(s2.substr(3).c_str()))//升序
  416. return true;
  417. else
  418. return false;
  419. }
  420. void GetComList_Reg(std::vector<string>& comList)
  421. {
  422. HKEY hkey;
  423. int result;
  424. int i = 0;
  425. result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  426. _T("Hardware\\DeviceMap\\SerialComm"),
  427. NULL,
  428. KEY_READ,
  429. &hkey);
  430. if (ERROR_SUCCESS == result) // 打开串口注册表
  431. {
  432. TCHAR portName[0x100], commName[0x100];
  433. DWORD dwLong, dwSize;
  434. do
  435. {
  436. dwSize = sizeof(portName) / sizeof(TCHAR);
  437. dwLong = dwSize;
  438. result = RegEnumValue(hkey, i, portName, &dwLong, NULL, NULL, (LPBYTE)commName, &dwSize);
  439. if (ERROR_NO_MORE_ITEMS == result)
  440. {
  441. // 枚举串口
  442. break; // commName就是串口名字"COM4"
  443. }
  444. comList.push_back(commName);
  445. i++;
  446. } while (1);
  447. RegCloseKey(hkey);
  448. }
  449. }
  450. void GetComm_Name(void)
  451. {
  452. vector<string> comList;
  453. GetComList_Reg(comList);
  454. sort(comList.begin(), comList.end(), cmp);
  455. printf("当前可用端口:");
  456. for (unsigned int i = 0; i < comList.size(); i++)
  457. {
  458. cout << comList[i] << " ";
  459. }
  460. printf("\n");
  461. }
  462. HANDLE Input_Parameter(void)
  463. {
  464. HANDLE handle_com;
  465. while (true)
  466. {
  467. GetComm_Name();
  468. string COMM = Input_COMM();
  469. unsigned int baud_rate = Input_Baud_Rate();
  470. BYTE date_bits = Input_Date_Bits();
  471. BYTE stop_bits = Input_Stop_Bits(date_bits);
  472. BYTE parity = Input_Parity();
  473. handle_com = Init_COM((LPCTSTR)COMM.c_str(), baud_rate, date_bits, stop_bits, parity);
  474. if (handle_com == INVALID_HANDLE_VALUE)
  475. {
  476. cout << "初始化串口失败,请重新输入设备信息\n" << endl;
  477. }
  478. else
  479. {
  480. printf("初始化串口成功\n");
  481. break;
  482. }
  483. }
  484. return handle_com;
  485. }
  486. void Printf_Message(UINT8 *Message, int flage, int message_len)
  487. {
  488. if (flage == 1)
  489. printf("主站请求 :");
  490. else
  491. printf("从站响应 :");
  492. for (int i = 0; i < message_len; i++)
  493. {
  494. printf("%02X ", Message[i]);
  495. }
  496. printf("\n");
  497. if (LOG_NOTE_SWITCH)
  498. Log_Note(Message, flage, message_len);
  499. }