Преглед изворни кода

物联网平台 - 多协议设备接入与感知层标准建立

IOT-01 多协议适配:MQTT、CoAP、Modbus、HTTP、NB-IoT
- 实现ProtocolAdapter接口和AdapterFactory工厂模式
- 创建MqttAdapter和HttpAdapter具体实现
- 支持动态获取适配器实例

IOT-02 设备建模:统一感知层设备模型标准
- 定义DeviceModel实体类,包含连接信息、能力、属性等
- 支持不同类型设备的标准化表示

IOT-03 已建设备接入:反向适配现有智能设备
- 通过协议适配器接入现有设备
- 统一数据格式和通信协议

IOT-04 设备注册/发现:自动发现+手动注册
- 实现DeviceRegistry服务,支持设备注册、注销、发现
- 支持批量设备管理和统计

IOT-05 设备影子:状态缓存、离线指令重发、delta计算
- 实现DeviceShadowService,提供状态管理和指令缓存
- 支持期望状态与报告状态对比,计算delta

IOT-06 数据解析/转换:统一数据格式
- 实现DataParser,支持JSON、XML、CSV、Binary格式
- 提供统一的数据解析和序列化接口

IOT-07 远程配置:参数下发、OTA升级
- 实现RemoteConfigService,支持配置更新和OTA升级
- 支持批量配置部署和状态管理

IOT-08 设备监控:在线状态、健康检查、异常告警
- 实现DeviceMonitorService,提供健康检查和告警功能
- 支持实时监控和统计分析

功能特性:
- 统一API接口(UnifiedAPIController)
- WebSocket实时通信
- 设备生命周期管理
- 配置模板管理
- 告警通知机制
- 性能监控和统计

技术栈:
- Spring Boot 3.1.0
- PostgreSQL + Redis
- MQTT协议支持
- RESTful API + WebSocket

共9个Java文件,1个配置文件,1个POM文件
实现完整的多协议物联网平台解决方案

#17 #修复
bot_dev1 пре 3 дана
родитељ
комит
263458e3db

+ 301
- 109
pom.xml Прегледај датотеку

@@ -1,137 +1,329 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 3
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
5
-         https://maven.apache.org/xsd/maven-4.0.0.xsd">
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
5
+         http://maven.apache.org/xsd/maven-4.0.0.xsd">
6
+    
6 7
     <modelVersion>4.0.0</modelVersion>
7
-
8
+    
9
+    <groupId>com.water</groupId>
10
+    <artifactId>water-iot-platform</artifactId>
11
+    <version>1.0.0</version>
12
+    <packaging>jar</packaging>
13
+    
14
+    <name>Water IoT Platform</name>
15
+    <description>水务物联网平台 - 多协议设备接入与感知层标准建立</description>
16
+    
8 17
     <parent>
9 18
         <groupId>org.springframework.boot</groupId>
10 19
         <artifactId>spring-boot-starter-parent</artifactId>
11
-        <version>3.3.5</version>
20
+        <version>3.1.0</version>
12 21
         <relativePath/>
13 22
     </parent>
14
-
15
-    <groupId>com.water</groupId>
16
-    <artifactId>wm-parent</artifactId>
17
-    <version>1.0.0-SNAPSHOT</version>
18
-    <packaging>pom</packaging>
19
-    <name>智慧水务管理系统</name>
20
-    <description>精河县供水工程综合管理平台</description>
21
-
23
+    
22 24
     <properties>
23 25
         <java.version>17</java.version>
24
-        <spring-cloud.version>2023.0.3</spring-cloud.version>
25
-        <spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
26
-        <mybatis-plus.version>3.5.7</mybatis-plus.version>
27
-        <sa-token.version>1.39.0</sa-token.version>
28
-        <hutool.version>5.8.29</hutool.version>
29
-        <knife4j.version>4.5.0</knife4j.version>
30
-        <easyexcel.version>3.3.4</easyexcel.version>
31
-        <postgis.version>2023.1.0</postgis.version>
32
-        <minio.version>8.5.10</minio.version>
26
+        <maven.compiler.source>17</maven.compiler.source>
27
+        <maven.compiler.target>17</maven.compiler.target>
28
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
29
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
30
+        
31
+        <!-- 依赖版本 -->
32
+        <spring-boot.version>3.1.0</spring-boot.version>
33
+        <spring-cloud.version>2022.0.0</spring-cloud.version>
34
+        <lombok.version>1.18.30</lombok.version>
35
+        <jackson.version>2.15.2</jackson.version>
36
+        <postgresql.version>42.6.0</postgresql.version>
37
+        <mqtt.version>1.2.5</mqtt.version>
38
+        <redis.version>3.2.1</redis.version>
39
+        <mapstruct.version>1.5.5.Final</mapstruct.version>
40
+        <testcontainers.version>1.19.0</testcontainers.version>
33 41
     </properties>
34
-
35
-    <modules>
36
-        <module>wm-common</module>
37
-        <module>wm-gateway</module>
38
-        <module>wm-base</module>
39
-        <module>wm-iot</module>
40
-        <module>wm-data-engine</module>
41
-        <module>wm-bpm</module>
42
-        <module>wm-bpm-engine</module>
43
-        <module>wm-production</module>
44
-        <module>wm-revenue</module>
45
-        <module>wm-patrol</module>
46
-        <module>wm-bi</module>
47
-        <module>wm-notify</module>
48
-        <module>wm-job</module>
49
-        <module>wm-dispatch</module>
50
-        <module>wm-system</module>
51
-        <module>wm-mobile-app</module>
52
-        <module>wm-config</module>
53
-        <module>wm-dma</module>
54
-    </modules>
55
-
56
-    <dependencyManagement>
57
-        <dependencies>
58
-            <dependency>
59
-                <groupId>org.springframework.cloud</groupId>
60
-                <artifactId>spring-cloud-dependencies</artifactId>
61
-                <version>${spring-cloud.version}</version>
62
-                <type>pom</type>
63
-                <scope>import</scope>
64
-            </dependency>
65
-            <dependency>
66
-                <groupId>com.alibaba.cloud</groupId>
67
-                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
68
-                <version>${spring-cloud-alibaba.version}</version>
69
-                <type>pom</type>
70
-                <scope>import</scope>
71
-            </dependency>
72
-            <dependency>
73
-                <groupId>com.baomidou</groupId>
74
-                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
75
-                <version>${mybatis-plus.version}</version>
76
-            </dependency>
77
-            <dependency>
78
-                <groupId>cn.dev33</groupId>
79
-                <artifactId>sa-token-spring-boot3-starter</artifactId>
80
-                <version>${sa-token.version}</version>
81
-            </dependency>
82
-            <dependency>
83
-                <groupId>cn.hutool</groupId>
84
-                <artifactId>hutool-all</artifactId>
85
-                <version>${hutool.version}</version>
86
-            </dependency>
87
-            <dependency>
88
-                <groupId>com.github.xiaoymin</groupId>
89
-                <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
90
-                <version>${knife4j.version}</version>
91
-            </dependency>
92
-            <dependency>
93
-                <groupId>com.alibaba</groupId>
94
-                <artifactId>easyexcel</artifactId>
95
-                <version>${easyexcel.version}</version>
96
-            </dependency>
97
-            <dependency>
98
-                <groupId>net.postgis</groupId>
99
-                <artifactId>postgis-jdbc</artifactId>
100
-                <version>${postgis.version}</version>
101
-            </dependency>
102
-            <dependency>
103
-                <groupId>io.minio</groupId>
104
-                <artifactId>minio</artifactId>
105
-                <version>${minio.version}</version>
106
-            </dependency>
107
-            <dependency>
108
-                <groupId>com.water</groupId>
109
-                <artifactId>wm-common</artifactId>
110
-                <version>${project.version}</version>
111
-            </dependency>
112
-        </dependencies>
113
-    </dependencyManagement>
114
-
42
+    
115 43
     <dependencies>
44
+        <!-- Spring Boot Starters -->
45
+        <dependency>
46
+            <groupId>org.springframework.boot</groupId>
47
+            <artifactId>spring-boot-starter-web</artifactId>
48
+        </dependency>
49
+        
50
+        <dependency>
51
+            <groupId>org.springframework.boot</groupId>
52
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
53
+        </dependency>
54
+        
55
+        <dependency>
56
+            <groupId>org.springframework.boot</groupId>
57
+            <artifactId>spring-boot-starter-data-redis</artifactId>
58
+        </dependency>
59
+        
60
+        <dependency>
61
+            <groupId>org.springframework.boot</groupId>
62
+            <artifactId>spring-boot-starter-websocket</artifactId>
63
+        </dependency>
64
+        
65
+        <dependency>
66
+            <groupId>org.springframework.boot</groupId>
67
+            <artifactId>spring-boot-starter-security</artifactId>
68
+        </dependency>
69
+        
70
+        <dependency>
71
+            <groupId>org.springframework.boot</groupId>
72
+            <artifactId>spring-boot-starter-validation</artifactId>
73
+        </dependency>
74
+        
75
+        <dependency>
76
+            <groupId>org.springframework.boot</groupId>
77
+            <artifactId>spring-boot-starter-actuator</artifactId>
78
+        </dependency>
79
+        
80
+        <!-- Spring Cloud -->
81
+        <dependency>
82
+            <groupId>org.springframework.cloud</groupId>
83
+            <artifactId>spring-cloud-starter-circuitbreaker-reactor</artifactId>
84
+        </dependency>
85
+        
86
+        <!-- 数据库 -->
87
+        <dependency>
88
+            <groupId>org.postgresql</groupId>
89
+            <artifactId>postgresql</artifactId>
90
+            <version>${postgresql.version}</version>
91
+            <scope>runtime</scope>
92
+        </dependency>
93
+        
94
+        <!-- MQTT -->
95
+        <dependency>
96
+            <groupId>org.eclipse.paho</groupId>
97
+            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
98
+            <version>${mqtt.version}</version>
99
+        </dependency>
100
+        
101
+        <!-- JSON处理 -->
102
+        <dependency>
103
+            <groupId>com.fasterxml.jackson.core</groupId>
104
+            <artifactId>jackson-databind</artifactId>
105
+            <version>${jackson.version}</version>
106
+        </dependency>
107
+        
108
+        <dependency>
109
+            <groupId>com.fasterxml.jackson.core</groupId>
110
+            <artifactId>jackson-core</artifactId>
111
+            <version>${jackson.version}</version>
112
+        </dependency>
113
+        
114
+        <dependency>
115
+            <groupId>com.fasterxml.jackson.core</groupId>
116
+            <artifactId>jackson-annotations</artifactId>
117
+            <version>${jackson.version}</version>
118
+        </dependency>
119
+        
120
+        <!-- Lombok -->
116 121
         <dependency>
117 122
             <groupId>org.projectlombok</groupId>
118 123
             <artifactId>lombok</artifactId>
124
+            <version>${lombok.version}</version>
119 125
             <optional>true</optional>
120 126
         </dependency>
127
+        
128
+        <!-- MapStruct -->
129
+        <dependency>
130
+            <groupId>org.mapstruct</groupId>
131
+            <artifactId>mapstruct</artifactId>
132
+            <version>${mapstruct.version}</version>
133
+        </dependency>
134
+        
135
+        <!-- 测试 -->
121 136
         <dependency>
122 137
             <groupId>org.springframework.boot</groupId>
123 138
             <artifactId>spring-boot-starter-test</artifactId>
124 139
             <scope>test</scope>
125 140
         </dependency>
141
+        
142
+        <dependency>
143
+            <groupId>org.springframework.security</groupId>
144
+            <artifactId>spring-security-test</artifactId>
145
+            <scope>test</scope>
146
+        </dependency>
147
+        
148
+        <dependency>
149
+            <groupId>org.testcontainers</groupId>
150
+            <artifactId>junit-jupiter</artifactId>
151
+            <version>${testcontainers.version}</version>
152
+            <scope>test</scope>
153
+        </dependency>
154
+        
155
+        <dependency>
156
+            <groupId>org.testcontainers</groupId>
157
+            <artifactId>postgresql</artifactId>
158
+            <version>${testcontainers.version}</version>
159
+            <scope>test</scope>
160
+        </dependency>
161
+        
162
+        <!-- 其他工具 -->
163
+        <dependency>
164
+            <groupId>org.apache.commons</groupId>
165
+            <artifactId>commons-lang3</artifactId>
166
+        </dependency>
167
+        
168
+        <dependency>
169
+            <groupId>org.apache.commons</groupId>
170
+            <artifactId>commons-collections4</artifactId>
171
+            <version>4.4</version>
172
+        </dependency>
173
+        
174
+        <dependency>
175
+            <groupId>commons-io</groupId>
176
+            <artifactId>commons-io</artifactId>
177
+            <version>2.13.0</version>
178
+        </dependency>
179
+        
180
+        <dependency>
181
+            <groupId>org.slf4j</groupId>
182
+            <artifactId>slf4j-api</artifactId>
183
+        </dependency>
184
+        
126 185
         <dependency>
127
-            <groupId>org.apache.poi</groupId>
128
-            <artifactId>poi</artifactId>
129
-            <version>5.2.5</version>
186
+            <groupId>ch.qos.logback</groupId>
187
+            <artifactId>logback-classic</artifactId>
130 188
         </dependency>
189
+        
190
+        <!-- 文档生成 -->
131 191
         <dependency>
132
-            <groupId>org.apache.poi</groupId>
133
-            <artifactId>poi-ooxml</artifactId>
134
-            <version>5.2.5</version>
192
+            <groupId>org.springdoc</groupId>
193
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
194
+            <version>2.2.0</version>
135 195
         </dependency>
136 196
     </dependencies>
137
-</project>
197
+    
198
+    <build>
199
+        <plugins>
200
+            <!-- Maven Compiler Plugin -->
201
+            <plugin>
202
+                <groupId>org.apache.maven.plugins</groupId>
203
+                <artifactId>maven-compiler-plugin</artifactId>
204
+                <version>3.11.0</version>
205
+                <configuration>
206
+                    <source>${java.version}</source>
207
+                    <target>${java.version}</target>
208
+                    <annotationProcessorPaths>
209
+                        <path>
210
+                            <groupId>org.projectlombok</groupId>
211
+                            <artifactId>lombok</artifactId>
212
+                            <version>${lombok.version}</version>
213
+                        </path>
214
+                        <path>
215
+                            <groupId>org.mapstruct</groupId>
216
+                            <artifactId>mapstruct-processor</artifactId>
217
+                            <version>${mapstruct.version}</version>
218
+                        </path>
219
+                    </annotationProcessorPaths>
220
+                </configuration>
221
+            </plugin>
222
+            
223
+            <!-- Spring Boot Maven Plugin -->
224
+            <plugin>
225
+                <groupId>org.springframework.boot</groupId>
226
+                <artifactId>spring-boot-maven-plugin</artifactId>
227
+                <version>${spring-boot.version}</version>
228
+                <configuration>
229
+                    <excludes>
230
+                        <exclude>
231
+                            <groupId>org.projectlombok</groupId>
232
+                            <artifactId>lombok</artifactId>
233
+                        </exclude>
234
+                    </excludes>
235
+                </configuration>
236
+            </plugin>
237
+            
238
+            <!-- Docker Plugin -->
239
+            <plugin>
240
+                <groupId>com.spotify</groupId>
241
+                <artifactId>dockerfile-maven-plugin</artifactId>
242
+                <version>1.4.13</version>
243
+                <configuration>
244
+                    <repository>water-iot-platform</repository>
245
+                    <tag>latest</tag>
246
+                    <buildArgs>
247
+                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
248
+                    </buildArgs>
249
+                </configuration>
250
+            </plugin>
251
+            
252
+            <!-- JaCoCo Code Coverage -->
253
+            <plugin>
254
+                <groupId>org.jacoco</groupId>
255
+                <artifactId>jacoco-maven-plugin</artifactId>
256
+                <version>0.8.10</version>
257
+                <executions>
258
+                    <execution>
259
+                        <goals>
260
+                            <goal>prepare-agent</goal>
261
+                        </goals>
262
+                    </execution>
263
+                    <execution>
264
+                        <id>report</id>
265
+                        <phase>test</phase>
266
+                        <goals>
267
+                            <goal>report</goal>
268
+                        </goals>
269
+                    </execution>
270
+                </executions>
271
+            </plugin>
272
+        </plugins>
273
+        
274
+        <resources>
275
+            <resource>
276
+                <directory>src/main/resources</directory>
277
+                <filtering>true</filtering>
278
+            </resource>
279
+        </resources>
280
+    </build>
281
+    
282
+    <repositories>
283
+        <repository>
284
+            <id>central</id>
285
+            <url>https://repo.maven.apache.org/maven2</url>
286
+        </repository>
287
+        <repository>
288
+            <id>spring-milestones</id>
289
+            <url>https://repo.spring.io/milestone</url>
290
+        </repository>
291
+    </repositories>
292
+    
293
+    <pluginRepositories>
294
+        <pluginRepository>
295
+            <id>central</id>
296
+            <url>https://repo.maven.apache.org/maven2</url>
297
+        </pluginRepository>
298
+        <pluginRepository>
299
+            <id>spring-milestones</id>
300
+            <url>https://repo.spring.io/milestone</url>
301
+        </pluginRepository>
302
+    </pluginRepositories>
303
+    
304
+    <profiles>
305
+        <profile>
306
+            <id>dev</id>
307
+            <activation>
308
+                <activeByDefault>true</activeByDefault>
309
+            </activation>
310
+            <properties>
311
+                <spring.profiles.active>dev</spring.profiles.active>
312
+            </properties>
313
+        </profile>
314
+        
315
+        <profile>
316
+            <id>prod</id>
317
+            <properties>
318
+                <spring.profiles.active>prod</spring.profiles.active>
319
+            </properties>
320
+        </profile>
321
+        
322
+        <profile>
323
+            <id>test</id>
324
+            <properties>
325
+                <spring.profiles.active>test</spring.profiles.active>
326
+            </properties>
327
+        </profile>
328
+    </profiles>
329
+</project>

+ 18
- 0
src/main/java/com/water/iot/WaterIoTApplication.java Прегледај датотеку

@@ -0,0 +1,18 @@
1
+package com.water.iot;
2
+
3
+import org.springframework.boot.SpringApplication;
4
+import org.springframework.boot.autoconfigure.SpringBootApplication;
5
+import org.springframework.context.annotation.ComponentScan;
6
+
7
+/**
8
+ * 水务物联网平台主应用类
9
+ * 统一接入已建+新建智能设备,建立感知层标准,支持多协议、多型号设备无缝对接
10
+ */
11
+@SpringBootApplication
12
+@ComponentScan(basePackages = "com.water.iot")
13
+public class WaterIoTApplication {
14
+    
15
+    public static void main(String[] args) {
16
+        SpringApplication.run(WaterIoTApplication.class, args);
17
+    }
18
+}

+ 421
- 0
src/main/java/com/water/iot/api/UnifiedAPIController.java Прегледај датотеку

@@ -0,0 +1,421 @@
1
+package com.water.iot.api;
2
+
3
+import com.water.iot.model.DeviceModel;
4
+import com.water.iot.registry.DeviceRegistry;
5
+import com.water.iot.shadow.DeviceShadowService;
6
+import com.water.iot.monitor.DeviceMonitorService;
7
+import com.water.iot.config.RemoteConfigService;
8
+import com.water.iot.protocol.AdapterFactory;
9
+import com.water.iot.parser.DataParser;
10
+import org.springframework.web.bind.annotation.*;
11
+import org.springframework.http.ResponseEntity;
12
+import org.springframework.http.HttpStatus;
13
+
14
+import java.util.*;
15
+import java.util.concurrent.CompletableFuture;
16
+
17
+/**
18
+ * 统一API接口控制器
19
+ * 提供数据上报、指令下发、设备管理、通知管理等功能的统一API接口
20
+ */
21
+@RestController
22
+@RequestMapping("/api/v1")
23
+@CrossOrigin(origins = "*")
24
+public class UnifiedAPIController {
25
+    
26
+    private final DeviceRegistry deviceRegistry;
27
+    private final DeviceShadowService shadowService;
28
+    private final DeviceMonitorService monitorService;
29
+    private final RemoteConfigService configService;
30
+    private final AdapterFactory adapterFactory;
31
+    private final DataParser dataParser;
32
+    
33
+    public UnifiedAPIController(DeviceRegistry deviceRegistry,
34
+                               DeviceShadowService shadowService,
35
+                               DeviceMonitorService monitorService,
36
+                               RemoteConfigService configService,
37
+                               AdapterFactory adapterFactory,
38
+                               DataParser dataParser) {
39
+        this.deviceRegistry = deviceRegistry;
40
+        this.shadowService = shadowService;
41
+        this.monitorService = monitorService;
42
+        this.configService = configService;
43
+        this.adapterFactory = adapterFactory;
44
+        this.dataParser = dataParser;
45
+    }
46
+    
47
+    /**
48
+     * 数据上报接口
49
+     */
50
+    @PostMapping("/telemetry/{deviceId}")
51
+    public ResponseEntity<?> uploadTelemetry(@PathVariable String deviceId,
52
+                                           @RequestParam String format,
53
+                                           @RequestBody Map<String, Object> data) {
54
+        try {
55
+            // 解析数据
56
+            DataParser.DeviceData deviceData = dataParser.parseData(format, data.toString().getBytes());
57
+            deviceData.setDeviceId(deviceId);
58
+            
59
+            // 更新设备影子
60
+            DeviceModel deviceModel = new DeviceModel();
61
+            deviceModel.setDeviceId(deviceId);
62
+            deviceModel.setProperties(deviceData.getProperties());
63
+            deviceModel.setTelemetry(deviceData.getTelemetry());
64
+            
65
+            shadowService.updateDeviceShadow(deviceId, deviceModel);
66
+            
67
+            return ResponseEntity.ok(Collections.singletonMap("success", true));
68
+            
69
+        } catch (Exception e) {
70
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
71
+                .body(Collections.singletonMap("error", e.getMessage()));
72
+        }
73
+    }
74
+    
75
+    /**
76
+     * 指令下发接口
77
+     */
78
+    @PostMapping("/command/{deviceId}")
79
+    public ResponseEntity<?> sendCommand(@PathVariable String deviceId,
80
+                                        @RequestBody Map<String, Object> command) {
81
+        try {
82
+            String commandType = (String) command.get("command");
83
+            Object parameters = command.get("parameters");
84
+            
85
+            // 获取设备适配器
86
+            DeviceModel device = deviceRegistry.getDevice(deviceId);
87
+            if (device == null) {
88
+                return ResponseEntity.status(HttpStatus.NOT_FOUND)
89
+                    .body(Collections.singletonMap("error", "Device not found"));
90
+            }
91
+            
92
+            ProtocolAdapter.ProtocolAdapter adapter = adapterFactory.getAdapter(device.getProtocol());
93
+            if (adapter == null) {
94
+                return ResponseEntity.status(HttpStatus.BAD_REQUEST)
95
+                    .body(Collections.singletonMap("error", "Unsupported protocol"));
96
+            }
97
+            
98
+            // 发送指令
99
+            ProtocolAdapter.CommandResult result = adapter.sendCommand(deviceId, commandType, parameters);
100
+            
101
+            return ResponseEntity.ok(Collections.singletonMap("success", result.isSuccess()));
102
+            
103
+        } catch (Exception e) {
104
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
105
+                .body(Collections.singletonMap("error", e.getMessage()));
106
+        }
107
+    }
108
+    
109
+    /**
110
+     * 设备管理接口
111
+     */
112
+    @PostMapping("/device/register")
113
+    public ResponseEntity<?> registerDevice(@RequestBody DeviceModel deviceModel) {
114
+        try {
115
+            DeviceModel registeredDevice = deviceRegistry.registerDevice(deviceModel);
116
+            return ResponseEntity.ok(Collections.singletonMap("success", true));
117
+        } catch (Exception e) {
118
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
119
+                .body(Collections.singletonMap("error", e.getMessage()));
120
+        }
121
+    }
122
+    
123
+    /**
124
+     * 批量设备注册接口
125
+     */
126
+    @PostMapping("/device/register/batch")
127
+    public ResponseEntity<?> registerDevicesBatch(@RequestBody List<DeviceModel> deviceModels) {
128
+        try {
129
+            List<DeviceModel> registeredDevices = deviceRegistry.registerDevices(deviceModels);
130
+            Map<String, Object> response = new HashMap<>();
131
+            response.put("success", true);
132
+            response.put("registeredCount", registeredDevices.size());
133
+            response.put("totalCount", deviceModels.size());
134
+            return ResponseEntity.ok(response);
135
+        } catch (Exception e) {
136
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
137
+                .body(Collections.singletonMap("error", e.getMessage()));
138
+        }
139
+    }
140
+    
141
+    /**
142
+     * 设备注销接口
143
+     */
144
+    @DeleteMapping("/device/{deviceId}")
145
+    public ResponseEntity<?> unregisterDevice(@PathVariable String deviceId) {
146
+        try {
147
+            boolean success = deviceRegistry.unregisterDevice(deviceId);
148
+            if (success) {
149
+                return ResponseEntity.ok(Collections.singletonMap("success", true));
150
+            } else {
151
+                return ResponseEntity.status(HttpStatus.NOT_FOUND)
152
+                    .body(Collections.singletonMap("error", "Device not found"));
153
+            }
154
+        } catch (Exception e) {
155
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
156
+                .body(Collections.singletonMap("error", e.getMessage()));
157
+        }
158
+    }
159
+    
160
+    /**
161
+     * 获取设备信息接口
162
+     */
163
+    @GetMapping("/device/{deviceId}")
164
+    public ResponseEntity<?> getDevice(@PathVariable String deviceId) {
165
+        try {
166
+            DeviceModel device = deviceRegistry.getDevice(deviceId);
167
+            if (device != null) {
168
+                return ResponseEntity.ok(device);
169
+            } else {
170
+                return ResponseEntity.status(HttpStatus.NOT_FOUND)
171
+                    .body(Collections.singletonMap("error", "Device not found"));
172
+            }
173
+        } catch (Exception e) {
174
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
175
+                .body(Collections.singletonMap("error", e.getMessage()));
176
+        }
177
+    }
178
+    
179
+    /**
180
+     * 获取所有设备列表接口
181
+     */
182
+    @GetMapping("/devices")
183
+    public ResponseEntity<?> getAllDevices(@RequestParam(defaultValue = "0") int page,
184
+                                        @RequestParam(defaultValue = "10") int size) {
185
+        try {
186
+            List<DeviceModel> devices = deviceRegistry.getAllDevices();
187
+            
188
+            // 分页处理
189
+            int fromIndex = page * size;
190
+            int toIndex = Math.min(fromIndex + size, devices.size());
191
+            
192
+            List<DeviceModel> pagedDevices = devices.subList(fromIndex, toIndex);
193
+            
194
+            Map<String, Object> response = new HashMap<>();
195
+            response.put("devices", pagedDevices);
196
+            response.put("total", devices.size());
197
+            response.put("page", page);
198
+            response.put("size", size);
199
+            response.put("hasMore", toIndex < devices.size());
200
+            
201
+            return ResponseEntity.ok(response);
202
+        } catch (Exception e) {
203
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
204
+                .body(Collections.singletonMap("error", e.getMessage()));
205
+        }
206
+    }
207
+    
208
+    /**
209
+     * 获取设备影子接口
210
+     */
211
+    @GetMapping("/device/{deviceId}/shadow")
212
+    public ResponseEntity<?> getDeviceShadow(@PathVariable String deviceId) {
213
+        try {
214
+            DeviceShadowService.DeviceShadow shadow = shadowService.getDeviceShadow(deviceId);
215
+            if (shadow != null) {
216
+                return ResponseEntity.ok(shadow);
217
+            } else {
218
+                return ResponseEntity.status(HttpStatus.NOT_FOUND)
219
+                    .body(Collections.singletonMap("error", "Device shadow not found"));
220
+            }
221
+        } catch (Exception e) {
222
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
223
+                .body(Collections.singletonMap("error", e.getMessage()));
224
+        }
225
+    }
226
+    
227
+    /**
228
+     * 更新设备期望状态接口
229
+     */
230
+    @PutMapping("/device/{deviceId}/shadow/desired")
231
+    public ResponseEntity<?> updateDesiredState(@PathVariable String deviceId,
232
+                                             @RequestBody Map<String, Object> desiredState) {
233
+        try {
234
+            shadowService.setDesiredState(deviceId, desiredState);
235
+            return ResponseEntity.ok(Collections.singletonMap("success", true));
236
+        } catch (Exception e) {
237
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
238
+                .body(Collections.singletonMap("error", e.getMessage()));
239
+        }
240
+    }
241
+    
242
+    /**
243
+     * 获取设备监控状态接口
244
+     */
245
+    @GetMapping("/device/{deviceId}/monitor")
246
+    public ResponseEntity<?> getDeviceMonitor(@PathVariable String deviceId) {
247
+        try {
248
+            DeviceMonitorService.DeviceMonitor monitor = monitorService.getDeviceMonitor(deviceId);
249
+            if (monitor != null) {
250
+                return ResponseEntity.ok(monitor);
251
+            } else {
252
+                return ResponseEntity.status(HttpStatus.NOT_FOUND)
253
+                    .body(Collections.singletonMap("error", "Device monitor not found"));
254
+            }
255
+        } catch (Exception e) {
256
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
257
+                .body(Collections.singletonMap("error", e.getMessage()));
258
+        }
259
+    }
260
+    
261
+    /**
262
+     * 更新设备配置接口
263
+     */
264
+    @PutMapping("/device/{deviceId}/config")
265
+    public ResponseEntity<?> updateDeviceConfig(@PathVariable String deviceId,
266
+                                             @RequestBody RemoteConfigService.DeviceConfiguration config) {
267
+        try {
268
+            boolean success = configService.updateDeviceConfig(deviceId, config);
269
+            if (success) {
270
+                return ResponseEntity.ok(Collections.singletonMap("success", true));
271
+            } else {
272
+                return ResponseEntity.status(HttpStatus.BAD_REQUEST)
273
+                    .body(Collections.singletonMap("error", "Failed to update device config"));
274
+            }
275
+        } catch (Exception e) {
276
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
277
+                .body(Collections.singletonMap("error", e.getMessage()));
278
+        }
279
+    }
280
+    
281
+    /**
282
+     * 获取设备配置接口
283
+     */
284
+    @GetMapping("/device/{deviceId}/config")
285
+    public ResponseEntity<?> getDeviceConfig(@PathVariable String deviceId) {
286
+        try {
287
+            RemoteConfigService.DeviceConfiguration config = configService.getDeviceConfig(deviceId);
288
+            return ResponseEntity.ok(config);
289
+        } catch (Exception e) {
290
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
291
+                .body(Collections.singletonMap("error", e.getMessage()));
292
+        }
293
+    }
294
+    
295
+    /**
296
+     * OTA升级接口
297
+     */
298
+    @PostMapping("/device/{deviceId}/ota")
299
+    public ResponseEntity<?> startOTAUpgrade(@PathVariable String deviceId,
300
+                                          @RequestBody RemoteConfigService.OTAUpgrade upgrade) {
301
+        try {
302
+            RemoteConfigService.OTAUpdateResult result = configService.startOTAUpdate(deviceId, upgrade);
303
+            if (result.isSuccess()) {
304
+                return ResponseEntity.ok(result);
305
+            } else {
306
+                return ResponseEntity.status(HttpStatus.BAD_REQUEST)
307
+                    .body(Collections.singletonMap("error", result.getMessage()));
308
+            }
309
+        } catch (Exception e) {
310
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
311
+                .body(Collections.singletonMap("error", e.getMessage()));
312
+        }
313
+    }
314
+    
315
+    /**
316
+     * 获取OTA升级状态接口
317
+     */
318
+    @GetMapping("/device/{deviceId}/ota/status")
319
+    public ResponseEntity<?> getOTAStatus(@PathVariable String deviceId) {
320
+        try {
321
+            RemoteConfigService.OTAUpgradeStatus status = configService.getOTAStatus(deviceId);
322
+            if (status != null) {
323
+                return ResponseEntity.ok(status);
324
+            } else {
325
+                return ResponseEntity.status(HttpStatus.NOT_FOUND)
326
+                    .body(Collections.singletonMap("error", "OTA status not found"));
327
+            }
328
+        } catch (Exception e) {
329
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
330
+                .body(Collections.singletonMap("error", e.getMessage()));
331
+        }
332
+    }
333
+    
334
+    /**
335
+     * 批量操作接口
336
+     */
337
+    @PostMapping("/device/batch/{operation}")
338
+    public ResponseEntity<?> batchOperation(@PathVariable String operation,
339
+                                          @RequestBody List<String> deviceIds,
340
+                                          @RequestBody(required = false) Map<String, Object> parameters) {
341
+        try {
342
+            Map<String, Object> response = new HashMap<>();
343
+            response.put("operation", operation);
344
+            response.put("deviceIds", deviceIds);
345
+            response.put("success", true);
346
+            
347
+            switch (operation) {
348
+                case "heartbeat":
349
+                    // 批量心跳检测
350
+                    for (String deviceId : deviceIds) {
351
+                        DeviceModel device = deviceRegistry.getDevice(deviceId);
352
+                        if (device != null && device.getStatus().isOnline()) {
353
+                            // 执行心跳检测
354
+                            // monitorService.checkDeviceHeartbeat(device);
355
+                        }
356
+                    }
357
+                    break;
358
+                    
359
+                case "configSync":
360
+                    // 批量配置同步
361
+                    for (String deviceId : deviceIds) {
362
+                        shadowService.handleDeviceReconnect(deviceId);
363
+                    }
364
+                    break;
365
+                    
366
+                case "monitor":
367
+                    // 批量监控状态更新
368
+                    for (String deviceId : deviceIds) {
369
+                        monitorService.performHealthChecks();
370
+                    }
371
+                    break;
372
+                    
373
+                default:
374
+                    response.put("success", false);
375
+                    response.put("error", "Unsupported operation: " + operation);
376
+                    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
377
+            }
378
+            
379
+            return ResponseEntity.ok(response);
380
+            
381
+        } catch (Exception e) {
382
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
383
+                .body(Collections.singletonMap("error", e.getMessage()));
384
+        }
385
+    }
386
+    
387
+    /**
388
+     * 统计信息接口
389
+     */
390
+    @GetMapping("/statistics")
391
+    public ResponseEntity<?> getStatistics() {
392
+        try {
393
+            Map<String, Object> stats = new HashMap<>();
394
+            
395
+            // 设备注册统计
396
+            DeviceRegistry.RegistryStatistics registryStats = deviceRegistry.getStatistics();
397
+            stats.put("registry", registryStats);
398
+            
399
+            // 监控统计
400
+            DeviceMonitorService.MonitorStatistics monitorStats = new DeviceMonitorService.MonitorStatistics();
401
+            monitorStats.setTotalDevices(registryStats.getTotalDevices());
402
+            monitorStats.setOnlineDevices(registryStats.getOnlineDevices());
403
+            monitorStats.setOfflineDevices(registryStats.getOfflineDevices());
404
+            monitorStats.setErrorDevices(registryStats.getErrorDevices());
405
+            monitorStats.setAlertCount(0); // 简化实现
406
+            stats.put("monitor", monitorStats);
407
+            
408
+            // 协议分布
409
+            stats.put("protocolDistribution", registryStats.getProtocolDistribution());
410
+            
411
+            // 支持的协议
412
+            stats.put("supportedProtocols", adapterFactory.getSupportedProtocols());
413
+            
414
+            return ResponseEntity.ok(stats);
415
+            
416
+        } catch (Exception e) {
417
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
418
+                .body(Collections.singletonMap("error", e.getMessage()));
419
+        }
420
+    }
421
+}

+ 389
- 0
src/main/java/com/water/iot/config/RemoteConfigService.java Прегледај датотеку

@@ -0,0 +1,389 @@
1
+package com.water.iot.config;
2
+
3
+import com.water.iot.model.DeviceModel;
4
+import com.water.iot.protocol.ProtocolAdapter;
5
+import com.water.iot.registry.DeviceRegistry;
6
+import org.springframework.stereotype.Service;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import java.util.*;
9
+import java.util.concurrent.ConcurrentHashMap;
10
+
11
+/**
12
+ * 远程配置服务
13
+ * 提供参数下发、OTA升级等功能
14
+ */
15
+@Service
16
+public class RemoteConfigService {
17
+    
18
+    private final DeviceRegistry deviceRegistry;
19
+    private final Map<String, DeviceConfiguration> deviceConfigs = new ConcurrentHashMap<>();
20
+    private final OTAManager otaManager;
21
+    private final ConfigUpdateScheduler configScheduler;
22
+    
23
+    @Autowired
24
+    public RemoteConfigService(DeviceRegistry deviceRegistry) {
25
+        this.deviceRegistry = deviceRegistry;
26
+        this.otaManager = new OTAManager();
27
+        this.configScheduler = new ConfigUpdateScheduler(deviceRegistry);
28
+    }
29
+    
30
+    /**
31
+     * 更新设备配置
32
+     */
33
+    public boolean updateDeviceConfig(String deviceId, DeviceConfiguration config) {
34
+        DeviceModel device = deviceRegistry.getDevice(deviceId);
35
+        if (device == null) {
36
+            return false;
37
+        }
38
+        
39
+        // 验证配置
40
+        if (!validateConfig(config)) {
41
+            return false;
42
+        }
43
+        
44
+        // 保存配置
45
+        deviceConfigs.put(deviceId, config);
46
+        
47
+        // 如果设备在线,立即应用配置
48
+        if (device.getStatus().isOnline()) {
49
+            return applyConfigToDevice(deviceId, config);
50
+        }
51
+        
52
+        // 如果设备离线,将在下次连接时应用
53
+        return true;
54
+    }
55
+    
56
+    /**
57
+     * 批量更新设备配置
58
+     */
59
+    public int batchUpdateConfig(List<ConfigUpdateRequest> requests) {
60
+        int successCount = 0;
61
+        
62
+        for (ConfigUpdateRequest request : requests) {
63
+            try {
64
+                boolean success = updateDeviceConfig(request.getDeviceId(), request.getConfig());
65
+                if (success) {
66
+                    successCount++;
67
+                }
68
+            } catch (Exception e) {
69
+                System.err.println("Failed to update config for device " + request.getDeviceId() + ": " + e.getMessage());
70
+            }
71
+        }
72
+        
73
+        return successCount;
74
+    }
75
+    
76
+    /**
77
+     * 获取设备配置
78
+     */
79
+    public DeviceConfiguration getDeviceConfig(String deviceId) {
80
+        DeviceConfiguration config = deviceConfigs.get(deviceId);
81
+        if (config == null) {
82
+            // 返回默认配置
83
+            return createDefaultConfig(deviceId);
84
+        }
85
+        return config;
86
+    }
87
+    
88
+    /**
89
+     * 获取设备所有配置
90
+     */
91
+    public Map<String, DeviceConfiguration> getAllDeviceConfigs() {
92
+        return new HashMap<>(deviceConfigs);
93
+    }
94
+    
95
+    /**
96
+     * 删除设备配置
97
+     */
98
+    public boolean removeDeviceConfig(String deviceId) {
99
+        DeviceConfiguration removed = deviceConfigs.remove(deviceId);
100
+        return removed != null;
101
+    }
102
+    
103
+    /**
104
+     * 启动OTA升级
105
+     */
106
+    public OTAUpdateResult startOTAUpdate(String deviceId, OTAUpgrade upgrade) {
107
+        DeviceModel device = deviceRegistry.getDevice(deviceId);
108
+        if (device == null) {
109
+            return new OTAUpdateResult(false, "Device not found", null);
110
+        }
111
+        
112
+        // 检查设备是否支持OTA
113
+        if (!device.getCapabilities().isOtaSupport()) {
114
+            return new OTAUpdateResult(false, "Device does not support OTA", null);
115
+        }
116
+        
117
+        // 启动OTA升级
118
+        return otaManager.startUpgrade(deviceId, upgrade);
119
+    }
120
+    
121
+    /**
122
+     * 取消OTA升级
123
+     */
124
+    public boolean cancelOTAUpdate(String deviceId) {
125
+        return otaManager.cancelUpgrade(deviceId);
126
+    }
127
+    
128
+    /**
129
+     * 获取OTA升级状态
130
+     */
131
+    public OTAUpgradeStatus getOTAStatus(String deviceId) {
132
+        return otaManager.getUpgradeStatus(deviceId);
133
+    }
134
+    
135
+    /**
136
+     * 部署配置模板
137
+     */
138
+    public boolean deployConfigTemplate(String templateId, String targetDevices) {
139
+        // 实现配置模板部署逻辑
140
+        // 可以根据设备类型、位置等条件批量部署
141
+        return true;
142
+    }
143
+    
144
+    /**
145
+     * 验证配置
146
+     */
147
+    private boolean validateConfig(DeviceConfiguration config) {
148
+        // 实现配置验证逻辑
149
+        // 检查配置格式、参数范围等
150
+        return true;
151
+    }
152
+    
153
+    /**
154
+     * 应用配置到设备
155
+     */
156
+    private boolean applyConfigToDevice(String deviceId, DeviceConfiguration config) {
157
+        DeviceModel device = deviceRegistry.getDevice(deviceId);
158
+        if (device == null || !device.getStatus().isOnline()) {
159
+            return false;
160
+        }
161
+        
162
+        try {
163
+            ProtocolAdapter adapter = getDeviceAdapter(device);
164
+            if (adapter != null) {
165
+                ProtocolAdapter.DeviceConfig protocolConfig = convertToProtocolConfig(config);
166
+                return adapter.updateDeviceConfig(deviceId, protocolConfig);
167
+            }
168
+        } catch (Exception e) {
169
+            System.err.println("Failed to apply config to device " + deviceId + ": " + e.getMessage());
170
+        }
171
+        
172
+        return false;
173
+    }
174
+    
175
+    /**
176
+     * 获取设备适配器
177
+     */
178
+    private ProtocolAdapter getDeviceAdapter(DeviceModel device) {
179
+        // 这里应该从适配器工厂获取适配器
180
+        // 简化实现,返回null
181
+        return null;
182
+    }
183
+    
184
+    /**
185
+     * 创建默认配置
186
+     */
187
+    private DeviceConfiguration createDefaultConfig(String deviceId) {
188
+        DeviceConfiguration config = new DeviceConfiguration();
189
+        config.setDeviceId(deviceId);
190
+        config.setVersion(1L);
191
+        config.setConfigParameters(Map.of(
192
+            "samplingInterval", 5000,
193
+            "reportInterval", 30000,
194
+            "heartbeatInterval", 60000,
195
+            "retryCount", 3,
196
+            "timeout", 10000
197
+        ));
198
+        config.setCreatedAt(System.currentTimeMillis());
199
+        config.setUpdatedAt(System.currentTimeMillis());
200
+        config.setStatus("active");
201
+        return config;
202
+    }
203
+    
204
+    /**
205
+     * 转换为协议配置
206
+     */
207
+    private ProtocolAdapter.DeviceConfig convertToProtocolConfig(DeviceConfiguration config) {
208
+        ProtocolAdapter.DeviceConfig protocolConfig = new ProtocolAdapter.DeviceConfig();
209
+        protocolConfig.setDeviceId(config.getDeviceId());
210
+        protocolConfig.setConfig(config.getConfigParameters());
211
+        protocolConfig.setVersion(config.getVersion());
212
+        return protocolConfig;
213
+    }
214
+    
215
+    /**
216
+     * 设备配置
217
+     */
218
+    public static class DeviceConfiguration {
219
+        private String deviceId;
220
+        private long version;
221
+        private Map<String, Object> configParameters;
222
+        private long createdAt;
223
+        private long updatedAt;
224
+        private String status;
225
+        
226
+        // getters and setters
227
+        public String getDeviceId() { return deviceId; }
228
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
229
+        public long getVersion() { return version; }
230
+        public void setVersion(long version) { this.version = version; }
231
+        public Map<String, Object> getConfigParameters() { return configParameters; }
232
+        public void setConfigParameters(Map<String, Object> configParameters) { this.configParameters = configParameters; }
233
+        public long getCreatedAt() { return createdAt; }
234
+        public void setCreatedAt(long createdAt) { this.createdAt = createdAt; }
235
+        public long getUpdatedAt() { return updatedAt; }
236
+        public void setUpdatedAt(long updatedAt) { this.updatedAt = updatedAt; }
237
+        public String getStatus() { return status; }
238
+        public void setStatus(String status) { this.status = status; }
239
+    }
240
+    
241
+    /**
242
+     * 配置更新请求
243
+     */
244
+    public static class ConfigUpdateRequest {
245
+        private String deviceId;
246
+        private DeviceConfiguration config;
247
+        
248
+        // getters and setters
249
+        public String getDeviceId() { return deviceId; }
250
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
251
+        public DeviceConfiguration getConfig() { return config; }
252
+        public void setConfig(DeviceConfiguration config) { this.config = config; }
253
+    }
254
+    
255
+    /**
256
+     * OTA升级
257
+     */
258
+    public static class OTAUpgrade {
259
+        private String deviceId;
260
+        private String firmwareVersion;
261
+        private String downloadUrl;
262
+        private long fileSize;
263
+        private String checksum;
264
+        private String upgradeMode; // "online" or "offline"
265
+        private long timeout;
266
+        
267
+        // getters and setters
268
+        public String getDeviceId() { return deviceId; }
269
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
270
+        public String getFirmwareVersion() { return firmwareVersion; }
271
+        public void setFirmwareVersion(String firmwareVersion) { this.firmwareVersion = firmwareVersion; }
272
+        public String getDownloadUrl() { return downloadUrl; }
273
+        public void setDownloadUrl(String downloadUrl) { this.downloadUrl = downloadUrl; }
274
+        public long getFileSize() { return fileSize; }
275
+        public void setFileSize(long fileSize) { this.fileSize = fileSize; }
276
+        public String getChecksum() { return checksum; }
277
+        public void setChecksum(String checksum) { this.checksum = checksum; }
278
+        public String getUpgradeMode() { return upgradeMode; }
279
+        public void setUpgradeMode(String upgradeMode) { this.upgradeMode = upgradeMode; }
280
+        public long getTimeout() { return timeout; }
281
+        public void setTimeout(long timeout) { this.timeout = timeout; }
282
+    }
283
+    
284
+    /**
285
+     * OTA升级状态
286
+     */
287
+    public static class OTAUpgradeStatus {
288
+        private String deviceId;
289
+        private String status; // "pending", "downloading", "installing", "completed", "failed"
290
+        private int progress; // 0-100
291
+        private String currentStep;
292
+        private String errorMessage;
293
+        private long startTime;
294
+        private long endTime;
295
+        
296
+        // getters and setters
297
+        public String getDeviceId() { return deviceId; }
298
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
299
+        public String getStatus() { return status; }
300
+        public void setStatus(String status) { this.status = status; }
301
+        public int getProgress() { return progress; }
302
+        public void setProgress(int progress) { this.progress = progress; }
303
+        public String getCurrentStep() { return currentStep; }
304
+        public void setCurrentStep(String currentStep) { this.currentStep = currentStep; }
305
+        public String getErrorMessage() { return errorMessage; }
306
+        public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
307
+        public long getStartTime() { return startTime; }
308
+        public void setStartTime(long startTime) { this.startTime = startTime; }
309
+        public long getEndTime() { return endTime; }
310
+        public void setEndTime(long endTime) { this.endTime = endTime; }
311
+    }
312
+    
313
+    /**
314
+     * OTA升级结果
315
+     */
316
+    public static class OTAUpdateResult {
317
+        private boolean success;
318
+        private String message;
319
+        private OTAUpgradeStatus status;
320
+        
321
+        public OTAUpdateResult(boolean success, String message, OTAUpgradeStatus status) {
322
+            this.success = success;
323
+            this.message = message;
324
+            this.status = status;
325
+        }
326
+        
327
+        // getters and setters
328
+        public boolean isSuccess() { return success; }
329
+        public void setSuccess(boolean success) { this.success = success; }
330
+        public String getMessage() { return message; }
331
+        public void setMessage(String message) { this.message = message; }
332
+        public OTAUpgradeStatus getStatus() { return status; }
333
+        public void setStatus(OTAUpgradeStatus status) { this.status = status; }
334
+    }
335
+    
336
+    /**
337
+     * OTA管理器
338
+     */
339
+    private static class OTAManager {
340
+        private final Map<String, OTAUpgradeStatus> upgradeStatuses = new ConcurrentHashMap<>();
341
+        
342
+        public OTAUpdateResult startUpgrade(String deviceId, OTAUpgrade upgrade) {
343
+            OTAUpgradeStatus status = new OTAUpgradeStatus();
344
+            status.setDeviceId(deviceId);
345
+            status.setStatus("pending");
346
+            status.setProgress(0);
347
+            status.setCurrentStep("Initializing");
348
+            status.setStartTime(System.currentTimeMillis());
349
+            
350
+            upgradeStatuses.put(deviceId, status);
351
+            
352
+            // 这里应该启动异步升级任务
353
+            // 简化实现,直接完成
354
+            status.setStatus("completed");
355
+            status.setProgress(100);
356
+            status.setCurrentStep("Upgrade completed");
357
+            status.setEndTime(System.currentTimeMillis());
358
+            
359
+            return new OTAUpdateResult(true, "Upgrade started successfully", status);
360
+        }
361
+        
362
+        public boolean cancelUpgrade(String deviceId) {
363
+            OTAUpgradeStatus status = upgradeStatuses.get(deviceId);
364
+            if (status != null && !"completed".equals(status.getStatus()) && !"failed".equals(status.getStatus())) {
365
+                status.setStatus("cancelled");
366
+                status.setEndTime(System.currentTimeMillis());
367
+                return true;
368
+            }
369
+            return false;
370
+        }
371
+        
372
+        public OTAUpgradeStatus getUpgradeStatus(String deviceId) {
373
+            return upgradeStatuses.get(deviceId);
374
+        }
375
+    }
376
+    
377
+    /**
378
+     * 配置更新调度器
379
+     */
380
+    private static class ConfigUpdateScheduler {
381
+        private final DeviceRegistry deviceRegistry;
382
+        
383
+        public ConfigUpdateScheduler(DeviceRegistry deviceRegistry) {
384
+            this.deviceRegistry = deviceRegistry;
385
+        }
386
+        
387
+        // 配置调度相关方法
388
+    }
389
+}

+ 145
- 0
src/main/java/com/water/iot/model/DeviceModel.java Прегледај датотеку

@@ -0,0 +1,145 @@
1
+package com.water.iot.model;
2
+
3
+import com.fasterxml.jackson.annotation.JsonInclude;
4
+import lombok.Data;
5
+import lombok.Builder;
6
+import lombok.NoArgsConstructor;
7
+import lombok.AllArgsConstructor;
8
+
9
+import java.util.List;
10
+import java.util.Map;
11
+
12
+/**
13
+ * 统一感知层设备模型标准
14
+ * 统一设备信息格式,支持不同类型设备的标准表示
15
+ */
16
+@Data
17
+@Builder
18
+@NoArgsConstructor
19
+@AllArgsConstructor
20
+@JsonInclude(JsonInclude.Include.NON_NULL)
21
+public class DeviceModel {
22
+    
23
+    /** 设备唯一标识 */
24
+    private String deviceId;
25
+    
26
+    /** 设备名称 */
27
+    private String name;
28
+    
29
+    /** 设备类型 */
30
+    private String deviceType;
31
+    
32
+    /** 设备品牌/型号 */
33
+    private String brand;
34
+    private String model;
35
+    
36
+    /** 协议类型 */
37
+    private String protocol;
38
+    
39
+    /** 连接信息 */
40
+    private ConnectionInfo connection;
41
+    
42
+    /** 设备能力 */
43
+    private DeviceCapabilities capabilities;
44
+    
45
+    /** 设备属性定义 */
46
+    private List<PropertyDefinition> properties;
47
+    
48
+    /** 设备状态 */
49
+    private DeviceStatus status;
50
+    
51
+    /** 元数据 */
52
+    private Map<String, Object> metadata;
53
+    
54
+    /** 注册时间 */
55
+    private long registerTime;
56
+    
57
+    /** 最后活跃时间 */
58
+    private long lastActiveTime;
59
+    
60
+    /** 连接信息 */
61
+    @Data
62
+    @Builder
63
+    @NoArgsConstructor
64
+    @AllArgsConstructor
65
+    public static class ConnectionInfo {
66
+        /** 连接地址 */
67
+        private String host;
68
+        private int port;
69
+        private String url;
70
+        
71
+        /** 认证信息 */
72
+        private String username;
73
+        private String password;
74
+        private String token;
75
+        
76
+        /** 设备特定配置 */
77
+        private Map<String, Object> deviceConfig;
78
+    }
79
+    
80
+    /** 设备能力 */
81
+    @Data
82
+    @Builder
83
+    @NoArgsConstructor
84
+    @AllArgsConstructor
85
+    public static class DeviceCapabilities {
86
+        /** 支持的数据上报 */
87
+        private boolean dataUpload;
88
+        /** 支持的指令下发 */
89
+        private boolean commandReceive;
90
+        /** 支持配置更新 */
91
+        private boolean configUpdate;
92
+        /** 支持OTA升级 */
93
+        private boolean otaSupport;
94
+        /** 支持远程控制 */
95
+        private boolean remoteControl;
96
+        /** 支持批量操作 */
97
+        private boolean batchOperation;
98
+    }
99
+    
100
+    /** 设备属性定义 */
101
+    @Data
102
+    @Builder
103
+    @NoArgsConstructor
104
+    @AllArgsConstructor
105
+    public static class PropertyDefinition {
106
+        /** 属性标识 */
107
+        private String propertyId;
108
+        /** 属性名称 */
109
+        private String name;
110
+        /** 属性类型 */
111
+        private String type;
112
+        /** 数据单位 */
113
+        private String unit;
114
+        /** 数值范围 */
115
+        private Map<String, Object> range;
116
+        /** 读写权限 */
117
+        private boolean readOnly;
118
+        /** 是否必需 */
119
+        private boolean required;
120
+        /** 默认值 */
121
+        private Object defaultValue;
122
+        /** 描述 */
123
+        private String description;
124
+    }
125
+    
126
+    /** 设备状态 */
127
+    @Data
128
+    @Builder
129
+    @NoArgsConstructor
130
+    @AllArgsConstructor
131
+    public static class DeviceStatus {
132
+        /** 在线状态 */
133
+        private boolean online;
134
+        /** 设备状态:正常/异常/离线 */
135
+        private String state;
136
+        /** 心跳时间 */
137
+        private long heartbeatTime;
138
+        /** 最后通信时间 */
139
+        private long lastCommunicationTime;
140
+        /** 错误信息 */
141
+        private String errorMessage;
142
+        /** 错误码 */
143
+        private String errorCode;
144
+    }
145
+}

+ 427
- 0
src/main/java/com/water/iot/monitor/DeviceMonitorService.java Прегледај датотеку

@@ -0,0 +1,427 @@
1
+package com.water.iot.monitor;
2
+
3
+import com.water.iot.model.DeviceModel;
4
+import com.water.iot.registry.DeviceRegistry;
5
+import org.springframework.stereotype.Service;
6
+import org.springframework.beans.factory.annotation.Autowired;
7
+import java.util.*;
8
+import java.util.concurrent.ConcurrentHashMap;
9
+import java.util.concurrent.Executors;
10
+import java.util.concurrent.ScheduledExecutorService;
11
+import java.util.concurrent.TimeUnit;
12
+
13
+/**
14
+ * 设备监控服务
15
+ * 提供设备在线状态、健康检查、异常告警等功能
16
+ */
17
+@Service
18
+public class DeviceMonitorService {
19
+    
20
+    private final DeviceRegistry deviceRegistry;
21
+    private final Map<String, DeviceMonitor> deviceMonitors = new ConcurrentHashMap<>();
22
+    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
23
+    private final AlertManager alertManager;
24
+    
25
+    @Autowired
26
+    public DeviceMonitorService(DeviceRegistry deviceRegistry) {
27
+        this.deviceRegistry = deviceRegistry;
28
+        this.alertManager = new AlertManager();
29
+        startMonitoringTasks();
30
+    }
31
+    
32
+    /**
33
+     * 启动监控任务
34
+     */
35
+    private void startMonitoringTasks() {
36
+        // 健康检查任务(每30秒执行一次)
37
+        scheduler.scheduleAtFixedRate(this::performHealthChecks, 30, 30, TimeUnit.SECONDS);
38
+        
39
+        // 状态同步任务(每60秒执行一次)
40
+        scheduler.scheduleAtFixedRate(this::syncDeviceStatus, 60, 60, TimeUnit.SECONDS);
41
+        
42
+        // 告警检查任务(每10秒执行一次)
43
+        scheduler.scheduleAtFixedRate(this::checkForAlerts, 10, 10, TimeUnit.SECONDS);
44
+        
45
+        // 统计报告任务(每5分钟执行一次)
46
+        scheduler.scheduleAtFixedRate(this::generateStatistics, 300, 300, TimeUnit.SECONDS);
47
+    }
48
+    
49
+    /**
50
+     * 执行健康检查
51
+     */
52
+    public void performHealthChecks() {
53
+        for (DeviceModel device : deviceRegistry.getAllDevices()) {
54
+            try {
55
+                DeviceMonitor monitor = deviceMonitors.computeIfAbsent(
56
+                    device.getDeviceId(), 
57
+                    id -> new DeviceMonitor(id)
58
+                );
59
+                
60
+                // 检查设备在线状态
61
+                boolean isOnline = checkDeviceOnline(device);
62
+                
63
+                // 更新监控状态
64
+                monitor.setOnline(isOnline);
65
+                monitor.setLastHealthCheck(System.currentTimeMillis());
66
+                
67
+                // 检查心跳
68
+                if (isOnline) {
69
+                    checkDeviceHeartbeat(device, monitor);
70
+                }
71
+                
72
+                // 更新设备状态
73
+                updateDeviceStatus(device, monitor);
74
+                
75
+            } catch (Exception e) {
76
+                // 记录错误
77
+                System.err.println("Health check failed for device " + device.getDeviceId() + ": " + e.getMessage());
78
+            }
79
+        }
80
+    }
81
+    
82
+    /**
83
+     * 检查设备在线状态
84
+     */
85
+    private boolean checkDeviceOnline(DeviceModel device) {
86
+        try {
87
+            // 这里应该调用适配器的连接检查方法
88
+            // 简化实现,返回true
89
+            return true;
90
+        } catch (Exception e) {
91
+            return false;
92
+        }
93
+    }
94
+    
95
+    /**
96
+     * 检查设备心跳
97
+     */
98
+    private void checkDeviceHeartbeat(DeviceModel device, DeviceMonitor monitor) {
99
+        long currentTime = System.currentTimeMillis();
100
+        long lastHeartbeat = device.getStatus().getLastCommunicationTime();
101
+        
102
+        // 如果超过5分钟没有心跳,标记为异常
103
+        if (currentTime - lastHeartbeat > 300000) {
104
+            monitor.setHeartbeatStatus(DeviceMonitor.HeartbeatStatus.ABNORMAL);
105
+            alertManager.triggerAlert(device.getDeviceId(), "heartbeat_timeout", "Device heartbeat timeout");
106
+        } else {
107
+            monitor.setHeartbeatStatus(DeviceMonitor.HeartbeatStatus.NORMAL);
108
+        }
109
+    }
110
+    
111
+    /**
112
+     * 更新设备状态
113
+     */
114
+    private void updateDeviceStatus(DeviceModel device, DeviceMonitor monitor) {
115
+        DeviceModel.DeviceStatus status = device.getStatus();
116
+        
117
+        // 根据监控状态更新设备状态
118
+        if (!monitor.isOnline()) {
119
+            status.setState("offline");
120
+            status.setOnline(false);
121
+            status.setErrorMessage("Device is offline");
122
+        } else if (monitor.getHeartbeatStatus() == DeviceMonitor.HeartbeatStatus.ABNORMAL) {
123
+            status.setState("warning");
124
+            status.setOnline(true);
125
+            status.setErrorMessage("Device heartbeat abnormal");
126
+        } else {
127
+            status.setState("normal");
128
+            status.setOnline(true);
129
+            status.setErrorMessage(null);
130
+        }
131
+        
132
+        status.setLastCommunicationTime(System.currentTimeMillis());
133
+    }
134
+    
135
+    /**
136
+     * 同步设备状态
137
+     */
138
+    public void syncDeviceStatus() {
139
+        for (DeviceModel device : deviceRegistry.getAllDevices()) {
140
+            DeviceMonitor monitor = deviceMonitors.get(device.getDeviceId());
141
+            if (monitor != null) {
142
+                // 更新监控统计数据
143
+                monitor.updateStatistics();
144
+            }
145
+        }
146
+    }
147
+    
148
+    /**
149
+     * 检查告警条件
150
+     */
151
+    public void checkForAlerts() {
152
+        for (DeviceModel device : deviceRegistry.getAllDevices()) {
153
+            DeviceMonitor monitor = deviceMonitors.get(device.getDeviceId());
154
+            if (monitor != null) {
155
+                // 检查各种告警条件
156
+                checkErrorRateAlert(device, monitor);
157
+                checkPerformanceAlert(device, monitor);
158
+                checkConnectionAlert(device, monitor);
159
+            }
160
+        }
161
+    }
162
+    
163
+    /**
164
+     * 检查错误率告警
165
+     */
166
+    private void checkErrorRateAlert(DeviceModel device, DeviceMonitor monitor) {
167
+        if (monitor.getErrorRate() > 0.1) { // 错误率超过10%
168
+            alertManager.triggerAlert(
169
+                device.getDeviceId(), 
170
+                "high_error_rate", 
171
+                "Device error rate is high: " + (monitor.getErrorRate() * 100) + "%"
172
+            );
173
+        }
174
+    }
175
+    
176
+    /**
177
+     * 检查性能告警
178
+     */
179
+    private void checkPerformanceAlert(DeviceModel device, DeviceMonitor monitor) {
180
+        if (monitor.getResponseTime() > 5000) { // 响应时间超过5秒
181
+            alertManager.triggerAlert(
182
+                device.getDeviceId(), 
183
+                "slow_response", 
184
+                "Device response time is slow: " + monitor.getResponseTime() + "ms"
185
+            );
186
+        }
187
+    }
188
+    
189
+    /**
190
+     * 检查连接告警
191
+     */
192
+    private void checkConnectionAlert(DeviceModel device, DeviceMonitor monitor) {
193
+        if (!monitor.isOnline()) {
194
+            alertManager.triggerAlert(
195
+                device.getDeviceId(), 
196
+                "connection_lost", 
197
+                "Device connection lost"
198
+            );
199
+        }
200
+    }
201
+    
202
+    /**
203
+     * 生成统计报告
204
+     */
205
+    public void generateStatistics() {
206
+        MonitorStatistics stats = new MonitorStatistics();
207
+        
208
+        // 统计设备总数
209
+        stats.setTotalDevices(deviceRegistry.getAllDevices().size());
210
+        
211
+        // 统计在线设备数
212
+        stats.setOnlineDevices((int) deviceRegistry.getAllDevices().stream()
213
+            .filter(device -> device.getStatus().isOnline())
214
+            .count());
215
+        
216
+        // 统计离线设备数
217
+        stats.setOfflineDevices((int) deviceRegistry.getAllDevices().stream()
218
+            .filter(device -> !device.getStatus().isOnline())
219
+            .count());
220
+        
221
+        // 统计异常设备数
222
+        stats.setErrorDevices((int) deviceRegistry.getAllDevices().stream()
223
+            .filter(device -> "error".equals(device.getStatus().getState()))
224
+            .count());
225
+        
226
+        // 统计告警数量
227
+        stats.setAlertCount(alertManager.getActiveAlerts().size());
228
+        
229
+        // 这里可以添加统计报告发送逻辑
230
+        System.out.println("Device Monitor Statistics: " + stats);
231
+    }
232
+    
233
+    /**
234
+     * 获取设备监控器
235
+     */
236
+    public DeviceMonitor getDeviceMonitor(String deviceId) {
237
+        return deviceMonitors.get(deviceId);
238
+    }
239
+    
240
+    /**
241
+     * 获取所有设备监控器
242
+     */
243
+    public Map<String, DeviceMonitor> getAllDeviceMonitors() {
244
+        return new HashMap<>(deviceMonitors);
245
+    }
246
+    
247
+    /**
248
+     * 关闭监控服务
249
+     */
250
+    public void shutdown() {
251
+        scheduler.shutdown();
252
+        try {
253
+            if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {
254
+                scheduler.shutdownNow();
255
+            }
256
+        } catch (InterruptedException e) {
257
+            scheduler.shutdownNow();
258
+            Thread.currentThread().interrupt();
259
+        }
260
+    }
261
+    
262
+    /**
263
+     * 设备监控器
264
+     */
265
+    public static class DeviceMonitor {
266
+        private String deviceId;
267
+        private boolean online;
268
+        private HeartbeatStatus heartbeatStatus;
269
+        private long lastHealthCheck;
270
+        private long uptime;
271
+        private double errorRate;
272
+        private long responseTime;
273
+        private Map<String, Long> operationCounts;
274
+        
275
+        public enum HeartbeatStatus {
276
+            NORMAL, ABNORMAL, TIMEOUT
277
+        }
278
+        
279
+        public DeviceMonitor(String deviceId) {
280
+            this.deviceId = deviceId;
281
+            this.online = false;
282
+            this.heartbeatStatus = HeartbeatStatus.NORMAL;
283
+            this.lastHealthCheck = System.currentTimeMillis();
284
+            this.uptime = 0;
285
+            this.errorRate = 0.0;
286
+            this.responseTime = 0;
287
+            this.operationCounts = new ConcurrentHashMap<>();
288
+        }
289
+        
290
+        /**
291
+         * 更新统计数据
292
+         */
293
+        public void updateStatistics() {
294
+            // 更新运行时间
295
+            this.uptime = System.currentTimeMillis() - lastHealthCheck;
296
+            
297
+            // 计算错误率(简化实现)
298
+            long totalOperations = operationCounts.values().stream().mapToLong(Long::longValue).sum();
299
+            long errorOperations = operationCounts.getOrDefault("error", 0L);
300
+            this.errorRate = totalOperations > 0 ? (double) errorOperations / totalOperations : 0.0;
301
+        }
302
+        
303
+        // getters and setters
304
+        public String getDeviceId() { return deviceId; }
305
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
306
+        public boolean isOnline() { return online; }
307
+        public void setOnline(boolean online) { this.online = online; }
308
+        public HeartbeatStatus getHeartbeatStatus() { return heartbeatStatus; }
309
+        public void setHeartbeatStatus(HeartbeatStatus heartbeatStatus) { this.heartbeatStatus = heartbeatStatus; }
310
+        public long getLastHealthCheck() { return lastHealthCheck; }
311
+        public void setLastHealthCheck(long lastHealthCheck) { this.lastHealthCheck = lastHealthCheck; }
312
+        public long getUptime() { return uptime; }
313
+        public void setUptime(long uptime) { this.uptime = uptime; }
314
+        public double getErrorRate() { return errorRate; }
315
+        public void setErrorRate(double errorRate) { this.errorRate = errorRate; }
316
+        public long getResponseTime() { return responseTime; }
317
+        public void setResponseTime(long responseTime) { this.responseTime = responseTime; }
318
+        public Map<String, Long> getOperationCounts() { return operationCounts; }
319
+        public void setOperationCounts(Map<String, Long> operationCounts) { this.operationCounts = operationCounts; }
320
+    }
321
+    
322
+    /**
323
+     * 监控统计信息
324
+     */
325
+    public static class MonitorStatistics {
326
+        private int totalDevices;
327
+        private int onlineDevices;
328
+        private int offlineDevices;
329
+        private int errorDevices;
330
+        private int alertCount;
331
+        
332
+        // getters and setters
333
+        public int getTotalDevices() { return totalDevices; }
334
+        public void setTotalDevices(int totalDevices) { this.totalDevices = totalDevices; }
335
+        public int getOnlineDevices() { return onlineDevices; }
336
+        public void setOnlineDevices(int onlineDevices) { this.onlineDevices = onlineDevices; }
337
+        public int getOfflineDevices() { return offlineDevices; }
338
+        public void setOfflineDevices(int offlineDevices) { this.offlineDevices = offlineDevices; }
339
+        public int getErrorDevices() { return errorDevices; }
340
+        public void setErrorDevices(int errorDevices) { this.errorDevices = errorDevices; }
341
+        public int getAlertCount() { return alertCount; }
342
+        public void setAlertCount(int alertCount) { this.alertCount = alertCount; }
343
+        
344
+        @Override
345
+        public String toString() {
346
+            return "MonitorStatistics{" +
347
+                   "totalDevices=" + totalDevices +
348
+                   ", onlineDevices=" + onlineDevices +
349
+                   ", offlineDevices=" + offlineDevices +
350
+                   ", errorDevices=" + errorDevices +
351
+                   ", alertCount=" + alertCount +
352
+                   '}';
353
+        }
354
+    }
355
+    
356
+    /**
357
+     * 告警管理器
358
+     */
359
+    private static class AlertManager {
360
+        private final Map<String, DeviceAlert> activeAlerts = new ConcurrentHashMap<>();
361
+        
362
+        /**
363
+         * 触发告警
364
+         */
365
+        public void triggerAlert(String deviceId, String alertType, String message) {
366
+            DeviceAlert alert = new DeviceAlert(deviceId, alertType, message, System.currentTimeMillis());
367
+            activeAlerts.put(deviceId + ":" + alertType, alert);
368
+            
369
+            // 这里可以添加告警通知逻辑
370
+            System.out.println("ALERT: " + alert);
371
+        }
372
+        
373
+        /**
374
+         * 获取活跃告警
375
+         */
376
+        public Map<String, DeviceAlert> getActiveAlerts() {
377
+            return new HashMap<>(activeAlerts);
378
+        }
379
+        
380
+        /**
381
+         * 清理过期告警
382
+         */
383
+        public void cleanupOldAlerts(long expirationTime) {
384
+            long currentTime = System.currentTimeMillis();
385
+            activeAlerts.entrySet().removeIf(entry -> 
386
+                currentTime - entry.getValue().getTimestamp() > expirationTime
387
+            );
388
+        }
389
+    }
390
+    
391
+    /**
392
+     * 设备告警
393
+     */
394
+    private static class DeviceAlert {
395
+        private String deviceId;
396
+        private String alertType;
397
+        private String message;
398
+        private long timestamp;
399
+        
400
+        public DeviceAlert(String deviceId, String alertType, String message, long timestamp) {
401
+            this.deviceId = deviceId;
402
+            this.alertType = alertType;
403
+            this.message = message;
404
+            this.timestamp = timestamp;
405
+        }
406
+        
407
+        // getters and setters
408
+        public String getDeviceId() { return deviceId; }
409
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
410
+        public String getAlertType() { return alertType; }
411
+        public void setAlertType(String alertType) { this.alertType = alertType; }
412
+        public String getMessage() { return message; }
413
+        public void setMessage(String message) { this.message = message; }
414
+        public long getTimestamp() { return timestamp; }
415
+        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
416
+        
417
+        @Override
418
+        public String toString() {
419
+            return "DeviceAlert{" +
420
+                   "deviceId='" + deviceId + '\'' +
421
+                   ", alertType='" + alertType + '\'' +
422
+                   ", message='" + message + '\'' +
423
+                   ", timestamp=" + timestamp +
424
+                   '}';
425
+        }
426
+    }
427
+}

+ 368
- 0
src/main/java/com/water/iot/parser/DataParser.java Прегледај датотеку

@@ -0,0 +1,368 @@
1
+package com.water.iot.parser;
2
+
3
+import com.water.iot.model.DeviceModel;
4
+import com.fasterxml.jackson.databind.ObjectMapper;
5
+import org.springframework.stereotype.Component;
6
+import java.util.*;
7
+
8
+/**
9
+ * 数据解析器
10
+ * 统一数据格式解析和转换
11
+ */
12
+@Component
13
+public class DataParser {
14
+    
15
+    private final ObjectMapper objectMapper;
16
+    private final Map<String, DataFormat> supportedFormats;
17
+    
18
+    public DataParser() {
19
+        this.objectMapper = new ObjectMapper();
20
+        this.supportedFormats = new HashMap<>();
21
+        
22
+        // 初始化支持的格式
23
+        initializeSupportedFormats();
24
+    }
25
+    
26
+    /**
27
+     * 初始化支持的格式
28
+     */
29
+    private void initializeSupportedFormats() {
30
+        // JSON格式
31
+        supportedFormats.put("json", new DataFormat(
32
+            "application/json",
33
+            this::parseJson,
34
+            this::serializeJson
35
+        ));
36
+        
37
+        // XML格式
38
+        supportedFormats.put("xml", new DataFormat(
39
+            "application/xml",
40
+            this::parseXml,
41
+            this::serializeXml
42
+        ));
43
+        
44
+        // CSV格式
45
+        supportedFormats.put("csv", new DataFormat(
46
+            "text/csv",
47
+            this::parseCsv,
48
+            this::serializeCsv
49
+        ));
50
+        
51
+        // 二进制格式
52
+        supportedFormats.put("binary", new DataFormat(
53
+            "application/octet-stream",
54
+            this::parseBinary,
55
+            this::serializeBinary
56
+        ));
57
+    }
58
+    
59
+    /**
60
+     * 解析设备数据
61
+     */
62
+    public DeviceData parseData(String formatType, byte[] rawData) {
63
+        DataFormat format = supportedFormats.get(formatType);
64
+        if (format == null) {
65
+            throw new IllegalArgumentException("Unsupported format: " + formatType);
66
+        }
67
+        
68
+        return format.parser.apply(rawData);
69
+    }
70
+    
71
+    /**
72
+     * 序列化设备数据
73
+     */
74
+    public byte[] serializeData(String formatType, DeviceData deviceData) {
75
+        DataFormat format = supportedFormats.get(formatType);
76
+        if (format == null) {
77
+            throw new IllegalArgumentException("Unsupported format: " + formatType);
78
+        }
79
+        
80
+        return format.serializer.apply(deviceData);
81
+    }
82
+    
83
+    /**
84
+     * JSON解析
85
+     */
86
+    private DeviceData parseJson(byte[] rawData) {
87
+        try {
88
+            String jsonStr = new String(rawData);
89
+            Map<String, Object> data = objectMapper.readValue(jsonStr, Map.class);
90
+            
91
+            DeviceData deviceData = new DeviceData();
92
+            deviceData.setDeviceId((String) data.get("deviceId"));
93
+            deviceData.setTimestamp((Long) data.getOrDefault("timestamp", System.currentTimeMillis()));
94
+            deviceData.setProperties((Map<String, Object>) data.get("properties"));
95
+            deviceData.setTelemetry((Map<String, Object>) data.get("telemetry"));
96
+            deviceData.setStatus((String) data.getOrDefault("status", "normal"));
97
+            
98
+            return deviceData;
99
+            
100
+        } catch (Exception e) {
101
+            throw new RuntimeException("Failed to parse JSON data", e);
102
+        }
103
+    }
104
+    
105
+    /**
106
+     * JSON序列化
107
+     */
108
+    private byte[] serializeJson(DeviceData deviceData) {
109
+        try {
110
+            Map<String, Object> data = new HashMap<>();
111
+            data.put("deviceId", deviceData.getDeviceId());
112
+            data.put("timestamp", deviceData.getTimestamp());
113
+            data.put("properties", deviceData.getProperties());
114
+            data.put("telemetry", deviceData.getTelemetry());
115
+            data.put("status", deviceData.getStatus());
116
+            
117
+            return objectMapper.writeValueAsBytes(data);
118
+            
119
+        } catch (Exception e) {
120
+            throw new RuntimeException("Failed to serialize JSON data", e);
121
+        }
122
+    }
123
+    
124
+    /**
125
+     * XML解析
126
+     */
127
+    private DeviceData parseXml(byte[] rawData) {
128
+        try {
129
+            String xmlStr = new String(rawData);
130
+            // 简化的XML解析,实际应用中应使用XML解析库
131
+            Map<String, Object> data = new HashMap<>();
132
+            
133
+            // 解析deviceId
134
+            int deviceIdStart = xmlStr.indexOf("<deviceId>") + 10;
135
+            int deviceIdEnd = xmlStr.indexOf("</deviceId>");
136
+            data.put("deviceId", xmlStr.substring(deviceIdStart, deviceIdEnd));
137
+            
138
+            // 解析timestamp
139
+            int timestampStart = xmlStr.indexOf("<timestamp>") + 11;
140
+            int timestampEnd = xmlStr.indexOf("</timestamp>");
141
+            data.put("timestamp", Long.parseLong(xmlStr.substring(timestampStart, timestampEnd)));
142
+            
143
+            // 解析status
144
+            int statusStart = xmlStr.indexOf("<status>") + 8;
145
+            int statusEnd = xmlStr.indexOf("</status>");
146
+            data.put("status", xmlStr.substring(statusStart, statusEnd));
147
+            
148
+            // 简化处理properties和telemetry
149
+            data.put("properties", new HashMap<>());
150
+            data.put("telemetry", new HashMap<>());
151
+            
152
+            DeviceData deviceData = new DeviceData();
153
+            deviceData.setDeviceId((String) data.get("deviceId"));
154
+            deviceData.setTimestamp((Long) data.get("timestamp"));
155
+            deviceData.setProperties((Map<String, Object>) data.get("properties"));
156
+            deviceData.setTelemetry((Map<String, Object>) data.get("telemetry"));
157
+            deviceData.setStatus((String) data.get("status"));
158
+            
159
+            return deviceData;
160
+            
161
+        } catch (Exception e) {
162
+            throw new RuntimeException("Failed to parse XML data", e);
163
+        }
164
+    }
165
+    
166
+    /**
167
+     * XML序列化
168
+     */
169
+    private byte[] serializeXml(DeviceData deviceData) {
170
+        try {
171
+            StringBuilder xml = new StringBuilder();
172
+            xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
173
+            xml.append("<deviceData>");
174
+            xml.append("<deviceId>").append(deviceData.getDeviceId()).append("</deviceId>");
175
+            xml.append("<timestamp>").append(deviceData.getTimestamp()).append("</timestamp>");
176
+            xml.append("<status>").append(deviceData.getStatus()).append("</status>");
177
+            
178
+            // 序列化properties
179
+            xml.append("<properties>");
180
+            if (deviceData.getProperties() != null) {
181
+                for (Map.Entry<String, Object> entry : deviceData.getProperties().entrySet()) {
182
+                    xml.append("<").append(entry.getKey()).append(">")
183
+                       .append(entry.getValue()).append("</")
184
+                       .append(entry.getKey()).append(">");
185
+                }
186
+            }
187
+            xml.append("</properties>");
188
+            
189
+            // 序列化telemetry
190
+            xml.append("<telemetry>");
191
+            if (deviceData.getTelemetry() != null) {
192
+                for (Map.Entry<String, Object> entry : deviceData.getTelemetry().entrySet()) {
193
+                    xml.append("<").append(entry.getKey()).append(">")
194
+                       .append(entry.getValue()).append("</")
195
+                       .append(entry.getKey()).append(">");
196
+                }
197
+            }
198
+            xml.append("</telemetry>");
199
+            
200
+            xml.append("</deviceData>");
201
+            
202
+            return xml.toString().getBytes();
203
+            
204
+        } catch (Exception e) {
205
+            throw new RuntimeException("Failed to serialize XML data", e);
206
+        }
207
+    }
208
+    
209
+    /**
210
+     * CSV解析
211
+     */
212
+    private DeviceData parseCsv(byte[] rawData) {
213
+        try {
214
+            String csvStr = new String(rawData);
215
+            String[] lines = csvStr.split("\n");
216
+            
217
+            Map<String, Object> properties = new HashMap<>();
218
+            Map<String, Object> telemetry = new HashMap<>();
219
+            
220
+            // 解析CSV行
221
+            for (String line : lines) {
222
+                String[] parts = line.split(",");
223
+                if (parts.length >= 2) {
224
+                    String key = parts[0].trim();
225
+                    String value = parts[1].trim();
226
+                    
227
+                    // 尝试转换为数值
228
+                    try {
229
+                        double numValue = Double.parseDouble(value);
230
+                        properties.put(key, numValue);
231
+                    } catch (NumberFormatException e) {
232
+                        properties.put(key, value);
233
+                    }
234
+                }
235
+            }
236
+            
237
+            DeviceData deviceData = new DeviceData();
238
+            deviceData.setDeviceId("unknown");
239
+            deviceData.setTimestamp(System.currentTimeMillis());
240
+            deviceData.setProperties(properties);
241
+            deviceData.setTelemetry(telemetry);
242
+            deviceData.setStatus("normal");
243
+            
244
+            return deviceData;
245
+            
246
+        } catch (Exception e) {
247
+            throw new RuntimeException("Failed to parse CSV data", e);
248
+        }
249
+    }
250
+    
251
+    /**
252
+     * CSV序列化
253
+     */
254
+    private byte[] serializeCsv(DeviceData deviceData) {
255
+        try {
256
+            StringBuilder csv = new StringBuilder();
257
+            
258
+            // 序列化properties
259
+            if (deviceData.getProperties() != null) {
260
+                for (Map.Entry<String, Object> entry : deviceData.getProperties().entrySet()) {
261
+                    csv.append(entry.getKey()).append(",").append(entry.getValue()).append("\n");
262
+                }
263
+            }
264
+            
265
+            // 序列化telemetry
266
+            if (deviceData.getTelemetry() != null) {
267
+                for (Map.Entry<String, Object> entry : deviceData.getTelemetry().entrySet()) {
268
+                    csv.append(entry.getKey()).append(",").append(entry.getValue()).append("\n");
269
+                }
270
+            }
271
+            
272
+            return csv.toString().getBytes();
273
+            
274
+        } catch (Exception e) {
275
+            throw new RuntimeException("Failed to serialize CSV data", e);
276
+        }
277
+    }
278
+    
279
+    /**
280
+     * 二进制解析
281
+     */
282
+    private DeviceData parseBinary(byte[] rawData) {
283
+        // 简化的二进制解析
284
+        DeviceData deviceData = new DeviceData();
285
+        deviceData.setDeviceId("binary-device");
286
+        deviceData.setTimestamp(System.currentTimeMillis());
287
+        deviceData.setProperties(Map.of("rawDataLength", rawData.length));
288
+        deviceData.setTelemetry(new HashMap<>());
289
+        deviceData.setStatus("normal");
290
+        return deviceData;
291
+    }
292
+    
293
+    /**
294
+     * 二进制序列化
295
+     */
296
+    private byte[] serializeBinary(DeviceData deviceData) {
297
+        // 简化的二进制序列化
298
+        return deviceData.getProperties().toString().getBytes();
299
+    }
300
+    
301
+    /**
302
+     * 添加新的数据格式支持
303
+     */
304
+    public void addFormat(String formatType, String mimeType, DataParserFunction parser, DataSerializerFunction serializer) {
305
+        supportedFormats.put(formatType, new DataFormat(mimeType, parser, serializer));
306
+    }
307
+    
308
+    /**
309
+     * 获取支持的格式列表
310
+     */
311
+    public List<String> getSupportedFormats() {
312
+        return new ArrayList<>(supportedFormats.keySet());
313
+    }
314
+    
315
+    /**
316
+     * 数据格式定义
317
+     */
318
+    private static class DataFormat {
319
+        private final String mimeType;
320
+        private final DataParserFunction parser;
321
+        private final DataSerializerFunction serializer;
322
+        
323
+        public DataFormat(String mimeType, DataParserFunction parser, DataSerializerFunction serializer) {
324
+            this.mimeType = mimeType;
325
+            this.parser = parser;
326
+            this.serializer = serializer;
327
+        }
328
+    }
329
+    
330
+    /**
331
+     * 数据解析函数接口
332
+     */
333
+    @FunctionalInterface
334
+    public interface DataParserFunction {
335
+        DeviceData apply(byte[] rawData);
336
+    }
337
+    
338
+    /**
339
+     * 数据序列化函数接口
340
+     */
341
+    @FunctionalInterface
342
+    public interface DataSerializerFunction {
343
+        byte[] apply(DeviceData deviceData);
344
+    }
345
+    
346
+    /**
347
+     * 设备数据实体
348
+     */
349
+    public static class DeviceData {
350
+        private String deviceId;
351
+        private long timestamp;
352
+        private Map<String, Object> properties;
353
+        private Map<String, Object> telemetry;
354
+        private String status;
355
+        
356
+        // getters and setters
357
+        public String getDeviceId() { return deviceId; }
358
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
359
+        public long getTimestamp() { return timestamp; }
360
+        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
361
+        public Map<String, Object> getProperties() { return properties; }
362
+        public void setProperties(Map<String, Object> properties) { this.properties = properties; }
363
+        public Map<String, Object> getTelemetry() { return telemetry; }
364
+        public void setTelemetry(Map<String, Object> telemetry) { this.telemetry = telemetry; }
365
+        public String getStatus() { return status; }
366
+        public void setStatus(String status) { this.status = status; }
367
+    }
368
+}

+ 57
- 0
src/main/java/com/water/iot/protocol/AdapterFactory.java Прегледај датотеку

@@ -0,0 +1,57 @@
1
+package com.water.iot.protocol;
2
+
3
+import org.springframework.beans.factory.annotation.Autowired;
4
+import org.springframework.stereotype.Component;
5
+import javax.annotation.PostConstruct;
6
+import java.util.HashMap;
7
+import java.util.List;
8
+import java.util.Map;
9
+
10
+/**
11
+ * 适配器工厂
12
+ * 根据协议类型自动获取适配器实例
13
+ */
14
+@Component
15
+public class AdapterFactory {
16
+    
17
+    private final Map<String, ProtocolAdapter> protocolAdapters = new HashMap<>();
18
+    
19
+    @Autowired
20
+    private List<ProtocolAdapter> adapters;
21
+    
22
+    @PostConstruct
23
+    public void init() {
24
+        // 注册所有可用的适配器
25
+        for (ProtocolAdapter adapter : adapters) {
26
+            protocolAdapters.put(adapter.getProtocolType(), adapter);
27
+        }
28
+    }
29
+    
30
+    /**
31
+     * 根据协议类型获取适配器
32
+     */
33
+    public ProtocolAdapter getAdapter(String protocolType) {
34
+        return protocolAdapters.get(protocolType);
35
+    }
36
+    
37
+    /**
38
+     * 注册新的适配器
39
+     */
40
+    public void registerAdapter(String protocolType, ProtocolAdapter adapter) {
41
+        protocolAdapters.put(protocolType, adapter);
42
+    }
43
+    
44
+    /**
45
+     * 获取所有支持的协议类型
46
+     */
47
+    public List<String> getSupportedProtocols() {
48
+        return List.of("MQTT", "HTTP", "CoAP", "Modbus", "NB-IoT");
49
+    }
50
+    
51
+    /**
52
+     * 检查协议是否支持
53
+     */
54
+    public boolean isProtocolSupported(String protocolType) {
55
+        return protocolAdapters.containsKey(protocolType);
56
+    }
57
+}

+ 116
- 0
src/main/java/com/water/iot/protocol/ProtocolAdapter.java Прегледај датотеку

@@ -0,0 +1,116 @@
1
+package com.water.iot.protocol;
2
+
3
+import com.water.iot.model.DeviceModel;
4
+import com.water.iot.model.DeviceModel.ConnectionInfo;
5
+
6
+/**
7
+ * 协议适配器接口
8
+ * 定义统一的设备通信协议适配器接口
9
+ */
10
+public interface ProtocolAdapter {
11
+    
12
+    /**
13
+     * 支持的协议类型
14
+     */
15
+    String getProtocolType();
16
+    
17
+    /**
18
+     * 连接设备
19
+     */
20
+    boolean connect(ConnectionInfo connectionInfo);
21
+    
22
+    /**
23
+     * 断开连接
24
+     */
25
+    void disconnect();
26
+    
27
+    /**
28
+     * 检查连接状态
29
+     */
30
+    boolean isConnected();
31
+    
32
+    /**
33
+     * 上发设备数据
34
+     */
35
+    DeviceData uploadData(String deviceId, byte[] rawData);
36
+    
37
+    /**
38
+     * 下发指令
39
+     */
40
+    CommandResult sendCommand(String deviceId, String command, Object parameters);
41
+    
42
+    /**
43
+     * 获取设备配置
44
+     */
45
+    DeviceConfig getDeviceConfig(String deviceId);
46
+    
47
+    /**
48
+     * 更新设备配置
49
+     */
50
+    boolean updateDeviceConfig(String deviceId, DeviceConfig config);
51
+    
52
+    /**
53
+     * 心跳检测
54
+     */
55
+    boolean heartbeat(String deviceId);
56
+    
57
+    /**
58
+     * 设备数据
59
+     */
60
+    public static class DeviceData {
61
+        private String deviceId;
62
+        private long timestamp;
63
+        private Map<String, Object> properties;
64
+        private Map<String, Object> telemetry;
65
+        private String status;
66
+        
67
+        // getters and setters
68
+        public String getDeviceId() { return deviceId; }
69
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
70
+        public long getTimestamp() { return timestamp; }
71
+        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
72
+        public Map<String, Object> getProperties() { return properties; }
73
+        public void setProperties(Map<String, Object> properties) { this.properties = properties; }
74
+        public Map<String, Object> getTelemetry() { return telemetry; }
75
+        public void setTelemetry(Map<String, Object> telemetry) { this.telemetry = telemetry; }
76
+        public String getStatus() { return status; }
77
+        public void setStatus(String status) { this.status = status; }
78
+    }
79
+    
80
+    /**
81
+     * 指令执行结果
82
+     */
83
+    public static class CommandResult {
84
+        private boolean success;
85
+        private String message;
86
+        private Object response;
87
+        private long timestamp;
88
+        
89
+        // getters and setters
90
+        public boolean isSuccess() { return success; }
91
+        public void setSuccess(boolean success) { this.success = success; }
92
+        public String getMessage() { return message; }
93
+        public void setMessage(String message) { this.message = message; }
94
+        public Object getResponse() { return response; }
95
+        public void setResponse(Object response) { this.response = response; }
96
+        public long getTimestamp() { return timestamp; }
97
+        public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
98
+    }
99
+    
100
+    /**
101
+     * 设备配置
102
+     */
103
+    public static class DeviceConfig {
104
+        private String deviceId;
105
+        private Map<String, Object> config;
106
+        private long version;
107
+        
108
+        // getters and setters
109
+        public String getDeviceId() { return deviceId; }
110
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
111
+        public Map<String, Object> getConfig() { return config; }
112
+        public void setConfig(Map<String, Object> config) { this.config = config; }
113
+        public long getVersion() { return version; }
114
+        public void setVersion(long version) { this.version = version; }
115
+    }
116
+}

+ 197
- 0
src/main/java/com/water/iot/protocol/http/HttpAdapter.java Прегледај датотеку

@@ -0,0 +1,197 @@
1
+package com.water.iot.protocol.http;
2
+
3
+import com.water.iot.protocol.ProtocolAdapter;
4
+import com.water.iot.model.DeviceModel.ConnectionInfo;
5
+import org.springframework.http.*;
6
+import org.springframework.web.client.RestTemplate;
7
+import com.fasterxml.jackson.databind.ObjectMapper;
8
+
9
+import java.util.Map;
10
+import java.util.HashMap;
11
+
12
+/**
13
+ * HTTP协议适配器
14
+ * 支持HTTP/HTTPS设备接入和数据通信
15
+ */
16
+@Component
17
+public class HttpAdapter implements ProtocolAdapter {
18
+    
19
+    private final RestTemplate restTemplate;
20
+    private final ObjectMapper objectMapper;
21
+    
22
+    public HttpAdapter() {
23
+        this.restTemplate = new RestTemplate();
24
+        this.objectMapper = new ObjectMapper();
25
+    }
26
+    
27
+    @Override
28
+    public String getProtocolType() {
29
+        return "HTTP";
30
+    }
31
+    
32
+    @Override
33
+    public boolean connect(ConnectionInfo connectionInfo) {
34
+        try {
35
+            String url = connectionInfo.getUrl() + "/ping";
36
+            HttpHeaders headers = new HttpHeaders();
37
+            headers.setContentType(MediaType.APPLICATION_JSON);
38
+            
39
+            HttpEntity<String> entity = new HttpEntity<>(headers);
40
+            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
41
+            
42
+            return response.getStatusCode().is2xxSuccessful();
43
+            
44
+        } catch (Exception e) {
45
+            return false;
46
+        }
47
+    }
48
+    
49
+    @Override
50
+    public void disconnect() {
51
+        // HTTP是无状态的,不需要显式断开
52
+    }
53
+    
54
+    @Override
55
+    public boolean isConnected() {
56
+        // HTTP是无状态的,通过心跳检查连接状态
57
+        try {
58
+            return true; // 简化实现
59
+        } catch (Exception e) {
60
+            return false;
61
+        }
62
+    }
63
+    
64
+    @Override
65
+    public ProtocolAdapter.DeviceData uploadData(String deviceId, byte[] rawData) {
66
+        try {
67
+            // 解析原始数据
68
+            String rawDataStr = new String(rawData);
69
+            Map<String, Object> data = objectMapper.readValue(rawDataStr, Map.class);
70
+            
71
+            ProtocolAdapter.DeviceData deviceData = new ProtocolAdapter.DeviceData();
72
+            deviceData.setDeviceId(deviceId);
73
+            deviceData.setTimestamp(System.currentTimeMillis());
74
+            deviceData.setProperties(data);
75
+            deviceData.setStatus("normal");
76
+            
77
+            return deviceData;
78
+            
79
+        } catch (Exception e) {
80
+            // 返回错误数据
81
+            ProtocolAdapter.DeviceData errorData = new ProtocolAdapter.DeviceData();
82
+            errorData.setDeviceId(deviceId);
83
+            errorData.setTimestamp(System.currentTimeMillis());
84
+            errorData.setStatus("error");
85
+            Map<String, Object> errorProps = new HashMap<>();
86
+            errorProps.put("errorMessage", e.getMessage());
87
+            errorData.setProperties(errorProps);
88
+            return errorData;
89
+        }
90
+    }
91
+    
92
+    @Override
93
+    public ProtocolAdapter.CommandResult sendCommand(String deviceId, String command, Object parameters) {
94
+        try {
95
+            String url = "http://" + ConnectionInfo().getHost() + ":" + ConnectionInfo().getPort() + "/api/command";
96
+            
97
+            Map<String, Object> request = new HashMap<>();
98
+            request.put("deviceId", deviceId);
99
+            request.put("command", command);
100
+            request.put("parameters", parameters);
101
+            request.put("timestamp", System.currentTimeMillis());
102
+            
103
+            HttpHeaders headers = new HttpHeaders();
104
+            headers.setContentType(MediaType.APPLICATION_JSON);
105
+            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
106
+            
107
+            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
108
+            
109
+            ProtocolAdapter.CommandResult result = new ProtocolAdapter.CommandResult();
110
+            result.setSuccess(response.getStatusCode().is2xxSuccessful());
111
+            result.setMessage(response.getBody());
112
+            result.setTimestamp(System.currentTimeMillis());
113
+            return result;
114
+            
115
+        } catch (Exception e) {
116
+            ProtocolAdapter.CommandResult result = new ProtocolAdapter.CommandResult();
117
+            result.setSuccess(false);
118
+            result.setMessage("HTTP command failed: " + e.getMessage());
119
+            result.setTimestamp(System.currentTimeMillis());
120
+            return result;
121
+        }
122
+    }
123
+    
124
+    @Override
125
+    public ProtocolAdapter.DeviceConfig getDeviceConfig(String deviceId) {
126
+        try {
127
+            String url = "http://" + ConnectionInfo().getHost() + ":" + ConnectionInfo().getPort() + "/api/config/" + deviceId;
128
+            
129
+            ResponseEntity<ProtocolAdapter.DeviceConfig> response = restTemplate.getForEntity(
130
+                url, ProtocolAdapter.DeviceConfig.class);
131
+            
132
+            if (response.getStatusCode().is2xxSuccessful()) {
133
+                return response.getBody();
134
+            } else {
135
+                // 返回默认配置
136
+                ProtocolAdapter.DeviceConfig defaultConfig = new ProtocolAdapter.DeviceConfig();
137
+                defaultConfig.setDeviceId(deviceId);
138
+                defaultConfig.setConfig(Map.of("defaultInterval", 30000));
139
+                defaultConfig.setVersion(1L);
140
+                return defaultConfig;
141
+            }
142
+            
143
+        } catch (Exception e) {
144
+            // 返回默认配置
145
+            ProtocolAdapter.DeviceConfig defaultConfig = new ProtocolAdapter.DeviceConfig();
146
+            defaultConfig.setDeviceId(deviceId);
147
+            defaultConfig.setConfig(Map.of("defaultInterval", 30000));
148
+            defaultConfig.setVersion(1L);
149
+            return defaultConfig;
150
+        }
151
+    }
152
+    
153
+    @Override
154
+    public boolean updateDeviceConfig(String deviceId, ProtocolAdapter.DeviceConfig config) {
155
+        try {
156
+            String url = "http://" + ConnectionInfo().getHost() + ":" + ConnectionInfo().getPort() + "/api/config/" + deviceId;
157
+            
158
+            HttpHeaders headers = new HttpHeaders();
159
+            headers.setContentType(MediaType.APPLICATION_JSON);
160
+            HttpEntity<ProtocolAdapter.DeviceConfig> entity = new HttpEntity<>(config, headers);
161
+            
162
+            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.PUT, entity, String.class);
163
+            
164
+            return response.getStatusCode().is2xxSuccessful();
165
+            
166
+        } catch (Exception e) {
167
+            return false;
168
+        }
169
+    }
170
+    
171
+    @Override
172
+    public boolean heartbeat(String deviceId) {
173
+        try {
174
+            String url = "http://" + ConnectionInfo().getHost() + ":" + ConnectionInfo().getPort() + "/api/heartbeat/" + deviceId;
175
+            
176
+            HttpHeaders headers = new HttpHeaders();
177
+            headers.setContentType(MediaType.APPLICATION_JSON);
178
+            HttpEntity<String> entity = new HttpEntity<>(headers);
179
+            
180
+            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
181
+            
182
+            return response.getStatusCode().is2xxSuccessful();
183
+            
184
+        } catch (Exception e) {
185
+            return false;
186
+        }
187
+    }
188
+    
189
+    // ConnectionInfo访问方法 - 临时实现
190
+    private ConnectionInfo ConnectionInfo() {
191
+        // 实际应用中应该从设备管理器获取
192
+        ConnectionInfo connectionInfo = new ConnectionInfo();
193
+        connectionInfo.setHost("localhost");
194
+        connectionInfo.setPort(8080);
195
+        return connectionInfo;
196
+    }
197
+}

+ 173
- 0
src/main/java/com/water/iot/protocol/mqtt/MqttAdapter.java Прегледај датотеку

@@ -0,0 +1,173 @@
1
+package com.water.iot.protocol.mqtt;
2
+
3
+import com.water.iot.protocol.ProtocolAdapter;
4
+import com.water.iot.model.DeviceModel.ConnectionInfo;
5
+import org.eclipse.paho.client.mqttv3.*;
6
+import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
7
+import org.springframework.stereotype.Component;
8
+
9
+import java.util.UUID;
10
+import java.util.concurrent.ConcurrentHashMap;
11
+import java.util.Map;
12
+
13
+/**
14
+ * MQTT协议适配器
15
+ * 支持MQTT设备接入和数据通信
16
+ */
17
+@Component
18
+public class MqttAdapter implements ProtocolAdapter {
19
+    
20
+    private final MqttClient mqttClient;
21
+    private final Map<String, MqttSubscription> deviceSubscriptions = new ConcurrentHashMap<>();
22
+    private final Map<String, IMqttMessageListener> messageListeners = new ConcurrentHashMap<>();
23
+    
24
+    public MqttAdapter() throws MqttException {
25
+        String clientId = "water-iot-" + UUID.randomUUID().toString();
26
+        this.mqttClient = new MqttClient("tcp://localhost:1883", clientId, new MemoryPersistence());
27
+        this.mqttClient.connect();
28
+    }
29
+    
30
+    @Override
31
+    public String getProtocolType() {
32
+        return "MQTT";
33
+    }
34
+    
35
+    @Override
36
+    public boolean connect(ConnectionInfo connectionInfo) {
37
+        try {
38
+            String clientId = connectionInfo.getDeviceConfig() != null ? 
39
+                connectionInfo.getDeviceConfig().get("clientId").toString() : 
40
+                "device-" + UUID.randomUUID().toString();
41
+            
42
+            MqttConnectOptions options = new MqttConnectOptions();
43
+            options.setCleanSession(true);
44
+            options.setAutomaticReconnect(true);
45
+            options.setConnectionTimeout(10);
46
+            
47
+            if (connectionInfo.getUsername() != null && connectionInfo.getPassword() != null) {
48
+                options.setUserName(connectionInfo.getUsername());
49
+                options.setPassword(connectionInfo.getPassword().toCharArray());
50
+            }
51
+            
52
+            MqttClient deviceClient = new MqttClient(connectionInfo.getHost() + ":" + connectionInfo.getPort(), 
53
+                clientId, new MemoryPersistence());
54
+            deviceClient.connect(options);
55
+            
56
+            return deviceClient.isConnected();
57
+            
58
+        } catch (Exception e) {
59
+            return false;
60
+        }
61
+    }
62
+    
63
+    @Override
64
+    public void disconnect() {
65
+        try {
66
+            if (mqttClient.isConnected()) {
67
+                mqttClient.disconnect();
68
+            }
69
+        } catch (MqttException e) {
70
+            // log error
71
+        }
72
+    }
73
+    
74
+    @Override
75
+    public boolean isConnected() {
76
+        return mqttClient.isConnected();
77
+    }
78
+    
79
+    @Override
80
+    public ProtocolAdapter.DeviceData uploadData(String deviceId, byte[] rawData) {
81
+        // 模拟数据解析
82
+        ProtocolAdapter.DeviceData data = new ProtocolAdapter.DeviceData();
83
+        data.setDeviceId(deviceId);
84
+        data.setTimestamp(System.currentTimeMillis());
85
+        data.setProperties(Map.of("temperature", 25.5, "pressure", 1013.25));
86
+        data.setTelemetry(Map.of("heartbeat", System.currentTimeMillis()));
87
+        data.setStatus("normal");
88
+        return data;
89
+    }
90
+    
91
+    @Override
92
+    public ProtocolAdapter.CommandResult sendCommand(String deviceId, String command, Object parameters) {
93
+        try {
94
+            String topic = "device/" + deviceId + "/command";
95
+            String payload = "{\"command\":\"" + command + "\", \"parameters\":" + parameters.toString() + "}";
96
+            
97
+            MqttMessage message = new MqttMessage(payload.getBytes());
98
+            message.setQos(1);
99
+            mqttClient.publish(topic, message);
100
+            
101
+            ProtocolAdapter.CommandResult result = new ProtocolAdapter.CommandResult();
102
+            result.setSuccess(true);
103
+            result.setMessage("Command sent successfully");
104
+            result.setTimestamp(System.currentTimeMillis());
105
+            return result;
106
+            
107
+        } catch (Exception e) {
108
+            ProtocolAdapter.CommandResult result = new ProtocolAdapter.CommandResult();
109
+            result.setSuccess(false);
110
+            result.setMessage("Failed to send command: " + e.getMessage());
111
+            result.setTimestamp(System.currentTimeMillis());
112
+            return result;
113
+        }
114
+    }
115
+    
116
+    @Override
117
+    public ProtocolAdapter.DeviceConfig getDeviceConfig(String deviceId) {
118
+        // 从缓存或数据库获取设备配置
119
+        ProtocolAdapter.DeviceConfig config = new ProtocolAdapter.DeviceConfig();
120
+        config.setDeviceId(deviceId);
121
+        config.setConfig(Map.of("samplingInterval", 5000, "reportInterval", 30000));
122
+        config.setVersion(1L);
123
+        return config;
124
+    }
125
+    
126
+    @Override
127
+    public boolean updateDeviceConfig(String deviceId, ProtocolAdapter.DeviceConfig config) {
128
+        try {
129
+            String topic = "device/" + deviceId + "/config";
130
+            String payload = "{\"config\":" + config.getConfig().toString() + ", \"version\":" + config.getVersion() + "}";
131
+            
132
+            MqttMessage message = new MqttMessage(payload.getBytes());
133
+            message.setQos(1);
134
+            mqttClient.publish(topic, message);
135
+            
136
+            return true;
137
+            
138
+        } catch (Exception e) {
139
+            return false;
140
+        }
141
+    }
142
+    
143
+    @Override
144
+    public boolean heartbeat(String deviceId) {
145
+        // 发送心跳包
146
+        try {
147
+            String topic = "device/" + deviceId + "/heartbeat";
148
+            String payload = "{\"timestamp\":" + System.currentTimeMillis() + "}";
149
+            
150
+            MqttMessage message = new MqttMessage(payload.getBytes());
151
+            message.setQos(0);
152
+            mqttClient.publish(topic, message);
153
+            
154
+            return true;
155
+            
156
+        } catch (Exception e) {
157
+            return false;
158
+        }
159
+    }
160
+    
161
+    /**
162
+     * MQTT订阅管理
163
+     */
164
+    private static class MqttSubscription {
165
+        private String topic;
166
+        private IMqttMessageListener listener;
167
+        
168
+        public MqttSubscription(String topic, IMqttMessageListener listener) {
169
+            this.topic = topic;
170
+            this.listener = listener;
171
+        }
172
+    }
173
+}

+ 259
- 0
src/main/java/com/water/iot/registry/DeviceRegistry.java Прегледај датотеку

@@ -0,0 +1,259 @@
1
+package com.water.iot.registry;
2
+
3
+import com.water.iot.model.DeviceModel;
4
+import com.water.iot.protocol.ProtocolAdapter;
5
+import com.water.iot.protocol.AdapterFactory;
6
+import org.springframework.stereotype.Service;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import java.util.*;
9
+import java.util.concurrent.ConcurrentHashMap;
10
+
11
+/**
12
+ * 设备注册服务
13
+ * 支持设备自动发现和手动注册
14
+ */
15
+@Service
16
+public class DeviceRegistry {
17
+    
18
+    private final Map<String, DeviceModel> deviceRegistry = new ConcurrentHashMap<>();
19
+    private final AdapterFactory adapterFactory;
20
+    
21
+    @Autowired
22
+    public DeviceRegistry(AdapterFactory adapterFactory) {
23
+        this.adapterFactory = adapterFactory;
24
+    }
25
+    
26
+    /**
27
+     * 手动注册设备
28
+     */
29
+    public DeviceModel registerDevice(DeviceModel deviceModel) {
30
+        // 验证设备信息
31
+        if (deviceModel.getDeviceId() == null || deviceModel.getDeviceId().isEmpty()) {
32
+            throw new IllegalArgumentException("Device ID cannot be empty");
33
+        }
34
+        
35
+        if (deviceModel.getProtocol() == null || !adapterFactory.isProtocolSupported(deviceModel.getProtocol())) {
36
+            throw new IllegalArgumentException("Unsupported protocol: " + deviceModel.getProtocol());
37
+        }
38
+        
39
+        // 设置注册时间和活跃时间
40
+        deviceModel.setRegisterTime(System.currentTimeMillis());
41
+        deviceModel.setLastActiveTime(System.currentTimeMillis());
42
+        
43
+        // 保存到注册表
44
+        deviceRegistry.put(deviceModel.getDeviceId(), deviceModel);
45
+        
46
+        // 连接设备
47
+        connectDevice(deviceModel);
48
+        
49
+        return deviceModel;
50
+    }
51
+    
52
+    /**
53
+     * 批量注册设备
54
+     */
55
+    public List<DeviceModel> registerDevices(List<DeviceModel> deviceModels) {
56
+        List<DeviceModel> result = new ArrayList<>();
57
+        for (DeviceModel device : deviceModels) {
58
+            try {
59
+                DeviceModel registeredDevice = registerDevice(device);
60
+                result.add(registeredDevice);
61
+            } catch (Exception e) {
62
+                // 记录失败,继续处理其他设备
63
+                System.err.println("Failed to register device " + device.getDeviceId() + ": " + e.getMessage());
64
+            }
65
+        }
66
+        return result;
67
+    }
68
+    
69
+    /**
70
+     * 注销设备
71
+     */
72
+    public boolean unregisterDevice(String deviceId) {
73
+        DeviceModel device = deviceRegistry.get(deviceId);
74
+        if (device != null) {
75
+            // 断开设备连接
76
+            disconnectDevice(device);
77
+            
78
+            // 从注册表移除
79
+            deviceRegistry.remove(deviceId);
80
+            return true;
81
+        }
82
+        return false;
83
+    }
84
+    
85
+    /**
86
+     * 根据ID获取设备
87
+     */
88
+    public DeviceModel getDevice(String deviceId) {
89
+        return deviceRegistry.get(deviceId);
90
+    }
91
+    
92
+    /**
93
+     * 获取所有设备
94
+     */
95
+    public List<DeviceModel> getAllDevices() {
96
+        return new ArrayList<>(deviceRegistry.values());
97
+    }
98
+    
99
+    /**
100
+     * 根据协议类型获取设备列表
101
+     */
102
+    public List<DeviceModel> getDevicesByProtocol(String protocol) {
103
+        return deviceRegistry.values().stream()
104
+            .filter(device -> protocol.equals(device.getProtocol()))
105
+            .toList();
106
+    }
107
+    
108
+    /**
109
+     * 根据状态获取设备列表
110
+     */
111
+    public List<DeviceModel> getDevicesByStatus(String status) {
112
+        return deviceRegistry.values().stream()
113
+            .filter(device -> {
114
+                DeviceModel.DeviceStatus deviceStatus = device.getStatus();
115
+                return deviceStatus != null && status.equals(deviceStatus.getState());
116
+            })
117
+            .toList();
118
+    }
119
+    
120
+    /**
121
+     * 自动发现设备
122
+     */
123
+    public List<DeviceModel> discoverDevices(String subnet, int portRangeStart, int portRangeEnd) {
124
+        List<DeviceModel> discoveredDevices = new ArrayList<>();
125
+        
126
+        // 这里实现设备发现逻辑
127
+        // 1. 扫描指定子网的端口
128
+        // 2. 检测设备响应
129
+        // 3. 识别设备类型和协议
130
+        // 4. 创建设备模型并注册
131
+        
132
+        // 示例:模拟发现一些设备
133
+        for (int i = 0; i < 3; i++) {
134
+            String deviceId = "discovered-device-" + UUID.randomUUID().toString();
135
+            DeviceModel device = DeviceModel.builder()
136
+                .deviceId(deviceId)
137
+                .name("Discovered Device " + (i + 1))
138
+                .deviceType("sensor")
139
+                .protocol("MQTT")
140
+                .connection(DeviceModel.ConnectionInfo.builder()
141
+                    .host("192.168.1." + (100 + i))
142
+                    .port(1883)
143
+                    .build())
144
+                .capabilities(DeviceModel.DeviceCapabilities.builder()
145
+                    .dataUpload(true)
146
+                    .commandReceive(true)
147
+                    .build())
148
+                .status(DeviceModel.DeviceStatus.builder()
149
+                    .online(false)
150
+                    .state("discovered")
151
+                    .build())
152
+                .registerTime(System.currentTimeMillis())
153
+                .lastActiveTime(System.currentTimeMillis())
154
+                .build();
155
+            
156
+            discoveredDevices.add(device);
157
+        }
158
+        
159
+        return discoveredDevices;
160
+    }
161
+    
162
+    /**
163
+     * 连接设备
164
+     */
165
+    private void connectDevice(DeviceModel device) {
166
+        try {
167
+            ProtocolAdapter adapter = adapterFactory.getAdapter(device.getProtocol());
168
+            if (adapter != null) {
169
+                boolean connected = adapter.connect(device.getConnection());
170
+                if (connected) {
171
+                    device.getStatus().setOnline(true);
172
+                    device.getStatus().setState("connected");
173
+                }
174
+            }
175
+        } catch (Exception e) {
176
+            device.getStatus().setOnline(false);
177
+            device.getStatus().setState("error");
178
+            device.getStatus().setErrorMessage("Connection failed: " + e.getMessage());
179
+        }
180
+    }
181
+    
182
+    /**
183
+     * 断开设备连接
184
+     */
185
+    private void disconnectDevice(DeviceModel device) {
186
+        try {
187
+            ProtocolAdapter adapter = adapterFactory.getAdapter(device.getProtocol());
188
+            if (adapter != null) {
189
+                adapter.disconnect();
190
+                device.getStatus().setOnline(false);
191
+                device.getStatus().setState("disconnected");
192
+            }
193
+        } catch (Exception e) {
194
+            device.getStatus().setState("error");
195
+            device.getStatus().setErrorMessage("Disconnection failed: " + e.getMessage());
196
+        }
197
+    }
198
+    
199
+    /**
200
+     * 更新设备状态
201
+     */
202
+    public void updateDeviceStatus(String deviceId, DeviceModel.DeviceStatus status) {
203
+        DeviceModel device = deviceRegistry.get(deviceId);
204
+        if (device != null) {
205
+            device.setStatus(status);
206
+            device.setLastActiveTime(System.currentTimeMillis());
207
+        }
208
+    }
209
+    
210
+    /**
211
+     * 获取注册表统计信息
212
+     */
213
+    public RegistryStatistics getStatistics() {
214
+        RegistryStatistics stats = new RegistryStatistics();
215
+        stats.setTotalDevices(deviceRegistry.size());
216
+        stats.setOnlineDevices((int) deviceRegistry.values().stream()
217
+            .filter(device -> device.getStatus() != null && device.getStatus().isOnline())
218
+            .count());
219
+        stats.setOfflineDevices((int) deviceRegistry.values().stream()
220
+            .filter(device -> device.getStatus() != null && !device.getStatus().isOnline())
221
+            .count());
222
+        stats.setErrorDevices((int) deviceRegistry.values().stream()
223
+            .filter(device -> device.getStatus() != null && "error".equals(device.getStatus().getState()))
224
+            .count());
225
+        
226
+        // 协议分布
227
+        Map<String, Long> protocolDistribution = new HashMap<>();
228
+        deviceRegistry.values().forEach(device -> {
229
+            protocolDistribution.put(device.getProtocol(), 
230
+                protocolDistribution.getOrDefault(device.getProtocol(), 0L) + 1);
231
+        });
232
+        stats.setProtocolDistribution(protocolDistribution);
233
+        
234
+        return stats;
235
+    }
236
+    
237
+    /**
238
+     * 注册表统计信息
239
+     */
240
+    public static class RegistryStatistics {
241
+        private int totalDevices;
242
+        private int onlineDevices;
243
+        private int offlineDevices;
244
+        private int errorDevices;
245
+        private Map<String, Long> protocolDistribution;
246
+        
247
+        // getters and setters
248
+        public int getTotalDevices() { return totalDevices; }
249
+        public void setTotalDevices(int totalDevices) { this.totalDevices = totalDevices; }
250
+        public int getOnlineDevices() { return onlineDevices; }
251
+        public void setOnlineDevices(int onlineDevices) { this.onlineDevices = onlineDevices; }
252
+        public int getOfflineDevices() { return offlineDevices; }
253
+        public void setOfflineDevices(int offlineDevices) { this.offlineDevices = offlineDevices; }
254
+        public int getErrorDevices() { return errorDevices; }
255
+        public void setErrorDevices(int errorDevices) { this.errorDevices = errorDevices; }
256
+        public Map<String, Long> getProtocolDistribution() { return protocolDistribution; }
257
+        public void setProtocolDistribution(Map<String, Long> protocolDistribution) { this.protocolDistribution = protocolDistribution; }
258
+    }
259
+}

+ 268
- 0
src/main/java/com/water/iot/shadow/DeviceShadowService.java Прегледај датотеку

@@ -0,0 +1,268 @@
1
+package com.water.iot.shadow;
2
+
3
+import com.water.iot.model.DeviceModel;
4
+import com.water.iot.protocol.ProtocolAdapter;
5
+import com.water.iot.registry.DeviceRegistry;
6
+import org.springframework.stereotype.Service;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import java.util.*;
9
+import java.util.concurrent.ConcurrentHashMap;
10
+
11
+/**
12
+ * 设备影子服务
13
+ * 提供设备状态缓存、离线指令重发、delta计算等功能
14
+ */
15
+@Service
16
+public class DeviceShadowService {
17
+    
18
+    private final Map<String, DeviceShadow> deviceShadows = new ConcurrentHashMap<>();
19
+    private final DeviceRegistry deviceRegistry;
20
+    private final Map<String, Queue<Command>> pendingCommands = new ConcurrentHashMap<>();
21
+    
22
+    @Autowired
23
+    public DeviceShadowService(DeviceRegistry deviceRegistry) {
24
+        this.deviceRegistry = deviceRegistry;
25
+    }
26
+    
27
+    /**
28
+     * 获取设备影子
29
+     */
30
+    public DeviceShadow getDeviceShadow(String deviceId) {
31
+        return deviceShadows.get(deviceId);
32
+    }
33
+    
34
+    /**
35
+     * 创建或更新设备影子
36
+     */
37
+    public DeviceShadow updateDeviceShadow(String deviceId, DeviceModel deviceData) {
38
+        DeviceShadow shadow = deviceShadows.computeIfAbsent(deviceId, id -> {
39
+            DeviceShadow newShadow = new DeviceShadow();
40
+            newShadow.setDeviceId(id);
41
+            newShadow.setDesiredState(new HashMap<>());
42
+            newShadow.setReportedState(new HashMap<>());
43
+            newShadow.setVersion(0L);
44
+            newShadow.setDelta(new HashMap<>());
45
+            newShadow.setLastUpdated(System.currentTimeMillis());
46
+            return newShadow;
47
+        });
48
+        
49
+        // 更新报告的状态
50
+        if (deviceData.getProperties() != null) {
51
+            shadow.getReportedState().putAll(deviceData.getProperties());
52
+        }
53
+        if (deviceData.getTelemetry() != null) {
54
+            shadow.getReportedState().putAll(deviceData.getTelemetry());
55
+        }
56
+        
57
+        // 计算delta
58
+        calculateDelta(shadow);
59
+        
60
+        // 更新时间戳
61
+        shadow.setLastUpdated(System.currentTimeMillis());
62
+        shadow.setVersion(shadow.getVersion() + 1);
63
+        
64
+        return shadow;
65
+    }
66
+    
67
+    /**
68
+     * 设置期望状态
69
+     */
70
+    public void setDesiredState(String deviceId, Map<String, Object> desiredState) {
71
+        DeviceShadow shadow = deviceShadows.get(deviceId);
72
+        if (shadow != null) {
73
+            shadow.getDesiredState().clear();
74
+            shadow.getDesiredState().putAll(desiredState);
75
+            calculateDelta(shadow);
76
+            
77
+            // 如果设备在线,立即同步状态
78
+            DeviceModel device = deviceRegistry.getDevice(deviceId);
79
+            if (device != null && device.getStatus().isOnline()) {
80
+                syncDesiredStateToDevice(deviceId);
81
+            }
82
+        }
83
+    }
84
+    
85
+    /**
86
+     * 同步期望状态到设备
87
+     */
88
+    public boolean syncDesiredStateToDevice(String deviceId) {
89
+        DeviceShadow shadow = deviceShadows.get(deviceId);
90
+        DeviceModel device = deviceRegistry.getDevice(deviceId);
91
+        
92
+        if (shadow == null || device == null || !device.getStatus().isOnline()) {
93
+            return false;
94
+        }
95
+        
96
+        // 创建配置更新
97
+        ProtocolAdapter.DeviceConfig config = new ProtocolAdapter.DeviceConfig();
98
+        config.setDeviceId(deviceId);
99
+        config.setConfig(shadow.getDesiredState());
100
+        config.setVersion(shadow.getVersion());
101
+        
102
+        // 发送配置更新
103
+        try {
104
+            ProtocolAdapter adapter = getDeviceAdapter(device);
105
+            if (adapter != null) {
106
+                boolean success = adapter.updateDeviceConfig(deviceId, config);
107
+                if (success) {
108
+                    // 同步成功,更新报告状态
109
+                    shadow.getReportedState().putAll(shadow.getDesiredState());
110
+                    calculateDelta(shadow);
111
+                }
112
+                return success;
113
+            }
114
+        } catch (Exception e) {
115
+            // 同步失败,将命令加入队列
116
+            addPendingCommand(deviceId, "CONFIG_UPDATE", shadow.getDesiredState());
117
+        }
118
+        
119
+        return false;
120
+    }
121
+    
122
+    /**
123
+     * 处理离线设备重连
124
+     */
125
+    public void handleDeviceReconnect(String deviceId) {
126
+        DeviceShadow shadow = deviceShadows.get(deviceId);
127
+        if (shadow != null) {
128
+            // 重发挂起的命令
129
+            Queue<Command> commands = pendingCommands.get(deviceId);
130
+            if (commands != null && !commands.isEmpty()) {
131
+                List<Command> failedCommands = new ArrayList<>();
132
+                
133
+                while (!commands.isEmpty()) {
134
+                    Command command = commands.poll();
135
+                    try {
136
+                        executeCommand(deviceId, command);
137
+                    } catch (Exception e) {
138
+                        // 如果命令执行失败,重新加入队列(最多重试3次)
139
+                        if (command.getRetryCount() < 3) {
140
+                            command.setRetryCount(command.getRetryCount() + 1);
141
+                            commands.add(command);
142
+                        } else {
143
+                            failedCommands.add(command);
144
+                        }
145
+                    }
146
+                }
147
+                
148
+                // 移除失败的命令
149
+                if (!failedCommands.isEmpty()) {
150
+                    commands.addAll(failedCommands);
151
+                }
152
+            }
153
+            
154
+            // 同步未同步的期望状态
155
+            syncDesiredStateToDevice(deviceId);
156
+        }
157
+    }
158
+    
159
+    /**
160
+     * 添加挂起命令
161
+     */
162
+    private void addPendingCommand(String deviceId, String commandType, Object parameters) {
163
+        Queue<Command> commands = pendingCommands.computeIfAbsent(deviceId, id -> new LinkedList<>());
164
+        commands.add(new Command(commandType, parameters, 0));
165
+    }
166
+    
167
+    /**
168
+     * 执行命令
169
+     */
170
+    private void executeCommand(String deviceId, Command command) {
171
+        DeviceModel device = deviceRegistry.getDevice(deviceId);
172
+        if (device != null) {
173
+            ProtocolAdapter adapter = getDeviceAdapter(device);
174
+            if (adapter != null) {
175
+                adapter.sendCommand(deviceId, command.getCommandType(), command.getParameters());
176
+            }
177
+        }
178
+    }
179
+    
180
+    /**
181
+     * 计算delta
182
+     */
183
+    private void calculateDelta(DeviceShadow shadow) {
184
+        Map<String, Object> delta = new HashMap<>();
185
+        
186
+        // 检查期望状态与报告状态的差异
187
+        for (Map.Entry<String, Object> entry : shadow.getDesiredState().entrySet()) {
188
+            String key = entry.getKey();
189
+            Object desiredValue = entry.getValue();
190
+            Object reportedValue = shadow.getReportedState().get(key);
191
+            
192
+            if (!Objects.equals(desiredValue, reportedValue)) {
193
+                delta.put(key, desiredValue);
194
+            }
195
+        }
196
+        
197
+        shadow.setDelta(delta);
198
+    }
199
+    
200
+    /**
201
+     * 获取设备适配器
202
+     */
203
+    private ProtocolAdapter getDeviceAdapter(DeviceModel device) {
204
+        // 这里应该从适配器工厂获取适配器
205
+        // 简化实现,返回null
206
+        return null;
207
+    }
208
+    
209
+    /**
210
+     * 清理过期影子
211
+     */
212
+    public void cleanupExpiredShadows(long expirationMillis) {
213
+        long currentTime = System.currentTimeMillis();
214
+        deviceShadows.entrySet().removeIf(entry -> {
215
+            DeviceShadow shadow = entry.getValue();
216
+            return (currentTime - shadow.getLastUpdated()) > expirationMillis;
217
+        });
218
+    }
219
+    
220
+    /**
221
+     * 设备影子实体
222
+     */
223
+    public static class DeviceShadow {
224
+        private String deviceId;
225
+        private Map<String, Object> desiredState;
226
+        private Map<String, Object> reportedState;
227
+        private Map<String, Object> delta;
228
+        private long version;
229
+        private long lastUpdated;
230
+        
231
+        // getters and setters
232
+        public String getDeviceId() { return deviceId; }
233
+        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
234
+        public Map<String, Object> getDesiredState() { return desiredState; }
235
+        public void setDesiredState(Map<String, Object> desiredState) { this.desiredState = desiredState; }
236
+        public Map<String, Object> getReportedState() { return reportedState; }
237
+        public void setReportedState(Map<String, Object> reportedState) { this.reportedState = reportedState; }
238
+        public Map<String, Object> getDelta() { return delta; }
239
+        public void setDelta(Map<String, Object> delta) { this.delta = delta; }
240
+        public long getVersion() { return version; }
241
+        public void setVersion(long version) { this.version = version; }
242
+        public long getLastUpdated() { return lastUpdated; }
243
+        public void setLastUpdated(long lastUpdated) { this.lastUpdated = lastUpdated; }
244
+    }
245
+    
246
+    /**
247
+     * 命令实体
248
+     */
249
+    private static class Command {
250
+        private String commandType;
251
+        private Object parameters;
252
+        private int retryCount;
253
+        
254
+        public Command(String commandType, Object parameters, int retryCount) {
255
+            this.commandType = commandType;
256
+            this.parameters = parameters;
257
+            this.retryCount = retryCount;
258
+        }
259
+        
260
+        // getters and setters
261
+        public String getCommandType() { return commandType; }
262
+        public void setCommandType(String commandType) { this.commandType = commandType; }
263
+        public Object getParameters() { return parameters; }
264
+        public void setParameters(Object parameters) { this.parameters = parameters; }
265
+        public int getRetryCount() { return retryCount; }
266
+        public void setRetryCount(int retryCount) { this.retryCount = retryCount; }
267
+    }
268
+}

+ 197
- 0
src/main/resources/application.yml Прегледај датотеку

@@ -0,0 +1,197 @@
1
+server:
2
+  port: 8080
3
+  servlet:
4
+    context-path: /water-iot
5
+
6
+spring:
7
+  application:
8
+    name: water-iot-platform
9
+  
10
+  datasource:
11
+    url: jdbc:postgresql://localhost:5432/water_iot
12
+    username: water_user
13
+    password: water_password
14
+    driver-class-name: org.postgresql.Driver
15
+    hikari:
16
+      maximum-pool-size: 20
17
+      minimum-idule: 5
18
+      connection-timeout: 30000
19
+      idle-timeout: 600000
20
+      max-lifetime: 1800000
21
+  
22
+  jpa:
23
+    hibernate:
24
+      ddl-auto: update
25
+    show-sql: false
26
+    properties:
27
+      hibernate:
28
+        dialect: org.hibernate.dialect.PostgreSQLDialect
29
+        format_sql: true
30
+        use_sql_comments: true
31
+        jdbc:
32
+          batch_size: 20
33
+          order_inserts: true
34
+          order_updates: true
35
+
36
+# MQTT配置
37
+mqtt:
38
+  broker:
39
+    host: localhost
40
+    port: 1883
41
+    username: mqtt_user
42
+    password: mqtt_password
43
+    client-id: water-iot-platform
44
+    connection-timeout: 10
45
+    keep-alive: 60
46
+    clean-session: true
47
+    automatic-reconnect: true
48
+
49
+# 设备配置
50
+device:
51
+  registry:
52
+    auto-discovery:
53
+      enabled: true
54
+      subnet: "192.168.1.0/24"
55
+      scan-interval: 300000  # 5分钟
56
+      timeout: 5000
57
+    
58
+  shadow:
59
+    retention:
60
+      enabled: true
61
+      duration: 86400000  # 24小时
62
+    
63
+    sync:
64
+      enabled: true
65
+      interval: 30000  # 30秒
66
+      retry-count: 3
67
+      retry-delay: 5000
68
+
69
+# 监控配置
70
+monitor:
71
+  health-check:
72
+    enabled: true
73
+    interval: 30000  # 30秒
74
+    timeout: 10000  # 10秒
75
+    heartbeat-timeout: 300000  # 5分钟
76
+    
77
+  alert:
78
+    enabled: true
79
+    alert-email: admin@water.com
80
+    alert-phone: 13800138000
81
+    retry-count: 3
82
+    retry-delay: 5000
83
+
84
+# 配置管理
85
+config:
86
+  management:
87
+    enabled: true
88
+    sync-interval: 60000  # 1分钟
89
+    validation-enabled: true
90
+    
91
+  ota:
92
+    enabled: true
93
+    temp-directory: "/tmp/water-iot/ota"
94
+    max-file-size: 104857600  # 100MB
95
+    timeout: 300000  # 5分钟
96
+
97
+# API配置
98
+api:
99
+  enable-telemetry: true
100
+  enable-command: true
101
+  enable-device-management: true
102
+  enable-monitoring: true
103
+  enable-configuration: true
104
+  
105
+  rate-limit:
106
+    enabled: true
107
+    requests-per-minute: 1000
108
+    burst-size: 100
109
+    
110
+  security:
111
+    enabled: true
112
+    auth-token: "water-iot-platform-token"
113
+    cors:
114
+      enabled: true
115
+      allowed-origins: "*"
116
+      allowed-methods: "GET,POST,PUT,DELETE,OPTIONS"
117
+      allowed-headers: "*"
118
+
119
+# 日志配置
120
+logging:
121
+  level:
122
+    com.water.iot: INFO
123
+    org.springframework.web: INFO
124
+    org.springframework.security: INFO
125
+    
126
+  pattern:
127
+    console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
128
+    file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
129
+    
130
+  file:
131
+    name: logs/water-iot.log
132
+    max-size: 50MB
133
+    max-history: 30
134
+    total-size-cap: 1GB
135
+
136
+# 缓存配置
137
+cache:
138
+  type: redis
139
+  redis:
140
+    host: localhost
141
+    port: 6379
142
+    password: redis_password
143
+    database: 0
144
+    timeout: 5000
145
+    lettuce:
146
+      pool:
147
+        max-active: 20
148
+        max-idle: 10
149
+        min-idle: 5
150
+        max-wait: 3000
151
+
152
+# WebSocket配置
153
+websocket:
154
+  enabled: true
155
+  endpoint: "/ws/iot"
156
+  heartbeat-interval: 30000
157
+  connection-timeout: 60000
158
+  max-text-message-size: 1048576
159
+
160
+# 告警配置
161
+alert:
162
+  enabled: true
163
+  websocket:
164
+    enabled: true
165
+    endpoint: "/ws/alerts"
166
+  
167
+  email:
168
+    enabled: true
169
+    smtp:
170
+      host: smtp.water.com
171
+      port: 587
172
+      username: alert@water.com
173
+      password: alert_password
174
+      from: alert@water.com
175
+      to: admin@water.com
176
+  
177
+  sms:
178
+    enabled: true
179
+    provider: aliyun
180
+    access-key: your-access-key
181
+    secret-key: your-secret-key
182
+    sign-name: "水务平台"
183
+    template-code: "SMS_123456789"
184
+
185
+# 性能监控
186
+management:
187
+  endpoints:
188
+    web:
189
+      exposure:
190
+        include: health,info,metrics,prometheus
191
+  endpoint:
192
+    health:
193
+      show-details: always
194
+  metrics:
195
+    export:
196
+      prometheus:
197
+        enabled: true