#update mqtt 上位机

This commit is contained in:
2026-08-07 08:45:43 +08:00
parent c3398c6c07
commit 3ba32b19ce
12 changed files with 403 additions and 80 deletions

80
.idea/workspace.xml generated
View File

@@ -4,23 +4,18 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="84ac1b15-3cfa-4375-9957-45b7b581d04e" name="Changes" comment="# commit">
<list default="true" id="84ac1b15-3cfa-4375-9957-45b7b581d04e" name="Changes" comment="#update">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-admin/src/main/resources/application-dev.yml" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-admin/src/main/resources/application-dev.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/core/business/Device.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/core/business/Device.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-external/src/main/java/com/maibu/uav/controller/UAVController.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-external/src/main/java/com/maibu/uav/controller/UAVController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-external/src/main/java/com/maibu/uav/dto/UavDeviceStateResponseDetailDTO.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-external/src/main/java/com/maibu/uav/dto/UavDeviceStateResponseDetailDTO.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-external/src/main/java/com/maibu/uav/service/UAVService.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-external/src/main/java/com/maibu/uav/service/UAVService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/SecurityConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/SecurityConfig.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/NettyServer.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/NettyServer.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/constant/Constant.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/constant/Constant.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/memory/GlobalMemory.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/memory/GlobalMemory.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/memory/SiteMemory.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/memory/SiteMemory.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/mqtt/MqttTopic.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-common/src/main/java/com/maibu/mqtt/MqttTopic.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/init/InitThread.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/init/InitThread.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/DataToDataBaseHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/DataToDataBaseHandler.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/DeviceConnectHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/DeviceConnectHandler.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/HeaderFooterDecoder.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/HeaderFooterDecoder.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/HexEncoder.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/netty/handler/HexEncoder.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/DeviceController.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/DeviceController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceService.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceServiceImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceMapper.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/common/MiddleConstant.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/common/MiddleConstant.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/service/DeviceThreadService.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/service/DeviceThreadService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/AlarmServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/AlarmServiceImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/IotDeviceCommonService.java" beforeDir="false" afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/IotDeviceCommonService.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -40,6 +35,11 @@
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="main" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="HttpClientEndpointsTabState">
@@ -100,7 +100,7 @@
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;Spring Boot.MiddlePlatformApplication.executor&quot;: &quot;Debug&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;git-widget-placeholder&quot;: &quot;dev__new__connect&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/maibu/Test/MiddlePlatform/maibu-external/src/main/java/com/maibu/uav&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
@@ -160,11 +160,11 @@
<recent name="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service" />
</key>
<key name="CopyClassDialog.RECENTS_KEY">
<recent name="com.maibu.mqtt" />
<recent name="com.maibu.core.business.dto" />
<recent name="com.maibu.uav.dto" />
<recent name="com.maibu.uav.controller" />
<recent name="com.maibu.mapper" />
<recent name="com.maibu.core.business.uav" />
</key>
</component>
<component name="RunManager" selected="Spring Boot.MiddlePlatformApplication">
@@ -190,17 +190,6 @@
<configuration name="MiddlePlatformApplication | #3" type="HttpClient.HttpRequestRunConfigurationType" factoryName="HTTP Request" temporary="true" nameIsGenerated="true" path="$APPLICATION_CONFIG_DIR$/scratches/MiddlePlatformApplication.http" executionIdentifier="#3" index="3" runType="Run single request">
<method v="2" />
</configuration>
<configuration default="true" type="JetRunConfigurationType">
<module name="maibu-iot-service" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
<module name="maibu-iot-service" />
<option name="filePath" />
<method v="2" />
</configuration>
<configuration name="MiddlePlatformApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" temporary="true" nameIsGenerated="true">
<option name="FRAME_DEACTIVATION_UPDATE_POLICY" value="UpdateClassesAndResources" />
<module name="maibu-admin" />
@@ -235,8 +224,8 @@
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-30f59d01ecdd-cffe25b9f5b3-intellij.indexing.shared.core-IU-253.28294.251" />
<option value="bundled-js-predefined-d6986cc7102b-c7e53b3be11b-JavaScript-IU-253.28294.251" />
<option value="bundled-jdk-30f59d01ecdd-26cb7f24e5b0-intellij.indexing.shared.core-IU-253.29346.138" />
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-IU-253.29346.138" />
</set>
</attachedChunks>
</component>
@@ -294,6 +283,7 @@
<workItem from="1779683678663" duration="17250000" />
<workItem from="1779755672380" duration="9935000" />
<workItem from="1779770107645" duration="17442000" />
<workItem from="1786062684292" duration="833000" />
</task>
<task id="LOCAL-00001" summary="#update">
<option name="closed" value="true" />
@@ -543,7 +533,23 @@
<option name="project" value="LOCAL" />
<updated>1779242181619</updated>
</task>
<option name="localTasksCounter" value="32" />
<task id="LOCAL-00032" summary="#update">
<option name="closed" value="true" />
<created>1785982626224</created>
<option name="number" value="00032" />
<option name="presentableId" value="LOCAL-00032" />
<option name="project" value="LOCAL" />
<updated>1785982626225</updated>
</task>
<task id="LOCAL-00033" summary="#update">
<option name="closed" value="true" />
<created>1785991726504</created>
<option name="number" value="00033" />
<option name="presentableId" value="LOCAL-00033" />
<option name="project" value="LOCAL" />
<updated>1785991726504</updated>
</task>
<option name="localTasksCounter" value="34" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -594,7 +600,6 @@
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<MESSAGE value="#init" />
<MESSAGE value="#update" />
<MESSAGE value="物模型的新增和修改和查看列表接口" />
<MESSAGE value="SysUser新增权限字段,和需改增加和修改和查询列表接口" />
<MESSAGE value="修改接口system/site/list 根据组织id得到场站list" />
@@ -617,7 +622,8 @@
<MESSAGE value="#id重复bug" />
<MESSAGE value="#修复工单修改接口" />
<MESSAGE value="# commit" />
<option name="LAST_COMMIT_MESSAGE" value="# commit" />
<MESSAGE value="#update" />
<option name="LAST_COMMIT_MESSAGE" value="#update" />
<option name="NON_MODAL_COMMIT_POSTPONE_SLOW_CHECKS" value="false" />
</component>
<component name="XDebuggerManager">
@@ -700,17 +706,17 @@
</line-breakpoint>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceServiceImpl.java</url>
<line>234</line>
<line>237</line>
<option name="timeStamp" value="51" />
</line-breakpoint>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceServiceImpl.java</url>
<line>247</line>
<line>250</line>
<option name="timeStamp" value="52" />
</line-breakpoint>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceServiceImpl.java</url>
<line>282</line>
<line>285</line>
<option name="timeStamp" value="61" />
</line-breakpoint>
<line-breakpoint enabled="true" type="java-method">
@@ -725,7 +731,7 @@
<line-breakpoint enabled="true" type="java-method">
<url>file://$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/controller/IOTWorkOrderController.java</url>
<line>58</line>
<properties class="com.maibu.controller.IOTWorkOrderController" method="edit">
<properties class="com.maibu.controller.IOTWorkOrderController" method="list">
<option name="EMULATED" value="true" />
<option name="WATCH_EXIT" value="false" />
</properties>

View File

@@ -24,4 +24,6 @@ public class Constant {
public static final String WheelGainCoefficients = "WheelGainCoefficients";
public static final String mqttClientPrefix = "common-device-client-";
public static final String mqttHandleKey = "hostHandle";
}

View File

@@ -1,17 +1,15 @@
package com.maibu.memory;
import com.maibu.constant.Constant;
import com.maibu.core.business.Device;
import com.maibu.core.business.IoTCommonDevice;
import com.maibu.influxdb.util.InfluxDBUtil;
import com.maibu.mqtt.MqttClientUtil;
import com.maibu.mqtt.*;
import lombok.Data;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component
@@ -28,8 +26,9 @@ public class GlobalMemory {
public static InfluxDBUtil influxDBUtil;
public static MqttClientUtil mqttClientUtil;
// public static MqttClientUtil mqttClientUtil;
public static CustomMqttDeviceMonitor customMqttDeviceMonitor = null; //这里面包含了 连我们自己的mqtt 和外部的一些设备,自己的用clientId 是固定的
public static SiteMemory getSiteMemory(Long orgId, Long siteId) {
@@ -52,7 +51,7 @@ public class GlobalMemory {
/**
* 新增场站内存
*
*
* @param orgId
* @param siteId
* @param siteMemory
@@ -72,7 +71,7 @@ public class GlobalMemory {
/**
* 删除
*
*
* @param orgId
* @param siteId
*/
@@ -85,4 +84,29 @@ public class GlobalMemory {
}
}
public static void initMqttDeviceMonitor(String mqttHostUrl, String mqttClientId, String mqttUsername, String mqttPassword) {
if (customMqttDeviceMonitor == null) {
// 1. 创建 MQTT 监控器和消息处理器
customMqttDeviceMonitor = new CustomMqttDeviceMonitor();
// customMqttDeviceMonitor.registerHandler(new GenericPropertyMessageHandler());
customMqttDeviceMonitor.registerHandler(new HostMessageHandler()); //专门处理上位机的
// customMqttDeviceMonitor.registerHandler(new ActionMessageHandler());
//单独注册 对接上位机的
// 注册设备到 MQTT 监控器
CustomMqttDeviceConfig deviceConfig = new CustomMqttDeviceConfig();
deviceConfig.setDeviceKey(mqttClientId);
deviceConfig.setBrokerUrl(mqttHostUrl);
deviceConfig.setClientId(mqttClientId);
deviceConfig.setUsername(mqttUsername);
deviceConfig.setPassword(mqttPassword);
//TODO 添加所有需要订阅的
deviceConfig.setSubscribeTopics(Arrays.asList(MqttTopic.MOWER_WILDCARD_LOCATION));
deviceConfig.setHandlerType(Constant.mqttHandleKey); //唯一的 用于处理上位机mqtt的
customMqttDeviceMonitor.registerDevice(deviceConfig);
}
}
}

View File

@@ -30,8 +30,6 @@ public class SiteMemory {
public SysSite sysSite;
public CustomMqttDeviceMonitor CustomMqttDeviceMonitor;
public static final ConcurrentHashMap<String, MqttClient> mqttClientMap = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<Long, OrderModel> orderModelMap = new ConcurrentHashMap<>();

View File

@@ -0,0 +1,283 @@
package com.maibu.mqtt;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.maibu.constant.Constant;
import lombok.extern.slf4j.Slf4j;
/**
* mqtt相关的 上位机处理器
*/
@Slf4j
public class HostMessageHandler implements CustomMqttMessageHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String getHandlerType() {
return Constant.mqttHandleKey;
}
@Override
public boolean supports(String handlerType) {
return Constant.mqttHandleKey.equals(handlerType);
}
/**
* 处理 MQTT 消息
*
* <p>通配符订阅场景下,deviceKey 为配置中的固定标识(如 "host-monitor"),
* 真正的设备 ID 需从 topic 中解析,通过 {@link #extractDeviceId(String)} 获取。</p>
*/
@Override
public void handleMessage(String deviceKey, String topic, String payload, CustomMqttDeviceConfig config) {
try {
String deviceId = extractDeviceId(topic);
if (deviceId == null) {
log.warn("无法从topic解析deviceId: {}", topic);
return;
}
String action = extractAction(topic);
if (action == null) {
log.debug("未识别的topic: {}", topic);
return;
}
switch (action) {
case "location_post":
handleLocationPost(deviceId, topic, payload);
break;
case "realtime_post":
handleRealtimePost(deviceId, topic, payload);
break;
case "status_post":
handleStatusPost(deviceId, topic, payload);
break;
case "heartbeat_post":
handleHeartbeatPost(deviceId, topic, payload);
break;
case "error_post":
handleErrorPost(deviceId, topic, payload);
break;
case "route_post":
case "point_post":
handleTaskPost(deviceId, topic, payload, action);
break;
case "task_status_post":
case "task_finish_post":
handleTaskPost(deviceId, topic, payload, action);
break;
case "config_reply":
handleConfigReply(deviceId, topic, payload);
break;
case "control_reply":
handleControlReply(deviceId, topic, payload);
break;
default:
log.debug("割草机消息: deviceId={}, topic={}, action={}", deviceId, topic, action);
break;
}
} catch (Exception e) {
log.error("消息处理异常: topic={}, error={}", topic, e.getMessage());
}
}
/**
* 处理 GPS 位置数据上报
* topic: mower/{deviceId}/property/location/post
*/
private void handleLocationPost(String deviceId, String topic, String payload) {
try {
JsonNode jsonNode = objectMapper.readTree(payload);
double latitude = getDouble(jsonNode, "latitude", "lat");
double longitude = getDouble(jsonNode, "longitude", "lng", "lon");
double speed = getDouble(jsonNode, "speed");
double heading = getDouble(jsonNode, "heading", "direction");
double altitude = getDouble(jsonNode, "altitude", "alt");
int satellites = getInt(jsonNode, "satellites", "sats");
String fixQuality = getString(jsonNode, "fixQuality", "fix_quality");
Double batteryLevel = getDoubleObj(jsonNode, "batteryLevel", "battery");
log.info("【位置数据】deviceId={}, lat={}, lng={}, speed={}, heading={}, sats={}",
deviceId, latitude, longitude, speed, heading, satellites);
onLocationUpdate(deviceId, latitude, longitude, speed, heading, altitude, satellites, fixQuality, batteryLevel);
} catch (Exception e) {
log.error("位置消息解析失败: deviceId={}, payload={}, error={}", deviceId, payload, e.getMessage());
}
}
/**
* 处理实时运行数据
* topic: mower/{deviceId}/property/realtime/post
*/
private void handleRealtimePost(String deviceId, String topic, String payload) {
log.debug("【实时数据】deviceId={}, payload={}", deviceId, payload);
}
/**
* 处理状态变更事件
* topic: mower/{deviceId}/event/status/post
*/
private void handleStatusPost(String deviceId, String topic, String payload) {
log.info("【状态变更】deviceId={}, payload={}", deviceId, payload);
}
/**
* 处理心跳
* topic: mower/{deviceId}/event/heartbeat/post
*/
private void handleHeartbeatPost(String deviceId, String topic, String payload) {
log.debug("【心跳】deviceId={}", deviceId);
}
/**
* 处理错误告警
* topic: mower/{deviceId}/event/error/post
*/
private void handleErrorPost(String deviceId, String topic, String payload) {
log.warn("【错误告警】deviceId={}, payload={}", deviceId, payload);
}
/**
* 处理任务相关上报
*/
private void handleTaskPost(String deviceId, String topic, String payload, String action) {
log.info("【任务上报】deviceId={}, action={}, payload={}", deviceId, action, payload);
}
/**
* 处理配置响应
*/
private void handleConfigReply(String deviceId, String topic, String payload) {
log.debug("【配置响应】deviceId={}, payload={}", deviceId, payload);
}
/**
* 处理控制指令响应
*/
private void handleControlReply(String deviceId, String topic, String payload) {
log.debug("【控制响应】deviceId={}, payload={}", deviceId, payload);
}
/**
* 位置数据回调,子类可覆盖实现业务逻辑
*/
protected void onLocationUpdate(String deviceId, double latitude, double longitude,
double speed, double heading, double altitude,
int satellites, String fixQuality, Double batteryLevel) {
}
/**
* 从 Topic 中解析 deviceId
* Topic 格式:mower/{deviceId}/...
*/
private String extractDeviceId(String topic) {
if (topic == null || topic.isEmpty()) {
return null;
}
String[] parts = topic.split("/");
if (parts.length < 2 || !"mower".equals(parts[0])) {
return null;
}
return parts[1];
}
/**
* 从 Topic 中提取动作标识
* 将 mower/{deviceId}/xxx/yyy/post 转为 xxx_yyy_post 形式
*/
private String extractAction(String topic) {
String[] parts = topic.split("/");
if (parts.length < 3 || !"mower".equals(parts[0])) {
return null;
}
StringBuilder path = new StringBuilder();
for (int i = 2; i < parts.length; i++) {
if (i > 2) path.append("/");
path.append(parts[i]);
}
String remaining = path.toString();
if (remaining.equals("property/location/post")) return "location_post";
if (remaining.equals("property/realtime/post")) return "realtime_post";
if (remaining.equals("event/status/post")) return "status_post";
if (remaining.equals("event/heartbeat/post")) return "heartbeat_post";
if (remaining.equals("event/error/post")) return "error_post";
if (remaining.equals("task/route/post")) return "route_post";
if (remaining.equals("task/point/post")) return "point_post";
if (remaining.equals("task/status/post")) return "task_status_post";
if (remaining.equals("task/finish/post")) return "task_finish_post";
if (remaining.equals("action/config/reply")) return "config_reply";
if (remaining.equals("action/control/reply")) return "control_reply";
return null;
}
private double getDouble(JsonNode node, String... names) {
for (String name : names) {
JsonNode fn = node.get(name);
if (fn != null && !fn.isNull()) {
if (fn.isNumber()) return fn.asDouble();
try {
return Double.parseDouble(fn.asText());
} catch (Exception ignored) {
}
}
}
return -1.0;
}
private Double getDoubleObj(JsonNode node, String... names) {
for (String name : names) {
JsonNode fn = node.get(name);
if (fn != null && !fn.isNull()) {
if (fn.isNumber()) return fn.asDouble();
try {
return Double.parseDouble(fn.asText());
} catch (Exception ignored) {
}
}
}
return null;
}
private int getInt(JsonNode node, String... names) {
for (String name : names) {
JsonNode fn = node.get(name);
if (fn != null && !fn.isNull()) {
if (fn.isNumber()) return fn.asInt();
try {
return Integer.parseInt(fn.asText());
} catch (Exception ignored) {
}
}
}
return -1;
}
private String getString(JsonNode node, String... names) {
for (String name : names) {
JsonNode fn = node.get(name);
if (fn != null && !fn.isNull()) {
return fn.asText();
}
}
return null;
}
@Override
public void onConnectionLost(String deviceKey, Throwable cause) {
log.warn("主机监控连接断开: deviceKey={}, reason={}", deviceKey, cause.getMessage());
}
@Override
public void onSubscribed(String deviceKey, String topic) {
log.info("主机监控订阅主题: deviceKey={}, topic={}", deviceKey, topic);
}
}

View File

@@ -18,12 +18,12 @@ public class MqttTopic {
// ==================== 设备连接与状态 ====================
/** 设备上线/下线事件上报(设备->平台),对应 TCP 0x03 connect */
public static final String MOWER_STATUS_POST = "mower/%s/event/status/post";
/** 状态变更确认回复(平台->设备) */
// public static final String MOWER_STATUS_REPLY = "mower/%s/event/status/reply";
// ==================== 实时运行数据 ====================
/** 实时运行数据上报:电压/速度/温度/位置等(设备->平台),对应 TCP 0x02 status */
public static final String MOWER_REALTIME_POST = "mower/%s/property/realtime/post";
/** GPS 位置数据上报:经纬度、速度、航向等(设备->平台) */
public static final String MOWER_LOCATION_POST = "mower/%s/property/location/post";
// ==================== 心跳保活 ====================
/** 设备心跳上报(设备->平台),对应 TCP 0xFF heartbeat */
@@ -43,22 +43,12 @@ public class MqttTopic {
/** 远程控制指令下发:前进/后退/转向/割草等(平台->设备),对应 TCP 0x00 remoteControl */
public static final String MOWER_CONTROL_SET = "mower/%s/action/control/set";
// /** 控制指令响应(设备->平台) */
// public static final String MOWER_CONTROL_REPLY = "mower/%s/action/control/reply";
// ==================== 任务管理 ====================
/** 路径规划下发(平台->设备),对应 TCP 0x01 path */
public static final String MOWER_TASK_ROUTE_SET = "mower/%s/task/route/set";
public static final String MOWER_TASK_ROUTE_POST = "mower/%s/task/route/post";
/** 启动任务执行(平台->设备),对应 TCP 0x12 interaction */
public static final String MOWER_TASK_STATUS_SET = "mower/%s/task/status/set";
// /** 暂停任务(平台->设备) */
// public static final String MOWER_TASK_PAUSE_SET = "mower/%s/task/pause/set";
// /** 继续任务(平台->设备) */
// public static final String MOWER_TASK_RESUME_SET = "mower/%s/task/resume/set";
// /** 取消任务(平台->设备) */
// public static final String MOWER_TASK_CANCEL_SET = "mower/%s/task/cancel/set";
// /** 到达路径点上报(设备->平台) */
public static final String MOWER_TASK_POINT_POST = "mower/%s/task/point/post";
/** 任务状态变更上报:执行中/暂停/完成等(设备->平台) */
public static final String MOWER_TASK_STATUS_POST = "mower/%s/task/status/post";
@@ -90,6 +80,8 @@ public class MqttTopic {
public static final String MOWER_WILDCARD_STATUS = "mower/+/event/status/post";
/** 订阅所有设备的实时运行数据 */
public static final String MOWER_WILDCARD_REALTIME = "mower/+/property/realtime/post";
/** 上位机的额外消息 */
public static final String MOWER_WILDCARD_LOCATION = "mower/+/property/location/post";
/** 订阅所有设备的错误告警 */
public static final String MOWER_WILDCARD_ERROR = "mower/+/event/error/post";
/** 订阅所有设备的任务状态变更 */

View File

@@ -121,8 +121,9 @@ public class InitThread implements ApplicationRunner {
private void init() throws MqttException {
// 初始化InfluxDBUtil
GlobalMemory.influxDBUtil = new InfluxDBUtil(influxDBUrl, influxDBToken, influxDBOrg, influxDBBucket);
// 初始化MqttClientUtil
GlobalMemory.mqttClientUtil = new MqttClientUtil(mqttHostUrl, mqttClientId, mqttUsername, mqttPassword);
GlobalMemory.initMqttDeviceMonitor(mqttHostUrl, mqttClientId, mqttUsername, mqttPassword);
// 设备错误监听线程
deviceErrorMonitorExecutor.execute(() -> {
try {

View File

@@ -20,6 +20,7 @@ import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -63,6 +64,10 @@ public class DataToDataBaseHandler extends SimpleChannelInboundHandler<String> {
@Autowired
private DeviceRunParamMapper deviceRunParamMapper;
@Value("${mqtt.client-id}")
private String mqttClientId;
private static final Long recordInterval = 5000L; // 5秒记录一次
// private static final Long pushInterval = 500L; // 0.5秒推送一次
@@ -97,9 +102,11 @@ public class DataToDataBaseHandler extends SimpleChannelInboundHandler<String> {
String[] split = deviceStatus.split(",");
WebStatusMessageDTO dto = createWebDeviceStatusMessage(split);
try {
GlobalMemory.mqttClientUtil.publish(
String.format(MqttTopic.DEVICE_STATUS_TOPIC, deviceID),
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.DEVICE_STATUS_TOPIC, deviceID),
JsonUtils.toJsonString(dto));
// GlobalMemory.mqttClientUtil.publish(
// String.format(MqttTopic.DEVICE_STATUS_TOPIC, deviceID),
// JsonUtils.toJsonString(dto));
} catch (MqttException e) {
logger.error("发送实时状态数据错误: {}", e.getMessage());
}

View File

@@ -15,6 +15,7 @@ import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -82,6 +83,9 @@ public class DeviceConnectHandler extends SimpleChannelInboundHandler<String> {
@Resource
private WebsocketMesDispather websocketMesDispather;
@Value("${mqtt.client-id}")
private String mqttClientId;
public DeviceConnectHandler(DeviceSessionManager sessionManager) {
this.sessionManager = sessionManager;
}
@@ -364,8 +368,12 @@ public class DeviceConnectHandler extends SimpleChannelInboundHandler<String> {
changeDTO.setTaskId(taskId);
try {
GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_TASK_STATUS_TOPIC, taskId),
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.DEVICE_TASK_STATUS_TOPIC, taskId),
JsonUtils.toJsonString(changeDTO));
// GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_TASK_STATUS_TOPIC, taskId),
// JsonUtils.toJsonString(changeDTO));
} catch (MqttException e) {
logger.error("任务完成状态推送失败 task:{},device:{}error:{}", taskId, deviceId, e.getMessage());
}
@@ -392,8 +400,10 @@ public class DeviceConnectHandler extends SimpleChannelInboundHandler<String> {
reportDTO.setType(MesType.device_task_arrive_point);
try {
GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_TASK_ARRIVE_TOPIC, taskId),
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.DEVICE_TASK_ARRIVE_TOPIC, taskId),
JsonUtils.toJsonString(reportDTO));
// GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_TASK_ARRIVE_TOPIC, taskId),
// JsonUtils.toJsonString(reportDTO));
} catch (MqttException e) {
logger.error("任务点位到达推送失败 task:{},device:{}error:{}", taskId, deviceId, e.getMessage());
}

View File

@@ -15,9 +15,11 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.maibu.memory.DeviceSessionManager;
import com.maibu.utils.json.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
@@ -69,6 +71,9 @@ public class DeviceThreadService {
@Autowired
private AlertPushService alertPushService;
@Value("${mqtt.client-id}")
private String mqttClientId;
public void deviceErrorMonitor() throws InterruptedException, IOException {
initStandard();
@@ -184,7 +189,8 @@ public class DeviceThreadService {
// }
// }
try {
GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto);
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto);
// GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto);
List<NettyDevice> controlMasters = sessionManager.getAllSlaveControl(deviceId);
if (!CollectionUtils.isEmpty(controlMasters)) {
controlMasters.forEach(x -> {

View File

@@ -13,6 +13,7 @@ import com.maibu.core.enums.AlarmType;
import com.maibu.core.redis.RedisCache;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -51,6 +52,10 @@ public class AlarmServiceImpl extends ServiceImpl<AlarmMapper, Alarm> implements
@Autowired
private RedisCache redisCache;
@Value("${mqtt.client-id}")
private String mqttClientId;
@Override
public List<Alarm> selectAlarmPage(AlarmQueryDTO queryDTO) {
// validateQueryParams(queryDTO);
@@ -407,7 +412,8 @@ public class AlarmServiceImpl extends ServiceImpl<AlarmMapper, Alarm> implements
public void pushErrorMessage(String deviceId, String dto) {
try {
GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto);
GlobalMemory.customMqttDeviceMonitor.publish(mqttClientId, String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto);
// GlobalMemory.mqttClientUtil.publish(String.format(MqttTopic.DEVICE_ERROR_PUSH_TOPIC, deviceId), dto);
// List<NettyDevice> controlMasters =
// sessionManager.getAllSlaveControl(deviceId);
// if (!CollectionUtils.isEmpty(controlMasters)) {

View File

@@ -273,25 +273,15 @@ public class IotDeviceCommonService {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleWithFixedDelay(() -> {
try {
CustomMqttDeviceMonitor monitor = GlobalMemory.customMqttDeviceMonitor;
GlobalMemory.getAllSiteMemory().forEach(siteMemory -> {
CustomMqttDeviceMonitor monitor = siteMemory.getCustomMqttDeviceMonitor();
if (monitor == null) {
// 1. 创建 MQTT 监控器和消息处理器
monitor = new CustomMqttDeviceMonitor();
monitor.registerHandler(new GenericPropertyMessageHandler());
monitor.registerHandler(new ActionMessageHandler());
siteMemory.setCustomMqttDeviceMonitor(monitor);
}
// 2. 查询所有外部设备
List<IoTCommonDevice> deviceList = siteMemory.getAllCommonDevice();
if (CollectionUtils.isEmpty(deviceList)) {
return;
}
for (IoTCommonDevice device : deviceList) {
if (!ConnectType.MQTT.equals(device.getConnectType())) {
continue;
@@ -333,8 +323,6 @@ public class IotDeviceCommonService {
monitor.registerDevice(deviceConfig);
}
siteMemory.setCustomMqttDeviceMonitor(monitor);
monitor.start();
});
} catch (Exception e) {