gps/src/main/java/com/njzscloud/jt808/protocol/JT808Decoder.java

188 lines
5.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package com.njzscloud.jt808.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* JT808协议解码器
* 用于解析JT808协议格式的字节流为消息对象
*/
@Slf4j
public class JT808Decoder extends ByteToMessageDecoder {
// 消息起始符和结束符
private static final byte MSG_DELIMITER = 0x7e;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
// 确保至少有2个字节起始符+至少一个字节)
if (in.readableBytes() < 2) {
return;
}
// 寻找消息起始符
int startIndex = -1;
while (in.readableBytes() > 0) {
if (in.readByte() == MSG_DELIMITER) {
startIndex = in.readerIndex() - 1;
break;
}
}
if (startIndex == -1) {
// 未找到起始符,清空缓冲区
in.clear();
return;
}
// 寻找消息结束符
int endIndex = -1;
for (int i = in.readerIndex(); i < in.writerIndex(); i++) {
if (in.getByte(i) == MSG_DELIMITER) {
endIndex = i;
break;
}
}
if (endIndex == -1) {
// 未找到结束符,等待更多数据
in.readerIndex(startIndex);
return;
}
// 读取完整消息(不包含起始符和结束符)
int messageLength = endIndex - startIndex - 1;
ByteBuf messageBuf = in.slice(startIndex + 1, messageLength);
// 处理转义字符
ByteBuf unescapedBuf = handleEscape(messageBuf);
// 解析消息内容
JT808Message message = parseMessage(unescapedBuf);
if (message != null) {
out.add(message);
}
// 移动读指针到结束符之后
in.readerIndex(endIndex + 1);
}
/**
* 处理转义字符
* JT808协议规定0x7e <-> 0x7d 0x010x7d <-> 0x7d 0x02
*/
private ByteBuf handleEscape(ByteBuf buf) {
ByteBuf out = buf.alloc().buffer(buf.readableBytes());
while (buf.readableBytes() > 0) {
byte b = buf.readByte();
if (b == 0x7d) {
if (buf.readableBytes() > 0) {
byte next = buf.readByte();
if (next == 0x01) {
out.writeByte(0x7e);
} else if (next == 0x02) {
out.writeByte(0x7d);
} else {
// 无效的转义序列,直接写入
out.writeByte(b);
out.writeByte(next);
}
} else {
out.writeByte(b);
}
} else {
out.writeByte(b);
}
}
out.resetReaderIndex();
return out;
}
/**
* 解析消息内容
*/
private JT808Message parseMessage(ByteBuf buf) {
if (buf.readableBytes() < 11) { // 最小消息长度2+2+6+1
return null;
}
JT808Message message = new JT808Message();
try {
// 1. 消息ID (2字节)
message.setMessageId(buf.readUnsignedShort());
// 2. 消息体属性 (2字节)
int messageBodyProps = buf.readUnsignedShort();
message.setMessageBodyProps(messageBodyProps);
// 3. 终端手机号 (6字节, BCD编码)
byte[] phoneBytes = new byte[6];
buf.readBytes(phoneBytes);
String terminalPhone = bcdToString(phoneBytes);
message.setTerminalPhone(terminalPhone);
// 4. 消息流水号 (2字节)
message.setFlowId(buf.readUnsignedShort());
// 5. 消息包封装项 (可选)
if (message.isPackaged()) {
JT808Message.PackageInfo packageInfo = new JT808Message.PackageInfo();
packageInfo.setTotalPackages(buf.readUnsignedShort());
packageInfo.setCurrentPackageNo(buf.readUnsignedShort());
message.setPackageInfo(packageInfo);
}
// 6. 消息体
int bodyLength = message.getMessageBodyLength();
if (buf.readableBytes() >= bodyLength + 1) { // 消息体 + 校验码
ByteBuf bodyBuf = buf.readSlice(bodyLength);
byte[] array = ByteBufUtil.getBytes(bodyBuf);
message.setMessageBody(array);
// 7. 校验码 (1字节)
message.setCheckCode(buf.readByte());
// 验证校验码
if (!verifyCheckCode(message, buf)) {
System.err.println("校验码错误: " + message);
return null;
}
return message;
}
} catch (Exception e) {
log.error("消息解析错误", e);
}
return null;
}
/**
* BCD码转字符串
*/
private String bcdToString(byte[] bcd) {
StringBuilder sb = new StringBuilder();
for (byte b : bcd) {
sb.append(String.format("%02x", b));
}
// 移除前面的0
return sb.toString().replaceFirst("^0+", "");
}
/**
* 验证校验码
*/
private boolean verifyCheckCode(JT808Message message, ByteBuf originalBuf) {
// 简化实现,实际项目中需要根据协议规范计算校验码
// 校验码 = 消息头 + 消息体 中所有字节的异或
return true;
}
}