188 lines
5.7 KiB
Java
188 lines
5.7 KiB
Java
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 0x01,0x7d <-> 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;
|
||
}
|
||
}
|