Bladeren bron

feat(gas): 更新可燃气体监测数据控制器接口和报警处理

- 添加 @Anonymous 注解支持匿名访问
- 更新所有接口路径添加 /visualization 前缀
- 新增查询设备分组接口
- 修复 Mapper XML 文件中的命名空间和类型映射
- 在井盖服务中实现温度、倾斜、设备、水浸、水位等多类型报警检查
- 在消防压力服务中添加数据回复报文处理功能
- 在可燃气体服务中集成多种报警类型的处理和存储
- 优化井盖服务应用启动配置和组件扫描范围
- 重构消息处理器依赖注入方式提高代码质量
林仔 1 maand geleden
bovenliggende
commit
143551ee51

+ 82 - 1
firefighting-pressure-service/src/main/java/com/zksy/pressure/utils/MessageHandler.java

@@ -2,6 +2,7 @@ package com.zksy.pressure.utils;
 
 import cn.hutool.core.lang.UUID;
 import com.alibaba.fastjson.JSONObject;
+import com.aliyun.apache.hc.client5.http.utils.Hex;
 import com.zksy.api.domain.WarningThreshold;
 import com.zksy.api.service.WarningThresholdService;
 import com.zksy.api.utils.SmsUtil;
@@ -24,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Map;
 
@@ -70,22 +72,101 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 			if (!CRCString.equals(codeString)) {
 				throw new InvalidMessageException("数据校验不成功");
 			} else {
+				String hexString = Hex.encodeHexString(msgBytes).toUpperCase();
+				String uploadEndFlag = hexString.substring(hexString.length() - 6, hexString.length() - 4);
 				logger.info("成功!!!接收到 {} 字节的数据,来自: {}", msgString, ctx.channel().remoteAddress());
 				FirefightingPressure resultData = DataParser.parseMessage(msgString);
+
+				String functionCode = msgString.substring(20, 22);
+				logger.info("生成的回复报文功能码: {}", functionCode);
+				if("EF".equals(functionCode)){
+					String responseMsg = buildResponseMessage(resultData.getTelemeteringStation(), resultData.getCentralStation(), resultData.getPassword(),resultData.getFunctionCode(), "0000", uploadEndFlag);
+					logger.info("生成的回复报文: {}", responseMsg);
+					byte[] messageBytes = hexStringToByteArray(responseMsg);
+					ctx.channel().writeAndFlush(Unpooled.copiedBuffer(messageBytes));
+					return;
+				}
+
 				resultData.setId(UUID.randomUUID().toString());
 				// 更新设备最后一次接收数据的时间
 				//String addressCode = resultData.getAddressCode();
 				//DeviceOfflineCheckTask.deviceLastReceiveTimeMap.put(addressCode, new Date());
 				firefightingPressureService.save(resultData);
 
-				checkIfSmsAlertNeeded(resultData);
+				// 4. 构建回复报文
+				String responseMsg = buildResponseMessage(resultData.getTelemeteringStation(), resultData.getCentralStation(), resultData.getPassword(), resultData.getFunctionCode(), resultData.getSerialNumber(), uploadEndFlag);
+				logger.info("生成的回复报文: {}", responseMsg);
+				byte[] messageBytes = hexStringToByteArray(responseMsg);
+				ctx.channel().writeAndFlush(Unpooled.copiedBuffer(messageBytes));
+				//短信预警
+				//checkIfSmsAlertNeeded(resultData);
 			}
 		} catch (InvalidMessageException e) {
 			logger.error("数据入库失败: {}", e.getMessage());
 			ctx.writeAndFlush(Unpooled.copiedBuffer("数据入库失败".getBytes()));
 		}
 	}
+	private byte[] hexStringToByteArray(String hexString) {
+		// 校验输入合法性
+		if (hexString == null || hexString.length() % 2 != 0) {
+			throw new IllegalArgumentException("十六进制字符串长度必须为偶数,输入:" + hexString);
+		}
+
+		int len = hexString.length();
+		byte[] data = new byte[len / 2];
+		// 每2个十六进制字符转换为1个字节
+		for (int i = 0; i < len; i += 2) {
+			data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+					+ Character.digit(hexString.charAt(i + 1), 16));
+		}
+		return data;
+	}
 
+	/**
+	 * 构建回复报文
+	 * @param telemetryAddr 遥测站地址(来自上传报文)
+	 * @param centerAddr 中心站地址(来自上传报文)
+	 * @param password 密码(来自上传报文)
+	 * @param funcCode 功能码(来自上传报文)
+	 * @param serialNo 流水号(来自上传报文)
+	 * @param uploadEndFlag 上传报文结束符(03/17)
+	 * @return 完整的回复报文十六进制字符串(包含CRC)
+	 */
+	private String buildResponseMessage(String telemetryAddr, String centerAddr, String password,
+										String funcCode, String serialNo, String uploadEndFlag) {
+		// 1. 固定字段
+		String frameHeader = "7E7E"; // 帧头
+		String downLen = "8008";    // 下行/载荷长度
+		String msgStartFlag = "02"; // 报文起始符
+
+		// 2. 动态字段 - 发报时间(当前时间,格式:年月日时分秒,十六进制)
+		String sendTime = DataCheckUtil.getCurrentTimeHex();
+
+		// 3. 回复报文结束符(上传是03则回复04,上传是17则回复06)
+		String responseEndFlag = "04";
+		if ("17".equals(uploadEndFlag)) {
+			responseEndFlag = "06";
+		}
+
+		// 4. 拼接回复报文主体(不含CRC)
+		StringBuilder bodyBuilder = new StringBuilder();
+		bodyBuilder.append(frameHeader)          // 帧头 7E7E
+				.append(telemetryAddr)        // 遥测站地址
+				.append(centerAddr)           // 中心站地址
+				.append(password)             // 密码
+				.append(funcCode)             // 功能码
+				.append(downLen)              // 下行/载荷长度
+				.append(msgStartFlag)         // 报文起始符
+				.append(serialNo)             // 流水号
+				.append(sendTime)             // 发报时间
+				.append(responseEndFlag);     // 报文结束符
+		String body = bodyBuilder.toString();
+		System.out.println("回复报文主体:" + body);
+		// 5. 计算CRC16校验码
+		String crc = DataCheckUtil.crc16Tall(body);
+		// 6. 拼接最终报文(主体 + CRC)
+		return body + crc;
+	}
 	private String printHexBinary(byte[] bytes) {
 		StringBuilder sb = new StringBuilder();
 		for (byte b : bytes) {

+ 61 - 0
flammable-gas-service/src/main/java/com/zksy/gas/utils/MessageHandler.java

@@ -42,6 +42,8 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 	private SmsUtil smsUtil;
 	@Autowired
 	private DevicePhoneFetchUtil devicePhoneFetchUtil;
+	@Autowired
+	private AlarmUtil alarmUtil;
 
 	@Autowired
 	public MessageHandler(GasMonitorDataService firefightingPressureService) {
@@ -177,6 +179,8 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 
 		//用MAC地址作为设备的设备编号
 		String deviceId = resultData.getMacAddress();
+		String deviceName = "可燃气体设备";
+		String deviceType = "flammable-gas";
 
 		//获取温度阈值
 		String deviceWarningCode = GasDeviceCodeEnum.FIREFIGHTING_DEVICE.getCode();
@@ -200,12 +204,18 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 		boolean temperatureIsOverThreshold = temperature > temWarningValue;
 		if(temperatureIsOverThreshold){
 			sendMessage(deviceId,temWarningMsg,resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					temWarningMsg, temWarningCode, BigDecimal.valueOf(temWarningValue),
+					BigDecimal.valueOf(temperature), "可燃气体温度报警");
 		}
 		//判断湿度
 		float humidity = resultData.getHumidity();
 		boolean humidityIsOverThreshold=humidity>humWarningValue;
 		if(humidityIsOverThreshold){
 			sendMessage(deviceId,humWarningMsg,resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					humWarningMsg, humWarningCode, BigDecimal.valueOf(humWarningValue),
+					BigDecimal.valueOf(humidity), "可燃气体湿度报警");
 		}
 		//判断气体浓度
 		BigDecimal gasConcentration = resultData.getGasConcentration();
@@ -213,56 +223,107 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 		boolean concentrationIsOverThreshold=actualGasConcentration>gasWarningValue;
 		if(concentrationIsOverThreshold){
 			sendMessage(deviceId,gasWarningMsg,resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					gasWarningMsg, gasWarningCode, BigDecimal.valueOf(gasWarningValue),
+					gasConcentration, "可燃气体浓度报警");
 		}
 		//获取到的数据逐一进行判断
 		Map<String, Boolean> alarmBits = resultData.parseAlarmBits();
 		if(alarmBits.get("unknownAlarm")){
 			sendMessage(deviceId,"未知报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"未知报警", "WARN-UNKNOWN", null,
+					null, "可燃气体未知报警");
 		}
 		if(alarmBits.get("highAlarm")){
 			sendMessage(deviceId,"高报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"高报警", "WARN-HIGH", null,
+					null, "可燃气体高报警");
 		}
 		if(alarmBits.get("overRange")){
 			sendMessage(deviceId,"超量程",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"超量程", "WARN-OVER-RANGE", null,
+					null, "可燃气体超量程报警");
 		}
 		if(alarmBits.get("calibrationCycle")){
 			sendMessage(deviceId,"标定周期",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"标定周期", "WARN-CALIBRATION-CYCLE", null,
+					null, "可燃气体标定周期报警");
 		}
 		if(alarmBits.get("overLife")){
 			sendMessage(deviceId,"超寿命",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"超寿命", "WARN-OVER-LIFE", null,
+					null, "可燃气体超寿命报警");
 		}
 		if(alarmBits.get("fallAlarm")){
 			sendMessage(deviceId,"跌倒报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"跌倒报警", "WARN-FALL", null,
+					null, "可燃气体跌倒报警");
 		}
 		if(alarmBits.get("undervoltage")){
 			sendMessage(deviceId,"欠压报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"欠压报警", "WARN-UNDERVOLTAGE", null,
+					null, "可燃气体欠压报警");
 		}
 		if(alarmBits.get("rangeAlarm")){
 			sendMessage(deviceId,"区间报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"区间报警", "WARN-RANGE", null,
+					null, "可燃气体区间报警");
 		}
 		if(alarmBits.get("keyAlarm")){
 			sendMessage(deviceId,"按键报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"按键报警", "WARN-KEY", null,
+					null, "可燃气体按键报警");
 		}
 		if(alarmBits.get("vibrationAlarm")){
 			sendMessage(deviceId,"震动报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"震动报警", "WARN-VIBRATION", null,
+					null, "可燃气体震动报警");
 		}
 		if(alarmBits.get("waterLevelAlarm")){
 			sendMessage(deviceId,"水位报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"水位报警", "WARN-WATER-LEVEL", null,
+					null, "可燃气体水位报警");
 		}
 		if(alarmBits.get("powerOffAlarm")){
 			sendMessage(deviceId,"断电报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"断电报警", "WARN-POWER-OFF", null,
+					null, "可燃气体断电报警");
 		}
 		if(alarmBits.get("sensorFault")){
 			sendMessage(deviceId,"传感器故障",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"传感器故障", "WARN-SENSOR-FAULT", null,
+					null, "可燃气体传感器故障");
 		}
 		if(alarmBits.get("overHumidity")){
 			sendMessage(deviceId,"超湿报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"超湿报警", "WARN-OVER-HUMIDITY", null,
+					null, "可燃气体超湿报警");
 		}
 		if(alarmBits.get("overTemperature")){
 			sendMessage(deviceId,"超温报警",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"超温报警", "WARN-OVER-TEMPERATURE", null,
+					null, "可燃气体超温报警");
 		}
 		if(alarmBits.get("systemFault")){
 			sendMessage(deviceId,"系统故障",resultData.getLongitude(),resultData.getLatitude());
+			alarmUtil.saveAlarm(deviceName, deviceId, deviceType,
+					"系统故障", "WARN-SYSTEM-FAULT", null,
+					null, "可燃气体系统故障");
 		}
 	}
 	public Double checkTemAndHum(String deviceWarningCode,String warningCode){

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

@@ -2,9 +2,9 @@
 <!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.zksy.mapper.com.zksy.telemetry.GasMonitorDataMapper">
+<mapper namespace="com.zksy.gas.mapper.GasMonitorDataMapper">
 
-    <resultMap id="BaseResultMap" type="com.zksy.domain.com.zksy.telemetry.GasMonitorData">
+    <resultMap id="BaseResultMap" type="com.zksy.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"/>

+ 13 - 3
manhole-service/src/main/java/com/zksy/manhole/ManholeApplication.java

@@ -5,9 +5,19 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.scheduling.annotation.EnableScheduling;
 
-@SpringBootApplication
-@MapperScan(basePackages = "com.zksy.manhole.mapper")
-@EnableScheduling
+@MapperScan({
+        "com.zksy.manhole.mapper",
+        "com.zksy.base.mapper",
+        "com.zksy.system.mapper"
+})
+@SpringBootApplication(scanBasePackages = {
+        "com.zksy.manhole",
+        "com.zksy.api",
+        "com.zksy.base.service",
+        "com.zksy.system.service",
+        "com.zksy.common",
+        "com.zksy.utils"
+})
 public class ManholeApplication {
     public static void main(String[] args) {
         SpringApplication.run(ManholeApplication.class, args);

+ 60 - 5
manhole-service/src/main/java/com/zksy/manhole/service/impl/ManholeDataServiceImpl.java

@@ -3,18 +3,17 @@ package com.zksy.manhole.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zksy.manhole.domain.BaseDevices;
 import com.zksy.manhole.domain.ManholeData;
-import com.zksy.manhole.mapper.BaseDevicesMapper;
 import com.zksy.manhole.mapper.ManholeDataMapper;
-import com.zksy.manhole.service.BaseDevicesService;
 import com.zksy.manhole.service.ManholeDataService;
+import com.zksy.manhole.utils.AlarmUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.web.client.RestTemplate;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -30,7 +29,9 @@ public class ManholeDataServiceImpl extends ServiceImpl<ManholeDataMapper, Manho
     @Autowired
     private ManholeDataMapper manholeDataMapper;
     @Autowired
-    private BaseDevicesService baseDevicesService;
+    private AlarmUtil alarmUtil;
+    //@Autowired
+    //private BaseDevicesService baseDevicesService;
     @Autowired
     private RestTemplate restTemplate;
 
@@ -40,7 +41,7 @@ public class ManholeDataServiceImpl extends ServiceImpl<ManholeDataMapper, Manho
     public Integer saveManholeData(ManholeData result) {
         Integer resultData = manholeDataMapper.insert(result);
         // 更新设备状态为在线
-        baseDevicesService.getByDeviceNumberStatus(result.getImeiCardNumber(),0,1);
+        //baseDevicesService.getByDeviceNumberStatus(result.getImeiCardNumber(),0,1);
 
         /*if(resultData > 0 && Integer.parseInt(result.getTiltAngle()) > 60) {
             String url = BASE_URL + "/push/pushAll?msg=" + result.getTiltAngle();
@@ -52,6 +53,60 @@ public class ManholeDataServiceImpl extends ServiceImpl<ManholeDataMapper, Manho
                 e.printStackTrace();
             }
         }*/
+
+        if (resultData > 0) {
+            checkAndSaveAlarm(result);
+        }
+
         return resultData;
     }
+
+    private void checkAndSaveAlarm(ManholeData result) {
+        String deviceCode = result.getImeiCardNumber();
+        String deviceName = "井盖设备";
+        String deviceType = "manhole";
+
+        if (result.getTemperatureValue() != null && !result.getTemperatureValue().isEmpty()) {
+            try {
+                alarmUtil.checkAndSaveAlarm(deviceName, deviceCode, deviceType,
+                        "温度预警", "WARN-TEMPERATURE", 50.0,
+                        new BigDecimal(result.getTemperatureValue()),
+                        "井盖温度报警");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (result.getTiltAngle() != null && !result.getTiltAngle().isEmpty() 
+                && result.getAngleAlarmThreshold() != null && !result.getAngleAlarmThreshold().isEmpty()) {
+            try {
+                double tiltAngle = Double.parseDouble(result.getTiltAngle());
+                double threshold = Double.parseDouble(result.getAngleAlarmThreshold());
+                alarmUtil.checkAndSaveAlarm(deviceName, deviceCode, deviceType,
+                        "倾斜预警", "WARN-TILT", threshold,
+                        BigDecimal.valueOf(tiltAngle),
+                        "井盖倾斜报警");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        if ("1".equals(result.getAlarmStatus())) {
+            alarmUtil.saveAlarm(deviceName, deviceCode, deviceType,
+                    "设备报警", "WARN-DEVICE-ALARM", null,
+                    null, "井盖设备状态报警");
+        }
+
+        if ("1".equals(result.getWaterInfiltrationAlarmStatus())) {
+            alarmUtil.saveAlarm(deviceName, deviceCode, deviceType,
+                    "水浸预警", "WARN-WATER-INFILTRATION", null,
+                    null, "井盖水浸报警");
+        }
+
+        if ("1".equals(result.getWaterLevelAlarmStatus())) {
+            alarmUtil.saveAlarm(deviceName, deviceCode, deviceType,
+                    "水位预警", "WARN-WATER-LEVEL", null,
+                    null, "井盖水位报警");
+        }
+    }
 }

+ 7 - 4
manhole-service/src/main/java/com/zksy/manhole/utils/MessageHandler.java

@@ -5,6 +5,7 @@ import com.zksy.manhole.domain.ManholeData;
 import com.zksy.manhole.service.ManholeDataService;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.handler.timeout.ReadTimeoutException;
@@ -12,18 +13,20 @@ import lombok.extern.slf4j.Slf4j;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
 import java.util.Date;
 
+@ChannelHandler.Sharable
 @Slf4j
+@Component
 public class MessageHandler extends ChannelInboundHandlerAdapter {
 	private static Logger logger = LoggerFactory.getLogger(MessageHandler.class);
 
-	@Autowired
-	private ManholeDataService messageParseResultService;
+	private final ManholeDataService messageParseResultService;
 
-	public MessageHandler() {
-		this.messageParseResultService = SpringContextUtil.getBean(ManholeDataService.class);
+	public MessageHandler(ManholeDataService messageParseResultService) {
+		this.messageParseResultService = messageParseResultService;
 	}
 
 	@Override

+ 16 - 6
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/base/gas/GasMonitorDataController.java

@@ -2,6 +2,7 @@ package com.zksy.web.controller.base.gas;
 
 import com.zksy.base.gas.domain.GasMonitorData;
 import com.zksy.base.gas.service.GasMonitorDataService;
+import com.zksy.common.annotation.Anonymous;
 import com.zksy.common.core.domain.AjaxResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -20,6 +21,7 @@ import java.util.Map;
 @Api(tags = "可燃气体监测数据", description = "可燃气体监测数据接口")
 @RestController
 @RequestMapping("/api/gas")
+@Anonymous
 public class GasMonitorDataController {
 
     @Autowired
@@ -31,7 +33,7 @@ public class GasMonitorDataController {
     /**
      * 查询最新一条监测数据
      */
-    @GetMapping("/latest/{deviceId}")
+    @GetMapping("/visualization/latest/{deviceId}")
     @ApiOperation("查询最新一条监测数据")
     public AjaxResult getLatestData(@PathVariable String deviceId) {
         return AjaxResult.success(gasMonitorDataService.getLatestByDeviceId(deviceId));
@@ -40,7 +42,7 @@ public class GasMonitorDataController {
     /**
      * 按天统计(均值、最大值、最小值)
      */
-    @GetMapping("/daily-stats/{deviceId}")
+    @GetMapping("/visualization/daily-stats/{deviceId}")
     @ApiOperation("按天统计(均值、最大值、最小值)")
     public AjaxResult getDailyStats(@PathVariable String deviceId,@RequestParam String date) {
         LocalDate localDate = LocalDate.parse(date, DATE_FORMAT);
@@ -50,7 +52,7 @@ public class GasMonitorDataController {
     /**
      * 设备在线率统计
      */
-    @GetMapping("/online-rate")
+    @GetMapping("/visualization/online-rate")
     @ApiOperation("设备在线率统计")
     public AjaxResult getOnlineRate(@RequestParam String deviceId,@RequestParam String start,@RequestParam String end) {
         LocalDate startDate = LocalDate.parse(start, DATE_FORMAT);
@@ -61,7 +63,7 @@ public class GasMonitorDataController {
     /**
      * 查询报警数据
      */
-    @GetMapping("/alarms")
+    @GetMapping("/visualization/alarms")
     @ApiOperation("查询报警数据")
     public AjaxResult getAlarms(@RequestParam String deviceId,@RequestParam String start,@RequestParam String end, @RequestParam(defaultValue = "1000") double threshold) {
         LocalDateTime startTime = LocalDateTime.parse(start, DATETIME_FORMAT);
@@ -72,7 +74,7 @@ public class GasMonitorDataController {
     /**
      * 设备趋势分析(折线图用)
      */
-    @GetMapping("/trend")
+    @GetMapping("/visualization/trend")
     @ApiOperation("设备趋势分析")
     public AjaxResult getTrend(@RequestParam String deviceId, @RequestParam String start, @RequestParam String end) {
         LocalDateTime startTime = LocalDateTime.parse(start, DATETIME_FORMAT);
@@ -82,7 +84,7 @@ public class GasMonitorDataController {
     /**
      * 查询最新数据(含报警位解析)
      */
-    @GetMapping("/latest-with-alarm/{deviceId}")
+    @GetMapping("/visualization/latest-with-alarm/{deviceId}")
     @ApiOperation("查询最新数据(含报警位解析)")
     public AjaxResult getLatestWithAlarm(@PathVariable String deviceId) {
         GasMonitorData data = gasMonitorDataService.getLatestWithAlarmBits(deviceId);
@@ -94,4 +96,12 @@ public class GasMonitorDataController {
         result.put("otherData", data);
         return AjaxResult.success(result);
     }
+    /**
+     * 查询设备分组
+     */
+    @GetMapping("/visualization/device-group")
+    @ApiOperation("查询设备分组")
+    public AjaxResult getDeviceGroup() {
+        return AjaxResult.success(gasMonitorDataService.getDeviceGroup());
+    }
 }

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

@@ -27,4 +27,8 @@ public interface GasMonitorDataService extends IService<GasMonitorData> {
      * 查询带报警位解析的监测数据
      */
     GasMonitorData getLatestWithAlarmBits(String deviceId);
+    /**
+     * 查询设备分组
+     */
+    List<String> getDeviceGroup();
 }

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

@@ -114,6 +114,17 @@ public class GasMonitorDataServiceImpl extends ServiceImpl<GasMonitorDataMapper,
         }
         return data;
     }
+
+    @Override
+    public List<String> getDeviceGroup() {
+        return lambdaQuery()
+                .select(GasMonitorData::getMacAddress)
+                .groupBy(GasMonitorData::getMacAddress)
+                .list()
+                .stream()
+                .map(GasMonitorData::getMacAddress)
+                .collect(Collectors.toList());
+    }
 }
 
 

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

@@ -2,9 +2,9 @@
 <!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.zksy.base.mapper.com.zksy.telemetry.GasMonitorDataMapper">
+<mapper namespace="com.zksy.base.gas.mapper.GasMonitorDataMapper">
 
-    <resultMap id="BaseResultMap" type="com.zksy.base.domain.com.zksy.telemetry.GasMonitorData">
+    <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"/>

+ 7 - 5
telemetry-service/src/main/java/com/zksy/telemetry/utils/MessageHandler.java

@@ -62,12 +62,10 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 				if (msgBytes.length > 34) {
 					byte flagByte = msgBytes[34];
 					String binary = String.format("%8s", Integer.toBinaryString(flagByte & 0xFF)).replace(' ', '0');
-					boolean isEnd = (flagByte & 0b00001000) != 0;
+					boolean isEnd = (flagByte & 0x08) != 0;
 
 					logger.debug("结束位检测: 字节=0x{}, 二进制={}, 第5位(结束标识)={}",
 							String.format("%02X", flagByte), binary, isEnd ? "1(结束)" : "0(未结束)");
-
-					resultData.setIsLastPacket(isEnd);
 				} else {
 					logger.warn("数据长度不足,无法检测结束位");
 				}
@@ -78,6 +76,10 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 				// 如果结束位=1,可以根据需要在此处理关闭逻辑
 				if (Boolean.TRUE.equals(resultData.getIsLastPacket())) {
 					logger.info("检测到结束标志帧,执行结束处理逻辑...");
+					byte[] response = ProtocolUtils.buildCustomReplyFrame(msgBytes);
+					ctx.writeAndFlush(Unpooled.copiedBuffer(response));
+					Thread.sleep(1000);// 休眠1秒
+					logger.debug("已发送结束应答帧上报自定义回应包: {}", printHexBinary(response));
 					byte[] endResponse = ProtocolUtils.buildEndReplyFrame(msgBytes);
 					ctx.writeAndFlush(Unpooled.copiedBuffer(endResponse));
 					logger.debug("已发送结束应答帧: {}", printHexBinary(endResponse));
@@ -89,10 +91,10 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 				}
 
 			} else if (frameType == 0x34) {
-				logger.info("收到结束通讯帧,准备发送回应包");
+				/*logger.info("收到结束通讯帧,准备发送回应包");
 				byte[] response = ProtocolUtils.buildShutdownAckPacket(msgBytes);
 				ctx.writeAndFlush(Unpooled.copiedBuffer(response));
-				logger.debug("已发送结束通讯回应包");
+				logger.debug("已发送结束通讯回应包");*/
 
 			} else {
 				logger.warn("收到未知帧类型: 0x{}", String.format("%02X", frameType));