|
@@ -0,0 +1,371 @@
|
|
|
|
+package com.hfln.device.infrastructure.mqtt.handler;
|
|
|
|
+
|
|
|
|
+import com.hfln.device.domain.port.DeviceEventPort;
|
|
|
|
+import com.hfln.device.common.util.JsonUtil;
|
|
|
|
+import com.hfln.device.common.constant.mqtt.topic.MqttTopics;
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
|
+import org.springframework.messaging.Message;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.regex.Matcher;
|
|
|
|
+import java.util.regex.Pattern;
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 设备消息处理器
|
|
|
|
+ * 使用Spring Integration MQTT替代@MqttSubscriber注解
|
|
|
|
+ * 处理设备相关的MQTT消息
|
|
|
|
+ */
|
|
|
|
+@Component
|
|
|
|
+@Slf4j
|
|
|
|
+public class DeviceMessageHandler {
|
|
|
|
+
|
|
|
|
+ private static final Pattern DEV_ID_PATTERN = Pattern.compile("^/dev/([^/]+)/.*$");
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ @Qualifier("deviceEventServiceImpl")
|
|
|
|
+ private DeviceEventPort deviceEventPort;
|
|
|
|
+
|
|
|
|
+ public void handleMessage(Message<?> message) {
|
|
|
|
+ try {
|
|
|
|
+ String topic = (String) message.getHeaders().get("mqtt_receivedTopic");
|
|
|
|
+ String payload = message.getPayload().toString();
|
|
|
|
+
|
|
|
|
+ if (topic == null) {
|
|
|
|
+ log.warn("Received message without topic header");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log.debug("Received device message: topic={}, payload={}", topic, payload);
|
|
|
|
+
|
|
|
|
+ // 根据主题路由到不同的处理方法
|
|
|
|
+ // 提取主题的操作部分(最后一段)
|
|
|
|
+ String action = extractActionFromTopic(topic);
|
|
|
|
+ if (action != null) {
|
|
|
|
+ switch (action) {
|
|
|
|
+ case "login":
|
|
|
|
+ handleDeviceLogin(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "keepalive":
|
|
|
|
+ handleDeviceKeepAlive(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "report_device_info":
|
|
|
|
+ handleDeviceReportDeviceInfo(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "report_device_param":
|
|
|
|
+ handleDeviceReportDeviceParam(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "dsp_data":
|
|
|
|
+ handleDeviceDspData(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "cloudpoint":
|
|
|
|
+ handleDeviceCloudPoint(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "report_falling_event":
|
|
|
|
+ handleDeviceReportFallEvent(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "report_presence_event":
|
|
|
|
+ handleDeviceReportPresenceEvent(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "set_debug_param":
|
|
|
|
+ handleSetDebugParam(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ case "get_debug_param":
|
|
|
|
+ handleGetDebugParam(topic, payload);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ log.debug("Unhandled device topic action: {} for topic: {}", action, topic);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ log.debug("Could not extract action from device topic: {}", topic);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device message: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备登录消息
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceLogin(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ log.info("Received device login message: {}", topic);
|
|
|
|
+
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+ Map<String, Object> deviceInfo = (Map<String, Object>) messageData.get("device_info");
|
|
|
|
+
|
|
|
|
+ if (deviceInfo == null) {
|
|
|
|
+ log.warn("Invalid device login message, missing device_info: {}", payload);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String deviceId = (String) deviceInfo.get("deviceid");
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleDeviceLogin(deviceId, deviceInfo, messageData);
|
|
|
|
+
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device login: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备保活消息
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceKeepAlive(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ log.debug("Received device keepalive: {}", deviceId);
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleDeviceKeepAlive(deviceId);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device keepalive: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备上报设备信息
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceReportDeviceInfo(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.info("Device info report: {}, payload: {}", deviceId, payload);
|
|
|
|
+
|
|
|
|
+ // 验证必要字段
|
|
|
|
+ if (!messageData.containsKey("deviceid") || !messageData.containsKey("device_type") ||
|
|
|
|
+ !messageData.containsKey("firmware") || !messageData.containsKey("device_ip")) {
|
|
|
|
+ log.warn("Invalid device info message, missing required fields: {}", payload);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleDeviceLogin(deviceId, messageData, messageData);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device info report: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备上报设备参数
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceReportDeviceParam(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.info("Device parameter report: {}, payload: {}", deviceId, payload);
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleDeviceParamReport(deviceId, messageData);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device parameter report: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备实时数据
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceDspData(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.debug("Device dsp data: {}", deviceId);
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleDspData(deviceId, messageData);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device dsp data: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备点云数据
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceCloudPoint(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.debug("Device cloud point data: {}", deviceId);
|
|
|
|
+
|
|
|
|
+ // 获取点云数据和目标点
|
|
|
|
+ List<List<Number>> cloudPoints = (List<List<Number>>) messageData.get("cloud_points");
|
|
|
|
+ List<List<Number>> trackerTargets = (List<List<Number>>) messageData.get("tracker_targets");
|
|
|
|
+
|
|
|
|
+ // 转换数据类型
|
|
|
|
+ List<List<Float>> cloudPointsFloat = null;
|
|
|
|
+ if (cloudPoints != null) {
|
|
|
|
+ cloudPointsFloat = cloudPoints.stream()
|
|
|
|
+ .map(point -> point.stream()
|
|
|
|
+ .map(number -> number.floatValue())
|
|
|
|
+ .collect(Collectors.toList()))
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ List<List<Float>> trackerTargetsFloat = null;
|
|
|
|
+ if (trackerTargets != null) {
|
|
|
|
+ trackerTargetsFloat = trackerTargets.stream()
|
|
|
|
+ .map(target -> target.stream()
|
|
|
|
+ .map(number -> number.floatValue())
|
|
|
|
+ .collect(Collectors.toList()))
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleCloudPoint(deviceId, cloudPointsFloat, trackerTargetsFloat);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device cloud point data: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备上报跌倒事件
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceReportFallEvent(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.info("Device fall event: {}, payload: {}", deviceId, payload);
|
|
|
|
+
|
|
|
|
+ // 验证必要字段
|
|
|
|
+ if (!messageData.containsKey("timestamp") || !messageData.containsKey("type") ||
|
|
|
|
+ !messageData.containsKey("event") || !messageData.containsKey("fallLocX") ||
|
|
|
|
+ !messageData.containsKey("fallLocY") || !messageData.containsKey("fallLocZ") ||
|
|
|
|
+ !messageData.containsKey("tarHeightEst")) {
|
|
|
|
+ log.warn("Invalid fall event message, missing required fields: {}", payload);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Long timestamp = ((Number) messageData.get("timestamp")).longValue();
|
|
|
|
+ String type = (String) messageData.get("type");
|
|
|
|
+ String event = (String) messageData.get("event");
|
|
|
|
+ // 坐标单位转换:厘米转米
|
|
|
|
+ Float fallLocX = ((Number) messageData.get("fallLocX")).floatValue() / 100.0f;
|
|
|
|
+ Float fallLocY = ((Number) messageData.get("fallLocY")).floatValue() / 100.0f;
|
|
|
|
+ Float fallLocZ = ((Number) messageData.get("fallLocZ")).floatValue() / 100.0f;
|
|
|
|
+ Float tarHeightEst = ((Number) messageData.get("tarHeightEst")).floatValue();
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleFallEvent(deviceId, timestamp, type, event,
|
|
|
|
+ fallLocX, fallLocY, fallLocZ, tarHeightEst);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device fall event: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设备上报存在事件
|
|
|
|
+ */
|
|
|
|
+ private void handleDeviceReportPresenceEvent(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.info("Device presence event: {}, payload: {}", deviceId, payload);
|
|
|
|
+
|
|
|
|
+ // 验证必要字段
|
|
|
|
+ if (!messageData.containsKey("timestamp") || !messageData.containsKey("type") ||
|
|
|
|
+ !messageData.containsKey("event")) {
|
|
|
|
+ log.warn("Invalid presence event message, missing required fields: {}", payload);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Long timestamp = ((Number) messageData.get("timestamp")).longValue();
|
|
|
|
+ String type = (String) messageData.get("type");
|
|
|
|
+ String event = (String) messageData.get("event");
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleExistEvent(deviceId, timestamp, type, event);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling device presence event: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理设置调试参数
|
|
|
|
+ */
|
|
|
|
+ private void handleSetDebugParam(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.info("Set debug param: {}, payload: {}", deviceId, payload);
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleSetDebugParam(deviceId, messageData);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling set debug param: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 处理获取调试参数
|
|
|
|
+ */
|
|
|
|
+ private void handleGetDebugParam(String topic, String payload) {
|
|
|
|
+ try {
|
|
|
|
+ String deviceId = extractDeviceIdFromTopic(topic);
|
|
|
|
+ if (deviceId != null) {
|
|
|
|
+ Map<String, Object> messageData = JsonUtil.parseMap(payload);
|
|
|
|
+
|
|
|
|
+ log.info("Get debug param: {}, payload: {}", deviceId, payload);
|
|
|
|
+
|
|
|
|
+ // 委托给应用层服务处理
|
|
|
|
+ deviceEventPort.handleGetDebugParam(deviceId, messageData);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("Error handling get debug param: {}", e.getMessage(), e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 从主题中提取设备ID
|
|
|
|
+ */
|
|
|
|
+ private String extractDeviceIdFromTopic(String topic) {
|
|
|
|
+ Matcher matcher = DEV_ID_PATTERN.matcher(topic);
|
|
|
|
+ if (matcher.find()) {
|
|
|
|
+ return matcher.group(1);
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 从主题中提取操作名称(最后一段)
|
|
|
|
+ * 格式:/dev/{device_id}/{action}
|
|
|
|
+ */
|
|
|
|
+ private String extractActionFromTopic(String topic) {
|
|
|
|
+ if (topic != null && topic.startsWith("/dev/")) {
|
|
|
|
+ String[] parts = topic.split("/");
|
|
|
|
+ if (parts.length >= 3) {
|
|
|
|
+ return parts[parts.length - 1]; // 返回最后一段
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+}
|