由于是做环保相关的,有时需要对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;
}
};
}
例如有如下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);
}
}
}