Procházet zdrojové kódy

feat(gas): 新增可燃气体监测数据处理模块

- 创建 GasMonitorData 实体类,映射可燃气体监测仪数据表
- 实现 GasMonitorDataMapper 接口,支持基础 CRUD 操作及在线天数统计
- 配置 MyBatis XML 映射文件,定义结果映射和字段列表
- 定义 GasMonitorDataService 接口,提供数据查询与统计分析方法
- 实现 GasMonitorDataServiceImpl服务类,包含最新数据、日报统计、在线率计算、报警记录及趋势分析功能
- 添加 ProtocolUtils 工具类,支持 BCD 转换、时间戳解析、小端序浮点数转换及 Hex 字符串处理
林仔 před 7 měsíci
rodič
revize
0c444c35af

+ 65 - 0
flammable-gas-service/src/main/java/com/zksy/gas/utils/ProtocolUtils.java

@@ -0,0 +1,65 @@
+package com.zksy.gas.utils;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.time.LocalDateTime;
+
+public class ProtocolUtils {
+
+    /**
+     * BCD 转换为十进制
+     */
+    public static int bcdToInt(byte b) {
+        return ((b >> 4) & 0x0F) * 10 + (b & 0x0F);
+    }
+
+    /**
+     * 解析 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);
+    }
+
+    /**
+     * 小端序字节数组转 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();
+    }
+
+    /**
+     * 字节数组转 Hex 字符串
+     */
+    public static String bytesToHex(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            sb.append(String.format("%02X", b));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 16进制字符串转字节数组
+     */
+    public static byte[] hexStringToBytes(String s) {
+        int len = s.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));
+        }
+        return data;
+    }
+}

+ 132 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/gas/domain/GasMonitorData.java

@@ -0,0 +1,132 @@
+package com.zksy.base.gas.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 可燃气体监测仪数据表
+ * @TableName gas_monitor_data
+ */
+@TableName(value ="gas_monitor_data")
+@Data
+public class GasMonitorData implements Serializable {
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_UUID)
+    private String id;
+
+    /**
+     * 帧类型
+     */
+    @TableField(value = "frame_type")
+    private Integer frameType;
+
+    /**
+     * MAC地址
+     */
+    @TableField(value = "mac_address")
+    private String macAddress;
+
+    /**
+     * 16位短地址
+     */
+    @TableField(value = "short_address")
+    private String shortAddress;
+
+    /**
+     * 自动应答选项
+     */
+    @TableField(value = "auto_reply_option")
+    private Integer autoReplyOption;
+
+    /**
+     * 数据帧序号
+     */
+    @TableField(value = "sequence_no")
+    private Integer sequenceNo;
+
+    /**
+     * 命令字
+     */
+    @TableField(value = "command")
+    private Integer command;
+
+    /**
+     * 设备属性
+     */
+    @TableField(value = "device_attr")
+    private Integer deviceAttr;
+
+    /**
+     * 协议版本
+     */
+    @TableField(value = "protocol_version")
+    private String protocolVersion;
+
+    /**
+     * 报警信息(解析后二进制)
+     */
+    @TableField(value = "alarm_info")
+    private Integer alarmInfo;
+
+    /**
+     * 设备上报时间戳
+     */
+    @TableField(value = "report_time")
+    private LocalDateTime reportTime;
+
+    /**
+     * 气体浓度(%LEL)
+     */
+    @TableField(value = "gas_concentration")
+    private BigDecimal gasConcentration;
+
+    /**
+     * 气体类型(如CH4)
+     */
+    @TableField(value = "gas_type")
+    private String gasType;
+
+    /**
+     * 电池电量
+     */
+    @TableField(value = "battery")
+    private BigDecimal battery;
+
+    /**
+     * 信号强度
+     */
+    @TableField(value = "signal_strength")
+    private Float signalStrength;
+
+    /**
+     * 经度
+     */
+    @TableField(value = "longitude")
+    private BigDecimal longitude;
+
+    /**
+     * 纬度
+     */
+    @TableField(value = "latitude")
+    private BigDecimal latitude;
+
+    /**
+     * 入库时间
+     */
+    @TableField(value = "create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+}

+ 28 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/gas/mapper/GasMonitorDataMapper.java

@@ -0,0 +1,28 @@
+package com.zksy.base.gas.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zksy.base.gas.domain.GasMonitorData;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.time.LocalDateTime;
+
+/**
+* @author Administrator
+* @description 针对表【gas_monitor_data(可燃气体监测仪数据表)】的数据库操作Mapper
+* @createDate 2025-09-19 14:51:16
+* @Entity com.zksy.gas.domain.GasMonitorData
+*/
+public interface GasMonitorDataMapper extends BaseMapper<GasMonitorData> {
+    @Select("SELECT COUNT(DISTINCT DATE(create_time)) " +
+            "FROM gas_monitor_data " +
+            "WHERE mac_address = #{deviceId} " +
+            "AND create_time BETWEEN #{start} AND #{end}")
+    long countDistinctOnlineDays(@Param("deviceId") String deviceId,
+                                 @Param("start") LocalDateTime start,
+                                 @Param("end") LocalDateTime end);
+}
+
+
+
+

+ 26 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/gas/service/GasMonitorDataService.java

@@ -0,0 +1,26 @@
+package com.zksy.base.gas.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zksy.base.gas.domain.GasMonitorData;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+/**
+* @author Administrator
+* @description 针对表【gas_monitor_data(可燃气体监测仪数据表)】的数据库操作Service
+* @createDate 2025-09-19 14:51:16
+*/
+public interface GasMonitorDataService extends IService<GasMonitorData> {
+    GasMonitorData getLatestByDeviceId(String deviceId);
+
+    Map<String, Object> getDailyStats(String deviceId, LocalDate date);
+
+    double getOnlineRate(String deviceId, LocalDate start, LocalDate end);
+
+    List<GasMonitorData> getAlarms(String deviceId, LocalDateTime start, LocalDateTime end, double threshold);
+
+    List<Map<String, Object>> getTrend(String deviceId, LocalDateTime start, LocalDateTime end);
+}

+ 108 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/gas/service/impl/GasMonitorDataServiceImpl.java

@@ -0,0 +1,108 @@
+package com.zksy.base.gas.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zksy.base.gas.domain.GasMonitorData;
+import com.zksy.base.gas.mapper.GasMonitorDataMapper;
+import com.zksy.base.gas.service.GasMonitorDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+* @author Administrator
+* @description 针对表【gas_monitor_data(可燃气体监测仪数据表)】的数据库操作Service实现
+* @createDate 2025-09-19 14:51:16
+*/
+@Service
+public class GasMonitorDataServiceImpl extends ServiceImpl<GasMonitorDataMapper, GasMonitorData>
+    implements GasMonitorDataService {
+    @Autowired
+    private GasMonitorDataMapper gasMonitorDataMapper;
+    @Override
+    public GasMonitorData getLatestByDeviceId(String deviceId) {
+        return lambdaQuery()
+                .eq(GasMonitorData::getMacAddress, deviceId) // 用 macAddress 或者设备ID字段
+                .orderByDesc(GasMonitorData::getCreateTime)
+                .last("limit 1")
+                .one();
+    }
+
+    @Override
+    public Map<String, Object> getDailyStats(String deviceId, LocalDate date) {
+        LocalDateTime start = date.atStartOfDay();
+        LocalDateTime end = start.plusDays(1);
+
+        List<Double> values = lambdaQuery()
+                .select(GasMonitorData::getGasConcentration)
+                .eq(GasMonitorData::getMacAddress, deviceId)
+                .between(GasMonitorData::getCreateTime, start, end)
+                .list()
+                .stream()
+                .map(v -> v.getGasConcentration() == null ? 0 : v.getGasConcentration().doubleValue())
+                .collect(Collectors.toList());
+
+        Map<String, Object> result = new HashMap<>();
+        if (!values.isEmpty()) {
+            result.put("avg", values.stream().mapToDouble(Double::doubleValue).average().orElse(0));
+            result.put("max", Collections.max(values));
+            result.put("min", Collections.min(values));
+        } else {
+            result.put("avg", 0);
+            result.put("max", 0);
+            result.put("min", 0);
+        }
+        return result;
+    }
+
+    @Override
+    public double getOnlineRate(String deviceId, LocalDate start, LocalDate end) {
+        LocalDateTime startTime = start.atStartOfDay();
+        LocalDateTime endTime = end.plusDays(1).atStartOfDay();
+
+        long totalDays = ChronoUnit.DAYS.between(start, end) + 1;
+        if (totalDays <= 0) {
+            return 0d;
+        }
+
+        long onlineDays = gasMonitorDataMapper.countDistinctOnlineDays(deviceId, startTime, endTime);
+        return (double) onlineDays / (double) totalDays;
+    }
+
+    @Override
+    public List<GasMonitorData> getAlarms(String deviceId, LocalDateTime start, LocalDateTime end, double threshold) {
+        return lambdaQuery()
+                .eq(GasMonitorData::getMacAddress, deviceId)
+                .between(GasMonitorData::getCreateTime, start, end)
+                .gt(GasMonitorData::getGasConcentration, threshold)
+                .list();
+    }
+
+    @Override
+    public List<Map<String, Object>> getTrend(String deviceId, LocalDateTime start, LocalDateTime end) {
+        return lambdaQuery()
+                .eq(GasMonitorData::getMacAddress, deviceId)
+                .between(GasMonitorData::getCreateTime, start, end)
+                .orderByAsc(GasMonitorData::getCreateTime)
+                .list()
+                .stream()
+                .map(data -> {
+                    Map<String, Object> map = new HashMap<>();
+                    map.put("time", data.getCreateTime());
+                    map.put("value", data.getGasConcentration());
+                    return map;
+                })
+                .collect(Collectors.toList());
+    }
+}
+
+
+
+

+ 36 - 0
pipe-network-service/zksy-system/src/main/resources/mapper/base/gas/GasMonitorDataMapper.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zksy.base.gas.mapper.GasMonitorDataMapper">
+
+    <resultMap id="BaseResultMap" type="com.zksy.base.gas.domain.GasMonitorData">
+            <id property="id" column="id" jdbcType="VARCHAR"/>
+            <result property="frameType" column="frame_type" jdbcType="TINYINT"/>
+            <result property="macAddress" column="mac_address" jdbcType="VARCHAR"/>
+            <result property="shortAddress" column="short_address" jdbcType="VARCHAR"/>
+            <result property="autoReplyOption" column="auto_reply_option" jdbcType="TINYINT"/>
+            <result property="sequenceNo" column="sequence_no" jdbcType="INTEGER"/>
+            <result property="command" column="command" jdbcType="TINYINT"/>
+            <result property="deviceAttr" column="device_attr" jdbcType="TINYINT"/>
+            <result property="protocolVersion" column="protocol_version" jdbcType="VARCHAR"/>
+            <result property="alarmInfo" column="alarm_info" jdbcType="INTEGER"/>
+            <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="battery" column="battery" jdbcType="DECIMAL"/>
+            <result property="signalStrength" column="signal_strength" jdbcType="FLOAT"/>
+            <result property="longitude" column="longitude" jdbcType="DECIMAL"/>
+            <result property="latitude" column="latitude" jdbcType="DECIMAL"/>
+            <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,frame_type,mac_address,
+        short_address,auto_reply_option,sequence_no,
+        command,device_attr,protocol_version,
+        alarm_info,report_time,gas_concentration,
+        gas_type,battery,signal_strength,
+        longitude,latitude,create_time
+    </sql>
+</mapper>