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,16 +123,16 @@ | ||
| 123 | <!-- </dependency>--> | 123 | <!-- </dependency>--> |
| 124 | 124 | ||
| 125 | <!-- IoT 网络组件:接收来自设备的上行数据 --> | 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 | <dependency> | 138 | <dependency> |
yudao-module-iot/yudao-module-iot-net-components/pom.xml
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <project xmlns="http://maven.apache.org/POM/4.0.0" | 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 | <parent> | 4 | <parent> |
| 6 | <artifactId>yudao-module-iot</artifactId> | 5 | <artifactId>yudao-module-iot</artifactId> |
| 7 | <groupId>cn.iocoder.boot</groupId> | 6 | <groupId>cn.iocoder.boot</groupId> |
| @@ -21,6 +20,7 @@ | @@ -21,6 +20,7 @@ | ||
| 21 | <module>yudao-module-iot-net-component-core</module> | 20 | <module>yudao-module-iot-net-component-core</module> |
| 22 | <module>yudao-module-iot-net-component-http</module> | 21 | <module>yudao-module-iot-net-component-http</module> |
| 23 | <module>yudao-module-iot-net-component-emqx</module> | 22 | <module>yudao-module-iot-net-component-emqx</module> |
| 23 | + <module>yudao-module-iot-net-component-server</module> | ||
| 24 | </modules> | 24 | </modules> |
| 25 | 25 | ||
| 26 | </project> | 26 | </project> |
| 27 | \ No newline at end of file | 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,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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 65 | \ No newline at end of file |