@thejrsoft/subway-protocol 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ACK_MESSAGES_IMPLEMENTATION_SUMMARY.md +128 -0
- package/ACK_MESSAGE_DESIGN.md +457 -0
- package/CHANGELOG.md +58 -0
- package/COMMAND_VALIDATION_RULES.md +178 -0
- package/DOCUMENTATION_REORGANIZATION_SUMMARY.md +81 -0
- package/DOCUMENTATION_STRUCTURE.md +106 -0
- package/GATEWAY_MIGRATION_GUIDE.md +130 -0
- package/GATEWAY_PROTOCOL_COMPARISON.md +216 -0
- package/INTEGRATION_GUIDE.md +190 -0
- package/OPTIONAL_FIELDS_WITHOUT_DEFAULTS.md +97 -0
- package/PROTOCOL_UTILS_USAGE.md +278 -0
- package/README.md +237 -0
- package/TYPE_FIXES_SUMMARY.md +210 -0
- package/UPDATE_ENUM_VALUES.md +139 -0
- package/dist/asyncapi-sync.d.ts +47 -0
- package/dist/asyncapi-sync.d.ts.map +1 -0
- package/dist/asyncapi-sync.js +85 -0
- package/dist/asyncapi-sync.js.map +1 -0
- package/dist/command-factory.d.ts +62 -0
- package/dist/command-factory.d.ts.map +1 -0
- package/dist/command-factory.js +137 -0
- package/dist/command-factory.js.map +1 -0
- package/dist/command-types.d.ts +27 -0
- package/dist/command-types.d.ts.map +1 -0
- package/dist/command-types.js +31 -0
- package/dist/command-types.js.map +1 -0
- package/dist/index.d.ts +403 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +413 -0
- package/dist/index.js.map +1 -0
- package/dist/message-validator.d.ts +102 -0
- package/dist/message-validator.d.ts.map +1 -0
- package/dist/message-validator.js +640 -0
- package/dist/message-validator.js.map +1 -0
- package/dist/protocol-utils.d.ts +108 -0
- package/dist/protocol-utils.d.ts.map +1 -0
- package/dist/protocol-utils.js +293 -0
- package/dist/protocol-utils.js.map +1 -0
- package/docs/01-protocol/README.md +45 -0
- package/docs/01-protocol/design-rationale.md +198 -0
- package/docs/01-protocol/message-types.md +669 -0
- package/docs/01-protocol/specification.md +1466 -0
- package/docs/02-commands/README.md +56 -0
- package/docs/02-commands/batch-command.md +435 -0
- package/docs/02-commands/complex-command.md +537 -0
- package/docs/02-commands/simple-command.md +332 -0
- package/docs/02-commands/typed-commands.md +362 -0
- package/docs/03-architecture/README.md +66 -0
- package/docs/03-architecture/device-protocol.md +430 -0
- package/docs/03-architecture/edge-proxy.md +727 -0
- package/docs/03-architecture/routing-flow.md +893 -0
- package/docs/04-integration/README.md +144 -0
- package/docs/04-integration/backend-guide.md +551 -0
- package/docs/04-integration/edge-guide.md +684 -0
- package/docs/04-integration/gateway-guide.md +180 -0
- package/docs/04-integration/migration-guide.md +226 -0
- package/docs/05-examples/README.md +141 -0
- package/docs/05-examples/progress-update-examples.md +757 -0
- package/docs/06-reference/README.md +67 -0
- package/docs/06-reference/api.md +572 -0
- package/docs/06-reference/faq.md +302 -0
- package/docs/06-reference/glossary.md +232 -0
- package/examples/backend-upgrade.ts +279 -0
- package/examples/edge-multi-device.ts +513 -0
- package/examples/gateway-upgrade.ts +150 -0
- package/examples/protocol-implementation.ts +715 -0
- package/package.json +48 -0
- package/scripts/validate-asyncapi.ts +78 -0
- package/src/__tests__/protocol.test.ts +297 -0
- package/src/asyncapi-sync.ts +84 -0
- package/src/command-factory.ts +183 -0
- package/src/command-types.ts +72 -0
- package/src/edge-proxy.ts +494 -0
- package/src/gateway-extensions.ts +278 -0
- package/src/index.ts +792 -0
- package/src/message-validator.ts +726 -0
- package/src/protocol-utils.ts +355 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 架构文档
|
|
2
|
+
|
|
3
|
+
本目录包含 JRSoft Subway 系统的架构设计文档。
|
|
4
|
+
|
|
5
|
+
## 📚 文档列表
|
|
6
|
+
|
|
7
|
+
### [Edge 代理架构](./edge-proxy.md)
|
|
8
|
+
Edge 节点的设计和实现:
|
|
9
|
+
- Edge 作为设备代理的角色
|
|
10
|
+
- 设备连接管理
|
|
11
|
+
- 消息路由和转发
|
|
12
|
+
- 故障处理和重连机制
|
|
13
|
+
|
|
14
|
+
### [设备协议](./device-protocol.md)
|
|
15
|
+
设备到 Edge 的 WebSocket 协议:
|
|
16
|
+
- 设备注册流程
|
|
17
|
+
- 心跳机制(30秒间隔)
|
|
18
|
+
- 命令转发和响应
|
|
19
|
+
- 连接状态管理
|
|
20
|
+
|
|
21
|
+
### [消息路由流程](./routing-flow.md)
|
|
22
|
+
系统中的消息路由机制:
|
|
23
|
+
- Backend → Gateway → Edge → Device 流程
|
|
24
|
+
- targetClientId 格式和解析
|
|
25
|
+
- 路由决策逻辑
|
|
26
|
+
- 错误处理和超时
|
|
27
|
+
|
|
28
|
+
## 🏗️ 系统架构
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
┌─────────────┐ ┌─────────────┐ ┌──────────┐ ┌──────────┐
|
|
32
|
+
│ Backend │────▶│ Gateway │────▶│ Edge │────▶│ Device │
|
|
33
|
+
│ (FastAPI) │◀────│(WebSocket) │◀────│ (Proxy) │◀────│(Client) │
|
|
34
|
+
└─────────────┘ └─────────────┘ └──────────┘ └──────────┘
|
|
35
|
+
18082 18081 Dynamic Dynamic
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 🔑 核心概念
|
|
39
|
+
|
|
40
|
+
### 连接管理
|
|
41
|
+
- **Backend-Gateway**: HTTP/WebSocket 混合模式
|
|
42
|
+
- **Gateway-Edge**: 持久 WebSocket 连接
|
|
43
|
+
- **Edge-Device**: 设备主动连接,Edge 管理
|
|
44
|
+
|
|
45
|
+
### 标识符格式
|
|
46
|
+
- **Edge ID**: `edge-{location}` (如 `edge-001`)
|
|
47
|
+
- **Device ID**: `{type}-{number}` (如 `td-01`, `screen-05`)
|
|
48
|
+
- **Target Client ID**: 直接使用设备ID (如 `td-01`),Gateway自动路由
|
|
49
|
+
|
|
50
|
+
### 消息流向
|
|
51
|
+
- **下行命令**: Backend → Gateway → Edge → Device
|
|
52
|
+
- **上行响应**: Device → Edge → Gateway → Backend
|
|
53
|
+
- **进度更新**: 支持多次中间状态报告
|
|
54
|
+
|
|
55
|
+
## 🚀 快速理解
|
|
56
|
+
|
|
57
|
+
1. **Gateway 是中心枢纽** - 所有消息都经过 Gateway 路由
|
|
58
|
+
2. **Edge 是设备代理** - 设备不直接连接 Gateway
|
|
59
|
+
3. **设备主动连接** - 设备连接到 Edge,而非相反
|
|
60
|
+
4. **自动路由** - Backend只需指定设备ID,Gateway自动查找对应Edge
|
|
61
|
+
|
|
62
|
+
## 🔗 相关文档
|
|
63
|
+
|
|
64
|
+
- [协议规范](../01-protocol/) - 了解消息格式
|
|
65
|
+
- [命令系统](../02-commands/) - 了解命令类型
|
|
66
|
+
- [集成指南](../04-integration/) - 如何集成各组件
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# 设备端到 Edge 的 WebSocket 协议
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
设备端通过 WebSocket 协议连接到 Edge 节点,使用与 Gateway 相同的协议规范。Edge 在此架构中扮演本地 Gateway 的角色,为设备端提供注册、心跳、命令执行等服务。
|
|
6
|
+
|
|
7
|
+
## 架构更新
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────┐
|
|
11
|
+
│ Gateway │
|
|
12
|
+
│ (中央路由和管理服务器) │
|
|
13
|
+
└─────────────────────────────────────────────────┘
|
|
14
|
+
↑
|
|
15
|
+
│ WebSocket (Edge→Gateway)
|
|
16
|
+
│
|
|
17
|
+
┌─────────────────────────────────────────────────┐
|
|
18
|
+
│ Edge Node (edge-001) │
|
|
19
|
+
│ (本地设备管理和代理服务) │
|
|
20
|
+
│ │
|
|
21
|
+
│ ┌───────────────────────────────────────────┐ │
|
|
22
|
+
│ │ 设备连接管理器 │ │
|
|
23
|
+
│ │ - WebSocket 服务器 (端口: 18083) │ │
|
|
24
|
+
│ │ - 设备注册处理 │ │
|
|
25
|
+
│ │ - 心跳监控 │ │
|
|
26
|
+
│ │ - 命令路由和转发 │ │
|
|
27
|
+
│ └───────────────────────────────────────────┘ │
|
|
28
|
+
└─────────────────────────────────────────────────┘
|
|
29
|
+
↑
|
|
30
|
+
│ WebSocket (Device→Edge)
|
|
31
|
+
│
|
|
32
|
+
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
|
|
33
|
+
│设备端1 │ │设备端2 │ │设备端3 │ │设备端4 │
|
|
34
|
+
└───────┘ └───────┘ └───────┘ └───────┘
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 设备端注册流程
|
|
38
|
+
|
|
39
|
+
### 1. 设备端连接到 Edge
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// 设备端代码示例
|
|
43
|
+
const ws = new WebSocket('ws://edge-001:18083');
|
|
44
|
+
|
|
45
|
+
ws.on('open', () => {
|
|
46
|
+
// 发送注册消息
|
|
47
|
+
const registerMsg = {
|
|
48
|
+
type: 'register',
|
|
49
|
+
clientId: 'device-001',
|
|
50
|
+
clientType: 'device',
|
|
51
|
+
clientInfo: {
|
|
52
|
+
version: '1.0.0',
|
|
53
|
+
deviceType: 'media_player',
|
|
54
|
+
capabilities: ['play', 'stop', 'pause', 'status_report']
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
ws.send(JSON.stringify(registerMsg));
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. Edge 处理设备注册
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Edge 端处理逻辑
|
|
66
|
+
class EdgeDeviceHandler {
|
|
67
|
+
private devices = new Map<string, DeviceConnection>();
|
|
68
|
+
|
|
69
|
+
handleDeviceRegister(ws: WebSocket, message: RegisterMessage) {
|
|
70
|
+
const { clientId, clientInfo } = message;
|
|
71
|
+
|
|
72
|
+
// 1. 记录设备连接
|
|
73
|
+
this.devices.set(clientId, {
|
|
74
|
+
ws,
|
|
75
|
+
clientId,
|
|
76
|
+
clientInfo,
|
|
77
|
+
registeredAt: new Date(),
|
|
78
|
+
lastHeartbeat: new Date()
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// 2. 发送注册确认给设备
|
|
82
|
+
const ack = {
|
|
83
|
+
type: 'register_ack',
|
|
84
|
+
clientId,
|
|
85
|
+
success: true,
|
|
86
|
+
sessionId: generateSessionId(),
|
|
87
|
+
timestamp: new Date().toISOString()
|
|
88
|
+
};
|
|
89
|
+
ws.send(JSON.stringify(ack));
|
|
90
|
+
|
|
91
|
+
// 3. 如果已连接到 Gateway,代理注册到 Gateway
|
|
92
|
+
if (this.gatewayConnected) {
|
|
93
|
+
this.proxyRegisterToGateway(clientId, clientInfo);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 代理注册到 Gateway
|
|
98
|
+
proxyRegisterToGateway(deviceId: string, deviceInfo: ClientInfo) {
|
|
99
|
+
const proxyRegister = {
|
|
100
|
+
type: 'register',
|
|
101
|
+
clientId: deviceId,
|
|
102
|
+
clientType: 'device',
|
|
103
|
+
clientInfo: deviceInfo,
|
|
104
|
+
edgeInfo: {
|
|
105
|
+
edgeId: this.edgeId,
|
|
106
|
+
edgeVersion: this.version,
|
|
107
|
+
connectionTime: new Date().toISOString()
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
this.gatewayWs.send(JSON.stringify(proxyRegister));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 心跳机制
|
|
117
|
+
|
|
118
|
+
### 设备端心跳
|
|
119
|
+
|
|
120
|
+
设备端需要定期向 Edge 发送心跳,保持连接活跃:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// 设备端心跳实现
|
|
124
|
+
class DeviceClient {
|
|
125
|
+
private heartbeatInterval: NodeJS.Timer;
|
|
126
|
+
private heartbeatSequence = 0;
|
|
127
|
+
|
|
128
|
+
startHeartbeat() {
|
|
129
|
+
this.heartbeatInterval = setInterval(() => {
|
|
130
|
+
const heartbeat = {
|
|
131
|
+
type: 'heartbeat',
|
|
132
|
+
clientId: this.clientId,
|
|
133
|
+
sequence: ++this.heartbeatSequence,
|
|
134
|
+
timestamp: new Date().toISOString()
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
this.ws.send(JSON.stringify(heartbeat));
|
|
138
|
+
}, 30000); // 30秒一次
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
handleHeartbeatAck(message: HeartbeatAckMessage) {
|
|
142
|
+
// 更新最后确认时间
|
|
143
|
+
this.lastHeartbeatAck = new Date();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Edge 心跳监控
|
|
149
|
+
|
|
150
|
+
Edge 监控所有设备的心跳状态:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
class EdgeHeartbeatMonitor {
|
|
154
|
+
private readonly HEARTBEAT_TIMEOUT = 90000; // 90秒超时
|
|
155
|
+
|
|
156
|
+
startMonitoring() {
|
|
157
|
+
setInterval(() => {
|
|
158
|
+
const now = Date.now();
|
|
159
|
+
|
|
160
|
+
for (const [clientId, device] of this.devices) {
|
|
161
|
+
const lastHeartbeat = device.lastHeartbeat.getTime();
|
|
162
|
+
|
|
163
|
+
if (now - lastHeartbeat > this.HEARTBEAT_TIMEOUT) {
|
|
164
|
+
this.handleDeviceTimeout(clientId);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}, 10000); // 每10秒检查一次
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
handleHeartbeat(clientId: string, message: HeartbeatMessage) {
|
|
171
|
+
const device = this.devices.get(clientId);
|
|
172
|
+
if (device) {
|
|
173
|
+
device.lastHeartbeat = new Date();
|
|
174
|
+
|
|
175
|
+
// 发送心跳确认
|
|
176
|
+
const ack = {
|
|
177
|
+
type: 'heartbeat_ack',
|
|
178
|
+
clientId,
|
|
179
|
+
sequence: message.sequence
|
|
180
|
+
};
|
|
181
|
+
device.ws.send(JSON.stringify(ack));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
handleDeviceTimeout(clientId: string) {
|
|
186
|
+
console.log(`Device ${clientId} heartbeat timeout`);
|
|
187
|
+
|
|
188
|
+
// 1. 标记设备为离线
|
|
189
|
+
const device = this.devices.get(clientId);
|
|
190
|
+
if (device) {
|
|
191
|
+
device.status = 'offline';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 2. 通知 Gateway 设备离线
|
|
195
|
+
if (this.gatewayConnected) {
|
|
196
|
+
this.notifyGatewayDeviceOffline(clientId);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 3. 清理连接
|
|
200
|
+
this.cleanupDevice(clientId);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## 完整通信流程
|
|
206
|
+
|
|
207
|
+
### 1. 启动时序
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
设备端启动 → 连接 Edge → 注册 → 开始心跳
|
|
211
|
+
↓
|
|
212
|
+
Edge 记录设备
|
|
213
|
+
↓
|
|
214
|
+
Edge 连接 Gateway
|
|
215
|
+
↓
|
|
216
|
+
Edge 批量注册所有设备到 Gateway
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 2. 命令执行流程
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
Backend → Gateway → Edge → Device
|
|
223
|
+
↑ ↓
|
|
224
|
+
└───────────────────────────┘
|
|
225
|
+
(响应返回路径)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 3. 状态同步
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// Edge 定期同步设备状态到 Gateway
|
|
232
|
+
class EdgeStatusReporter {
|
|
233
|
+
reportDeviceStatus() {
|
|
234
|
+
const statusReport = {
|
|
235
|
+
type: 'edge_status_report',
|
|
236
|
+
edgeId: this.edgeId,
|
|
237
|
+
devices: Array.from(this.devices.values()).map(device => ({
|
|
238
|
+
clientId: device.clientId,
|
|
239
|
+
status: device.status,
|
|
240
|
+
lastHeartbeat: device.lastHeartbeat,
|
|
241
|
+
clientInfo: device.clientInfo
|
|
242
|
+
})),
|
|
243
|
+
timestamp: new Date().toISOString()
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
this.gatewayWs.send(JSON.stringify(statusReport));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 错误处理
|
|
252
|
+
|
|
253
|
+
### 1. 设备断线重连
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// 设备端重连逻辑
|
|
257
|
+
class ResilientDeviceClient {
|
|
258
|
+
private reconnectAttempts = 0;
|
|
259
|
+
private maxReconnectAttempts = 10;
|
|
260
|
+
|
|
261
|
+
connect() {
|
|
262
|
+
this.ws = new WebSocket(this.edgeUrl);
|
|
263
|
+
|
|
264
|
+
this.ws.on('close', () => {
|
|
265
|
+
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
266
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
267
|
+
setTimeout(() => {
|
|
268
|
+
this.reconnectAttempts++;
|
|
269
|
+
this.connect();
|
|
270
|
+
}, delay);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
this.ws.on('open', () => {
|
|
275
|
+
this.reconnectAttempts = 0;
|
|
276
|
+
this.register();
|
|
277
|
+
this.startHeartbeat();
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 2. Edge 处理设备异常
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
class EdgeDeviceManager {
|
|
287
|
+
handleDeviceError(clientId: string, error: any) {
|
|
288
|
+
console.error(`Device ${clientId} error:`, error);
|
|
289
|
+
|
|
290
|
+
const device = this.devices.get(clientId);
|
|
291
|
+
if (device) {
|
|
292
|
+
// 发送错误消息给设备
|
|
293
|
+
const errorMsg = {
|
|
294
|
+
type: 'error',
|
|
295
|
+
code: 'DEVICE_ERROR',
|
|
296
|
+
message: error.message,
|
|
297
|
+
retryable: true
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
device.ws.send(JSON.stringify(errorMsg));
|
|
302
|
+
} catch (e) {
|
|
303
|
+
// WebSocket 可能已关闭
|
|
304
|
+
this.cleanupDevice(clientId);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## 性能优化
|
|
312
|
+
|
|
313
|
+
### 1. 连接池管理
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
class EdgeConnectionPool {
|
|
317
|
+
private readonly MAX_CONNECTIONS = 1000;
|
|
318
|
+
private readonly CONNECTION_TIMEOUT = 300000; // 5分钟
|
|
319
|
+
|
|
320
|
+
acceptConnection(ws: WebSocket): boolean {
|
|
321
|
+
if (this.devices.size >= this.MAX_CONNECTIONS) {
|
|
322
|
+
// 拒绝新连接
|
|
323
|
+
ws.close(1008, 'Connection limit exceeded');
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 清理超时未注册的连接
|
|
328
|
+
this.cleanupStaleConnections();
|
|
329
|
+
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 2. 消息批处理
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
class EdgeMessageBatcher {
|
|
339
|
+
private messageQueue = new Map<string, any[]>();
|
|
340
|
+
private batchInterval = 100; // 100ms
|
|
341
|
+
|
|
342
|
+
queueMessage(clientId: string, message: any) {
|
|
343
|
+
if (!this.messageQueue.has(clientId)) {
|
|
344
|
+
this.messageQueue.set(clientId, []);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
this.messageQueue.get(clientId)!.push(message);
|
|
348
|
+
|
|
349
|
+
// 调度批量发送
|
|
350
|
+
this.scheduleBatch(clientId);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private scheduleBatch(clientId: string) {
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
const messages = this.messageQueue.get(clientId);
|
|
356
|
+
if (messages && messages.length > 0) {
|
|
357
|
+
const batchMessage = {
|
|
358
|
+
type: 'batch',
|
|
359
|
+
messages: messages
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const device = this.devices.get(clientId);
|
|
363
|
+
if (device) {
|
|
364
|
+
device.ws.send(JSON.stringify(batchMessage));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
this.messageQueue.delete(clientId);
|
|
368
|
+
}
|
|
369
|
+
}, this.batchInterval);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## 安全考虑
|
|
375
|
+
|
|
376
|
+
### 1. 设备认证
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
// Edge 可以要求设备提供认证信息
|
|
380
|
+
interface AuthenticatedRegisterMessage extends RegisterMessage {
|
|
381
|
+
auth?: {
|
|
382
|
+
token?: string;
|
|
383
|
+
apiKey?: string;
|
|
384
|
+
certificate?: string;
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
class SecureEdgeHandler {
|
|
389
|
+
validateDevice(message: AuthenticatedRegisterMessage): boolean {
|
|
390
|
+
if (!message.auth) {
|
|
391
|
+
return false; // 需要认证
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// 验证 token 或 API key
|
|
395
|
+
return this.authService.validate(message.auth);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 2. 消息加密
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// 支持加密通信
|
|
404
|
+
class EncryptedDeviceClient {
|
|
405
|
+
sendSecureMessage(message: any) {
|
|
406
|
+
const encrypted = this.encrypt(JSON.stringify(message));
|
|
407
|
+
this.ws.send(encrypted);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
handleEncryptedMessage(data: any) {
|
|
411
|
+
const decrypted = this.decrypt(data);
|
|
412
|
+
const message = JSON.parse(decrypted);
|
|
413
|
+
this.handleMessage(message);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## 总结
|
|
419
|
+
|
|
420
|
+
设备端到 Edge 的通信使用标准 WebSocket 协议,包括:
|
|
421
|
+
|
|
422
|
+
1. **注册机制** - 设备向 Edge 注册自己的信息和能力
|
|
423
|
+
2. **心跳保活** - 30秒心跳间隔,90秒超时断开
|
|
424
|
+
3. **命令执行** - Edge 转发来自 Gateway 的命令
|
|
425
|
+
4. **状态同步** - Edge 汇总设备状态上报给 Gateway
|
|
426
|
+
5. **错误处理** - 支持断线重连和异常恢复
|
|
427
|
+
6. **性能优化** - 连接池管理和消息批处理
|
|
428
|
+
7. **安全特性** - 可选的认证和加密支持
|
|
429
|
+
|
|
430
|
+
这种设计确保了设备端与系统其他部分的无缝集成,同时保持了协议的一致性和可扩展性。
|