java-水文协议-SL651-2014-解析
有一个传感器是通过水文协议进行上报的,估按照协议对报文进行解析,报文分类很多种,我这种是定时报文,所以当前文章是对定时报文的解析,如果需要其他报文则不用观看本文章了…
·
文章目录
前言
有一个传感器是通过水文协议进行上报的,估按照协议对报文进行解析,报文分类很多种,我这种是 定时报文 ,所以当前文章是对定时报文的解析,如果需要其他报文则不用观看本文章了…
需要了解水文协议报文
文件在目录下方下载
解析工具类
解析用的到一些自写工具类
数组工具类
用于将一串字符串进行切割
public class ArrayUtils {
public static List<String> split(String data) {
List<String> result = new ArrayList<>();
for (int i = 0; i < data.length(); i += 2) {
result.add(data.substring(i, i + 2));
}
return result;
}
}
byte工具类
将字符串转换成byte,其中用到 netty 的Buffer
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>4.1.67.Final</version>
</dependency>
工具类
public class ByteUtils {
public static ByteBuf hexString2ByteBuf(String src) {
return Unpooled.buffer().writeBytes(hexString2Bytes(src));
}
public static byte[] hexString2Bytes(String src) {
if (null == src || src.isEmpty()) {
return null;
}
byte[] ret = new byte[src.length() / 2];
byte[] tmp = src.getBytes();
for (int i = 0; i < (tmp.length / 2); i++) {
ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
}
return ret;
}
public static byte uniteBytes(byte src0, byte src1) {
byte _b0 = Byte.decode("0x" + new String(new byte[]{src0}));
_b0 = (byte) (_b0 << 4);
byte _b1 = Byte.decode("0x" + new String(new byte[]{src1}));
return (byte) (_b0 ^ _b1);
}
}
处理数据长度
用于处理数据长度和小数位
public class DataLengthParser {
/**
* 解析一个字节,返回数据长度和小数位信息
*
* @param b 输入字节(如 (byte) 0x19)
* @return LengthInfo 解析结果
*/
public static LengthInfo parse(byte b) {
// 转为无符号整数(byte 是有符号的,范围 -128~127,需要转成 0~255)
int unsignedByte = b & 0xFF;
// 高5位:(unsignedByte >> 3) & 0x1F
// 低3位:unsignedByte & 0x07
int length = (unsignedByte >> 3) & 0x1F; // 取高5位
int decimals = unsignedByte & 0x07; // 取低3位
return new LengthInfo(length, decimals);
}
/**
* 解析单个字节,提取高5位和低3位
*/
@Data
public static class LengthInfo {
private final int dataLength; // 数据部分占用字节数(高5位)
private final int decimalDigits; // 小数位数(低3位)
public LengthInfo(int dataLength, int decimalDigits) {
this.dataLength = dataLength;
this.decimalDigits = decimalDigits;
}
@Override
public String toString() {
return String.format("数据长度: %d 字节, 小数位数: %d 位", dataLength, decimalDigits);
}
}
}
解析类
@Data
public static class TestBody {
/**
* 起始位
*/
private String qsw;
/**
* 中心站 范围为1~255
*/
private String zxz;
/**
* 遥测站地址
*/
private String ycdz;
/**
* 密码
*/
private String mm;
/**
* 功能码
*/
private String gnm;
/**
* 数据长度
*/
private Integer sjcd;
/**
* 数据起始符
* 02 : 一个帧包含了当前数据..
* 16 : 相当于是进行了分包把
*/
private String sjqsf;
private YCZDSBSXBody yczdsbsxBody;
public TestBody(List<String> split) {
this.qsw = split.get(0) + split.get(1);
this.zxz = split.get(2);
this.ycdz = split.get(3) + split.get(4) + split.get(5) + split.get(6) + split.get(7);
this.mm = split.get(8) + split.get(9);
this.gnm = split.get(10);
// 将数据长度进行转换
ByteBuf byteBuf = ByteUtils.hexString2ByteBuf(split.get(11) + split.get(12));
this.sjcd = byteBuf.getUnsignedShort(0);
this.sjqsf = split.get(13);
this.yczdsbsxBody = new YCZDSBSXBody(split, this.sjcd);
}
}
/**
* 遥测站 定时报 上行 报文 正文
*/
@Data
private static class YCZDSBSXBody {
/**
* 流水号
*/
private Integer lsh;
/**
* 发报时间
*/
private StringBuilder fbsj = new StringBuilder();
/**
* 遥测站地址
*/
private StringBuilder yczdz = new StringBuilder();
/**
* 遥测站分类
*/
private String yczfl;
/**
* 观测时间
*/
private StringBuilder yczsj = new StringBuilder();
/**
* 要素标识符
*/
private String ysbsf;
private StringBuilder data = new StringBuilder();
public YCZDSBSXBody(List<String> split, Integer sjcd) {
// 从14帧开始截取,截取到 特定帧 减去 电源电压
List<String> body = split.subList(14, 14 + (sjcd - 4));
// 流水号
ByteBuf byteBuf = ByteUtils.hexString2ByteBuf(body.get(0) + body.get(1));
this.lsh = byteBuf.getUnsignedShort(0);
byteBuf.release();
// 发报时间
for (int i = 2; i <= 7; i++) {
fbsj.append(i != 7 ? body.get(i) + "-" : body.get(i));
}
// 遥测地址
for (int i = 8; i <= 14; i++) {
yczdz.append(body.get(i));
}
// 遥测站分类
this.yczfl = body.get(15);
// 观测时间
for (int i = 16; i <= 22; i++) {
yczsj.append(i != 22 ? body.get(i) + "-" : body.get(i));
}
// 计算数据..
// 39 23 00 00 03 70
this.ysbsf = body.get(23);
DataLengthParser.LengthInfo parse = DataLengthParser.parse(ByteUtils.hexString2Bytes(body.get(24))[0]);
// 从 25 截取到 多少个字节
for (int i = 25; i <= 25 + parse.getDataLength() - 1; i++) {
data.append(body.get(i));
}
BigDecimal bigDecimal = formatNumber(Integer.parseInt(data.toString()), parse.getDecimalDigits());
System.out.println(bigDecimal); // 0.370
}
}
formatNumber
public static BigDecimal formatNumber(int number, int decimalPlaces) {
BigDecimal decimal = new BigDecimal(number);
return decimal.movePointLeft(decimalPlaces);
}
测试
public static void main(String[] args) {
String data = "7E7E014160325858A00032002102001A250429183000F1F1416032585848F0F025042918303923000003703812122803027E";
List<String> split = ArrayUtils.split(data);
TestBody testBody = new TestBody(split);
System.out.println(testBody);
}
后言
部分内容我没解析是因为我用不到,我实际上用的就是数据这块,只是为了方便排查估才将整个报文都通过截取的方式解析了一遍…
更多推荐
所有评论(0)