我们在进行物联网开发的时候,除了自己开发的硬件设备之外,我们还会直接采购其他厂商提供的设备,这时我们就需要根据厂商提供的SDK进行设备控制。
一般来说,每一种设备的数据帧格式格式都是一样的,都遵循moudlebus,通过观察我们发现,帧格式 一般 是由 数据+固定帧格式+crc校验构成,也就是说,我们只需要提供 数据和固定的帧格式组成一条指令就好。所以为了程序的灵活性,我们可以配置指令。
假设指令格式如下:
地址+固定的帧格式+crc
固定的帧格式:
读 固定格式
写 固定格式
如上这种形式,可以看为一个固定的字符串,因此我们可以在数据库中保存,利用json的形式,存为一个json数组。
json数组的处理我们从数据库中查询获得的,是长得有点像json的 String字符串,举个长这样的例子
[{"Tname":"read","Tlabel":"read","Ttype":"String","Tvalue":"A0400000013"},
{"name":"","label":"","type":"","value":"","Tname":"write","Ttype":"String","Tvalue":"A100008000204E","Tlabel":"write"},
{"name":"","label":"","type":"","value":"","Tname":"switch","Ttype":"String","Tvalue":"A100010000102W","Tlabel":"switch"}]
我们需要的方便处理的是长这样:
{
"read": "A0400000013" ,
"write": "A10008000204E" ,
"switch": "A100010000102W" ,
}
所以为了得到这么一个样子的长得json的String,我们还需要处理上边的那个json数组。
处理代码:
HashMap map =new HashMap();
JSONArray array = JSONUtil.parseArray(scDeviceComponent.getConfig());
for(int i=0; i
通过hutool工具包,我们可以方便进行转换。最后得到jsonObject 的格式就是我们需要的json格式。
这个时候,我们根据TCP服务是否和业务解耦来选择是否存入redis中。
redisTemplate.boundHashOps("deviceConfig").put(scDeviceComponent.getId().toString(),jsonObject.toString());
这里redisTemplate默认配置的是JDK的序列化器,因此它存放到redis缓存中长这个样子
"{"read":"A0400000013","switch":"A100010000102W","write":"A100008000204E"}"
这是序列化之后的数据,这个时候我们在TCP处用redisTemplate获取,如果获取不到,可以使用StringRedisTemplate,这里是因为序列化的问题。
获取到之后
“{“read”:“A0400000013”,“switch”:“A100010000102W”,“write”:“A100008000204E”}”
这个序列化过的长得像json的字符串,我们还需要转为json对象,这样处理起来才方便。
configJson = configJson.substring(1, configJson.length() - 1);
configJson = configJson.replaceAll("\\","");
JSON json = JSONUtil.parse(configJson);
String write =(String) json.getByPath("write"); //获取读读指令,格式 address + 固定帧格式 + expect
转换之后我们就可以直接过去对应的值了,然后替换数据和固定的帧格式组成指令即可。
但是在拼接过程中,我们会遇见这种问题。
地址一般是1字节两个16进制数,最多可到十进制的255,我们获得数据是十进制数,我们就需要单独转换为16进制,但是像1~ 15对应的是1~ F,我们需要的是两个16进制数,也就是说,缺位我们需要补零,因此我们这么转换:
currentAddress = String.format("%02x",Integer.parseInt(currentAddress));
这样 1 转换的就是 01,我们就可以正确拼接指令。
有一些设备规定,要转换为4个16进制数的浮点型数,我们可以这么转换
expectAddress = Integer.toHexString(Float.floatToIntBits(Integer.parseInt(expectAddress))); //转 4字节16进制浮点数
16进制浮点型转10进制浮点数
String E = parseHextoFloat(Estr).toString();
这里附上 crc校验码的算法
package com.ruoyi.tcpserver.util;
import java.math.BigInteger;
public class CRCUtil {
public static String getCRC(byte[] bytes) {
int CRC = 0x0000ffff;
int POLYNOMIAL = 0x0000a001;
int i, j;
for (i = 0; i < bytes.length; i++) {
CRC ^= ((int) bytes[i] & 0x000000ff);
for (j = 0; j < 8; j++) {
if ((CRC & 0x00000001) != 0) {
CRC >>= 1;
CRC ^= POLYNOMIAL;
} else {
CRC >>= 1;
}
}
}
return Integer.toHexString(CRC);
}
private float parseHex2Float(String hexStr) {
BigInteger bigInteger = new BigInteger(hexStr, 16);
return Float.intBitsToFloat(bigInteger.intValue());
}
private String parseFloat2Hex(float data) {
return Integer.toHexString(Float.floatToIntBits(data));
}
}
需要注意的是,crc校验码是2个字节4个16进制数,我们在加入crc校验码时,需要注意高低位的问题,以及例如0A32这种的校验码0是不显示的的,所以我们转为字符串的时候,需要加入校验,补全0。
RabbitMq问题
在测试的过程中,使用的是spring的mq,在默认配置下,mq消息再被消费时,如果出现异常,该消息是不会被ack的,会一直不停的消费同一条数据,也就是不停的报错。
以上是近期开发遇见的问题,再此进行总结。功不唐捐,玉汝于成! 


