From 6d4022743503484c1d4137f6fd81123c4e4cdc8e Mon Sep 17 00:00:00 2001 From: wenshijun Date: Tue, 7 Apr 2026 07:59:11 +0800 Subject: [PATCH] =?UTF-8?q?mqtt=E6=A8=A1=E5=9D=97=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/shzg/WornApplication.java | 3 +- .../shzg/framework/config/SecurityConfig.java | 1 + .../controller/DeviceCommandController.java | 36 +++ .../MqttSensorCommandController.java | 104 +++++++ .../controller/MqttSensorDataController.java | 104 +++++++ .../MqttSensorDeviceController.java | 104 +++++++ .../controller/MqttSensorEventController.java | 104 +++++++ .../MqttSensorThresholdController.java | 104 +++++++ .../worn/domain/MqttSensorCommand.java | 150 +++++++++++ .../project/worn/domain/MqttSensorData.java | 254 ++++++++++++++++++ .../project/worn/domain/MqttSensorDevice.java | 132 +++++++++ .../project/worn/domain/MqttSensorEvent.java | 132 +++++++++ .../worn/domain/MqttSensorThreshold.java | 179 ++++++++++++ .../worn/domain/device/DeviceTypeEnum.java | 19 ++ .../worn/mapper/MqttSensorCommandMapper.java | 61 +++++ .../worn/mapper/MqttSensorDataMapper.java | 61 +++++ .../worn/mapper/MqttSensorDeviceMapper.java | 70 +++++ .../worn/mapper/MqttSensorEventMapper.java | 61 +++++ .../mapper/MqttSensorThresholdMapper.java | 69 +++++ .../worn/sensor/config/MqttClientConfig.java | 95 ++----- .../worn/sensor/config/MqttProperties.java | 21 +- .../worn/sensor/config/MqttPublishClient.java | 117 +++++--- .../dispatcher/MqttMessageDispatcher.java | 137 +++++++--- .../sensor/mqtt/handler/EnvSensorHandler.java | 251 +++++++++++++++++ .../mqtt/handler/HumiSensorHandler.java | 43 --- .../mqtt/handler/SmokeSensorHandler.java | 135 ++++++++-- .../mqtt/handler/TempSensorHandler.java | 46 ---- .../mqtt/handler/WaterSensorHandler.java | 214 +++++++++++++++ .../worn/service/IDeviceCommandService.java | 8 + .../service/IMqttSensorCommandService.java | 61 +++++ .../worn/service/IMqttSensorDataService.java | 61 +++++ .../service/IMqttSensorDeviceService.java | 68 +++++ .../worn/service/IMqttSensorEventService.java | 61 +++++ .../service/IMqttSensorThresholdService.java | 69 +++++ .../impl/DeviceCommandServiceImpl.java | 147 ++++++++++ .../impl/MqttSensorCommandServiceImpl.java | 96 +++++++ .../impl/MqttSensorDataServiceImpl.java | 96 +++++++ .../impl/MqttSensorDeviceServiceImpl.java | 130 +++++++++ .../impl/MqttSensorEventServiceImpl.java | 96 +++++++ .../impl/MqttSensorThresholdServiceImpl.java | 108 ++++++++ .../project/worn/unit/MqttDeviceCache.java | 68 +++++ .../worn/unit/MqttThreadPoolConfig.java | 22 ++ src/main/resources/application.yml | 32 +-- .../mybatis/worn/MqttSensorCommandMapper.xml | 106 ++++++++ .../mybatis/worn/MqttSensorDataMapper.xml | 155 +++++++++++ .../mybatis/worn/MqttSensorDeviceMapper.xml | 108 ++++++++ .../mybatis/worn/MqttSensorEventMapper.xml | 101 +++++++ .../worn/MqttSensorThresholdMapper.xml | 126 +++++++++ 48 files changed, 4228 insertions(+), 298 deletions(-) create mode 100644 src/main/java/com/shzg/project/worn/controller/DeviceCommandController.java create mode 100644 src/main/java/com/shzg/project/worn/controller/MqttSensorCommandController.java create mode 100644 src/main/java/com/shzg/project/worn/controller/MqttSensorDataController.java create mode 100644 src/main/java/com/shzg/project/worn/controller/MqttSensorDeviceController.java create mode 100644 src/main/java/com/shzg/project/worn/controller/MqttSensorEventController.java create mode 100644 src/main/java/com/shzg/project/worn/controller/MqttSensorThresholdController.java create mode 100644 src/main/java/com/shzg/project/worn/domain/MqttSensorCommand.java create mode 100644 src/main/java/com/shzg/project/worn/domain/MqttSensorData.java create mode 100644 src/main/java/com/shzg/project/worn/domain/MqttSensorDevice.java create mode 100644 src/main/java/com/shzg/project/worn/domain/MqttSensorEvent.java create mode 100644 src/main/java/com/shzg/project/worn/domain/MqttSensorThreshold.java create mode 100644 src/main/java/com/shzg/project/worn/domain/device/DeviceTypeEnum.java create mode 100644 src/main/java/com/shzg/project/worn/mapper/MqttSensorCommandMapper.java create mode 100644 src/main/java/com/shzg/project/worn/mapper/MqttSensorDataMapper.java create mode 100644 src/main/java/com/shzg/project/worn/mapper/MqttSensorDeviceMapper.java create mode 100644 src/main/java/com/shzg/project/worn/mapper/MqttSensorEventMapper.java create mode 100644 src/main/java/com/shzg/project/worn/mapper/MqttSensorThresholdMapper.java create mode 100644 src/main/java/com/shzg/project/worn/sensor/mqtt/handler/EnvSensorHandler.java delete mode 100644 src/main/java/com/shzg/project/worn/sensor/mqtt/handler/HumiSensorHandler.java delete mode 100644 src/main/java/com/shzg/project/worn/sensor/mqtt/handler/TempSensorHandler.java create mode 100644 src/main/java/com/shzg/project/worn/sensor/mqtt/handler/WaterSensorHandler.java create mode 100644 src/main/java/com/shzg/project/worn/service/IDeviceCommandService.java create mode 100644 src/main/java/com/shzg/project/worn/service/IMqttSensorCommandService.java create mode 100644 src/main/java/com/shzg/project/worn/service/IMqttSensorDataService.java create mode 100644 src/main/java/com/shzg/project/worn/service/IMqttSensorDeviceService.java create mode 100644 src/main/java/com/shzg/project/worn/service/IMqttSensorEventService.java create mode 100644 src/main/java/com/shzg/project/worn/service/IMqttSensorThresholdService.java create mode 100644 src/main/java/com/shzg/project/worn/service/impl/DeviceCommandServiceImpl.java create mode 100644 src/main/java/com/shzg/project/worn/service/impl/MqttSensorCommandServiceImpl.java create mode 100644 src/main/java/com/shzg/project/worn/service/impl/MqttSensorDataServiceImpl.java create mode 100644 src/main/java/com/shzg/project/worn/service/impl/MqttSensorDeviceServiceImpl.java create mode 100644 src/main/java/com/shzg/project/worn/service/impl/MqttSensorEventServiceImpl.java create mode 100644 src/main/java/com/shzg/project/worn/service/impl/MqttSensorThresholdServiceImpl.java create mode 100644 src/main/java/com/shzg/project/worn/unit/MqttDeviceCache.java create mode 100644 src/main/java/com/shzg/project/worn/unit/MqttThreadPoolConfig.java create mode 100644 src/main/resources/mybatis/worn/MqttSensorCommandMapper.xml create mode 100644 src/main/resources/mybatis/worn/MqttSensorDataMapper.xml create mode 100644 src/main/resources/mybatis/worn/MqttSensorDeviceMapper.xml create mode 100644 src/main/resources/mybatis/worn/MqttSensorEventMapper.xml create mode 100644 src/main/resources/mybatis/worn/MqttSensorThresholdMapper.xml diff --git a/src/main/java/com/shzg/WornApplication.java b/src/main/java/com/shzg/WornApplication.java index c2889de..d3165f2 100644 --- a/src/main/java/com/shzg/WornApplication.java +++ b/src/main/java/com/shzg/WornApplication.java @@ -3,6 +3,7 @@ package com.shzg; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.scheduling.annotation.EnableScheduling; /** * 启动程序 @@ -10,11 +11,11 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; * @author wisdom */ @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +@EnableScheduling public class WornApplication { public static void main(String[] args) { - // System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(WornApplication.class, args); System.out.println("(♥◠‿◠)ノ゙ Wisdom 系统启动成功 ლ(´ڡ`ლ)゙ \n" + diff --git a/src/main/java/com/shzg/framework/config/SecurityConfig.java b/src/main/java/com/shzg/framework/config/SecurityConfig.java index 013c2ef..4f85d56 100644 --- a/src/main/java/com/shzg/framework/config/SecurityConfig.java +++ b/src/main/java/com/shzg/framework/config/SecurityConfig.java @@ -114,6 +114,7 @@ public class SecurityConfig requests.antMatchers("/login", "/register", "/unique/code/**", + "/device/cmd/**", "/captchaImage").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() diff --git a/src/main/java/com/shzg/project/worn/controller/DeviceCommandController.java b/src/main/java/com/shzg/project/worn/controller/DeviceCommandController.java new file mode 100644 index 0000000..6b283df --- /dev/null +++ b/src/main/java/com/shzg/project/worn/controller/DeviceCommandController.java @@ -0,0 +1,36 @@ +package com.shzg.project.worn.controller; + +import com.shzg.framework.web.controller.BaseController; +import com.shzg.framework.web.domain.AjaxResult; +import com.shzg.project.worn.service.IDeviceCommandService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/device/cmd") +public class DeviceCommandController extends BaseController { + + @Autowired + private IDeviceCommandService deviceCommandService; + + /** + * 烟雾传感器 - 持续消警 + */ + @PostMapping("/smoke/stop") + public AjaxResult smokeStop(@RequestParam Long deviceId) { + deviceCommandService.smokeStop(deviceId); + return AjaxResult.success("消警指令已发送"); + } + + /** + * 烟雾传感器 - 单次消警(80秒) + */ + @PostMapping("/smoke/stopOnce") + public AjaxResult smokeStopOnce(@RequestParam Long deviceId) { + deviceCommandService.smokeStopOnce(deviceId); + return AjaxResult.success("单次消警指令已发送"); + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/controller/MqttSensorCommandController.java b/src/main/java/com/shzg/project/worn/controller/MqttSensorCommandController.java new file mode 100644 index 0000000..03842ee --- /dev/null +++ b/src/main/java/com/shzg/project/worn/controller/MqttSensorCommandController.java @@ -0,0 +1,104 @@ +package com.shzg.project.worn.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.shzg.framework.aspectj.lang.annotation.Log; +import com.shzg.framework.aspectj.lang.enums.BusinessType; +import com.shzg.project.worn.domain.MqttSensorCommand; +import com.shzg.project.worn.service.IMqttSensorCommandService; +import com.shzg.framework.web.controller.BaseController; +import com.shzg.framework.web.domain.AjaxResult; +import com.shzg.common.utils.poi.ExcelUtil; +import com.shzg.framework.web.page.TableDataInfo; + +/** + * 指令管理Controller + * + * @author shzg + * @date 2026-04-01 + */ +@RestController +@RequestMapping("/worn/mqttCommand") +public class MqttSensorCommandController extends BaseController +{ + @Autowired + private IMqttSensorCommandService mqttSensorCommandService; + + /** + * 查询指令管理列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttCommand:list')") + @GetMapping("/list") + public TableDataInfo list(MqttSensorCommand mqttSensorCommand) + { + startPage(); + List list = mqttSensorCommandService.selectMqttSensorCommandList(mqttSensorCommand); + return getDataTable(list); + } + + /** + * 导出指令管理列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttCommand:export')") + @Log(title = "指令管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MqttSensorCommand mqttSensorCommand) + { + List list = mqttSensorCommandService.selectMqttSensorCommandList(mqttSensorCommand); + ExcelUtil util = new ExcelUtil(MqttSensorCommand.class); + util.exportExcel(response, list, "指令管理数据"); + } + + /** + * 获取指令管理详细信息 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttCommand:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(mqttSensorCommandService.selectMqttSensorCommandById(id)); + } + + /** + * 新增指令管理 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttCommand:add')") + @Log(title = "指令管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MqttSensorCommand mqttSensorCommand) + { + return toAjax(mqttSensorCommandService.insertMqttSensorCommand(mqttSensorCommand)); + } + + /** + * 修改指令管理 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttCommand:edit')") + @Log(title = "指令管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MqttSensorCommand mqttSensorCommand) + { + return toAjax(mqttSensorCommandService.updateMqttSensorCommand(mqttSensorCommand)); + } + + /** + * 删除指令管理 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttCommand:remove')") + @Log(title = "指令管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(mqttSensorCommandService.deleteMqttSensorCommandByIds(ids)); + } +} diff --git a/src/main/java/com/shzg/project/worn/controller/MqttSensorDataController.java b/src/main/java/com/shzg/project/worn/controller/MqttSensorDataController.java new file mode 100644 index 0000000..8a20fee --- /dev/null +++ b/src/main/java/com/shzg/project/worn/controller/MqttSensorDataController.java @@ -0,0 +1,104 @@ +package com.shzg.project.worn.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.shzg.framework.aspectj.lang.annotation.Log; +import com.shzg.framework.aspectj.lang.enums.BusinessType; +import com.shzg.project.worn.domain.MqttSensorData; +import com.shzg.project.worn.service.IMqttSensorDataService; +import com.shzg.framework.web.controller.BaseController; +import com.shzg.framework.web.domain.AjaxResult; +import com.shzg.common.utils.poi.ExcelUtil; +import com.shzg.framework.web.page.TableDataInfo; + +/** + * 设备数据Controller + * + * @author shzg + * @date 2026-04-01 + */ +@RestController +@RequestMapping("/worn/mqttData") +public class MqttSensorDataController extends BaseController +{ + @Autowired + private IMqttSensorDataService mqttSensorDataService; + + /** + * 查询设备数据列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttData:list')") + @GetMapping("/list") + public TableDataInfo list(MqttSensorData mqttSensorData) + { + startPage(); + List list = mqttSensorDataService.selectMqttSensorDataList(mqttSensorData); + return getDataTable(list); + } + + /** + * 导出设备数据列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttData:export')") + @Log(title = "设备数据", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MqttSensorData mqttSensorData) + { + List list = mqttSensorDataService.selectMqttSensorDataList(mqttSensorData); + ExcelUtil util = new ExcelUtil(MqttSensorData.class); + util.exportExcel(response, list, "设备数据数据"); + } + + /** + * 获取设备数据详细信息 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttData:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(mqttSensorDataService.selectMqttSensorDataById(id)); + } + + /** + * 新增设备数据 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttData:add')") + @Log(title = "设备数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MqttSensorData mqttSensorData) + { + return toAjax(mqttSensorDataService.insertMqttSensorData(mqttSensorData)); + } + + /** + * 修改设备数据 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttData:edit')") + @Log(title = "设备数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MqttSensorData mqttSensorData) + { + return toAjax(mqttSensorDataService.updateMqttSensorData(mqttSensorData)); + } + + /** + * 删除设备数据 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttData:remove')") + @Log(title = "设备数据", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(mqttSensorDataService.deleteMqttSensorDataByIds(ids)); + } +} diff --git a/src/main/java/com/shzg/project/worn/controller/MqttSensorDeviceController.java b/src/main/java/com/shzg/project/worn/controller/MqttSensorDeviceController.java new file mode 100644 index 0000000..c8dfc8a --- /dev/null +++ b/src/main/java/com/shzg/project/worn/controller/MqttSensorDeviceController.java @@ -0,0 +1,104 @@ +package com.shzg.project.worn.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.shzg.framework.aspectj.lang.annotation.Log; +import com.shzg.framework.aspectj.lang.enums.BusinessType; +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.service.IMqttSensorDeviceService; +import com.shzg.framework.web.controller.BaseController; +import com.shzg.framework.web.domain.AjaxResult; +import com.shzg.common.utils.poi.ExcelUtil; +import com.shzg.framework.web.page.TableDataInfo; + +/** + * MQTT设备Controller + * + * @author shzg + * @date 2026-04-01 + */ +@RestController +@RequestMapping("/worn/mqttDevice") +public class MqttSensorDeviceController extends BaseController +{ + @Autowired + private IMqttSensorDeviceService mqttSensorDeviceService; + + /** + * 查询MQTT设备列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttDevice:list')") + @GetMapping("/list") + public TableDataInfo list(MqttSensorDevice mqttSensorDevice) + { + startPage(); + List list = mqttSensorDeviceService.selectMqttSensorDeviceList(mqttSensorDevice); + return getDataTable(list); + } + + /** + * 导出MQTT设备列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttDevice:export')") + @Log(title = "MQTT设备", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MqttSensorDevice mqttSensorDevice) + { + List list = mqttSensorDeviceService.selectMqttSensorDeviceList(mqttSensorDevice); + ExcelUtil util = new ExcelUtil(MqttSensorDevice.class); + util.exportExcel(response, list, "MQTT设备数据"); + } + + /** + * 获取MQTT设备详细信息 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttDevice:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(mqttSensorDeviceService.selectMqttSensorDeviceById(id)); + } + + /** + * 新增MQTT设备 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttDevice:add')") + @Log(title = "MQTT设备", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MqttSensorDevice mqttSensorDevice) + { + return toAjax(mqttSensorDeviceService.insertMqttSensorDevice(mqttSensorDevice)); + } + + /** + * 修改MQTT设备 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttDevice:edit')") + @Log(title = "MQTT设备", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MqttSensorDevice mqttSensorDevice) + { + return toAjax(mqttSensorDeviceService.updateMqttSensorDevice(mqttSensorDevice)); + } + + /** + * 删除MQTT设备 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttDevice:remove')") + @Log(title = "MQTT设备", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(mqttSensorDeviceService.deleteMqttSensorDeviceByIds(ids)); + } +} diff --git a/src/main/java/com/shzg/project/worn/controller/MqttSensorEventController.java b/src/main/java/com/shzg/project/worn/controller/MqttSensorEventController.java new file mode 100644 index 0000000..67bbfee --- /dev/null +++ b/src/main/java/com/shzg/project/worn/controller/MqttSensorEventController.java @@ -0,0 +1,104 @@ +package com.shzg.project.worn.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.shzg.framework.aspectj.lang.annotation.Log; +import com.shzg.framework.aspectj.lang.enums.BusinessType; +import com.shzg.project.worn.domain.MqttSensorEvent; +import com.shzg.project.worn.service.IMqttSensorEventService; +import com.shzg.framework.web.controller.BaseController; +import com.shzg.framework.web.domain.AjaxResult; +import com.shzg.common.utils.poi.ExcelUtil; +import com.shzg.framework.web.page.TableDataInfo; + +/** + * 设备事件Controller + * + * @author shzg + * @date 2026-04-01 + */ +@RestController +@RequestMapping("/worn/mqttEvent") +public class MqttSensorEventController extends BaseController +{ + @Autowired + private IMqttSensorEventService mqttSensorEventService; + + /** + * 查询设备事件列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttEvent:list')") + @GetMapping("/list") + public TableDataInfo list(MqttSensorEvent mqttSensorEvent) + { + startPage(); + List list = mqttSensorEventService.selectMqttSensorEventList(mqttSensorEvent); + return getDataTable(list); + } + + /** + * 导出设备事件列表 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttEvent:export')") + @Log(title = "设备事件", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MqttSensorEvent mqttSensorEvent) + { + List list = mqttSensorEventService.selectMqttSensorEventList(mqttSensorEvent); + ExcelUtil util = new ExcelUtil(MqttSensorEvent.class); + util.exportExcel(response, list, "设备事件数据"); + } + + /** + * 获取设备事件详细信息 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttEvent:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(mqttSensorEventService.selectMqttSensorEventById(id)); + } + + /** + * 新增设备事件 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttEvent:add')") + @Log(title = "设备事件", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MqttSensorEvent mqttSensorEvent) + { + return toAjax(mqttSensorEventService.insertMqttSensorEvent(mqttSensorEvent)); + } + + /** + * 修改设备事件 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttEvent:edit')") + @Log(title = "设备事件", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MqttSensorEvent mqttSensorEvent) + { + return toAjax(mqttSensorEventService.updateMqttSensorEvent(mqttSensorEvent)); + } + + /** + * 删除设备事件 + */ + @PreAuthorize("@ss.hasPermi('worn:mqttEvent:remove')") + @Log(title = "设备事件", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(mqttSensorEventService.deleteMqttSensorEventByIds(ids)); + } +} diff --git a/src/main/java/com/shzg/project/worn/controller/MqttSensorThresholdController.java b/src/main/java/com/shzg/project/worn/controller/MqttSensorThresholdController.java new file mode 100644 index 0000000..ee7fb67 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/controller/MqttSensorThresholdController.java @@ -0,0 +1,104 @@ +package com.shzg.project.worn.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.shzg.framework.aspectj.lang.annotation.Log; +import com.shzg.framework.aspectj.lang.enums.BusinessType; +import com.shzg.project.worn.domain.MqttSensorThreshold; +import com.shzg.project.worn.service.IMqttSensorThresholdService; +import com.shzg.framework.web.controller.BaseController; +import com.shzg.framework.web.domain.AjaxResult; +import com.shzg.common.utils.poi.ExcelUtil; +import com.shzg.framework.web.page.TableDataInfo; + +/** + * 传感器阈值配置Controller + * + * @author shzg + * @date 2026-04-01 + */ +@RestController +@RequestMapping("/worn/threshold") +public class MqttSensorThresholdController extends BaseController +{ + @Autowired + private IMqttSensorThresholdService mqttSensorThresholdService; + + /** + * 查询传感器阈值配置列表 + */ + @PreAuthorize("@ss.hasPermi('worn:threshold:list')") + @GetMapping("/list") + public TableDataInfo list(MqttSensorThreshold mqttSensorThreshold) + { + startPage(); + List list = mqttSensorThresholdService.selectMqttSensorThresholdList(mqttSensorThreshold); + return getDataTable(list); + } + + /** + * 导出传感器阈值配置列表 + */ + @PreAuthorize("@ss.hasPermi('worn:threshold:export')") + @Log(title = "传感器阈值配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MqttSensorThreshold mqttSensorThreshold) + { + List list = mqttSensorThresholdService.selectMqttSensorThresholdList(mqttSensorThreshold); + ExcelUtil util = new ExcelUtil(MqttSensorThreshold.class); + util.exportExcel(response, list, "传感器阈值配置数据"); + } + + /** + * 获取传感器阈值配置详细信息 + */ + @PreAuthorize("@ss.hasPermi('worn:threshold:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(mqttSensorThresholdService.selectMqttSensorThresholdById(id)); + } + + /** + * 新增传感器阈值配置 + */ + @PreAuthorize("@ss.hasPermi('worn:threshold:add')") + @Log(title = "传感器阈值配置", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MqttSensorThreshold mqttSensorThreshold) + { + return toAjax(mqttSensorThresholdService.insertMqttSensorThreshold(mqttSensorThreshold)); + } + + /** + * 修改传感器阈值配置 + */ + @PreAuthorize("@ss.hasPermi('worn:threshold:edit')") + @Log(title = "传感器阈值配置", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MqttSensorThreshold mqttSensorThreshold) + { + return toAjax(mqttSensorThresholdService.updateMqttSensorThreshold(mqttSensorThreshold)); + } + + /** + * 删除传感器阈值配置 + */ + @PreAuthorize("@ss.hasPermi('worn:threshold:remove')") + @Log(title = "传感器阈值配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(mqttSensorThresholdService.deleteMqttSensorThresholdByIds(ids)); + } +} diff --git a/src/main/java/com/shzg/project/worn/domain/MqttSensorCommand.java b/src/main/java/com/shzg/project/worn/domain/MqttSensorCommand.java new file mode 100644 index 0000000..8a25ded --- /dev/null +++ b/src/main/java/com/shzg/project/worn/domain/MqttSensorCommand.java @@ -0,0 +1,150 @@ +package com.shzg.project.worn.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.shzg.framework.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.shzg.framework.aspectj.lang.annotation.Excel; + +/** + * 指令管理对象 mqtt_sensor_command + * + * @author shzg + * @date 2026-04-01 + */ +public class MqttSensorCommand extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 设备ID */ + @Excel(name = "设备ID") + private Long deviceId; + + /** 下发Topic */ + @Excel(name = "下发Topic") + private String topic; + + /** 指令类型 */ + @Excel(name = "指令类型") + private String command; + + /** 指令JSON */ + @Excel(name = "指令JSON") + private String payload; + + /** 状态(0待发送 1成功 2失败) */ + @Excel(name = "状态", readConverterExp = "0=待发送,1=成功,2=失败") + private String status; + + /** 发送时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "发送时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date sendTime; + + /** 删除标识 */ + @Excel(name = "删除标识") + private String isDelete; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setDeviceId(Long deviceId) + { + this.deviceId = deviceId; + } + + public Long getDeviceId() + { + return deviceId; + } + + public void setTopic(String topic) + { + this.topic = topic; + } + + public String getTopic() + { + return topic; + } + + public void setCommand(String command) + { + this.command = command; + } + + public String getCommand() + { + return command; + } + + public void setPayload(String payload) + { + this.payload = payload; + } + + public String getPayload() + { + return payload; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setSendTime(Date sendTime) + { + this.sendTime = sendTime; + } + + public Date getSendTime() + { + return sendTime; + } + + public void setIsDelete(String isDelete) + { + this.isDelete = isDelete; + } + + public String getIsDelete() + { + return isDelete; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("deviceId", getDeviceId()) + .append("topic", getTopic()) + .append("command", getCommand()) + .append("payload", getPayload()) + .append("status", getStatus()) + .append("sendTime", getSendTime()) + .append("remark", getRemark()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("isDelete", getIsDelete()) + .toString(); + } +} diff --git a/src/main/java/com/shzg/project/worn/domain/MqttSensorData.java b/src/main/java/com/shzg/project/worn/domain/MqttSensorData.java new file mode 100644 index 0000000..94490bc --- /dev/null +++ b/src/main/java/com/shzg/project/worn/domain/MqttSensorData.java @@ -0,0 +1,254 @@ +package com.shzg.project.worn.domain; + +import java.math.BigDecimal; + +import com.shzg.framework.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.shzg.framework.aspectj.lang.annotation.Excel; + +/** + * 设备数据对象 mqtt_sensor_data + * + * @author shzg + * @date 2026-04-01 + */ +public class MqttSensorData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 设备ID */ + @Excel(name = "设备ID") + private Long deviceId; + + /** MQTT Topic */ + @Excel(name = "MQTT Topic") + private String topic; + + /** 项目(如 tangshan) */ + @Excel(name = "项目", readConverterExp = "如=,t=angshan") + private String project; + + /** 仓库(如 dianchi) */ + @Excel(name = "仓库", readConverterExp = "如=,d=ianchi") + private String warehouse; + + /** 原始消息 */ + @Excel(name = "原始消息") + private String payload; + + /** 解析JSON */ + @Excel(name = "解析JSON") + private String dataJson; + + /** 电量 */ + @Excel(name = "电量") + private Long battery; + + /** 温度 */ + @Excel(name = "温度") + private BigDecimal temperature; + + /** 湿度 */ + @Excel(name = "湿度") + private BigDecimal humidity; + + /** 氨气 */ + @Excel(name = "氨气") + private BigDecimal nh3; + + /** 硫化氢 */ + @Excel(name = "硫化氢") + private BigDecimal h2s; + + /** 烟雾浓度 */ + @Excel(name = "烟雾浓度") + private Long concentration; + + /** ✅ 水浸状态(0=正常,1=报警) */ + @Excel(name = "水浸状态") + private Integer waterStatus; + + /** 删除标识 */ + @Excel(name = "删除标识") + private String isDelete; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setDeviceId(Long deviceId) + { + this.deviceId = deviceId; + } + + public Long getDeviceId() + { + return deviceId; + } + + public void setTopic(String topic) + { + this.topic = topic; + } + + public String getTopic() + { + return topic; + } + + public void setProject(String project) + { + this.project = project; + } + + public String getProject() + { + return project; + } + + public void setWarehouse(String warehouse) + { + this.warehouse = warehouse; + } + + public String getWarehouse() + { + return warehouse; + } + + public void setPayload(String payload) + { + this.payload = payload; + } + + public String getPayload() + { + return payload; + } + + public void setDataJson(String dataJson) + { + this.dataJson = dataJson; + } + + public String getDataJson() + { + return dataJson; + } + + public void setBattery(Long battery) + { + this.battery = battery; + } + + public Long getBattery() + { + return battery; + } + + public void setTemperature(BigDecimal temperature) + { + this.temperature = temperature; + } + + public BigDecimal getTemperature() + { + return temperature; + } + + public void setHumidity(BigDecimal humidity) + { + this.humidity = humidity; + } + + public BigDecimal getHumidity() + { + return humidity; + } + + public void setNh3(BigDecimal nh3) + { + this.nh3 = nh3; + } + + public BigDecimal getNh3() + { + return nh3; + } + + public void setH2s(BigDecimal h2s) + { + this.h2s = h2s; + } + + public BigDecimal getH2s() + { + return h2s; + } + + public void setConcentration(Long concentration) + { + this.concentration = concentration; + } + + public Long getConcentration() + { + return concentration; + } + + public void setWaterStatus(Integer waterStatus) + { + this.waterStatus = waterStatus; + } + + public Integer getWaterStatus() + { + return waterStatus; + } + + public void setIsDelete(String isDelete) + { + this.isDelete = isDelete; + } + + public String getIsDelete() + { + return isDelete; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("deviceId", getDeviceId()) + .append("topic", getTopic()) + .append("project", getProject()) + .append("warehouse", getWarehouse()) + .append("payload", getPayload()) + .append("dataJson", getDataJson()) + .append("battery", getBattery()) + .append("temperature", getTemperature()) + .append("humidity", getHumidity()) + .append("nh3", getNh3()) + .append("h2s", getH2s()) + .append("concentration", getConcentration()) + .append("waterStatus", getWaterStatus()) + .append("remark", getRemark()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("isDelete", getIsDelete()) + .toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/domain/MqttSensorDevice.java b/src/main/java/com/shzg/project/worn/domain/MqttSensorDevice.java new file mode 100644 index 0000000..c5743e6 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/domain/MqttSensorDevice.java @@ -0,0 +1,132 @@ +package com.shzg.project.worn.domain; + +import com.shzg.framework.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.shzg.framework.aspectj.lang.annotation.Excel; + +/** + * MQTT设备对象 mqtt_sensor_device + * + * @author shzg + * @date 2026-04-01 + */ +public class MqttSensorDevice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 设备唯一标识(DevEUI) */ + @Excel(name = "设备唯一标识", readConverterExp = "D=evEUI") + private String devEui; + + /** 设备名称 */ + @Excel(name = "设备名称") + private String deviceName; + + /** 设备类型(smoke / env) */ + @Excel(name = "设备类型", readConverterExp = "s=moke,/=,e=nv") + private String deviceType; + + /** 所属部门ID */ + @Excel(name = "所属部门ID") + private Long deptId; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标识(0正常 1删除) */ + @Excel(name = "删除标识", readConverterExp = "0=正常,1=删除") + private String isDelete; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setDevEui(String devEui) + { + this.devEui = devEui; + } + + public String getDevEui() + { + return devEui; + } + + public void setDeviceName(String deviceName) + { + this.deviceName = deviceName; + } + + public String getDeviceName() + { + return deviceName; + } + + public void setDeviceType(String deviceType) + { + this.deviceType = deviceType; + } + + public String getDeviceType() + { + return deviceType; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setIsDelete(String isDelete) + { + this.isDelete = isDelete; + } + + public String getIsDelete() + { + return isDelete; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("devEui", getDevEui()) + .append("deviceName", getDeviceName()) + .append("deviceType", getDeviceType()) + .append("deptId", getDeptId()) + .append("status", getStatus()) + .append("remark", getRemark()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("isDelete", getIsDelete()) + .toString(); + } +} diff --git a/src/main/java/com/shzg/project/worn/domain/MqttSensorEvent.java b/src/main/java/com/shzg/project/worn/domain/MqttSensorEvent.java new file mode 100644 index 0000000..3fa568e --- /dev/null +++ b/src/main/java/com/shzg/project/worn/domain/MqttSensorEvent.java @@ -0,0 +1,132 @@ +package com.shzg.project.worn.domain; + +import com.shzg.framework.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.shzg.framework.aspectj.lang.annotation.Excel; + +/** + * 设备事件对象 mqtt_sensor_event + * + * @author shzg + * @date 2026-04-01 + */ +public class MqttSensorEvent extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 设备ID */ + @Excel(name = "设备ID") + private Long deviceId; + + /** 事件类型(alarm / threshold / normal) */ + @Excel(name = "事件类型", readConverterExp = "a=larm,/=,t=hreshold,/=,n=ormal") + private String eventType; + + /** 事件描述 */ + @Excel(name = "事件描述") + private String eventDesc; + + /** 等级(LOW/MEDIUM/HIGH) */ + @Excel(name = "等级", readConverterExp = "L=OW/MEDIUM/HIGH") + private String level; + + /** 处理状态(0未处理 1已处理) */ + @Excel(name = "处理状态", readConverterExp = "0=未处理,1=已处理") + private String status; + + /** 删除标识 */ + @Excel(name = "删除标识") + private String isDelete; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setDeviceId(Long deviceId) + { + this.deviceId = deviceId; + } + + public Long getDeviceId() + { + return deviceId; + } + + public void setEventType(String eventType) + { + this.eventType = eventType; + } + + public String getEventType() + { + return eventType; + } + + public void setEventDesc(String eventDesc) + { + this.eventDesc = eventDesc; + } + + public String getEventDesc() + { + return eventDesc; + } + + public void setLevel(String level) + { + this.level = level; + } + + public String getLevel() + { + return level; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setIsDelete(String isDelete) + { + this.isDelete = isDelete; + } + + public String getIsDelete() + { + return isDelete; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("deviceId", getDeviceId()) + .append("eventType", getEventType()) + .append("eventDesc", getEventDesc()) + .append("level", getLevel()) + .append("status", getStatus()) + .append("remark", getRemark()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("isDelete", getIsDelete()) + .toString(); + } +} diff --git a/src/main/java/com/shzg/project/worn/domain/MqttSensorThreshold.java b/src/main/java/com/shzg/project/worn/domain/MqttSensorThreshold.java new file mode 100644 index 0000000..d39f7c0 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/domain/MqttSensorThreshold.java @@ -0,0 +1,179 @@ +package com.shzg.project.worn.domain; + +import java.math.BigDecimal; + +import com.shzg.framework.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.shzg.framework.aspectj.lang.annotation.Excel; + +/** + * 传感器阈值配置对象 mqtt_sensor_threshold + * + * @author shzg + * @date 2026-04-01 + */ +public class MqttSensorThreshold extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 设备ID(关联 mqtt_sensor_device.id) */ + @Excel(name = "设备ID", readConverterExp = "关=联,m=qtt_sensor_device.id") + private Long deviceId; + + /** 指标类型(temperature/humidity/nh3/h2s/battery) */ + @Excel(name = "指标类型", readConverterExp = "t=emperature/humidity/nh3/h2s/battery") + private String metricType; + + /** 预警最小值 */ + @Excel(name = "预警最小值") + private BigDecimal warnMin; + + /** 预警最大值 */ + @Excel(name = "预警最大值") + private BigDecimal warnMax; + + /** 报警最小值 */ + @Excel(name = "报警最小值") + private BigDecimal alarmMin; + + /** 报警最大值 */ + @Excel(name = "报警最大值") + private BigDecimal alarmMax; + + /** 单位(℃/%/ppm) */ + @Excel(name = "单位", readConverterExp = "℃=/%/ppm") + private String unit; + + /** 状态(1启用 0停用) */ + @Excel(name = "状态", readConverterExp = "1=启用,0=停用") + private String status; + + /** 是否删除(0正常 1删除) */ + @Excel(name = "是否删除", readConverterExp = "0=正常,1=删除") + private String isDelete; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setDeviceId(Long deviceId) + { + this.deviceId = deviceId; + } + + public Long getDeviceId() + { + return deviceId; + } + + public void setMetricType(String metricType) + { + this.metricType = metricType; + } + + public String getMetricType() + { + return metricType; + } + + public void setWarnMin(BigDecimal warnMin) + { + this.warnMin = warnMin; + } + + public BigDecimal getWarnMin() + { + return warnMin; + } + + public void setWarnMax(BigDecimal warnMax) + { + this.warnMax = warnMax; + } + + public BigDecimal getWarnMax() + { + return warnMax; + } + + public void setAlarmMin(BigDecimal alarmMin) + { + this.alarmMin = alarmMin; + } + + public BigDecimal getAlarmMin() + { + return alarmMin; + } + + public void setAlarmMax(BigDecimal alarmMax) + { + this.alarmMax = alarmMax; + } + + public BigDecimal getAlarmMax() + { + return alarmMax; + } + + public void setUnit(String unit) + { + this.unit = unit; + } + + public String getUnit() + { + return unit; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setIsDelete(String isDelete) + { + this.isDelete = isDelete; + } + + public String getIsDelete() + { + return isDelete; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("deviceId", getDeviceId()) + .append("metricType", getMetricType()) + .append("warnMin", getWarnMin()) + .append("warnMax", getWarnMax()) + .append("alarmMin", getAlarmMin()) + .append("alarmMax", getAlarmMax()) + .append("unit", getUnit()) + .append("status", getStatus()) + .append("remark", getRemark()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("isDelete", getIsDelete()) + .toString(); + } +} diff --git a/src/main/java/com/shzg/project/worn/domain/device/DeviceTypeEnum.java b/src/main/java/com/shzg/project/worn/domain/device/DeviceTypeEnum.java new file mode 100644 index 0000000..ffdd5bf --- /dev/null +++ b/src/main/java/com/shzg/project/worn/domain/device/DeviceTypeEnum.java @@ -0,0 +1,19 @@ +package com.shzg.project.worn.domain.device; + +public enum DeviceTypeEnum { + + SMOKE("smoke", "烟雾传感器"), + ENV("env", "环境传感器"); + + private final String code; + private final String name; + + DeviceTypeEnum(String code, String name) { + this.code = code; + this.name = name; + } + + public String getCode() { + return code; + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/mapper/MqttSensorCommandMapper.java b/src/main/java/com/shzg/project/worn/mapper/MqttSensorCommandMapper.java new file mode 100644 index 0000000..51bd0bd --- /dev/null +++ b/src/main/java/com/shzg/project/worn/mapper/MqttSensorCommandMapper.java @@ -0,0 +1,61 @@ +package com.shzg.project.worn.mapper; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorCommand; + +/** + * 指令管理Mapper接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface MqttSensorCommandMapper +{ + /** + * 查询指令管理 + * + * @param id 指令管理主键 + * @return 指令管理 + */ + public MqttSensorCommand selectMqttSensorCommandById(Long id); + + /** + * 查询指令管理列表 + * + * @param mqttSensorCommand 指令管理 + * @return 指令管理集合 + */ + public List selectMqttSensorCommandList(MqttSensorCommand mqttSensorCommand); + + /** + * 新增指令管理 + * + * @param mqttSensorCommand 指令管理 + * @return 结果 + */ + public int insertMqttSensorCommand(MqttSensorCommand mqttSensorCommand); + + /** + * 修改指令管理 + * + * @param mqttSensorCommand 指令管理 + * @return 结果 + */ + public int updateMqttSensorCommand(MqttSensorCommand mqttSensorCommand); + + /** + * 删除指令管理 + * + * @param id 指令管理主键 + * @return 结果 + */ + public int deleteMqttSensorCommandById(Long id); + + /** + * 批量删除指令管理 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteMqttSensorCommandByIds(Long[] ids); +} diff --git a/src/main/java/com/shzg/project/worn/mapper/MqttSensorDataMapper.java b/src/main/java/com/shzg/project/worn/mapper/MqttSensorDataMapper.java new file mode 100644 index 0000000..1b1495d --- /dev/null +++ b/src/main/java/com/shzg/project/worn/mapper/MqttSensorDataMapper.java @@ -0,0 +1,61 @@ +package com.shzg.project.worn.mapper; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorData; + +/** + * 设备数据Mapper接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface MqttSensorDataMapper +{ + /** + * 查询设备数据 + * + * @param id 设备数据主键 + * @return 设备数据 + */ + public MqttSensorData selectMqttSensorDataById(Long id); + + /** + * 查询设备数据列表 + * + * @param mqttSensorData 设备数据 + * @return 设备数据集合 + */ + public List selectMqttSensorDataList(MqttSensorData mqttSensorData); + + /** + * 新增设备数据 + * + * @param mqttSensorData 设备数据 + * @return 结果 + */ + public int insertMqttSensorData(MqttSensorData mqttSensorData); + + /** + * 修改设备数据 + * + * @param mqttSensorData 设备数据 + * @return 结果 + */ + public int updateMqttSensorData(MqttSensorData mqttSensorData); + + /** + * 删除设备数据 + * + * @param id 设备数据主键 + * @return 结果 + */ + public int deleteMqttSensorDataById(Long id); + + /** + * 批量删除设备数据 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteMqttSensorDataByIds(Long[] ids); +} diff --git a/src/main/java/com/shzg/project/worn/mapper/MqttSensorDeviceMapper.java b/src/main/java/com/shzg/project/worn/mapper/MqttSensorDeviceMapper.java new file mode 100644 index 0000000..3b4a51b --- /dev/null +++ b/src/main/java/com/shzg/project/worn/mapper/MqttSensorDeviceMapper.java @@ -0,0 +1,70 @@ +package com.shzg.project.worn.mapper; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorDevice; +import io.lettuce.core.dynamic.annotation.Param; + +/** + * MQTT设备Mapper接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface MqttSensorDeviceMapper +{ + /** + * 查询MQTT设备 + * + * @param id MQTT设备主键 + * @return MQTT设备 + */ + public MqttSensorDevice selectMqttSensorDeviceById(Long id); + + /** + * 查询MQTT设备列表 + * + * @param mqttSensorDevice MQTT设备 + * @return MQTT设备集合 + */ + public List selectMqttSensorDeviceList(MqttSensorDevice mqttSensorDevice); + + /** + * 新增MQTT设备 + * + * @param mqttSensorDevice MQTT设备 + * @return 结果 + */ + public int insertMqttSensorDevice(MqttSensorDevice mqttSensorDevice); + + /** + * 修改MQTT设备 + * + * @param mqttSensorDevice MQTT设备 + * @return 结果 + */ + public int updateMqttSensorDevice(MqttSensorDevice mqttSensorDevice); + + /** + * 删除MQTT设备 + * + * @param id MQTT设备主键 + * @return 结果 + */ + public int deleteMqttSensorDeviceById(Long id); + + /** + * 批量删除MQTT设备 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteMqttSensorDeviceByIds(Long[] ids); + + /** + * 根据devEui查询设备 + * @param + * @return + */ + MqttSensorDevice selectByDevEui(@Param("devEui") String devEui); + +} diff --git a/src/main/java/com/shzg/project/worn/mapper/MqttSensorEventMapper.java b/src/main/java/com/shzg/project/worn/mapper/MqttSensorEventMapper.java new file mode 100644 index 0000000..7252246 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/mapper/MqttSensorEventMapper.java @@ -0,0 +1,61 @@ +package com.shzg.project.worn.mapper; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorEvent; + +/** + * 设备事件Mapper接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface MqttSensorEventMapper +{ + /** + * 查询设备事件 + * + * @param id 设备事件主键 + * @return 设备事件 + */ + public MqttSensorEvent selectMqttSensorEventById(Long id); + + /** + * 查询设备事件列表 + * + * @param mqttSensorEvent 设备事件 + * @return 设备事件集合 + */ + public List selectMqttSensorEventList(MqttSensorEvent mqttSensorEvent); + + /** + * 新增设备事件 + * + * @param mqttSensorEvent 设备事件 + * @return 结果 + */ + public int insertMqttSensorEvent(MqttSensorEvent mqttSensorEvent); + + /** + * 修改设备事件 + * + * @param mqttSensorEvent 设备事件 + * @return 结果 + */ + public int updateMqttSensorEvent(MqttSensorEvent mqttSensorEvent); + + /** + * 删除设备事件 + * + * @param id 设备事件主键 + * @return 结果 + */ + public int deleteMqttSensorEventById(Long id); + + /** + * 批量删除设备事件 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteMqttSensorEventByIds(Long[] ids); +} diff --git a/src/main/java/com/shzg/project/worn/mapper/MqttSensorThresholdMapper.java b/src/main/java/com/shzg/project/worn/mapper/MqttSensorThresholdMapper.java new file mode 100644 index 0000000..d9d6f04 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/mapper/MqttSensorThresholdMapper.java @@ -0,0 +1,69 @@ +package com.shzg.project.worn.mapper; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorThreshold; + +/** + * 传感器阈值配置Mapper接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface MqttSensorThresholdMapper +{ + /** + * 查询传感器阈值配置 + * + * @param id 传感器阈值配置主键 + * @return 传感器阈值配置 + */ + public MqttSensorThreshold selectMqttSensorThresholdById(Long id); + + /** + * 查询传感器阈值配置列表 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 传感器阈值配置集合 + */ + public List selectMqttSensorThresholdList(MqttSensorThreshold mqttSensorThreshold); + + /** + * 新增传感器阈值配置 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 结果 + */ + public int insertMqttSensorThreshold(MqttSensorThreshold mqttSensorThreshold); + + /** + * 修改传感器阈值配置 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 结果 + */ + public int updateMqttSensorThreshold(MqttSensorThreshold mqttSensorThreshold); + + /** + * 删除传感器阈值配置 + * + * @param id 传感器阈值配置主键 + * @return 结果 + */ + public int deleteMqttSensorThresholdById(Long id); + + /** + * 批量删除传感器阈值配置 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteMqttSensorThresholdByIds(Long[] ids); + + /** + * 根据设备id和指标类型查询阈值配置 + * @param deviceId + * @param metricType + * @return + */ + MqttSensorThreshold selectByDeviceAndMetric(Long deviceId, String metricType); +} diff --git a/src/main/java/com/shzg/project/worn/sensor/config/MqttClientConfig.java b/src/main/java/com/shzg/project/worn/sensor/config/MqttClientConfig.java index 5e1246a..6bed523 100644 --- a/src/main/java/com/shzg/project/worn/sensor/config/MqttClientConfig.java +++ b/src/main/java/com/shzg/project/worn/sensor/config/MqttClientConfig.java @@ -1,4 +1,4 @@ -package com.shzg.project.worn.sensor.config;//package com.zg.project.wisdom.config; +package com.shzg.project.worn.sensor.config; import com.shzg.project.worn.sensor.mqtt.dispatcher.MqttMessageDispatcher; import lombok.extern.slf4j.Slf4j; @@ -10,73 +10,44 @@ import org.springframework.context.annotation.Configuration; import javax.annotation.PreDestroy; import javax.annotation.Resource; import java.nio.charset.StandardCharsets; +import java.util.UUID; /** * MQTT 客户端初始化配置类 - * - * 作用说明: - * 1. Spring Boot 启动时自动创建 MQTT 客户端 - * 2. 连接 EMQX Broker - * 3. 订阅 application.yml 中配置的 Topic(支持通配符) - * 4. 接收传感器上报的数据 - * - * 说明: - * - 同一个 MQTT Client 同时支持「订阅(接收)」和「发布(下发指令)」 - * - 后端一般只需要一个 Client */ @Slf4j @Configuration public class MqttClientConfig { - /** - * MQTT 客户端实例 - * 由本配置类统一维护 - */ private MqttClient mqttClient; @Resource private MqttMessageDispatcher mqttMessageDispatcher; - /** - * 创建 MQTT Client Bean - * - * @param props MQTT 配置(来自 application.yml) - */ @Bean public MqttClient mqttClient(MqttProperties props) throws MqttException { - // 如果未开启 MQTT 功能,直接不初始化 - // 注意:此时 mqttClient Bean 为 null if (!props.isEnabled()) { log.warn("[MQTT] mqtt.enabled=false,MQTT 功能未启用"); return null; } - // 1. 创建 MQTT 客户端 + // ✅ clientId 动态生成 + String clientId = "worn-backend-" + UUID.randomUUID(); + mqttClient = new MqttClient( props.getBroker(), - props.getClientId(), + clientId, new MemoryPersistence() ); - // 2. MQTT 连接参数 + // 连接参数 MqttConnectOptions options = new MqttConnectOptions(); - - // 是否清除会话: - // true - 断开后清除订阅(推荐后端使用) - // false - 保留会话(多用于设备端) options.setCleanSession(props.isCleanSession()); - - // 心跳时间(秒) options.setKeepAliveInterval(props.getKeepAlive()); - - // 连接超时时间(秒) options.setConnectionTimeout(props.getTimeout()); - - // 自动重连(非常重要,防止网络抖动) options.setAutomaticReconnect(true); - // 设置用户名密码(如果配置了) if (props.getUsername() != null && !props.getUsername().trim().isEmpty()) { options.setUserName(props.getUsername().trim()); } @@ -84,89 +55,63 @@ public class MqttClientConfig { options.setPassword(props.getPassword().toCharArray()); } - // 3. 设置 MQTT 回调 + // 回调 mqttClient.setCallback(new MqttCallbackExtended() { - /** - * 连接成功(或重连成功)回调 - */ @Override public void connectComplete(boolean reconnect, String serverURI) { log.info("[MQTT] 连接成功 reconnect={}, serverURI={}", reconnect, serverURI); - // 连接成功后订阅 Topic subscribe(props); } - /** - * 连接丢失回调 - */ @Override public void connectionLost(Throwable cause) { log.warn("[MQTT] 连接断开", cause); } - /** - * 接收到消息回调 - * - * @param topic 消息主题(用于区分设备、类型) - * @param message 消息内容 - */ - @Override public void messageArrived(String topic, MqttMessage message) { String payload = new String(message.getPayload(), StandardCharsets.UTF_8); + + log.info("🔥 收到MQTT消息 topic={} payload={}", topic, payload); + mqttMessageDispatcher.dispatch(topic, payload); } - /** - * 消息发送完成回调(发布消息后触发) - */ @Override public void deliveryComplete(IMqttDeliveryToken token) { - // 后端下发指令时可用于确认发送完成 } }); - // 4. 连接 Broker + // 连接 log.info("[MQTT] 正在连接 Broker:{}", props.getBroker()); mqttClient.connect(options); log.info("[MQTT] MQTT 已连接"); - // 5. 启动时先订阅一次(防止 connectComplete 未触发) + // 启动订阅 subscribe(props); return mqttClient; } - /** - * 订阅 Topic(支持通配符) - */ + private void subscribe(MqttProperties props) { if (mqttClient == null || !mqttClient.isConnected()) { log.warn("[MQTT] 订阅失败:客户端未连接"); return; } - if (props.getTopics() == null || props.getTopics().isEmpty()) { - log.warn("[MQTT] 未配置订阅 Topic"); - return; - } try { - for (String topic : props.getTopics()) { - if (topic == null || topic.trim().isEmpty()) { - continue; - } - mqttClient.subscribe(topic.trim(), props.getQos()); - log.info("[MQTT] 已订阅 Topic:{},QoS={}", topic.trim(), props.getQos()); - } + + mqttClient.subscribe("worn/tangshan/dianchi/up", props.getQos()); + + log.info("[MQTT] 已订阅 Topic:worn/tangshan/dianchi/up,QoS={}", props.getQos()); + } catch (Exception e) { log.error("[MQTT] 订阅 Topic 失败", e); } } - /** - * Spring 容器关闭时,优雅断开 MQTT 连接 - */ @PreDestroy public void destroy() { try { @@ -179,4 +124,4 @@ public class MqttClientConfig { } catch (Exception ignored) { } } -} +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/sensor/config/MqttProperties.java b/src/main/java/com/shzg/project/worn/sensor/config/MqttProperties.java index d60edfc..de87159 100644 --- a/src/main/java/com/shzg/project/worn/sensor/config/MqttProperties.java +++ b/src/main/java/com/shzg/project/worn/sensor/config/MqttProperties.java @@ -4,10 +4,13 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -import java.util.List; - /** * MQTT 配置属性(对应 application.yml 中 mqtt.*) + * + * 当前说明: + * 1. 这里只保留 MQTT 连接相关配置 + * 2. clientId 由代码中动态生成,不再从 yml 配置 + * 3. topic 不再写死,统一从数据库 mqtt_sensor_device 获取 */ @Data @Component @@ -17,16 +20,13 @@ public class MqttProperties { /** 是否启用 MQTT */ private boolean enabled; - /** Broker 地址:tcp://192.168.1.29:1883 */ + /** Broker 地址,例如:tcp://47.100.212.83:1883 */ private String broker; - /** 客户端ID(在 EMQX 中唯一) */ - private String clientId; - - /** 账号 */ + /** MQTT 账号 */ private String username; - /** 密码 */ + /** MQTT 密码 */ private String password; /** 是否清除会话 */ @@ -40,7 +40,4 @@ public class MqttProperties { /** 默认 QoS */ private int qos = 1; - - /** 订阅 Topic 列表(支持通配符) */ - private List topics; -} +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/sensor/config/MqttPublishClient.java b/src/main/java/com/shzg/project/worn/sensor/config/MqttPublishClient.java index 109f7c5..6db4e10 100644 --- a/src/main/java/com/shzg/project/worn/sensor/config/MqttPublishClient.java +++ b/src/main/java/com/shzg/project/worn/sensor/config/MqttPublishClient.java @@ -10,51 +10,27 @@ import java.nio.charset.StandardCharsets; /** * MQTT 消息发布客户端 * - * 作用说明: - * - 专门用于向指定设备下发控制指令 - * - 例如:开启 / 关闭 / 重启 传感器 - * - * 说明: - * - 依赖同一个 MqttClient - * - 发布消息时使用“精确 Topic”(不能使用通配符) + * 支持: + * 1. JSON消息发送 + * 2. HEX指令发送(设备控制) */ @Slf4j @Component public class MqttPublishClient { - /** - * MQTT 客户端(由 Spring 注入) - */ private final MqttClient mqttClient; public MqttPublishClient(MqttClient mqttClient) { this.mqttClient = mqttClient; } - /** - * 发布 MQTT 消息(默认 QoS=1,不保留消息) - * - * @param topic 目标 Topic(必须是具体设备的 Topic) - * @param payload 消息内容(JSON 字符串) - */ + // ================== JSON发送 ================== public void publish(String topic, String payload) { publish(topic, payload, 1, false); } - /** - * 发布 MQTT 消息(自定义参数) - */ public void publish(String topic, String payload, int qos, boolean retained) { - - // 如果 MQTT 未启用或未初始化 - if (mqttClient == null) { - throw new IllegalStateException("MQTT 客户端未初始化(mqtt.enabled=false)"); - } - - // 如果 MQTT 尚未连接 - if (!mqttClient.isConnected()) { - throw new IllegalStateException("MQTT 客户端未连接到 Broker"); - } + checkClient(); try { MqttMessage msg = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8)); @@ -63,12 +39,85 @@ public class MqttPublishClient { mqttClient.publish(topic, msg); - log.info("[MQTT] 指令已发送 topic={}, qos={}, retained={}, payload={}", - topic, qos, retained, payload); + log.info("[MQTT] JSON发送成功 topic={}, payload={}, qos={}, retained={}", + topic, payload, qos, retained); } catch (Exception e) { - log.error("[MQTT] 指令发送失败 topic=" + topic, e); - throw new RuntimeException(e); + log.error("[MQTT] JSON发送失败 topic=" + topic + ", payload=" + payload, e); + throw new RuntimeException("MQTT JSON发送失败", e); } } -} + + // ================== ⭐ HEX发送 ================== + public void publishHex(String topic, String hexPayload) { + publishHex(topic, hexPayload, 1, false); + } + + public void publishHex(String topic, String hexPayload, int qos, boolean retained) { + checkClient(); + + try { + byte[] bytes = hexStringToByteArray(hexPayload); + + MqttMessage msg = new MqttMessage(bytes); + msg.setQos(qos); + msg.setRetained(retained); + + mqttClient.publish(topic, msg); + + log.info("[MQTT] HEX发送成功 topic={}, hex={}, length={}, qos={}, retained={}", + topic, hexPayload, bytes.length, qos, retained); + + } catch (Exception e) { + log.error("[MQTT] HEX发送失败 topic=" + topic + ", hex=" + hexPayload, e); + throw new RuntimeException("MQTT HEX发送失败", e); + } + } + + // ================== 客户端校验 ================== + private void checkClient() { + if (mqttClient == null) { + throw new IllegalStateException("MQTT客户端未初始化(mqtt.enabled=false)"); + } + if (!mqttClient.isConnected()) { + throw new IllegalStateException("MQTT客户端未连接到Broker"); + } + } + + // ================== HEX工具 ================== + /** + * HEX字符串 → byte[] + * 支持: + * - 大小写 + * - 自动去空格 + * + * 示例: + * 2218000000C6 → [0x22,0x18,0x00,0x00,0x00,0xC6] + */ + private byte[] hexStringToByteArray(String hex) { + + if (hex == null || hex.trim().isEmpty()) { + throw new IllegalArgumentException("HEX字符串不能为空"); + } + + // 去空格 + 转大写 + hex = hex.replace(" ", "").toUpperCase(); + + if (hex.length() % 2 != 0) { + throw new IllegalArgumentException("HEX字符串长度必须为偶数:" + hex); + } + + int len = hex.length(); + byte[] data = new byte[len / 2]; + + try { + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16); + } + } catch (Exception e) { + throw new IllegalArgumentException("HEX字符串解析失败:" + hex, e); + } + + return data; + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/sensor/mqtt/dispatcher/MqttMessageDispatcher.java b/src/main/java/com/shzg/project/worn/sensor/mqtt/dispatcher/MqttMessageDispatcher.java index b445002..f4b02ec 100644 --- a/src/main/java/com/shzg/project/worn/sensor/mqtt/dispatcher/MqttMessageDispatcher.java +++ b/src/main/java/com/shzg/project/worn/sensor/mqtt/dispatcher/MqttMessageDispatcher.java @@ -1,55 +1,126 @@ package com.shzg.project.worn.sensor.mqtt.dispatcher; -import com.shzg.project.worn.sensor.mqtt.handler.HumiSensorHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.sensor.mqtt.handler.EnvSensorHandler; import com.shzg.project.worn.sensor.mqtt.handler.SmokeSensorHandler; -import com.shzg.project.worn.sensor.mqtt.handler.TempSensorHandler; +import com.shzg.project.worn.sensor.mqtt.handler.WaterSensorHandler; +import com.shzg.project.worn.unit.MqttDeviceCache; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; +import java.util.concurrent.Executor; + /** - * MQTT消息分发器 - * 负责根据MQTT主题(topic)将消息分发给相应的传感器处理器 + * MQTT消息分发器(支持烟雾 / 环境 / 水浸) */ @Slf4j @Component public class MqttMessageDispatcher { @Autowired - private TempSensorHandler tempSensorHandler; - @Autowired - private HumiSensorHandler humiSensorHandler; + private MqttDeviceCache deviceCache; + @Autowired private SmokeSensorHandler smokeSensorHandler; + @Autowired + private EnvSensorHandler envSensorHandler; + + // ✅ 新增:水浸处理器 + @Autowired + private WaterSensorHandler waterSensorHandler; + + @Autowired + @Qualifier("mqttExecutor") + private Executor executor; + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + /** - * 分发MQTT消息到对应的处理器 - * 根据topic的前缀判断消息类型,并调用相应的处理器进行处理 - * - * @param topic MQTT主题,用于判断消息类型 - * @param payload 消息负载,包含具体的传感器数据 + * MQTT入口(异步) */ public void dispatch(String topic, String payload) { - - // 处理温度传感器消息 - if (topic.startsWith("zg/wms/test/temp/")) { - tempSensorHandler.handle(topic, payload); - return; - } - - // 处理湿度传感器消息 - if (topic.startsWith("zg/wms/test/humi/")) { - humiSensorHandler.handle(topic, payload); - return; - } - - // 处理烟雾传感器消息 - if (topic.startsWith("zg/wms/test/smoke/")) { - smokeSensorHandler.handle(topic, payload); - return; - } - - log.warn("未知 MQTT Topic: {}", topic); + executor.execute(() -> doDispatch(topic, payload)); } -} + /** + * 核心分发逻辑 + */ + private void doDispatch(String topic, String payload) { + + log.info("[MQTT] 收到消息 topic={}, payload={}", topic, payload); + + try { + // ========================= + // 1️⃣ JSON解析 + // ========================= + JsonNode root = OBJECT_MAPPER.readTree(payload); + + if (!root.has("devEUI")) { + log.warn("[MQTT] payload中未找到devEUI"); + return; + } + + String devEui = root.get("devEUI").asText(); + + if (devEui == null || devEui.isEmpty()) { + log.warn("[MQTT] devEUI为空"); + return; + } + + // 👉 统一小写(关键) + devEui = devEui.toLowerCase(); + + // ========================= + // 2️⃣ 获取设备 + // ========================= + MqttSensorDevice device = deviceCache.get(devEui); + + if (device == null) { + log.warn("[MQTT] 未匹配设备 devEUI={}", devEui); + return; + } + + String deviceType = device.getDeviceType(); + + if (deviceType == null || deviceType.isEmpty()) { + log.warn("[MQTT] device_type为空 devEUI={}", devEui); + return; + } + + deviceType = deviceType.toLowerCase(); + + // ========================= + // 3️⃣ 分发(核心) + // ========================= + + // 🔥 烟雾 + if (deviceType.contains("smoke")) { + smokeSensorHandler.handle(device, topic, payload); + return; + } + + // 🌡 环境 + if (deviceType.contains("env")) { + envSensorHandler.handle(device, topic, payload); + return; + } + + // 💧 水浸(新增) + if (deviceType.contains("water")) { + waterSensorHandler.handle(device, topic, payload); + return; + } + + // ❌ 未识别 + log.warn("[MQTT] 未识别设备类型 deviceType={}", deviceType); + + } catch (Exception e) { + log.error("[MQTT] 分发异常 topic=" + topic + ", payload=" + payload, e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/EnvSensorHandler.java b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/EnvSensorHandler.java new file mode 100644 index 0000000..1d1ad48 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/EnvSensorHandler.java @@ -0,0 +1,251 @@ +package com.shzg.project.worn.sensor.mqtt.handler; + +import com.alibaba.fastjson2.JSONObject; +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.domain.MqttSensorData; +import com.shzg.project.worn.domain.MqttSensorEvent; +import com.shzg.project.worn.domain.MqttSensorThreshold; +import com.shzg.project.worn.service.IMqttSensorDataService; +import com.shzg.project.worn.service.IMqttSensorEventService; +import com.shzg.project.worn.service.IMqttSensorThresholdService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 综合环境传感器 Handler(最终生产版:防重复报警 + 状态机) + */ +@Slf4j +@Component +public class EnvSensorHandler { + + @Autowired + private IMqttSensorDataService sensorDataService; + + @Autowired + private IMqttSensorEventService eventService; + + @Autowired + private IMqttSensorThresholdService thresholdService; + + /** + * 状态缓存(核心:防重复) + * key = deviceId_metric + * value = normal / warning / alarm + */ + private static final Map STATUS_CACHE = new ConcurrentHashMap<>(); + + /** + * 主入口 + */ + public void handle(MqttSensorDevice device, String topic, String payload) { + + log.info("[ENV] deviceId={}, topic={}, payload={}", device.getId(), topic, payload); + + JSONObject json = parseJson(payload); + if (json == null) return; + + SensorValue val = buildValue(json); + if (val.isEmpty()) return; + + TopicInfo topicInfo = parseTopic(topic); + + // 1️⃣ 数据入库 + saveData(device, topic, payload, topicInfo, val); + + // 2️⃣ 状态检测(核心) + handleEvent(device, val); + } + + // ================== 事件处理 ================== + private void handleEvent(MqttSensorDevice device, SensorValue v) { + + check(device, "temperature", v.temperature, "温度"); + check(device, "humidity", v.humidity, "湿度"); + check(device, "nh3", v.nh3, "氨气"); + check(device, "h2s", v.h2s, "硫化氢"); + + // 电量(单独处理) + if (v.battery != null) { + MqttSensorThreshold t = thresholdService.getThreshold(device.getId(), "battery"); + if (t != null) { + BigDecimal val = new BigDecimal(v.battery); + String status = calcStatus(val, t); + changeStatus(device, "battery", status, "电量:" + v.battery + "%"); + } + } + } + + // ================== 通用检测 ================== + private void check(MqttSensorDevice device, + String metric, + BigDecimal value, + String name) { + + if (value == null) return; + + MqttSensorThreshold t = thresholdService.getThreshold(device.getId(), metric); + if (t == null) return; + + String status = calcStatus(value, t); + + changeStatus(device, metric, status, name + ":" + value); + } + + // ================== 状态计算 ================== + private String calcStatus(BigDecimal value, MqttSensorThreshold t) { + + if (t.getAlarmMax() != null && value.compareTo(t.getAlarmMax()) > 0) { + return "alarm"; + } + if (t.getAlarmMin() != null && value.compareTo(t.getAlarmMin()) < 0) { + return "alarm"; + } + if (t.getWarnMax() != null && value.compareTo(t.getWarnMax()) > 0) { + return "warning"; + } + if (t.getWarnMin() != null && value.compareTo(t.getWarnMin()) < 0) { + return "warning"; + } + + return "normal"; + } + + // ================== 状态变更 ================== + private void changeStatus(MqttSensorDevice device, + String metric, + String newStatus, + String desc) { + + String key = device.getId() + "_" + metric; + String oldStatus = STATUS_CACHE.get(key); + + // ❗ 防重复:状态没变,不处理 + if (newStatus.equals(oldStatus)) { + return; + } + + // 更新缓存 + STATUS_CACHE.put(key, newStatus); + + // 状态变化 → 记录事件 + if ("alarm".equals(newStatus)) { + triggerEvent(device, "alarm", "HIGH", desc); + } else if ("warning".equals(newStatus)) { + triggerEvent(device, "warning", "MEDIUM", desc); + } else if ("normal".equals(newStatus)) { + triggerEvent(device, "recovery", "LOW", desc + " 正常"); + } + } + + // ================== 事件记录 ================== + private void triggerEvent(MqttSensorDevice device, + String type, + String level, + String desc) { + + log.warn("[ENV事件] deviceId={}, type={}, level={}, desc={}", + device.getId(), type, level, desc); + + MqttSensorEvent event = new MqttSensorEvent(); + event.setDeviceId(device.getId()); + event.setEventType(type); + event.setLevel(level); + event.setEventDesc(desc); + event.setStatus("0"); + event.setIsDelete("0"); + event.setCreateTime(new Date()); + + eventService.insertMqttSensorEvent(event); + + // TODO: D2D联动(风机/排风) + // TODO: 短信通知 + // TODO: APP推送 + } + + // ================== 数据入库 ================== + private void saveData(MqttSensorDevice device, + String topic, + String payload, + TopicInfo topicInfo, + SensorValue v) { + + MqttSensorData data = new MqttSensorData(); + + data.setDeviceId(device.getId()); + data.setTopic(topic); + data.setProject(topicInfo.project); + data.setWarehouse(topicInfo.warehouse); + + data.setPayload(payload); + data.setDataJson(payload); + + data.setTemperature(v.temperature); + data.setHumidity(v.humidity); + data.setNh3(v.nh3); + data.setH2s(v.h2s); + data.setBattery(v.battery == null ? null : Long.valueOf(v.battery)); + + data.setCreateTime(new Date()); + data.setIsDelete("0"); + + sensorDataService.insertMqttSensorData(data); + } + + // ================== JSON解析 ================== + private JSONObject parseJson(String payload) { + try { + return JSONObject.parseObject(payload); + } catch (Exception e) { + log.error("[ENV] JSON解析失败 payload={}", payload, e); + return null; + } + } + + // ================== 构建数据 ================== + private SensorValue buildValue(JSONObject json) { + SensorValue v = new SensorValue(); + v.temperature = json.getBigDecimal("temperature"); + v.humidity = json.getBigDecimal("humidity"); + v.nh3 = json.getBigDecimal("nh3"); + v.h2s = json.getBigDecimal("h2s"); + v.battery = json.getInteger("battery"); + return v; + } + + // ================== topic解析 ================== + private TopicInfo parseTopic(String topic) { + TopicInfo info = new TopicInfo(); + try { + String[] arr = topic.split("/"); + info.project = arr.length > 1 ? arr[1] : ""; + info.warehouse = arr.length > 2 ? arr[2] : ""; + } catch (Exception e) { + log.warn("[ENV] topic解析失败 topic={}", topic); + } + return info; + } + + // ================== 内部结构 ================== + private static class SensorValue { + BigDecimal temperature; + BigDecimal humidity; + BigDecimal nh3; + BigDecimal h2s; + Integer battery; + + boolean isEmpty() { + return temperature == null && humidity == null && nh3 == null && h2s == null; + } + } + + private static class TopicInfo { + String project; + String warehouse; + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/HumiSensorHandler.java b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/HumiSensorHandler.java deleted file mode 100644 index 2f33912..0000000 --- a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/HumiSensorHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.shzg.project.worn.sensor.mqtt.handler; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 湿度传感器 Handler - * Topic 示例:zg/wms/test/humi/{deviceId} - */ -@Slf4j -@Component -public class HumiSensorHandler { - - public static final String TOPIC_PREFIX = "zg/wms/test/humi/"; - - /** - * 处理湿度传感器消息 - * @param topic MQTT主题 - * @param payload 消息负载 - */ - public void handle(String topic, String payload) { - String deviceId = resolveDeviceId(topic, TOPIC_PREFIX); - - log.info("[HUMI] deviceId={}, topic={}, payload={}", deviceId, topic, payload); - - // TODO: 这里后续写湿度业务逻辑 - } - - /** - * 从MQTT主题中解析设备ID - * @param topic MQTT主题 - * @param prefix 主题前缀 - * @return 设备ID - */ - private String resolveDeviceId(String topic, String prefix) { - if (topic == null || !topic.startsWith(prefix)) { - return ""; - } - // 提取设备ID部分 - return topic.substring(prefix.length()); - } -} - diff --git a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/SmokeSensorHandler.java b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/SmokeSensorHandler.java index df4ca2a..78b21ba 100644 --- a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/SmokeSensorHandler.java +++ b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/SmokeSensorHandler.java @@ -1,47 +1,124 @@ package com.shzg.project.worn.sensor.mqtt.handler; +import com.alibaba.fastjson2.JSONObject; +import com.shzg.project.worn.domain.MqttSensorData; +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.domain.MqttSensorEvent; +import com.shzg.project.worn.service.IMqttSensorDataService; +import com.shzg.project.worn.service.IMqttSensorEventService; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -/** - * 烟雾传感器 Handler - * Topic 示例:zg/wms/test/smoke/{deviceId} - */ +import java.math.BigDecimal; + @Slf4j @Component public class SmokeSensorHandler { - /** - * MQTT主题前缀,用于烟雾传感器消息 - */ - public static final String TOPIC_PREFIX = "zg/wms/test/smoke/"; + @Autowired + private IMqttSensorDataService dataService; + + @Autowired + private IMqttSensorEventService eventService; /** - * 处理烟雾传感器消息 * - * @param topic MQTT主题 - * @param payload 消息负载内容 */ - public void handle(String topic, String payload) { - String deviceId = resolveDeviceId(topic, TOPIC_PREFIX); + public void handle(MqttSensorDevice device, String topic, String payload) { - log.info("[SMOKE] deviceId={}, topic={}, payload={}", deviceId, topic, payload); + log.info("[SMOKE] deviceId={}, deviceName={}, topic={}, payload={}", + device.getId(), device.getDeviceName(), topic, payload); - // TODO: 这里后续写烟雾业务逻辑 - } + JSONObject json; - /** - * 从MQTT主题中解析设备ID - * - * @param topic 完整的MQTT主题 - * @param prefix 主题前缀 - * @return 设备ID,如果主题格式不正确则返回空字符串 - */ - private String resolveDeviceId(String topic, String prefix) { - if (topic == null || !topic.startsWith(prefix)) { - return ""; + try { + json = JSONObject.parseObject(payload); + } catch (Exception e) { + log.error("[SMOKE] JSON解析失败 payload={}", payload, e); + return; } - // 提取前缀后的设备ID部分 - return topic.substring(prefix.length()); + + // ================== topic解析 ================== + String project = null; + String warehouse = null; + + try { + String[] arr = topic.split("/"); + if (arr.length >= 3) { + project = arr[1]; // tangshan + warehouse = arr[2]; // dianchi + } + } catch (Exception e) { + log.warn("[SMOKE] topic解析失败 topic={}", topic); + } + + // ================== 字段 ================== + String event = json.getString("event"); + Integer battery = json.getInteger("battery"); + Integer concentration = json.getInteger("concentration"); + Integer temperature = json.getInteger("temperature"); + + // ================== 入库 ================== + MqttSensorData data = new MqttSensorData(); + + data.setDeviceId(device.getId()); + data.setTopic(topic); + data.setProject(project); + data.setWarehouse(warehouse); + + data.setPayload(payload); + data.setDataJson(json.toJSONString()); + + data.setBattery(battery != null ? battery.longValue() : null); + data.setConcentration(concentration != null ? concentration.longValue() : null); + data.setTemperature(temperature != null ? new BigDecimal(temperature) : null); + + data.setIsDelete("0"); + + dataService.insertMqttSensorData(data); + + // ================== 事件 ================== + + if ("silent".equals(event)) { + insertEvent(device, "silent", "人为静音报警", "LOW"); + return; + } + + if ("alarm".equals(event) || (concentration != null && concentration > 0)) { + insertEvent(device, "alarm", "烟雾报警", "HIGH"); + return; + } + + if ("removed".equals(event)) { + insertEvent(device, "removed", "设备被拆/离位", "MEDIUM"); + return; + } + + if ("low_battery".equals(event)) { + insertEvent(device, "low_battery", "低电量告警", "LOW"); + return; + } + + if (battery != null && battery < 20) { + log.info("[SMOKE] 电量偏低 deviceId={}, battery={}", + device.getId(), battery); + } + + log.info("[SMOKE] 正常数据 deviceId={}, battery={}, temp={}", + device.getId(), battery, temperature); } -} + + private void insertEvent(MqttSensorDevice device, String type, String desc, String level) { + + MqttSensorEvent event = new MqttSensorEvent(); + + event.setDeviceId(device.getId()); + event.setEventType(type); + event.setEventDesc(desc); + event.setLevel(level); + event.setStatus("0"); + + eventService.insertMqttSensorEvent(event); + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/TempSensorHandler.java b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/TempSensorHandler.java deleted file mode 100644 index a1f82a2..0000000 --- a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/TempSensorHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.shzg.project.worn.sensor.mqtt.handler; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 温度传感器 Handler - * Topic 示例:zg/wms/test/temp/{deviceId} - */ -@Slf4j -@Component -public class TempSensorHandler { - - /** - * MQTT主题前缀,用于温度传感器数据 - */ - public static final String TOPIC_PREFIX = "zg/wms/test/temp/"; - - /** - * 处理温度传感器数据 - * - * @param topic MQTT主题,格式为 zg/wms/test/temp/{deviceId} - * @param payload 传感器数据载荷 - */ - public void handle(String topic, String payload) { - String deviceId = resolveDeviceId(topic, TOPIC_PREFIX); - - log.info("[TEMP] deviceId={}, topic={}, payload={}", deviceId, topic, payload); - - // TODO: 这里后续写温度业务逻辑(解析JSON、落库、告警等) - } - - /** - * 从MQTT主题中解析设备ID - * - * @param topic MQTT主题 - * @param prefix 主题前缀 - * @return 设备ID,如果主题格式不正确则返回空字符串 - */ - private String resolveDeviceId(String topic, String prefix) { - if (topic == null || !topic.startsWith(prefix)) { - return ""; - } - return topic.substring(prefix.length()); - } -} diff --git a/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/WaterSensorHandler.java b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/WaterSensorHandler.java new file mode 100644 index 0000000..a4a2d6a --- /dev/null +++ b/src/main/java/com/shzg/project/worn/sensor/mqtt/handler/WaterSensorHandler.java @@ -0,0 +1,214 @@ +package com.shzg.project.worn.sensor.mqtt.handler; + +import com.alibaba.fastjson2.JSONObject; +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.domain.MqttSensorData; +import com.shzg.project.worn.domain.MqttSensorEvent; +import com.shzg.project.worn.service.IMqttSensorDataService; +import com.shzg.project.worn.service.IMqttSensorEventService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 水浸传感器 Handler(对齐 Env 结构:状态机 + 防重复) + */ +@Slf4j +@Component +public class WaterSensorHandler { + + @Autowired + private IMqttSensorDataService sensorDataService; + + @Autowired + private IMqttSensorEventService eventService; + + /** + * 状态缓存(防重复报警) + * key = deviceId_water + */ + private static final Map STATUS_CACHE = new ConcurrentHashMap<>(); + + /** + * 主入口 + */ + public void handle(MqttSensorDevice device, String topic, String payload) { + + log.info("[WATER] deviceId={}, topic={}, payload={}", device.getId(), topic, payload); + + JSONObject json = parseJson(payload); + if (json == null) return; + + // 👉 从 data 字段解析(Base64) + Integer water = parseWater(json); + if (water == null) { + log.warn("[WATER] 无法解析水浸状态"); + return; + } + + TopicInfo topicInfo = parseTopic(topic); + + // 1️⃣ 数据入库 + saveData(device, topic, payload, topicInfo, water); + + // 2️⃣ 状态处理 + handleEvent(device, water); + } + + // ================== 事件处理 ================== + private void handleEvent(MqttSensorDevice device, Integer water) { + + String status; + + // 👉 水浸:1=有水(报警),0=正常 + if (water == 1) { + status = "alarm"; + } else { + status = "normal"; + } + + changeStatus(device, status, water); + } + + // ================== 状态变更 ================== + private void changeStatus(MqttSensorDevice device, + String newStatus, + Integer water) { + + String key = device.getId() + "_water"; + String oldStatus = STATUS_CACHE.get(key); + + // ❗ 防重复 + if (newStatus.equals(oldStatus)) { + return; + } + + STATUS_CACHE.put(key, newStatus); + + if ("alarm".equals(newStatus)) { + triggerEvent(device, "alarm", "HIGH", "浸水预警!"); + } else if ("normal".equals(newStatus)) { + triggerEvent(device, "recovery", "LOW", "水浸正常"); + } + } + + // ================== 事件记录 ================== + private void triggerEvent(MqttSensorDevice device, + String type, + String level, + String desc) { + + log.warn("[WATER事件] deviceId={}, type={}, level={}, desc={}", + device.getId(), type, level, desc); + + MqttSensorEvent event = new MqttSensorEvent(); + event.setDeviceId(device.getId()); + event.setEventType(type); + event.setLevel(level); + event.setEventDesc(desc); + event.setStatus("0"); + event.setIsDelete("0"); + event.setCreateTime(new Date()); + + eventService.insertMqttSensorEvent(event); + + // TODO: 联动排水 / 告警推送 + } + + // ================== 数据入库 ================== + private void saveData(MqttSensorDevice device, + String topic, + String payload, + TopicInfo topicInfo, + Integer water) { + + MqttSensorData data = new MqttSensorData(); + + data.setDeviceId(device.getId()); + data.setTopic(topic); + data.setProject(topicInfo.project); + data.setWarehouse(topicInfo.warehouse); + + data.setPayload(payload); + data.setDataJson(payload); + + data.setWaterStatus(water); + + data.setCreateTime(new Date()); + data.setIsDelete("0"); + + sensorDataService.insertMqttSensorData(data); + } + + // ================== JSON解析 ================== + private JSONObject parseJson(String payload) { + try { + return JSONObject.parseObject(payload); + } catch (Exception e) { + log.error("[WATER] JSON解析失败 payload={}", payload, e); + return null; + } + } + + // ================== 解析水浸 ================== + private Integer parseWater(JSONObject json) { + + try { + // 👉 LoRaWAN 原始数据(Base64) + String data = json.getString("data"); + if (data == null) return null; + + byte[] bytes = java.util.Base64.getDecoder().decode(data); + + /** + * Milesight水浸协议(关键) + * 一般结构: + * [channel_id][channel_type][value] + * + * 水浸: + * channel_id = 0x05 + * channel_type = 0x00 + * value = 0/1 + */ + + for (int i = 0; i < bytes.length - 2; i++) { + + int channelId = bytes[i] & 0xFF; + int channelType = bytes[i + 1] & 0xFF; + + // 👉 水浸 + if (channelId == 0x05 && channelType == 0x00) { + return bytes[i + 2] & 0xFF; + } + } + + } catch (Exception e) { + log.error("[WATER] 解析payload失败", e); + } + + return null; + } + + // ================== topic解析 ================== + private TopicInfo parseTopic(String topic) { + TopicInfo info = new TopicInfo(); + try { + String[] arr = topic.split("/"); + info.project = arr.length > 1 ? arr[1] : ""; + info.warehouse = arr.length > 2 ? arr[2] : ""; + } catch (Exception e) { + log.warn("[WATER] topic解析失败 topic={}", topic); + } + return info; + } + + // ================== 内部结构 ================== + private static class TopicInfo { + String project; + String warehouse; + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/service/IDeviceCommandService.java b/src/main/java/com/shzg/project/worn/service/IDeviceCommandService.java new file mode 100644 index 0000000..879ea7b --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/IDeviceCommandService.java @@ -0,0 +1,8 @@ +package com.shzg.project.worn.service; + +public interface IDeviceCommandService { + + void smokeStop(Long deviceId); + + void smokeStopOnce(Long deviceId); +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/service/IMqttSensorCommandService.java b/src/main/java/com/shzg/project/worn/service/IMqttSensorCommandService.java new file mode 100644 index 0000000..86fc282 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/IMqttSensorCommandService.java @@ -0,0 +1,61 @@ +package com.shzg.project.worn.service; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorCommand; + +/** + * 指令管理Service接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface IMqttSensorCommandService +{ + /** + * 查询指令管理 + * + * @param id 指令管理主键 + * @return 指令管理 + */ + public MqttSensorCommand selectMqttSensorCommandById(Long id); + + /** + * 查询指令管理列表 + * + * @param mqttSensorCommand 指令管理 + * @return 指令管理集合 + */ + public List selectMqttSensorCommandList(MqttSensorCommand mqttSensorCommand); + + /** + * 新增指令管理 + * + * @param mqttSensorCommand 指令管理 + * @return 结果 + */ + public int insertMqttSensorCommand(MqttSensorCommand mqttSensorCommand); + + /** + * 修改指令管理 + * + * @param mqttSensorCommand 指令管理 + * @return 结果 + */ + public int updateMqttSensorCommand(MqttSensorCommand mqttSensorCommand); + + /** + * 批量删除指令管理 + * + * @param ids 需要删除的指令管理主键集合 + * @return 结果 + */ + public int deleteMqttSensorCommandByIds(Long[] ids); + + /** + * 删除指令管理信息 + * + * @param id 指令管理主键 + * @return 结果 + */ + public int deleteMqttSensorCommandById(Long id); +} diff --git a/src/main/java/com/shzg/project/worn/service/IMqttSensorDataService.java b/src/main/java/com/shzg/project/worn/service/IMqttSensorDataService.java new file mode 100644 index 0000000..51e4cb4 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/IMqttSensorDataService.java @@ -0,0 +1,61 @@ +package com.shzg.project.worn.service; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorData; + +/** + * 设备数据Service接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface IMqttSensorDataService +{ + /** + * 查询设备数据 + * + * @param id 设备数据主键 + * @return 设备数据 + */ + public MqttSensorData selectMqttSensorDataById(Long id); + + /** + * 查询设备数据列表 + * + * @param mqttSensorData 设备数据 + * @return 设备数据集合 + */ + public List selectMqttSensorDataList(MqttSensorData mqttSensorData); + + /** + * 新增设备数据 + * + * @param mqttSensorData 设备数据 + * @return 结果 + */ + public int insertMqttSensorData(MqttSensorData mqttSensorData); + + /** + * 修改设备数据 + * + * @param mqttSensorData 设备数据 + * @return 结果 + */ + public int updateMqttSensorData(MqttSensorData mqttSensorData); + + /** + * 批量删除设备数据 + * + * @param ids 需要删除的设备数据主键集合 + * @return 结果 + */ + public int deleteMqttSensorDataByIds(Long[] ids); + + /** + * 删除设备数据信息 + * + * @param id 设备数据主键 + * @return 结果 + */ + public int deleteMqttSensorDataById(Long id); +} diff --git a/src/main/java/com/shzg/project/worn/service/IMqttSensorDeviceService.java b/src/main/java/com/shzg/project/worn/service/IMqttSensorDeviceService.java new file mode 100644 index 0000000..c2d849e --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/IMqttSensorDeviceService.java @@ -0,0 +1,68 @@ +package com.shzg.project.worn.service; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorDevice; + +/** + * MQTT设备Service接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface IMqttSensorDeviceService +{ + /** + * 查询MQTT设备 + * + * @param id MQTT设备主键 + * @return MQTT设备 + */ + public MqttSensorDevice selectMqttSensorDeviceById(Long id); + + /** + * 查询MQTT设备列表 + * + * @param mqttSensorDevice MQTT设备 + * @return MQTT设备集合 + */ + public List selectMqttSensorDeviceList(MqttSensorDevice mqttSensorDevice); + + /** + * 新增MQTT设备 + * + * @param mqttSensorDevice MQTT设备 + * @return 结果 + */ + public int insertMqttSensorDevice(MqttSensorDevice mqttSensorDevice); + + /** + * 修改MQTT设备 + * + * @param mqttSensorDevice MQTT设备 + * @return 结果 + */ + public int updateMqttSensorDevice(MqttSensorDevice mqttSensorDevice); + + /** + * 批量删除MQTT设备 + * + * @param ids 需要删除的MQTT设备主键集合 + * @return 结果 + */ + public int deleteMqttSensorDeviceByIds(Long[] ids); + + /** + * 删除MQTT设备信息 + * + * @param id MQTT设备主键 + * @return 结果 + */ + public int deleteMqttSensorDeviceById(Long id); + + /** + * 根据devEui查询设备 + * @param devEui + * @return + */ + MqttSensorDevice selectByDevEui(String devEui); +} diff --git a/src/main/java/com/shzg/project/worn/service/IMqttSensorEventService.java b/src/main/java/com/shzg/project/worn/service/IMqttSensorEventService.java new file mode 100644 index 0000000..247531d --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/IMqttSensorEventService.java @@ -0,0 +1,61 @@ +package com.shzg.project.worn.service; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorEvent; + +/** + * 设备事件Service接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface IMqttSensorEventService +{ + /** + * 查询设备事件 + * + * @param id 设备事件主键 + * @return 设备事件 + */ + public MqttSensorEvent selectMqttSensorEventById(Long id); + + /** + * 查询设备事件列表 + * + * @param mqttSensorEvent 设备事件 + * @return 设备事件集合 + */ + public List selectMqttSensorEventList(MqttSensorEvent mqttSensorEvent); + + /** + * 新增设备事件 + * + * @param mqttSensorEvent 设备事件 + * @return 结果 + */ + public int insertMqttSensorEvent(MqttSensorEvent mqttSensorEvent); + + /** + * 修改设备事件 + * + * @param mqttSensorEvent 设备事件 + * @return 结果 + */ + public int updateMqttSensorEvent(MqttSensorEvent mqttSensorEvent); + + /** + * 批量删除设备事件 + * + * @param ids 需要删除的设备事件主键集合 + * @return 结果 + */ + public int deleteMqttSensorEventByIds(Long[] ids); + + /** + * 删除设备事件信息 + * + * @param id 设备事件主键 + * @return 结果 + */ + public int deleteMqttSensorEventById(Long id); +} diff --git a/src/main/java/com/shzg/project/worn/service/IMqttSensorThresholdService.java b/src/main/java/com/shzg/project/worn/service/IMqttSensorThresholdService.java new file mode 100644 index 0000000..ed4323f --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/IMqttSensorThresholdService.java @@ -0,0 +1,69 @@ +package com.shzg.project.worn.service; + +import java.util.List; +import com.shzg.project.worn.domain.MqttSensorThreshold; + +/** + * 传感器阈值配置Service接口 + * + * @author shzg + * @date 2026-04-01 + */ +public interface IMqttSensorThresholdService +{ + /** + * 查询传感器阈值配置 + * + * @param id 传感器阈值配置主键 + * @return 传感器阈值配置 + */ + public MqttSensorThreshold selectMqttSensorThresholdById(Long id); + + /** + * 查询传感器阈值配置列表 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 传感器阈值配置集合 + */ + public List selectMqttSensorThresholdList(MqttSensorThreshold mqttSensorThreshold); + + /** + * 新增传感器阈值配置 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 结果 + */ + public int insertMqttSensorThreshold(MqttSensorThreshold mqttSensorThreshold); + + /** + * 修改传感器阈值配置 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 结果 + */ + public int updateMqttSensorThreshold(MqttSensorThreshold mqttSensorThreshold); + + /** + * 批量删除传感器阈值配置 + * + * @param ids 需要删除的传感器阈值配置主键集合 + * @return 结果 + */ + public int deleteMqttSensorThresholdByIds(Long[] ids); + + /** + * 删除传感器阈值配置信息 + * + * @param id 传感器阈值配置主键 + * @return 结果 + */ + public int deleteMqttSensorThresholdById(Long id); + + /** + * 根据设备ID和指标类型获取阈值 + * @param deviceId + * @param metricType + * @return + */ + MqttSensorThreshold getThreshold(Long deviceId, String metricType); +} diff --git a/src/main/java/com/shzg/project/worn/service/impl/DeviceCommandServiceImpl.java b/src/main/java/com/shzg/project/worn/service/impl/DeviceCommandServiceImpl.java new file mode 100644 index 0000000..5f872d8 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/impl/DeviceCommandServiceImpl.java @@ -0,0 +1,147 @@ +package com.shzg.project.worn.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.shzg.project.worn.domain.MqttSensorCommand; +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.mapper.MqttSensorDeviceMapper; +import com.shzg.project.worn.sensor.config.MqttPublishClient; +import com.shzg.project.worn.service.IDeviceCommandService; +import com.shzg.project.worn.service.IMqttSensorCommandService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Base64; +import java.util.Date; + +@Service +public class DeviceCommandServiceImpl implements IDeviceCommandService { + + @Autowired + private MqttPublishClient mqttPublishClient; + + @Autowired + private MqttSensorDeviceMapper deviceMapper; + + @Autowired + private IMqttSensorCommandService commandService; + + // ================== Topic ================== + + private String buildDownTopic(String devEui) { + return "worn/tangshan/dianchi/downlink/" + devEui; + } + + // ================== 指令 ================== + + @Override + public void smokeStop(Long deviceId) { + + MqttSensorDevice device = getDevice(deviceId); + + String topic = buildDownTopic(device.getDevEui()); + String base64 = hexToBase64("2219000000C5"); + + String payload = buildPayload(base64); + + executeCommand(deviceId, topic, "smokeStop", payload); + } + + @Override + public void smokeStopOnce(Long deviceId) { + + MqttSensorDevice device = getDevice(deviceId); + + String topic = buildDownTopic(device.getDevEui()); + String base64 = hexToBase64("2218000000C6"); + + String payload = buildPayload(base64); + + executeCommand(deviceId, topic, "smokeStopOnce", payload); + } + + // ================== 指令执行 ================== + + private void executeCommand(Long deviceId, String topic, String command, String payload) { + + MqttSensorCommand cmd = new MqttSensorCommand(); + cmd.setDeviceId(deviceId); + cmd.setTopic(topic); + cmd.setCommand(command); + cmd.setPayload(payload); + cmd.setStatus("0"); + cmd.setSendTime(new Date()); + + commandService.insertMqttSensorCommand(cmd); + + try { + mqttPublishClient.publish(topic, payload); + + cmd.setStatus("1"); + commandService.updateMqttSensorCommand(cmd); + + } catch (Exception e) { + + cmd.setStatus("2"); + commandService.updateMqttSensorCommand(cmd); + + throw new RuntimeException("指令发送失败", e); + } + } + + // ================== 公共方法 ================== + + private MqttSensorDevice getDevice(Long deviceId) { + + if (deviceId == null) { + throw new RuntimeException("设备ID不能为空"); + } + + MqttSensorDevice device = deviceMapper.selectMqttSensorDeviceById(deviceId); + + if (device == null) { + throw new RuntimeException("设备不存在"); + } + + if (device.getDevEui() == null || device.getDevEui().isEmpty()) { + throw new RuntimeException("设备未配置devEUI"); + } + + if (!"0".equals(device.getStatus())) { + throw new RuntimeException("设备已禁用"); + } + + return device; + } + + // ================== 工具方法 ================== + + private String hexToBase64(String hex) { + byte[] bytes = hexStringToByteArray(hex); + return Base64.getEncoder().encodeToString(bytes); + } + + + private String buildPayload(String base64) { + + JSONObject json = new JSONObject(); + json.put("confirmed", true); + json.put("fport", 85); + json.put("data", base64); + + return json.toJSONString(); + } + + private byte[] hexStringToByteArray(String hex) { + + hex = hex.replace(" ", "").toUpperCase(); + + int len = hex.length(); + byte[] data = new byte[len / 2]; + + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16); + } + + return data; + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/service/impl/MqttSensorCommandServiceImpl.java b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorCommandServiceImpl.java new file mode 100644 index 0000000..6afd052 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorCommandServiceImpl.java @@ -0,0 +1,96 @@ +package com.shzg.project.worn.service.impl; + +import java.util.List; +import com.shzg.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.shzg.project.worn.mapper.MqttSensorCommandMapper; +import com.shzg.project.worn.domain.MqttSensorCommand; +import com.shzg.project.worn.service.IMqttSensorCommandService; + +/** + * 指令管理Service业务层处理 + * + * @author shzg + * @date 2026-04-01 + */ +@Service +public class MqttSensorCommandServiceImpl implements IMqttSensorCommandService +{ + @Autowired + private MqttSensorCommandMapper mqttSensorCommandMapper; + + /** + * 查询指令管理 + * + * @param id 指令管理主键 + * @return 指令管理 + */ + @Override + public MqttSensorCommand selectMqttSensorCommandById(Long id) + { + return mqttSensorCommandMapper.selectMqttSensorCommandById(id); + } + + /** + * 查询指令管理列表 + * + * @param mqttSensorCommand 指令管理 + * @return 指令管理 + */ + @Override + public List selectMqttSensorCommandList(MqttSensorCommand mqttSensorCommand) + { + return mqttSensorCommandMapper.selectMqttSensorCommandList(mqttSensorCommand); + } + + /** + * 新增指令管理 + * + * @param mqttSensorCommand 指令管理 + * @return 结果 + */ + @Override + public int insertMqttSensorCommand(MqttSensorCommand mqttSensorCommand) + { + mqttSensorCommand.setCreateTime(DateUtils.getNowDate()); + return mqttSensorCommandMapper.insertMqttSensorCommand(mqttSensorCommand); + } + + /** + * 修改指令管理 + * + * @param mqttSensorCommand 指令管理 + * @return 结果 + */ + @Override + public int updateMqttSensorCommand(MqttSensorCommand mqttSensorCommand) + { + mqttSensorCommand.setUpdateTime(DateUtils.getNowDate()); + return mqttSensorCommandMapper.updateMqttSensorCommand(mqttSensorCommand); + } + + /** + * 批量删除指令管理 + * + * @param ids 需要删除的指令管理主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorCommandByIds(Long[] ids) + { + return mqttSensorCommandMapper.deleteMqttSensorCommandByIds(ids); + } + + /** + * 删除指令管理信息 + * + * @param id 指令管理主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorCommandById(Long id) + { + return mqttSensorCommandMapper.deleteMqttSensorCommandById(id); + } +} diff --git a/src/main/java/com/shzg/project/worn/service/impl/MqttSensorDataServiceImpl.java b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorDataServiceImpl.java new file mode 100644 index 0000000..1756bc6 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorDataServiceImpl.java @@ -0,0 +1,96 @@ +package com.shzg.project.worn.service.impl; + +import java.util.List; +import com.shzg.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.shzg.project.worn.mapper.MqttSensorDataMapper; +import com.shzg.project.worn.domain.MqttSensorData; +import com.shzg.project.worn.service.IMqttSensorDataService; + +/** + * 设备数据Service业务层处理 + * + * @author shzg + * @date 2026-04-01 + */ +@Service +public class MqttSensorDataServiceImpl implements IMqttSensorDataService +{ + @Autowired + private MqttSensorDataMapper mqttSensorDataMapper; + + /** + * 查询设备数据 + * + * @param id 设备数据主键 + * @return 设备数据 + */ + @Override + public MqttSensorData selectMqttSensorDataById(Long id) + { + return mqttSensorDataMapper.selectMqttSensorDataById(id); + } + + /** + * 查询设备数据列表 + * + * @param mqttSensorData 设备数据 + * @return 设备数据 + */ + @Override + public List selectMqttSensorDataList(MqttSensorData mqttSensorData) + { + return mqttSensorDataMapper.selectMqttSensorDataList(mqttSensorData); + } + + /** + * 新增设备数据 + * + * @param mqttSensorData 设备数据 + * @return 结果 + */ + @Override + public int insertMqttSensorData(MqttSensorData mqttSensorData) + { + mqttSensorData.setCreateTime(DateUtils.getNowDate()); + return mqttSensorDataMapper.insertMqttSensorData(mqttSensorData); + } + + /** + * 修改设备数据 + * + * @param mqttSensorData 设备数据 + * @return 结果 + */ + @Override + public int updateMqttSensorData(MqttSensorData mqttSensorData) + { + mqttSensorData.setUpdateTime(DateUtils.getNowDate()); + return mqttSensorDataMapper.updateMqttSensorData(mqttSensorData); + } + + /** + * 批量删除设备数据 + * + * @param ids 需要删除的设备数据主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorDataByIds(Long[] ids) + { + return mqttSensorDataMapper.deleteMqttSensorDataByIds(ids); + } + + /** + * 删除设备数据信息 + * + * @param id 设备数据主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorDataById(Long id) + { + return mqttSensorDataMapper.deleteMqttSensorDataById(id); + } +} diff --git a/src/main/java/com/shzg/project/worn/service/impl/MqttSensorDeviceServiceImpl.java b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorDeviceServiceImpl.java new file mode 100644 index 0000000..e57cb61 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorDeviceServiceImpl.java @@ -0,0 +1,130 @@ +package com.shzg.project.worn.service.impl; + +import java.util.List; +import com.shzg.common.utils.DateUtils; +import com.shzg.project.worn.unit.MqttDeviceCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.shzg.project.worn.mapper.MqttSensorDeviceMapper; +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.service.IMqttSensorDeviceService; + +/** + * MQTT设备Service业务层处理 + * + * @author shzg + * @date 2026-04-01 + */ +@Service +public class MqttSensorDeviceServiceImpl implements IMqttSensorDeviceService +{ + @Autowired + private MqttSensorDeviceMapper mqttSensorDeviceMapper; + + @Autowired + private MqttDeviceCache deviceCache; + + /** + * 查询MQTT设备 + * + * @param id MQTT设备主键 + * @return MQTT设备 + */ + @Override + public MqttSensorDevice selectMqttSensorDeviceById(Long id) + { + return mqttSensorDeviceMapper.selectMqttSensorDeviceById(id); + } + + /** + * 查询MQTT设备列表 + * + * @param mqttSensorDevice MQTT设备 + * @return MQTT设备 + */ + @Override + public List selectMqttSensorDeviceList(MqttSensorDevice mqttSensorDevice) + { + return mqttSensorDeviceMapper.selectMqttSensorDeviceList(mqttSensorDevice); + } + + /** + * 新增MQTT设备 + * + * @param mqttSensorDevice MQTT设备 + * @return 结果 + */ + @Override + public int insertMqttSensorDevice(MqttSensorDevice mqttSensorDevice) + { + mqttSensorDevice.setCreateTime(DateUtils.getNowDate()); + int rows = mqttSensorDeviceMapper.insertMqttSensorDevice(mqttSensorDevice); + + // 👉 刷新缓存 + deviceCache.refresh(); + + return rows; + } + + /** + * 修改MQTT设备 + * + * @param mqttSensorDevice MQTT设备 + * @return 结果 + */ + @Override + public int updateMqttSensorDevice(MqttSensorDevice mqttSensorDevice) + { + mqttSensorDevice.setUpdateTime(DateUtils.getNowDate()); + int rows = mqttSensorDeviceMapper.updateMqttSensorDevice(mqttSensorDevice); + + // 👉 刷新缓存 + deviceCache.refresh(); + + return rows; + } + + /** + * 批量删除MQTT设备 + * + * @param ids 需要删除的MQTT设备主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorDeviceByIds(Long[] ids) + { + int rows = mqttSensorDeviceMapper.deleteMqttSensorDeviceByIds(ids); + + // 👉 刷新缓存 + deviceCache.refresh(); + + return rows; + } + + /** + * 删除MQTT设备信息 + * + * @param id MQTT设备主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorDeviceById(Long id) + { + return mqttSensorDeviceMapper.deleteMqttSensorDeviceById(id); + } + + /** + * 根据DevEui查询设备 + * @param devEui + * @return + */ + @Override + public MqttSensorDevice selectByDevEui(String devEui) { + + if (devEui == null || devEui.isEmpty()) { + return null; + } + + return mqttSensorDeviceMapper.selectByDevEui(devEui.toLowerCase()); + } +} diff --git a/src/main/java/com/shzg/project/worn/service/impl/MqttSensorEventServiceImpl.java b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorEventServiceImpl.java new file mode 100644 index 0000000..fb6b84c --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorEventServiceImpl.java @@ -0,0 +1,96 @@ +package com.shzg.project.worn.service.impl; + +import java.util.List; +import com.shzg.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.shzg.project.worn.mapper.MqttSensorEventMapper; +import com.shzg.project.worn.domain.MqttSensorEvent; +import com.shzg.project.worn.service.IMqttSensorEventService; + +/** + * 设备事件Service业务层处理 + * + * @author shzg + * @date 2026-04-01 + */ +@Service +public class MqttSensorEventServiceImpl implements IMqttSensorEventService +{ + @Autowired + private MqttSensorEventMapper mqttSensorEventMapper; + + /** + * 查询设备事件 + * + * @param id 设备事件主键 + * @return 设备事件 + */ + @Override + public MqttSensorEvent selectMqttSensorEventById(Long id) + { + return mqttSensorEventMapper.selectMqttSensorEventById(id); + } + + /** + * 查询设备事件列表 + * + * @param mqttSensorEvent 设备事件 + * @return 设备事件 + */ + @Override + public List selectMqttSensorEventList(MqttSensorEvent mqttSensorEvent) + { + return mqttSensorEventMapper.selectMqttSensorEventList(mqttSensorEvent); + } + + /** + * 新增设备事件 + * + * @param mqttSensorEvent 设备事件 + * @return 结果 + */ + @Override + public int insertMqttSensorEvent(MqttSensorEvent mqttSensorEvent) + { + mqttSensorEvent.setCreateTime(DateUtils.getNowDate()); + return mqttSensorEventMapper.insertMqttSensorEvent(mqttSensorEvent); + } + + /** + * 修改设备事件 + * + * @param mqttSensorEvent 设备事件 + * @return 结果 + */ + @Override + public int updateMqttSensorEvent(MqttSensorEvent mqttSensorEvent) + { + mqttSensorEvent.setUpdateTime(DateUtils.getNowDate()); + return mqttSensorEventMapper.updateMqttSensorEvent(mqttSensorEvent); + } + + /** + * 批量删除设备事件 + * + * @param ids 需要删除的设备事件主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorEventByIds(Long[] ids) + { + return mqttSensorEventMapper.deleteMqttSensorEventByIds(ids); + } + + /** + * 删除设备事件信息 + * + * @param id 设备事件主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorEventById(Long id) + { + return mqttSensorEventMapper.deleteMqttSensorEventById(id); + } +} diff --git a/src/main/java/com/shzg/project/worn/service/impl/MqttSensorThresholdServiceImpl.java b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorThresholdServiceImpl.java new file mode 100644 index 0000000..bc440c9 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/service/impl/MqttSensorThresholdServiceImpl.java @@ -0,0 +1,108 @@ +package com.shzg.project.worn.service.impl; + +import java.util.List; +import com.shzg.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.shzg.project.worn.mapper.MqttSensorThresholdMapper; +import com.shzg.project.worn.domain.MqttSensorThreshold; +import com.shzg.project.worn.service.IMqttSensorThresholdService; + +/** + * 传感器阈值配置Service业务层处理 + * + * @author shzg + * @date 2026-04-01 + */ +@Service +public class MqttSensorThresholdServiceImpl implements IMqttSensorThresholdService +{ + @Autowired + private MqttSensorThresholdMapper mqttSensorThresholdMapper; + + /** + * 查询传感器阈值配置 + * + * @param id 传感器阈值配置主键 + * @return 传感器阈值配置 + */ + @Override + public MqttSensorThreshold selectMqttSensorThresholdById(Long id) + { + return mqttSensorThresholdMapper.selectMqttSensorThresholdById(id); + } + + /** + * 查询传感器阈值配置列表 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 传感器阈值配置 + */ + @Override + public List selectMqttSensorThresholdList(MqttSensorThreshold mqttSensorThreshold) + { + return mqttSensorThresholdMapper.selectMqttSensorThresholdList(mqttSensorThreshold); + } + + /** + * 新增传感器阈值配置 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 结果 + */ + @Override + public int insertMqttSensorThreshold(MqttSensorThreshold mqttSensorThreshold) + { + mqttSensorThreshold.setCreateTime(DateUtils.getNowDate()); + return mqttSensorThresholdMapper.insertMqttSensorThreshold(mqttSensorThreshold); + } + + /** + * 修改传感器阈值配置 + * + * @param mqttSensorThreshold 传感器阈值配置 + * @return 结果 + */ + @Override + public int updateMqttSensorThreshold(MqttSensorThreshold mqttSensorThreshold) + { + mqttSensorThreshold.setUpdateTime(DateUtils.getNowDate()); + return mqttSensorThresholdMapper.updateMqttSensorThreshold(mqttSensorThreshold); + } + + /** + * 批量删除传感器阈值配置 + * + * @param ids 需要删除的传感器阈值配置主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorThresholdByIds(Long[] ids) + { + return mqttSensorThresholdMapper.deleteMqttSensorThresholdByIds(ids); + } + + /** + * 删除传感器阈值配置信息 + * + * @param id 传感器阈值配置主键 + * @return 结果 + */ + @Override + public int deleteMqttSensorThresholdById(Long id) + { + return mqttSensorThresholdMapper.deleteMqttSensorThresholdById(id); + } + + /** + * 根据设备id和指标类型获取阈值配置 + * @param deviceId + * @param metricType + * @return + */ + @Override + public MqttSensorThreshold getThreshold(Long deviceId, String metricType) + { + return mqttSensorThresholdMapper.selectByDeviceAndMetric(deviceId, metricType); + } +} diff --git a/src/main/java/com/shzg/project/worn/unit/MqttDeviceCache.java b/src/main/java/com/shzg/project/worn/unit/MqttDeviceCache.java new file mode 100644 index 0000000..6ec9533 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/unit/MqttDeviceCache.java @@ -0,0 +1,68 @@ +package com.shzg.project.worn.unit; + +import com.shzg.project.worn.domain.MqttSensorDevice; +import com.shzg.project.worn.service.IMqttSensorDeviceService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Component +public class MqttDeviceCache { + + @Autowired + private IMqttSensorDeviceService deviceService; + + // 本地缓存 + private final Map cache = new ConcurrentHashMap<>(); + + /** + * 启动加载 + */ + @PostConstruct + public void init() { + refresh(); + } + + /** + * 定时刷新(10分钟) + */ + @Scheduled(fixedDelay = 10 * 60 * 1000) + public void refresh() { + try { + List list = deviceService.selectMqttSensorDeviceList(new MqttSensorDevice()); + + Map newCache = new ConcurrentHashMap<>(); + + for (MqttSensorDevice device : list) { + if (device.getDevEui() != null) { + newCache.put(device.getDevEui().toLowerCase(), device); + } + } + + cache.clear(); + cache.putAll(newCache); + + log.info("[MQTT] 设备缓存刷新完成,数量={}", cache.size()); + + } catch (Exception e) { + log.error("[MQTT] 设备缓存刷新失败", e); + } + } + + /** + * 获取设备 + */ + public MqttSensorDevice get(String devEui) { + if (devEui == null) { + return null; + } + return cache.get(devEui.toLowerCase()); + } +} \ No newline at end of file diff --git a/src/main/java/com/shzg/project/worn/unit/MqttThreadPoolConfig.java b/src/main/java/com/shzg/project/worn/unit/MqttThreadPoolConfig.java new file mode 100644 index 0000000..e778093 --- /dev/null +++ b/src/main/java/com/shzg/project/worn/unit/MqttThreadPoolConfig.java @@ -0,0 +1,22 @@ +package com.shzg.project.worn.unit; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.*; + +@Configuration +public class MqttThreadPoolConfig { + + @Bean("mqttExecutor") + public Executor mqttExecutor() { + return new ThreadPoolExecutor( + 10, // 核心线程 + 50, // 最大线程 + 60, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(1000), + new ThreadPoolExecutor.CallerRunsPolicy() + ); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 342029c..db09a80 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -69,13 +69,15 @@ spring: # redis 配置 redis: # 地址 -# host: localhost - host: 192.168.1.251 + host: localhost +# host: 192.168.1.251 +# host: 47.100.212.83 # 端口,默认为6379 port: 6379 # 数据库索引 database: 0 # 密码 +# password: shzg password: # 连接超时时间 timeout: 10s @@ -155,45 +157,25 @@ gen: # ========================= mqtt: # 是否启用 MQTT 功能 - # true:系统启动时自动连接 EMQX 并订阅 Topic - # false:不初始化 MQTT 客户端 enabled: true # MQTT Broker 地址 - # tcp://IP:1883 —— MQTT TCP - # ws://IP:8083 —— MQTT WebSocket - # ssl://IP:8883 —— MQTT SSL broker: tcp://47.100.212.83:1883 - # MQTT 客户端 ID(在 EMQX 中唯一) - # 建议:系统名 + 模块名,避免重复 - clientId: zg-wms-wsj - - # MQTT 账号(EMQX Dashboard 中配置) + # MQTT 账号 username: test # MQTT 密码 password: 123456 # 是否清除会话 - # true :断开后清除订阅和未接收消息(推荐后端使用) - # false :保留会话(适合设备端) cleanSession: true # 心跳时间(秒) - # 客户端与 Broker 保持连接的心跳间隔 keepAlive: 30 # 连接超时时间(秒) timeout: 10 - # 默认消息 QoS - # 0:最多一次(不保证到达,性能最好) - # 1:至少一次(推荐,业务数据常用) - # 2:仅一次(最严格,性能最差) - qos: 1 - - # 订阅的 Topic 列表(支持通配符) - # 建议先使用测试 Topic,确认链路正常后再接入真实业务 Topic - topics: - - zg/wms/test/# \ No newline at end of file + # 默认 QoS + qos: 1 \ No newline at end of file diff --git a/src/main/resources/mybatis/worn/MqttSensorCommandMapper.xml b/src/main/resources/mybatis/worn/MqttSensorCommandMapper.xml new file mode 100644 index 0000000..26bf134 --- /dev/null +++ b/src/main/resources/mybatis/worn/MqttSensorCommandMapper.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + select id, device_id, topic, command, payload, status, send_time, remark, create_by, create_time, update_by, update_time, is_delete from mqtt_sensor_command + + + + + + + + insert into mqtt_sensor_command + + device_id, + topic, + command, + payload, + status, + send_time, + remark, + create_by, + create_time, + update_by, + update_time, + is_delete, + + + #{deviceId}, + #{topic}, + #{command}, + #{payload}, + #{status}, + #{sendTime}, + #{remark}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + #{isDelete}, + + + + + update mqtt_sensor_command + + device_id = #{deviceId}, + topic = #{topic}, + command = #{command}, + payload = #{payload}, + status = #{status}, + send_time = #{sendTime}, + remark = #{remark}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + is_delete = #{isDelete}, + + where id = #{id} + + + + delete from mqtt_sensor_command where id = #{id} + + + + delete from mqtt_sensor_command where id in + + #{id} + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/worn/MqttSensorDataMapper.xml b/src/main/resources/mybatis/worn/MqttSensorDataMapper.xml new file mode 100644 index 0000000..e488b1d --- /dev/null +++ b/src/main/resources/mybatis/worn/MqttSensorDataMapper.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + id, device_id, topic, project, warehouse, payload, data_json, + battery, temperature, humidity, nh3, h2s, concentration, + water_status, + remark, create_by, create_time, update_by, update_time, is_delete + from mqtt_sensor_data + + + + + + + + insert into mqtt_sensor_data + + device_id, + topic, + project, + warehouse, + payload, + data_json, + battery, + temperature, + humidity, + nh3, + h2s, + concentration, + + water_status, + + remark, + create_by, + create_time, + update_by, + update_time, + is_delete, + + + #{deviceId}, + #{topic}, + #{project}, + #{warehouse}, + #{payload}, + #{dataJson}, + #{battery}, + #{temperature}, + #{humidity}, + #{nh3}, + #{h2s}, + #{concentration}, + #{waterStatus}, + #{remark}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + #{isDelete}, + + + + + update mqtt_sensor_data + + device_id = #{deviceId}, + topic = #{topic}, + project = #{project}, + warehouse = #{warehouse}, + payload = #{payload}, + data_json = #{dataJson}, + battery = #{battery}, + temperature = #{temperature}, + humidity = #{humidity}, + nh3 = #{nh3}, + h2s = #{h2s}, + concentration = #{concentration}, + + + water_status = #{waterStatus}, + + remark = #{remark}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + is_delete = #{isDelete}, + + where id = #{id} + + + + delete from mqtt_sensor_data where id = #{id} + + + + delete from mqtt_sensor_data where id in + + #{id} + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/worn/MqttSensorDeviceMapper.xml b/src/main/resources/mybatis/worn/MqttSensorDeviceMapper.xml new file mode 100644 index 0000000..9ba4c9e --- /dev/null +++ b/src/main/resources/mybatis/worn/MqttSensorDeviceMapper.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + select id, dev_eui, device_name, device_type, dept_id, status, remark, create_by, create_time, update_by, update_time, is_delete from mqtt_sensor_device + + + + + + + + insert into mqtt_sensor_device + + dev_eui, + device_name, + device_type, + dept_id, + status, + remark, + create_by, + create_time, + update_by, + update_time, + is_delete, + + + #{devEui}, + #{deviceName}, + #{deviceType}, + #{deptId}, + #{status}, + #{remark}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + #{isDelete}, + + + + + update mqtt_sensor_device + + dev_eui = #{devEui}, + device_name = #{deviceName}, + device_type = #{deviceType}, + dept_id = #{deptId}, + status = #{status}, + remark = #{remark}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + is_delete = #{isDelete}, + + where id = #{id} + + + + delete from mqtt_sensor_device where id = #{id} + + + + delete from mqtt_sensor_device where id in + + #{id} + + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/worn/MqttSensorEventMapper.xml b/src/main/resources/mybatis/worn/MqttSensorEventMapper.xml new file mode 100644 index 0000000..976b52c --- /dev/null +++ b/src/main/resources/mybatis/worn/MqttSensorEventMapper.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + select id, device_id, event_type, event_desc, level, status, remark, create_by, create_time, update_by, update_time, is_delete from mqtt_sensor_event + + + + + + + + insert into mqtt_sensor_event + + device_id, + event_type, + event_desc, + level, + status, + remark, + create_by, + create_time, + update_by, + update_time, + is_delete, + + + #{deviceId}, + #{eventType}, + #{eventDesc}, + #{level}, + #{status}, + #{remark}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + #{isDelete}, + + + + + update mqtt_sensor_event + + device_id = #{deviceId}, + event_type = #{eventType}, + event_desc = #{eventDesc}, + level = #{level}, + status = #{status}, + remark = #{remark}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + is_delete = #{isDelete}, + + where id = #{id} + + + + delete from mqtt_sensor_event where id = #{id} + + + + delete from mqtt_sensor_event where id in + + #{id} + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/worn/MqttSensorThresholdMapper.xml b/src/main/resources/mybatis/worn/MqttSensorThresholdMapper.xml new file mode 100644 index 0000000..418d590 --- /dev/null +++ b/src/main/resources/mybatis/worn/MqttSensorThresholdMapper.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + select id, device_id, metric_type, warn_min, warn_max, alarm_min, alarm_max, unit, status, remark, create_by, create_time, update_by, update_time, is_delete from mqtt_sensor_threshold + + + + + + + + insert into mqtt_sensor_threshold + + device_id, + metric_type, + warn_min, + warn_max, + alarm_min, + alarm_max, + unit, + status, + remark, + create_by, + create_time, + update_by, + update_time, + is_delete, + + + #{deviceId}, + #{metricType}, + #{warnMin}, + #{warnMax}, + #{alarmMin}, + #{alarmMax}, + #{unit}, + #{status}, + #{remark}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + #{isDelete}, + + + + + update mqtt_sensor_threshold + + device_id = #{deviceId}, + metric_type = #{metricType}, + warn_min = #{warnMin}, + warn_max = #{warnMax}, + alarm_min = #{alarmMin}, + alarm_max = #{alarmMax}, + unit = #{unit}, + status = #{status}, + remark = #{remark}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + is_delete = #{isDelete}, + + where id = #{id} + + + + delete from mqtt_sensor_threshold where id = #{id} + + + + delete from mqtt_sensor_threshold where id in + + #{id} + + + + + \ No newline at end of file