由于是做环保相关的,有时需要对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

#include

// 国标 212

// 所有的通讯包都是由ASCII码

class GB212

{

public:

typedef std::vector GB212Array;

typedef std::vector 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 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

// 数组

#include

#include

#include

#include

#include

typedef std::string String;

typedef std::vector Uint8Array;

typedef std::vector Uint16Array;

typedef std::vector Uint32Array;

typedef std::vector Int8Array;

typedef std::vector Int16Array;

typedef std::vector Int32Array;

typedef std::vector Int64Array;

typedef std::vector Uint64Array;

typedef std::vector Float32Array;

typedef std::vector Float64Array;

typedef std::vector<:string> StringArray;

typedef std::map<:string std::string> StringMap;

typedef std::map Int32StringMap;

typedef std::list<:string> StringList;

typedef std::vector Uint8sArray;

typedef std::function publish_handler;

typedef std::function subscribe_handler;

typedef std::function request_handler;

typedef std::function data_handler;

typedef std::function handler_subscribe;

typedef std::function handler_empty;

typedef std::function 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

typedef std::thread Thread;

#include

// 休眠毫秒

#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

#include

#else

#include

#include

#include

#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<: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<:chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();

// return system_clock::to_time_t(time_point_cast(system_clock::now()));

}

// 微秒

static time_t now_mms() {

return std::chrono::time_point_cast<: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 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);

}

}

}

java实现hj协议_环保 HJ212协议解析相关推荐

  1. 环保 HJ212协议解析

    环保 HJ212协议解析 HJ212协议简介 基于C++的HJ212解析类 使用GB212类进行212协议包的解析, 关于HJ212Receiver项目 HJ212协议简介 由于是做环保相关的,有时需 ...

  2. igmp是哪个层协议_通俗易懂网络协议(IP)

    之前写过一篇<通俗易懂TCP/IP(概述)>,广受欢迎和好评,有网友催更,便抽空续写IP章节,回应粉丝期待. TCP/IP网络模型 TCP/IP网络模型分为4层,自下而上分布为链路层(又叫 ...

  3. ftp协议是一种用于_______的协议_网络安全常见协议解析:TCP、UDP、HTTP、FTP、SMTP等之间的区别...

    了解网络安全行业的都知道,网络安全协议是营造网络安全环境的基础,是构建安全网络的关键技术.常见的网络协议如HTTP协议.TCP/IP协议.FTP协议等. 如果你想进入网安行业,这些协议都是需要重点要学 ...

  4. 为什么tcp不采用停等协议_为什么 TCP 协议有粘包问题

    来自公众号:真没什么逻辑 链接:https://draveness.me/whys-the-design-tcp-message-frame/ 为什么这么设计(Why's THE Design)是一系 ...

  5. java实现hj协议_HJ212协议java 实现 封装好的环保212协议代码 - 下载 - 搜珍网

    压缩包 : hj212.zip 列表 hj212/format/ hj212/format/hbt212/ hj212/format/hbt212/base/ hj212/format/hbt212/ ...

  6. java xmpp即时通讯_基于XMPP协议即时通讯工具开发总结

    一.概要 转眼毕业了,毕业设计的课题是"基于XMPP协议的通讯工具",开发平台式android,实现了基本的离线消息,文字聊天,表情聊天,文件传输,语音聊天的功能. 本文主要介绍开 ...

  7. java伪协议_通过伪协议解决父页面与iframe页面通信的问题

    我们经常会有父页面与iframe页面的操作,比如 这个iframe里面的内容是js写的.如以下代码 var iframe = document.getElementById("iframe& ...

  8. netty实现mysql协议_基于Netty模拟解析Binlog

    前言 最近一段时间一直再看mysql binlog相关的内容,也整理了几篇相关的文章,对mysql的事件以及通讯协议在理论上有了一个大概的了解,但是缺少实战:本文的目的就是从实战出发,了解binlog ...

  9. xrdp协议_远程桌面协议-阿里云开发者社区

    libgssglue yum -y install libX11-devel 目前常用的协议有VNC/SPICE/RDP三种,就在这里做一个简单的介绍. 三种协议的对比 SPICE VNC RDP B ...

最新文章

  1. php grid 分页,jqGrid实现前端分页
  2. [MyBatisPlus]通用Service接口测试通用Service
  3. html网页和cgi程序编程,CGI 编程方式学习
  4. 微信公众号关注用户的信息拉取
  5. 20210530:力扣第53场双周赛题解
  6. 单分子荧光原位杂交(smFISH)
  7. ASP.NET对HTML元素进行权限控制(二)
  8. python自动化笔记_python自动化学习笔记(一)
  9. 20171001~08总结
  10. python植物大战僵尸代码
  11. C语言利用getchar()与while循环解决跳步问题
  12. i5 1240p使用perf避坑指南
  13. linux系统如何设置程序开机自启动
  14. 数据结构之线性表(手绘版)
  15. web前端人员培训要学些什么?
  16. 计算机考试 北京考场一日游
  17. 360对决手机恶意广告
  18. 哪种邮件群发软件最好用?不骗人,能免费发邮件.群发邮件进收件箱.
  19. JavaScript 实现空间直角坐标系转换为大地坐标系(XYZ→BLH)
  20. 还是关于apk文件的反编译

热门文章

  1. 【算法学习】求得一定数值范围内的所有质数
  2. 总结篇--String、StringBuffer、StringBuilder
  3. 【金融财经】金融市场一周简报(2017-12-01)
  4. 高数之 左右极限求法
  5. 在 PostgreSQL 中简单实现 Insert ignore 操作
  6. 都知道vue3响应式是Proxy实现的,进来把proxy与Reflect吃透
  7. 北京SAP-AGS CoE support consultant intern 面试总结
  8. 新“病毒营销”案例--凡客体
  9. 简练软考知识点整理-规划进度管理
  10. linux fprintf sprintf 函数