为了丰富研博工业物联网统一接入系统(stew-ot)接入场景,需要对环保行业协议的报文做出解码接入。HJ212的通讯包的组成是由ASCII码 (汉字除外,采用UTF-8码,8位,1字节)字符组成的。
通讯协议数据结构如下:
通讯包结构组成如下:
其中,数据段是整个通讯的核心数据,数据段是由字段名称,=,字段内容三部分组成,具体的组成结构如下:
更多关于HJ212-2017的内容可点击查看。
本次协议解析基于研博工业物联网统一接入系统(stew-ot)协议扩展规范开发。示例只针对HJ212-2017协议的数据解码,不涉及对该类设备的控制,也就没有对应的编码解析。
新建类com.yanboot.iot.protocol.hj212.tcp.HJ212ProtocolCodec,根据SDK包开发规范完成协议报文的解析工作。
实现com.yanboot.iot.sdk.protocol.ProtocolCodec接口,重写support方法,指定协议的唯一标识、名称、特性等内容。
@Override
public ProtocolSupport support() {
return new ProtocolSupport(TransportProtocol.TCP)
.id("HJ212-2017")
.name("环保HJ212-2017协议")
.description("环境污染物采集仪器传输协议")
.feature(new ProtocolFeature().keepOnline(true).keepOnlineTimeoutSeconds(1800));
}
实现com.yanboot.iot.sdk.protocol.ProtocolCodec接口的decode方法,完成协议的解码工作。
private static final String PACKAGE_HEAD = "##";
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
@Override
public void decode(OperatorSupplier supplier, DeviceSession deviceSession, ProtocolMessage<?> message, MessageExporter<DeviceMessage<?>> messageExporter) {
TcpProtocolMessage tcpProtocolMessage = (TcpProtocolMessage) message;
ByteBuf payload = tcpProtocolMessage.payload();
//解析协议
processPayload(payload, messageExporter);
}
private void processPayload(ByteBuf payload, MessageExporter<DeviceMessage<?>> messageExporter) {
String packageHead = payload.readCharSequence(2, CharsetUtil.US_ASCII).toString();
//判断包头是否符合包尾规范,不符合退出后续校验解析
if (!PACKAGE_HEAD.equals(packageHead)) {
return;
}
int dataLen = Integer.parseInt(payload.readCharSequence(4, Charset.defaultCharset()).toString());
String dataSegments = payload.readCharSequence(dataLen, Charset.defaultCharset()).toString();
int checkCrc = Integer.parseInt(CRC16_Checkout(dataSegments, dataLen), 16);
int crc = Integer.parseInt(payload.readCharSequence(4, Charset.defaultCharset()).toString(), 16);
//校验crc是否正确,不正确退出后续解析
if (checkCrc != crc) {
log.error("crc错误,执行结束。");
return;
}
//解析数据段
JSONObject jsonData = JSONObject.parseObject("{\"" + dataSegments.replaceFirst("CP=", "")
.replaceAll("&&", "")
.replaceAll("=", "\":\"")
.replaceAll(",", "\",\"")
.replaceAll(";", "\",\"") + "\"}");
String deviceId = jsonData.getString("MN");
long timestamp = LocalDateTime.parse(jsonData.getString("QN"), dtf).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
//将数据封装成设备消息并发送到研博工业物联网统一接入系统(stew-ot)
messageExporter.export(new ReportPropertyMessage().deviceId(deviceId).properties(jsonData).timestamp(timestamp));
}
// CRC16校验函数
private static String CRC16_Checkout(String src, int len) {
int crc = 0x0000FFFF;
short tc;
char sbit;
for (int i = 0; i < len; i++) {
tc = (short) (crc >>> 8);
crc = ((tc ^ src.charAt(i)) & 0x00FF);
for (int r = 0; r < 8; r++) {
sbit = (char) (crc & 0x01);
crc >>>= 1;
if (sbit != 0)
crc = (crc ^ 0xA001) & 0x0000FFFF;
}
}
String str = Integer.toHexString(crc);
if (str.length() == 3) {
return "0" + str.toUpperCase();
} else if (str.length() == 2) {
return "00" + str.toUpperCase();
}else if (str.length() == 1) {
return "000" + str.toUpperCase();
}
return str.toUpperCase();
}
通过maven clean package完成协议的打包,将打包完成的协议包上传到研博工业物联网统一接入系统。
创建组件实例并启动。
创建产品、管理物模型、接入组件。
管理设备,并发送HJ212-2017报文样例,查看具体详情
try (Socket socket = new Socket(TcpCommon.address(),TcpCommon.serverPort());OutputStream
outputStream = socket.getOutputStream();){//
发送数据给服务器
String message = "##0141QN=20210331101308609;ST=21;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=4;CP=&&DataTime=20210331101308;a01002-Rtd=12.69;a01002-Flag=N&&9E81";
outputStream.write(message.getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
至此,HJ212-2017报文解析结束,通过研博工业物联网统一接入系统(stew-ot)可以查看设备上报的具体监测值。