package org.yzh.protocol.codec;
|
|
import io.github.yezhihao.protostar.SchemaManager;
|
import io.github.yezhihao.protostar.schema.RuntimeSchema;
|
import io.github.yezhihao.protostar.util.ArrayMap;
|
import io.github.yezhihao.protostar.util.Explain;
|
import io.netty.buffer.*;
|
import org.yzh.protocol.basics.JTMessage;
|
import org.yzh.protocol.commons.Bit;
|
import org.yzh.protocol.commons.JTUtils;
|
|
import java.util.ArrayList;
|
import java.util.List;
|
|
/**
|
* JT协议解码器
|
* @author yezhihao
|
* https://gitee.com/yezhihao/jt808-server
|
*/
|
public class JTMessageDecoder {
|
|
private static final ByteBufAllocator ALLOC = PooledByteBufAllocator.DEFAULT;
|
|
private final SchemaManager schemaManager;
|
|
private final ArrayMap<RuntimeSchema> headerSchemaMap;
|
|
public JTMessageDecoder(String... basePackages) {
|
this.schemaManager = new SchemaManager(basePackages);
|
this.headerSchemaMap = schemaManager.getRuntimeSchema(JTMessage.class);
|
}
|
|
public JTMessageDecoder(SchemaManager schemaManager) {
|
this.schemaManager = schemaManager;
|
this.headerSchemaMap = schemaManager.getRuntimeSchema(JTMessage.class);
|
}
|
|
public JTMessage decode(ByteBuf input) {
|
return decode(input, null);
|
}
|
|
public JTMessage decode(ByteBuf input, Explain explain) {
|
ByteBuf buf = unescape(input);
|
|
boolean verified = verify(buf);
|
int messageId = buf.getUnsignedShort(0);
|
int properties = buf.getUnsignedShort(2);
|
|
int version = 0;//缺省值为2013版本
|
if (Bit.isTrue(properties, 14))//识别2019及后续版本
|
version = buf.getUnsignedByte(4);
|
|
boolean isSubpackage = Bit.isTrue(properties, 13);
|
int headLen = JTUtils.headerLength(version, isSubpackage);
|
|
RuntimeSchema<JTMessage> headSchema = headerSchemaMap.get(version);
|
RuntimeSchema<JTMessage> bodySchema = schemaManager.getRuntimeSchema(messageId, version);
|
|
JTMessage message;
|
if (bodySchema == null)
|
message = new JTMessage();
|
else
|
message = bodySchema.newInstance();
|
message.setVerified(verified);
|
message.setPayload(input);
|
|
int writerIndex = buf.writerIndex();
|
buf.writerIndex(headLen);
|
headSchema.mergeFrom(buf, message, explain);
|
buf.writerIndex(writerIndex - 1);
|
|
int realVersion = message.getProtocolVersion();
|
if (realVersion != version)
|
bodySchema = schemaManager.getRuntimeSchema(messageId, realVersion);
|
|
if (bodySchema != null) {
|
int bodyLen = message.getBodyLength();
|
|
if (isSubpackage) {
|
|
ByteBuf bytes = ALLOC.buffer(bodyLen);
|
buf.getBytes(headLen, bytes);
|
|
ByteBuf[] packages = addAndGet(message, bytes);
|
if (packages == null)
|
return message;
|
|
ByteBuf bodyBuf = Unpooled.wrappedBuffer(packages);
|
bodySchema.mergeFrom(bodyBuf, message, explain);
|
if (message.noBuffer()) {
|
bodyBuf.release();
|
}
|
} else {
|
buf.readerIndex(headLen);
|
bodySchema.mergeFrom(buf, message, explain);
|
}
|
}
|
return message;
|
}
|
|
protected ByteBuf[] addAndGet(JTMessage message, ByteBuf bytes) {
|
return null;
|
}
|
|
/** 校验 */
|
public static boolean verify(ByteBuf buf) {
|
byte checkCode = JTUtils.bcc(buf, -1);
|
return checkCode == buf.getByte(buf.writerIndex() - 1);
|
}
|
|
/** 反转义 */
|
public static ByteBuf unescape(ByteBuf source) {
|
int low = source.readerIndex();
|
int high = source.writerIndex();
|
|
if (source.getByte(low) == 0x7e)
|
low++;
|
|
if (source.getByte(high - 1) == 0x7e)
|
high--;
|
|
int mark = source.indexOf(low, high - 1, (byte) 0x7d);
|
if (mark == -1) {
|
return source.slice(low, high - low);
|
}
|
|
List<ByteBuf> bufList = new ArrayList<>(3);
|
|
int len;
|
do {
|
|
len = mark + 2 - low;
|
bufList.add(slice(source, low, len));
|
low += len;
|
|
mark = source.indexOf(low, high, (byte) 0x7d);
|
} while (mark > 0);
|
|
bufList.add(source.slice(low, high - low));
|
|
return new CompositeByteBuf(ALLOC, false, bufList.size(), bufList);
|
}
|
|
/** 截取转义前报文,并还原转义位 */
|
protected static ByteBuf slice(ByteBuf byteBuf, int index, int length) {
|
byte second = byteBuf.getByte(index + length - 1);
|
if (second == 0x01) {
|
return byteBuf.slice(index, length - 1);
|
} else if (second == 0x02) {
|
byteBuf.setByte(index + length - 2, 0x7e);
|
return byteBuf.slice(index, length - 1);
|
} else {
|
return byteBuf.slice(index, length);
|
}
|
}
|
}
|