Modbus ASCII的报文生成顺序为:
1、生成PDU
2、生成LRC校验码,将LRC附加到PDU后面
3、将2中的数组转换成HEX格式的文本
4、在HEX格式文本的0位置插入冒号,在HEX格式文本的后面附加Windows换行符
生成LRC的公式为:LRC?= - sum(PDU)
以下为LRC校验码生成算法的通用性示范:
/**将HEX格式的数据流包装成字符串对象*/
public static final int getLRC8FromAscIIBytes(byte[] hex, int first, int last) {
String hexString = new String(hex, Math.min(first, last), Math.max(first, last) + 1);
return getLRC8FromAscIIBytes(hexString, 0, hexString.length() - 1);
}
/**将字符串对象解码为Modbus数据*/
public static int getLRC8FromAscIIBytes(String hex, int first, int last) {
if (hex == null || hex.isEmpty()) {
return 0;
}
String subHex = hex.substring(Math.min(first, last), Math.max(first, last) + 1);
byte[] bytes = convertHexStringToBytes(subHex);
if (first < last) {
return getLRC8FromRealBytes(bytes, 0, bytes.length - 1);
} else {
return getLRC8FromRealBytes(bytes, bytes.length - 1, 0);
}
}
/**将Modbus数据求和,变为负数,返回低字节,即Modbus ASCII LRC8校验码的值*/
public static final int getLRC8FromRealBytes(byte[] bytes, int first, int last) {
if (bytes == null) {
return 0;
}
return (-getADD8(bytes, first, last)) & 0xff;
}
/**求和函数*/
public static final int getADD8(byte[] bytes, int first, int last) {
if (bytes == null) {
return 0;
}
byte result = 0;
int step = (first < last) ? 1 : -1;
for (int i = first; i != last + step; i += step) {
result += bytes[i];
}
return result & 0xff;
}
需要注意的是,这里的函数专门用于Modbus ASCII校验,不同的通信规约中对LRC的定义可能不一样。LRC8是对字节统计求和校验的包装,包装的方法不唯一。对Modbus ASCII而言,包装的目的在于使有效报文字节(排除掉首字节和Windows换行符共3个字节的常量部分)的统计和为0。这一点也体现在Modbus RTU的CRC16校验上。
一些相关问题
为什么Modbus TCP不需要这种归零校验码?
归零校验码的作用在于快速确定报文是否结束,而无需等待最后一步超时。Modbus TCP带有报文长度信息,所以不需要归零。另外,TCP层自带通信数据校验,其可靠性强于应用层及会话层的校验,因此Modbus TCP的应用层规约中没有校验的规定。但由于尾部空闲数据区未进行封闭性的定义,开发者也可以自己增加几个字节的校验码,甚至可将MD5放到后面,这样即有校验码又能兼容不做校验的程序。
为什么有了归零校验码,Modbus ASCII仍然需要需要Windows换行符来结尾?
这样可以兼容一些缓冲字符流。比如Java中的BufferedReader就需要这个换行符才能知道这段报文已经结束了。至于为什么是Windows换行符,那是因为Windows换行符可以兼容Linux和iOS,反之则不能通用。另外,几乎所有上位机工业软件都运行于Windows系统,采用Windows换行符是大势所趋。使用Windows换行符来作为结束符无可厚非,但Mosbus ASCII报文的起始符是一个败笔。单一字节的起始符可能无法兼容一些PLC。比如欧姆龙PLC要求起始符必须是两个字节。当然,为了解决这一问题,PLC厂商也会提供相应的硬件模块,不过是钱的问题。
|