147 lines
4.2 KiB
Java
147 lines
4.2 KiB
Java
package com.njzscloud.jt808.protocol;
|
||
|
||
import io.netty.buffer.ByteBuf;
|
||
import io.netty.channel.ChannelHandlerContext;
|
||
import io.netty.handler.codec.MessageToByteEncoder;
|
||
|
||
/**
|
||
* JT808协议编码器
|
||
* 将JT808Message对象编码为符合协议规范的字节流
|
||
*/
|
||
public class JT808Encoder extends MessageToByteEncoder<JT808Message> {
|
||
// 消息起始符和结束符
|
||
private static final byte MSG_DELIMITER = 0x7e;
|
||
|
||
@Override
|
||
protected void encode(ChannelHandlerContext ctx, JT808Message msg, ByteBuf out) throws Exception {
|
||
// 1. 创建缓冲区存储消息内容(不含起始符和结束符)
|
||
ByteBuf contentBuf = ctx.alloc().buffer();
|
||
|
||
try {
|
||
// 2. 写入消息ID
|
||
contentBuf.writeShort(msg.getMessageId());
|
||
|
||
// 3. 写入消息体属性
|
||
contentBuf.writeShort(msg.getMessageBodyProps());
|
||
|
||
// 4. 写入终端手机号(BCD编码)
|
||
byte[] phoneBytes = stringToBcd(msg.getTerminalPhone(), 6);
|
||
contentBuf.writeBytes(phoneBytes);
|
||
|
||
// 5. 写入消息流水号
|
||
contentBuf.writeShort(msg.getFlowId());
|
||
|
||
// 6. 写入消息包封装项(如果需要)
|
||
if (msg.isPackaged() && msg.getPackageInfo() != null) {
|
||
contentBuf.writeShort(msg.getPackageInfo().getTotalPackages());
|
||
contentBuf.writeShort(msg.getPackageInfo().getCurrentPackageNo());
|
||
}
|
||
|
||
// 7. 写入消息体
|
||
byte[] messageBody = msg.getMessageBody();
|
||
if (messageBody != null && messageBody.length > 0) {
|
||
contentBuf.writeBytes(messageBody);
|
||
}
|
||
|
||
// 8. 计算并写入校验码
|
||
byte checkCode = calculateCheckCode(contentBuf);
|
||
contentBuf.writeByte(checkCode);
|
||
msg.setCheckCode(checkCode);
|
||
|
||
// 9. 处理转义
|
||
ByteBuf escapedBuf = handleEscape(contentBuf);
|
||
|
||
// 10. 写入起始符
|
||
out.writeByte(MSG_DELIMITER);
|
||
|
||
// 11. 写入转义后的内容
|
||
out.writeBytes(escapedBuf);
|
||
|
||
// 12. 写入结束符
|
||
out.writeByte(MSG_DELIMITER);
|
||
} finally {
|
||
// 释放缓冲区
|
||
if (contentBuf.refCnt() > 0) {
|
||
contentBuf.release();
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理转义字符
|
||
*/
|
||
private ByteBuf handleEscape(ByteBuf buf) {
|
||
ByteBuf out = buf.alloc().buffer(buf.readableBytes() * 2); // 预留足够空间
|
||
|
||
buf.resetReaderIndex();
|
||
while (buf.readableBytes() > 0) {
|
||
byte b = buf.readByte();
|
||
if (b == MSG_DELIMITER) {
|
||
out.writeByte(0x7d);
|
||
out.writeByte(0x01);
|
||
} else if (b == 0x7d) {
|
||
out.writeByte(0x7d);
|
||
out.writeByte(0x02);
|
||
} else {
|
||
out.writeByte(b);
|
||
}
|
||
}
|
||
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 计算校验码
|
||
* 校验码 = 消息头 + 消息体 中所有字节的异或
|
||
*/
|
||
private byte calculateCheckCode(ByteBuf buf) {
|
||
byte checkCode = 0;
|
||
int readerIndex = buf.readerIndex();
|
||
|
||
while (buf.readableBytes() > 0) {
|
||
checkCode ^= buf.readByte();
|
||
}
|
||
|
||
buf.readerIndex(readerIndex);
|
||
return checkCode;
|
||
}
|
||
|
||
/**
|
||
* 字符串转BCD码
|
||
*/
|
||
private byte[] stringToBcd(String str, int length) {
|
||
if (str == null || str.length() == 0) {
|
||
return new byte[length];
|
||
}
|
||
|
||
// 补0至偶数长度
|
||
int strLen = str.length();
|
||
if (strLen % 2 != 0) {
|
||
str = "0" + str;
|
||
}
|
||
|
||
// 确保不超过指定长度
|
||
if (str.length() > length * 2) {
|
||
str = str.substring(0, length * 2);
|
||
}
|
||
|
||
byte[] bcd = new byte[length];
|
||
int index = 0;
|
||
|
||
for (int i = 0; i < str.length(); i += 2) {
|
||
if (index >= length) {
|
||
break;
|
||
}
|
||
|
||
// 高4位
|
||
byte high = (byte) (Character.digit(str.charAt(i), 16) << 4);
|
||
// 低4位
|
||
byte low = (byte) Character.digit(str.charAt(i + 1), 16);
|
||
|
||
bcd[index++] = (byte) (high | low);
|
||
}
|
||
|
||
return bcd;
|
||
}
|
||
}
|