| package com.genersoft.iot.vmp.jt1078.codec.encode; | 
|   | 
| import com.genersoft.iot.vmp.jt1078.annotation.MsgId; | 
| import com.genersoft.iot.vmp.jt1078.proc.Header; | 
| import com.genersoft.iot.vmp.jt1078.proc.entity.Cmd; | 
| import com.genersoft.iot.vmp.jt1078.proc.response.Rs; | 
| import com.genersoft.iot.vmp.jt1078.session.Session; | 
| import com.genersoft.iot.vmp.jt1078.util.Bin; | 
| import io.netty.buffer.ByteBuf; | 
| import io.netty.buffer.ByteBufUtil; | 
| import io.netty.buffer.CompositeByteBuf; | 
| import io.netty.buffer.Unpooled; | 
| import io.netty.channel.ChannelHandlerContext; | 
| import io.netty.handler.codec.MessageToByteEncoder; | 
| import io.netty.util.ByteProcessor; | 
| import org.slf4j.Logger; | 
| import org.slf4j.LoggerFactory; | 
| import org.springframework.util.StringUtils; | 
|   | 
| import java.util.LinkedList; | 
|   | 
| /** | 
|  * @author QingtaiJiang | 
|  * @date 2023/4/27 18:25 | 
|  * @email qingtaij@163.com | 
|  */ | 
| public class Jt808EncoderCmd extends MessageToByteEncoder<Cmd> { | 
|     private final static Logger log = LoggerFactory.getLogger(Jt808EncoderCmd.class); | 
|   | 
|     @Override | 
|     protected void encode(ChannelHandlerContext ctx, Cmd cmd, ByteBuf out) throws Exception { | 
|         Session session = ctx.channel().attr(Session.KEY).get(); | 
|         Rs msg = cmd.getRs(); | 
|         ByteBuf encode = encode(msg, session, cmd.getPackageNo().intValue()); | 
|         if (encode != null) { | 
|             log.info("< {} hex:{}", session, ByteBufUtil.hexDump(encode)); | 
|             out.writeBytes(encode); | 
|         } | 
|     } | 
|   | 
|   | 
|     public static ByteBuf encode(Rs msg, Session session, Integer packageNo) { | 
|         String id = msg.getClass().getAnnotation(MsgId.class).id(); | 
|         if (!StringUtils.hasLength(id)) { | 
|             log.error("Not find msgId"); | 
|             return null; | 
|         } | 
|   | 
|         ByteBuf byteBuf = Unpooled.buffer(); | 
|   | 
|         byteBuf.writeBytes(ByteBufUtil.decodeHexDump(id)); | 
|   | 
|         ByteBuf encode = msg.encode(); | 
|   | 
|         Header header = msg.getHeader(); | 
|         if (header == null) { | 
|             header = session.getHeader(); | 
|         } | 
|   | 
|         if (header.is2019Version()) { | 
|             // 消息体属性 | 
|             byteBuf.writeShort(encode.readableBytes() | 1 << 14); | 
|   | 
|             // 版本号 | 
|             byteBuf.writeByte(header.getVersion()); | 
|   | 
|             // 终端手机号 | 
|             byteBuf.writeBytes(ByteBufUtil.decodeHexDump(Bin.strHexPaddingLeft(header.getDevId(), 20))); | 
|         } else { | 
|             // 消息体属性 | 
|             byteBuf.writeShort(encode.readableBytes()); | 
|   | 
|             byteBuf.writeBytes(ByteBufUtil.decodeHexDump(Bin.strHexPaddingLeft(header.getDevId(), 12))); | 
|         } | 
|   | 
|         // 消息体流水号 | 
|         byteBuf.writeShort(packageNo); | 
|   | 
|         // 写入消息体 | 
|         byteBuf.writeBytes(encode); | 
|   | 
|         // 计算校验码,并反转义 | 
|         byteBuf = escapeAndCheck0(byteBuf); | 
|         return byteBuf; | 
|     } | 
|   | 
|   | 
|     private static final ByteProcessor searcher = value -> !(value == 0x7d || value == 0x7e); | 
|   | 
|     //转义与校验 | 
|     public static ByteBuf escapeAndCheck0(ByteBuf source) { | 
|   | 
|         sign(source); | 
|   | 
|         int low = source.readerIndex(); | 
|         int high = source.writerIndex(); | 
|   | 
|         LinkedList<ByteBuf> bufList = new LinkedList<>(); | 
|         int mark, len; | 
|         while ((mark = source.forEachByte(low, high - low, searcher)) > 0) { | 
|   | 
|             len = mark + 1 - low; | 
|             ByteBuf[] slice = slice(source, low, len); | 
|             bufList.add(slice[0]); | 
|             bufList.add(slice[1]); | 
|             low += len; | 
|         } | 
|   | 
|         if (bufList.size() > 0) { | 
|             bufList.add(source.slice(low, high - low)); | 
|         } else { | 
|             bufList.add(source); | 
|         } | 
|   | 
|         ByteBuf delimiter = Unpooled.buffer(1, 1).writeByte(0x7e).retain(); | 
|         bufList.addFirst(delimiter); | 
|         bufList.addLast(delimiter); | 
|   | 
|         CompositeByteBuf byteBufLs = Unpooled.compositeBuffer(bufList.size()); | 
|         byteBufLs.addComponents(true, bufList); | 
|         return byteBufLs; | 
|     } | 
|   | 
|     public static void sign(ByteBuf buf) { | 
|         byte checkCode = bcc(buf); | 
|         buf.writeByte(checkCode); | 
|     } | 
|   | 
|     public static byte bcc(ByteBuf byteBuf) { | 
|         byte cs = 0; | 
|         while (byteBuf.isReadable()) | 
|             cs ^= byteBuf.readByte(); | 
|         byteBuf.resetReaderIndex(); | 
|         return cs; | 
|     } | 
|   | 
|     protected static ByteBuf[] slice(ByteBuf byteBuf, int index, int length) { | 
|         byte first = byteBuf.getByte(index + length - 1); | 
|   | 
|         ByteBuf[] byteBufList = new ByteBuf[2]; | 
|         byteBufList[0] = byteBuf.retainedSlice(index, length); | 
|   | 
|         if (first == 0x7d) { | 
|             byteBufList[1] = Unpooled.buffer(1, 1).writeByte(0x01); | 
|         } else { | 
|             byteBuf.setByte(index + length - 1, 0x7d); | 
|             byteBufList[1] = Unpooled.buffer(1, 1).writeByte(0x02); | 
|         } | 
|         return byteBufList; | 
|     } | 
| } |