「 Modbus-RTU报文解析」解析03、06、10功能码报文示例
文章目錄
- 背景介紹
- 方案思路
- 報文示例
- 需求分解
- 簡要思路
- 代碼示例
- 共贏共享
- 番外
背景介紹
項目用到modbus-rtu通訊協議與三方平臺通訊,由于三方平臺沒有寄存器地址點表信息,只提供了報文數據,故需要對報文進行二次解析,從而獲得三方平臺使用到的寄存器地址信息。
方案思路
報文示例
報文示例無包尾校驗位,從站地址為1,數據位高前低后,一個寄存器占用兩個字節。
01030001002B
| 0x01 | 0x03 | 0x00 | 0x04 | 0x00 | 0x2B |
010600580000
| 0x01 | 0x06 | 0x00 | 0x58 | 0x00 | 0x00 |
0110003300020400010001
| 0x01 | 0x10 | 0x00 | 0x33 | 0x00 | 0x02 | 0x04 | 0x00 | 0x01 | 0x00 | 0x01 |
需求分解
1.三方平臺報文以excel形式提供;
2.三方平臺報文里很多重復報文需要剔除;
3.統計輸出時希望可以按照順序輸出,便于統計與觀看;
簡要思路
需要根據提供的報文,解析出modbus主站所使用的寄存器地址與個數,方便點表的統計與維護。
1.把excel報文復制到txt文檔中,解析程序讀取txt文檔,這樣方便后期其他報文導入解析,只需要替換txt文檔即可,程序靈活,可擴展性強;
2.報文有重復,需要去重,由于我近期使用unordered_map比較多,所以使用了unordered_map,其實map更合適;
3.由于使用了unordered_map,所以還需要做下排序處理;
代碼示例
#include <stdio.h> #include <string.h> #include <vector> #include <iostream> #include <cctype> #include <unordered_map> #include <algorithm>using namespace std;vector<string> str_apps_input; ///< 文本輸入///< 利用unordered_map實現去重 unordered_map<string,int> str_apps_output_F703; ///< 文本輸出 F703 unordered_map<string,int> str_apps_output_F706; ///< 文本輸出 F706 unordered_map<string,int> str_apps_output_F710; ///< 文本輸出 F710void parseProtocolFile(const char *input_file); int hex2dec(char *hex); int c2i(char ch);///< 對unordered_map進行排序 bool comp(const pair<string, int>& a, const pair<string, int> &b) {return a.second < b.second; }int main(int argc, char ** argv) {int count = 0;//打開本地文件,解析報文char filepath[128] = "./test.txt";parseProtocolFile(filepath);int n = str_apps_input.size();for (int i = 0; i < n ; i++){//printf("(*str_apps[i].c_str()) is %s\n", *str_apps[i].c_str());char str_addr1[16] = { 0 };char str_addr2[16] = { 0 };char str_addr3[16] = { 0 };char str_addr[128] = { 0 };if (str_apps_input[i].find("F703") != string::npos){sprintf_s(str_addr1, "%c%c", *((char*)(str_apps_input[i].c_str()) + 4), *((char*)(str_apps_input[i].c_str()) + 5));sprintf_s(str_addr2, "%c%c", *((char*)(str_apps_input[i].c_str()) + 6), *((char*)(str_apps_input[i].c_str()) + 7));sprintf_s(str_addr3, "%c%c", *((char*)(str_apps_input[i].c_str()) + 10), *((char*)(str_apps_input[i].c_str()) + 11));int address = hex2dec(str_addr1) * 256 + hex2dec(str_addr2);int number = hex2dec(str_addr3);sprintf_s(str_addr, "address is %d, number is %d\n", address, number);str_apps_output_F703[str_addr] = address;}else if (str_apps_input[i].find("F706") != string::npos){sprintf_s(str_addr1, "%c%c", *((char*)(str_apps_input[i].c_str()) + 4), *((char*)(str_apps_input[i].c_str()) + 5));sprintf_s(str_addr2, "%c%c", *((char*)(str_apps_input[i].c_str()) + 6), *((char*)(str_apps_input[i].c_str()) + 7));int address = hex2dec(str_addr1) * 256 + hex2dec(str_addr2);sprintf_s(str_addr, "address is %d\n", address);str_apps_output_F706[str_addr] = address;}else if (str_apps_input[i].find("F710") != string::npos){sprintf_s(str_addr1, "%c%c", *((char*)(str_apps_input[i].c_str()) + 4), *((char*)(str_apps_input[i].c_str()) + 5));sprintf_s(str_addr2, "%c%c", *((char*)(str_apps_input[i].c_str()) + 6), *((char*)(str_apps_input[i].c_str()) + 7));sprintf_s(str_addr3, "%c%c", *((char*)(str_apps_input[i].c_str()) + 10), *((char*)(str_apps_input[i].c_str()) + 11));int address = hex2dec(str_addr1) * 256 + hex2dec(str_addr2);int number = hex2dec(str_addr3);sprintf_s(str_addr, "address is %d, number is %d\n", address, number);str_apps_output_F710[str_addr] = address;}else{printf("can not parse:%s\n", str_apps_input[i].c_str());}}///< F703vector<pair<string, int>> vec_F703;for (auto x : str_apps_output_F703){vec_F703.push_back(x);}sort(vec_F703.begin(), vec_F703.end(),comp);printf("\nF703 protocol\n");for (auto x : vec_F703){printf("%s", x.first.c_str());}///< F706vector<pair<string, int>> vec_F706;for (auto x : str_apps_output_F706){vec_F706.push_back(x);}sort(vec_F706.begin(), vec_F706.end(), comp);printf("\nF706 protocol\n");for (auto x : vec_F706){printf("%s", x.first.c_str());}///< F710vector<pair<string, int>> vec_F710;for (auto x : str_apps_output_F710){vec_F710.push_back(x);}sort(vec_F710.begin(), vec_F710.end(), comp);printf("\nF710 protocol\n");for (auto x : vec_F710){printf("%s", x.first.c_str());}///< 解析規約報文文件 void parseProtocolFile(const char *map_file) {FILE* fp_cfg;if (0 == (fopen_s(&fp_cfg,(const char*)map_file, (const char*)"rt"))){char line[1024] = { 0 };void* ret = NULL;while (true) {memset(line, 0, sizeof(line));ret = fgets(line, sizeof(line), fp_cfg);if (ret == NULL) break;if (line[strlen(line) - 1] == '\n' || line[strlen(line) - 1] == '\r') {line[strlen(line) - 1] = '\0';}if (line[strlen(line) - 1] == '\r' || line[strlen(line) - 1] == '\n') {line[strlen(line) - 1] = '\0';}//printf("%s\n", line);str_apps_input.push_back(line);}fclose(fp_cfg);} }int c2i(char ch) {if (isdigit(ch))return ch - 48;if (ch < 'A' || (ch > 'F' && ch < 'a') || ch > 'z')return -1;if (isalpha(ch))return isupper(ch) ? ch - 55 : ch - 87;return -1; }int hex2dec(char *hex) {int len;int num = 0;int temp;int bits;int i;len = strlen(hex);for (i = 0, temp = 0; i < len; i++, temp = 0){temp = c2i(*(hex + i));bits = (len - i - 1) * 4;temp = temp << bits;num = num | temp;}// 返回結果 return num; }共贏共享
示例代碼已上傳,可運行。vs2019工程可運行,供參考
番外
由于我這里需求只需要寄存器地址與個數,所以只解析了部分。有全部解析需求的同學可以在此基礎上做二次開發。
1.針對技術方案選型,我分享幾點想法吧。考慮到程序的適應性,我選擇了從文件讀取數據的方式,這樣比較靈活,只要把海量的數據拷貝進文件即可,無需其他復雜的操作。后期還方便其他功能碼報文的導入與解析,只需要加解析分支即可。
2.針對亂序報文的情況,我采用預篩選的策略,讀取報文文件時按行讀取內容,根據報文類型,插入到對應的unordered_map中,便于后面打印輸出;
3.另外針對去重與排序的需求點,應該是使用map更合適,有興趣的同學可以使用map實現下這個功能,還有余力的話,可以了解了解map、unordered_map兩者的異同、優缺點等。
最后祝大家代碼一把過,全都沒bug!互相學習,共同進行!
總結
以上是生活随笔為你收集整理的「 Modbus-RTU报文解析」解析03、06、10功能码报文示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你可能学了假流程图,三步教会你绘制大厂流
- 下一篇: 「 每日一练,快乐水题 」258. 各位