Commit a4e80d45fed7fe541e207510aea2ce7581b4c29d
1 parent
c658ac69
fix:【IoT 物联网】修复设备属性上报和配置设置主题,优化 MQTT 消息处理逻辑,更新相关配置文件
Showing
9 changed files
with
263 additions
and
84 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
| @@ -144,12 +144,13 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -144,12 +144,13 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 144 | 144 | ||
| 145 | // 3. 回复消息。前提:非 _reply 消息,并且非禁用回复的消息 | 145 | // 3. 回复消息。前提:非 _reply 消息,并且非禁用回复的消息 |
| 146 | if (IotDeviceMessageUtils.isReplyMessage(message) | 146 | if (IotDeviceMessageUtils.isReplyMessage(message) |
| 147 | - || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod()) | ||
| 148 | - || StrUtil.isEmpty(message.getServerId())) { | 147 | + || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod()) |
| 148 | + || StrUtil.isEmpty(message.getServerId())) { | ||
| 149 | return; | 149 | return; |
| 150 | } | 150 | } |
| 151 | try { | 151 | try { |
| 152 | - IotDeviceMessage replyMessage = IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(), replyData, | 152 | + IotDeviceMessage replyMessage = IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(), |
| 153 | + replyData, | ||
| 153 | serviceException != null ? serviceException.getCode() : null, | 154 | serviceException != null ? serviceException.getCode() : null, |
| 154 | serviceException != null ? serviceException.getMessage() : null); | 155 | serviceException != null ? serviceException.getMessage() : null); |
| 155 | sendDeviceMessage(replyMessage, device, message.getServerId()); | 156 | sendDeviceMessage(replyMessage, device, message.getServerId()); |
| @@ -175,7 +176,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -175,7 +176,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 175 | } | 176 | } |
| 176 | 177 | ||
| 177 | // 属性上报 | 178 | // 属性上报 |
| 178 | - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_REPORT.getMethod())) { | 179 | + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_REPORT.getMethod()) || |
| 180 | + Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())) { | ||
| 179 | devicePropertyService.saveDeviceProperty(device, message); | 181 | devicePropertyService.saveDeviceProperty(device, message); |
| 180 | return null; | 182 | return null; |
| 181 | } | 183 | } |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
| @@ -24,6 +24,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | @@ -24,6 +24,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | ||
| 24 | 24 | ||
| 25 | // ========== 设备属性 ========== | 25 | // ========== 设备属性 ========== |
| 26 | // 可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services | 26 | // 可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services |
| 27 | + PROPERTY_POST("thing.property.post", true), | ||
| 27 | PROPERTY_REPORT("thing.property.report", true), | 28 | PROPERTY_REPORT("thing.property.report", true), |
| 28 | 29 | ||
| 29 | PROPERTY_SET("thing.property.set", false), | 30 | PROPERTY_SET("thing.property.set", false), |
| @@ -33,7 +34,8 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | @@ -33,7 +34,8 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | ||
| 33 | 34 | ||
| 34 | ; | 35 | ; |
| 35 | 36 | ||
| 36 | - public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod).toArray(String[]::new); | 37 | + public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod) |
| 38 | + .toArray(String[]::new); | ||
| 37 | 39 | ||
| 38 | /** | 40 | /** |
| 39 | * 不进行 reply 回复的方法集合 | 41 | * 不进行 reply 回复的方法集合 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
| @@ -128,6 +128,10 @@ public class IotGatewayProperties { | @@ -128,6 +128,10 @@ public class IotGatewayProperties { | ||
| 128 | */ | 128 | */ |
| 129 | private Boolean mqttSsl; | 129 | private Boolean mqttSsl; |
| 130 | /** | 130 | /** |
| 131 | + * MQTT客户端 ID | ||
| 132 | + */ | ||
| 133 | + private String mqttClientId; | ||
| 134 | + /** | ||
| 131 | * MQTT 主题 | 135 | * MQTT 主题 |
| 132 | */ | 136 | */ |
| 133 | private List<String> mqttTopics; | 137 | private List<String> mqttTopics; |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/enums/IotDeviceTopicEnum.java
| @@ -14,52 +14,40 @@ import lombok.RequiredArgsConstructor; | @@ -14,52 +14,40 @@ import lombok.RequiredArgsConstructor; | ||
| 14 | @Getter | 14 | @Getter |
| 15 | public enum IotDeviceTopicEnum { | 15 | public enum IotDeviceTopicEnum { |
| 16 | 16 | ||
| 17 | - // TODO @haohao:SYS_TOPIC_PREFIX、SERVICE_TOPIC_PREFIX、REPLY_SUFFIX 类似这种,要不搞成这个里面的静态变量?不是枚举值 | ||
| 18 | - /** | ||
| 19 | - * 系统主题前缀 | ||
| 20 | - */ | ||
| 21 | - SYS_TOPIC_PREFIX("/sys/", "系统主题前缀"), | ||
| 22 | - | ||
| 23 | - /** | ||
| 24 | - * 服务调用主题前缀 | ||
| 25 | - */ | ||
| 26 | - SERVICE_TOPIC_PREFIX("/thing/service/", "服务调用主题前缀"), | ||
| 27 | - | ||
| 28 | - // TODO @haohao:注释时,中英文之间,有个空格; | ||
| 29 | /** | 17 | /** |
| 30 | * 设备属性设置主题 | 18 | * 设备属性设置主题 |
| 31 | - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/property/set | ||
| 32 | - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply | 19 | + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/property/set |
| 20 | + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/property/set_reply | ||
| 33 | */ | 21 | */ |
| 34 | - PROPERTY_SET_TOPIC("/thing/service/property/set", "设备属性设置主题"), | 22 | + PROPERTY_SET_TOPIC("/thing/property/set", "设备属性设置主题"), |
| 35 | 23 | ||
| 36 | /** | 24 | /** |
| 37 | * 设备属性获取主题 | 25 | * 设备属性获取主题 |
| 38 | - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/property/get | ||
| 39 | - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/property/get_reply | 26 | + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/property/get |
| 27 | + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/property/get_reply | ||
| 40 | */ | 28 | */ |
| 41 | - PROPERTY_GET_TOPIC("/thing/service/property/get", "设备属性获取主题"), | 29 | + PROPERTY_GET_TOPIC("/thing/property/get", "设备属性获取主题"), |
| 42 | 30 | ||
| 43 | /** | 31 | /** |
| 44 | * 设备配置设置主题 | 32 | * 设备配置设置主题 |
| 45 | - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/config/set | ||
| 46 | - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/config/set_reply | 33 | + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/config/set |
| 34 | + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/config/set_reply | ||
| 47 | */ | 35 | */ |
| 48 | - CONFIG_SET_TOPIC("/thing/service/config/set", "设备配置设置主题"), | 36 | + CONFIG_SET_TOPIC("/thing/config/set", "设备配置设置主题"), |
| 49 | 37 | ||
| 50 | /** | 38 | /** |
| 51 | - * 设备OTA升级主题 | ||
| 52 | - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade | ||
| 53 | - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade_reply | 39 | + * 设备 OTA 升级主题 |
| 40 | + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/ota/upgrade | ||
| 41 | + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/ota/upgrade_reply | ||
| 54 | */ | 42 | */ |
| 55 | - OTA_UPGRADE_TOPIC("/thing/service/ota/upgrade", "设备OTA升级主题"), | 43 | + OTA_UPGRADE_TOPIC("/thing/ota/upgrade", "设备 OTA 升级主题"), |
| 56 | 44 | ||
| 57 | /** | 45 | /** |
| 58 | * 设备属性上报主题 | 46 | * 设备属性上报主题 |
| 59 | * 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post | 47 | * 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post |
| 60 | * 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply | 48 | * 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply |
| 61 | */ | 49 | */ |
| 62 | - PROPERTY_POST_TOPIC("/thing/event/property/post", "设备属性上报主题"), | 50 | + PROPERTY_POST_TOPIC("/thing/property/post", "设备属性上报主题"), |
| 63 | 51 | ||
| 64 | /** | 52 | /** |
| 65 | * 设备事件上报主题前缀 | 53 | * 设备事件上报主题前缀 |
| @@ -69,95 +57,194 @@ public enum IotDeviceTopicEnum { | @@ -69,95 +57,194 @@ public enum IotDeviceTopicEnum { | ||
| 69 | /** | 57 | /** |
| 70 | * 设备事件上报主题后缀 | 58 | * 设备事件上报主题后缀 |
| 71 | */ | 59 | */ |
| 72 | - EVENT_POST_TOPIC_SUFFIX("/post", "设备事件上报主题后缀"), | 60 | + EVENT_POST_TOPIC_SUFFIX("/post", "设备事件上报主题后缀"); |
| 61 | + | ||
| 62 | + // ========== 静态常量 ========== | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 系统主题前缀 | ||
| 66 | + */ | ||
| 67 | + public static final String SYS_TOPIC_PREFIX = "/sys/"; | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 服务调用主题前缀 | ||
| 71 | + */ | ||
| 72 | + public static final String SERVICE_TOPIC_PREFIX = "/thing/"; | ||
| 73 | 73 | ||
| 74 | /** | 74 | /** |
| 75 | * 响应主题后缀 | 75 | * 响应主题后缀 |
| 76 | */ | 76 | */ |
| 77 | - REPLY_SUFFIX("_reply", "响应主题后缀"); | 77 | + public static final String REPLY_SUFFIX = "_reply"; |
| 78 | + | ||
| 79 | + // ========== 方法常量 ========== | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 服务方法前缀 | ||
| 83 | + */ | ||
| 84 | + public static final String SERVICE_METHOD_PREFIX = "thing."; | ||
| 85 | + | ||
| 86 | + /** | ||
| 87 | + * 属性服务方法前缀 | ||
| 88 | + */ | ||
| 89 | + public static final String PROPERTY_SERVICE_METHOD_PREFIX = "thing.property."; | ||
| 90 | + | ||
| 91 | + /** | ||
| 92 | + * 配置服务方法前缀 | ||
| 93 | + */ | ||
| 94 | + public static final String CONFIG_SERVICE_METHOD_PREFIX = "thing.config."; | ||
| 95 | + | ||
| 96 | + /** | ||
| 97 | + * OTA 服务方法前缀 | ||
| 98 | + */ | ||
| 99 | + public static final String OTA_SERVICE_METHOD_PREFIX = "thing.ota."; | ||
| 100 | + | ||
| 101 | + /** | ||
| 102 | + * 属性设置方法 | ||
| 103 | + */ | ||
| 104 | + public static final String PROPERTY_SET_METHOD = "thing.property.set"; | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * 属性获取方法 | ||
| 108 | + */ | ||
| 109 | + public static final String PROPERTY_GET_METHOD = "thing.property.get"; | ||
| 110 | + | ||
| 111 | + // ========== 主题匹配常量 ========== | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * 事件上报主题模式 | ||
| 115 | + */ | ||
| 116 | + public static final String EVENT_POST_TOPIC_PATTERN = "/thing/event/"; | ||
| 117 | + | ||
| 118 | + /** | ||
| 119 | + * 主题后缀:post | ||
| 120 | + */ | ||
| 121 | + public static final String POST_SUFFIX = "/post"; | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * 属性上报主题后缀 | ||
| 125 | + */ | ||
| 126 | + public static final String PROPERTY_POST_SUFFIX = "/thing/property/post"; | ||
| 127 | + | ||
| 128 | + /** | ||
| 129 | + * 属性设置响应主题包含 | ||
| 130 | + */ | ||
| 131 | + public static final String PROPERTY_SET_TOPIC_CONTAINS = "/thing/property/set"; | ||
| 132 | + | ||
| 133 | + /** | ||
| 134 | + * 属性获取响应主题包含 | ||
| 135 | + */ | ||
| 136 | + public static final String PROPERTY_GET_TOPIC_CONTAINS = "/thing/property/get"; | ||
| 137 | + | ||
| 138 | + // ========== MQTT 认证路径常量 ========== | ||
| 139 | + | ||
| 140 | + /** | ||
| 141 | + * MQTT 认证路径 | ||
| 142 | + */ | ||
| 143 | + public static final String MQTT_AUTH_AUTHENTICATE_PATH = "/mqtt/auth/authenticate"; | ||
| 144 | + | ||
| 145 | + /** | ||
| 146 | + * MQTT 连接事件路径 | ||
| 147 | + */ | ||
| 148 | + public static final String MQTT_AUTH_CONNECTED_PATH = "/mqtt/auth/connected"; | ||
| 149 | + | ||
| 150 | + /** | ||
| 151 | + * MQTT 断开事件路径 | ||
| 152 | + */ | ||
| 153 | + public static final String MQTT_AUTH_DISCONNECTED_PATH = "/mqtt/auth/disconnected"; | ||
| 78 | 154 | ||
| 79 | private final String topic; | 155 | private final String topic; |
| 80 | private final String description; | 156 | private final String description; |
| 81 | 157 | ||
| 158 | + // ========== 工具方法 ========== | ||
| 159 | + | ||
| 160 | + /** | ||
| 161 | + * 构建设备主题前缀 | ||
| 162 | + * | ||
| 163 | + * @param productKey 产品 Key | ||
| 164 | + * @param deviceName 设备名称 | ||
| 165 | + * @return 设备主题前缀:/sys/{productKey}/{deviceName} | ||
| 166 | + */ | ||
| 167 | + private static String buildDeviceTopicPrefix(String productKey, String deviceName) { | ||
| 168 | + return SYS_TOPIC_PREFIX + productKey + "/" + deviceName; | ||
| 169 | + } | ||
| 170 | + | ||
| 82 | /** | 171 | /** |
| 83 | * 构建设备服务调用主题 | 172 | * 构建设备服务调用主题 |
| 84 | * | 173 | * |
| 85 | - * @param productKey 产品Key | 174 | + * @param productKey 产品 Key |
| 86 | * @param deviceName 设备名称 | 175 | * @param deviceName 设备名称 |
| 87 | * @param serviceIdentifier 服务标识符 | 176 | * @param serviceIdentifier 服务标识符 |
| 88 | * @return 完整的主题路径 | 177 | * @return 完整的主题路径 |
| 89 | */ | 178 | */ |
| 90 | public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) { | 179 | public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) { |
| 91 | - // TODO @haohao:貌似 SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName 是统一的; | ||
| 92 | - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + | ||
| 93 | - SERVICE_TOPIC_PREFIX.getTopic() + serviceIdentifier; | 180 | + return buildDeviceTopicPrefix(productKey, deviceName) + SERVICE_TOPIC_PREFIX + serviceIdentifier; |
| 94 | } | 181 | } |
| 95 | 182 | ||
| 96 | /** | 183 | /** |
| 97 | * 构建设备属性设置主题 | 184 | * 构建设备属性设置主题 |
| 98 | * | 185 | * |
| 99 | - * @param productKey 产品Key | 186 | + * @param productKey 产品 Key |
| 100 | * @param deviceName 设备名称 | 187 | * @param deviceName 设备名称 |
| 101 | * @return 完整的主题路径 | 188 | * @return 完整的主题路径 |
| 102 | */ | 189 | */ |
| 103 | public static String buildPropertySetTopic(String productKey, String deviceName) { | 190 | public static String buildPropertySetTopic(String productKey, String deviceName) { |
| 104 | - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_SET_TOPIC.getTopic(); | 191 | + return buildDeviceTopicPrefix(productKey, deviceName) + PROPERTY_SET_TOPIC.getTopic(); |
| 105 | } | 192 | } |
| 106 | 193 | ||
| 107 | /** | 194 | /** |
| 108 | * 构建设备属性获取主题 | 195 | * 构建设备属性获取主题 |
| 109 | * | 196 | * |
| 110 | - * @param productKey 产品Key | 197 | + * @param productKey 产品 Key |
| 111 | * @param deviceName 设备名称 | 198 | * @param deviceName 设备名称 |
| 112 | * @return 完整的主题路径 | 199 | * @return 完整的主题路径 |
| 113 | */ | 200 | */ |
| 114 | public static String buildPropertyGetTopic(String productKey, String deviceName) { | 201 | public static String buildPropertyGetTopic(String productKey, String deviceName) { |
| 115 | - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_GET_TOPIC.getTopic(); | 202 | + return buildDeviceTopicPrefix(productKey, deviceName) + PROPERTY_GET_TOPIC.getTopic(); |
| 116 | } | 203 | } |
| 117 | 204 | ||
| 118 | /** | 205 | /** |
| 119 | * 构建设备配置设置主题 | 206 | * 构建设备配置设置主题 |
| 120 | * | 207 | * |
| 121 | - * @param productKey 产品Key | 208 | + * @param productKey 产品 Key |
| 122 | * @param deviceName 设备名称 | 209 | * @param deviceName 设备名称 |
| 123 | * @return 完整的主题路径 | 210 | * @return 完整的主题路径 |
| 124 | */ | 211 | */ |
| 125 | public static String buildConfigSetTopic(String productKey, String deviceName) { | 212 | public static String buildConfigSetTopic(String productKey, String deviceName) { |
| 126 | - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + CONFIG_SET_TOPIC.getTopic(); | 213 | + return buildDeviceTopicPrefix(productKey, deviceName) + CONFIG_SET_TOPIC.getTopic(); |
| 127 | } | 214 | } |
| 128 | 215 | ||
| 129 | /** | 216 | /** |
| 130 | * 构建设备 OTA 升级主题 | 217 | * 构建设备 OTA 升级主题 |
| 131 | * | 218 | * |
| 132 | - * @param productKey 产品Key | 219 | + * @param productKey 产品 Key |
| 133 | * @param deviceName 设备名称 | 220 | * @param deviceName 设备名称 |
| 134 | * @return 完整的主题路径 | 221 | * @return 完整的主题路径 |
| 135 | */ | 222 | */ |
| 136 | public static String buildOtaUpgradeTopic(String productKey, String deviceName) { | 223 | public static String buildOtaUpgradeTopic(String productKey, String deviceName) { |
| 137 | - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + OTA_UPGRADE_TOPIC.getTopic(); | 224 | + return buildDeviceTopicPrefix(productKey, deviceName) + OTA_UPGRADE_TOPIC.getTopic(); |
| 138 | } | 225 | } |
| 139 | 226 | ||
| 140 | /** | 227 | /** |
| 141 | * 构建设备属性上报主题 | 228 | * 构建设备属性上报主题 |
| 142 | * | 229 | * |
| 143 | - * @param productKey 产品Key | 230 | + * @param productKey 产品 Key |
| 144 | * @param deviceName 设备名称 | 231 | * @param deviceName 设备名称 |
| 145 | * @return 完整的主题路径 | 232 | * @return 完整的主题路径 |
| 146 | */ | 233 | */ |
| 147 | public static String buildPropertyPostTopic(String productKey, String deviceName) { | 234 | public static String buildPropertyPostTopic(String productKey, String deviceName) { |
| 148 | - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_POST_TOPIC.getTopic(); | 235 | + return buildDeviceTopicPrefix(productKey, deviceName) + PROPERTY_POST_TOPIC.getTopic(); |
| 149 | } | 236 | } |
| 150 | 237 | ||
| 151 | /** | 238 | /** |
| 152 | * 构建设备事件上报主题 | 239 | * 构建设备事件上报主题 |
| 153 | * | 240 | * |
| 154 | - * @param productKey 产品Key | 241 | + * @param productKey 产品 Key |
| 155 | * @param deviceName 设备名称 | 242 | * @param deviceName 设备名称 |
| 156 | * @param eventIdentifier 事件标识符 | 243 | * @param eventIdentifier 事件标识符 |
| 157 | * @return 完整的主题路径 | 244 | * @return 完整的主题路径 |
| 158 | */ | 245 | */ |
| 159 | public static String buildEventPostTopic(String productKey, String deviceName, String eventIdentifier) { | 246 | public static String buildEventPostTopic(String productKey, String deviceName, String eventIdentifier) { |
| 160 | - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + | 247 | + return buildDeviceTopicPrefix(productKey, deviceName) + |
| 161 | EVENT_POST_TOPIC_PREFIX.getTopic() + eventIdentifier + EVENT_POST_TOPIC_SUFFIX.getTopic(); | 248 | EVENT_POST_TOPIC_PREFIX.getTopic() + eventIdentifier + EVENT_POST_TOPIC_SUFFIX.getTopic(); |
| 162 | } | 249 | } |
| 163 | 250 | ||
| @@ -168,7 +255,7 @@ public enum IotDeviceTopicEnum { | @@ -168,7 +255,7 @@ public enum IotDeviceTopicEnum { | ||
| 168 | * @return 响应主题 | 255 | * @return 响应主题 |
| 169 | */ | 256 | */ |
| 170 | public static String getReplyTopic(String requestTopic) { | 257 | public static String getReplyTopic(String requestTopic) { |
| 171 | - return requestTopic + REPLY_SUFFIX.getTopic(); | 258 | + return requestTopic + REPLY_SUFFIX; |
| 172 | } | 259 | } |
| 173 | 260 | ||
| 174 | } | 261 | } |
| 175 | \ No newline at end of file | 262 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttDownstreamSubscriber.java
| @@ -105,33 +105,33 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDevi | @@ -105,33 +105,33 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDevi | ||
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | // 属性相关操作 | 107 | // 属性相关操作 |
| 108 | - if (method.startsWith("thing.service.property.")) { | ||
| 109 | - if ("thing.service.property.set".equals(method)) { | 108 | + if (method.startsWith(IotDeviceTopicEnum.PROPERTY_SERVICE_METHOD_PREFIX)) { |
| 109 | + if (IotDeviceTopicEnum.PROPERTY_SET_METHOD.equals(method)) { | ||
| 110 | return IotDeviceTopicEnum.buildPropertySetTopic(productKey, deviceName); | 110 | return IotDeviceTopicEnum.buildPropertySetTopic(productKey, deviceName); |
| 111 | - } else if ("thing.service.property.get".equals(method)) { | 111 | + } else if (IotDeviceTopicEnum.PROPERTY_GET_METHOD.equals(method)) { |
| 112 | return IotDeviceTopicEnum.buildPropertyGetTopic(productKey, deviceName); | 112 | return IotDeviceTopicEnum.buildPropertyGetTopic(productKey, deviceName); |
| 113 | } | 113 | } |
| 114 | return null; | 114 | return null; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | // 配置设置操作 | 117 | // 配置设置操作 |
| 118 | - if (method.startsWith("thing.service.config.")) { | 118 | + if (method.startsWith(IotDeviceTopicEnum.CONFIG_SERVICE_METHOD_PREFIX)) { |
| 119 | return IotDeviceTopicEnum.buildConfigSetTopic(productKey, deviceName); | 119 | return IotDeviceTopicEnum.buildConfigSetTopic(productKey, deviceName); |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | // OTA 升级操作 | 122 | // OTA 升级操作 |
| 123 | - if (method.startsWith("thing.service.ota.")) { | 123 | + if (method.startsWith(IotDeviceTopicEnum.OTA_SERVICE_METHOD_PREFIX)) { |
| 124 | return IotDeviceTopicEnum.buildOtaUpgradeTopic(productKey, deviceName); | 124 | return IotDeviceTopicEnum.buildOtaUpgradeTopic(productKey, deviceName); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | // 一般服务调用操作 | 127 | // 一般服务调用操作 |
| 128 | - if (method.startsWith("thing.service.")) { | ||
| 129 | - // 排除属性、配置、OTA相关的服务调用 | 128 | + if (method.startsWith(IotDeviceTopicEnum.SERVICE_METHOD_PREFIX)) { |
| 129 | + // 排除属性、配置、OTA 相关的服务调用 | ||
| 130 | if (method.contains("property") || method.contains("config") || method.contains("ota")) { | 130 | if (method.contains("property") || method.contains("config") || method.contains("ota")) { |
| 131 | return null; // 已在上面处理 | 131 | return null; // 已在上面处理 |
| 132 | } | 132 | } |
| 133 | // 从方法中提取服务标识符 | 133 | // 从方法中提取服务标识符 |
| 134 | - String serviceIdentifier = method.substring("thing.service.".length()); | 134 | + String serviceIdentifier = method.substring(IotDeviceTopicEnum.SERVICE_METHOD_PREFIX.length()); |
| 135 | return IotDeviceTopicEnum.buildServiceTopic(productKey, deviceName, serviceIdentifier); | 135 | return IotDeviceTopicEnum.buildServiceTopic(productKey, deviceName, serviceIdentifier); |
| 136 | } | 136 | } |
| 137 | 137 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.util.IdUtil; | ||
| 5 | import cn.hutool.core.util.ObjUtil; | 4 | import cn.hutool.core.util.ObjUtil; |
| 6 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 7 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 6 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 8 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 7 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; |
| 8 | +import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; | ||
| 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttHttpAuthHandler; | 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttHttpAuthHandler; |
| 10 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler; | 10 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler; |
| 11 | import io.netty.handler.codec.mqtt.MqttQoS; | 11 | import io.netty.handler.codec.mqtt.MqttQoS; |
| @@ -137,9 +137,9 @@ public class IotMqttUpstreamProtocol { | @@ -137,9 +137,9 @@ public class IotMqttUpstreamProtocol { | ||
| 137 | this.authHandler = new IotMqttHttpAuthHandler(); | 137 | this.authHandler = new IotMqttHttpAuthHandler(); |
| 138 | 138 | ||
| 139 | // 添加认证路由 | 139 | // 添加认证路由 |
| 140 | - router.post("/mqtt/auth/authenticate").handler(authHandler::authenticate); | ||
| 141 | - router.post("/mqtt/auth/connected").handler(authHandler::connected); | ||
| 142 | - router.post("/mqtt/auth/disconnected").handler(authHandler::disconnected); | 140 | + router.post(IotDeviceTopicEnum.MQTT_AUTH_AUTHENTICATE_PATH).handler(authHandler::authenticate); |
| 141 | + router.post(IotDeviceTopicEnum.MQTT_AUTH_CONNECTED_PATH).handler(authHandler::connected); | ||
| 142 | + router.post(IotDeviceTopicEnum.MQTT_AUTH_DISCONNECTED_PATH).handler(authHandler::disconnected); | ||
| 143 | 143 | ||
| 144 | // 启动 HTTP 服务器 | 144 | // 启动 HTTP 服务器 |
| 145 | int authPort = emqxProperties.getHttpAuthPort(); | 145 | int authPort = emqxProperties.getHttpAuthPort(); |
| @@ -176,11 +176,13 @@ public class IotMqttUpstreamProtocol { | @@ -176,11 +176,13 @@ public class IotMqttUpstreamProtocol { | ||
| 176 | log.info("[startMqttClient][开始启动 MQTT 客户端]"); | 176 | log.info("[startMqttClient][开始启动 MQTT 客户端]"); |
| 177 | 177 | ||
| 178 | // 初始化消息处理器 | 178 | // 初始化消息处理器 |
| 179 | - this.upstreamHandler = new IotMqttUpstreamHandler(); | 179 | + this.upstreamHandler = new IotMqttUpstreamHandler(this); |
| 180 | 180 | ||
| 181 | // 创建 MQTT 客户端 | 181 | // 创建 MQTT 客户端 |
| 182 | + log.info("[startMqttClient][使用 MQTT 客户端 ID: {}]", emqxProperties.getMqttClientId()); | ||
| 183 | + | ||
| 182 | MqttClientOptions options = new MqttClientOptions() | 184 | MqttClientOptions options = new MqttClientOptions() |
| 183 | - .setClientId("yudao-iot-gateway-" + IdUtil.fastSimpleUUID()) | 185 | + .setClientId(emqxProperties.getMqttClientId()) |
| 184 | .setUsername(emqxProperties.getMqttUsername()) | 186 | .setUsername(emqxProperties.getMqttUsername()) |
| 185 | .setPassword(emqxProperties.getMqttPassword()) | 187 | .setPassword(emqxProperties.getMqttPassword()) |
| 186 | .setSsl(ObjUtil.defaultIfNull(emqxProperties.getMqttSsl(), false)); | 188 | .setSsl(ObjUtil.defaultIfNull(emqxProperties.getMqttSsl(), false)); |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java
| @@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil; | @@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil; | ||
| 4 | import cn.hutool.extra.spring.SpringUtil; | 4 | import cn.hutool.extra.spring.SpringUtil; |
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; | 6 | import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; |
| 7 | +import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; | ||
| 7 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 8 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| 8 | import io.vertx.mqtt.messages.MqttPublishMessage; | 9 | import io.vertx.mqtt.messages.MqttPublishMessage; |
| 9 | import lombok.extern.slf4j.Slf4j; | 10 | import lombok.extern.slf4j.Slf4j; |
| @@ -22,10 +23,12 @@ public class IotMqttUpstreamHandler { | @@ -22,10 +23,12 @@ public class IotMqttUpstreamHandler { | ||
| 22 | 23 | ||
| 23 | private final IotDeviceMessageProducer deviceMessageProducer; | 24 | private final IotDeviceMessageProducer deviceMessageProducer; |
| 24 | private final IotDeviceMessageService deviceMessageService; | 25 | private final IotDeviceMessageService deviceMessageService; |
| 26 | + private final String serverId; | ||
| 25 | 27 | ||
| 26 | - public IotMqttUpstreamHandler() { | 28 | + public IotMqttUpstreamHandler(cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol protocol) { |
| 27 | this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class); | 29 | this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class); |
| 28 | this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); | 30 | this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); |
| 31 | + this.serverId = protocol.getServerId(); | ||
| 29 | } | 32 | } |
| 30 | 33 | ||
| 31 | /** | 34 | /** |
| @@ -92,8 +95,8 @@ public class IotMqttUpstreamHandler { | @@ -92,8 +95,8 @@ public class IotMqttUpstreamHandler { | ||
| 92 | IotDeviceMessage message = deviceMessageService.decodeDeviceMessage( | 95 | IotDeviceMessage message = deviceMessageService.decodeDeviceMessage( |
| 93 | messageBytes, productKey, deviceName); | 96 | messageBytes, productKey, deviceName); |
| 94 | 97 | ||
| 95 | - // 发送消息到队列 | ||
| 96 | - deviceMessageProducer.sendDeviceMessage(message); | 98 | + // 发送消息到队列(需要补充设备信息) |
| 99 | + deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId); | ||
| 97 | 100 | ||
| 98 | // 记录成功日志 | 101 | // 记录成功日志 |
| 99 | log.info("[processMessage][处理{}成功][topic: {}]", messageType, topic); | 102 | log.info("[processMessage][处理{}成功][topic: {}]", messageType, topic); |
| @@ -106,22 +109,46 @@ public class IotMqttUpstreamHandler { | @@ -106,22 +109,46 @@ public class IotMqttUpstreamHandler { | ||
| 106 | * @return 消息类型描述,如果不支持返回 null | 109 | * @return 消息类型描述,如果不支持返回 null |
| 107 | */ | 110 | */ |
| 108 | private String getMessageType(String topic) { | 111 | private String getMessageType(String topic) { |
| 109 | - // 设备事件上报: /sys/{productKey}/{deviceName}/thing/event/{eventIdentifier}/post | ||
| 110 | - if (topic.contains("/thing/event/") && topic.endsWith("/post")) { | 112 | + if (StrUtil.isBlank(topic)) { |
| 113 | + return null; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + // 按优先级匹配主题类型,避免误匹配 | ||
| 117 | + | ||
| 118 | + // 1. 设备属性上报: /sys/{productKey}/{deviceName}/thing/event/property/post | ||
| 119 | + if (isPropertyPostTopic(topic)) { | ||
| 120 | + return IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getDescription(); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + // 2. 设备事件上报: /sys/{productKey}/{deviceName}/thing/event/{eventIdentifier}/post | ||
| 124 | + if (isEventPostTopic(topic)) { | ||
| 111 | return "设备事件上报"; | 125 | return "设备事件上报"; |
| 112 | } | 126 | } |
| 113 | 127 | ||
| 114 | - // 设备属性操作: /sys/{productKey}/{deviceName}/thing/property/post | ||
| 115 | - // 或属性响应: /sys/{productKey}/{deviceName}/thing/service/property/set_reply | ||
| 116 | - if (topic.endsWith("/thing/property/post") || | ||
| 117 | - topic.contains("/thing/service/property/set") || | ||
| 118 | - topic.contains("/thing/service/property/get")) { | ||
| 119 | - return "设备属性操作"; | 128 | + // 3. 设备属性设置响应: /sys/{productKey}/{deviceName}/thing/service/property/set_reply |
| 129 | + if (isPropertySetReplyTopic(topic)) { | ||
| 130 | + return "设备属性设置响应"; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + // 4. 设备属性获取响应: /sys/{productKey}/{deviceName}/thing/service/property/get_reply | ||
| 134 | + if (isPropertyGetReplyTopic(topic)) { | ||
| 135 | + return "设备属性获取响应"; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + // 5. 设备配置设置响应: /sys/{productKey}/{deviceName}/thing/service/config/set_reply | ||
| 139 | + if (isConfigSetReplyTopic(topic)) { | ||
| 140 | + return IotDeviceTopicEnum.CONFIG_SET_TOPIC.getDescription() + "响应"; | ||
| 120 | } | 141 | } |
| 121 | 142 | ||
| 122 | - // 设备服务调用: /sys/{productKey}/{deviceName}/thing/service/{serviceIdentifier} | ||
| 123 | - if (topic.contains("/thing/service/") && !topic.contains("/property/")) { | ||
| 124 | - return "设备服务调用"; | 143 | + // 6. 设备 OTA 升级响应: |
| 144 | + // /sys/{productKey}/{deviceName}/thing/service/ota/upgrade_reply | ||
| 145 | + if (isOtaUpgradeReplyTopic(topic)) { | ||
| 146 | + return IotDeviceTopicEnum.OTA_UPGRADE_TOPIC.getDescription() + "响应"; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + // 7. 其他服务调用响应: 通用服务调用响应 | ||
| 150 | + if (isServiceReplyTopic(topic)) { | ||
| 151 | + return "设备服务调用响应"; | ||
| 125 | } | 152 | } |
| 126 | 153 | ||
| 127 | // 不支持的消息类型 | 154 | // 不支持的消息类型 |
| @@ -129,6 +156,65 @@ public class IotMqttUpstreamHandler { | @@ -129,6 +156,65 @@ public class IotMqttUpstreamHandler { | ||
| 129 | } | 156 | } |
| 130 | 157 | ||
| 131 | /** | 158 | /** |
| 159 | + * 判断是否为属性上报主题 | ||
| 160 | + */ | ||
| 161 | + private boolean isPropertyPostTopic(String topic) { | ||
| 162 | + return topic.contains(IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic()); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + /** | ||
| 166 | + * 判断是否为事件上报主题 | ||
| 167 | + */ | ||
| 168 | + private boolean isEventPostTopic(String topic) { | ||
| 169 | + return topic.contains(IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic()) | ||
| 170 | + && topic.endsWith(IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic()) | ||
| 171 | + && !topic.contains("property"); // 排除属性上报主题 | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + /** | ||
| 175 | + * 判断是否为属性设置响应主题 | ||
| 176 | + */ | ||
| 177 | + private boolean isPropertySetReplyTopic(String topic) { | ||
| 178 | + return topic.contains(IotDeviceTopicEnum.PROPERTY_SET_TOPIC.getTopic()) | ||
| 179 | + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX); | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + /** | ||
| 183 | + * 判断是否为属性获取响应主题 | ||
| 184 | + */ | ||
| 185 | + private boolean isPropertyGetReplyTopic(String topic) { | ||
| 186 | + return topic.contains(IotDeviceTopicEnum.PROPERTY_GET_TOPIC.getTopic()) | ||
| 187 | + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX); | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + /** | ||
| 191 | + * 判断是否为配置设置响应主题 | ||
| 192 | + */ | ||
| 193 | + private boolean isConfigSetReplyTopic(String topic) { | ||
| 194 | + return topic.contains(IotDeviceTopicEnum.CONFIG_SET_TOPIC.getTopic()) | ||
| 195 | + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX); | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + /** | ||
| 199 | + * 判断是否为 OTA 升级响应主题 | ||
| 200 | + */ | ||
| 201 | + private boolean isOtaUpgradeReplyTopic(String topic) { | ||
| 202 | + return topic.contains(IotDeviceTopicEnum.OTA_UPGRADE_TOPIC.getTopic()) | ||
| 203 | + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + /** | ||
| 207 | + * 判断是否为服务调用响应主题(排除已处理的特殊服务) | ||
| 208 | + */ | ||
| 209 | + private boolean isServiceReplyTopic(String topic) { | ||
| 210 | + return topic.contains(IotDeviceTopicEnum.SERVICE_TOPIC_PREFIX) | ||
| 211 | + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX) | ||
| 212 | + && !topic.contains("property") | ||
| 213 | + && !topic.contains("config") | ||
| 214 | + && !topic.contains("ota"); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + /** | ||
| 132 | * 解析主题,获取主题各部分 | 218 | * 解析主题,获取主题各部分 |
| 133 | * | 219 | * |
| 134 | * @param topic 主题 | 220 | * @param topic 主题 |
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application-local.yaml
| @@ -39,9 +39,7 @@ yudao: | @@ -39,9 +39,7 @@ yudao: | ||
| 39 | mqtt-password: public # MQTT 密码 | 39 | mqtt-password: public # MQTT 密码 |
| 40 | mqtt-ssl: false # 是否开启 SSL | 40 | mqtt-ssl: false # 是否开启 SSL |
| 41 | mqtt-topics: | 41 | mqtt-topics: |
| 42 | - - "/sys/#" # 系统主题(设备上报) | ||
| 43 | - - "/ota/#" # OTA 升级主题 | ||
| 44 | - - "/config/#" # 配置主题 | 42 | + - "/sys/#" # 系统主题 |
| 45 | 43 | ||
| 46 | # 消息总线配置 | 44 | # 消息总线配置 |
| 47 | message-bus: | 45 | message-bus: |
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
| @@ -40,9 +40,7 @@ yudao: | @@ -40,9 +40,7 @@ yudao: | ||
| 40 | enabled: true | 40 | enabled: true |
| 41 | mqtt-ssl: false | 41 | mqtt-ssl: false |
| 42 | mqtt-topics: | 42 | mqtt-topics: |
| 43 | - - "/sys/#" # 系统主题(设备上报) | ||
| 44 | - - "/ota/#" # OTA 升级主题 | ||
| 45 | - - "/config/#" # 配置主题 | 43 | + - "/sys/#" # 系统主题 |
| 46 | 44 | ||
| 47 | # 消息总线配置 | 45 | # 消息总线配置 |
| 48 | message-bus: | 46 | message-bus: |