Commit ae96ff4a25beb188f0b4ca527793721308b86e60
1 parent
72d8511d
【功能修改】IoT: 修改网络组件模块,包含 HTTP 和 EMQX 组件,重构相关配置和处理逻辑,更新文档说明。
Showing
51 changed files
with
1526 additions
and
1042 deletions
yudao-module-iot/pom.xml
| ... | ... | @@ -10,7 +10,7 @@ |
| 10 | 10 | <modules> |
| 11 | 11 | <module>yudao-module-iot-api</module> |
| 12 | 12 | <module>yudao-module-iot-biz</module> |
| 13 | - <module>yudao-module-iot-components</module> | |
| 13 | + <module>yudao-module-iot-net-components</module> | |
| 14 | 14 | <!-- <module>yudao-module-iot-plugins</module>--> |
| 15 | 15 | </modules> |
| 16 | 16 | <modelVersion>4.0.0</modelVersion> | ... | ... |
yudao-module-iot/yudao-module-iot-biz/pom.xml
| ... | ... | @@ -93,10 +93,10 @@ |
| 93 | 93 | <optional>true</optional> |
| 94 | 94 | </dependency> |
| 95 | 95 | |
| 96 | - <dependency> | |
| 97 | - <groupId>org.pf4j</groupId> <!-- PF4J:内置插件机制 --> | |
| 98 | - <artifactId>pf4j-spring</artifactId> | |
| 99 | - </dependency> | |
| 96 | + <!-- <dependency>--> | |
| 97 | + <!-- <groupId>org.pf4j</groupId> <!– PF4J:内置插件机制 –>--> | |
| 98 | + <!-- <artifactId>pf4j-spring</artifactId>--> | |
| 99 | + <!-- </dependency>--> | |
| 100 | 100 | |
| 101 | 101 | <!-- TODO @芋艿:bom 管理 --> |
| 102 | 102 | <dependency> |
| ... | ... | @@ -137,12 +137,12 @@ |
| 137 | 137 | <!-- IoT 网络组件:接收来自设备的上行数据 --> |
| 138 | 138 | <dependency> |
| 139 | 139 | <groupId>cn.iocoder.boot</groupId> |
| 140 | - <artifactId>yudao-module-iot-component-http</artifactId> | |
| 140 | + <artifactId>yudao-module-iot-net-component-http</artifactId> | |
| 141 | 141 | <version>${revision}</version> |
| 142 | 142 | </dependency> |
| 143 | 143 | <dependency> |
| 144 | 144 | <groupId>cn.iocoder.boot</groupId> |
| 145 | - <artifactId>yudao-module-iot-component-emqx</artifactId> | |
| 145 | + <artifactId>yudao-module-iot-net-component-emqx</artifactId> | |
| 146 | 146 | <version>${revision}</version> |
| 147 | 147 | </dependency> |
| 148 | 148 | </dependencies> | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java
| ... | ... | @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 16 | 16 | */ |
| 17 | 17 | @RestController |
| 18 | 18 | @Validated |
| 19 | -@Primary // 保证优先匹配,因为 yudao-module-iot-component-core 也有 IotDeviceUpstreamApi 的实现,并且也可能会被 biz 引入 | |
| 19 | +@Primary // 保证优先匹配,因为 yudao-module-iot-net-component-core 也有 IotDeviceUpstreamApi 的实现,并且也可能会被 biz 引入 | |
| 20 | 20 | public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { |
| 21 | 21 | |
| 22 | 22 | @Resource | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.framework.plugin.config; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStartRunner; | |
| 4 | -import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStateListener; | |
| 5 | -import cn.iocoder.yudao.module.iot.service.plugin.IotPluginConfigService; | |
| 6 | -import lombok.extern.slf4j.Slf4j; | |
| 7 | -import org.pf4j.spring.SpringPluginManager; | |
| 8 | -import org.springframework.beans.factory.annotation.Value; | |
| 9 | -import org.springframework.context.annotation.Bean; | |
| 10 | -import org.springframework.context.annotation.Configuration; | |
| 11 | - | |
| 12 | -import java.nio.file.Paths; | |
| 13 | - | |
| 14 | -/** | |
| 15 | - * IoT 插件配置类 | |
| 16 | - * | |
| 17 | - * @author haohao | |
| 18 | - */ | |
| 19 | -@Configuration | |
| 20 | -@Slf4j | |
| 21 | -public class IotPluginConfiguration { | |
| 22 | - | |
| 23 | - @Bean | |
| 24 | - public IotPluginStartRunner pluginStartRunner(SpringPluginManager pluginManager, | |
| 25 | - IotPluginConfigService pluginConfigService) { | |
| 26 | - return new IotPluginStartRunner(pluginManager, pluginConfigService); | |
| 27 | - } | |
| 28 | - | |
| 29 | - // TODO @芋艿:需要 review 下 | |
| 30 | - @Bean | |
| 31 | - public SpringPluginManager pluginManager(@Value("${pf4j.pluginsDir:pluginsDir}") String pluginsDir) { | |
| 32 | - log.info("[init][实例化 SpringPluginManager]"); | |
| 33 | - SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { | |
| 34 | - | |
| 35 | - @Override | |
| 36 | - public void startPlugins() { | |
| 37 | - // 禁用插件启动,避免插件启动时,启动所有插件 | |
| 38 | - log.info("[init][禁用默认启动所有插件]"); | |
| 39 | - } | |
| 40 | - | |
| 41 | - }; | |
| 42 | - springPluginManager.addPluginStateListener(new IotPluginStateListener()); | |
| 43 | - return springPluginManager; | |
| 44 | - } | |
| 45 | - | |
| 46 | -} | |
| 47 | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.framework.plugin.core; | |
| 2 | - | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | |
| 5 | -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; | |
| 6 | -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; | |
| 7 | -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; | |
| 8 | -import cn.iocoder.yudao.module.iot.service.plugin.IotPluginConfigService; | |
| 9 | -import lombok.RequiredArgsConstructor; | |
| 10 | -import lombok.extern.slf4j.Slf4j; | |
| 11 | -import org.pf4j.spring.SpringPluginManager; | |
| 12 | -import org.springframework.boot.ApplicationArguments; | |
| 13 | -import org.springframework.boot.ApplicationRunner; | |
| 14 | - | |
| 15 | -import java.util.List; | |
| 16 | - | |
| 17 | -/** | |
| 18 | - * IoT 插件启动 Runner | |
| 19 | - * | |
| 20 | - * 用于 Spring Boot 启动时,启动 {@link IotPluginDeployTypeEnum#JAR} 部署类型的插件 | |
| 21 | - */ | |
| 22 | -@RequiredArgsConstructor | |
| 23 | -@Slf4j | |
| 24 | -public class IotPluginStartRunner implements ApplicationRunner { | |
| 25 | - | |
| 26 | - private final SpringPluginManager springPluginManager; | |
| 27 | - | |
| 28 | - private final IotPluginConfigService pluginConfigService; | |
| 29 | - | |
| 30 | - @Override | |
| 31 | - public void run(ApplicationArguments args) { | |
| 32 | - List<IotPluginConfigDO> pluginConfigList = TenantUtils.executeIgnore( | |
| 33 | - () -> pluginConfigService.getPluginConfigListByStatusAndDeployType( | |
| 34 | - IotPluginStatusEnum.RUNNING.getStatus(), IotPluginDeployTypeEnum.JAR.getDeployType())); | |
| 35 | - if (CollUtil.isEmpty(pluginConfigList)) { | |
| 36 | - log.info("[run][没有需要启动的插件]"); | |
| 37 | - return; | |
| 38 | - } | |
| 39 | - | |
| 40 | - // 遍历插件列表,逐个启动 | |
| 41 | - pluginConfigList.forEach(pluginConfig -> { | |
| 42 | - try { | |
| 43 | - log.info("[run][插件({}) 启动开始]", pluginConfig.getPluginKey()); | |
| 44 | - springPluginManager.startPlugin(pluginConfig.getPluginKey()); | |
| 45 | - log.info("[run][插件({}) 启动完成]", pluginConfig.getPluginKey()); | |
| 46 | - } catch (Exception e) { | |
| 47 | - log.error("[run][插件({}) 启动异常]", pluginConfig.getPluginKey(), e); | |
| 48 | - } | |
| 49 | - }); | |
| 50 | - } | |
| 51 | - | |
| 52 | -} | |
| 53 | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.framework.plugin.core; | |
| 2 | - | |
| 3 | -import lombok.extern.slf4j.Slf4j; | |
| 4 | -import org.pf4j.PluginStateEvent; | |
| 5 | -import org.pf4j.PluginStateListener; | |
| 6 | - | |
| 7 | -/** | |
| 8 | - * IoT 插件状态监听器,用于 log 插件的状态变化 | |
| 9 | - * | |
| 10 | - * @author haohao | |
| 11 | - */ | |
| 12 | -@Slf4j | |
| 13 | -public class IotPluginStateListener implements PluginStateListener { | |
| 14 | - | |
| 15 | - @Override | |
| 16 | - public void pluginStateChanged(PluginStateEvent event) { | |
| 17 | - log.info("[pluginStateChanged][插件({}) 状态变化,从 {} 变为 {}]", event.getPlugin().getPluginId(), | |
| 18 | - event.getOldState().toString(), event.getPluginState().toString()); | |
| 19 | - } | |
| 20 | - | |
| 21 | -} | |
| 22 | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigServiceImpl.java
| ... | ... | @@ -9,8 +9,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.plugin.IotPluginConfigMapper; |
| 9 | 9 | import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; |
| 10 | 10 | import jakarta.annotation.Resource; |
| 11 | 11 | import lombok.extern.slf4j.Slf4j; |
| 12 | -import org.pf4j.PluginWrapper; | |
| 13 | -import org.pf4j.spring.SpringPluginManager; | |
| 14 | 12 | import org.springframework.stereotype.Service; |
| 15 | 13 | import org.springframework.validation.annotation.Validated; |
| 16 | 14 | import org.springframework.web.multipart.MultipartFile; |
| ... | ... | @@ -35,8 +33,8 @@ public class IotPluginConfigServiceImpl implements IotPluginConfigService { |
| 35 | 33 | @Resource |
| 36 | 34 | private IotPluginInstanceService pluginInstanceService; |
| 37 | 35 | |
| 38 | - @Resource | |
| 39 | - private SpringPluginManager springPluginManager; | |
| 36 | +// @Resource | |
| 37 | +// private SpringPluginManager springPluginManager; | |
| 40 | 38 | |
| 41 | 39 | @Override |
| 42 | 40 | public Long createPluginConfig(PluginConfigSaveReqVO createReqVO) { |
| ... | ... | @@ -130,16 +128,16 @@ public class IotPluginConfigServiceImpl implements IotPluginConfigService { |
| 130 | 128 | validatePluginConfigFile(pluginKeyNew); |
| 131 | 129 | |
| 132 | 130 | // 4. 更新插件配置 |
| 133 | - IotPluginConfigDO updatedPluginConfig = new IotPluginConfigDO() | |
| 134 | - .setId(pluginConfigDO.getId()) | |
| 135 | - .setPluginKey(pluginKeyNew) | |
| 136 | - .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) // TODO @haohao:这个状态,是不是非 stop 哈? | |
| 137 | - .setFileName(file.getOriginalFilename()) | |
| 138 | - .setScript("") // TODO @haohao:这个设置为 "" 会不会覆盖数据里的哈?应该从插件里读取?未来? | |
| 139 | - .setConfigSchema(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()) | |
| 140 | - .setVersion(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion()) | |
| 141 | - .setDescription(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()); | |
| 142 | - pluginConfigMapper.updateById(updatedPluginConfig); | |
| 131 | +// IotPluginConfigDO updatedPluginConfig = new IotPluginConfigDO() | |
| 132 | +// .setId(pluginConfigDO.getId()) | |
| 133 | +// .setPluginKey(pluginKeyNew) | |
| 134 | +// .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) // TODO @haohao:这个状态,是不是非 stop 哈? | |
| 135 | +// .setFileName(file.getOriginalFilename()) | |
| 136 | +// .setScript("") // TODO @haohao:这个设置为 "" 会不会覆盖数据里的哈?应该从插件里读取?未来? | |
| 137 | +// .setConfigSchema(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()) | |
| 138 | +// .setVersion(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion()) | |
| 139 | +// .setDescription(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()); | |
| 140 | +// pluginConfigMapper.updateById(updatedPluginConfig); | |
| 143 | 141 | } |
| 144 | 142 | |
| 145 | 143 | /** |
| ... | ... | @@ -149,13 +147,13 @@ public class IotPluginConfigServiceImpl implements IotPluginConfigService { |
| 149 | 147 | */ |
| 150 | 148 | private void validatePluginConfigFile(String pluginKeyNew) { |
| 151 | 149 | // TODO @haohao:校验 file 相关参数,是否完整,类似:version 之类是不是可以解析到 |
| 152 | - PluginWrapper plugin = springPluginManager.getPlugin(pluginKeyNew); | |
| 153 | - if (plugin == null) { | |
| 154 | - throw exception(PLUGIN_INSTALL_FAILED); | |
| 155 | - } | |
| 156 | - if (plugin.getDescriptor().getVersion() == null) { | |
| 157 | - throw exception(PLUGIN_INSTALL_FAILED); | |
| 158 | - } | |
| 150 | +// PluginWrapper plugin = springPluginManager.getPlugin(pluginKeyNew); | |
| 151 | +// if (plugin == null) { | |
| 152 | +// throw exception(PLUGIN_INSTALL_FAILED); | |
| 153 | +// } | |
| 154 | +// if (plugin.getDescriptor().getVersion() == null) { | |
| 155 | +// throw exception(PLUGIN_INSTALL_FAILED); | |
| 156 | +// } | |
| 159 | 157 | } |
| 160 | 158 | |
| 161 | 159 | @Override | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.service.plugin; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.io.FileUtil; | |
| 5 | 4 | import cn.hutool.core.util.StrUtil; |
| 6 | 5 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 7 | 6 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; |
| ... | ... | @@ -9,13 +8,8 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; |
| 9 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; |
| 10 | 9 | import cn.iocoder.yudao.module.iot.dal.mysql.plugin.IotPluginInstanceMapper; |
| 11 | 10 | import cn.iocoder.yudao.module.iot.dal.redis.plugin.DevicePluginProcessIdRedisDAO; |
| 12 | -import cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants; | |
| 13 | -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; | |
| 14 | 11 | import jakarta.annotation.Resource; |
| 15 | 12 | import lombok.extern.slf4j.Slf4j; |
| 16 | -import org.pf4j.PluginState; | |
| 17 | -import org.pf4j.PluginWrapper; | |
| 18 | -import org.pf4j.spring.SpringPluginManager; | |
| 19 | 13 | import org.springframework.beans.factory.annotation.Value; |
| 20 | 14 | import org.springframework.context.annotation.Lazy; |
| 21 | 15 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -23,17 +17,10 @@ import org.springframework.validation.annotation.Validated; |
| 23 | 17 | import org.springframework.web.multipart.MultipartFile; |
| 24 | 18 | |
| 25 | 19 | import java.io.File; |
| 26 | -import java.io.IOException; | |
| 27 | -import java.nio.file.Files; | |
| 28 | -import java.nio.file.Path; | |
| 29 | -import java.nio.file.Paths; | |
| 30 | -import java.nio.file.StandardCopyOption; | |
| 31 | 20 | import java.time.LocalDateTime; |
| 32 | 21 | import java.util.List; |
| 33 | 22 | import java.util.concurrent.TimeUnit; |
| 34 | 23 | |
| 35 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 36 | - | |
| 37 | 24 | /** |
| 38 | 25 | * IoT 插件实例 Service 实现类 |
| 39 | 26 | * |
| ... | ... | @@ -54,8 +41,8 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { |
| 54 | 41 | @Resource |
| 55 | 42 | private DevicePluginProcessIdRedisDAO devicePluginProcessIdRedisDAO; |
| 56 | 43 | |
| 57 | - @Resource | |
| 58 | - private SpringPluginManager pluginManager; | |
| 44 | +// @Resource | |
| 45 | +// private SpringPluginManager pluginManager; | |
| 59 | 46 | |
| 60 | 47 | @Value("${pf4j.pluginsDir}") |
| 61 | 48 | private String pluginsDir; |
| ... | ... | @@ -120,17 +107,17 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { |
| 120 | 107 | |
| 121 | 108 | @Override |
| 122 | 109 | public void stopAndUnloadPlugin(String pluginKey) { |
| 123 | - PluginWrapper plugin = pluginManager.getPlugin(pluginKey); | |
| 124 | - if (plugin == null) { | |
| 125 | - log.warn("插件不存在或已卸载: {}", pluginKey); | |
| 126 | - return; | |
| 127 | - } | |
| 128 | - if (plugin.getPluginState().equals(PluginState.STARTED)) { | |
| 129 | - pluginManager.stopPlugin(pluginKey); // 停止插件 | |
| 130 | - log.info("已停止插件: {}", pluginKey); | |
| 131 | - } | |
| 132 | - pluginManager.unloadPlugin(pluginKey); // 卸载插件 | |
| 133 | - log.info("已卸载插件: {}", pluginKey); | |
| 110 | +// PluginWrapper plugin = pluginManager.getPlugin(pluginKey); | |
| 111 | +// if (plugin == null) { | |
| 112 | +// log.warn("插件不存在或已卸载: {}", pluginKey); | |
| 113 | +// return; | |
| 114 | +// } | |
| 115 | +// if (plugin.getPluginState().equals(PluginState.STARTED)) { | |
| 116 | +// pluginManager.stopPlugin(pluginKey); // 停止插件 | |
| 117 | +// log.info("已停止插件: {}", pluginKey); | |
| 118 | +// } | |
| 119 | +// pluginManager.unloadPlugin(pluginKey); // 卸载插件 | |
| 120 | +// log.info("已卸载插件: {}", pluginKey); | |
| 134 | 121 | } |
| 135 | 122 | |
| 136 | 123 | @Override |
| ... | ... | @@ -151,65 +138,66 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { |
| 151 | 138 | |
| 152 | 139 | @Override |
| 153 | 140 | public String uploadAndLoadNewPlugin(MultipartFile file) { |
| 154 | - String pluginKeyNew; | |
| 155 | - // TODO @haohao:多节点,是不是要上传 s3 之类的存储器;然后定时去加载 | |
| 156 | - Path pluginsPath = Paths.get(pluginsDir); | |
| 157 | - try { | |
| 158 | - FileUtil.mkdir(pluginsPath.toFile()); // 创建插件目录 | |
| 159 | - String filename = file.getOriginalFilename(); | |
| 160 | - if (filename != null) { | |
| 161 | - Path jarPath = pluginsPath.resolve(filename); | |
| 162 | - Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 | |
| 163 | - pluginKeyNew = pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 | |
| 164 | - log.info("已加载插件: {}", pluginKeyNew); | |
| 165 | - } else { | |
| 166 | - throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED); | |
| 167 | - } | |
| 168 | - } catch (IOException e) { | |
| 169 | - log.error("[uploadAndLoadNewPlugin][上传插件文件失败]", e); | |
| 170 | - throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); | |
| 171 | - } catch (Exception e) { | |
| 172 | - log.error("[uploadAndLoadNewPlugin][加载插件失败]", e); | |
| 173 | - throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); | |
| 174 | - } | |
| 175 | - return pluginKeyNew; | |
| 141 | +// String pluginKeyNew; | |
| 142 | +// // TODO @haohao:多节点,是不是要上传 s3 之类的存储器;然后定时去加载 | |
| 143 | +// Path pluginsPath = Paths.get(pluginsDir); | |
| 144 | +// try { | |
| 145 | +// FileUtil.mkdir(pluginsPath.toFile()); // 创建插件目录 | |
| 146 | +// String filename = file.getOriginalFilename(); | |
| 147 | +// if (filename != null) { | |
| 148 | +// Path jarPath = pluginsPath.resolve(filename); | |
| 149 | +// Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 | |
| 150 | +//// pluginKeyNew = pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 | |
| 151 | +//// log.info("已加载插件: {}", pluginKeyNew); | |
| 152 | +// } else { | |
| 153 | +// throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED); | |
| 154 | +// } | |
| 155 | +// } catch (IOException e) { | |
| 156 | +// log.error("[uploadAndLoadNewPlugin][上传插件文件失败]", e); | |
| 157 | +// throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); | |
| 158 | +// } catch (Exception e) { | |
| 159 | +// log.error("[uploadAndLoadNewPlugin][加载插件失败]", e); | |
| 160 | +// throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); | |
| 161 | +// } | |
| 162 | +// return pluginKeyNew; | |
| 163 | + return null; | |
| 176 | 164 | } |
| 177 | 165 | |
| 178 | 166 | @Override |
| 179 | 167 | public void updatePluginStatus(IotPluginConfigDO pluginConfigDO, Integer status) { |
| 180 | - String pluginKey = pluginConfigDO.getPluginKey(); | |
| 181 | - PluginWrapper plugin = pluginManager.getPlugin(pluginKey); | |
| 182 | - | |
| 183 | - if (plugin == null) { | |
| 184 | - // 插件不存在且状态为停止,抛出异常 | |
| 185 | - if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginConfigDO.getStatus())) { | |
| 186 | - throw exception(ErrorCodeConstants.PLUGIN_STATUS_INVALID); | |
| 187 | - } | |
| 188 | - return; | |
| 189 | - } | |
| 190 | - | |
| 191 | - // 启动插件 | |
| 192 | - if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) | |
| 193 | - && plugin.getPluginState() != PluginState.STARTED) { | |
| 194 | - try { | |
| 195 | - pluginManager.startPlugin(pluginKey); | |
| 196 | - } catch (Exception e) { | |
| 197 | - log.error("[updatePluginStatus][启动插件({}) 失败]", pluginKey, e); | |
| 198 | - throw exception(ErrorCodeConstants.PLUGIN_START_FAILED, e); | |
| 199 | - } | |
| 200 | - log.info("已启动插件: {}", pluginKey); | |
| 201 | - } | |
| 202 | - // 停止插件 | |
| 203 | - else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) | |
| 204 | - && plugin.getPluginState() == PluginState.STARTED) { | |
| 205 | - try { | |
| 206 | - pluginManager.stopPlugin(pluginKey); | |
| 207 | - } catch (Exception e) { | |
| 208 | - log.error("[updatePluginStatus][停止插件({}) 失败]", pluginKey, e); | |
| 209 | - throw exception(ErrorCodeConstants.PLUGIN_STOP_FAILED, e); | |
| 210 | - } | |
| 211 | - log.info("已停止插件: {}", pluginKey); | |
| 212 | - } | |
| 168 | +// String pluginKey = pluginConfigDO.getPluginKey(); | |
| 169 | +// PluginWrapper plugin = pluginManager.getPlugin(pluginKey); | |
| 170 | +// | |
| 171 | +// if (plugin == null) { | |
| 172 | +// // 插件不存在且状态为停止,抛出异常 | |
| 173 | +// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginConfigDO.getStatus())) { | |
| 174 | +// throw exception(ErrorCodeConstants.PLUGIN_STATUS_INVALID); | |
| 175 | +// } | |
| 176 | +// return; | |
| 177 | +// } | |
| 178 | +// | |
| 179 | +// // 启动插件 | |
| 180 | +// if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) | |
| 181 | +// && plugin.getPluginState() != PluginState.STARTED) { | |
| 182 | +// try { | |
| 183 | +// pluginManager.startPlugin(pluginKey); | |
| 184 | +// } catch (Exception e) { | |
| 185 | +// log.error("[updatePluginStatus][启动插件({}) 失败]", pluginKey, e); | |
| 186 | +// throw exception(ErrorCodeConstants.PLUGIN_START_FAILED, e); | |
| 187 | +// } | |
| 188 | +// log.info("已启动插件: {}", pluginKey); | |
| 189 | +// } | |
| 190 | +// // 停止插件 | |
| 191 | +// else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) | |
| 192 | +// && plugin.getPluginState() == PluginState.STARTED) { | |
| 193 | +// try { | |
| 194 | +// pluginManager.stopPlugin(pluginKey); | |
| 195 | +// } catch (Exception e) { | |
| 196 | +// log.error("[updatePluginStatus][停止插件({}) 失败]", pluginKey, e); | |
| 197 | +// throw exception(ErrorCodeConstants.PLUGIN_STOP_FAILED, e); | |
| 198 | +// } | |
| 199 | +// log.info("已停止插件: {}", pluginKey); | |
| 200 | +// } | |
| 213 | 201 | } |
| 214 | 202 | |
| 215 | 203 | // ========== 设备与插件的映射操作 ========== | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/config/IotComponentCommonProperties.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.component.core.config; | |
| 2 | - | |
| 3 | -import lombok.Data; | |
| 4 | -import org.springframework.boot.context.properties.ConfigurationProperties; | |
| 5 | -import org.springframework.validation.annotation.Validated; | |
| 6 | - | |
| 7 | -/** | |
| 8 | - * IoT 组件通用配置属性 | |
| 9 | - * | |
| 10 | - * @author haohao | |
| 11 | - */ | |
| 12 | -@ConfigurationProperties(prefix = "yudao.iot.component.core") | |
| 13 | -@Validated | |
| 14 | -@Data | |
| 15 | -public class IotComponentCommonProperties { | |
| 16 | - | |
| 17 | - /** | |
| 18 | - * 组件的唯一标识 | |
| 19 | - * <p> | |
| 20 | - * 注意:该值将在运行时由各组件设置,不再从配置读取 | |
| 21 | - */ | |
| 22 | - private String pluginKey; | |
| 23 | - | |
| 24 | -} | |
| 25 | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/resources/META-INF/spring.factories deleted
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/src/main/java/cn/iocoder/yudao/module/iot/component/http/config/IotComponentHttpAutoConfiguration.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.component.http.config; | |
| 2 | - | |
| 3 | -import cn.hutool.system.SystemUtil; | |
| 4 | -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 5 | -import cn.iocoder.yudao.module.iot.component.core.config.IotComponentCommonProperties; | |
| 6 | -import cn.iocoder.yudao.module.iot.component.core.downstream.IotDeviceDownstreamHandler; | |
| 7 | -import cn.iocoder.yudao.module.iot.component.core.heartbeat.IotComponentRegistry; | |
| 8 | -import cn.iocoder.yudao.module.iot.component.http.downstream.IotDeviceDownstreamHandlerImpl; | |
| 9 | -import cn.iocoder.yudao.module.iot.component.http.upstream.IotDeviceUpstreamServer; | |
| 10 | -import lombok.extern.slf4j.Slf4j; | |
| 11 | -import org.springframework.boot.autoconfigure.AutoConfiguration; | |
| 12 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 13 | -import org.springframework.boot.context.event.ApplicationStartedEvent; | |
| 14 | -import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 15 | -import org.springframework.context.ApplicationContext; | |
| 16 | -import org.springframework.context.annotation.Bean; | |
| 17 | -import org.springframework.context.annotation.ComponentScan; | |
| 18 | -import org.springframework.context.event.EventListener; | |
| 19 | - | |
| 20 | -import java.lang.management.ManagementFactory; | |
| 21 | - | |
| 22 | -// TODO @haohao:类似 IotComponentEmqxAutoConfiguration 的建议 | |
| 23 | -/** | |
| 24 | - * IoT 组件 HTTP 的自动配置类 | |
| 25 | - * | |
| 26 | - * @author haohao | |
| 27 | - */ | |
| 28 | -@Slf4j | |
| 29 | -@AutoConfiguration | |
| 30 | -@EnableConfigurationProperties(IotComponentHttpProperties.class) | |
| 31 | -@ConditionalOnProperty(prefix = "yudao.iot.component.http", name = "enabled", havingValue = "true", matchIfMissing = false) | |
| 32 | -@ComponentScan(basePackages = { | |
| 33 | - "cn.iocoder.yudao.module.iot.component.core", // 核心包 | |
| 34 | - "cn.iocoder.yudao.module.iot.component.http" // HTTP组件包 | |
| 35 | -}) | |
| 36 | -public class IotComponentHttpAutoConfiguration { | |
| 37 | - | |
| 38 | - /** | |
| 39 | - * 组件key | |
| 40 | - */ | |
| 41 | - private static final String PLUGIN_KEY = "http"; | |
| 42 | - | |
| 43 | - public IotComponentHttpAutoConfiguration() { | |
| 44 | - log.info("[IotComponentHttpAutoConfiguration][已启动]"); | |
| 45 | - } | |
| 46 | - | |
| 47 | - @EventListener(ApplicationStartedEvent.class) | |
| 48 | - public void initialize(ApplicationStartedEvent event) { | |
| 49 | - // 从应用上下文中获取需要的Bean | |
| 50 | - IotComponentRegistry componentRegistry = event.getApplicationContext().getBean(IotComponentRegistry.class); | |
| 51 | - IotComponentCommonProperties commonProperties = event.getApplicationContext() | |
| 52 | - .getBean(IotComponentCommonProperties.class); | |
| 53 | - | |
| 54 | - // 设置当前组件的核心标识 | |
| 55 | - commonProperties.setPluginKey(PLUGIN_KEY); | |
| 56 | - | |
| 57 | - // 将HTTP组件注册到组件注册表 | |
| 58 | - componentRegistry.registerComponent( | |
| 59 | - PLUGIN_KEY, | |
| 60 | - SystemUtil.getHostInfo().getAddress(), | |
| 61 | - 0, // 内嵌模式固定为0 | |
| 62 | - getProcessId()); | |
| 63 | - | |
| 64 | - log.info("[initialize][IoT HTTP 组件初始化完成]"); | |
| 65 | - } | |
| 66 | - | |
| 67 | - @Bean(name = "httpDeviceUpstreamServer", initMethod = "start", destroyMethod = "stop") | |
| 68 | - public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi, | |
| 69 | - IotComponentHttpProperties properties, | |
| 70 | - ApplicationContext applicationContext, | |
| 71 | - IotComponentRegistry componentRegistry) { | |
| 72 | - return new IotDeviceUpstreamServer(properties, deviceUpstreamApi, applicationContext, componentRegistry); | |
| 73 | - } | |
| 74 | - | |
| 75 | - @Bean(name = "httpDeviceDownstreamHandler") | |
| 76 | - public IotDeviceDownstreamHandler deviceDownstreamHandler() { | |
| 77 | - return new IotDeviceDownstreamHandlerImpl(); | |
| 78 | - } | |
| 79 | - | |
| 80 | - /** | |
| 81 | - * 获取当前进程ID | |
| 82 | - * | |
| 83 | - * @return 进程ID | |
| 84 | - */ | |
| 85 | - private String getProcessId() { | |
| 86 | - // 获取进程的 name | |
| 87 | - String name = ManagementFactory.getRuntimeMXBean().getName(); | |
| 88 | - // 分割名称,格式为 pid@hostname | |
| 89 | - String pid = name.split("@")[0]; | |
| 90 | - return pid; | |
| 91 | - } | |
| 92 | -} |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/src/main/java/cn/iocoder/yudao/module/iot/component/http/upstream/IotDeviceUpstreamServer.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.component.http.upstream; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 4 | -import cn.iocoder.yudao.module.iot.component.core.heartbeat.IotComponentRegistry; | |
| 5 | -import cn.iocoder.yudao.module.iot.component.http.config.IotComponentHttpProperties; | |
| 6 | -import cn.iocoder.yudao.module.iot.component.http.upstream.router.IotDeviceUpstreamVertxHandler; | |
| 7 | -import io.vertx.core.Vertx; | |
| 8 | -import io.vertx.core.http.HttpServer; | |
| 9 | -import io.vertx.ext.web.Router; | |
| 10 | -import io.vertx.ext.web.handler.BodyHandler; | |
| 11 | -import lombok.extern.slf4j.Slf4j; | |
| 12 | -import org.springframework.context.ApplicationContext; | |
| 13 | - | |
| 14 | -/** | |
| 15 | - * IoT 设备下行服务端,接收来自 device 设备的请求,转发给 server 服务器 | |
| 16 | - * <p> | |
| 17 | - * 协议:HTTP | |
| 18 | - * | |
| 19 | - * @author haohao | |
| 20 | - */ | |
| 21 | -@Slf4j | |
| 22 | -public class IotDeviceUpstreamServer { | |
| 23 | - | |
| 24 | - private final Vertx vertx; | |
| 25 | - private final HttpServer server; | |
| 26 | - private final IotComponentHttpProperties properties; | |
| 27 | - private final IotComponentRegistry componentRegistry; | |
| 28 | - | |
| 29 | - public IotDeviceUpstreamServer(IotComponentHttpProperties properties, | |
| 30 | - IotDeviceUpstreamApi deviceUpstreamApi, | |
| 31 | - ApplicationContext applicationContext, | |
| 32 | - IotComponentRegistry componentRegistry) { | |
| 33 | - this.properties = properties; | |
| 34 | - this.componentRegistry = componentRegistry; | |
| 35 | - | |
| 36 | - // 创建 Vertx 实例 | |
| 37 | - this.vertx = Vertx.vertx(); | |
| 38 | - // 创建 Router 实例 | |
| 39 | - Router router = Router.router(vertx); | |
| 40 | - router.route().handler(BodyHandler.create()); // 处理 Body | |
| 41 | - | |
| 42 | - // 使用统一的 Handler 处理所有上行请求 | |
| 43 | - IotDeviceUpstreamVertxHandler upstreamHandler = new IotDeviceUpstreamVertxHandler(deviceUpstreamApi, | |
| 44 | - applicationContext); | |
| 45 | - router.post(IotDeviceUpstreamVertxHandler.PROPERTY_PATH).handler(upstreamHandler); | |
| 46 | - router.post(IotDeviceUpstreamVertxHandler.EVENT_PATH).handler(upstreamHandler); | |
| 47 | - | |
| 48 | - // 创建 HttpServer 实例 | |
| 49 | - this.server = vertx.createHttpServer().requestHandler(router); | |
| 50 | - } | |
| 51 | - | |
| 52 | - /** | |
| 53 | - * 启动 HTTP 服务器 | |
| 54 | - */ | |
| 55 | - public void start() { | |
| 56 | - log.info("[start][开始启动]"); | |
| 57 | - server.listen(properties.getServerPort()) | |
| 58 | - .toCompletionStage() | |
| 59 | - .toCompletableFuture() | |
| 60 | - .join(); | |
| 61 | - log.info("[start][启动完成,端口({})]", this.server.actualPort()); | |
| 62 | - } | |
| 63 | - | |
| 64 | - /** | |
| 65 | - * 停止所有 | |
| 66 | - */ | |
| 67 | - public void stop() { | |
| 68 | - log.info("[stop][开始关闭]"); | |
| 69 | - try { | |
| 70 | - // 关闭 HTTP 服务器 | |
| 71 | - if (server != null) { | |
| 72 | - server.close() | |
| 73 | - .toCompletionStage() | |
| 74 | - .toCompletableFuture() | |
| 75 | - .join(); | |
| 76 | - } | |
| 77 | - | |
| 78 | - // 关闭 Vertx 实例 | |
| 79 | - if (vertx != null) { | |
| 80 | - vertx.close() | |
| 81 | - .toCompletionStage() | |
| 82 | - .toCompletableFuture() | |
| 83 | - .join(); | |
| 84 | - } | |
| 85 | - log.info("[stop][关闭完成]"); | |
| 86 | - } catch (Exception e) { | |
| 87 | - log.error("[stop][关闭异常]", e); | |
| 88 | - throw new RuntimeException(e); | |
| 89 | - } | |
| 90 | - } | |
| 91 | -} |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/src/main/java/cn/iocoder/yudao/module/iot/component/http/upstream/router/IotDeviceUpstreamVertxHandler.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.component.http.upstream.router; | |
| 2 | - | |
| 3 | -import cn.hutool.core.util.IdUtil; | |
| 4 | -import cn.hutool.core.util.ObjUtil; | |
| 5 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 6 | -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 7 | -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; | |
| 8 | -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; | |
| 9 | -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; | |
| 10 | -import cn.iocoder.yudao.module.iot.component.core.pojo.IotStandardResponse; | |
| 11 | -import cn.iocoder.yudao.module.iot.component.core.util.IotPluginCommonUtils; | |
| 12 | -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; | |
| 13 | -import io.vertx.core.Handler; | |
| 14 | -import io.vertx.core.json.JsonObject; | |
| 15 | -import io.vertx.ext.web.RoutingContext; | |
| 16 | -import lombok.extern.slf4j.Slf4j; | |
| 17 | -import org.springframework.context.ApplicationContext; | |
| 18 | - | |
| 19 | -import java.time.LocalDateTime; | |
| 20 | -import java.util.HashMap; | |
| 21 | -import java.util.Map; | |
| 22 | - | |
| 23 | -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | |
| 24 | -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | |
| 25 | - | |
| 26 | -/** | |
| 27 | - * IoT 设备上行统一处理的 Vert.x Handler | |
| 28 | - * <p> | |
| 29 | - * 统一处理设备属性上报和事件上报的请求 | |
| 30 | - * | |
| 31 | - * @author haohao | |
| 32 | - */ | |
| 33 | -@Slf4j | |
| 34 | -public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { | |
| 35 | - | |
| 36 | - /** | |
| 37 | - * 属性上报路径 | |
| 38 | - */ | |
| 39 | - public static final String PROPERTY_PATH = "/sys/:productKey/:deviceName/thing/event/property/post"; | |
| 40 | - /** | |
| 41 | - * 事件上报路径 | |
| 42 | - */ | |
| 43 | - public static final String EVENT_PATH = "/sys/:productKey/:deviceName/thing/event/:identifier/post"; | |
| 44 | - | |
| 45 | - private static final String PROPERTY_METHOD = "thing.event.property.post"; | |
| 46 | - private static final String EVENT_METHOD_PREFIX = "thing.event."; | |
| 47 | - private static final String EVENT_METHOD_SUFFIX = ".post"; | |
| 48 | - | |
| 49 | - private final IotDeviceUpstreamApi deviceUpstreamApi; | |
| 50 | -// private final HttpScriptService scriptService; | |
| 51 | - | |
| 52 | - public IotDeviceUpstreamVertxHandler(IotDeviceUpstreamApi deviceUpstreamApi, | |
| 53 | - ApplicationContext applicationContext) { | |
| 54 | - this.deviceUpstreamApi = deviceUpstreamApi; | |
| 55 | -// this.scriptService = applicationContext.getBean(HttpScriptService.class); | |
| 56 | - } | |
| 57 | - | |
| 58 | - @Override | |
| 59 | - public void handle(RoutingContext routingContext) { | |
| 60 | - String path = routingContext.request().path(); | |
| 61 | - String requestId = IdUtil.fastSimpleUUID(); | |
| 62 | - | |
| 63 | - try { | |
| 64 | - // 1. 解析通用参数 | |
| 65 | - String productKey = routingContext.pathParam("productKey"); | |
| 66 | - String deviceName = routingContext.pathParam("deviceName"); | |
| 67 | - JsonObject body = routingContext.body().asJsonObject(); | |
| 68 | - requestId = ObjUtil.defaultIfBlank(body.getString("id"), requestId); | |
| 69 | - | |
| 70 | - // 2. 根据路径模式处理不同类型的请求 | |
| 71 | - CommonResult<Boolean> result; | |
| 72 | - String method; | |
| 73 | - if (path.matches(".*/thing/event/property/post")) { | |
| 74 | - // 处理属性上报 | |
| 75 | - IotDevicePropertyReportReqDTO reportReqDTO = parsePropertyReportRequest(productKey, deviceName, | |
| 76 | - requestId, body); | |
| 77 | - | |
| 78 | - // 设备上线 | |
| 79 | - updateDeviceState(reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); | |
| 80 | - | |
| 81 | - // 属性上报 | |
| 82 | - result = deviceUpstreamApi.reportDeviceProperty(reportReqDTO); | |
| 83 | - method = PROPERTY_METHOD; | |
| 84 | - } else if (path.matches(".*/thing/event/.+/post")) { | |
| 85 | - // 处理事件上报 | |
| 86 | - String identifier = routingContext.pathParam("identifier"); | |
| 87 | - IotDeviceEventReportReqDTO reportReqDTO = parseEventReportRequest(productKey, deviceName, identifier, | |
| 88 | - requestId, body); | |
| 89 | - | |
| 90 | - // 设备上线 | |
| 91 | - updateDeviceState(reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); | |
| 92 | - | |
| 93 | - // 事件上报 | |
| 94 | - result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO); | |
| 95 | - method = EVENT_METHOD_PREFIX + identifier + EVENT_METHOD_SUFFIX; | |
| 96 | - } else { | |
| 97 | - // 不支持的请求路径 | |
| 98 | - IotStandardResponse errorResponse = IotStandardResponse.error(requestId, "unknown", | |
| 99 | - BAD_REQUEST.getCode(), "不支持的请求路径"); | |
| 100 | - IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); | |
| 101 | - return; | |
| 102 | - } | |
| 103 | - | |
| 104 | - // 3. 返回标准响应 | |
| 105 | - IotStandardResponse response; | |
| 106 | - if (result.isSuccess()) { | |
| 107 | - response = IotStandardResponse.success(requestId, method, result.getData()); | |
| 108 | - } else { | |
| 109 | - response = IotStandardResponse.error(requestId, method, result.getCode(), result.getMsg()); | |
| 110 | - } | |
| 111 | - IotPluginCommonUtils.writeJsonResponse(routingContext, response); | |
| 112 | - } catch (Exception e) { | |
| 113 | - log.error("[handle][处理上行请求异常] path={}", path, e); | |
| 114 | - String method = path.contains("/property/") ? PROPERTY_METHOD | |
| 115 | - : EVENT_METHOD_PREFIX + (routingContext.pathParams().containsKey("identifier") | |
| 116 | - ? routingContext.pathParam("identifier") | |
| 117 | - : "unknown") + EVENT_METHOD_SUFFIX; | |
| 118 | - IotStandardResponse errorResponse = IotStandardResponse.error(requestId, method, | |
| 119 | - INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); | |
| 120 | - IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); | |
| 121 | - } | |
| 122 | - } | |
| 123 | - | |
| 124 | - /** | |
| 125 | - * 更新设备状态 | |
| 126 | - * | |
| 127 | - * @param productKey 产品 Key | |
| 128 | - * @param deviceName 设备名称 | |
| 129 | - */ | |
| 130 | - private void updateDeviceState(String productKey, String deviceName) { | |
| 131 | - deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO() | |
| 132 | - .setRequestId(IdUtil.fastSimpleUUID()).setProcessId(IotPluginCommonUtils.getProcessId()) | |
| 133 | - .setReportTime(LocalDateTime.now()) | |
| 134 | - .setProductKey(productKey).setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState())); | |
| 135 | - } | |
| 136 | - | |
| 137 | - /** | |
| 138 | - * 解析属性上报请求 | |
| 139 | - * | |
| 140 | - * @param productKey 产品 Key | |
| 141 | - * @param deviceName 设备名称 | |
| 142 | - * @param requestId 请求 ID | |
| 143 | - * @param body 请求体 | |
| 144 | - * @return 属性上报请求 DTO | |
| 145 | - */ | |
| 146 | - private IotDevicePropertyReportReqDTO parsePropertyReportRequest(String productKey, String deviceName, | |
| 147 | - String requestId, JsonObject body) { | |
| 148 | - // 使用脚本解析数据 | |
| 149 | -// Map<String, Object> properties = scriptService.parsePropertyData(productKey, deviceName, body); | |
| 150 | - | |
| 151 | - | |
| 152 | - // 如果脚本解析结果为空,使用默认解析逻辑 | |
| 153 | - // TODO @芋艿:注释说明一下,为什么要这么处理? | |
| 154 | -// if (CollUtil.isNotEmpty(properties)) { | |
| 155 | - Map<String, Object> properties = new HashMap<>(); | |
| 156 | - Map<String, Object> params = body.getJsonObject("params") != null ? | |
| 157 | - body.getJsonObject("params").getMap() : null; | |
| 158 | - if (params != null) { | |
| 159 | - // 将标准格式的 params 转换为平台需要的 properties 格式 | |
| 160 | - for (Map.Entry<String, Object> entry : params.entrySet()) { | |
| 161 | - String key = entry.getKey(); | |
| 162 | - Object valueObj = entry.getValue(); | |
| 163 | - // 如果是复杂结构(包含 value 和 time) | |
| 164 | - if (valueObj instanceof Map) { | |
| 165 | - @SuppressWarnings("unchecked") | |
| 166 | - Map<String, Object> valueMap = (Map<String, Object>) valueObj; | |
| 167 | - properties.put(key, valueMap.getOrDefault("value", valueObj)); | |
| 168 | - } else { | |
| 169 | - properties.put(key, valueObj); | |
| 170 | - } | |
| 171 | - } | |
| 172 | - } | |
| 173 | -// } | |
| 174 | - | |
| 175 | - // 构建属性上报请求 DTO | |
| 176 | - return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId) | |
| 177 | - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) | |
| 178 | - .setProductKey(productKey).setDeviceName(deviceName)).setProperties(properties); | |
| 179 | - } | |
| 180 | - | |
| 181 | - /** | |
| 182 | - * 解析事件上报请求 | |
| 183 | - * | |
| 184 | - * @param productKey 产品K ey | |
| 185 | - * @param deviceName 设备名称 | |
| 186 | - * @param identifier 事件标识符 | |
| 187 | - * @param requestId 请求 ID | |
| 188 | - * @param body 请求体 | |
| 189 | - * @return 事件上报请求 DTO | |
| 190 | - */ | |
| 191 | - private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier, | |
| 192 | - String requestId, JsonObject body) { | |
| 193 | - // 使用脚本解析事件数据 | |
| 194 | -// Map<String, Object> params = scriptService.parseEventData(productKey, deviceName, identifier, body); | |
| 195 | - Map<String, Object> params = null; | |
| 196 | - | |
| 197 | - // 如果脚本解析结果为空,使用默认解析逻辑 | |
| 198 | -// if (CollUtil.isNotEmpty(params)) { | |
| 199 | - if (body.containsKey("params")) { | |
| 200 | - params = body.getJsonObject("params").getMap(); | |
| 201 | - } else { | |
| 202 | - // 兼容旧格式 | |
| 203 | - params = new HashMap<>(); | |
| 204 | - } | |
| 205 | -// } | |
| 206 | - | |
| 207 | - // 构建事件上报请求 DTO | |
| 208 | - return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) | |
| 209 | - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) | |
| 210 | - .setProductKey(productKey).setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); | |
| 211 | - } | |
| 212 | -} | |
| 213 | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted
yudao-module-iot/yudao-module-iot-components/README.md renamed to yudao-module-iot/yudao-module-iot-net-components/README.md
| ... | ... | @@ -4,9 +4,9 @@ |
| 4 | 4 | |
| 5 | 5 | 该模块包含多个 IoT 设备连接组件,提供不同的通信协议支持: |
| 6 | 6 | |
| 7 | -- `yudao-module-iot-component-core`: 核心接口和通用类 | |
| 8 | -- `yudao-module-iot-component-http`: 基于 HTTP 协议的设备通信组件 | |
| 9 | -- `yudao-module-iot-component-emqx`: 基于 MQTT/EMQX 的设备通信组件 | |
| 7 | +- `yudao-module-iot-net-component-core`: 核心接口和通用类 | |
| 8 | +- `yudao-module-iot-net-component-http`: 基于 HTTP 协议的设备通信组件 | |
| 9 | +- `yudao-module-iot-net-component-emqx`: 基于 MQTT/EMQX 的设备通信组件 | |
| 10 | 10 | |
| 11 | 11 | ## 组件架构 |
| 12 | 12 | ... | ... |
yudao-module-iot/yudao-module-iot-components/pom.xml renamed to yudao-module-iot/yudao-module-iot-net-components/pom.xml
| ... | ... | @@ -9,18 +9,18 @@ |
| 9 | 9 | </parent> |
| 10 | 10 | <modelVersion>4.0.0</modelVersion> |
| 11 | 11 | |
| 12 | - <artifactId>yudao-module-iot-components</artifactId> | |
| 12 | + <artifactId>yudao-module-iot-net-components</artifactId> | |
| 13 | 13 | <packaging>pom</packaging> |
| 14 | 14 | |
| 15 | 15 | <name>${project.artifactId}</name> |
| 16 | 16 | <description> |
| 17 | - 物联网组件模块,提供与物联网设备通讯、管理的组件实现 | |
| 17 | + 物联网网络组件模块,提供与物联网设备通讯、管理的网络组件实现 | |
| 18 | 18 | </description> |
| 19 | 19 | |
| 20 | 20 | <modules> |
| 21 | - <module>yudao-module-iot-component-core</module> | |
| 22 | - <module>yudao-module-iot-component-http</module> | |
| 23 | - <module>yudao-module-iot-component-emqx</module> | |
| 21 | + <module>yudao-module-iot-net-component-core</module> | |
| 22 | + <module>yudao-module-iot-net-component-http</module> | |
| 23 | + <module>yudao-module-iot-net-component-emqx</module> | |
| 24 | 24 | </modules> |
| 25 | 25 | |
| 26 | 26 | </project> |
| 27 | 27 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/pom.xml renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/pom.xml
| ... | ... | @@ -3,19 +3,18 @@ |
| 3 | 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 4 | 4 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| 5 | 5 | <parent> |
| 6 | - <artifactId>yudao-module-iot-components</artifactId> | |
| 6 | + <artifactId>yudao-module-iot-net-components</artifactId> | |
| 7 | 7 | <groupId>cn.iocoder.boot</groupId> |
| 8 | 8 | <version>${revision}</version> |
| 9 | 9 | </parent> |
| 10 | 10 | <modelVersion>4.0.0</modelVersion> |
| 11 | 11 | |
| 12 | - <artifactId>yudao-module-iot-component-core</artifactId> | |
| 12 | + <artifactId>yudao-module-iot-net-component-core</artifactId> | |
| 13 | 13 | <packaging>jar</packaging> |
| 14 | 14 | |
| 15 | 15 | <name>${project.artifactId}</name> |
| 16 | - <!-- TODO @芋艿:description 后续统一优化一波 --> | |
| 17 | 16 | <description> |
| 18 | - 物联网组件核心模块 | |
| 17 | + 物联网网络组件核心模块 | |
| 19 | 18 | </description> |
| 20 | 19 | |
| 21 | 20 | <dependencies> | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/config/IotComponentCommonAutoConfiguration.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/config/IotNetComponentCommonAutoConfiguration.java
| 1 | -package cn.iocoder.yudao.module.iot.component.core.config; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.config; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 4 | -import cn.iocoder.yudao.module.iot.component.core.downstream.IotDeviceDownstreamHandler; | |
| 5 | -import cn.iocoder.yudao.module.iot.component.core.downstream.IotDeviceDownstreamServer; | |
| 6 | -import cn.iocoder.yudao.module.iot.component.core.heartbeat.IotComponentInstanceHeartbeatJob; | |
| 7 | -import cn.iocoder.yudao.module.iot.component.core.heartbeat.IotComponentRegistry; | |
| 8 | -import cn.iocoder.yudao.module.iot.component.core.upstream.IotDeviceUpstreamClient; | |
| 4 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 5 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamServer; | |
| 6 | +import cn.iocoder.yudao.module.iot.net.component.core.heartbeat.IotNetComponentInstanceHeartbeatJob; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.core.heartbeat.IotNetComponentRegistry; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.core.upstream.IotDeviceUpstreamClient; | |
| 9 | 9 | import org.springframework.beans.factory.annotation.Qualifier; |
| 10 | 10 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 11 | 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| ... | ... | @@ -14,37 +14,45 @@ import org.springframework.context.annotation.Bean; |
| 14 | 14 | import org.springframework.scheduling.annotation.EnableScheduling; |
| 15 | 15 | |
| 16 | 16 | /** |
| 17 | - * IoT 组件的通用自动配置类 | |
| 17 | + * IoT 网络组件的通用自动配置类 | |
| 18 | 18 | * |
| 19 | 19 | * @author haohao |
| 20 | 20 | */ |
| 21 | 21 | @AutoConfiguration |
| 22 | -@EnableConfigurationProperties(IotComponentCommonProperties.class) | |
| 23 | -@EnableScheduling // 开启定时任务,因为 IotComponentInstanceHeartbeatJob 是一个定时任务 | |
| 24 | -public class IotComponentCommonAutoConfiguration { | |
| 22 | +@EnableConfigurationProperties(IotNetComponentCommonProperties.class) | |
| 23 | +@EnableScheduling // 开启定时任务,因为 IotNetComponentInstanceHeartbeatJob 是一个定时任务 | |
| 24 | +public class IotNetComponentCommonAutoConfiguration { | |
| 25 | 25 | |
| 26 | 26 | /** |
| 27 | 27 | * 创建 EMQX 设备下行服务器 |
| 28 | - * | |
| 28 | + * <p> | |
| 29 | 29 | * 当 yudao.iot.component.emqx.enabled = true 时,优先使用 emqxDeviceDownstreamHandler |
| 30 | 30 | */ |
| 31 | 31 | @Bean |
| 32 | 32 | @ConditionalOnProperty(prefix = "yudao.iot.component.emqx", name = "enabled", havingValue = "true") |
| 33 | 33 | public IotDeviceDownstreamServer emqxDeviceDownstreamServer( |
| 34 | - IotComponentCommonProperties properties, | |
| 34 | + IotNetComponentCommonProperties properties, | |
| 35 | 35 | @Qualifier("emqxDeviceDownstreamHandler") IotDeviceDownstreamHandler deviceDownstreamHandler) { |
| 36 | 36 | return new IotDeviceDownstreamServer(properties, deviceDownstreamHandler); |
| 37 | 37 | } |
| 38 | 38 | |
| 39 | + /** | |
| 40 | + * 创建网络组件实例心跳任务 | |
| 41 | + */ | |
| 39 | 42 | @Bean(initMethod = "init", destroyMethod = "stop") |
| 40 | - public IotComponentInstanceHeartbeatJob pluginInstanceHeartbeatJob(IotDeviceUpstreamApi deviceUpstreamApi, | |
| 41 | - IotDeviceDownstreamServer deviceDownstreamServer, | |
| 42 | - IotComponentCommonProperties commonProperties, | |
| 43 | - IotComponentRegistry componentRegistry) { | |
| 44 | - return new IotComponentInstanceHeartbeatJob(deviceUpstreamApi, deviceDownstreamServer, commonProperties, | |
| 43 | + public IotNetComponentInstanceHeartbeatJob pluginInstanceHeartbeatJob( | |
| 44 | + IotDeviceUpstreamApi deviceUpstreamApi, | |
| 45 | + IotNetComponentCommonProperties commonProperties, | |
| 46 | + IotNetComponentRegistry componentRegistry) { | |
| 47 | + return new IotNetComponentInstanceHeartbeatJob( | |
| 48 | + deviceUpstreamApi, | |
| 49 | + commonProperties, | |
| 45 | 50 | componentRegistry); |
| 46 | 51 | } |
| 47 | 52 | |
| 53 | + /** | |
| 54 | + * 创建设备上行客户端 | |
| 55 | + */ | |
| 48 | 56 | @Bean |
| 49 | 57 | public IotDeviceUpstreamClient deviceUpstreamClient() { |
| 50 | 58 | return new IotDeviceUpstreamClient(); | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/config/IotNetComponentCommonProperties.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.config; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
| 5 | +import org.springframework.validation.annotation.Validated; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * IoT 网络组件通用配置属性 | |
| 9 | + * | |
| 10 | + * @author haohao | |
| 11 | + */ | |
| 12 | +@ConfigurationProperties(prefix = "yudao.iot.component") | |
| 13 | +@Validated | |
| 14 | +@Data | |
| 15 | +public class IotNetComponentCommonProperties { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 组件的唯一标识 | |
| 19 | + * <p> | |
| 20 | + * 注意:该值将在运行时由各组件设置,不再从配置读取 | |
| 21 | + */ | |
| 22 | + private String pluginKey; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 组件实例心跳超时时间,单位:毫秒 | |
| 26 | + * <p> | |
| 27 | + * 默认值:30 秒 | |
| 28 | + */ | |
| 29 | + private Long instanceHeartbeatTimeout = 30000L; | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 网络组件消息转发配置 | |
| 33 | + */ | |
| 34 | + private ForwardMessage forwardMessage = new ForwardMessage(); | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * 消息转发配置 | |
| 38 | + */ | |
| 39 | + @Data | |
| 40 | + public static class ForwardMessage { | |
| 41 | + | |
| 42 | + /** | |
| 43 | + * 是否转发所有设备消息到 EMQX | |
| 44 | + * <p> | |
| 45 | + * 默认为 true 开启 | |
| 46 | + */ | |
| 47 | + private boolean forwardAllDeviceMessageToEmqx = true; | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * 是否转发所有设备消息到 HTTP | |
| 51 | + * <p> | |
| 52 | + * 默认为 false 关闭 | |
| 53 | + */ | |
| 54 | + private boolean forwardAllDeviceMessageToHttp = false; | |
| 55 | + } | |
| 56 | +} | |
| 0 | 57 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/constants/IotDeviceTopicEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.constants; | |
| 2 | + | |
| 3 | +import lombok.Getter; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * IoT 设备主题枚举 | |
| 7 | + * <p> | |
| 8 | + * 用于统一管理 MQTT 协议中的主题常量,基于 Alink 协议规范 | |
| 9 | + * | |
| 10 | + * @author haohao | |
| 11 | + */ | |
| 12 | +@Getter | |
| 13 | +public enum IotDeviceTopicEnum { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * 系统主题前缀 | |
| 17 | + */ | |
| 18 | + SYS_TOPIC_PREFIX("/sys/", "系统主题前缀"), | |
| 19 | + | |
| 20 | + /** | |
| 21 | + * 服务调用主题前缀 | |
| 22 | + */ | |
| 23 | + SERVICE_TOPIC_PREFIX("/thing/service/", "服务调用主题前缀"), | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 设备属性设置主题 | |
| 27 | + * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set | |
| 28 | + * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply | |
| 29 | + */ | |
| 30 | + PROPERTY_SET_TOPIC("/thing/service/property/set", "设备属性设置主题"), | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 设备属性获取主题 | |
| 34 | + * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/get | |
| 35 | + * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/get_reply | |
| 36 | + */ | |
| 37 | + PROPERTY_GET_TOPIC("/thing/service/property/get", "设备属性获取主题"), | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * 设备配置设置主题 | |
| 41 | + * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/config/set | |
| 42 | + * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/config/set_reply | |
| 43 | + */ | |
| 44 | + CONFIG_SET_TOPIC("/thing/service/config/set", "设备配置设置主题"), | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 设备OTA升级主题 | |
| 48 | + * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade | |
| 49 | + * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade_reply | |
| 50 | + */ | |
| 51 | + OTA_UPGRADE_TOPIC("/thing/service/ota/upgrade", "设备OTA升级主题"), | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 设备属性上报主题 | |
| 55 | + * 请求Topic:/sys/${productKey}/${deviceName}/thing/event/property/post | |
| 56 | + * 响应Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply | |
| 57 | + */ | |
| 58 | + PROPERTY_POST_TOPIC("/thing/event/property/post", "设备属性上报主题"), | |
| 59 | + | |
| 60 | + /** | |
| 61 | + * 设备事件上报主题前缀 | |
| 62 | + */ | |
| 63 | + EVENT_POST_TOPIC_PREFIX("/thing/event/", "设备事件上报主题前缀"), | |
| 64 | + | |
| 65 | + /** | |
| 66 | + * 设备事件上报主题后缀 | |
| 67 | + */ | |
| 68 | + EVENT_POST_TOPIC_SUFFIX("/post", "设备事件上报主题后缀"), | |
| 69 | + | |
| 70 | + /** | |
| 71 | + * 响应主题后缀 | |
| 72 | + */ | |
| 73 | + REPLY_SUFFIX("_reply", "响应主题后缀"); | |
| 74 | + | |
| 75 | + private final String topic; | |
| 76 | + private final String description; | |
| 77 | + | |
| 78 | + IotDeviceTopicEnum(String topic, String description) { | |
| 79 | + this.topic = topic; | |
| 80 | + this.description = description; | |
| 81 | + } | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * 构建设备服务调用主题 | |
| 85 | + * | |
| 86 | + * @param productKey 产品Key | |
| 87 | + * @param deviceName 设备名称 | |
| 88 | + * @param serviceIdentifier 服务标识符 | |
| 89 | + * @return 完整的主题路径 | |
| 90 | + */ | |
| 91 | + public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) { | |
| 92 | + return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + | |
| 93 | + SERVICE_TOPIC_PREFIX.getTopic() + serviceIdentifier; | |
| 94 | + } | |
| 95 | + | |
| 96 | + /** | |
| 97 | + * 构建设备属性设置主题 | |
| 98 | + * | |
| 99 | + * @param productKey 产品Key | |
| 100 | + * @param deviceName 设备名称 | |
| 101 | + * @return 完整的主题路径 | |
| 102 | + */ | |
| 103 | + public static String buildPropertySetTopic(String productKey, String deviceName) { | |
| 104 | + return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_SET_TOPIC.getTopic(); | |
| 105 | + } | |
| 106 | + | |
| 107 | + /** | |
| 108 | + * 构建设备属性获取主题 | |
| 109 | + * | |
| 110 | + * @param productKey 产品Key | |
| 111 | + * @param deviceName 设备名称 | |
| 112 | + * @return 完整的主题路径 | |
| 113 | + */ | |
| 114 | + public static String buildPropertyGetTopic(String productKey, String deviceName) { | |
| 115 | + return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_GET_TOPIC.getTopic(); | |
| 116 | + } | |
| 117 | + | |
| 118 | + /** | |
| 119 | + * 构建设备配置设置主题 | |
| 120 | + * | |
| 121 | + * @param productKey 产品Key | |
| 122 | + * @param deviceName 设备名称 | |
| 123 | + * @return 完整的主题路径 | |
| 124 | + */ | |
| 125 | + public static String buildConfigSetTopic(String productKey, String deviceName) { | |
| 126 | + return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + CONFIG_SET_TOPIC.getTopic(); | |
| 127 | + } | |
| 128 | + | |
| 129 | + /** | |
| 130 | + * 构建设备OTA升级主题 | |
| 131 | + * | |
| 132 | + * @param productKey 产品Key | |
| 133 | + * @param deviceName 设备名称 | |
| 134 | + * @return 完整的主题路径 | |
| 135 | + */ | |
| 136 | + public static String buildOtaUpgradeTopic(String productKey, String deviceName) { | |
| 137 | + return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + OTA_UPGRADE_TOPIC.getTopic(); | |
| 138 | + } | |
| 139 | + | |
| 140 | + /** | |
| 141 | + * 构建设备属性上报主题 | |
| 142 | + * | |
| 143 | + * @param productKey 产品Key | |
| 144 | + * @param deviceName 设备名称 | |
| 145 | + * @return 完整的主题路径 | |
| 146 | + */ | |
| 147 | + public static String buildPropertyPostTopic(String productKey, String deviceName) { | |
| 148 | + return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_POST_TOPIC.getTopic(); | |
| 149 | + } | |
| 150 | + | |
| 151 | + /** | |
| 152 | + * 构建设备事件上报主题 | |
| 153 | + * | |
| 154 | + * @param productKey 产品Key | |
| 155 | + * @param deviceName 设备名称 | |
| 156 | + * @param eventIdentifier 事件标识符 | |
| 157 | + * @return 完整的主题路径 | |
| 158 | + */ | |
| 159 | + public static String buildEventPostTopic(String productKey, String deviceName, String eventIdentifier) { | |
| 160 | + return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + | |
| 161 | + EVENT_POST_TOPIC_PREFIX.getTopic() + eventIdentifier + EVENT_POST_TOPIC_SUFFIX.getTopic(); | |
| 162 | + } | |
| 163 | + | |
| 164 | + /** | |
| 165 | + * 获取响应主题 | |
| 166 | + * | |
| 167 | + * @param requestTopic 请求主题 | |
| 168 | + * @return 响应主题 | |
| 169 | + */ | |
| 170 | + public static String getReplyTopic(String requestTopic) { | |
| 171 | + return requestTopic + REPLY_SUFFIX.getTopic(); | |
| 172 | + } | |
| 173 | +} | |
| 0 | 174 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/downstream/IotDeviceDownstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/downstream/IotDeviceDownstreamHandler.java
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/downstream/IotDeviceDownstreamServer.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/downstream/IotDeviceDownstreamServer.java
| 1 | -package cn.iocoder.yudao.module.iot.component.core.downstream; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.downstream; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; |
| 5 | -import cn.iocoder.yudao.module.iot.component.core.config.IotComponentCommonProperties; | |
| 5 | +import cn.iocoder.yudao.module.iot.net.component.core.config.IotNetComponentCommonProperties; | |
| 6 | 6 | import lombok.RequiredArgsConstructor; |
| 7 | 7 | import lombok.extern.slf4j.Slf4j; |
| 8 | 8 | |
| ... | ... | @@ -15,7 +15,7 @@ import lombok.extern.slf4j.Slf4j; |
| 15 | 15 | @RequiredArgsConstructor |
| 16 | 16 | public class IotDeviceDownstreamServer { |
| 17 | 17 | |
| 18 | - private final IotComponentCommonProperties properties; | |
| 18 | + private final IotNetComponentCommonProperties properties; | |
| 19 | 19 | private final IotDeviceDownstreamHandler deviceDownstreamHandler; |
| 20 | 20 | |
| 21 | 21 | /** | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/heartbeat/IotComponentInstanceHeartbeatJob.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/heartbeat/IotNetComponentInstanceHeartbeatJob.java
| 1 | -package cn.iocoder.yudao.module.iot.component.core.heartbeat; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.heartbeat; | |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.hutool.system.SystemUtil; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 6 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 6 | 7 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; |
| 7 | -import cn.iocoder.yudao.module.iot.component.core.config.IotComponentCommonProperties; | |
| 8 | -import cn.iocoder.yudao.module.iot.component.core.downstream.IotDeviceDownstreamServer; | |
| 9 | -import cn.iocoder.yudao.module.iot.component.core.heartbeat.IotComponentRegistry.IotComponentInfo; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.core.config.IotNetComponentCommonProperties; | |
| 9 | +import cn.iocoder.yudao.module.iot.net.component.core.heartbeat.IotNetComponentRegistry.IotNetComponentInfo; | |
| 10 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 10 | 11 | import lombok.RequiredArgsConstructor; |
| 11 | 12 | import lombok.extern.slf4j.Slf4j; |
| 12 | 13 | import org.springframework.scheduling.annotation.Scheduled; |
| 13 | 14 | |
| 14 | -import java.lang.management.ManagementFactory; | |
| 15 | +import java.util.Collection; | |
| 15 | 16 | import java.util.concurrent.TimeUnit; |
| 16 | 17 | |
| 17 | 18 | /** |
| 18 | - * IoT 组件实例心跳定时任务 | |
| 19 | + * IoT 网络组件实例心跳定时任务 | |
| 19 | 20 | * <p> |
| 20 | 21 | * 将组件的状态,定时上报给 server 服务器 |
| 22 | + * | |
| 23 | + * @author haohao | |
| 21 | 24 | */ |
| 22 | 25 | @RequiredArgsConstructor |
| 23 | 26 | @Slf4j |
| 24 | -public class IotComponentInstanceHeartbeatJob { | |
| 25 | - | |
| 26 | - /** | |
| 27 | - * 内嵌模式的端口值(固定为 0) | |
| 28 | - */ | |
| 29 | - private static final Integer EMBEDDED_PORT = 0; | |
| 27 | +public class IotNetComponentInstanceHeartbeatJob { | |
| 30 | 28 | |
| 31 | 29 | private final IotDeviceUpstreamApi deviceUpstreamApi; |
| 32 | - private final IotDeviceDownstreamServer deviceDownstreamServer; // TODO @haohao:这个变量还需要哇? | |
| 33 | - private final IotComponentCommonProperties commonProperties; | |
| 34 | - private final IotComponentRegistry componentRegistry; | |
| 30 | + private final IotNetComponentCommonProperties commonProperties; | |
| 31 | + private final IotNetComponentRegistry componentRegistry; | |
| 35 | 32 | |
| 36 | 33 | /** |
| 37 | - * 初始化方法,由 Spring调 用:注册当前组件并发送上线心跳 | |
| 34 | + * 初始化方法,由 Spring 调用:注册当前组件并发送上线心跳 | |
| 38 | 35 | */ |
| 39 | 36 | public void init() { |
| 40 | - // 将当前组件注册到注册表 | |
| 41 | - String processId = getProcessId(); | |
| 42 | - String hostIp = SystemUtil.getHostInfo().getAddress(); | |
| 43 | - | |
| 44 | - // 注册当前组件 | |
| 45 | - componentRegistry.registerComponent( | |
| 46 | - commonProperties.getPluginKey(), | |
| 47 | - hostIp, | |
| 48 | - EMBEDDED_PORT, | |
| 49 | - processId); | |
| 50 | - | |
| 51 | 37 | // 发送所有组件的上线心跳 |
| 52 | - for (IotComponentInfo component : componentRegistry.getAllComponents()) { | |
| 38 | + Collection<IotNetComponentInfo> components = componentRegistry.getAllComponents(); | |
| 39 | + if (CollUtil.isEmpty(components)) { | |
| 40 | + return; | |
| 41 | + } | |
| 42 | + for (IotNetComponentInfo component : components) { | |
| 53 | 43 | try { |
| 54 | 44 | CommonResult<Boolean> result = deviceUpstreamApi.heartbeatPluginInstance( |
| 55 | 45 | buildPluginInstanceHeartbeatReqDTO(component, true)); |
| 56 | - log.info("[init][组件({})上线结果:{})]", component.getPluginKey(), result); | |
| 46 | + log.info("[init][组件({})上线结果:{}]", component.getPluginKey(), result); | |
| 57 | 47 | } catch (Exception e) { |
| 58 | 48 | log.error("[init][组件({})上线发送异常]", component.getPluginKey(), e); |
| 59 | 49 | } |
| ... | ... | @@ -65,11 +55,15 @@ public class IotComponentInstanceHeartbeatJob { |
| 65 | 55 | */ |
| 66 | 56 | public void stop() { |
| 67 | 57 | // 发送所有组件的下线心跳 |
| 68 | - for (IotComponentInfo component : componentRegistry.getAllComponents()) { | |
| 58 | + Collection<IotNetComponentInfo> components = componentRegistry.getAllComponents(); | |
| 59 | + if (CollUtil.isEmpty(components)) { | |
| 60 | + return; | |
| 61 | + } | |
| 62 | + for (IotNetComponentInfo component : components) { | |
| 69 | 63 | try { |
| 70 | 64 | CommonResult<Boolean> result = deviceUpstreamApi.heartbeatPluginInstance( |
| 71 | 65 | buildPluginInstanceHeartbeatReqDTO(component, false)); |
| 72 | - log.info("[stop][组件({})下线结果:{})]", component.getPluginKey(), result); | |
| 66 | + log.info("[stop][组件({})下线结果:{}]", component.getPluginKey(), result); | |
| 73 | 67 | } catch (Exception e) { |
| 74 | 68 | log.error("[stop][组件({})下线发送异常]", component.getPluginKey(), e); |
| 75 | 69 | } |
| ... | ... | @@ -85,11 +79,15 @@ public class IotComponentInstanceHeartbeatJob { |
| 85 | 79 | @Scheduled(initialDelay = 1, fixedRate = 1, timeUnit = TimeUnit.MINUTES) // 1 分钟执行一次 |
| 86 | 80 | public void execute() { |
| 87 | 81 | // 发送所有组件的心跳 |
| 88 | - for (IotComponentInfo component : componentRegistry.getAllComponents()) { | |
| 82 | + Collection<IotNetComponentInfo> components = componentRegistry.getAllComponents(); | |
| 83 | + if (CollUtil.isEmpty(components)) { | |
| 84 | + return; | |
| 85 | + } | |
| 86 | + for (IotNetComponentInfo component : components) { | |
| 89 | 87 | try { |
| 90 | 88 | CommonResult<Boolean> result = deviceUpstreamApi.heartbeatPluginInstance( |
| 91 | 89 | buildPluginInstanceHeartbeatReqDTO(component, true)); |
| 92 | - log.info("[execute][组件({})心跳结果:{})]", component.getPluginKey(), result); | |
| 90 | + log.info("[execute][组件({})心跳结果:{}]", component.getPluginKey(), result); | |
| 93 | 91 | } catch (Exception e) { |
| 94 | 92 | log.error("[execute][组件({})心跳发送异常]", component.getPluginKey(), e); |
| 95 | 93 | } |
| ... | ... | @@ -103,23 +101,11 @@ public class IotComponentInstanceHeartbeatJob { |
| 103 | 101 | * @param online 是否在线 |
| 104 | 102 | * @return 心跳 DTO |
| 105 | 103 | */ |
| 106 | - private IotPluginInstanceHeartbeatReqDTO buildPluginInstanceHeartbeatReqDTO(IotComponentInfo component, | |
| 104 | + private IotPluginInstanceHeartbeatReqDTO buildPluginInstanceHeartbeatReqDTO(IotNetComponentInfo component, | |
| 107 | 105 | Boolean online) { |
| 108 | 106 | return new IotPluginInstanceHeartbeatReqDTO() |
| 109 | 107 | .setPluginKey(component.getPluginKey()).setProcessId(component.getProcessId()) |
| 110 | 108 | .setHostIp(component.getHostIp()).setDownstreamPort(component.getDownstreamPort()) |
| 111 | 109 | .setOnline(online); |
| 112 | 110 | } |
| 113 | - | |
| 114 | - // TODO @haohao:要和 IotPluginCommonUtils 保持一致么? | |
| 115 | - /** | |
| 116 | - * 获取当前进程 ID | |
| 117 | - * | |
| 118 | - * @return 进程 ID | |
| 119 | - */ | |
| 120 | - private String getProcessId() { | |
| 121 | - String name = ManagementFactory.getRuntimeMXBean().getName(); | |
| 122 | - // TODO @haohao:是不是 SystemUtil.getCurrentPID(); 直接获取 pid 哈? | |
| 123 | - return name.split("@")[0]; | |
| 124 | - } | |
| 125 | -} | |
| 111 | +} | |
| 126 | 112 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/heartbeat/IotComponentRegistry.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/heartbeat/IotNetComponentRegistry.java
| 1 | -package cn.iocoder.yudao.module.iot.component.core.heartbeat; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.heartbeat; | |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.map.MapUtil; | |
| 3 | 5 | import lombok.Data; |
| 4 | 6 | import lombok.extern.slf4j.Slf4j; |
| 5 | 7 | import org.springframework.stereotype.Component; |
| ... | ... | @@ -8,49 +10,51 @@ import java.util.Collection; |
| 8 | 10 | import java.util.Map; |
| 9 | 11 | import java.util.concurrent.ConcurrentHashMap; |
| 10 | 12 | |
| 11 | -// TODO @haohao:组件相关的注释,要不把 组件 => 网络组件?可能更容易理解? | |
| 12 | -// TODO @haohao:yudao-module-iot-components => yudao-module-iot-net-components 增加一个 net 如何?虽然会长一点,但是意思更精准? | |
| 13 | 13 | /** |
| 14 | - * IoT 组件注册表 | |
| 14 | + * IoT 网络组件注册表 | |
| 15 | 15 | * <p> |
| 16 | - * 用于管理多个组件的注册信息,解决多组件心跳问题 | |
| 16 | + * 用于管理多个网络组件的注册信息,解决多组件心跳问题 | |
| 17 | + * | |
| 18 | + * @author haohao | |
| 17 | 19 | */ |
| 18 | 20 | @Component |
| 19 | 21 | @Slf4j |
| 20 | -public class IotComponentRegistry { | |
| 22 | +public class IotNetComponentRegistry { | |
| 21 | 23 | |
| 22 | 24 | /** |
| 23 | - * 组件信息 | |
| 25 | + * 网络组件信息 | |
| 24 | 26 | */ |
| 25 | 27 | @Data |
| 26 | - public static class IotComponentInfo { | |
| 28 | + public static class IotNetComponentInfo { | |
| 27 | 29 | |
| 28 | 30 | /** |
| 29 | 31 | * 组件 Key |
| 30 | 32 | */ |
| 31 | 33 | private final String pluginKey; |
| 34 | + | |
| 32 | 35 | /** |
| 33 | 36 | * 主机 IP |
| 34 | 37 | */ |
| 35 | 38 | private final String hostIp; |
| 39 | + | |
| 36 | 40 | /** |
| 37 | 41 | * 下游端口 |
| 38 | 42 | */ |
| 39 | 43 | private final Integer downstreamPort; |
| 44 | + | |
| 40 | 45 | /** |
| 41 | 46 | * 进程 ID |
| 42 | 47 | */ |
| 43 | 48 | private final String processId; |
| 44 | - | |
| 45 | 49 | } |
| 46 | 50 | |
| 47 | 51 | /** |
| 48 | 52 | * 组件映射表:key 为组件 Key |
| 49 | 53 | */ |
| 50 | - private final Map<String, IotComponentInfo> components = new ConcurrentHashMap<>(); | |
| 54 | + private final Map<String, IotNetComponentInfo> components = new ConcurrentHashMap<>(); | |
| 51 | 55 | |
| 52 | 56 | /** |
| 53 | - * 注册组件 | |
| 57 | + * 注册网络组件 | |
| 54 | 58 | * |
| 55 | 59 | * @param pluginKey 组件 Key |
| 56 | 60 | * @param hostIp 主机 IP |
| ... | ... | @@ -58,38 +62,37 @@ public class IotComponentRegistry { |
| 58 | 62 | * @param processId 进程 ID |
| 59 | 63 | */ |
| 60 | 64 | public void registerComponent(String pluginKey, String hostIp, Integer downstreamPort, String processId) { |
| 61 | - log.info("[registerComponent][注册组件, pluginKey={}, hostIp={}, downstreamPort={}, processId={}]", | |
| 65 | + log.info("[registerComponent][注册网络组件, pluginKey={}, hostIp={}, downstreamPort={}, processId={}]", | |
| 62 | 66 | pluginKey, hostIp, downstreamPort, processId); |
| 63 | - components.put(pluginKey, new IotComponentInfo(pluginKey, hostIp, downstreamPort, processId)); | |
| 67 | + components.put(pluginKey, new IotNetComponentInfo(pluginKey, hostIp, downstreamPort, processId)); | |
| 64 | 68 | } |
| 65 | 69 | |
| 66 | 70 | /** |
| 67 | - * 注销组件 | |
| 71 | + * 注销网络组件 | |
| 68 | 72 | * |
| 69 | 73 | * @param pluginKey 组件 Key |
| 70 | 74 | */ |
| 71 | 75 | public void unregisterComponent(String pluginKey) { |
| 72 | - log.info("[unregisterComponent][注销组件, pluginKey={}]", pluginKey); | |
| 76 | + log.info("[unregisterComponent][注销网络组件, pluginKey={}]", pluginKey); | |
| 73 | 77 | components.remove(pluginKey); |
| 74 | 78 | } |
| 75 | 79 | |
| 76 | 80 | /** |
| 77 | - * 获取所有组件 | |
| 81 | + * 获取所有网络组件 | |
| 78 | 82 | * |
| 79 | 83 | * @return 所有组件集合 |
| 80 | 84 | */ |
| 81 | - public Collection<IotComponentInfo> getAllComponents() { | |
| 82 | - return components.values(); | |
| 85 | + public Collection<IotNetComponentInfo> getAllComponents() { | |
| 86 | + return CollUtil.isEmpty(components) ? CollUtil.newArrayList() : components.values(); | |
| 83 | 87 | } |
| 84 | 88 | |
| 85 | 89 | /** |
| 86 | - * 获取指定组件 | |
| 90 | + * 获取指定网络组件 | |
| 87 | 91 | * |
| 88 | 92 | * @param pluginKey 组件 Key |
| 89 | 93 | * @return 组件信息 |
| 90 | 94 | */ |
| 91 | - public IotComponentInfo getComponent(String pluginKey) { | |
| 92 | - return components.get(pluginKey); | |
| 95 | + public IotNetComponentInfo getComponent(String pluginKey) { | |
| 96 | + return MapUtil.isEmpty(components) ? null : components.get(pluginKey); | |
| 93 | 97 | } |
| 94 | - | |
| 95 | 98 | } |
| 96 | 99 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/message/IotAlinkMessage.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.message; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.IdUtil; | |
| 4 | +import cn.hutool.json.JSONObject; | |
| 5 | +import lombok.Builder; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +import java.util.Map; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * IoT Alink 消息模型 | |
| 12 | + * <p> | |
| 13 | + * 基于阿里云 Alink 协议规范实现的标准消息格式 | |
| 14 | + * | |
| 15 | + * @author haohao | |
| 16 | + */ | |
| 17 | +@Data | |
| 18 | +@Builder | |
| 19 | +public class IotAlinkMessage { | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 消息 ID | |
| 23 | + */ | |
| 24 | + private String id; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 协议版本 | |
| 28 | + */ | |
| 29 | + @Builder.Default | |
| 30 | + private String version = "1.0"; | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 消息方法 | |
| 34 | + */ | |
| 35 | + private String method; | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 消息参数 | |
| 39 | + */ | |
| 40 | + private Map<String, Object> params; | |
| 41 | + | |
| 42 | + /** | |
| 43 | + * 转换为 JSONObject | |
| 44 | + * | |
| 45 | + * @return JSONObject 对象 | |
| 46 | + */ | |
| 47 | + public JSONObject toJsonObject() { | |
| 48 | + JSONObject json = new JSONObject(); | |
| 49 | + json.set("id", id); | |
| 50 | + json.set("version", version); | |
| 51 | + json.set("method", method); | |
| 52 | + json.set("params", params != null ? params : new JSONObject()); | |
| 53 | + return json; | |
| 54 | + } | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * 转换为 JSON 字符串 | |
| 58 | + * | |
| 59 | + * @return JSON 字符串 | |
| 60 | + */ | |
| 61 | + public String toJsonString() { | |
| 62 | + return toJsonObject().toString(); | |
| 63 | + } | |
| 64 | + | |
| 65 | + /** | |
| 66 | + * 创建设备服务调用消息 | |
| 67 | + * | |
| 68 | + * @param requestId 请求 ID,为空时自动生成 | |
| 69 | + * @param serviceIdentifier 服务标识符 | |
| 70 | + * @param params 服务参数 | |
| 71 | + * @return Alink 消息对象 | |
| 72 | + */ | |
| 73 | + public static IotAlinkMessage createServiceInvokeMessage(String requestId, String serviceIdentifier, | |
| 74 | + Map<String, Object> params) { | |
| 75 | + return IotAlinkMessage.builder() | |
| 76 | + .id(requestId != null ? requestId : generateRequestId()) | |
| 77 | + .method("thing.service." + serviceIdentifier) | |
| 78 | + .params(params) | |
| 79 | + .build(); | |
| 80 | + } | |
| 81 | + | |
| 82 | + /** | |
| 83 | + * 创建设备属性设置消息 | |
| 84 | + * | |
| 85 | + * @param requestId 请求 ID,为空时自动生成 | |
| 86 | + * @param properties 设备属性 | |
| 87 | + * @return Alink 消息对象 | |
| 88 | + */ | |
| 89 | + public static IotAlinkMessage createPropertySetMessage(String requestId, Map<String, Object> properties) { | |
| 90 | + return IotAlinkMessage.builder() | |
| 91 | + .id(requestId != null ? requestId : generateRequestId()) | |
| 92 | + .method("thing.service.property.set") | |
| 93 | + .params(properties) | |
| 94 | + .build(); | |
| 95 | + } | |
| 96 | + | |
| 97 | + /** | |
| 98 | + * 创建设备属性获取消息 | |
| 99 | + * | |
| 100 | + * @param requestId 请求 ID,为空时自动生成 | |
| 101 | + * @param identifiers 要获取的属性标识符列表 | |
| 102 | + * @return Alink 消息对象 | |
| 103 | + */ | |
| 104 | + public static IotAlinkMessage createPropertyGetMessage(String requestId, String[] identifiers) { | |
| 105 | + JSONObject params = new JSONObject(); | |
| 106 | + params.set("identifiers", identifiers); | |
| 107 | + | |
| 108 | + return IotAlinkMessage.builder() | |
| 109 | + .id(requestId != null ? requestId : generateRequestId()) | |
| 110 | + .method("thing.service.property.get") | |
| 111 | + .params(params) | |
| 112 | + .build(); | |
| 113 | + } | |
| 114 | + | |
| 115 | + /** | |
| 116 | + * 创建设备配置设置消息 | |
| 117 | + * | |
| 118 | + * @param requestId 请求 ID,为空时自动生成 | |
| 119 | + * @param configs 设备配置 | |
| 120 | + * @return Alink 消息对象 | |
| 121 | + */ | |
| 122 | + public static IotAlinkMessage createConfigSetMessage(String requestId, Map<String, Object> configs) { | |
| 123 | + return IotAlinkMessage.builder() | |
| 124 | + .id(requestId != null ? requestId : generateRequestId()) | |
| 125 | + .method("thing.service.config.set") | |
| 126 | + .params(configs) | |
| 127 | + .build(); | |
| 128 | + } | |
| 129 | + | |
| 130 | + /** | |
| 131 | + * 创建设备 OTA 升级消息 | |
| 132 | + * | |
| 133 | + * @param requestId 请求 ID,为空时自动生成 | |
| 134 | + * @param otaInfo OTA 升级信息 | |
| 135 | + * @return Alink 消息对象 | |
| 136 | + */ | |
| 137 | + public static IotAlinkMessage createOtaUpgradeMessage(String requestId, Map<String, Object> otaInfo) { | |
| 138 | + return IotAlinkMessage.builder() | |
| 139 | + .id(requestId != null ? requestId : generateRequestId()) | |
| 140 | + .method("thing.service.ota.upgrade") | |
| 141 | + .params(otaInfo) | |
| 142 | + .build(); | |
| 143 | + } | |
| 144 | + | |
| 145 | + /** | |
| 146 | + * 生成请求 ID | |
| 147 | + * | |
| 148 | + * @return 请求 ID | |
| 149 | + */ | |
| 150 | + public static String generateRequestId() { | |
| 151 | + return IdUtil.fastSimpleUUID(); | |
| 152 | + } | |
| 153 | +} | |
| 0 | 154 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/pojo/IotStandardResponse.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/pojo/IotStandardResponse.java
| 1 | -package cn.iocoder.yudao.module.iot.component.core.pojo; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.pojo; | |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 3 | 4 | import lombok.Data; |
| 5 | +import lombok.experimental.Accessors; | |
| 4 | 6 | |
| 5 | 7 | /** |
| 6 | 8 | * IoT 标准协议响应实体类 |
| ... | ... | @@ -10,10 +12,11 @@ import lombok.Data; |
| 10 | 12 | * @author haohao |
| 11 | 13 | */ |
| 12 | 14 | @Data |
| 15 | +@Accessors(chain = true) | |
| 13 | 16 | public class IotStandardResponse { |
| 14 | 17 | |
| 15 | 18 | /** |
| 16 | - * 消息ID | |
| 19 | + * 消息 ID | |
| 17 | 20 | */ |
| 18 | 21 | private String id; |
| 19 | 22 | |
| ... | ... | @@ -45,7 +48,7 @@ public class IotStandardResponse { |
| 45 | 48 | /** |
| 46 | 49 | * 创建成功响应 |
| 47 | 50 | * |
| 48 | - * @param id 消息ID | |
| 51 | + * @param id 消息 ID | |
| 49 | 52 | * @param method 方法名 |
| 50 | 53 | * @return 成功响应 |
| 51 | 54 | */ |
| ... | ... | @@ -56,28 +59,37 @@ public class IotStandardResponse { |
| 56 | 59 | /** |
| 57 | 60 | * 创建成功响应 |
| 58 | 61 | * |
| 59 | - * @param id 消息ID | |
| 62 | + * @param id 消息 ID | |
| 60 | 63 | * @param method 方法名 |
| 61 | 64 | * @param data 响应数据 |
| 62 | 65 | * @return 成功响应 |
| 63 | 66 | */ |
| 64 | 67 | public static IotStandardResponse success(String id, String method, Object data) { |
| 65 | - return new IotStandardResponse().setId(id).setCode(200).setData(data).setMessage("success") | |
| 66 | - .setMethod(method).setVersion("1.0"); | |
| 68 | + return new IotStandardResponse() | |
| 69 | + .setId(id) | |
| 70 | + .setCode(200) | |
| 71 | + .setData(data) | |
| 72 | + .setMessage("success") | |
| 73 | + .setMethod(method) | |
| 74 | + .setVersion("1.0"); | |
| 67 | 75 | } |
| 68 | 76 | |
| 69 | 77 | /** |
| 70 | 78 | * 创建错误响应 |
| 71 | 79 | * |
| 72 | - * @param id 消息ID | |
| 80 | + * @param id 消息 ID | |
| 73 | 81 | * @param method 方法名 |
| 74 | 82 | * @param code 错误码 |
| 75 | 83 | * @param message 错误消息 |
| 76 | 84 | * @return 错误响应 |
| 77 | 85 | */ |
| 78 | 86 | public static IotStandardResponse error(String id, String method, Integer code, String message) { |
| 79 | - return new IotStandardResponse().setId(id).setCode(code).setData(null).setMessage(message) | |
| 80 | - .setMethod(method).setVersion("1.0"); | |
| 87 | + return new IotStandardResponse() | |
| 88 | + .setId(id) | |
| 89 | + .setCode(code) | |
| 90 | + .setData(null) | |
| 91 | + .setMessage(StrUtil.blankToDefault(message, "error")) | |
| 92 | + .setMethod(method) | |
| 93 | + .setVersion("1.0"); | |
| 81 | 94 | } |
| 82 | - | |
| 83 | 95 | } |
| 84 | 96 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/upstream/IotDeviceUpstreamClient.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/upstream/IotDeviceUpstreamClient.java
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-core/src/main/java/cn/iocoder/yudao/module/iot/component/core/util/IotPluginCommonUtils.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/util/IotNetComponentCommonUtils.java
| 1 | -package cn.iocoder.yudao.module.iot.component.core.util; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.core.util; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.IdUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.hutool.system.SystemUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 7 | -import cn.iocoder.yudao.module.iot.component.core.pojo.IotStandardResponse; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.core.pojo.IotStandardResponse; | |
| 8 | 8 | import io.vertx.core.http.HttpHeaders; |
| 9 | 9 | import io.vertx.ext.web.RoutingContext; |
| 10 | 10 | import org.springframework.http.MediaType; |
| 11 | 11 | |
| 12 | -// TODO @haohao:名字要改下哈。 | |
| 13 | 12 | /** |
| 14 | - * IoT 插件的通用工具类 | |
| 13 | + * IoT 网络组件的通用工具类 | |
| 15 | 14 | * |
| 16 | 15 | * @author 芋道源码 |
| 17 | 16 | */ |
| 18 | -public class IotPluginCommonUtils { | |
| 17 | +public class IotNetComponentCommonUtils { | |
| 19 | 18 | |
| 20 | 19 | /** |
| 21 | 20 | * 流程实例的进程编号 |
| 22 | 21 | */ |
| 23 | 22 | private static String processId; |
| 24 | 23 | |
| 24 | + /** | |
| 25 | + * 获取进程ID | |
| 26 | + * | |
| 27 | + * @return 进程ID | |
| 28 | + */ | |
| 25 | 29 | public static String getProcessId() { |
| 26 | 30 | if (StrUtil.isEmpty(processId)) { |
| 27 | 31 | initProcessId(); |
| ... | ... | @@ -29,12 +33,24 @@ public class IotPluginCommonUtils { |
| 29 | 33 | return processId; |
| 30 | 34 | } |
| 31 | 35 | |
| 36 | + /** | |
| 37 | + * 初始化进程ID | |
| 38 | + */ | |
| 32 | 39 | private synchronized static void initProcessId() { |
| 33 | 40 | processId = String.format("%s@%d@%s", // IP@PID@${uuid} |
| 34 | 41 | SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID(), IdUtil.fastSimpleUUID()); |
| 35 | 42 | } |
| 36 | 43 | |
| 37 | 44 | /** |
| 45 | + * 生成请求ID | |
| 46 | + * | |
| 47 | + * @return 生成的唯一请求ID | |
| 48 | + */ | |
| 49 | + public static String generateRequestId() { | |
| 50 | + return IdUtil.fastSimpleUUID(); | |
| 51 | + } | |
| 52 | + | |
| 53 | + /** | |
| 38 | 54 | * 将对象转换为JSON字符串后写入HTTP响应 |
| 39 | 55 | * |
| 40 | 56 | * @param routingContext 路由上下文 |
| ... | ... | @@ -51,20 +67,20 @@ public class IotPluginCommonUtils { |
| 51 | 67 | /** |
| 52 | 68 | * 生成标准JSON格式的响应并写入HTTP响应(基于IotStandardResponse) |
| 53 | 69 | * <p> |
| 54 | - * 推荐使用此方法,统一MQTT和HTTP的响应格式。使用方式: | |
| 70 | + * 推荐使用此方法,统一 MQTT 和 HTTP 的响应格式。使用方式: | |
| 55 | 71 | * |
| 56 | 72 | * <pre> |
| 57 | 73 | * // 成功响应 |
| 58 | 74 | * IotStandardResponse response = IotStandardResponse.success(requestId, method, data); |
| 59 | - * IotPluginCommonUtils.writeJsonResponse(routingContext, response); | |
| 75 | + * IotNetComponentCommonUtils.writeJsonResponse(routingContext, response); | |
| 60 | 76 | * |
| 61 | 77 | * // 错误响应 |
| 62 | 78 | * IotStandardResponse errorResponse = IotStandardResponse.error(requestId, method, code, message); |
| 63 | - * IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); | |
| 79 | + * IotNetComponentCommonUtils.writeJsonResponse(routingContext, errorResponse); | |
| 64 | 80 | * </pre> |
| 65 | 81 | * |
| 66 | 82 | * @param routingContext 路由上下文 |
| 67 | - * @param response IotStandardResponse响应对象 | |
| 83 | + * @param response IotStandardResponse 响应对象 | |
| 68 | 84 | */ |
| 69 | 85 | @SuppressWarnings("deprecation") |
| 70 | 86 | public static void writeJsonResponse(RoutingContext routingContext, IotStandardResponse response) { |
| ... | ... | @@ -73,5 +89,4 @@ public class IotPluginCommonUtils { |
| 73 | 89 | .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) |
| 74 | 90 | .end(JsonUtils.toJsonString(response)); |
| 75 | 91 | } |
| 76 | - | |
| 77 | -} | |
| 92 | +} | |
| 78 | 93 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/resources/META-INF/spring.factories
0 → 100644
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
0 → 100644
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/pom.xml renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/pom.xml
| ... | ... | @@ -3,23 +3,23 @@ |
| 3 | 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 4 | 4 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| 5 | 5 | <parent> |
| 6 | - <artifactId>yudao-module-iot-components</artifactId> | |
| 6 | + <artifactId>yudao-module-iot-net-components</artifactId> | |
| 7 | 7 | <groupId>cn.iocoder.boot</groupId> |
| 8 | 8 | <version>${revision}</version> |
| 9 | 9 | </parent> |
| 10 | 10 | <modelVersion>4.0.0</modelVersion> |
| 11 | - <artifactId>yudao-module-iot-component-emqx</artifactId> | |
| 11 | + <artifactId>yudao-module-iot-net-component-emqx</artifactId> | |
| 12 | 12 | <packaging>jar</packaging> |
| 13 | 13 | |
| 14 | 14 | <name>${project.artifactId}</name> |
| 15 | 15 | <description> |
| 16 | - 物联网组件 EMQX 模块 | |
| 16 | + 物联网网络组件 EMQX 模块 | |
| 17 | 17 | </description> |
| 18 | 18 | |
| 19 | 19 | <dependencies> |
| 20 | 20 | <dependency> |
| 21 | 21 | <groupId>cn.iocoder.boot</groupId> |
| 22 | - <artifactId>yudao-module-iot-component-core</artifactId> | |
| 22 | + <artifactId>yudao-module-iot-net-component-core</artifactId> | |
| 23 | 23 | <version>${revision}</version> |
| 24 | 24 | </dependency> |
| 25 | 25 | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/component/emqx/config/IotComponentEmqxAutoConfiguration.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxAutoConfiguration.java
| 1 | -package cn.iocoder.yudao.module.iot.component.emqx.config; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.emqx.config; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.IdUtil; |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 4 | 5 | import cn.hutool.system.SystemUtil; |
| 5 | 6 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 6 | -import cn.iocoder.yudao.module.iot.component.core.config.IotComponentCommonProperties; | |
| 7 | -import cn.iocoder.yudao.module.iot.component.core.downstream.IotDeviceDownstreamHandler; | |
| 8 | -import cn.iocoder.yudao.module.iot.component.core.heartbeat.IotComponentRegistry; | |
| 9 | -import cn.iocoder.yudao.module.iot.component.emqx.downstream.IotDeviceDownstreamHandlerImpl; | |
| 10 | -import cn.iocoder.yudao.module.iot.component.emqx.upstream.IotDeviceUpstreamServer; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.core.config.IotNetComponentCommonProperties; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 9 | +import cn.iocoder.yudao.module.iot.net.component.core.heartbeat.IotNetComponentRegistry; | |
| 10 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 11 | +import cn.iocoder.yudao.module.iot.net.component.emqx.downstream.IotDeviceDownstreamHandlerImpl; | |
| 12 | +import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.IotDeviceUpstreamServer; | |
| 11 | 13 | import io.vertx.core.Vertx; |
| 12 | 14 | import io.vertx.mqtt.MqttClient; |
| 13 | 15 | import io.vertx.mqtt.MqttClientOptions; |
| 14 | 16 | import lombok.extern.slf4j.Slf4j; |
| 17 | +import org.springframework.beans.factory.annotation.Qualifier; | |
| 15 | 18 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 16 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 17 | 20 | import org.springframework.boot.context.event.ApplicationStartedEvent; |
| ... | ... | @@ -20,42 +23,46 @@ import org.springframework.context.annotation.Bean; |
| 20 | 23 | import org.springframework.context.annotation.ComponentScan; |
| 21 | 24 | import org.springframework.context.event.EventListener; |
| 22 | 25 | |
| 23 | -import java.lang.management.ManagementFactory; | |
| 24 | - | |
| 25 | 26 | /** |
| 26 | - * IoT 组件 EMQX 的自动配置类 | |
| 27 | + * IoT 网络组件 EMQX 的自动配置类 | |
| 27 | 28 | * |
| 28 | 29 | * @author haohao |
| 29 | 30 | */ |
| 30 | 31 | @Slf4j |
| 31 | 32 | @AutoConfiguration |
| 32 | -@EnableConfigurationProperties(IotComponentEmqxProperties.class) | |
| 33 | +@EnableConfigurationProperties(IotNetComponentEmqxProperties.class) | |
| 33 | 34 | @ConditionalOnProperty(prefix = "yudao.iot.component.emqx", name = "enabled", havingValue = "true", matchIfMissing = false) |
| 34 | -// TODO @haohao:是不是不用扫 cn.iocoder.yudao.module.iot.component.core 拉,它尽量靠自动配置 | |
| 35 | 35 | @ComponentScan(basePackages = { |
| 36 | - "cn.iocoder.yudao.module.iot.component.core", // 核心包 | |
| 37 | - "cn.iocoder.yudao.module.iot.component.emqx" // EMQX 组件包 | |
| 36 | + "cn.iocoder.yudao.module.iot.net.component.emqx" // 只扫描 EMQX 组件包 | |
| 38 | 37 | }) |
| 39 | -public class IotComponentEmqxAutoConfiguration { | |
| 38 | +public class IotNetComponentEmqxAutoConfiguration { | |
| 40 | 39 | |
| 41 | 40 | /** |
| 42 | 41 | * 组件 key |
| 43 | 42 | */ |
| 44 | 43 | private static final String PLUGIN_KEY = "emqx"; |
| 45 | 44 | |
| 46 | - public IotComponentEmqxAutoConfiguration() { | |
| 47 | - // TODO @haohao:这个日志,融合到 initialize ? | |
| 48 | - log.info("[IotComponentEmqxAutoConfiguration][已启动]"); | |
| 45 | + public IotNetComponentEmqxAutoConfiguration() { | |
| 46 | + // 构造函数中不输出日志,移到 initialize 方法中 | |
| 49 | 47 | } |
| 50 | 48 | |
| 49 | + /** | |
| 50 | + * 初始化 EMQX 组件 | |
| 51 | + * | |
| 52 | + * @param event 应用启动事件 | |
| 53 | + */ | |
| 51 | 54 | @EventListener(ApplicationStartedEvent.class) |
| 52 | 55 | public void initialize(ApplicationStartedEvent event) { |
| 56 | + log.info("[IotNetComponentEmqxAutoConfiguration][开始初始化]"); | |
| 57 | + | |
| 53 | 58 | // 从应用上下文中获取需要的 Bean |
| 54 | - IotComponentRegistry componentRegistry = event.getApplicationContext().getBean(IotComponentRegistry.class); | |
| 55 | - IotComponentCommonProperties commonProperties = event.getApplicationContext().getBean(IotComponentCommonProperties.class); | |
| 59 | + IotNetComponentRegistry componentRegistry = event.getApplicationContext() | |
| 60 | + .getBean(IotNetComponentRegistry.class); | |
| 61 | + IotNetComponentCommonProperties commonProperties = event.getApplicationContext() | |
| 62 | + .getBean(IotNetComponentCommonProperties.class); | |
| 56 | 63 | |
| 57 | 64 | // 设置当前组件的核心标识 |
| 58 | - // TODO @haohao:如果多个组件,都去设置,会不会冲突哈? | |
| 65 | + // 注意:这里只为当前 EMQX 组件设置 pluginKey,不影响其他组件 | |
| 59 | 66 | commonProperties.setPluginKey(PLUGIN_KEY); |
| 60 | 67 | |
| 61 | 68 | // 将 EMQX 组件注册到组件注册表 |
| ... | ... | @@ -63,64 +70,60 @@ public class IotComponentEmqxAutoConfiguration { |
| 63 | 70 | PLUGIN_KEY, |
| 64 | 71 | SystemUtil.getHostInfo().getAddress(), |
| 65 | 72 | 0, // 内嵌模式固定为 0 |
| 66 | - getProcessId() | |
| 67 | - ); | |
| 73 | + IotNetComponentCommonUtils.getProcessId()); | |
| 68 | 74 | |
| 69 | 75 | log.info("[initialize][IoT EMQX 组件初始化完成]"); |
| 70 | 76 | } |
| 71 | 77 | |
| 72 | - // TODO @haohao:这个可能要注意,可能会有多个?冲突? | |
| 73 | - @Bean | |
| 78 | + /** | |
| 79 | + * 创建 Vert.x 实例 | |
| 80 | + */ | |
| 81 | + @Bean(name = "emqxVertx") | |
| 74 | 82 | public Vertx vertx() { |
| 75 | 83 | return Vertx.vertx(); |
| 76 | 84 | } |
| 77 | 85 | |
| 86 | + /** | |
| 87 | + * 创建 MQTT 客户端 | |
| 88 | + */ | |
| 78 | 89 | @Bean |
| 79 | - public MqttClient mqttClient(Vertx vertx, IotComponentEmqxProperties emqxProperties) { | |
| 80 | - // TODO @haohao:这个日志,要不要去掉,避免过多哈 | |
| 81 | - log.info("MQTT配置: host={}, port={}, username={}, ssl={}", | |
| 82 | - emqxProperties.getMqttHost(), emqxProperties.getMqttPort(), | |
| 83 | - emqxProperties.getMqttUsername(), emqxProperties.getMqttSsl()); | |
| 90 | + public MqttClient mqttClient(@Qualifier("emqxVertx") Vertx vertx, IotNetComponentEmqxProperties emqxProperties) { | |
| 91 | + // 使用 debug 级别记录详细配置,减少生产环境日志 | |
| 92 | + if (log.isDebugEnabled()) { | |
| 93 | + log.debug("MQTT 配置: host={}, port={}, username={}, ssl={}", | |
| 94 | + emqxProperties.getMqttHost(), emqxProperties.getMqttPort(), | |
| 95 | + emqxProperties.getMqttUsername(), emqxProperties.getMqttSsl()); | |
| 96 | + } else { | |
| 97 | + log.info("MQTT 连接至: {}:{}", emqxProperties.getMqttHost(), emqxProperties.getMqttPort()); | |
| 98 | + } | |
| 84 | 99 | |
| 85 | 100 | MqttClientOptions options = new MqttClientOptions() |
| 86 | 101 | .setClientId("yudao-iot-downstream-" + IdUtil.fastSimpleUUID()) |
| 87 | 102 | .setUsername(emqxProperties.getMqttUsername()) |
| 88 | 103 | .setPassword(emqxProperties.getMqttPassword()); |
| 89 | - // TODO @haohao:可以用 ObjUtil.default | |
| 90 | - if (emqxProperties.getMqttSsl() != null) { | |
| 91 | - options.setSsl(emqxProperties.getMqttSsl()); | |
| 92 | - } else { | |
| 93 | - options.setSsl(false); | |
| 94 | - } | |
| 104 | + // 设置 SSL 选项 | |
| 105 | + options.setSsl(ObjUtil.defaultIfNull(emqxProperties.getMqttSsl(), false)); | |
| 95 | 106 | return MqttClient.create(vertx, options); |
| 96 | 107 | } |
| 97 | 108 | |
| 109 | + /** | |
| 110 | + * 创建设备上行服务器 | |
| 111 | + */ | |
| 98 | 112 | @Bean(name = "emqxDeviceUpstreamServer", initMethod = "start", destroyMethod = "stop") |
| 99 | - public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi, | |
| 100 | - IotComponentEmqxProperties emqxProperties, | |
| 101 | - Vertx vertx, | |
| 102 | - MqttClient mqttClient, | |
| 103 | - IotComponentRegistry componentRegistry) { | |
| 113 | + public IotDeviceUpstreamServer deviceUpstreamServer( | |
| 114 | + IotDeviceUpstreamApi deviceUpstreamApi, | |
| 115 | + IotNetComponentEmqxProperties emqxProperties, | |
| 116 | + @Qualifier("emqxVertx") Vertx vertx, | |
| 117 | + MqttClient mqttClient, | |
| 118 | + IotNetComponentRegistry componentRegistry) { | |
| 104 | 119 | return new IotDeviceUpstreamServer(emqxProperties, deviceUpstreamApi, vertx, mqttClient, componentRegistry); |
| 105 | 120 | } |
| 106 | 121 | |
| 122 | + /** | |
| 123 | + * 创建设备下行处理器 | |
| 124 | + */ | |
| 107 | 125 | @Bean(name = "emqxDeviceDownstreamHandler") |
| 108 | 126 | public IotDeviceDownstreamHandler deviceDownstreamHandler(MqttClient mqttClient) { |
| 109 | 127 | return new IotDeviceDownstreamHandlerImpl(mqttClient); |
| 110 | 128 | } |
| 111 | - | |
| 112 | - // TODO @haohao:这个通用下一下哈。 | |
| 113 | - /** | |
| 114 | - * 获取当前进程ID | |
| 115 | - * | |
| 116 | - * @return 进程ID | |
| 117 | - */ | |
| 118 | - private String getProcessId() { | |
| 119 | - // 获取进程的 name | |
| 120 | - String name = ManagementFactory.getRuntimeMXBean().getName(); | |
| 121 | - // 分割名称,格式为 pid@hostname | |
| 122 | - String pid = name.split("@")[0]; | |
| 123 | - return pid; | |
| 124 | - } | |
| 125 | - | |
| 126 | 129 | } |
| 127 | 130 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/component/emqx/config/IotComponentEmqxProperties.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxProperties.java
| 1 | -package cn.iocoder.yudao.module.iot.component.emqx.config; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.emqx.config; | |
| 2 | 2 | |
| 3 | 3 | import jakarta.validation.constraints.NotBlank; |
| 4 | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | 6 | import lombok.Data; |
| 7 | 7 | import org.springframework.boot.context.properties.ConfigurationProperties; |
| 8 | +import org.springframework.validation.annotation.Validated; | |
| 8 | 9 | |
| 9 | 10 | /** |
| 10 | - * IoT EMQX 组件配置属性 | |
| 11 | + * IoT EMQX 网络组件配置属性 | |
| 12 | + * | |
| 13 | + * @author haohao | |
| 11 | 14 | */ |
| 12 | 15 | @ConfigurationProperties(prefix = "yudao.iot.component.emqx") |
| 13 | 16 | @Data |
| 14 | -public class IotComponentEmqxProperties { | |
| 17 | +@Validated | |
| 18 | +public class IotNetComponentEmqxProperties { | |
| 15 | 19 | |
| 16 | 20 | /** |
| 17 | 21 | * 是否启用 EMQX 组件 |
| 18 | 22 | */ |
| 19 | 23 | private Boolean enabled; |
| 20 | 24 | |
| 21 | - // TODO @haohao:一般中英文之间,加个空格哈,写作(注释)习惯。类似 MQTT 密码; | |
| 22 | 25 | /** |
| 23 | - * 服务主机 | |
| 26 | + * MQTT 服务主机 | |
| 24 | 27 | */ |
| 25 | 28 | @NotBlank(message = "MQTT 服务器主机不能为空") |
| 26 | 29 | private String mqttHost; |
| 30 | + | |
| 27 | 31 | /** |
| 28 | - * 服务端口 | |
| 32 | + * MQTT 服务端口 | |
| 29 | 33 | */ |
| 30 | 34 | @NotNull(message = "MQTT 服务器端口不能为空") |
| 31 | 35 | private Integer mqttPort; |
| 36 | + | |
| 32 | 37 | /** |
| 33 | - * 服务用户名 | |
| 38 | + * MQTT 服务用户名 | |
| 34 | 39 | */ |
| 35 | 40 | @NotBlank(message = "MQTT 服务器用户名不能为空") |
| 36 | 41 | private String mqttUsername; |
| 42 | + | |
| 37 | 43 | /** |
| 38 | - * 服务密码 | |
| 44 | + * MQTT 服务密码 | |
| 39 | 45 | */ |
| 40 | 46 | @NotBlank(message = "MQTT 服务器密码不能为空") |
| 41 | 47 | private String mqttPassword; |
| 48 | + | |
| 42 | 49 | /** |
| 43 | 50 | * 是否启用 SSL |
| 44 | 51 | */ |
| ... | ... | @@ -57,4 +64,17 @@ public class IotComponentEmqxProperties { |
| 57 | 64 | @NotNull(message = "认证端口不能为空") |
| 58 | 65 | private Integer authPort; |
| 59 | 66 | |
| 67 | + /** | |
| 68 | + * 重连延迟时间(毫秒) | |
| 69 | + * <p> | |
| 70 | + * 默认值:5000 毫秒 | |
| 71 | + */ | |
| 72 | + private Integer reconnectDelayMs = 5000; | |
| 73 | + | |
| 74 | + /** | |
| 75 | + * 连接超时时间(毫秒) | |
| 76 | + * <p> | |
| 77 | + * 默认值:10000 毫秒 | |
| 78 | + */ | |
| 79 | + private Integer connectionTimeoutMs = 10000; | |
| 60 | 80 | } |
| 61 | 81 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/component/emqx/downstream/IotDeviceDownstreamHandlerImpl.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/downstream/IotDeviceDownstreamHandlerImpl.java
| 1 | -package cn.iocoder.yudao.module.iot.component.emqx.downstream; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.emqx.downstream; | |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.IdUtil; | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | 4 | import cn.hutool.json.JSONObject; |
| 5 | 5 | import cn.hutool.json.JSONUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; |
| 8 | -import cn.iocoder.yudao.module.iot.component.core.downstream.IotDeviceDownstreamHandler; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.core.constants.IotDeviceTopicEnum; | |
| 9 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 10 | +import cn.iocoder.yudao.module.iot.net.component.core.message.IotAlinkMessage; | |
| 11 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 9 | 12 | import io.netty.handler.codec.mqtt.MqttQoS; |
| 10 | 13 | import io.vertx.core.buffer.Buffer; |
| 11 | 14 | import io.vertx.mqtt.MqttClient; |
| 12 | 15 | import lombok.extern.slf4j.Slf4j; |
| 13 | 16 | |
| 14 | -import java.util.Map; | |
| 15 | - | |
| 16 | 17 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.MQTT_TOPIC_ILLEGAL; |
| 17 | 18 | |
| 18 | 19 | /** |
| 19 | - * EMQX 插件的 {@link IotDeviceDownstreamHandler} 实现类 | |
| 20 | + * EMQX 网络组件的 {@link IotDeviceDownstreamHandler} 实现类 | |
| 20 | 21 | * |
| 21 | 22 | * @author 芋道源码 |
| 22 | 23 | */ |
| 23 | 24 | @Slf4j |
| 24 | 25 | public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { |
| 25 | 26 | |
| 26 | - private static final String SYS_TOPIC_PREFIX = "/sys/"; | |
| 27 | - | |
| 28 | - // TODO @haohao:是不是可以类似 IotDeviceConfigSetVertxHandler 的建议,抽到统一的枚举类 | |
| 29 | - // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。;回复 都使用 Alink 格式,方便后续扩展。 | |
| 30 | - // 设备服务调用 标准 JSON | |
| 31 | - // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} | |
| 32 | - // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier}_reply | |
| 33 | - private static final String SERVICE_TOPIC_PREFIX = "/thing/service/"; | |
| 34 | - | |
| 35 | - // 设置设备属性 标准 JSON | |
| 36 | - // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set | |
| 37 | - // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply | |
| 38 | - private static final String PROPERTY_SET_TOPIC = "/thing/service/property/set"; | |
| 39 | - | |
| 27 | + /** | |
| 28 | + * MQTT 客户端 | |
| 29 | + */ | |
| 40 | 30 | private final MqttClient mqttClient; |
| 41 | 31 | |
| 42 | 32 | /** |
| 43 | 33 | * 构造函数 |
| 44 | 34 | * |
| 45 | - * @param mqttClient MQTT客户端 | |
| 35 | + * @param mqttClient MQTT 客户端 | |
| 46 | 36 | */ |
| 47 | 37 | public IotDeviceDownstreamHandlerImpl(MqttClient mqttClient) { |
| 48 | 38 | this.mqttClient = mqttClient; |
| ... | ... | @@ -60,12 +50,17 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 60 | 50 | |
| 61 | 51 | try { |
| 62 | 52 | // 构建请求主题 |
| 63 | - String topic = buildServiceTopic(reqDTO.getProductKey(), reqDTO.getDeviceName(), reqDTO.getIdentifier()); | |
| 53 | + String topic = IotDeviceTopicEnum.buildServiceTopic(reqDTO.getProductKey(), reqDTO.getDeviceName(), | |
| 54 | + reqDTO.getIdentifier()); | |
| 55 | + | |
| 64 | 56 | // 构建请求消息 |
| 65 | - String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); | |
| 66 | - JSONObject request = buildServiceRequest(requestId, reqDTO.getIdentifier(), reqDTO.getParams()); | |
| 57 | + String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId() | |
| 58 | + : IotNetComponentCommonUtils.generateRequestId(); | |
| 59 | + IotAlinkMessage message = IotAlinkMessage.createServiceInvokeMessage( | |
| 60 | + requestId, reqDTO.getIdentifier(), reqDTO.getParams()); | |
| 61 | + | |
| 67 | 62 | // 发送消息 |
| 68 | - publishMessage(topic, request); | |
| 63 | + publishMessage(topic, message.toJsonObject()); | |
| 69 | 64 | |
| 70 | 65 | log.info("[invokeService][调用设备服务成功][requestId: {}][topic: {}]", requestId, topic); |
| 71 | 66 | return CommonResult.success(true); |
| ... | ... | @@ -77,13 +72,15 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 77 | 72 | |
| 78 | 73 | @Override |
| 79 | 74 | public CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { |
| 75 | + // 暂未实现,返回成功 | |
| 80 | 76 | return CommonResult.success(true); |
| 81 | 77 | } |
| 82 | 78 | |
| 83 | 79 | @Override |
| 84 | 80 | public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO reqDTO) { |
| 85 | - // 验证参数 | |
| 86 | 81 | log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); |
| 82 | + | |
| 83 | + // 验证参数 | |
| 87 | 84 | if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null) { |
| 88 | 85 | log.error("[setProperty][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); |
| 89 | 86 | return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); |
| ... | ... | @@ -91,12 +88,15 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 91 | 88 | |
| 92 | 89 | try { |
| 93 | 90 | // 构建请求主题 |
| 94 | - String topic = buildPropertySetTopic(reqDTO.getProductKey(), reqDTO.getDeviceName()); | |
| 91 | + String topic = IotDeviceTopicEnum.buildPropertySetTopic(reqDTO.getProductKey(), reqDTO.getDeviceName()); | |
| 92 | + | |
| 95 | 93 | // 构建请求消息 |
| 96 | - String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); | |
| 97 | - JSONObject request = buildPropertySetRequest(requestId, reqDTO.getProperties()); | |
| 94 | + String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId() | |
| 95 | + : IotNetComponentCommonUtils.generateRequestId(); | |
| 96 | + IotAlinkMessage message = IotAlinkMessage.createPropertySetMessage(requestId, reqDTO.getProperties()); | |
| 97 | + | |
| 98 | 98 | // 发送消息 |
| 99 | - publishMessage(topic, request); | |
| 99 | + publishMessage(topic, message.toJsonObject()); | |
| 100 | 100 | |
| 101 | 101 | log.info("[setProperty][设置设备属性成功][requestId: {}][topic: {}]", requestId, topic); |
| 102 | 102 | return CommonResult.success(true); |
| ... | ... | @@ -108,54 +108,21 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 108 | 108 | |
| 109 | 109 | @Override |
| 110 | 110 | public CommonResult<Boolean> setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) { |
| 111 | + // 暂未实现,返回成功 | |
| 111 | 112 | return CommonResult.success(true); |
| 112 | 113 | } |
| 113 | 114 | |
| 114 | 115 | @Override |
| 115 | 116 | public CommonResult<Boolean> upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) { |
| 117 | + // 暂未实现,返回成功 | |
| 116 | 118 | return CommonResult.success(true); |
| 117 | 119 | } |
| 118 | 120 | |
| 119 | 121 | /** |
| 120 | - * 构建服务调用主题 | |
| 121 | - */ | |
| 122 | - private String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) { | |
| 123 | - return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + SERVICE_TOPIC_PREFIX + serviceIdentifier; | |
| 124 | - } | |
| 125 | - | |
| 126 | - /** | |
| 127 | - * 构建属性设置主题 | |
| 128 | - */ | |
| 129 | - private String buildPropertySetTopic(String productKey, String deviceName) { | |
| 130 | - return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + PROPERTY_SET_TOPIC; | |
| 131 | - } | |
| 132 | - | |
| 133 | - // TODO @haohao:这个,后面搞个对象,会不会好点哈? | |
| 134 | - | |
| 135 | - /** | |
| 136 | - * 构建服务调用请求 | |
| 137 | - */ | |
| 138 | - private JSONObject buildServiceRequest(String requestId, String serviceIdentifier, Map<String, Object> params) { | |
| 139 | - return new JSONObject() | |
| 140 | - .set("id", requestId) | |
| 141 | - .set("version", "1.0") | |
| 142 | - .set("method", "thing.service." + serviceIdentifier) | |
| 143 | - .set("params", params != null ? params : new JSONObject()); | |
| 144 | - } | |
| 145 | - | |
| 146 | - /** | |
| 147 | - * 构建属性设置请求 | |
| 148 | - */ | |
| 149 | - private JSONObject buildPropertySetRequest(String requestId, Map<String, Object> properties) { | |
| 150 | - return new JSONObject() | |
| 151 | - .set("id", requestId) | |
| 152 | - .set("version", "1.0") | |
| 153 | - .set("method", "thing.service.property.set") | |
| 154 | - .set("params", properties); | |
| 155 | - } | |
| 156 | - | |
| 157 | - /** | |
| 158 | 122 | * 发布 MQTT 消息 |
| 123 | + * | |
| 124 | + * @param topic 主题 | |
| 125 | + * @param payload 消息内容 | |
| 159 | 126 | */ |
| 160 | 127 | private void publishMessage(String topic, JSONObject payload) { |
| 161 | 128 | mqttClient.publish( |
| ... | ... | @@ -166,13 +133,4 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 166 | 133 | false); |
| 167 | 134 | log.info("[publishMessage][发送消息成功][topic: {}][payload: {}]", topic, payload); |
| 168 | 135 | } |
| 169 | - | |
| 170 | - // TODO @haohao:这个要不抽到 IotPluginCommonUtils 里? | |
| 171 | - /** | |
| 172 | - * 生成请求 ID | |
| 173 | - */ | |
| 174 | - private String generateRequestId() { | |
| 175 | - return IdUtil.fastSimpleUUID(); | |
| 176 | - } | |
| 177 | - | |
| 178 | 136 | } |
| 179 | 137 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/component/emqx/upstream/IotDeviceUpstreamServer.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/IotDeviceUpstreamServer.java
| 1 | -package cn.iocoder.yudao.module.iot.component.emqx.upstream; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.emqx.upstream; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.ArrayUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 6 | -import cn.iocoder.yudao.module.iot.component.core.heartbeat.IotComponentRegistry; | |
| 7 | -import cn.iocoder.yudao.module.iot.component.emqx.config.IotComponentEmqxProperties; | |
| 8 | -import cn.iocoder.yudao.module.iot.component.emqx.upstream.router.IotDeviceAuthVertxHandler; | |
| 9 | -import cn.iocoder.yudao.module.iot.component.emqx.upstream.router.IotDeviceMqttMessageHandler; | |
| 10 | -import cn.iocoder.yudao.module.iot.component.emqx.upstream.router.IotDeviceWebhookVertxHandler; | |
| 6 | +import cn.iocoder.yudao.module.iot.net.component.core.heartbeat.IotNetComponentRegistry; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.emqx.config.IotNetComponentEmqxProperties; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceAuthVertxHandler; | |
| 9 | +import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceMqttMessageHandler; | |
| 10 | +import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceWebhookVertxHandler; | |
| 11 | 11 | import io.netty.handler.codec.mqtt.MqttQoS; |
| 12 | 12 | import io.vertx.core.Future; |
| 13 | 13 | import io.vertx.core.Vertx; |
| ... | ... | @@ -21,7 +21,7 @@ import java.util.concurrent.CompletableFuture; |
| 21 | 21 | import java.util.concurrent.TimeUnit; |
| 22 | 22 | |
| 23 | 23 | /** |
| 24 | - * IoT 设备下行服务端,接收来自 device 设备的请求,转发给 server 服务器 | |
| 24 | + * IoT 设备上行服务端,接收来自 device 设备的请求,转发给 server 服务器 | |
| 25 | 25 | * <p> |
| 26 | 26 | * 协议:HTTP、MQTT |
| 27 | 27 | * |
| ... | ... | @@ -30,15 +30,6 @@ import java.util.concurrent.TimeUnit; |
| 30 | 30 | @Slf4j |
| 31 | 31 | public class IotDeviceUpstreamServer { |
| 32 | 32 | |
| 33 | - // TODO @haohao:抽到 IotComponentEmqxProperties 里? | |
| 34 | - /** | |
| 35 | - * 重连延迟时间(毫秒) | |
| 36 | - */ | |
| 37 | - private static final int RECONNECT_DELAY_MS = 5000; | |
| 38 | - /** | |
| 39 | - * 连接超时时间(毫秒) | |
| 40 | - */ | |
| 41 | - private static final int CONNECTION_TIMEOUT_MS = 10000; | |
| 42 | 33 | /** |
| 43 | 34 | * 默认 QoS 级别 |
| 44 | 35 | */ |
| ... | ... | @@ -47,20 +38,20 @@ public class IotDeviceUpstreamServer { |
| 47 | 38 | private final Vertx vertx; |
| 48 | 39 | private final HttpServer server; |
| 49 | 40 | private final MqttClient client; |
| 50 | - private final IotComponentEmqxProperties emqxProperties; | |
| 41 | + private final IotNetComponentEmqxProperties emqxProperties; | |
| 51 | 42 | private final IotDeviceMqttMessageHandler mqttMessageHandler; |
| 52 | - private final IotComponentRegistry componentRegistry; | |
| 43 | + private final IotNetComponentRegistry componentRegistry; | |
| 53 | 44 | |
| 54 | 45 | /** |
| 55 | 46 | * 服务运行状态标志 |
| 56 | 47 | */ |
| 57 | 48 | private volatile boolean isRunning = false; |
| 58 | 49 | |
| 59 | - public IotDeviceUpstreamServer(IotComponentEmqxProperties emqxProperties, | |
| 50 | + public IotDeviceUpstreamServer(IotNetComponentEmqxProperties emqxProperties, | |
| 60 | 51 | IotDeviceUpstreamApi deviceUpstreamApi, |
| 61 | 52 | Vertx vertx, |
| 62 | 53 | MqttClient client, |
| 63 | - IotComponentRegistry componentRegistry) { | |
| 54 | + IotNetComponentRegistry componentRegistry) { | |
| 64 | 55 | this.vertx = vertx; |
| 65 | 56 | this.emqxProperties = emqxProperties; |
| 66 | 57 | this.client = client; |
| ... | ... | @@ -70,8 +61,7 @@ public class IotDeviceUpstreamServer { |
| 70 | 61 | Router router = Router.router(vertx); |
| 71 | 62 | router.route().handler(BodyHandler.create()); // 处理 Body |
| 72 | 63 | router.post(IotDeviceAuthVertxHandler.PATH) |
| 73 | - // TODO @haohao:疑问,mqtt 的认证,需要通过 http 呀? | |
| 74 | - // 回复:MQTT 认证不必须通过 HTTP 进行,但 HTTP 认证是 EMQX 等 MQTT 服务器支持的一种灵活的认证方式 | |
| 64 | + // MQTT 认证不必须通过 HTTP 进行,但 HTTP 认证是 EMQX 等 MQTT 服务器支持的一种灵活的认证方式 | |
| 75 | 65 | .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); |
| 76 | 66 | // 添加 Webhook 处理器,用于处理设备连接和断开连接事件 |
| 77 | 67 | router.post(IotDeviceWebhookVertxHandler.PATH) |
| ... | ... | @@ -91,15 +81,20 @@ public class IotDeviceUpstreamServer { |
| 91 | 81 | } |
| 92 | 82 | log.info("[start][开始启动服务]"); |
| 93 | 83 | |
| 94 | - // 检查authPort是否为null | |
| 84 | + // 检查 authPort 是否为 null | |
| 95 | 85 | Integer authPort = emqxProperties.getAuthPort(); |
| 96 | 86 | if (authPort == null) { |
| 97 | - log.warn("[start][authPort为null,使用默认端口8080]"); | |
| 87 | + log.warn("[start][authPort 为 null,使用默认端口 8080]"); | |
| 98 | 88 | authPort = 8080; // 默认端口 |
| 99 | 89 | } |
| 100 | 90 | |
| 91 | + // 获取连接超时时间 | |
| 92 | + int connectionTimeoutMs = emqxProperties.getConnectionTimeoutMs() != null | |
| 93 | + ? emqxProperties.getConnectionTimeoutMs() | |
| 94 | + : 10000; | |
| 95 | + | |
| 101 | 96 | // 1. 启动 HTTP 服务器 |
| 102 | - final Integer finalAuthPort = authPort; // 为lambda表达式创建final变量 | |
| 97 | + final Integer finalAuthPort = authPort; // 为 lambda 表达式创建 final 变量 | |
| 103 | 98 | CompletableFuture<Void> httpFuture = server.listen(finalAuthPort) |
| 104 | 99 | .toCompletionStage() |
| 105 | 100 | .toCompletableFuture() |
| ... | ... | @@ -115,13 +110,13 @@ public class IotDeviceUpstreamServer { |
| 115 | 110 | log.warn("[closeHandler][MQTT 连接已断开,准备重连]"); |
| 116 | 111 | reconnectWithDelay(); |
| 117 | 112 | }); |
| 118 | - // 2. 设置 MQTT 消息处理器 | |
| 113 | + // 2.2 设置 MQTT 消息处理器 | |
| 119 | 114 | setupMessageHandler(); |
| 120 | 115 | }); |
| 121 | 116 | |
| 122 | 117 | // 3. 等待所有服务启动完成 |
| 123 | 118 | CompletableFuture.allOf(httpFuture, mqttFuture) |
| 124 | - .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) | |
| 119 | + .orTimeout(connectionTimeoutMs, TimeUnit.MILLISECONDS) | |
| 125 | 120 | .whenComplete((result, error) -> { |
| 126 | 121 | if (error != null) { |
| 127 | 122 | log.error("[start][服务启动失败]", error); |
| ... | ... | @@ -149,7 +144,12 @@ public class IotDeviceUpstreamServer { |
| 149 | 144 | return; |
| 150 | 145 | } |
| 151 | 146 | |
| 152 | - vertx.setTimer(RECONNECT_DELAY_MS, id -> { | |
| 147 | + // 获取重连延迟时间 | |
| 148 | + int reconnectDelayMs = emqxProperties.getReconnectDelayMs() != null | |
| 149 | + ? emqxProperties.getReconnectDelayMs() | |
| 150 | + : 5000; | |
| 151 | + | |
| 152 | + vertx.setTimer(reconnectDelayMs, id -> { | |
| 153 | 153 | log.info("[reconnectWithDelay][开始重新连接 MQTT]"); |
| 154 | 154 | connectMqtt(); |
| 155 | 155 | }); |
| ... | ... | @@ -158,14 +158,14 @@ public class IotDeviceUpstreamServer { |
| 158 | 158 | /** |
| 159 | 159 | * 连接 MQTT Broker 并订阅主题 |
| 160 | 160 | * |
| 161 | - * @return 连接结果的Future | |
| 161 | + * @return 连接结果的 Future | |
| 162 | 162 | */ |
| 163 | 163 | private Future<Void> connectMqtt() { |
| 164 | 164 | // 检查必要的 MQTT 配置 |
| 165 | 165 | String host = emqxProperties.getMqttHost(); |
| 166 | 166 | Integer port = emqxProperties.getMqttPort(); |
| 167 | - if (host == null) { | |
| 168 | - String msg = "[connectMqtt][MQTT Host 为 null,无法连接]"; | |
| 167 | + if (StrUtil.isBlank(host)) { | |
| 168 | + String msg = "[connectMqtt][MQTT Host 为空,无法连接]"; | |
| 169 | 169 | log.error(msg); |
| 170 | 170 | return Future.failedFuture(new IllegalStateException(msg)); |
| 171 | 171 | } |
| ... | ... | @@ -177,11 +177,11 @@ public class IotDeviceUpstreamServer { |
| 177 | 177 | final Integer finalPort = port; |
| 178 | 178 | return client.connect(finalPort, host) |
| 179 | 179 | .compose(connAck -> { |
| 180 | - log.info("[connectMqtt][MQTT客户端连接成功]"); | |
| 180 | + log.info("[connectMqtt][MQTT 客户端连接成功]"); | |
| 181 | 181 | return subscribeToTopics(); |
| 182 | 182 | }) |
| 183 | 183 | .recover(error -> { |
| 184 | - log.error("[connectMqtt][连接MQTT Broker失败:]", error); | |
| 184 | + log.error("[connectMqtt][连接 MQTT Broker 失败:]", error); | |
| 185 | 185 | reconnectWithDelay(); |
| 186 | 186 | return Future.failedFuture(error); |
| 187 | 187 | }); |
| ... | ... | @@ -198,62 +198,67 @@ public class IotDeviceUpstreamServer { |
| 198 | 198 | log.warn("[subscribeToTopics][未配置 MQTT 主题或为 null,使用默认主题]"); |
| 199 | 199 | topics = new String[]{"/device/#"}; // 默认订阅所有设备上下行主题 |
| 200 | 200 | } |
| 201 | - log.info("[subscribeToTopics][开始订阅设备上行消息主题]"); | |
| 202 | 201 | |
| 203 | - Future<Void> compositeFuture = Future.succeededFuture(); | |
| 202 | + // 使用协调器追踪多个 Future 的完成状态 | |
| 203 | + Future<Void> result = Future.succeededFuture(); | |
| 204 | 204 | for (String topic : topics) { |
| 205 | - String trimmedTopic = StrUtil.trim(topic); | |
| 206 | - if (StrUtil.isBlank(trimmedTopic)) { | |
| 205 | + if (StrUtil.isBlank(topic)) { | |
| 206 | + log.warn("[subscribeToTopics][跳过空主题]"); | |
| 207 | 207 | continue; |
| 208 | 208 | } |
| 209 | - compositeFuture = compositeFuture.compose(v -> client.subscribe(trimmedTopic, DEFAULT_QOS.value()) | |
| 209 | + | |
| 210 | + result = result.compose(v -> client.subscribe(topic, DEFAULT_QOS.value()) | |
| 210 | 211 | .<Void>map(ack -> { |
| 211 | - log.info("[subscribeToTopics][成功订阅主题: {}]", trimmedTopic); | |
| 212 | + log.info("[subscribeToTopics][订阅主题成功: {}]", topic); | |
| 212 | 213 | return null; |
| 213 | 214 | }) |
| 214 | - .recover(error -> { | |
| 215 | - log.error("[subscribeToTopics][订阅主题失败: {}]", trimmedTopic, error); | |
| 216 | - return Future.<Void>succeededFuture(); // 继续订阅其他主题 | |
| 215 | + .recover(err -> { | |
| 216 | + log.error("[subscribeToTopics][订阅主题失败: {}]", topic, err); | |
| 217 | + return Future.failedFuture(err); | |
| 217 | 218 | })); |
| 218 | 219 | } |
| 219 | - return compositeFuture; | |
| 220 | + return result; | |
| 220 | 221 | } |
| 221 | 222 | |
| 222 | 223 | /** |
| 223 | - * 停止所有服务 | |
| 224 | + * 停止服务 | |
| 224 | 225 | */ |
| 225 | 226 | public void stop() { |
| 226 | 227 | if (!isRunning) { |
| 227 | - log.warn("[stop][服务未运行,无需停止]"); | |
| 228 | + log.warn("[stop][服务已经停止,无需再次停止]"); | |
| 228 | 229 | return; |
| 229 | 230 | } |
| 230 | - log.info("[stop][开始关闭服务]"); | |
| 231 | - isRunning = false; | |
| 231 | + log.info("[stop][开始停止服务]"); | |
| 232 | + | |
| 233 | + // 1. 取消 MQTT 主题订阅 | |
| 234 | + if (client.isConnected()) { | |
| 235 | + for (String topic : emqxProperties.getMqttTopics()) { | |
| 236 | + try { | |
| 237 | + client.unsubscribe(topic); | |
| 238 | + } catch (Exception e) { | |
| 239 | + log.warn("[stop][取消订阅主题异常: {}]", topic, e); | |
| 240 | + } | |
| 241 | + } | |
| 242 | + } | |
| 232 | 243 | |
| 244 | + // 2. 关闭 MQTT 客户端 | |
| 233 | 245 | try { |
| 234 | - CompletableFuture<Void> serverFuture = server != null | |
| 235 | - ? server.close().toCompletionStage().toCompletableFuture() | |
| 236 | - : CompletableFuture.completedFuture(null); | |
| 237 | - CompletableFuture<Void> clientFuture = client != null | |
| 238 | - ? client.disconnect().toCompletionStage().toCompletableFuture() | |
| 239 | - : CompletableFuture.completedFuture(null); | |
| 240 | - CompletableFuture<Void> vertxFuture = vertx != null | |
| 241 | - ? vertx.close().toCompletionStage().toCompletableFuture() | |
| 242 | - : CompletableFuture.completedFuture(null); | |
| 246 | + if (client.isConnected()) { | |
| 247 | + client.disconnect(); | |
| 248 | + } | |
| 249 | + } catch (Exception e) { | |
| 250 | + log.warn("[stop][关闭 MQTT 客户端异常]", e); | |
| 251 | + } | |
| 243 | 252 | |
| 244 | - // 等待所有资源关闭 | |
| 245 | - CompletableFuture.allOf(serverFuture, clientFuture, vertxFuture) | |
| 246 | - .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) | |
| 247 | - .whenComplete((result, error) -> { | |
| 248 | - if (error != null) { | |
| 249 | - log.error("[stop][服务关闭过程中发生异常]", error); | |
| 250 | - } else { | |
| 251 | - log.info("[stop][所有服务关闭完成]"); | |
| 252 | - } | |
| 253 | - }); | |
| 253 | + // 3. 关闭 HTTP 服务器 | |
| 254 | + try { | |
| 255 | + server.close(); | |
| 254 | 256 | } catch (Exception e) { |
| 255 | - log.error("[stop][关闭服务异常]", e); | |
| 256 | - throw new RuntimeException("关闭 IoT 设备上行服务失败", e); | |
| 257 | + log.warn("[stop][关闭 HTTP 服务器异常]", e); | |
| 257 | 258 | } |
| 259 | + | |
| 260 | + // 4. 更新状态 | |
| 261 | + isRunning = false; | |
| 262 | + log.info("[stop][服务已停止]"); | |
| 258 | 263 | } |
| 259 | 264 | } |
| 260 | 265 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/component/emqx/upstream/router/IotDeviceAuthVertxHandler.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceAuthVertxHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.component.emqx.upstream.router; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEmqxAuthReqDTO; |
| 6 | -import cn.iocoder.yudao.module.iot.component.core.util.IotPluginCommonUtils; | |
| 6 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 7 | 7 | import io.vertx.core.Handler; |
| 8 | 8 | import io.vertx.core.json.JsonObject; |
| 9 | 9 | import io.vertx.ext.web.RoutingContext; |
| ... | ... | @@ -47,17 +47,17 @@ public class IotDeviceAuthVertxHandler implements Handler<RoutingContext> { |
| 47 | 47 | CommonResult<Boolean> authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); |
| 48 | 48 | if (authResult.getCode() != 0 || !authResult.getData()) { |
| 49 | 49 | // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求 |
| 50 | - IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); | |
| 50 | + IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); | |
| 51 | 51 | return; |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | // 响应结果 |
| 55 | 55 | // 注意:这里必须返回 {"result": "allow"} 格式,以符合 EMQX 认证插件的要求 |
| 56 | - IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "allow")); | |
| 56 | + IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "allow")); | |
| 57 | 57 | } catch (Exception e) { |
| 58 | 58 | log.error("[handle][EMQX 认证异常]", e); |
| 59 | 59 | // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求 |
| 60 | - IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); | |
| 60 | + IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); | |
| 61 | 61 | } |
| 62 | 62 | } |
| 63 | 63 | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/component/emqx/upstream/router/IotDeviceMqttMessageHandler.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceMqttMessageHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.component.emqx.upstream.router; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | 4 | import cn.hutool.json.JSONObject; |
| ... | ... | @@ -7,8 +7,9 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 8 | 8 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; |
| 9 | 9 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; |
| 10 | -import cn.iocoder.yudao.module.iot.component.core.pojo.IotStandardResponse; | |
| 11 | -import cn.iocoder.yudao.module.iot.component.core.util.IotPluginCommonUtils; | |
| 10 | +import cn.iocoder.yudao.module.iot.net.component.core.constants.IotDeviceTopicEnum; | |
| 11 | +import cn.iocoder.yudao.module.iot.net.component.core.pojo.IotStandardResponse; | |
| 12 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 12 | 13 | import io.netty.handler.codec.mqtt.MqttQoS; |
| 13 | 14 | import io.vertx.core.buffer.Buffer; |
| 14 | 15 | import io.vertx.mqtt.MqttClient; |
| ... | ... | @@ -23,25 +24,12 @@ import java.util.Map; |
| 23 | 24 | /** |
| 24 | 25 | * IoT 设备 MQTT 消息处理器 |
| 25 | 26 | * <p> |
| 26 | - * 参考:<a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">设备属性、事件、服务</a> | |
| 27 | + * 参考:<a href= | |
| 28 | + * "https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">设备属性、事件、服务</a> | |
| 27 | 29 | */ |
| 28 | 30 | @Slf4j |
| 29 | 31 | public class IotDeviceMqttMessageHandler { |
| 30 | 32 | |
| 31 | - // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈;回复 都使用 Alink 格式,方便后续扩展。 | |
| 32 | - // 设备上报属性 标准 JSON | |
| 33 | - // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post | |
| 34 | - // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply | |
| 35 | - | |
| 36 | - // 设备上报事件 标准 JSON | |
| 37 | - // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post | |
| 38 | - // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply | |
| 39 | - | |
| 40 | - private static final String SYS_TOPIC_PREFIX = "/sys/"; | |
| 41 | - private static final String PROPERTY_POST_TOPIC = "/thing/event/property/post"; | |
| 42 | - private static final String EVENT_POST_TOPIC_PREFIX = "/thing/event/"; | |
| 43 | - private static final String EVENT_POST_TOPIC_SUFFIX = "/post"; | |
| 44 | - private static final String REPLY_SUFFIX = "_reply"; | |
| 45 | 33 | private static final String PROPERTY_METHOD = "thing.event.property.post"; |
| 46 | 34 | private static final String EVENT_METHOD_PREFIX = "thing.event."; |
| 47 | 35 | private static final String EVENT_METHOD_SUFFIX = ".post"; |
| ... | ... | @@ -83,20 +71,21 @@ public class IotDeviceMqttMessageHandler { |
| 83 | 71 | */ |
| 84 | 72 | private void handleMessage(String topic, String payload) { |
| 85 | 73 | // 校验前缀 |
| 86 | - if (!topic.startsWith(SYS_TOPIC_PREFIX)) { | |
| 74 | + if (!topic.startsWith(IotDeviceTopicEnum.SYS_TOPIC_PREFIX.getTopic())) { | |
| 87 | 75 | log.warn("[handleMessage][未知的消息类型][topic: {}]", topic); |
| 88 | 76 | return; |
| 89 | 77 | } |
| 90 | 78 | |
| 91 | 79 | // 处理设备属性上报消息 |
| 92 | - if (topic.endsWith(PROPERTY_POST_TOPIC)) { | |
| 80 | + if (topic.endsWith(IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic())) { | |
| 93 | 81 | log.info("[handleMessage][接收到设备属性上报][topic: {}]", topic); |
| 94 | 82 | handlePropertyPost(topic, payload); |
| 95 | 83 | return; |
| 96 | 84 | } |
| 97 | 85 | |
| 98 | 86 | // 处理设备事件上报消息 |
| 99 | - if (topic.contains(EVENT_POST_TOPIC_PREFIX) && topic.endsWith(EVENT_POST_TOPIC_SUFFIX)) { | |
| 87 | + if (topic.contains(IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic()) && | |
| 88 | + topic.endsWith(IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic())) { | |
| 100 | 89 | log.info("[handleMessage][接收到设备事件上报][topic: {}]", topic); |
| 101 | 90 | handleEventPost(topic, payload); |
| 102 | 91 | return; |
| ... | ... | @@ -212,7 +201,7 @@ public class IotDeviceMqttMessageHandler { |
| 212 | 201 | * @param customData 自定义数据,可为 null |
| 213 | 202 | */ |
| 214 | 203 | private void sendResponse(String topic, JSONObject jsonObject, String method, Object customData) { |
| 215 | - String replyTopic = topic + REPLY_SUFFIX; | |
| 204 | + String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic); | |
| 216 | 205 | |
| 217 | 206 | // 响应结果 |
| 218 | 207 | IotStandardResponse response = IotStandardResponse.success( |
| ... | ... | @@ -236,7 +225,7 @@ public class IotDeviceMqttMessageHandler { |
| 236 | 225 | private IotDevicePropertyReportReqDTO buildPropertyReportDTO(JSONObject jsonObject, String[] topicParts) { |
| 237 | 226 | IotDevicePropertyReportReqDTO reportReqDTO = new IotDevicePropertyReportReqDTO(); |
| 238 | 227 | reportReqDTO.setRequestId(jsonObject.getStr("id")); |
| 239 | - reportReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); | |
| 228 | + reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); | |
| 240 | 229 | reportReqDTO.setReportTime(LocalDateTime.now()); |
| 241 | 230 | reportReqDTO.setProductKey(topicParts[2]); |
| 242 | 231 | reportReqDTO.setDeviceName(topicParts[3]); |
| ... | ... | @@ -276,7 +265,7 @@ public class IotDeviceMqttMessageHandler { |
| 276 | 265 | private IotDeviceEventReportReqDTO buildEventReportDTO(JSONObject jsonObject, String[] topicParts) { |
| 277 | 266 | IotDeviceEventReportReqDTO reportReqDTO = new IotDeviceEventReportReqDTO(); |
| 278 | 267 | reportReqDTO.setRequestId(jsonObject.getStr("id")); |
| 279 | - reportReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); | |
| 268 | + reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); | |
| 280 | 269 | reportReqDTO.setReportTime(LocalDateTime.now()); |
| 281 | 270 | reportReqDTO.setProductKey(topicParts[2]); |
| 282 | 271 | reportReqDTO.setDeviceName(topicParts[3]); | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/component/emqx/upstream/router/IotDeviceWebhookVertxHandler.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceWebhookVertxHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.component.emqx.upstream.router; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; |
| 7 | -import cn.iocoder.yudao.module.iot.component.core.util.IotPluginCommonUtils; | |
| 8 | 7 | import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 9 | 9 | import io.vertx.core.Handler; |
| 10 | 10 | import io.vertx.core.json.JsonObject; |
| 11 | 11 | import io.vertx.ext.web.RoutingContext; |
| ... | ... | @@ -57,11 +57,11 @@ public class IotDeviceWebhookVertxHandler implements Handler<RoutingContext> { |
| 57 | 57 | |
| 58 | 58 | // 返回成功响应 |
| 59 | 59 | // 注意:这里必须返回 {"result": "success"} 格式,以符合 EMQX Webhook 插件的要求 |
| 60 | - IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "success")); | |
| 60 | + IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "success")); | |
| 61 | 61 | } catch (Exception e) { |
| 62 | 62 | log.error("[handle][处理 Webhook 事件异常]", e); |
| 63 | 63 | // 注意:这里必须返回 {"result": "error"} 格式,以符合 EMQX Webhook 插件的要求 |
| 64 | - IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "error")); | |
| 64 | + IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "error")); | |
| 65 | 65 | } |
| 66 | 66 | } |
| 67 | 67 | |
| ... | ... | @@ -87,7 +87,7 @@ public class IotDeviceWebhookVertxHandler implements Handler<RoutingContext> { |
| 87 | 87 | updateReqDTO.setProductKey(parts[1]); |
| 88 | 88 | updateReqDTO.setDeviceName(parts[0]); |
| 89 | 89 | updateReqDTO.setState(IotDeviceStateEnum.ONLINE.getState()); |
| 90 | - updateReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); | |
| 90 | + updateReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); | |
| 91 | 91 | updateReqDTO.setReportTime(LocalDateTime.now()); |
| 92 | 92 | CommonResult<Boolean> result = deviceUpstreamApi.updateDeviceState(updateReqDTO); |
| 93 | 93 | if (result.getCode() != 0 || !result.getData()) { |
| ... | ... | @@ -120,7 +120,7 @@ public class IotDeviceWebhookVertxHandler implements Handler<RoutingContext> { |
| 120 | 120 | offlineReqDTO.setProductKey(parts[1]); |
| 121 | 121 | offlineReqDTO.setDeviceName(parts[0]); |
| 122 | 122 | offlineReqDTO.setState(IotDeviceStateEnum.OFFLINE.getState()); |
| 123 | - offlineReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); | |
| 123 | + offlineReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); | |
| 124 | 124 | offlineReqDTO.setReportTime(LocalDateTime.now()); |
| 125 | 125 | CommonResult<Boolean> offlineResult = deviceUpstreamApi.updateDeviceState(offlineReqDTO); |
| 126 | 126 | if (offlineResult.getCode() != 0 || !offlineResult.getData()) { | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
0 → 100644
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-emqx/src/main/resources/application.yml renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/application.yml
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/pom.xml renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/pom.xml
| ... | ... | @@ -3,23 +3,23 @@ |
| 3 | 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 4 | 4 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| 5 | 5 | <parent> |
| 6 | - <artifactId>yudao-module-iot-components</artifactId> | |
| 6 | + <artifactId>yudao-module-iot-net-components</artifactId> | |
| 7 | 7 | <groupId>cn.iocoder.boot</groupId> |
| 8 | 8 | <version>${revision}</version> |
| 9 | 9 | </parent> |
| 10 | 10 | <modelVersion>4.0.0</modelVersion> |
| 11 | - <artifactId>yudao-module-iot-component-http</artifactId> | |
| 11 | + <artifactId>yudao-module-iot-net-component-http</artifactId> | |
| 12 | 12 | <packaging>jar</packaging> |
| 13 | 13 | |
| 14 | 14 | <name>${project.artifactId}</name> |
| 15 | 15 | <description> |
| 16 | - 物联网组件 HTTP 模块 | |
| 16 | + 物联网网络组件 HTTP 模块 | |
| 17 | 17 | </description> |
| 18 | 18 | |
| 19 | 19 | <dependencies> |
| 20 | 20 | <dependency> |
| 21 | 21 | <groupId>cn.iocoder.boot</groupId> |
| 22 | - <artifactId>yudao-module-iot-component-core</artifactId> | |
| 22 | + <artifactId>yudao-module-iot-net-component-core</artifactId> | |
| 23 | 23 | <version>${revision}</version> |
| 24 | 24 | </dependency> |
| 25 | 25 | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/java/cn/iocoder/yudao/module/iot/net/component/http/config/IotNetComponentHttpAutoConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.http.config; | |
| 2 | + | |
| 3 | +import cn.hutool.system.SystemUtil; | |
| 4 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 5 | +import cn.iocoder.yudao.module.iot.net.component.http.downstream.IotDeviceDownstreamHandlerImpl; | |
| 6 | +import cn.iocoder.yudao.module.iot.net.component.http.upstream.IotDeviceUpstreamServer; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.core.config.IotNetComponentCommonProperties; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 9 | +import cn.iocoder.yudao.module.iot.net.component.core.heartbeat.IotNetComponentRegistry; | |
| 10 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 11 | +import io.vertx.core.Vertx; | |
| 12 | +import lombok.extern.slf4j.Slf4j; | |
| 13 | +import org.springframework.beans.factory.annotation.Qualifier; | |
| 14 | +import org.springframework.boot.autoconfigure.AutoConfiguration; | |
| 15 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 16 | +import org.springframework.boot.context.event.ApplicationStartedEvent; | |
| 17 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 18 | +import org.springframework.context.ApplicationContext; | |
| 19 | +import org.springframework.context.annotation.Bean; | |
| 20 | +import org.springframework.context.annotation.ComponentScan; | |
| 21 | +import org.springframework.context.annotation.Lazy; | |
| 22 | +import org.springframework.context.event.EventListener; | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * IoT 网络组件 HTTP 的自动配置类 | |
| 26 | + * | |
| 27 | + * @author haohao | |
| 28 | + */ | |
| 29 | +@Slf4j | |
| 30 | +@AutoConfiguration | |
| 31 | +@EnableConfigurationProperties(IotNetComponentHttpProperties.class) | |
| 32 | +@ConditionalOnProperty(prefix = "yudao.iot.component.http", name = "enabled", havingValue = "true", matchIfMissing = false) | |
| 33 | +@ComponentScan(basePackages = { | |
| 34 | + "cn.iocoder.yudao.module.iot.net.component.http" // 只扫描 HTTP 组件包 | |
| 35 | +}) | |
| 36 | +public class IotNetComponentHttpAutoConfiguration { | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 组件 key | |
| 40 | + */ | |
| 41 | + private static final String PLUGIN_KEY = "http"; | |
| 42 | + | |
| 43 | + public IotNetComponentHttpAutoConfiguration() { | |
| 44 | + // 构造函数中不输出日志,移到 initialize 方法中 | |
| 45 | + } | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * 初始化 HTTP 组件 | |
| 49 | + * | |
| 50 | + * @param event 应用启动事件 | |
| 51 | + */ | |
| 52 | + @EventListener(ApplicationStartedEvent.class) | |
| 53 | + public void initialize(ApplicationStartedEvent event) { | |
| 54 | + log.info("[IotNetComponentHttpAutoConfiguration][开始初始化]"); | |
| 55 | + | |
| 56 | + // 从应用上下文中获取需要的 Bean | |
| 57 | + IotNetComponentRegistry componentRegistry = event.getApplicationContext() | |
| 58 | + .getBean(IotNetComponentRegistry.class); | |
| 59 | + IotNetComponentCommonProperties commonProperties = event.getApplicationContext() | |
| 60 | + .getBean(IotNetComponentCommonProperties.class); | |
| 61 | + | |
| 62 | + // 设置当前组件的核心标识 | |
| 63 | + // 注意:这里只为当前 HTTP 组件设置 pluginKey,不影响其他组件 | |
| 64 | + commonProperties.setPluginKey(PLUGIN_KEY); | |
| 65 | + | |
| 66 | + // 将 HTTP 组件注册到组件注册表 | |
| 67 | + componentRegistry.registerComponent( | |
| 68 | + PLUGIN_KEY, | |
| 69 | + SystemUtil.getHostInfo().getAddress(), | |
| 70 | + 0, // 内嵌模式固定为 0 | |
| 71 | + IotNetComponentCommonUtils.getProcessId()); | |
| 72 | + | |
| 73 | + log.info("[initialize][IoT HTTP 组件初始化完成]"); | |
| 74 | + } | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * 创建 Vert.x 实例 | |
| 78 | + * | |
| 79 | + * @return Vert.x 实例 | |
| 80 | + */ | |
| 81 | + @Bean(name = "httpVertx") | |
| 82 | + public Vertx vertx() { | |
| 83 | + return Vertx.vertx(); | |
| 84 | + } | |
| 85 | + | |
| 86 | + /** | |
| 87 | + * 创建设备上行服务器 | |
| 88 | + * | |
| 89 | + * @param vertx Vert.x 实例 | |
| 90 | + * @param deviceUpstreamApi 设备上行 API | |
| 91 | + * @param properties HTTP 组件配置属性 | |
| 92 | + * @param applicationContext 应用上下文 | |
| 93 | + * @return 设备上行服务器 | |
| 94 | + */ | |
| 95 | + @Bean(name = "httpDeviceUpstreamServer", initMethod = "start", destroyMethod = "stop") | |
| 96 | + public IotDeviceUpstreamServer deviceUpstreamServer( | |
| 97 | + @Lazy @Qualifier("httpVertx") Vertx vertx, | |
| 98 | + IotDeviceUpstreamApi deviceUpstreamApi, | |
| 99 | + IotNetComponentHttpProperties properties, | |
| 100 | + ApplicationContext applicationContext) { | |
| 101 | + if (log.isDebugEnabled()) { | |
| 102 | + log.debug("HTTP 服务器配置: port={}", properties.getServerPort()); | |
| 103 | + } else { | |
| 104 | + log.info("HTTP 服务器将监听端口: {}", properties.getServerPort()); | |
| 105 | + } | |
| 106 | + return new IotDeviceUpstreamServer(vertx, properties, deviceUpstreamApi, applicationContext); | |
| 107 | + } | |
| 108 | + | |
| 109 | + /** | |
| 110 | + * 创建设备下行处理器 | |
| 111 | + * | |
| 112 | + * @return 设备下行处理器 | |
| 113 | + */ | |
| 114 | + @Bean(name = "httpDeviceDownstreamHandler") | |
| 115 | + public IotDeviceDownstreamHandler deviceDownstreamHandler() { | |
| 116 | + return new IotDeviceDownstreamHandlerImpl(); | |
| 117 | + } | |
| 118 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/src/main/java/cn/iocoder/yudao/module/iot/component/http/config/IotComponentHttpProperties.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/java/cn/iocoder/yudao/module/iot/net/component/http/config/IotNetComponentHttpProperties.java
| 1 | -package cn.iocoder.yudao.module.iot.component.http.config; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.http.config; | |
| 2 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | 4 | import org.springframework.boot.context.properties.ConfigurationProperties; |
| 5 | 5 | import org.springframework.validation.annotation.Validated; |
| 6 | 6 | |
| 7 | 7 | /** |
| 8 | - * IoT HTTP 组件配置属性 | |
| 8 | + * IoT HTTP 网络组件配置属性 | |
| 9 | + * | |
| 10 | + * @author haohao | |
| 9 | 11 | */ |
| 10 | 12 | @ConfigurationProperties(prefix = "yudao.iot.component.http") |
| 11 | 13 | @Validated |
| 12 | 14 | @Data |
| 13 | -public class IotComponentHttpProperties { | |
| 15 | +public class IotNetComponentHttpProperties { | |
| 14 | 16 | |
| 15 | 17 | /** |
| 16 | - * 是否启用 | |
| 18 | + * 是否启用 HTTP 组件 | |
| 17 | 19 | */ |
| 18 | 20 | private Boolean enabled; |
| 19 | 21 | |
| ... | ... | @@ -22,4 +24,10 @@ public class IotComponentHttpProperties { |
| 22 | 24 | */ |
| 23 | 25 | private Integer serverPort; |
| 24 | 26 | |
| 27 | + /** | |
| 28 | + * 连接超时时间(毫秒) | |
| 29 | + * <p> | |
| 30 | + * 默认值:10000 毫秒 | |
| 31 | + */ | |
| 32 | + private Integer connectionTimeoutMs = 10000; | |
| 25 | 33 | } |
| 26 | 34 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/src/main/java/cn/iocoder/yudao/module/iot/component/http/downstream/IotDeviceDownstreamHandlerImpl.java renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/java/cn/iocoder/yudao/module/iot/net/component/http/downstream/IotDeviceDownstreamHandlerImpl.java
| 1 | -package cn.iocoder.yudao.module.iot.component.http.downstream; | |
| 1 | +package cn.iocoder.yudao.module.iot.net.component.http.downstream; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; |
| 5 | -import cn.iocoder.yudao.module.iot.component.core.downstream.IotDeviceDownstreamHandler; | |
| 5 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 6 | 7 | |
| 7 | 8 | import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED; |
| 8 | 9 | |
| 9 | 10 | /** |
| 10 | - * HTTP 插件的 {@link IotDeviceDownstreamHandler} 实现类 | |
| 11 | + * HTTP 网络组件的 {@link IotDeviceDownstreamHandler} 实现类 | |
| 11 | 12 | * <p> |
| 12 | 13 | * 但是:由于设备通过 HTTP 短链接接入,导致其实无法下行指导给 device 设备,所以基本都是直接返回失败!!! |
| 13 | - * 类似 MQTT、WebSocket、TCP 插件,是可以实现下行指令的。 | |
| 14 | + * 类似 MQTT、WebSocket、TCP 网络组件,是可以实现下行指令的。 | |
| 14 | 15 | * |
| 15 | 16 | * @author 芋道源码 |
| 16 | 17 | */ |
| 18 | +@Slf4j | |
| 17 | 19 | public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { |
| 18 | 20 | |
| 21 | + /** | |
| 22 | + * 不支持的错误消息 | |
| 23 | + */ | |
| 24 | + private static final String NOT_SUPPORTED_MSG = "HTTP 不支持设备下行通信"; | |
| 25 | + | |
| 19 | 26 | @Override |
| 20 | 27 | public CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { |
| 21 | - return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持调用设备服务"); | |
| 28 | + return CommonResult.error(NOT_IMPLEMENTED.getCode(), NOT_SUPPORTED_MSG); | |
| 22 | 29 | } |
| 23 | 30 | |
| 24 | 31 | @Override |
| 25 | 32 | public CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { |
| 26 | - return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持获取设备属性"); | |
| 33 | + return CommonResult.error(NOT_IMPLEMENTED.getCode(), NOT_SUPPORTED_MSG); | |
| 27 | 34 | } |
| 28 | 35 | |
| 29 | 36 | @Override |
| 30 | 37 | public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { |
| 31 | - return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); | |
| 38 | + return CommonResult.error(NOT_IMPLEMENTED.getCode(), NOT_SUPPORTED_MSG); | |
| 32 | 39 | } |
| 33 | 40 | |
| 34 | 41 | @Override |
| 35 | 42 | public CommonResult<Boolean> setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) { |
| 36 | - return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); | |
| 43 | + return CommonResult.error(NOT_IMPLEMENTED.getCode(), NOT_SUPPORTED_MSG); | |
| 37 | 44 | } |
| 38 | 45 | |
| 39 | 46 | @Override |
| 40 | 47 | public CommonResult<Boolean> upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) { |
| 41 | - return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); | |
| 48 | + return CommonResult.error(NOT_IMPLEMENTED.getCode(), NOT_SUPPORTED_MSG); | |
| 42 | 49 | } |
| 43 | - | |
| 44 | 50 | } | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/java/cn/iocoder/yudao/module/iot/net/component/http/upstream/IotDeviceUpstreamServer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.http.upstream; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 4 | +import cn.iocoder.yudao.module.iot.net.component.http.config.IotNetComponentHttpProperties; | |
| 5 | +import cn.iocoder.yudao.module.iot.net.component.http.upstream.router.IotDeviceUpstreamVertxHandler; | |
| 6 | +import io.vertx.core.AbstractVerticle; | |
| 7 | +import io.vertx.core.Promise; | |
| 8 | +import io.vertx.core.Vertx; | |
| 9 | +import io.vertx.ext.web.Router; | |
| 10 | +import io.vertx.ext.web.handler.BodyHandler; | |
| 11 | +import lombok.extern.slf4j.Slf4j; | |
| 12 | +import org.springframework.context.ApplicationContext; | |
| 13 | +import org.springframework.context.annotation.Lazy; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * IoT 设备上行服务器 | |
| 17 | + * <p> | |
| 18 | + * 处理设备通过 HTTP 方式接入的上行消息 | |
| 19 | + * | |
| 20 | + * @author 芋道源码 | |
| 21 | + */ | |
| 22 | +@Slf4j | |
| 23 | +public class IotDeviceUpstreamServer extends AbstractVerticle { | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * Vert.x 实例 | |
| 27 | + */ | |
| 28 | + private final Vertx vertx; | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * HTTP 组件配置属性 | |
| 32 | + */ | |
| 33 | + private final IotNetComponentHttpProperties httpProperties; | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * 设备上行 API | |
| 37 | + */ | |
| 38 | + private final IotDeviceUpstreamApi deviceUpstreamApi; | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * Spring 应用上下文 | |
| 42 | + */ | |
| 43 | + private final ApplicationContext applicationContext; | |
| 44 | + | |
| 45 | + /** | |
| 46 | + * 构造函数 | |
| 47 | + * | |
| 48 | + * @param vertx Vert.x 实例 | |
| 49 | + * @param httpProperties HTTP 组件配置属性 | |
| 50 | + * @param deviceUpstreamApi 设备上行 API | |
| 51 | + * @param applicationContext Spring 应用上下文 | |
| 52 | + */ | |
| 53 | + public IotDeviceUpstreamServer( | |
| 54 | + @Lazy Vertx vertx, | |
| 55 | + IotNetComponentHttpProperties httpProperties, | |
| 56 | + IotDeviceUpstreamApi deviceUpstreamApi, | |
| 57 | + ApplicationContext applicationContext) { | |
| 58 | + this.vertx = vertx; | |
| 59 | + this.httpProperties = httpProperties; | |
| 60 | + this.deviceUpstreamApi = deviceUpstreamApi; | |
| 61 | + this.applicationContext = applicationContext; | |
| 62 | + } | |
| 63 | + | |
| 64 | + @Override | |
| 65 | + public void start(Promise<Void> startPromise) { | |
| 66 | + // 创建路由 | |
| 67 | + Router router = Router.router(vertx); | |
| 68 | + router.route().handler(BodyHandler.create()); | |
| 69 | + | |
| 70 | + // 创建处理器 | |
| 71 | + IotDeviceUpstreamVertxHandler upstreamHandler = new IotDeviceUpstreamVertxHandler( | |
| 72 | + deviceUpstreamApi, applicationContext); | |
| 73 | + | |
| 74 | + // 添加路由处理器 | |
| 75 | + router.post(IotDeviceUpstreamVertxHandler.PROPERTY_PATH).handler(upstreamHandler::handle); | |
| 76 | + router.post(IotDeviceUpstreamVertxHandler.EVENT_PATH).handler(upstreamHandler::handle); | |
| 77 | + | |
| 78 | + // 启动 HTTP 服务器 | |
| 79 | + vertx.createHttpServer() | |
| 80 | + .requestHandler(router) | |
| 81 | + .listen(httpProperties.getServerPort(), result -> { | |
| 82 | + if (result.succeeded()) { | |
| 83 | + log.info("[start][IoT 设备上行服务器启动成功,端口:{}]", httpProperties.getServerPort()); | |
| 84 | + startPromise.complete(); | |
| 85 | + } else { | |
| 86 | + log.error("[start][IoT 设备上行服务器启动失败]", result.cause()); | |
| 87 | + startPromise.fail(result.cause()); | |
| 88 | + } | |
| 89 | + }); | |
| 90 | + } | |
| 91 | + | |
| 92 | + @Override | |
| 93 | + public void stop(Promise<Void> stopPromise) { | |
| 94 | + log.info("[stop][IoT 设备上行服务器已停止]"); | |
| 95 | + stopPromise.complete(); | |
| 96 | + } | |
| 97 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/java/cn/iocoder/yudao/module/iot/net/component/http/upstream/auth/IotDeviceAuthProvider.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.http.upstream.auth; | |
| 2 | + | |
| 3 | +import io.vertx.core.Future; | |
| 4 | +import io.vertx.ext.web.RoutingContext; | |
| 5 | +import lombok.extern.slf4j.Slf4j; | |
| 6 | +import org.springframework.context.ApplicationContext; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * IoT 设备认证提供者 | |
| 10 | + * <p> | |
| 11 | + * 用于 HTTP 设备接入时的身份认证 | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Slf4j | |
| 16 | +public class IotDeviceAuthProvider { | |
| 17 | + | |
| 18 | + private final ApplicationContext applicationContext; | |
| 19 | + | |
| 20 | + /** | |
| 21 | + * 构造函数 | |
| 22 | + * | |
| 23 | + * @param applicationContext Spring 应用上下文 | |
| 24 | + */ | |
| 25 | + public IotDeviceAuthProvider(ApplicationContext applicationContext) { | |
| 26 | + this.applicationContext = applicationContext; | |
| 27 | + } | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * 认证设备 | |
| 31 | + * | |
| 32 | + * @param context 路由上下文 | |
| 33 | + * @param clientId 设备唯一标识 | |
| 34 | + * @return 认证结果 Future 对象 | |
| 35 | + */ | |
| 36 | + public Future<Void> authenticate(RoutingContext context, String clientId) { | |
| 37 | + if (clientId == null || clientId.isEmpty()) { | |
| 38 | + return Future.failedFuture("clientId 不能为空"); | |
| 39 | + } | |
| 40 | + | |
| 41 | + try { | |
| 42 | + log.info("[authenticate][设备认证成功,clientId={}]", clientId); | |
| 43 | + return Future.succeededFuture(); | |
| 44 | + } catch (Exception e) { | |
| 45 | + log.error("[authenticate][设备认证异常,clientId={}]", clientId, e); | |
| 46 | + return Future.failedFuture(e); | |
| 47 | + } | |
| 48 | + } | |
| 49 | +} | |
| 0 | 50 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/java/cn/iocoder/yudao/module/iot/net/component/http/upstream/router/IotDeviceUpstreamVertxHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.http.upstream.router; | |
| 2 | + | |
| 3 | +import cn.hutool.core.map.MapUtil; | |
| 4 | +import cn.hutool.core.util.IdUtil; | |
| 5 | +import cn.hutool.core.util.ObjUtil; | |
| 6 | +import cn.hutool.core.util.StrUtil; | |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 8 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 9 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; | |
| 10 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; | |
| 11 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; | |
| 12 | +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; | |
| 13 | +import cn.iocoder.yudao.module.iot.net.component.core.constants.IotDeviceTopicEnum; | |
| 14 | +import cn.iocoder.yudao.module.iot.net.component.core.pojo.IotStandardResponse; | |
| 15 | +import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; | |
| 16 | +import io.vertx.core.Handler; | |
| 17 | +import io.vertx.core.json.JsonObject; | |
| 18 | +import io.vertx.ext.web.RoutingContext; | |
| 19 | +import lombok.extern.slf4j.Slf4j; | |
| 20 | +import org.springframework.context.ApplicationContext; | |
| 21 | + | |
| 22 | +import java.time.LocalDateTime; | |
| 23 | +import java.util.Map; | |
| 24 | + | |
| 25 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | |
| 26 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * IoT 设备上行统一处理的 Vert.x Handler | |
| 30 | + * <p> | |
| 31 | + * 统一处理设备属性上报和事件上报的请求。 | |
| 32 | + * | |
| 33 | + * @author 芋道源码 | |
| 34 | + */ | |
| 35 | +@Slf4j | |
| 36 | +public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 属性上报路径 | |
| 40 | + */ | |
| 41 | + public static final String PROPERTY_PATH = "/sys/:productKey/:deviceName" | |
| 42 | + + IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic(); | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 事件上报路径 | |
| 46 | + */ | |
| 47 | + public static final String EVENT_PATH = "/sys/:productKey/:deviceName" | |
| 48 | + + IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic() + ":identifier" | |
| 49 | + + IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic(); | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * 属性上报方法标识 | |
| 53 | + */ | |
| 54 | + private static final String PROPERTY_METHOD = "thing.event.property.post"; | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * 事件上报方法前缀 | |
| 58 | + */ | |
| 59 | + private static final String EVENT_METHOD_PREFIX = "thing.event."; | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * 事件上报方法后缀 | |
| 63 | + */ | |
| 64 | + private static final String EVENT_METHOD_SUFFIX = ".post"; | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 设备上行 API | |
| 68 | + */ | |
| 69 | + private final IotDeviceUpstreamApi deviceUpstreamApi; | |
| 70 | + | |
| 71 | + /** | |
| 72 | + * 构造函数 | |
| 73 | + * | |
| 74 | + * @param deviceUpstreamApi 设备上行 API | |
| 75 | + * @param applicationContext 应用上下文 | |
| 76 | + */ | |
| 77 | + public IotDeviceUpstreamVertxHandler(IotDeviceUpstreamApi deviceUpstreamApi, | |
| 78 | + ApplicationContext applicationContext) { | |
| 79 | + this.deviceUpstreamApi = deviceUpstreamApi; | |
| 80 | + } | |
| 81 | + | |
| 82 | + @Override | |
| 83 | + public void handle(RoutingContext routingContext) { | |
| 84 | + String path = routingContext.request().path(); | |
| 85 | + String requestId = IdUtil.fastSimpleUUID(); | |
| 86 | + | |
| 87 | + try { | |
| 88 | + // 1. 解析通用参数 | |
| 89 | + Map<String, String> params = parseCommonParams(routingContext, requestId); | |
| 90 | + String productKey = params.get("productKey"); | |
| 91 | + String deviceName = params.get("deviceName"); | |
| 92 | + JsonObject body = routingContext.body().asJsonObject(); | |
| 93 | + requestId = params.get("requestId"); | |
| 94 | + | |
| 95 | + // 2. 根据路径模式处理不同类型的请求 | |
| 96 | + if (isPropertyPostPath(path)) { | |
| 97 | + // 处理属性上报 | |
| 98 | + handlePropertyPost(routingContext, productKey, deviceName, requestId, body); | |
| 99 | + return; | |
| 100 | + } | |
| 101 | + | |
| 102 | + if (isEventPostPath(path)) { | |
| 103 | + // 处理事件上报 | |
| 104 | + String identifier = routingContext.pathParam("identifier"); | |
| 105 | + handleEventPost(routingContext, productKey, deviceName, identifier, requestId, body); | |
| 106 | + return; | |
| 107 | + } | |
| 108 | + | |
| 109 | + // 不支持的请求路径 | |
| 110 | + sendErrorResponse(routingContext, requestId, "unknown", BAD_REQUEST.getCode(), "不支持的请求路径"); | |
| 111 | + } catch (Exception e) { | |
| 112 | + log.error("[handle][处理上行请求异常] path={}", path, e); | |
| 113 | + String method = determineMethodFromPath(path, routingContext); | |
| 114 | + sendErrorResponse(routingContext, requestId, method, INTERNAL_SERVER_ERROR.getCode(), | |
| 115 | + INTERNAL_SERVER_ERROR.getMsg()); | |
| 116 | + } | |
| 117 | + } | |
| 118 | + | |
| 119 | + /** | |
| 120 | + * 解析通用参数 | |
| 121 | + * | |
| 122 | + * @param routingContext 路由上下文 | |
| 123 | + * @param defaultRequestId 默认请求 ID | |
| 124 | + * @return 参数映射 | |
| 125 | + */ | |
| 126 | + private Map<String, String> parseCommonParams(RoutingContext routingContext, String defaultRequestId) { | |
| 127 | + Map<String, String> params = MapUtil.newHashMap(); | |
| 128 | + params.put("productKey", routingContext.pathParam("productKey")); | |
| 129 | + params.put("deviceName", routingContext.pathParam("deviceName")); | |
| 130 | + | |
| 131 | + JsonObject body = routingContext.body().asJsonObject(); | |
| 132 | + String requestId = ObjUtil.defaultIfNull(body.getString("id"), defaultRequestId); | |
| 133 | + params.put("requestId", requestId); | |
| 134 | + | |
| 135 | + return params; | |
| 136 | + } | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * 判断是否是属性上报路径 | |
| 140 | + * | |
| 141 | + * @param path 路径 | |
| 142 | + * @return 是否是属性上报路径 | |
| 143 | + */ | |
| 144 | + private boolean isPropertyPostPath(String path) { | |
| 145 | + return StrUtil.endWith(path, IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic()); | |
| 146 | + } | |
| 147 | + | |
| 148 | + /** | |
| 149 | + * 判断是否是事件上报路径 | |
| 150 | + * | |
| 151 | + * @param path 路径 | |
| 152 | + * @return 是否是事件上报路径 | |
| 153 | + */ | |
| 154 | + private boolean isEventPostPath(String path) { | |
| 155 | + return StrUtil.contains(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic()) | |
| 156 | + && StrUtil.endWith(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic()); | |
| 157 | + } | |
| 158 | + | |
| 159 | + /** | |
| 160 | + * 处理属性上报请求 | |
| 161 | + * | |
| 162 | + * @param routingContext 路由上下文 | |
| 163 | + * @param productKey 产品 Key | |
| 164 | + * @param deviceName 设备名称 | |
| 165 | + * @param requestId 请求 ID | |
| 166 | + * @param body 请求体 | |
| 167 | + */ | |
| 168 | + private void handlePropertyPost(RoutingContext routingContext, String productKey, String deviceName, | |
| 169 | + String requestId, JsonObject body) { | |
| 170 | + // 处理属性上报 | |
| 171 | + IotDevicePropertyReportReqDTO reportReqDTO = parsePropertyReportRequest(productKey, deviceName, | |
| 172 | + requestId, body); | |
| 173 | + | |
| 174 | + // 设备上线 | |
| 175 | + updateDeviceState(reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); | |
| 176 | + | |
| 177 | + // 属性上报 | |
| 178 | + CommonResult<Boolean> result = deviceUpstreamApi.reportDeviceProperty(reportReqDTO); | |
| 179 | + | |
| 180 | + // 返回响应 | |
| 181 | + sendResponse(routingContext, requestId, PROPERTY_METHOD, result); | |
| 182 | + } | |
| 183 | + | |
| 184 | + /** | |
| 185 | + * 处理事件上报请求 | |
| 186 | + * | |
| 187 | + * @param routingContext 路由上下文 | |
| 188 | + * @param productKey 产品 Key | |
| 189 | + * @param deviceName 设备名称 | |
| 190 | + * @param identifier 事件标识符 | |
| 191 | + * @param requestId 请求 ID | |
| 192 | + * @param body 请求体 | |
| 193 | + */ | |
| 194 | + private void handleEventPost(RoutingContext routingContext, String productKey, String deviceName, | |
| 195 | + String identifier, String requestId, JsonObject body) { | |
| 196 | + // 处理事件上报 | |
| 197 | + IotDeviceEventReportReqDTO reportReqDTO = parseEventReportRequest(productKey, deviceName, identifier, | |
| 198 | + requestId, body); | |
| 199 | + | |
| 200 | + // 设备上线 | |
| 201 | + updateDeviceState(reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); | |
| 202 | + | |
| 203 | + // 事件上报 | |
| 204 | + CommonResult<Boolean> result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO); | |
| 205 | + String method = EVENT_METHOD_PREFIX + identifier + EVENT_METHOD_SUFFIX; | |
| 206 | + | |
| 207 | + // 返回响应 | |
| 208 | + sendResponse(routingContext, requestId, method, result); | |
| 209 | + } | |
| 210 | + | |
| 211 | + /** | |
| 212 | + * 发送响应 | |
| 213 | + * | |
| 214 | + * @param routingContext 路由上下文 | |
| 215 | + * @param requestId 请求 ID | |
| 216 | + * @param method 方法名 | |
| 217 | + * @param result 结果 | |
| 218 | + */ | |
| 219 | + private void sendResponse(RoutingContext routingContext, String requestId, String method, | |
| 220 | + CommonResult<Boolean> result) { | |
| 221 | + IotStandardResponse response; | |
| 222 | + if (result.isSuccess()) { | |
| 223 | + response = IotStandardResponse.success(requestId, method, result.getData()); | |
| 224 | + } else { | |
| 225 | + response = IotStandardResponse.error(requestId, method, result.getCode(), result.getMsg()); | |
| 226 | + } | |
| 227 | + IotNetComponentCommonUtils.writeJsonResponse(routingContext, response); | |
| 228 | + } | |
| 229 | + | |
| 230 | + /** | |
| 231 | + * 发送错误响应 | |
| 232 | + * | |
| 233 | + * @param routingContext 路由上下文 | |
| 234 | + * @param requestId 请求 ID | |
| 235 | + * @param method 方法名 | |
| 236 | + * @param code 错误代码 | |
| 237 | + * @param message 错误消息 | |
| 238 | + */ | |
| 239 | + private void sendErrorResponse(RoutingContext routingContext, String requestId, String method, Integer code, | |
| 240 | + String message) { | |
| 241 | + IotStandardResponse errorResponse = IotStandardResponse.error(requestId, method, code, message); | |
| 242 | + IotNetComponentCommonUtils.writeJsonResponse(routingContext, errorResponse); | |
| 243 | + } | |
| 244 | + | |
| 245 | + /** | |
| 246 | + * 从路径确定方法名 | |
| 247 | + * | |
| 248 | + * @param path 路径 | |
| 249 | + * @param routingContext 路由上下文 | |
| 250 | + * @return 方法名 | |
| 251 | + */ | |
| 252 | + private String determineMethodFromPath(String path, RoutingContext routingContext) { | |
| 253 | + if (StrUtil.contains(path, "/property/")) { | |
| 254 | + return PROPERTY_METHOD; | |
| 255 | + } | |
| 256 | + | |
| 257 | + return EVENT_METHOD_PREFIX + | |
| 258 | + (routingContext.pathParams().containsKey("identifier") | |
| 259 | + ? routingContext.pathParam("identifier") | |
| 260 | + : "unknown") | |
| 261 | + + | |
| 262 | + EVENT_METHOD_SUFFIX; | |
| 263 | + } | |
| 264 | + | |
| 265 | + /** | |
| 266 | + * 更新设备状态 | |
| 267 | + * | |
| 268 | + * @param productKey 产品 Key | |
| 269 | + * @param deviceName 设备名称 | |
| 270 | + */ | |
| 271 | + private void updateDeviceState(String productKey, String deviceName) { | |
| 272 | + IotDeviceStateUpdateReqDTO reqDTO = ((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO() | |
| 273 | + .setRequestId(IdUtil.fastSimpleUUID()) | |
| 274 | + .setProcessId(IotNetComponentCommonUtils.getProcessId()) | |
| 275 | + .setReportTime(LocalDateTime.now()) | |
| 276 | + .setProductKey(productKey) | |
| 277 | + .setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState()); | |
| 278 | + | |
| 279 | + deviceUpstreamApi.updateDeviceState(reqDTO); | |
| 280 | + } | |
| 281 | + | |
| 282 | + /** | |
| 283 | + * 解析属性上报请求 | |
| 284 | + * | |
| 285 | + * @param productKey 产品 Key | |
| 286 | + * @param deviceName 设备名称 | |
| 287 | + * @param requestId 请求 ID | |
| 288 | + * @param body 请求体 | |
| 289 | + * @return 属性上报请求 DTO | |
| 290 | + */ | |
| 291 | + private IotDevicePropertyReportReqDTO parsePropertyReportRequest(String productKey, String deviceName, | |
| 292 | + String requestId, JsonObject body) { | |
| 293 | + // 解析属性 | |
| 294 | + Map<String, Object> properties = parsePropertiesFromBody(body); | |
| 295 | + | |
| 296 | + // 构建属性上报请求 DTO | |
| 297 | + return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO() | |
| 298 | + .setRequestId(requestId) | |
| 299 | + .setProcessId(IotNetComponentCommonUtils.getProcessId()) | |
| 300 | + .setReportTime(LocalDateTime.now()) | |
| 301 | + .setProductKey(productKey) | |
| 302 | + .setDeviceName(deviceName)).setProperties(properties); | |
| 303 | + } | |
| 304 | + | |
| 305 | + /** | |
| 306 | + * 从请求体解析属性 | |
| 307 | + * | |
| 308 | + * @param body 请求体 | |
| 309 | + * @return 属性映射 | |
| 310 | + */ | |
| 311 | + private Map<String, Object> parsePropertiesFromBody(JsonObject body) { | |
| 312 | + Map<String, Object> properties = MapUtil.newHashMap(); | |
| 313 | + JsonObject params = body.getJsonObject("params"); | |
| 314 | + | |
| 315 | + if (params == null) { | |
| 316 | + return properties; | |
| 317 | + } | |
| 318 | + | |
| 319 | + // 将标准格式的 params 转换为平台需要的 properties 格式 | |
| 320 | + for (String key : params.fieldNames()) { | |
| 321 | + Object valueObj = params.getValue(key); | |
| 322 | + // 如果是复杂结构(包含 value 和 time) | |
| 323 | + if (valueObj instanceof JsonObject) { | |
| 324 | + JsonObject valueJson = (JsonObject) valueObj; | |
| 325 | + properties.put(key, valueJson.containsKey("value") ? valueJson.getValue("value") : valueObj); | |
| 326 | + } else { | |
| 327 | + properties.put(key, valueObj); | |
| 328 | + } | |
| 329 | + } | |
| 330 | + | |
| 331 | + return properties; | |
| 332 | + } | |
| 333 | + | |
| 334 | + /** | |
| 335 | + * 解析事件上报请求 | |
| 336 | + * | |
| 337 | + * @param productKey 产品 Key | |
| 338 | + * @param deviceName 设备名称 | |
| 339 | + * @param identifier 事件标识符 | |
| 340 | + * @param requestId 请求 ID | |
| 341 | + * @param body 请求体 | |
| 342 | + * @return 事件上报请求 DTO | |
| 343 | + */ | |
| 344 | + private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier, | |
| 345 | + String requestId, JsonObject body) { | |
| 346 | + // 解析参数 | |
| 347 | + Map<String, Object> params = parseParamsFromBody(body); | |
| 348 | + | |
| 349 | + // 构建事件上报请求 DTO | |
| 350 | + return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO() | |
| 351 | + .setRequestId(requestId) | |
| 352 | + .setProcessId(IotNetComponentCommonUtils.getProcessId()) | |
| 353 | + .setReportTime(LocalDateTime.now()) | |
| 354 | + .setProductKey(productKey) | |
| 355 | + .setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); | |
| 356 | + } | |
| 357 | + | |
| 358 | + /** | |
| 359 | + * 从请求体解析参数 | |
| 360 | + * | |
| 361 | + * @param body 请求体 | |
| 362 | + * @return 参数映射 | |
| 363 | + */ | |
| 364 | + private Map<String, Object> parseParamsFromBody(JsonObject body) { | |
| 365 | + Map<String, Object> params = MapUtil.newHashMap(); | |
| 366 | + JsonObject paramsJson = body.getJsonObject("params"); | |
| 367 | + | |
| 368 | + if (paramsJson == null) { | |
| 369 | + return params; | |
| 370 | + } | |
| 371 | + | |
| 372 | + for (String key : paramsJson.fieldNames()) { | |
| 373 | + params.put(key, paramsJson.getValue(key)); | |
| 374 | + } | |
| 375 | + | |
| 376 | + return params; | |
| 377 | + } | |
| 378 | +} | |
| 0 | 379 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
0 → 100644
yudao-module-iot/yudao-module-iot-components/yudao-module-iot-component-http/src/main/resources/application.yml renamed to yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-http/src/main/resources/application.yml