Commit 44b835bd4a488edae25ef51d7bf3d2c1fa92c058
1 parent
2954445d
【功能新增】IoT: 新增网络组件服务器,支持设备上行和下行处理,添加健康检查接口,配置心跳任务,更新相关依赖和配置文件。
Showing
13 changed files
with
959 additions
and
29 deletions
yudao-module-iot/yudao-module-iot-biz/pom.xml
| ... | ... | @@ -123,16 +123,16 @@ |
| 123 | 123 | <!-- </dependency>--> |
| 124 | 124 | |
| 125 | 125 | <!-- IoT 网络组件:接收来自设备的上行数据 --> |
| 126 | - <dependency> | |
| 127 | - <groupId>cn.iocoder.boot</groupId> | |
| 128 | - <artifactId>yudao-module-iot-net-component-http</artifactId> | |
| 129 | - <version>${revision}</version> | |
| 130 | - </dependency> | |
| 131 | - <dependency> | |
| 132 | - <groupId>cn.iocoder.boot</groupId> | |
| 133 | - <artifactId>yudao-module-iot-net-component-emqx</artifactId> | |
| 134 | - <version>${revision}</version> | |
| 135 | - </dependency> | |
| 126 | + <!-- <dependency>--> | |
| 127 | + <!-- <groupId>cn.iocoder.boot</groupId>--> | |
| 128 | + <!-- <artifactId>yudao-module-iot-net-component-http</artifactId>--> | |
| 129 | + <!-- <version>${revision}</version>--> | |
| 130 | + <!-- </dependency>--> | |
| 131 | + <!-- <dependency>--> | |
| 132 | + <!-- <groupId>cn.iocoder.boot</groupId>--> | |
| 133 | + <!-- <artifactId>yudao-module-iot-net-component-emqx</artifactId>--> | |
| 134 | + <!-- <version>${revision}</version>--> | |
| 135 | + <!-- </dependency>--> | |
| 136 | 136 | |
| 137 | 137 | <!-- 脚本相关 --> |
| 138 | 138 | <dependency> | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/pom.xml
| 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | 2 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
| 3 | - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| 4 | - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 5 | 4 | <parent> |
| 6 | 5 | <artifactId>yudao-module-iot</artifactId> |
| 7 | 6 | <groupId>cn.iocoder.boot</groupId> |
| ... | ... | @@ -21,6 +20,7 @@ |
| 21 | 20 | <module>yudao-module-iot-net-component-core</module> |
| 22 | 21 | <module>yudao-module-iot-net-component-http</module> |
| 23 | 22 | <module>yudao-module-iot-net-component-emqx</module> |
| 23 | + <module>yudao-module-iot-net-component-server</module> | |
| 24 | 24 | </modules> |
| 25 | 25 | |
| 26 | 26 | </project> |
| 27 | 27 | \ 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/config/IotNetComponentCommonProperties.java
| ... | ... | @@ -31,26 +31,28 @@ public class IotNetComponentCommonProperties { |
| 31 | 31 | /** |
| 32 | 32 | * 网络组件消息转发配置 |
| 33 | 33 | */ |
| 34 | - private ForwardMessage forwardMessage = new ForwardMessage(); | |
| 34 | + // private ForwardMessage forwardMessage = new ForwardMessage(); | |
| 35 | 35 | |
| 36 | 36 | /** |
| 37 | 37 | * 消息转发配置 |
| 38 | 38 | */ |
| 39 | - @Data | |
| 40 | - public static class ForwardMessage { | |
| 41 | - | |
| 42 | - /** | |
| 43 | - * 是否转发所有设备消息到 EMQX | |
| 44 | - * <p> | |
| 45 | - * 默认为 true 开启 | |
| 46 | - */ | |
| 47 | - private boolean forwardAllDeviceMessageToEmqx = true; | |
| 39 | + /* | |
| 40 | + * @Data | |
| 41 | + * public static class ForwardMessage { | |
| 42 | + * | |
| 43 | + * /** | |
| 44 | + * 是否转发所有设备消息到 EMQX | |
| 45 | + * <p> | |
| 46 | + * 默认为 true 开启 | |
| 47 | + */ | |
| 48 | + // private boolean forwardAllDeviceMessageToEmqx = true; | |
| 48 | 49 | |
| 49 | - /** | |
| 50 | - * 是否转发所有设备消息到 HTTP | |
| 51 | - * <p> | |
| 52 | - * 默认为 false 关闭 | |
| 53 | - */ | |
| 54 | - private boolean forwardAllDeviceMessageToHttp = false; | |
| 55 | - } | |
| 50 | + /** | |
| 51 | + * 是否转发所有设备消息到 HTTP | |
| 52 | + * <p> | |
| 53 | + * 默认为 false 关闭 | |
| 54 | + */ | |
| 55 | + // private boolean forwardAllDeviceMessageToHttp = false; | |
| 56 | + // } | |
| 57 | + | |
| 56 | 58 | } |
| 57 | 59 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | |
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 4 | + <modelVersion>4.0.0</modelVersion> | |
| 5 | + <parent> | |
| 6 | + <groupId>cn.iocoder.boot</groupId> | |
| 7 | + <artifactId>yudao-module-iot-net-components</artifactId> | |
| 8 | + <version>${revision}</version> | |
| 9 | + </parent> | |
| 10 | + | |
| 11 | + <artifactId>yudao-module-iot-net-component-server</artifactId> | |
| 12 | + <packaging>jar</packaging> | |
| 13 | + | |
| 14 | + <name>${project.artifactId}</name> | |
| 15 | + <description> | |
| 16 | + IoT 网络组件的独立运行服务,用于聚合和启动多个网络组件实例。 | |
| 17 | + </description> | |
| 18 | + | |
| 19 | + <dependencies> | |
| 20 | + <!-- Spring Boot 核心 --> | |
| 21 | + <dependency> | |
| 22 | + <groupId>org.springframework.boot</groupId> | |
| 23 | + <artifactId>spring-boot-starter</artifactId> | |
| 24 | + </dependency> | |
| 25 | + | |
| 26 | + <!-- Web 相关 --> | |
| 27 | + <dependency> | |
| 28 | + <groupId>org.springframework.boot</groupId> | |
| 29 | + <artifactId>spring-boot-starter-web</artifactId> | |
| 30 | + </dependency> | |
| 31 | + | |
| 32 | + <!-- Net Component 核心 --> | |
| 33 | + <dependency> | |
| 34 | + <groupId>cn.iocoder.boot</groupId> | |
| 35 | + <artifactId>yudao-module-iot-net-component-core</artifactId> | |
| 36 | + <version>${revision}</version> | |
| 37 | + </dependency> | |
| 38 | + | |
| 39 | + <!-- Net Component HTTP --> | |
| 40 | + <dependency> | |
| 41 | + <groupId>cn.iocoder.boot</groupId> | |
| 42 | + <artifactId>yudao-module-iot-net-component-http</artifactId> | |
| 43 | + <version>${revision}</version> | |
| 44 | + </dependency> | |
| 45 | + | |
| 46 | + <!-- Net Component EMQX --> | |
| 47 | + <dependency> | |
| 48 | + <groupId>cn.iocoder.boot</groupId> | |
| 49 | + <artifactId>yudao-module-iot-net-component-emqx</artifactId> | |
| 50 | + <version>${revision}</version> | |
| 51 | + </dependency> | |
| 52 | + | |
| 53 | + </dependencies> | |
| 54 | + | |
| 55 | + <build> | |
| 56 | + <!-- 设置构建的 jar 包名 --> | |
| 57 | + <finalName>${project.artifactId}</finalName> | |
| 58 | + <plugins> | |
| 59 | + <!-- 打包 --> | |
| 60 | + <plugin> | |
| 61 | + <groupId>org.springframework.boot</groupId> | |
| 62 | + <artifactId>spring-boot-maven-plugin</artifactId> | |
| 63 | + <version>${spring.boot.version}</version> <!-- 需要确认父 POM 中有定义 --> | |
| 64 | + <executions> | |
| 65 | + <execution> | |
| 66 | + <goals> | |
| 67 | + <goal>repackage</goal> <!-- 将引入的 jar 打入其中 --> | |
| 68 | + </goals> | |
| 69 | + </execution> | |
| 70 | + </executions> | |
| 71 | + </plugin> | |
| 72 | + </plugins> | |
| 73 | + </build> | |
| 74 | + | |
| 75 | +</project> | |
| 0 | 76 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/NetComponentServerApplication.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server; | |
| 2 | + | |
| 3 | +import org.springframework.boot.SpringApplication; | |
| 4 | +import org.springframework.boot.autoconfigure.SpringBootApplication; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * IoT 网络组件聚合启动服务 | |
| 8 | + * | |
| 9 | + * @author haohao | |
| 10 | + */ | |
| 11 | +@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}.module.iot.net.component"}) | |
| 12 | +public class NetComponentServerApplication { | |
| 13 | + | |
| 14 | + public static void main(String[] args) { | |
| 15 | + SpringApplication.run(NetComponentServerApplication.class, args); | |
| 16 | + } | |
| 17 | + | |
| 18 | +} | |
| 0 | 19 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 4 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 5 | +import cn.iocoder.yudao.module.iot.net.component.server.downstream.IotComponentDownstreamHandlerImpl; | |
| 6 | +import cn.iocoder.yudao.module.iot.net.component.server.downstream.IotComponentDownstreamServer; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.server.heartbeat.IotComponentHeartbeatJob; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.server.upstream.IotComponentUpstreamClient; | |
| 9 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |
| 10 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 11 | +import org.springframework.boot.web.client.RestTemplateBuilder; | |
| 12 | +import org.springframework.context.annotation.Bean; | |
| 13 | +import org.springframework.context.annotation.Configuration; | |
| 14 | +import org.springframework.context.annotation.Primary; | |
| 15 | +import org.springframework.scheduling.annotation.EnableScheduling; | |
| 16 | +import org.springframework.web.client.RestTemplate; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * IoT 网络组件服务器配置类 | |
| 20 | + * | |
| 21 | + * @author haohao | |
| 22 | + */ | |
| 23 | +@Configuration | |
| 24 | +@EnableConfigurationProperties(IotNetComponentServerProperties.class) | |
| 25 | +@EnableScheduling | |
| 26 | +public class IotNetComponentServerConfiguration { | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * 配置 RestTemplate | |
| 30 | + * | |
| 31 | + * @param properties 配置 | |
| 32 | + * @return RestTemplate | |
| 33 | + */ | |
| 34 | + @Bean | |
| 35 | + public RestTemplate restTemplate(IotNetComponentServerProperties properties) { | |
| 36 | + return new RestTemplateBuilder() | |
| 37 | + .connectTimeout(properties.getUpstreamConnectTimeout()) | |
| 38 | + .readTimeout(properties.getUpstreamReadTimeout()) | |
| 39 | + .build(); | |
| 40 | + } | |
| 41 | + | |
| 42 | + /** | |
| 43 | + * 配置设备上行客户端 | |
| 44 | + * | |
| 45 | + * @param properties 配置 | |
| 46 | + * @param restTemplate RestTemplate | |
| 47 | + * @return 上行客户端 | |
| 48 | + */ | |
| 49 | + @Bean | |
| 50 | + @Primary | |
| 51 | + public IotDeviceUpstreamApi deviceUpstreamApi(IotNetComponentServerProperties properties, | |
| 52 | + RestTemplate restTemplate) { | |
| 53 | + return new IotComponentUpstreamClient(properties, restTemplate); | |
| 54 | + } | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * 配置设备下行处理器 | |
| 58 | + * | |
| 59 | + * @return 下行处理器 | |
| 60 | + */ | |
| 61 | + @Bean | |
| 62 | + @Primary | |
| 63 | + public IotDeviceDownstreamHandler deviceDownstreamHandler() { | |
| 64 | + return new IotComponentDownstreamHandlerImpl(); | |
| 65 | + } | |
| 66 | + | |
| 67 | + /** | |
| 68 | + * 配置下行服务器 | |
| 69 | + * | |
| 70 | + * @param properties 配置 | |
| 71 | + * @param downstreamHandler 下行处理器 | |
| 72 | + * @return 下行服务器 | |
| 73 | + */ | |
| 74 | + @Bean(initMethod = "start", destroyMethod = "stop") | |
| 75 | + public IotComponentDownstreamServer deviceDownstreamServer(IotNetComponentServerProperties properties, | |
| 76 | + @org.springframework.beans.factory.annotation.Qualifier("deviceDownstreamHandler") IotDeviceDownstreamHandler downstreamHandler) { | |
| 77 | + return new IotComponentDownstreamServer(properties, downstreamHandler); | |
| 78 | + } | |
| 79 | + | |
| 80 | + /** | |
| 81 | + * 配置心跳任务 | |
| 82 | + * | |
| 83 | + * @param deviceUpstreamApi 上行接口 | |
| 84 | + * @param downstreamServer 下行服务器 | |
| 85 | + * @param properties 配置 | |
| 86 | + * @return 心跳任务 | |
| 87 | + */ | |
| 88 | + @Bean(initMethod = "init", destroyMethod = "stop") | |
| 89 | + public IotComponentHeartbeatJob heartbeatJob(IotDeviceUpstreamApi deviceUpstreamApi, | |
| 90 | + IotComponentDownstreamServer downstreamServer, | |
| 91 | + IotNetComponentServerProperties properties) { | |
| 92 | + return new IotComponentHeartbeatJob(deviceUpstreamApi, downstreamServer, properties); | |
| 93 | + } | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * 配置默认的设备上行客户端,避免在独立运行模式下的循环依赖问题 | |
| 97 | + * | |
| 98 | + * @return 设备上行客户端 | |
| 99 | + */ | |
| 100 | + @Bean | |
| 101 | + @ConditionalOnMissingBean(name = "serverDeviceUpstreamClient") | |
| 102 | + public Object serverDeviceUpstreamClient() { | |
| 103 | + // 返回一个空对象,避免找不到类的问题 | |
| 104 | + return new Object(); | |
| 105 | + } | |
| 106 | + | |
| 107 | + /** | |
| 108 | + * 配置默认的组件实例注册客户端 | |
| 109 | + * | |
| 110 | + * @return 插件实例注册客户端 | |
| 111 | + */ | |
| 112 | + @Bean | |
| 113 | + @ConditionalOnMissingBean(name = "serverPluginInstanceRegistryClient") | |
| 114 | + public Object serverPluginInstanceRegistryClient() { | |
| 115 | + // 返回一个空对象,避免找不到类的问题 | |
| 116 | + return new Object(); | |
| 117 | + } | |
| 118 | +} | |
| 0 | 119 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerProperties.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server.config; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
| 5 | +import org.springframework.validation.annotation.Validated; | |
| 6 | + | |
| 7 | +import java.time.Duration; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * IoT 网络组件服务配置属性 | |
| 11 | + * | |
| 12 | + * @author haohao | |
| 13 | + */ | |
| 14 | +@ConfigurationProperties(prefix = "yudao.iot.component.server") | |
| 15 | +@Validated | |
| 16 | +@Data | |
| 17 | +public class IotNetComponentServerProperties { | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 上行 URL,用于向主应用程序上报数据 | |
| 21 | + * <p> | |
| 22 | + * 默认:http://127.0.0.1:48080 | |
| 23 | + */ | |
| 24 | + private String upstreamUrl = "http://127.0.0.1:48080"; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 上行连接超时时间 | |
| 28 | + */ | |
| 29 | + private Duration upstreamConnectTimeout = Duration.ofSeconds(30); | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 上行读取超时时间 | |
| 33 | + */ | |
| 34 | + private Duration upstreamReadTimeout = Duration.ofSeconds(30); | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * 下行服务端口,用于接收主应用程序的请求 | |
| 38 | + * <p> | |
| 39 | + * 默认:18888 | |
| 40 | + */ | |
| 41 | + private Integer downstreamPort = 18888; | |
| 42 | + | |
| 43 | + /** | |
| 44 | + * 组件服务器唯一标识 | |
| 45 | + * <p> | |
| 46 | + * 默认:yudao-module-iot-net-component-server | |
| 47 | + */ | |
| 48 | + private String serverKey = "yudao-module-iot-net-component-server"; | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * 心跳发送频率,单位:毫秒 | |
| 52 | + * <p> | |
| 53 | + * 默认:30 秒 | |
| 54 | + */ | |
| 55 | + private Long heartbeatInterval = 30000L; | |
| 56 | +} | |
| 0 | 57 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/controller/HealthController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server.controller; | |
| 2 | + | |
| 3 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 4 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 5 | +import org.springframework.web.bind.annotation.RestController; | |
| 6 | + | |
| 7 | +import java.util.HashMap; | |
| 8 | +import java.util.Map; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 健康检查接口 | |
| 12 | + * | |
| 13 | + * @author haohao | |
| 14 | + */ | |
| 15 | +@RestController | |
| 16 | +@RequestMapping("/health") | |
| 17 | +public class HealthController { | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 健康检查接口 | |
| 21 | + * | |
| 22 | + * @return 返回服务状态信息 | |
| 23 | + */ | |
| 24 | + @GetMapping("/status") | |
| 25 | + public Map<String, Object> status() { | |
| 26 | + Map<String, Object> result = new HashMap<>(); | |
| 27 | + result.put("status", "UP"); | |
| 28 | + result.put("message", "IoT 网络组件服务运行正常"); | |
| 29 | + result.put("timestamp", System.currentTimeMillis()); | |
| 30 | + return result; | |
| 31 | + } | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/downstream/IotComponentDownstreamHandlerImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server.downstream; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; | |
| 5 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | + | |
| 8 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.SUCCESS; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 网络组件下行处理器实现 | |
| 12 | + * <p> | |
| 13 | + * 处理来自主程序的设备控制指令 | |
| 14 | + * | |
| 15 | + * @author haohao | |
| 16 | + */ | |
| 17 | +@Slf4j | |
| 18 | +public class IotComponentDownstreamHandlerImpl implements IotDeviceDownstreamHandler { | |
| 19 | + | |
| 20 | + @Override | |
| 21 | + public CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { | |
| 22 | + log.info("[invokeDeviceService][收到服务调用请求:{}]", invokeReqDTO); | |
| 23 | + // 在这里处理服务调用,可以根据设备类型转发到对应的处理器 | |
| 24 | + // 如 MQTT 设备、HTTP 设备等的具体实现 | |
| 25 | + | |
| 26 | + // 这里仅作为示例,实际应根据接入的组件进行转发 | |
| 27 | + return CommonResult.success(true); | |
| 28 | + } | |
| 29 | + | |
| 30 | + @Override | |
| 31 | + public CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { | |
| 32 | + log.info("[getDeviceProperty][收到属性获取请求:{}]", getReqDTO); | |
| 33 | + // 在这里处理属性获取请求 | |
| 34 | + | |
| 35 | + // 这里仅作为示例,实际应根据接入的组件进行转发 | |
| 36 | + return CommonResult.success(true); | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { | |
| 41 | + log.info("[setDeviceProperty][收到属性设置请求:{}]", setReqDTO); | |
| 42 | + // 在这里处理属性设置请求 | |
| 43 | + | |
| 44 | + // 这里仅作为示例,实际应根据接入的组件进行转发 | |
| 45 | + return CommonResult.success(true); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @Override | |
| 49 | + public CommonResult<Boolean> setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) { | |
| 50 | + log.info("[setDeviceConfig][收到配置设置请求:{}]", setReqDTO); | |
| 51 | + // 在这里处理配置设置请求 | |
| 52 | + | |
| 53 | + // 这里仅作为示例,实际应根据接入的组件进行转发 | |
| 54 | + return CommonResult.success(true); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @Override | |
| 58 | + public CommonResult<Boolean> upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) { | |
| 59 | + log.info("[upgradeDeviceOta][收到OTA升级请求:{}]", upgradeReqDTO); | |
| 60 | + // 在这里处理OTA升级请求 | |
| 61 | + | |
| 62 | + // 这里仅作为示例,实际应根据接入的组件进行转发 | |
| 63 | + return CommonResult.success(true); | |
| 64 | + } | |
| 65 | +} | |
| 0 | 66 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/downstream/IotComponentDownstreamServer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server.downstream; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.IdUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; | |
| 6 | +import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.server.config.IotNetComponentServerProperties; | |
| 8 | +import io.vertx.core.Vertx; | |
| 9 | +import io.vertx.core.http.HttpServer; | |
| 10 | +import io.vertx.core.json.Json; | |
| 11 | +import io.vertx.core.json.JsonObject; | |
| 12 | +import io.vertx.ext.web.Router; | |
| 13 | +import io.vertx.ext.web.RoutingContext; | |
| 14 | +import io.vertx.ext.web.handler.BodyHandler; | |
| 15 | +import lombok.extern.slf4j.Slf4j; | |
| 16 | + | |
| 17 | +import java.util.List; | |
| 18 | +import java.util.Map; | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * 组件下行服务器,接收来自主程序的控制指令 | |
| 22 | + * | |
| 23 | + * @author haohao | |
| 24 | + */ | |
| 25 | +@Slf4j | |
| 26 | +public class IotComponentDownstreamServer { | |
| 27 | + | |
| 28 | + public static final String SERVICE_INVOKE_PATH = "/sys/:productKey/:deviceName/thing/service/:identifier"; | |
| 29 | + public static final String PROPERTY_SET_PATH = "/sys/:productKey/:deviceName/thing/service/property/set"; | |
| 30 | + public static final String PROPERTY_GET_PATH = "/sys/:productKey/:deviceName/thing/service/property/get"; | |
| 31 | + public static final String CONFIG_SET_PATH = "/sys/:productKey/:deviceName/thing/service/config/set"; | |
| 32 | + public static final String OTA_UPGRADE_PATH = "/sys/:productKey/:deviceName/thing/service/ota/upgrade"; | |
| 33 | + | |
| 34 | + private final Vertx vertx; | |
| 35 | + private final HttpServer server; | |
| 36 | + private final IotNetComponentServerProperties properties; | |
| 37 | + private final IotDeviceDownstreamHandler downstreamHandler; | |
| 38 | + | |
| 39 | + public IotComponentDownstreamServer(IotNetComponentServerProperties properties, | |
| 40 | + IotDeviceDownstreamHandler downstreamHandler) { | |
| 41 | + this.properties = properties; | |
| 42 | + this.downstreamHandler = downstreamHandler; | |
| 43 | + // 创建 Vertx 实例 | |
| 44 | + this.vertx = Vertx.vertx(); | |
| 45 | + // 创建 Router 实例 | |
| 46 | + Router router = Router.router(vertx); | |
| 47 | + router.route().handler(BodyHandler.create()); // 处理 Body | |
| 48 | + | |
| 49 | + // 服务调用路由 | |
| 50 | + router.post(SERVICE_INVOKE_PATH).handler(this::handleServiceInvoke); | |
| 51 | + // 属性设置路由 | |
| 52 | + router.post(PROPERTY_SET_PATH).handler(this::handlePropertySet); | |
| 53 | + // 属性获取路由 | |
| 54 | + router.post(PROPERTY_GET_PATH).handler(this::handlePropertyGet); | |
| 55 | + // 配置设置路由 | |
| 56 | + router.post(CONFIG_SET_PATH).handler(this::handleConfigSet); | |
| 57 | + // OTA 升级路由 | |
| 58 | + router.post(OTA_UPGRADE_PATH).handler(this::handleOtaUpgrade); | |
| 59 | + | |
| 60 | + // 创建 HttpServer 实例 | |
| 61 | + this.server = vertx.createHttpServer().requestHandler(router); | |
| 62 | + } | |
| 63 | + | |
| 64 | + /** | |
| 65 | + * 启动服务器 | |
| 66 | + */ | |
| 67 | + public void start() { | |
| 68 | + log.info("[start][开始启动下行服务器]"); | |
| 69 | + server.listen(properties.getDownstreamPort()) | |
| 70 | + .toCompletionStage() | |
| 71 | + .toCompletableFuture() | |
| 72 | + .join(); | |
| 73 | + log.info("[start][下行服务器启动完成,端口({})]", server.actualPort()); | |
| 74 | + } | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * 停止服务器 | |
| 78 | + */ | |
| 79 | + public void stop() { | |
| 80 | + log.info("[stop][开始关闭下行服务器]"); | |
| 81 | + try { | |
| 82 | + // 关闭 HTTP 服务器 | |
| 83 | + if (server != null) { | |
| 84 | + server.close() | |
| 85 | + .toCompletionStage() | |
| 86 | + .toCompletableFuture() | |
| 87 | + .join(); | |
| 88 | + } | |
| 89 | + | |
| 90 | + // 关闭 Vertx 实例 | |
| 91 | + if (vertx != null) { | |
| 92 | + vertx.close() | |
| 93 | + .toCompletionStage() | |
| 94 | + .toCompletableFuture() | |
| 95 | + .join(); | |
| 96 | + } | |
| 97 | + log.info("[stop][下行服务器关闭完成]"); | |
| 98 | + } catch (Exception e) { | |
| 99 | + log.error("[stop][下行服务器关闭异常]", e); | |
| 100 | + throw new RuntimeException(e); | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + /** | |
| 105 | + * 获取服务器端口 | |
| 106 | + * | |
| 107 | + * @return 端口号 | |
| 108 | + */ | |
| 109 | + public int getPort() { | |
| 110 | + return server.actualPort(); | |
| 111 | + } | |
| 112 | + | |
| 113 | + /** | |
| 114 | + * 处理服务调用请求 | |
| 115 | + */ | |
| 116 | + private void handleServiceInvoke(RoutingContext ctx) { | |
| 117 | + try { | |
| 118 | + // 解析路径参数 | |
| 119 | + String productKey = ctx.pathParam("productKey"); | |
| 120 | + String deviceName = ctx.pathParam("deviceName"); | |
| 121 | + String identifier = ctx.pathParam("identifier"); | |
| 122 | + | |
| 123 | + // 解析请求体 | |
| 124 | + JsonObject body = ctx.body().asJsonObject(); | |
| 125 | + String requestId = body.getString("requestId", IdUtil.fastSimpleUUID()); | |
| 126 | + Object params = body.getMap().get("params"); | |
| 127 | + | |
| 128 | + // 创建请求对象 | |
| 129 | + IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO(); | |
| 130 | + reqDTO.setRequestId(requestId); | |
| 131 | + reqDTO.setProductKey(productKey); | |
| 132 | + reqDTO.setDeviceName(deviceName); | |
| 133 | + reqDTO.setIdentifier(identifier); | |
| 134 | + reqDTO.setParams((Map<String, Object>) params); | |
| 135 | + | |
| 136 | + // 调用处理器 | |
| 137 | + CommonResult<Boolean> result = downstreamHandler.invokeDeviceService(reqDTO); | |
| 138 | + | |
| 139 | + // 响应结果 | |
| 140 | + ctx.response() | |
| 141 | + .putHeader("Content-Type", "application/json") | |
| 142 | + .end(Json.encode(result)); | |
| 143 | + } catch (Exception e) { | |
| 144 | + log.error("[handleServiceInvoke][处理服务调用请求失败]", e); | |
| 145 | + ctx.response() | |
| 146 | + .setStatusCode(500) | |
| 147 | + .putHeader("Content-Type", "application/json") | |
| 148 | + .end(Json.encode(CommonResult.error(500, "处理服务调用请求失败:" + e.getMessage()))); | |
| 149 | + } | |
| 150 | + } | |
| 151 | + | |
| 152 | + /** | |
| 153 | + * 处理属性设置请求 | |
| 154 | + */ | |
| 155 | + private void handlePropertySet(RoutingContext ctx) { | |
| 156 | + try { | |
| 157 | + // 解析路径参数 | |
| 158 | + String productKey = ctx.pathParam("productKey"); | |
| 159 | + String deviceName = ctx.pathParam("deviceName"); | |
| 160 | + | |
| 161 | + // 解析请求体 | |
| 162 | + JsonObject body = ctx.body().asJsonObject(); | |
| 163 | + String requestId = body.getString("requestId", IdUtil.fastSimpleUUID()); | |
| 164 | + Object properties = body.getMap().get("properties"); | |
| 165 | + | |
| 166 | + // 创建请求对象 | |
| 167 | + IotDevicePropertySetReqDTO reqDTO = new IotDevicePropertySetReqDTO(); | |
| 168 | + reqDTO.setRequestId(requestId); | |
| 169 | + reqDTO.setProductKey(productKey); | |
| 170 | + reqDTO.setDeviceName(deviceName); | |
| 171 | + reqDTO.setProperties((Map<String, Object>) properties); | |
| 172 | + | |
| 173 | + // 调用处理器 | |
| 174 | + CommonResult<Boolean> result = downstreamHandler.setDeviceProperty(reqDTO); | |
| 175 | + | |
| 176 | + // 响应结果 | |
| 177 | + ctx.response() | |
| 178 | + .putHeader("Content-Type", "application/json") | |
| 179 | + .end(Json.encode(result)); | |
| 180 | + } catch (Exception e) { | |
| 181 | + log.error("[handlePropertySet][处理属性设置请求失败]", e); | |
| 182 | + ctx.response() | |
| 183 | + .setStatusCode(500) | |
| 184 | + .putHeader("Content-Type", "application/json") | |
| 185 | + .end(Json.encode(CommonResult.error(500, "处理属性设置请求失败:" + e.getMessage()))); | |
| 186 | + } | |
| 187 | + } | |
| 188 | + | |
| 189 | + /** | |
| 190 | + * 处理属性获取请求 | |
| 191 | + */ | |
| 192 | + private void handlePropertyGet(RoutingContext ctx) { | |
| 193 | + try { | |
| 194 | + // 解析路径参数 | |
| 195 | + String productKey = ctx.pathParam("productKey"); | |
| 196 | + String deviceName = ctx.pathParam("deviceName"); | |
| 197 | + | |
| 198 | + // 解析请求体 | |
| 199 | + JsonObject body = ctx.body().asJsonObject(); | |
| 200 | + String requestId = body.getString("requestId", IdUtil.fastSimpleUUID()); | |
| 201 | + Object identifiers = body.getMap().get("identifiers"); | |
| 202 | + | |
| 203 | + // 创建请求对象 | |
| 204 | + IotDevicePropertyGetReqDTO reqDTO = new IotDevicePropertyGetReqDTO(); | |
| 205 | + reqDTO.setRequestId(requestId); | |
| 206 | + reqDTO.setProductKey(productKey); | |
| 207 | + reqDTO.setDeviceName(deviceName); | |
| 208 | + reqDTO.setIdentifiers((List<String>) identifiers); | |
| 209 | + | |
| 210 | + // 调用处理器 | |
| 211 | + CommonResult<Boolean> result = downstreamHandler.getDeviceProperty(reqDTO); | |
| 212 | + | |
| 213 | + // 响应结果 | |
| 214 | + ctx.response() | |
| 215 | + .putHeader("Content-Type", "application/json") | |
| 216 | + .end(Json.encode(result)); | |
| 217 | + } catch (Exception e) { | |
| 218 | + log.error("[handlePropertyGet][处理属性获取请求失败]", e); | |
| 219 | + ctx.response() | |
| 220 | + .setStatusCode(500) | |
| 221 | + .putHeader("Content-Type", "application/json") | |
| 222 | + .end(Json.encode(CommonResult.error(500, "处理属性获取请求失败:" + e.getMessage()))); | |
| 223 | + } | |
| 224 | + } | |
| 225 | + | |
| 226 | + /** | |
| 227 | + * 处理配置设置请求 | |
| 228 | + */ | |
| 229 | + private void handleConfigSet(RoutingContext ctx) { | |
| 230 | + try { | |
| 231 | + // 解析路径参数 | |
| 232 | + String productKey = ctx.pathParam("productKey"); | |
| 233 | + String deviceName = ctx.pathParam("deviceName"); | |
| 234 | + | |
| 235 | + // 解析请求体 | |
| 236 | + JsonObject body = ctx.body().asJsonObject(); | |
| 237 | + String requestId = body.getString("requestId", IdUtil.fastSimpleUUID()); | |
| 238 | + Object config = body.getMap().get("config"); | |
| 239 | + | |
| 240 | + // 创建请求对象 | |
| 241 | + IotDeviceConfigSetReqDTO reqDTO = new IotDeviceConfigSetReqDTO(); | |
| 242 | + reqDTO.setRequestId(requestId); | |
| 243 | + reqDTO.setProductKey(productKey); | |
| 244 | + reqDTO.setDeviceName(deviceName); | |
| 245 | + reqDTO.setConfig((Map<String, Object>) config); | |
| 246 | + | |
| 247 | + // 调用处理器 | |
| 248 | + CommonResult<Boolean> result = downstreamHandler.setDeviceConfig(reqDTO); | |
| 249 | + | |
| 250 | + // 响应结果 | |
| 251 | + ctx.response() | |
| 252 | + .putHeader("Content-Type", "application/json") | |
| 253 | + .end(Json.encode(result)); | |
| 254 | + } catch (Exception e) { | |
| 255 | + log.error("[handleConfigSet][处理配置设置请求失败]", e); | |
| 256 | + ctx.response() | |
| 257 | + .setStatusCode(500) | |
| 258 | + .putHeader("Content-Type", "application/json") | |
| 259 | + .end(Json.encode(CommonResult.error(500, "处理配置设置请求失败:" + e.getMessage()))); | |
| 260 | + } | |
| 261 | + } | |
| 262 | + | |
| 263 | + /** | |
| 264 | + * 处理 OTA 升级请求 | |
| 265 | + */ | |
| 266 | + private void handleOtaUpgrade(RoutingContext ctx) { | |
| 267 | + try { | |
| 268 | + // 解析路径参数 | |
| 269 | + String productKey = ctx.pathParam("productKey"); | |
| 270 | + String deviceName = ctx.pathParam("deviceName"); | |
| 271 | + | |
| 272 | + // 解析请求体 | |
| 273 | + JsonObject body = ctx.body().asJsonObject(); | |
| 274 | + String requestId = body.getString("requestId", IdUtil.fastSimpleUUID()); | |
| 275 | + Object data = body.getMap().get("data"); | |
| 276 | + | |
| 277 | + // 创建请求对象 | |
| 278 | + IotDeviceOtaUpgradeReqDTO reqDTO = new IotDeviceOtaUpgradeReqDTO(); | |
| 279 | + reqDTO.setRequestId(requestId); | |
| 280 | + reqDTO.setProductKey(productKey); | |
| 281 | + reqDTO.setDeviceName(deviceName); | |
| 282 | + | |
| 283 | + // 数据采用 IotDeviceOtaUpgradeReqDTO.build 方法转换 | |
| 284 | + if (data instanceof Map) { | |
| 285 | + IotDeviceOtaUpgradeReqDTO builtDTO = IotDeviceOtaUpgradeReqDTO.build((Map<?, ?>) data); | |
| 286 | + reqDTO.setFirmwareId(builtDTO.getFirmwareId()); | |
| 287 | + reqDTO.setVersion(builtDTO.getVersion()); | |
| 288 | + reqDTO.setSignMethod(builtDTO.getSignMethod()); | |
| 289 | + reqDTO.setFileSign(builtDTO.getFileSign()); | |
| 290 | + reqDTO.setFileSize(builtDTO.getFileSize()); | |
| 291 | + reqDTO.setFileUrl(builtDTO.getFileUrl()); | |
| 292 | + reqDTO.setInformation(builtDTO.getInformation()); | |
| 293 | + } | |
| 294 | + | |
| 295 | + // 调用处理器 | |
| 296 | + CommonResult<Boolean> result = downstreamHandler.upgradeDeviceOta(reqDTO); | |
| 297 | + | |
| 298 | + // 响应结果 | |
| 299 | + ctx.response() | |
| 300 | + .putHeader("Content-Type", "application/json") | |
| 301 | + .end(Json.encode(result)); | |
| 302 | + } catch (Exception e) { | |
| 303 | + log.error("[handleOtaUpgrade][处理OTA升级请求失败]", e); | |
| 304 | + ctx.response() | |
| 305 | + .setStatusCode(500) | |
| 306 | + .putHeader("Content-Type", "application/json") | |
| 307 | + .end(Json.encode(CommonResult.error(500, "处理OTA升级请求失败:" + e.getMessage()))); | |
| 308 | + } | |
| 309 | + } | |
| 310 | +} | |
| 0 | 311 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/heartbeat/IotComponentHeartbeatJob.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server.heartbeat; | |
| 2 | + | |
| 3 | +import cn.hutool.system.SystemUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 6 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; | |
| 7 | +import cn.iocoder.yudao.module.iot.net.component.server.config.IotNetComponentServerProperties; | |
| 8 | +import cn.iocoder.yudao.module.iot.net.component.server.downstream.IotComponentDownstreamServer; | |
| 9 | +import lombok.extern.slf4j.Slf4j; | |
| 10 | +import org.springframework.scheduling.annotation.Scheduled; | |
| 11 | + | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | +import java.util.concurrent.ScheduledExecutorService; | |
| 14 | +import java.util.concurrent.ScheduledThreadPoolExecutor; | |
| 15 | +import java.util.concurrent.TimeUnit; | |
| 16 | +import java.lang.ProcessHandle; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * IoT 组件心跳任务 | |
| 20 | + * <p> | |
| 21 | + * 定期向主程序发送心跳,报告组件服务状态 | |
| 22 | + * | |
| 23 | + * @author haohao | |
| 24 | + */ | |
| 25 | +@Slf4j | |
| 26 | +public class IotComponentHeartbeatJob { | |
| 27 | + | |
| 28 | + private final IotDeviceUpstreamApi deviceUpstreamApi; | |
| 29 | + private final IotComponentDownstreamServer downstreamServer; | |
| 30 | + private final IotNetComponentServerProperties properties; | |
| 31 | + private ScheduledExecutorService executorService; | |
| 32 | + | |
| 33 | + public IotComponentHeartbeatJob(IotDeviceUpstreamApi deviceUpstreamApi, | |
| 34 | + IotComponentDownstreamServer downstreamServer, | |
| 35 | + IotNetComponentServerProperties properties) { | |
| 36 | + this.deviceUpstreamApi = deviceUpstreamApi; | |
| 37 | + this.downstreamServer = downstreamServer; | |
| 38 | + this.properties = properties; | |
| 39 | + } | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * 初始化心跳任务 | |
| 43 | + */ | |
| 44 | + public void init() { | |
| 45 | + log.info("[init][开始初始化心跳任务]"); | |
| 46 | + // 创建一个单线程的调度线程池 | |
| 47 | + executorService = new ScheduledThreadPoolExecutor(1); | |
| 48 | + // 延迟 5 秒后开始执行,避免服务刚启动就发送心跳 | |
| 49 | + executorService.scheduleAtFixedRate(this::sendHeartbeat, | |
| 50 | + 5000, properties.getHeartbeatInterval(), TimeUnit.MILLISECONDS); | |
| 51 | + log.info("[init][心跳任务初始化完成]"); | |
| 52 | + } | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 停止心跳任务 | |
| 56 | + */ | |
| 57 | + public void stop() { | |
| 58 | + log.info("[stop][开始停止心跳任务]"); | |
| 59 | + if (executorService != null) { | |
| 60 | + executorService.shutdown(); | |
| 61 | + executorService = null; | |
| 62 | + } | |
| 63 | + log.info("[stop][心跳任务已停止]"); | |
| 64 | + } | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 发送心跳 | |
| 68 | + */ | |
| 69 | + private void sendHeartbeat() { | |
| 70 | + try { | |
| 71 | + // 创建心跳请求 | |
| 72 | + IotPluginInstanceHeartbeatReqDTO heartbeatReqDTO = new IotPluginInstanceHeartbeatReqDTO(); | |
| 73 | + // 设置插件标识 | |
| 74 | + heartbeatReqDTO.setPluginKey(properties.getServerKey()); | |
| 75 | + // 设置进程ID | |
| 76 | + heartbeatReqDTO.setProcessId(String.valueOf(ProcessHandle.current().pid())); | |
| 77 | + // 设置IP和端口 | |
| 78 | + try { | |
| 79 | + String hostIp = SystemUtil.getHostInfo().getAddress(); | |
| 80 | + heartbeatReqDTO.setHostIp(hostIp); | |
| 81 | + heartbeatReqDTO.setDownstreamPort(downstreamServer.getPort()); | |
| 82 | + } catch (Exception e) { | |
| 83 | + log.warn("[sendHeartbeat][获取本地主机信息异常]", e); | |
| 84 | + } | |
| 85 | + // 设置在线状态 | |
| 86 | + heartbeatReqDTO.setOnline(true); | |
| 87 | + | |
| 88 | + // 发送心跳 | |
| 89 | + CommonResult<Boolean> result = deviceUpstreamApi.heartbeatPluginInstance(heartbeatReqDTO); | |
| 90 | + if (result != null && result.isSuccess()) { | |
| 91 | + log.debug("[sendHeartbeat][发送心跳成功:{}]", heartbeatReqDTO); | |
| 92 | + } else { | |
| 93 | + log.error("[sendHeartbeat][发送心跳失败:{}, 结果:{}]", heartbeatReqDTO, result); | |
| 94 | + } | |
| 95 | + } catch (Exception e) { | |
| 96 | + log.error("[sendHeartbeat][发送心跳异常]", e); | |
| 97 | + } | |
| 98 | + } | |
| 99 | + | |
| 100 | +} | |
| 0 | 101 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/upstream/IotComponentUpstreamClient.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.net.component.server.upstream; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; | |
| 5 | +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; | |
| 6 | +import cn.iocoder.yudao.module.iot.net.component.server.config.IotNetComponentServerProperties; | |
| 7 | +import lombok.RequiredArgsConstructor; | |
| 8 | +import lombok.extern.slf4j.Slf4j; | |
| 9 | +import org.springframework.web.client.RestTemplate; | |
| 10 | + | |
| 11 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * 组件上行客户端,用于向主程序上报设备数据 | |
| 15 | + * <p> | |
| 16 | + * 通过 HTTP 调用远程的 IotDeviceUpstreamApi 接口 | |
| 17 | + * | |
| 18 | + * @author haohao | |
| 19 | + */ | |
| 20 | +@RequiredArgsConstructor | |
| 21 | +@Slf4j | |
| 22 | +public class IotComponentUpstreamClient implements IotDeviceUpstreamApi { | |
| 23 | + | |
| 24 | + public static final String URL_PREFIX = "/rpc-api/iot/device/upstream"; | |
| 25 | + | |
| 26 | + private final IotNetComponentServerProperties properties; | |
| 27 | + | |
| 28 | + private final RestTemplate restTemplate; | |
| 29 | + | |
| 30 | + @Override | |
| 31 | + public CommonResult<Boolean> updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) { | |
| 32 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/update-state"; | |
| 33 | + return doPost(url, updateReqDTO); | |
| 34 | + } | |
| 35 | + | |
| 36 | + @Override | |
| 37 | + public CommonResult<Boolean> reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { | |
| 38 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-event"; | |
| 39 | + return doPost(url, reportReqDTO); | |
| 40 | + } | |
| 41 | + | |
| 42 | + @Override | |
| 43 | + public CommonResult<Boolean> registerDevice(IotDeviceRegisterReqDTO registerReqDTO) { | |
| 44 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/register-device"; | |
| 45 | + return doPost(url, registerReqDTO); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @Override | |
| 49 | + public CommonResult<Boolean> registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) { | |
| 50 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/register-sub-device"; | |
| 51 | + return doPost(url, registerReqDTO); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + public CommonResult<Boolean> addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) { | |
| 56 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/add-device-topology"; | |
| 57 | + return doPost(url, addReqDTO); | |
| 58 | + } | |
| 59 | + | |
| 60 | + @Override | |
| 61 | + public CommonResult<Boolean> authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { | |
| 62 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/authenticate-emqx-connection"; | |
| 63 | + return doPost(url, authReqDTO); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @Override | |
| 67 | + public CommonResult<Boolean> reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { | |
| 68 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property"; | |
| 69 | + return doPost(url, reportReqDTO); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @Override | |
| 73 | + public CommonResult<Boolean> heartbeatPluginInstance(IotPluginInstanceHeartbeatReqDTO heartbeatReqDTO) { | |
| 74 | + String url = properties.getUpstreamUrl() + URL_PREFIX + "/heartbeat-plugin-instance"; | |
| 75 | + return doPost(url, heartbeatReqDTO); | |
| 76 | + } | |
| 77 | + | |
| 78 | + @SuppressWarnings("unchecked") | |
| 79 | + private <T> CommonResult<Boolean> doPost(String url, T requestBody) { | |
| 80 | + try { | |
| 81 | + CommonResult<Boolean> result = restTemplate.postForObject(url, requestBody, | |
| 82 | + (Class<CommonResult<Boolean>>) (Class<?>) CommonResult.class); | |
| 83 | + log.info("[doPost][url({}) requestBody({}) result({})]", url, requestBody, result); | |
| 84 | + return result; | |
| 85 | + } catch (Exception e) { | |
| 86 | + log.error("[doPost][url({}) requestBody({}) 发生异常]", url, requestBody, e); | |
| 87 | + return CommonResult.error(INTERNAL_SERVER_ERROR); | |
| 88 | + } | |
| 89 | + } | |
| 90 | +} | |
| 0 | 91 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/resources/application.yml
0 → 100644
| 1 | +# 服务器配置 | |
| 2 | +server: | |
| 3 | + port: 18080 # 修改端口,避免与主应用的8080端口冲突 | |
| 4 | + | |
| 5 | +# Spring 配置 | |
| 6 | +spring: | |
| 7 | + application: | |
| 8 | + name: iot-component-server | |
| 9 | + # 允许循环引用 | |
| 10 | + main: | |
| 11 | + allow-circular-references: true | |
| 12 | + allow-bean-definition-overriding: true | |
| 13 | + | |
| 14 | +# Yudao 配置 | |
| 15 | +yudao: | |
| 16 | + info: | |
| 17 | + base-package: cn.iocoder.yudao # 主项目包路径,确保正确 | |
| 18 | + iot: | |
| 19 | + component: | |
| 20 | + # 这里可以覆盖或添加 component-core 中的通用配置 | |
| 21 | + instance-heartbeat-timeout: 30000 # 心跳超时时间 | |
| 22 | + | |
| 23 | + # 网络组件服务器专用配置 | |
| 24 | + server: | |
| 25 | + # 上行通信配置,用于向主程序上报数据 | |
| 26 | + upstream-url: http://127.0.0.1:48080 # 主程序 API 地址 | |
| 27 | + upstream-connect-timeout: 30s # 连接超时 | |
| 28 | + upstream-read-timeout: 30s # 读取超时 | |
| 29 | + | |
| 30 | + # 下行通信配置,用于接收主程序的控制指令 | |
| 31 | + downstream-port: 18888 # 下行服务器端口 | |
| 32 | + | |
| 33 | + # 组件服务唯一标识 | |
| 34 | + server-key: yudao-module-iot-net-component-server | |
| 35 | + | |
| 36 | + # 心跳频率,单位:毫秒 | |
| 37 | + heartbeat-interval: 30000 | |
| 38 | + | |
| 39 | + # ==================================== | |
| 40 | + # 针对引入的 HTTP 组件的配置 | |
| 41 | + # ==================================== | |
| 42 | + http: | |
| 43 | + enabled: true # 启用HTTP组件 | |
| 44 | + server-port: 8092 # HTTP组件服务端口 | |
| 45 | + | |
| 46 | + # ==================================== | |
| 47 | + # 针对引入的 EMQX 组件的配置 | |
| 48 | + # ==================================== | |
| 49 | + emqx: | |
| 50 | + enabled: true # 启用EMQX组件 | |
| 51 | + mqtt-host: 127.0.0.1 # MQTT服务器主机地址 | |
| 52 | + mqtt-port: 1883 # MQTT服务器端口 | |
| 53 | + mqtt-username: yudao # MQTT服务器用户名 | |
| 54 | + mqtt-password: 123456 # MQTT服务器密码 | |
| 55 | + mqtt-ssl: false # 是否启用SSL | |
| 56 | + mqtt-topics: # 订阅的主题列表 | |
| 57 | + - "/sys/#" | |
| 58 | + auth-port: 8101 # 认证端口 | |
| 59 | + | |
| 60 | +# 日志配置 | |
| 61 | +logging: | |
| 62 | + level: | |
| 63 | + cn.iocoder.yudao: INFO | |
| 64 | + root: INFO | |
| 0 | 65 | \ No newline at end of file | ... | ... |