Explorar el Código

feat(gas): 增加气体单位、温湿度字段并优化协议解析

- 新增气体单位字段 gasUnit
- 添加温度和湿度监测字段
-优化消息校验逻辑,支持双校验和计算- 改进时间戳和浮点数解析方法
- 更新数据库映射配置以支持新字段- 调整BCD转换工具方法访问权限- 增强协议解析工具类的健壮性
林仔 hace 7 meses
padre
commit
d72068e57f

+ 18 - 1
flammable-gas-service/src/main/java/com/zksy/gas/domain/GasMonitorData.java

@@ -85,7 +85,7 @@ public class GasMonitorData implements Serializable {
     private LocalDateTime reportTime;
 
     /**
-     * 气体浓度(%LEL)
+     * 气体浓度(%LEL)
      */
     @TableField(value = "gas_concentration")
     private BigDecimal gasConcentration;
@@ -95,6 +95,11 @@ public class GasMonitorData implements Serializable {
      */
     @TableField(value = "gas_type")
     private String gasType;
+    /**
+     * 气体单位
+     */
+    @TableField(value = "gas_unit")
+    private String gasUnit;
 
     /**
      * 电池电量
@@ -108,6 +113,18 @@ public class GasMonitorData implements Serializable {
     @TableField(value = "signal_strength")
     private Float signalStrength;
 
+    /**
+     * 温度
+     */
+    @TableField(value = "temperature")
+    private Float temperature;
+
+    /**
+     * 湿度
+     */
+    @TableField(value = "humidity")
+    private Float humidity;
+
     /**
      * 经度
      */

+ 43 - 8
flammable-gas-service/src/main/java/com/zksy/gas/utils/MessageHandler.java

@@ -16,6 +16,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+
 @ChannelHandler.Sharable
 @Slf4j
 @Component
@@ -46,15 +49,48 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 			msgByteBuf.readBytes(msgBytes);
 			logger.info("接收到 {} 字节的数据: {}, 来自: {}",
 					readable, ProtocolUtils.bytesToHex(msgBytes), ctx.channel().remoteAddress());
-			//计算校验和
-			int check = DataCheckUtil.calcChecksum(msgBytes, 2, msgBytes.length - 3);
-			int checksum = msgBytes[msgBytes.length - 2] & 0xFF;
 
-			if (check != checksum) {
-				throw new InvalidMessageException("数据校验失败,本地计算=" + check + ",报文携带=" + checksum);
+			int checksumTotalLength = 2; // 末尾2字节校验码(如ED34)
+			int bodyStartOffset = 16;    // 校验和07的主体起始索引
+			int fullDataStartOffset = 3; // 校验和16的完整数据起始索引
+
+			// 校验报文长度合法性
+			if (readable < fullDataStartOffset + checksumTotalLength) {
+				throw new InvalidMessageException("报文长度不足,无法完成校验");
+			}
+
+			int bodyEndOffset = msgBytes.length - checksumTotalLength; // 避开末尾2字节校验码
+			int bodyLength = bodyEndOffset - bodyStartOffset;
+			if (bodyLength <= 0) {
+				throw new InvalidMessageException("主体数据长度无效,无法计算校验和07");
+			}
+			int checksum07 = DataCheckUtil.calcSumOnly(msgBytes, bodyStartOffset, bodyLength);
+
+			int fullDataEndOffset = msgBytes.length - 1; // 包含倒数第二个字节
+			int fullDataLength = fullDataEndOffset - fullDataStartOffset;
+			if (fullDataLength <= 0) {
+				throw new InvalidMessageException("完整数据长度无效,无法计算校验和16");
 			}
-			GasMonitorData resultData = DataParser.parseMessage(msgBytes);
-			resultData.setId(UUID.randomUUID().toString());
+			int checksum16 = DataCheckUtil.calcChecksum(msgBytes, fullDataStartOffset, fullDataLength);
+
+			int calculatedChecksum = (checksum07 << 8) | checksum16;
+			int receivedChecksum = ((msgBytes[msgBytes.length - 2] & 0xFF) << 8)
+					| (msgBytes[msgBytes.length - 1] & 0xFF);
+
+			// 打印调试信息,确认拼接结果
+			logger.debug("校验和07(高8位,仅求和): {}", Integer.toHexString(checksum07).toUpperCase());
+			logger.debug("校验和16(低8位,FF-求和): {}", Integer.toHexString(checksum16).toUpperCase());
+			logger.debug("修改后拼接校验码: {}", Integer.toHexString(calculatedChecksum).toUpperCase());
+			logger.debug("报文携带校验码: {}", Integer.toHexString(receivedChecksum).toUpperCase());
+
+			if (calculatedChecksum != receivedChecksum) {
+				throw new InvalidMessageException(
+						String.format("数据校验失败:本地计算(07+16)=%04X(07=%02X, 16=%02X),报文携带=%04X",
+								calculatedChecksum, checksum07, checksum16, receivedChecksum));
+			}
+
+			com.zksy.gas.domain.GasMonitorData resultData = DataParser.parseMessage(msgBytes);
+			resultData.setId(java.util.UUID.randomUUID().toString());
 			service.save(resultData);
 			logger.info("数据解析入库成功: {}", resultData);
 		} catch (InvalidMessageException e) {
@@ -68,7 +104,6 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 		}
 	}
 
-
 	private String printHexBinary(byte[] bytes) {
 		StringBuilder sb = new StringBuilder();
 		for (byte b : bytes) {

+ 31 - 21
flammable-gas-service/src/main/java/com/zksy/gas/utils/ProtocolUtils.java

@@ -9,7 +9,7 @@ public class ProtocolUtils {
     /**
      * BCD 转换为十进制
      */
-    public static int bcdToInt(byte b) {
+    private static int bcdToInt(byte b) {
         return ((b >> 4) & 0x0F) * 10 + (b & 0x0F);
     }
 
@@ -17,28 +17,37 @@ public class ProtocolUtils {
      * 解析 6 字节 BCD 时间戳
      * 格式:YY MM DD HH mm ss
      */
-    public static LocalDateTime parseTimestamp(byte[] data, int offset) {
-        int year   = bcdToInt(data[offset]);
-        int month  = bcdToInt(data[offset + 1]);
-        int day    = bcdToInt(data[offset + 2]);
-        int hour   = bcdToInt(data[offset + 3]);
-        int minute = bcdToInt(data[offset + 4]);
-        int second = bcdToInt(data[offset + 5]);
-        return LocalDateTime.of(2000 + year, month, day, hour, minute, second);
+    public static LocalDateTime parseTimestamp(byte[] frame, int offset) {
+        int year = 2000 + bcdToInt(frame[offset]);
+        int month = bcdToInt(frame[offset + 1]);
+        int day = bcdToInt(frame[offset + 2]);
+        int hour = bcdToInt(frame[offset + 3]);
+        int minute = bcdToInt(frame[offset + 4]);
+        int second = bcdToInt(frame[offset + 5]);
+        return LocalDateTime.of(year, month, day, hour, minute, second);
     }
 
+
+
     /**
      * 小端序字节数组转 float
      */
-    public static float bytesToFloatLE(byte[] data, int offset) {
-        byte[] tmp = new byte[4];
-        tmp[0] = data[offset];
-        tmp[1] = data[offset + 1];
-        tmp[2] = data[offset + 2];
-        tmp[3] = data[offset + 3];
-        return ByteBuffer.wrap(tmp).order(ByteOrder.LITTLE_ENDIAN).getFloat();
+    public static float bytesToFloatLE(byte[] bytes, int offset) {
+        int bits = (bytes[offset] & 0xFF)
+                | ((bytes[offset + 1] & 0xFF) << 8)
+                | ((bytes[offset + 2] & 0xFF) << 16)
+                | ((bytes[offset + 3] & 0xFF) << 24);
+        return Float.intBitsToFloat(bits);
+    }
+    public static float readFloat6(byte[] frame, int offset) {
+        // 小端转大端:取后4字节反转后再解析
+        byte[] floatBytes = new byte[4];
+        floatBytes[0] = frame[offset + 5];
+        floatBytes[1] = frame[offset + 4];
+        floatBytes[2] = frame[offset + 3];
+        floatBytes[3] = frame[offset + 2];
+        return ByteBuffer.wrap(floatBytes).order(ByteOrder.BIG_ENDIAN).getFloat();
     }
-
     /**
      * 字节数组转 Hex 字符串
      */
@@ -53,12 +62,13 @@ public class ProtocolUtils {
     /**
      * 16进制字符串转字节数组
      */
-    public static byte[] hexStringToBytes(String s) {
-        int len = s.length();
+    public static byte[] hexStringToBytes(String hex) {
+        hex = hex.replaceAll("\\s+", "");
+        int len = hex.length();
         byte[] data = new byte[len / 2];
         for (int i = 0; i < len; i += 2) {
-            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
-                    + Character.digit(s.charAt(i+1), 16));
+            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+                    + Character.digit(hex.charAt(i + 1), 16));
         }
         return data;
     }

+ 4 - 1
flammable-gas-service/src/main/resources/mapper/GasMonitorDataMapper.xml

@@ -18,8 +18,11 @@
             <result property="reportTime" column="report_time" jdbcType="TIMESTAMP"/>
             <result property="gasConcentration" column="gas_concentration" jdbcType="DECIMAL"/>
             <result property="gasType" column="gas_type" jdbcType="VARCHAR"/>
+            <result property="gasUnit" column="gas_unit" jdbcType="VARCHAR"/>
             <result property="battery" column="battery" jdbcType="DECIMAL"/>
             <result property="signalStrength" column="signal_strength" jdbcType="FLOAT"/>
+            <result property="temperature" column="temperature" jdbcType="FLOAT"/>
+            <result property="humidity" column="humidity" jdbcType="FLOAT"/>
             <result property="longitude" column="longitude" jdbcType="DECIMAL"/>
             <result property="latitude" column="latitude" jdbcType="DECIMAL"/>
             <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
@@ -30,7 +33,7 @@
         short_address,auto_reply_option,sequence_no,
         command,device_attr,protocol_version,
         alarm_info,report_time,gas_concentration,
-        gas_type,battery,signal_strength,
+        gas_type,gas_unit,battery,signal_strength,temperature,humidity
         longitude,latitude,create_time
     </sql>
 </mapper>