DeviceFrameDecoder.java 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package com.zksy.common.netty;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import io.netty.handler.codec.ByteToMessageDecoder;
  5. import java.util.List;
  6. /** 每条连接独立累积报文;校验失败只丢弃坏帧,不关闭设备连接。 */
  7. public final class DeviceFrameDecoder extends ByteToMessageDecoder {
  8. public enum Protocol { PINGSHENG, MANHOLE, GAS, FIRE_PRESSURE }
  9. private static final int MAX_FRAME = 8192;
  10. private final Protocol protocol;
  11. public DeviceFrameDecoder(Protocol protocol) { this.protocol = protocol; }
  12. @Override
  13. protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
  14. int minimum = protocol == Protocol.MANHOLE ? 72 : protocol == Protocol.GAS ? 74 : 5;
  15. while (in.readableBytes() >= minimum) {
  16. int start = in.readerIndex();
  17. int length;
  18. if (protocol == Protocol.PINGSHENG) {
  19. if (in.getUnsignedMedium(start) != 0x123456) { in.skipBytes(1); continue; }
  20. length = in.getUnsignedShort(start + 3);
  21. if (length < 10 || length > MAX_FRAME) { in.skipBytes(1); continue; }
  22. if (in.readableBytes() < length) {
  23. // 长度字段损坏时,寻找后续完整且两层校验通过的上报帧。
  24. int next = nextPingshengFrame(in, start + 3);
  25. if (next < 0) return;
  26. in.readerIndex(next);
  27. continue;
  28. }
  29. int xor = 0;
  30. for (int i = 0; i < length; i++) xor ^= in.getUnsignedByte(start + i);
  31. if (xor != 0) { in.skipBytes(1); continue; }
  32. } else if (protocol == Protocol.MANHOLE) {
  33. // 当前井盖解析器:35字节参数 + 20字节SIM + 15字节IMEI + CRC16。
  34. length = 72;
  35. if (crc(in, start, 70) != in.getUnsignedShortLE(start + 70)) { in.skipBytes(1); continue; }
  36. } else if (protocol == Protocol.GAS) {
  37. // 当前已支持的7指标上报结构固定为74字节,内外两层校验与接收器一致。
  38. length = 74;
  39. if (in.getUnsignedByte(start) != 0x7E || in.getUnsignedByte(start + 15) != 0xAA
  40. || !gasChecksum(in, start)) { in.skipBytes(1); continue; }
  41. } else {
  42. if (in.getUnsignedShort(start) != 0x7E7E) { in.skipBytes(1); continue; }
  43. // 现有消防设备长度字段不作为可信边界,使用结束符 + 完整CRC确认边界。
  44. // 不能单靠03分隔:日期、TLV和压力数据中也可能出现03。
  45. length = fireLength(in, start);
  46. if (length == 0) {
  47. // 坏帧后可能紧接新报文,不能让坏CRC永久阻塞下一条有效数据。
  48. boolean recovered = false;
  49. for (int next = start + 2; next + 16 < in.writerIndex(); next++) {
  50. if (in.getUnsignedShort(next) == 0x7E7E && fireLength(in, next) > 0) {
  51. in.readerIndex(next);
  52. recovered = true;
  53. break;
  54. }
  55. }
  56. if (recovered) continue;
  57. if (in.readableBytes() >= MAX_FRAME) { in.skipBytes(1); continue; }
  58. return;
  59. }
  60. }
  61. out.add(in.readRetainedSlice(length));
  62. }
  63. }
  64. private static boolean gasChecksum(ByteBuf in, int start) {
  65. int body = 0, full = 0;
  66. for (int i = 16; i < 72; i++) body += in.getUnsignedByte(start + i);
  67. for (int i = 3; i < 73; i++) full += in.getUnsignedByte(start + i);
  68. return (body & 255) == in.getUnsignedByte(start + 72)
  69. && ((255 - full) & 255) == in.getUnsignedByte(start + 73);
  70. }
  71. private static int nextPingshengFrame(ByteBuf in, int from) {
  72. for (int start = from; start + 10 <= in.writerIndex(); start++) {
  73. if (in.getUnsignedMedium(start) != 0x123456) continue;
  74. int length = in.getUnsignedShort(start + 3);
  75. if (length < 14 || length > MAX_FRAME || start + length > in.writerIndex()
  76. || in.getUnsignedByte(start + 6) != 0x31) continue;
  77. int xor = 0;
  78. for (int i = 0; i < length; i++) xor ^= in.getUnsignedByte(start + i);
  79. if (xor != 0) continue;
  80. int src = (in.getUnsignedByte(start + 7) + 1) / 2;
  81. int destIndex = start + 8 + src;
  82. if (src == 0 || src > 10 || destIndex >= start + length - 3) continue;
  83. int dest = (in.getUnsignedByte(destIndex) + 1) / 2;
  84. int body = destIndex + 1 + dest;
  85. if (dest > 10 || body + 2 >= start + length - 3) continue;
  86. if (crc(in, body, start + length - 3 - body) == in.getUnsignedShortLE(start + length - 3)) return start;
  87. }
  88. return -1;
  89. }
  90. private static int fireLength(ByteBuf in, int start) {
  91. if (in.writerIndex() - start < 14) return 0;
  92. if (in.getUnsignedByte(start + 10) != 0xEF) {
  93. // 上报数据固定头之后按TLV跨过值区,值中偶然的03+CRC不能作为帧尾。
  94. int cursor = start + 37;
  95. int limit = Math.min(in.writerIndex(), start + MAX_FRAME);
  96. while (cursor + 3 <= limit) {
  97. int marker = in.getUnsignedByte(cursor);
  98. if (marker == 0x03 || marker == 0x17) {
  99. return crc(in, start, cursor - start + 1) == in.getUnsignedShort(cursor + 1)
  100. ? cursor - start + 3 : 0;
  101. }
  102. int tag = in.getUnsignedShort(cursor);
  103. int bytes = (in.getUnsignedByte(cursor + 2) >>> 3) & 31;
  104. if (bytes == 0 && tag == 0x581A) bytes = 2;
  105. if (bytes == 0 && tag == 0x4520) bytes = 4;
  106. cursor += 3 + bytes;
  107. }
  108. return 0;
  109. }
  110. int value = 0xFFFF;
  111. int end = Math.min(in.writerIndex() - 2, start + MAX_FRAME - 2);
  112. for (int i = start; i < end; i++) {
  113. value = crcByte(value, in.getUnsignedByte(i));
  114. int marker = in.getUnsignedByte(i);
  115. // 消防现有crc16Tall以高字节在前追加CRC。
  116. if (i >= start + 14 && (marker == 0x03 || marker == 0x17)
  117. && value == in.getUnsignedShort(i + 1)) return i - start + 3;
  118. }
  119. return 0;
  120. }
  121. private static int crc(ByteBuf in, int start, int length) {
  122. int value = 0xFFFF;
  123. for (int i = 0; i < length; i++) value = crcByte(value, in.getUnsignedByte(start + i));
  124. return value;
  125. }
  126. private static int crcByte(int value, int b) {
  127. value ^= b;
  128. for (int i = 0; i < 8; i++) value = (value & 1) == 1 ? (value >>> 1) ^ 0xA001 : value >>> 1;
  129. return value;
  130. }
  131. }