| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- package com.zksy.common.netty;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.ByteToMessageDecoder;
- import java.util.List;
- /** 每条连接独立累积报文;校验失败只丢弃坏帧,不关闭设备连接。 */
- public final class DeviceFrameDecoder extends ByteToMessageDecoder {
- public enum Protocol { PINGSHENG, MANHOLE, GAS, FIRE_PRESSURE }
- private static final int MAX_FRAME = 8192;
- private final Protocol protocol;
- public DeviceFrameDecoder(Protocol protocol) { this.protocol = protocol; }
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
- int minimum = protocol == Protocol.MANHOLE ? 72 : protocol == Protocol.GAS ? 74 : 5;
- while (in.readableBytes() >= minimum) {
- int start = in.readerIndex();
- int length;
- if (protocol == Protocol.PINGSHENG) {
- if (in.getUnsignedMedium(start) != 0x123456) { in.skipBytes(1); continue; }
- length = in.getUnsignedShort(start + 3);
- if (length < 10 || length > MAX_FRAME) { in.skipBytes(1); continue; }
- if (in.readableBytes() < length) {
- // 长度字段损坏时,寻找后续完整且两层校验通过的上报帧。
- int next = nextPingshengFrame(in, start + 3);
- if (next < 0) return;
- in.readerIndex(next);
- continue;
- }
- int xor = 0;
- for (int i = 0; i < length; i++) xor ^= in.getUnsignedByte(start + i);
- if (xor != 0) { in.skipBytes(1); continue; }
- } else if (protocol == Protocol.MANHOLE) {
- // 当前井盖解析器:35字节参数 + 20字节SIM + 15字节IMEI + CRC16。
- length = 72;
- if (crc(in, start, 70) != in.getUnsignedShortLE(start + 70)) { in.skipBytes(1); continue; }
- } else if (protocol == Protocol.GAS) {
- // 当前已支持的7指标上报结构固定为74字节,内外两层校验与接收器一致。
- length = 74;
- if (in.getUnsignedByte(start) != 0x7E || in.getUnsignedByte(start + 15) != 0xAA
- || !gasChecksum(in, start)) { in.skipBytes(1); continue; }
- } else {
- if (in.getUnsignedShort(start) != 0x7E7E) { in.skipBytes(1); continue; }
- // 现有消防设备长度字段不作为可信边界,使用结束符 + 完整CRC确认边界。
- // 不能单靠03分隔:日期、TLV和压力数据中也可能出现03。
- length = fireLength(in, start);
- if (length == 0) {
- // 坏帧后可能紧接新报文,不能让坏CRC永久阻塞下一条有效数据。
- boolean recovered = false;
- for (int next = start + 2; next + 16 < in.writerIndex(); next++) {
- if (in.getUnsignedShort(next) == 0x7E7E && fireLength(in, next) > 0) {
- in.readerIndex(next);
- recovered = true;
- break;
- }
- }
- if (recovered) continue;
- if (in.readableBytes() >= MAX_FRAME) { in.skipBytes(1); continue; }
- return;
- }
- }
- out.add(in.readRetainedSlice(length));
- }
- }
- private static boolean gasChecksum(ByteBuf in, int start) {
- int body = 0, full = 0;
- for (int i = 16; i < 72; i++) body += in.getUnsignedByte(start + i);
- for (int i = 3; i < 73; i++) full += in.getUnsignedByte(start + i);
- return (body & 255) == in.getUnsignedByte(start + 72)
- && ((255 - full) & 255) == in.getUnsignedByte(start + 73);
- }
- private static int nextPingshengFrame(ByteBuf in, int from) {
- for (int start = from; start + 10 <= in.writerIndex(); start++) {
- if (in.getUnsignedMedium(start) != 0x123456) continue;
- int length = in.getUnsignedShort(start + 3);
- if (length < 14 || length > MAX_FRAME || start + length > in.writerIndex()
- || in.getUnsignedByte(start + 6) != 0x31) continue;
- int xor = 0;
- for (int i = 0; i < length; i++) xor ^= in.getUnsignedByte(start + i);
- if (xor != 0) continue;
- int src = (in.getUnsignedByte(start + 7) + 1) / 2;
- int destIndex = start + 8 + src;
- if (src == 0 || src > 10 || destIndex >= start + length - 3) continue;
- int dest = (in.getUnsignedByte(destIndex) + 1) / 2;
- int body = destIndex + 1 + dest;
- if (dest > 10 || body + 2 >= start + length - 3) continue;
- if (crc(in, body, start + length - 3 - body) == in.getUnsignedShortLE(start + length - 3)) return start;
- }
- return -1;
- }
- private static int fireLength(ByteBuf in, int start) {
- if (in.writerIndex() - start < 14) return 0;
- if (in.getUnsignedByte(start + 10) != 0xEF) {
- // 上报数据固定头之后按TLV跨过值区,值中偶然的03+CRC不能作为帧尾。
- int cursor = start + 37;
- int limit = Math.min(in.writerIndex(), start + MAX_FRAME);
- while (cursor + 3 <= limit) {
- int marker = in.getUnsignedByte(cursor);
- if (marker == 0x03 || marker == 0x17) {
- return crc(in, start, cursor - start + 1) == in.getUnsignedShort(cursor + 1)
- ? cursor - start + 3 : 0;
- }
- int tag = in.getUnsignedShort(cursor);
- int bytes = (in.getUnsignedByte(cursor + 2) >>> 3) & 31;
- if (bytes == 0 && tag == 0x581A) bytes = 2;
- if (bytes == 0 && tag == 0x4520) bytes = 4;
- cursor += 3 + bytes;
- }
- return 0;
- }
- int value = 0xFFFF;
- int end = Math.min(in.writerIndex() - 2, start + MAX_FRAME - 2);
- for (int i = start; i < end; i++) {
- value = crcByte(value, in.getUnsignedByte(i));
- int marker = in.getUnsignedByte(i);
- // 消防现有crc16Tall以高字节在前追加CRC。
- if (i >= start + 14 && (marker == 0x03 || marker == 0x17)
- && value == in.getUnsignedShort(i + 1)) return i - start + 3;
- }
- return 0;
- }
- private static int crc(ByteBuf in, int start, int length) {
- int value = 0xFFFF;
- for (int i = 0; i < length; i++) value = crcByte(value, in.getUnsignedByte(start + i));
- return value;
- }
- private static int crcByte(int value, int b) {
- value ^= b;
- for (int i = 0; i < 8; i++) value = (value & 1) == 1 ? (value >>> 1) ^ 0xA001 : value >>> 1;
- return value;
- }
- }
|