java实现hj协议_环保 HJ212协议解析

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

3817b88df8fe

通讯协议的数据结构

图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, ";&

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值