首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >环保 HJ212协议解析

环保 HJ212协议解析

作者头像
ccf19881030
发布2020-09-22 10:21:06
2.6K0
发布2020-09-22 10:21:06
举报
文章被收录于专栏:ccf19881030的博客ccf19881030的博客

由于是做环保相关的,有时需要对212协议进行拆包和解包。HJ212协议是一种字符串协议,数据传输通讯包主要由包头、数据段长度、数据段、CRC校验、包尾组成,其中“数据段”内容包括请求编码、系统编码、命令编码、密码、设备唯一标识、总包数、包号、指令参数。请求编码为请求的时间戳,系统编码ST统一规定为22,命令编码CN为该数据包的时间类型,访问密码、设备唯一标识在对接时由平台提供,指令参数为数据内容。通讯协议的数据结构如图4所示。

通讯协议的数据结构
通讯协议的数据结构

图4 通讯协议的数据结构

6.1.1通讯包结构组成

名称

类型

长度

描述

包头

字符

2

固定为##

数据段长度

十进制整数

4

数据段的ASCII字符数。例如数据段的字符数为128,则写为“0128”

数据段

字符

0<=n<=9999

变长的数据

CRC校验

十六进制

4

数据段的校验结果,例如C901,如果CRC错,即执行超时

包尾

字符

2

回车换行(\r\n)

下面是一个使用C++写的212解析类 GB212: GB212.h

#pragma once
#include <type.h>
#include <func.h>

// 国标 212
// 所有的通讯包都是由ASCII码
class GB212
{
public:
	typedef std::vector<GB212> GB212Array;
	typedef std::vector<StringMap> DataItemArray;
	// 切分数据
	static void split_kv(DataItemArray& _return, const String& cp) {
		auto arr1 = Math::Tools::split(cp, ";", true);
		for (auto& i : arr1) {
			StringMap item;
			auto arr2 = Math::Tools::split(i, ",", true);
			for (auto& j : arr2) {
				auto arrkv = Math::Tools::split(j, "=", false);
				if (arrkv.size() == 2) {
					item.insert(std::make_pair(arrkv[0], arrkv[1]));
				}
			}
			_return.emplace_back(item);
		}
	}
	// 组合数据
	static String join_kv(const DataItemArray& arr) {
		StringArray item;
		for (auto& i : arr) {
			StringArray arrkv;
			for (auto& j : i) {
				arrkv.emplace_back(j.first + "=" + j.second);
			}
			item.emplace_back(Math::Tools::join(arrkv, ","));
		}
		return Math::Tools::join(item, ";");
	}
	// 数据区
	struct DataCP {
		DataCP() {}
		DataCP(const String& s) {
			DataItemArray arr;
			split_kv(arr, s);
			for (auto& i : arr) {
				auto kvlen = i.size();
				String key;
				for (auto& j : i) {
					// 对指定监测因子的项,统一使用因子代表
					if (j.first == "PolId") {
						key = j.second;
						this->Value[key] = StringMap();
						continue;
					}
					String name, field;
					// 查询是否包含标准协议中的设备状态
					auto f1 = j.first.find("SB");
					auto f2 = j.first.find("-");
					if (f2 != String::npos && f1 != 0) {
						// a20004-Rtd   name-field
						name = j.first.substr(0, f2);
						field = j.first.substr(f2 + 1);
						// i11001-Info   field
						if (field == "Info") {
							field = name;
						}
						else {
							key = name;
						}
					}
					else {
						if (j.first == "DataTime") {
							this->DataTime = j.second;
						}
						name = j.first;
						key = name;
						field = "value";
					}
					this->Value[key].insert(std::make_pair(field, j.second));
				}
			}
			// 如果不包含DataTime字段,则将当前时间作为数据时间
			if (this->Value.find("DataTime") == this->Value.end()) {
				this->DataTime = Math::Date::getnow("%04d%02d%02d%02d%02d%02d");
			}
		}
		void clear() {
			this->Value.clear();
		}
		std::map<String, StringMap> Value;
		String DataTime;
	};
	// 数据段
	struct Data {
		String QN;		// 请求编码 20字符	QN=YYYYMMDDHHmmssZZZ
		String ST;		// 系统编码 5字符		ST=21
		String CN;		// 命令编码 7字符		CN=2011
		String PW;		// 访问密码 9字符		PW=123456
		String MN;		// 设备标识 27字符	MN=[0-9A-F]
		String Flag = "4";	// 标志位   8整数		Flag=7	bit:000001(协议版本)0(是否有包号)0(是否需应答)
		String PNUM;	// 总包数   9字符		PNUM=0000	[不分包则没有本字段]
		String PNO;		// 包号     8字符	PNO=0000	[不分包则没有本字段]
		String CP;		// 指令参数	<=950字符	CP=&&数据区&&
		DataCP CPs;
		// 设置flag, bit从1开始
		void set_flag(uint32 bit, bool enable) {
			int32 flag = 4;
			Math::Tools::to_int(flag, Flag);
			enable ? SETBIT(flag, bit) : CLRBIT(flag, bit);
			this->Flag = std::to_string(flag);
		}
		// 获取flag, bit从1开始
		bool get_flag(uint32 bit) const {
			int32 flag = 4;
			Math::Tools::to_int(flag, Flag);
			return GETBIT(flag, bit);
		}
		// 有效性
		bool valid() const {
			return bvalid;
		}
		// 长度
		size_t size() const {
#define ADDDataItem(name, subnum) result += name.size() + (name.empty() ? 0 : subnum);
			size_t result = 0;
			ADDDataItem(QN, 3);
			ADDDataItem(ST, 3);
			ADDDataItem(CN, 3);
			ADDDataItem(PW, 3);
			ADDDataItem(MN, 3);
			ADDDataItem(Flag, 5);
			ADDDataItem(PNUM, 5);
			ADDDataItem(PNO, 4);
			ADDDataItem(CP, 3);
#undef ADDDataItem
			return QN.size() + ST.size() + CN.size() + PW.size() + MN.size() + Flag.size() +
				PNUM.size() + PNO.size() + CP.size();
		}
		// 构造函数
		Data() {}
		Data(const String& s){
			CopyStr(s);
		}
		// 赋值构造
		Data& operator=(const String& str) {
			CopyStr(str);
			return *this;
		}
		void CopyStr(const String& s) {
			auto d1 = s.find("CP=&&");
			auto d2 = s.find("&&", d1 + 5);
			String tmp;
			if (d1 != String::npos && d2 != String::npos) {
				CP = s.substr(d1 + 5, d2 - d1 - 5);
				CPs = CP;
				tmp = s.substr(0, d1) + s.substr(d2 + 2);
			}
			else {
				tmp = s;
			}
			StringMap it;
			auto arr1 = Math::Tools::split(tmp, ";", true);
			for (auto& i : arr1) {
				auto arr2 = Math::Tools::split(i, "=", false);
				if (arr2.size() == 2) {
					it.insert(std::make_pair(arr2[0], arr2[1]));
				}
			}
#define SETDataItem(name) name = it[#name];
			SETDataItem(QN);
			SETDataItem(ST);
			SETDataItem(CN);
			SETDataItem(PW);
			SETDataItem(MN);
			SETDataItem(Flag);
			SETDataItem(PNUM);
			SETDataItem(PNO);
#undef SETDataItem
			bvalid = true;
		}
		// 获取数据项
		void CP2Object(DataItemArray& _return) const {
			GB212::split_kv(_return, CP);
		}
		// 字符串输出
		String toString() const {
			String str;
#define StrDataItem(name) if (name.size()) str += (#name"=" + name + ";");
			StrDataItem(QN);
			StrDataItem(ST);
			StrDataItem(CN);
			StrDataItem(PW);
			StrDataItem(MN);
			StrDataItem(Flag);
			StrDataItem(PNUM);
			StrDataItem(PNO);
#undef StrDataItem
			str += ("CP=&&" + CP + "&&");
			return str;
		}

	private:
		bool bvalid = false;
	};
	String	header = "##";		// 包头	2字符
	String	datalen = "0000";	// 数据段长度		4整数,如长100,写为"0100"
	Data	data;				// 数据段	<=1024
	String	crc = "0000";		// CRC校验	4hex
	String	tailer = "\r\n";	// 包尾	2字符

	String	full;	// 全数据

	// 输出
	String toString() const {
		auto datastr = data.toString();
		uint16 jisuan_crc16 = Math::Byte::crc16_checksum((uint8*)datastr.data(), datastr.size());
		char out[2048];
		sprintf(out, "##%04d%s%04X\r\n", datastr.size(), datastr.c_str(), jisuan_crc16);
		return out;
	}
	// 有效性
	bool valid() const {
		return bvalid;
	}
	// 有效性
	bool is_data() const {
		return this->data.CN == "2011" || this->data.CN == "2051" || this->data.CN == "2061" 
			|| this->data.CN == "2031" || this->data.CN == "2041" || this->data.CN == "3020";
	}
	// 长度
	size_t size() const {
		return header.size() + datalen.size() + data.size() + crc.size() + tailer.size();
	}
	// 清空数据区
	void clear_cp() {
		this->data.CP.clear();
		this->data.CPs.clear();
	}
	// 设置是否需应答
	void set_need_reply(bool need) {
		data.set_flag(1, need);
	}
	// 设置是否有包号
	void set_need_subpack(bool need) {
		data.set_flag(2, need);
		if (!need) {
			data.PNUM.clear();
			data.PNO.clear();
		}
	}
	// 是否需应答
	bool is_need_reply() const {
		return data.get_flag(1);
	}
	// 是否有包号
	bool is_need_subpack() {
		return data.get_flag(2);
	}

	// 数据区。字段与其值用‘=’连接;
	// 在数据区中,同一项目的不同分类值间用‘,’来分隔,不同项目之间 用‘;’来分隔。
	void combine() {
		this->datalen = Math::Tools::to_string("%04d", this->data.size());
		auto datastr = this->data.toString();
		uint16 jisuan_crc = Math::Byte::crc16_checksum((uint8*)datastr.data(), datastr.size());
		this->crc = Math::Tools::to_string("%04X", jisuan_crc);
		this->bvalid = true;
	}

private:
	GB212(const String& str) {
		this->full = str;
		size_t total_size = str.size();
		datalen = str.substr(header.size(), datalen.size());
		int32 data_len = 0;
		Math::Tools::to_int(data_len, datalen);
		if ((10 + data_len) > total_size/* || data_len == 0*/) {
			return;
		}
		auto datastr = str.substr(header.size() + datalen.size(), data_len);
		data = datastr;
		crc = str.substr(header.size() + datalen.size() + data_len, crc.size());
		uint16 jisuan_crc16 = Math::Byte::crc16_checksum((uint8*)datastr.data(), datastr.size());
		int32 src_crc16;
		if (!Math::Tools::to_int(src_crc16, crc, 16)) {
			return;
		}
		if (src_crc16 != jisuan_crc16) {
			return;
		}
		bvalid = true;
	}

	bool bvalid = false;

public:
	GB212(const GB212& r) {
		this->header = r.header;
		this->datalen = r.datalen;
		this->data = r.data;
		this->crc = r.crc;
		this->tailer = r.tailer;
		this->full = r.full;
		this->bvalid = r.bvalid;
	}
	GB212(const String& st, const String& cn, const String& mn, const String& pw, const String& cp, bool need_reply) {
		this->data.QN = Math::Date::getnow("%04d%02d%02d%02d%02d%02d000");
		this->data.ST = st;
		this->data.CN = cn;
		this->data.MN = mn;
		this->data.PW = pw;
		this->data.CP = cp;
		this->set_need_reply(need_reply);
		this->combine();
	}
	// 获取响应报文
	GB212 data_res(const char* cn) const {
		GB212 out(*this);
		out.data.ST = "91";
		out.data.CN = cn;
		out.clear_cp();
		out.combine();;
		return out;
	}
	// 解析
	static void Parser(GB212Array& _return, const String& buffer) {
		auto buffsize = buffer.size();
		String it;
		for (int i = 0; i < buffsize; i++)
		{
			if (i < buffsize - 1 && buffer[i] == '#' && buffer[i + 1] == '#') {
				if (it.size()) {
					_return.emplace_back(GB212(it));
					it.clear();
				}
			}
			it += (char)buffer[i];
			if (i > 0 && buffer[i - 1] == '\r' && buffer[i] == '\n') {
				if (it.size()) {
					_return.emplace_back(GB212(it));
					it.clear();
				}
			}
		}
		if (it.size()) {
			_return.emplace_back(GB212(it));
			it.clear();
		}
	}
};

相关的type.h类型定义文件如下:

#ifndef _XM_DATA_TYPE_H_
#define _XM_DATA_TYPE_H_

// 自定义
typedef unsigned char		uint8;
typedef unsigned short		uint16;
typedef unsigned int		uint32;
#ifdef WIN32
typedef unsigned __int64	uint64;
typedef __int64	 int64;
#else
typedef unsigned long long	uint64;
typedef long long	int64;
#endif
typedef char	int8;
typedef short	int16;
typedef int		int32;

#include <string.h>

// 数组
#include <string>
#include <vector>
#include <map>
#include <list>
#include <functional>
typedef std::string	String;
typedef std::vector<uint8>		Uint8Array;
typedef std::vector<uint16>		Uint16Array;
typedef std::vector<uint32>		Uint32Array;
typedef std::vector<int8>		Int8Array;
typedef std::vector<int16>		Int16Array;
typedef std::vector<int32>		Int32Array;
typedef std::vector<int64>		Int64Array;
typedef std::vector<uint64>		Uint64Array;
typedef std::vector<float>		Float32Array;
typedef std::vector<double>		Float64Array;
typedef std::vector<std::string>	StringArray;
typedef std::map<std::string, std::string> StringMap;
typedef std::map<int32, std::string> Int32StringMap;
typedef std::list<std::string> StringList;
typedef std::vector<Uint8Array> Uint8sArray;


typedef std::function<bool(const char* topic, const char* payload, int payload_size, int qos)> publish_handler;
typedef std::function<void(const char* topic, const char* payload, int payload_size, int sid)> subscribe_handler;

typedef std::function<int(const Uint8Array& req, Uint8Array& res)> request_handler;

typedef std::function<void(int type, const String& data)> data_handler; 

typedef std::function<void(const char* topic, const void* payload, size_t payload_size)> handler_subscribe;

typedef std::function<void()> handler_empty;

typedef std::function<void(void* d)> handler_void;

#include "type_define.h"
typedef void(*log_cb)(const char* file, const char* func, long line, log_level level, bool need_send, const char* msg);

#include <thread>
typedef std::thread Thread;

#include <chrono>
// 休眠毫秒
#define msleep(millsec) std::this_thread::sleep_for(std::chrono::milliseconds(millsec));
// 休眠秒
#define ssleep(sec) std::this_thread::sleep_for(std::chrono::seconds(sec));


#define macroStr(s) #s

// 获取16位数据的高8位
#define GET16H(d)		((d)>>8&0xff)
// 获取16位数据的低8位
#define GET16L(d)		((d)&0xff)
// 通过高低8位构造16位数据
#define GET16T(h,l)		(((h)<<8&0xff00)|((l)&0xff))
// 交换16位数据的高低8位获取数据
#define GET16S(d)		GET16T(GET16L(d),GET16H(d))

#define GET32H(d)		((d)>>16&0xffff)
#define GET32L(d)		((d)&0xffff)

// 将对应位置1, n从1开始
#define SETBIT(x, n) (x |= 1u << (n-1))
// 将对应位置零, n从1开始
#define CLRBIT(x, n) (x &= ~(1u << (n-1)))
// 取对应位数, n从1开始
#define GETBIT(x, n) (x & 1u << (n-1))

#ifdef WIN32
#ifndef snprintf
#define snprintf sprintf_s
#endif
#endif


// 类宏
#define CLASS_DISCOPY(Class) \
Class(const Class&) = delete;\
Class& operator=(const Class&) = delete;


// 类成员的set,get函数定义
#define	PROERTY_DEFINE(type, var) void set_##var(const type var); const type get_##var() const;

// 类成员的set,get函数实现
#define	PROERTY_FUNC(type, var) void set_##var(const type var) { var##_ = var; } const type get_##var() const { return var##_; }

// 类成员的set,get函数实现
#define	PROERTY_CLASS(Class, type, var) void Class::set_##var(const type var) { d_ptr->set_##var(var); } const type Class::get_##var() const { return d_ptr->get_##var(); }


// 删除内存
#define ReleaseObj(ptr) if(ptr) delete ptr;
#define ReleaseObj2(ptr) if(ptr) { delete ptr; ptr = nullptr; }


#endif

功能呢函数定义文件func.h如下所示:

#pragma once
#include "type.h"
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif

namespace Math
{
	class Byte
	{
	public:
		// bytes转换成BCD码
		static Uint8Array bytes2bcd(const Uint8Array& data)
		{
			Uint8Array out;
			// 比如 20 18 转换成 0x20 0x18
			for (size_t i = 0; i < data.size(); i++)
			{
				const uint8 d = data[i];
				out.push_back(d / 10 * 16 + d % 10);
			}
			return out;
		}

		// BCD码转换成bytes
		static Uint8Array bcd2bytes(const Uint8Array& data)
		{
			Uint8Array out;
			// 比如 0x20 0x18 转换成 20 18
			for (size_t i = 0; i < data.size(); i++)
			{
				const uint8 d = data[i];
				out.push_back(d / 16 * 10 + d % 16);
			}
			return out;
		}

		// BCD码转换成bytes
		static Uint8Array bcd2bytes(const uint8* buff, uint32 buffsize)
		{
			Uint8Array out(buff, buff + buffsize);
			return bcd2bytes(out);
		}

		// 计算16位校验和
		static uint16 getsum(const uint8* buff, uint32 buffsize)
		{
			uint32 sum = 0;
			for (uint32 i = 0; i < buffsize; i++)
			{
				sum += (uint8)buff[i];
			}
			return sum;
		}

		// 计算32位校验和
		static uint32 getsum32(const uint8* buff, uint32 buffsize)
		{
			uint32 sum = 0;
			for (uint32 i = 0; i < buffsize; i++)
			{
				sum += (uint8)buff[i];
			}
			return sum;
		}

		// 计算CRC16循环冗余校验
		static uint16 crc16_checksum(uint8 *puchMsg, uint32 usDataLen)
		{
			uint32 crc_reg, check;
			crc_reg = 0xFFFF;
			for (uint32 i = 0; i < usDataLen; i++) {
				crc_reg = (crc_reg >> 8) ^ puchMsg[i];
				for (uint32 j = 0; j < 8; j++) {
					check = crc_reg & 0x0001;
					crc_reg >>= 1;
					if (check == 0x0001) {    
						crc_reg ^= 0xA001;   
					}
				}
			} 
			return crc_reg;
		}
	};
	
	// 时间
	class Date
	{
	public:
		// 获取unix时间戳
		static time_t now_unix() {
			return time(0);
		}
		// 获取本地时间tm
		static tm gettm(const time_t unix_time = 0)
		{
			time_t t = unix_time == 0 ? time(0) : unix_time;
			tm tt;
#ifdef WIN32
			localtime_s(&tt, &t);
#else
			localtime_r(&t, &tt);
#endif
			return tt;
		}
		// 获取当前时间的BCD码20190308150102
		static Uint8Array getbcd(const time_t unix_time = 0)
		{
			time_t t = unix_time <= 0 ? time(0) : unix_time;
			Uint8Array _return;
			tm tt = gettm(t);
			int year = tt.tm_year + 1900;
			_return.push_back(year / 100);
			_return.push_back(year % 100);
			_return.push_back(tt.tm_mon + 1);
			_return.push_back(tt.tm_mday);
			_return.push_back(tt.tm_hour);
			_return.push_back(tt.tm_min);
			_return.push_back(tt.tm_sec);
			return Byte::bytes2bcd(_return);
		}
		// 将unix时间戳转换成本地时间, fmt默认格式 %04d-%02d-%02d %02d:%02d:%02d
		static std::string unix2str(const time_t unix_time, const char* fmt = "%04d-%02d-%02d %02d:%02d:%02d")
		{
			tm tt = gettm(unix_time);
			char date[64];
			snprintf(date, sizeof(date), fmt, tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec);
			return date;
		}
		// 获取当前时间字符串,默认格式 2018-10-01 01:10:20
		static std::string getnow(const char* fmt = "%04d-%02d-%02d %02d:%02d:%02d")
		{
			return unix2str(::time(0), fmt);
		}

		// 转换字符串时间的格式, 输入顺序需和输出顺序一致, %04d-%02d-%02d %02d:%02d:%02d
		static std::string convertfmt(const char* indatetime, const char* infmt, const char* outfmt)
		{
			int year, month, day, hour, minute, second;
			sscanf(indatetime, infmt, &year, &month, &day, &hour, &minute, &second);
			char out[64];
			sprintf(out, outfmt, year, month, day, hour, minute, second);
			return out;
		}

		// 获取输入字符串时间时间戳,默认格式 2018-10-01 01:10:20, %04d-%02d-%02d
		static time_t getunix(const char* indatetime, const char* infmt = "%04d-%02d-%02d %02d:%02d:%02d")
		{
			int year, month, day, hour, minute, second;
			sscanf(indatetime, infmt, &year, &month, &day, &hour, &minute, &second);
			return gettime(year, month, day, hour, minute, second);
		}

		// 构造时间
		static time_t gettime(int year, int month, int day, int hour = 0, int minute = 0, int second = 0)
		{
			tm tt = { 0 };
			tt.tm_year = year - 1900;
			tt.tm_mon = month - 1;
			tt.tm_mday = day;
			tt.tm_hour = hour;
			tt.tm_min = minute;
			tt.tm_sec = second;
			return mktime(&tt);
		}

	};

	//  定时器
	class Timer
	{
		// 毫秒时钟
		typedef std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> millisecClock_type;
	public:
		// 秒
		static time_t now_s() {
			return std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
		}
		// 毫秒
		static time_t now_ms() {
			return std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
			// return system_clock::to_time_t(time_point_cast<milliseconds>(system_clock::now()));
		}
		// 微秒
		static time_t now_mms() {
			return std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
		}
		// 条件休眠,单位秒
		static inline void sleep_if(volatile bool& condition, int32 sec) {
			auto t0 = now_s();
			while (condition && (now_s() - t0) < sec)
			{
				msleep(10);
			}
		}
		// 条件休眠,单位毫秒
		static inline void usleep_if(volatile bool& condition, int32 millsec) {
			auto t0 = now_ms();
			while (condition && (now_ms() - t0) < millsec)
			{
				msleep(10);
			}
		}
	public:
		typedef std::function<void()> timer_handler;
		void start(timer_handler func, int timeout_ms)
		{
			cancel();
			continue_ = true;
			auto t1 = Timer::now_ms();
			thread_ = std::thread([this, func, t1, timeout_ms]() {
				while (continue_ && (Timer::now_ms() - t1 < timeout_ms))
				{
					msleep(10);
				}
				func();
			});
		}
		void cancel()
		{
			continue_ = false;
			if (thread_.joinable()) {
				thread_.join();
			}
		}

	private:
		std::thread		thread_;
		volatile bool	continue_ = false;
	};

	// 文件相关
	class File
	{
	public:
		// 多级创建文件夹,path不包含文件名
		static bool mk_dirs(const char* path)
		{
			if (!path) {
				return false;
			}
			int32 len = strlen(path);
			if (len > 256) {
				return false;
			}
			const char* p1 = path;
			const char* p2;
			while (p1 - path < len)
			{
				p2 = strchr(p1, '/');
				if (!p2) {
					p2 = strchr(p1, '\\');
				}
				if (!p2) {
					p2 = path + len;
				}
				char dir[64] = { 0 };
				memcpy(dir, path, p2 - path + 1);
#ifdef _WIN32
				if (_access(dir, 0) == 0 || _mkdir(dir) == 0)
#else
				if (access(dir, R_OK | W_OK) == 0 || mkdir(dir, 0666) == 0)
#endif
				{
					p1 = p2 + 1;
					continue;
				}
				return false;
			}
			return true;
		}
		// 文件是否存在
		static bool exist(const char* path) {
#ifdef _WIN32
			return _access(path, 0) == 0;
#else
			return access(path, R_OK | W_OK) == 0;
#endif
		}
		// 删除文件
		static bool remove(const char* filename) {
			return ::remove(filename) == 0;
		}
		// 重命名文件
		static bool rename(const char* oldfile, const char* newfile) {
			return ::rename(oldfile, newfile) == 0;
		}
	};

	class Tools
	{
	public:
		// 将数字类型转成字符类型. fmt同printf中的fmt
		static inline String to_string(const char* fmt, const uint32 val)
		{
			char tmp[256];
			sprintf(tmp, fmt, val);
			return tmp;
		}
		// 替换字符
		static inline String replace(const String& str, const String& search, const String& dest)
		{
			String result;
			size_t start = 0, end = 0;	
			while (start < str.size())
			{
				end = str.find_first_of(search, start);
				result += str.substr(start, end - start);
				start = end + search.size();
				if (end == String::npos) {
					break;
				}
				result += dest;
			}
			return result;
		}

		// 字符串分割函数
		static inline StringArray split(const std::string& s, const std::string& sep, const bool compress = false)
		{
			StringArray out;
			int pos = 0;
			int index = -1;
			while ((index = s.find(sep, pos)) != s.npos)
			{
				if (index - pos == 0)
				{

				}
				// s.substr(pos, index - pos == 0 ? 1 : index - pos);
				std::string it = index - pos == 0 ? "" : s.substr(pos, index - pos);
				if (compress && it == "") // 压缩 index - pos == sep.size() && 
				{

				}
				else // 不用压缩
				{
					out.push_back(it);
				}

				pos = index + sep.size();
			}
			if (pos < (int32)s.size())
			{
				out.push_back(s.substr(pos));
			}
			else if (pos == (int32)s.size() && !compress)
			{
				out.push_back("");
			}
			return out;
		}

		// 数据组合字符串
		static inline String join(const StringArray& arr, const std::string& sep) {
			String result;
			for (auto& i : arr) {
				if (result.empty()) {
					result = i;
					continue;
				}
				result += sep + i;
			}
			return result;
		}
		
		// 转换hex到字符串显示
		static inline String hex2str(const char* buff, const size_t buffsize, const char* sep = "", bool is_case = false) {
			String out;
			char ch[4];
			const char* fmt = is_case ? "%02x" : "%02X";
			for (size_t i = 0; i < buffsize; i++) {
				sprintf(ch, fmt, buff[i] & 0xFF);
				if (out.empty()) {
					out = ch;
				}
				else {
					out += sep;
					out += ch;
				}
			}
			return out;
		}

		// 转换字符串显示到hex数组
		static inline String str2hex(const String& buff, const String& sep = "") {
			String out;
			size_t buffsize = buff.size();
			StringArray items;
			if (sep.empty() && buffsize % 2 == 0) {
				for (size_t i = 0; i < buffsize / 2; i++) {
					items.emplace_back(buff.substr(i * 2, 2));
				}
			}
			else if (sep.size()) {
				items = split(buff, sep, true);
			}
			for (auto& i : items) {
				int ch;
				if (!to_int(ch, i, 16)) {
					return out;
				}
				out.push_back(ch & 0xff);
			}
			return out;
		}

		// 获取字符串中数字
		static inline bool to_int(int& _return, const std::string& buff, int base = 10, size_t offset = 0, size_t count = String::npos)
		{
			_return = 0;
			if (buff.empty())
				return false;
			try {
				_return = std::stoi(buff.substr(offset, count), 0, base);
			}
			catch (...) {
				return false;
			}
			return true;
		}

		// 截取字符串buff中从offset开始的count个字符转换成double
		static inline bool to_double(double& _return, const std::string& buff, size_t offset = 0, size_t count = String::npos)
		{
			_return = 0;
			if (buff.empty())
				return false;
			try {
				_return = std::stod(buff.substr(offset, count));
			}
			catch (...) {
				return false;
			}
			return true;
		}

		// 截取字符串buff中从offset开始的count个字符转换成float
		static inline bool to_float(float& _return, const std::string& buff, size_t offset = 0, size_t count = String::npos)
		{
			_return = 0;
			if (buff.empty())
				return false;
			try {
				_return = std::stof(buff.substr(offset, count));
			}
			catch (...) {
				return false;
			}
			return true;
		}

		// 去掉尾部的特定字符
		static inline String trim(const String& buffer, const String& sep = " ") {
			String result;
			size_t pos = 0;
			do
			{
				size_t n = buffer.find(sep, pos);
				result += buffer.substr(pos, n - pos);
				pos = (n != String::npos) ? (n + sep.size()) : n;
			} while (pos < buffer.size());
			return result;
		}
	};
}

使用GB212类进行212协议包的解析,

例如有如下212协议数据报文:

##0285QN=20190925181031464;ST=22;CN=2061;PW=BF470F88957588DE902D1A52;MN=Z13401000010301;Flag=5;CP=&&DataTime=20190924220000;a34006-Avg=2.69700,a34006-Flag=N;a34007-Avg=7.96600,a34007-Flag=N;a34048-Avg=3.30600,a34048-Flag=N;a34047-Avg=7.35700,a34047-Flag=N;a34049-Avg=10.66300,a34049-Flag=N&&C181\r\n

相关解析代码如下:

	// parse gb212 format data
	GB212::GB212Array arr;
	GB212::Parser(arr, body);
	// 实时,最新,报文
	for (auto& i : arr) {
		if (i.valid()) {
			// 实时数据, 设置回复数据 , 推送
			if (i.is_data()) {
			    // 解析处理212数据报文
				task_real_data(i, res);
			}
		}
	}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用GB212类进行212协议包的解析,
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档