背景介绍

项目用到modbus-rtu通讯协议与三方平台通讯,由于三方平台没有寄存器地址点表信息,只提供了报文数据,故需要对报文进行二次解析,从而获得三方平台使用到的寄存器地址信息。

方案思路

报文示例

报文示例无包尾校验位,从站地址为1,数据位高前低后,一个寄存器占用两个字节。
01030001002B

从站地址 功能码 寄存器起始地址(高位) 寄存器起始地址(低位) 读取寄存器个数(高位) 读取寄存器个数(低位)
0x01 0x03 0x00 0x04 0x00 0x2B

010600580000

从站地址 功能码 寄存器地址(高位) 寄存器地址(低位) 写入值(高位) 写入值((低位)
0x01 0x06 0x00 0x58 0x00 0x00

0110003300020400010001

从站地址 功能码 寄存器起始地址(高位) 寄存器起始地址(低位) 设置寄存器个数(高位) 设置寄存器个数(低位) 字节数 字节1 字节2 字节3 字节4
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; ///< 文本输出 F710

void 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());
		}
	}

	///< F703
	vector<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());
	}


	///< F706
	vector<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());
	}


	///< F710
	vector<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实现下这个功能,还有余力的话,可以了解了解mapunordered_map两者的异同、优缺点等。
最后祝大家代码一把过,全都没bug!互相学习,共同进行!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐