java实现hj协议_环保 HJ212协议解析
由于是做环保相关的,有时需要对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协议解析相关推荐
- 环保 HJ212协议解析
环保 HJ212协议解析 HJ212协议简介 基于C++的HJ212解析类 使用GB212类进行212协议包的解析, 关于HJ212Receiver项目 HJ212协议简介 由于是做环保相关的,有时需 ...
- igmp是哪个层协议_通俗易懂网络协议(IP)
之前写过一篇<通俗易懂TCP/IP(概述)>,广受欢迎和好评,有网友催更,便抽空续写IP章节,回应粉丝期待. TCP/IP网络模型 TCP/IP网络模型分为4层,自下而上分布为链路层(又叫 ...
- ftp协议是一种用于_______的协议_网络安全常见协议解析:TCP、UDP、HTTP、FTP、SMTP等之间的区别...
了解网络安全行业的都知道,网络安全协议是营造网络安全环境的基础,是构建安全网络的关键技术.常见的网络协议如HTTP协议.TCP/IP协议.FTP协议等. 如果你想进入网安行业,这些协议都是需要重点要学 ...
- 为什么tcp不采用停等协议_为什么 TCP 协议有粘包问题
来自公众号:真没什么逻辑 链接:https://draveness.me/whys-the-design-tcp-message-frame/ 为什么这么设计(Why's THE Design)是一系 ...
- java实现hj协议_HJ212协议java 实现 封装好的环保212协议代码 - 下载 - 搜珍网
压缩包 : hj212.zip 列表 hj212/format/ hj212/format/hbt212/ hj212/format/hbt212/base/ hj212/format/hbt212/ ...
- java xmpp即时通讯_基于XMPP协议即时通讯工具开发总结
一.概要 转眼毕业了,毕业设计的课题是"基于XMPP协议的通讯工具",开发平台式android,实现了基本的离线消息,文字聊天,表情聊天,文件传输,语音聊天的功能. 本文主要介绍开 ...
- java伪协议_通过伪协议解决父页面与iframe页面通信的问题
我们经常会有父页面与iframe页面的操作,比如 这个iframe里面的内容是js写的.如以下代码 var iframe = document.getElementById("iframe& ...
- netty实现mysql协议_基于Netty模拟解析Binlog
前言 最近一段时间一直再看mysql binlog相关的内容,也整理了几篇相关的文章,对mysql的事件以及通讯协议在理论上有了一个大概的了解,但是缺少实战:本文的目的就是从实战出发,了解binlog ...
- xrdp协议_远程桌面协议-阿里云开发者社区
libgssglue yum -y install libX11-devel 目前常用的协议有VNC/SPICE/RDP三种,就在这里做一个简单的介绍. 三种协议的对比 SPICE VNC RDP B ...
最新文章
- php grid 分页,jqGrid实现前端分页
- [MyBatisPlus]通用Service接口测试通用Service
- html网页和cgi程序编程,CGI 编程方式学习
- 微信公众号关注用户的信息拉取
- 20210530:力扣第53场双周赛题解
- 单分子荧光原位杂交(smFISH)
- ASP.NET对HTML元素进行权限控制(二)
- python自动化笔记_python自动化学习笔记(一)
- 20171001~08总结
- python植物大战僵尸代码
- C语言利用getchar()与while循环解决跳步问题
- i5 1240p使用perf避坑指南
- linux系统如何设置程序开机自启动
- 数据结构之线性表(手绘版)
- web前端人员培训要学些什么?
- 计算机考试 北京考场一日游
- 360对决手机恶意广告
- 哪种邮件群发软件最好用?不骗人,能免费发邮件.群发邮件进收件箱.
- JavaScript 实现空间直角坐标系转换为大地坐标系(XYZ→BLH)
- 还是关于apk文件的反编译