@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.
Files changed (78) hide show
  1. package/ACK_MESSAGES_IMPLEMENTATION_SUMMARY.md +128 -0
  2. package/ACK_MESSAGE_DESIGN.md +457 -0
  3. package/CHANGELOG.md +58 -0
  4. package/COMMAND_VALIDATION_RULES.md +178 -0
  5. package/DOCUMENTATION_REORGANIZATION_SUMMARY.md +81 -0
  6. package/DOCUMENTATION_STRUCTURE.md +106 -0
  7. package/GATEWAY_MIGRATION_GUIDE.md +130 -0
  8. package/GATEWAY_PROTOCOL_COMPARISON.md +216 -0
  9. package/INTEGRATION_GUIDE.md +190 -0
  10. package/OPTIONAL_FIELDS_WITHOUT_DEFAULTS.md +97 -0
  11. package/PROTOCOL_UTILS_USAGE.md +278 -0
  12. package/README.md +237 -0
  13. package/TYPE_FIXES_SUMMARY.md +210 -0
  14. package/UPDATE_ENUM_VALUES.md +139 -0
  15. package/dist/asyncapi-sync.d.ts +47 -0
  16. package/dist/asyncapi-sync.d.ts.map +1 -0
  17. package/dist/asyncapi-sync.js +85 -0
  18. package/dist/asyncapi-sync.js.map +1 -0
  19. package/dist/command-factory.d.ts +62 -0
  20. package/dist/command-factory.d.ts.map +1 -0
  21. package/dist/command-factory.js +137 -0
  22. package/dist/command-factory.js.map +1 -0
  23. package/dist/command-types.d.ts +27 -0
  24. package/dist/command-types.d.ts.map +1 -0
  25. package/dist/command-types.js +31 -0
  26. package/dist/command-types.js.map +1 -0
  27. package/dist/index.d.ts +403 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +413 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/message-validator.d.ts +102 -0
  32. package/dist/message-validator.d.ts.map +1 -0
  33. package/dist/message-validator.js +640 -0
  34. package/dist/message-validator.js.map +1 -0
  35. package/dist/protocol-utils.d.ts +108 -0
  36. package/dist/protocol-utils.d.ts.map +1 -0
  37. package/dist/protocol-utils.js +293 -0
  38. package/dist/protocol-utils.js.map +1 -0
  39. package/docs/01-protocol/README.md +45 -0
  40. package/docs/01-protocol/design-rationale.md +198 -0
  41. package/docs/01-protocol/message-types.md +669 -0
  42. package/docs/01-protocol/specification.md +1466 -0
  43. package/docs/02-commands/README.md +56 -0
  44. package/docs/02-commands/batch-command.md +435 -0
  45. package/docs/02-commands/complex-command.md +537 -0
  46. package/docs/02-commands/simple-command.md +332 -0
  47. package/docs/02-commands/typed-commands.md +362 -0
  48. package/docs/03-architecture/README.md +66 -0
  49. package/docs/03-architecture/device-protocol.md +430 -0
  50. package/docs/03-architecture/edge-proxy.md +727 -0
  51. package/docs/03-architecture/routing-flow.md +893 -0
  52. package/docs/04-integration/README.md +144 -0
  53. package/docs/04-integration/backend-guide.md +551 -0
  54. package/docs/04-integration/edge-guide.md +684 -0
  55. package/docs/04-integration/gateway-guide.md +180 -0
  56. package/docs/04-integration/migration-guide.md +226 -0
  57. package/docs/05-examples/README.md +141 -0
  58. package/docs/05-examples/progress-update-examples.md +757 -0
  59. package/docs/06-reference/README.md +67 -0
  60. package/docs/06-reference/api.md +572 -0
  61. package/docs/06-reference/faq.md +302 -0
  62. package/docs/06-reference/glossary.md +232 -0
  63. package/examples/backend-upgrade.ts +279 -0
  64. package/examples/edge-multi-device.ts +513 -0
  65. package/examples/gateway-upgrade.ts +150 -0
  66. package/examples/protocol-implementation.ts +715 -0
  67. package/package.json +48 -0
  68. package/scripts/validate-asyncapi.ts +78 -0
  69. package/src/__tests__/protocol.test.ts +297 -0
  70. package/src/asyncapi-sync.ts +84 -0
  71. package/src/command-factory.ts +183 -0
  72. package/src/command-types.ts +72 -0
  73. package/src/edge-proxy.ts +494 -0
  74. package/src/gateway-extensions.ts +278 -0
  75. package/src/index.ts +792 -0
  76. package/src/message-validator.ts +726 -0
  77. package/src/protocol-utils.ts +355 -0
  78. package/tsconfig.json +24 -0
@@ -0,0 +1,144 @@
1
+ # 集成指南
2
+
3
+ 本目录包含将 JRSoft Subway 协议集成到各个组件的详细指南。
4
+
5
+ ## 📚 文档列表
6
+
7
+ ### [Gateway 集成指南](./gateway-guide.md)
8
+ Gateway 组件的集成说明:
9
+ - WebSocket 服务器配置
10
+ - 客户端连接管理
11
+ - 消息路由实现
12
+ - 性能优化建议
13
+
14
+ ### [Backend 集成指南](./backend-guide.md)
15
+ Backend 服务的集成说明:
16
+ - FastAPI WebSocket 客户端
17
+ - 命令发送和响应处理
18
+ - 异步任务管理
19
+ - 错误处理策略
20
+
21
+ ### [Edge 集成指南](./edge-guide.md)
22
+ Edge 节点的集成说明:
23
+ - 双向代理实现
24
+ - 设备连接管理
25
+ - 消息转发逻辑
26
+ - 故障恢复机制
27
+
28
+ ### [迁移指南](./migration-guide.md)
29
+ 从旧系统迁移到新协议:
30
+ - 版本兼容性说明
31
+ - 迁移步骤详解
32
+ - 常见问题解决
33
+ - 回滚方案
34
+
35
+ ## 🚀 快速开始
36
+
37
+ 根据您的角色选择相应的指南:
38
+
39
+ - **Gateway 开发者** → 阅读 [Gateway 集成指南](./gateway-guide.md)
40
+ - **Backend 开发者** → 阅读 [Backend 集成指南](./backend-guide.md)
41
+ - **Edge 开发者** → 阅读 [Edge 集成指南](./edge-guide.md)
42
+ - **系统升级** → 阅读 [迁移指南](./migration-guide.md)
43
+
44
+ ## 🏗️ 集成架构
45
+
46
+ ```
47
+ ┌─────────────┐
48
+ │ Backend │ ← HTTP/WebSocket 混合模式
49
+ └──────┬──────┘
50
+
51
+ ┌──────▼──────┐
52
+ │ Gateway │ ← 中心路由节点
53
+ └──────┬──────┘
54
+
55
+ ┌──────▼──────┐
56
+ │ Edge │ ← 设备代理节点
57
+ └──────┬──────┘
58
+
59
+ ┌──────▼──────┐
60
+ │ Device │ ← 终端设备
61
+ └─────────────┘
62
+ ```
63
+
64
+ ## 🔑 核心集成要点
65
+
66
+ ### 1. 协议版本
67
+ - 当前版本:1.0
68
+ - 所有组件必须使用相同版本
69
+ - 版本信息在每个消息中携带
70
+
71
+ ### 2. 连接管理
72
+ - 自动重连机制
73
+ - 心跳保活(30秒间隔)
74
+ - 优雅断开处理
75
+
76
+ ### 3. 消息验证
77
+ - 使用提供的验证函数
78
+ - 严格的类型检查
79
+ - 详细的错误信息
80
+
81
+ ### 4. 性能考虑
82
+ - 消息批处理
83
+ - 连接池复用
84
+ - 异步处理模式
85
+
86
+ ## 📋 集成检查清单
87
+
88
+ ### Gateway
89
+ - [ ] WebSocket 服务器启动(端口 18081)
90
+ - [ ] 客户端注册处理
91
+ - [ ] 消息路由实现
92
+ - [ ] 心跳机制
93
+ - [ ] 错误处理
94
+
95
+ ### Backend
96
+ - [ ] WebSocket 客户端连接
97
+ - [ ] 命令发送接口
98
+ - [ ] 响应处理逻辑
99
+ - [ ] 超时处理
100
+ - [ ] 日志记录
101
+
102
+ ### Edge
103
+ - [ ] Gateway 连接管理
104
+ - [ ] 设备连接接受
105
+ - [ ] 双向消息转发
106
+ - [ ] 设备状态跟踪
107
+ - [ ] 故障恢复
108
+
109
+ ## 🛠️ 调试工具
110
+
111
+ ### 消息调试
112
+ ```bash
113
+ # 监听 WebSocket 消息
114
+ wscat -c ws://localhost:18081
115
+
116
+ # 发送测试消息
117
+ echo '{"type":"heartbeat","clientId":"test","timestamp":"2024-01-20T10:00:00Z","version":"1.0"}' | wscat -c ws://localhost:18081
118
+ ```
119
+
120
+ ### 日志查看
121
+ ```bash
122
+ # Gateway 日志
123
+ tail -f /var/log/jrsoft-gateway/gateway.log
124
+
125
+ # Backend 日志
126
+ tail -f /var/log/jrsoft-backend/backend.log
127
+ ```
128
+
129
+ ## 📊 监控指标
130
+
131
+ 建议监控的关键指标:
132
+
133
+ 1. **连接数** - 活跃的 WebSocket 连接数
134
+ 2. **消息吞吐** - 每秒处理的消息数
135
+ 3. **响应时间** - 命令响应的平均时间
136
+ 4. **错误率** - 失败消息的比例
137
+ 5. **重连次数** - 连接断开和重连的频率
138
+
139
+ ## 🔗 相关资源
140
+
141
+ - [协议规范](../01-protocol/) - 了解协议细节
142
+ - [命令系统](../02-commands/) - 了解命令类型
143
+ - [架构文档](../03-architecture/) - 了解系统架构
144
+ - [示例代码](../05-examples/) - 查看实现示例
@@ -0,0 +1,551 @@
1
+ # Backend 集成指南
2
+
3
+ 本文档说明如何在 Backend 服务中集成 JRSoft Subway WebSocket 协议。
4
+
5
+ ## 概述
6
+
7
+ Backend 作为业务逻辑层,通过 WebSocket 连接到 Gateway 发送命令并接收响应。Backend 使用 FastAPI 框架,支持异步操作。
8
+
9
+ ## 集成步骤
10
+
11
+ ### 1. 安装依赖
12
+
13
+ ```bash
14
+ pip install websockets aiohttp pydantic
15
+ ```
16
+
17
+ ### 2. WebSocket 客户端实现
18
+
19
+ ```python
20
+ import asyncio
21
+ import json
22
+ from typing import Dict, Optional, Callable
23
+ import websockets
24
+ from datetime import datetime
25
+
26
+ class GatewayClient:
27
+ def __init__(self, gateway_url: str, client_id: str):
28
+ self.gateway_url = gateway_url
29
+ self.client_id = client_id
30
+ self.websocket: Optional[websockets.WebSocketClientProtocol] = None
31
+ self.pending_requests: Dict[str, asyncio.Future] = {}
32
+ self.message_handlers: Dict[str, Callable] = {}
33
+
34
+ async def connect(self):
35
+ """连接到 Gateway"""
36
+ self.websocket = await websockets.connect(self.gateway_url)
37
+
38
+ # 发送注册消息
39
+ await self.register()
40
+
41
+ # 启动消息接收循环
42
+ asyncio.create_task(self.receive_messages())
43
+
44
+ # 启动心跳
45
+ asyncio.create_task(self.heartbeat_loop())
46
+
47
+ async def register(self):
48
+ """注册客户端"""
49
+ register_message = {
50
+ "type": "REGISTER",
51
+ "clientId": self.client_id,
52
+ "clientType": "BACKEND",
53
+ "clientInfo": {
54
+ "name": "JRSoft Backend Service",
55
+ "version": "1.0.0",
56
+ "capabilities": ["command", "query", "monitor"]
57
+ },
58
+ "timestamp": datetime.utcnow().isoformat() + "Z",
59
+ "version": "1.0"
60
+ }
61
+ await self.websocket.send(json.dumps(register_message))
62
+
63
+ async def heartbeat_loop(self):
64
+ """心跳循环"""
65
+ while True:
66
+ try:
67
+ if self.websocket and not self.websocket.closed:
68
+ heartbeat = {
69
+ "type": "HEARTBEAT",
70
+ "clientId": self.client_id,
71
+ "timestamp": datetime.utcnow().isoformat() + "Z",
72
+ "version": "1.0"
73
+ }
74
+ await self.websocket.send(json.dumps(heartbeat))
75
+ await asyncio.sleep(30) # 30秒心跳间隔
76
+ except Exception as e:
77
+ print(f"Heartbeat error: {e}")
78
+ await self.reconnect()
79
+
80
+ async def receive_messages(self):
81
+ """接收消息循环"""
82
+ try:
83
+ async for message in self.websocket:
84
+ data = json.loads(message)
85
+ await self.handle_message(data)
86
+ except websockets.exceptions.ConnectionClosed:
87
+ await self.reconnect()
88
+
89
+ async def handle_message(self, message: dict):
90
+ """处理接收到的消息"""
91
+ message_type = message.get("type")
92
+
93
+ if message_type == "Ucommand_response":
94
+ # 处理命令响应
95
+ request_ref = message.get("requestRef")
96
+ if request_ref in self.pending_requests:
97
+ future = self.pending_requests.pop(request_ref)
98
+ future.set_result(message)
99
+
100
+ elif message_type == "Uprogress_update":
101
+ # 处理进度更新
102
+ handler = self.message_handlers.get("Uprogress_update")
103
+ if handler:
104
+ await handler(message)
105
+
106
+ elif message_type == "Uerror":
107
+ # 处理错误消息
108
+ request_ref = message.get("requestRef")
109
+ if request_ref in self.pending_requests:
110
+ future = self.pending_requests.pop(request_ref)
111
+ future.set_exception(Exception(message.get("message")))
112
+ ```
113
+
114
+ ### 3. 命令发送接口
115
+
116
+ ```python
117
+ import uuid
118
+ from enum import Enum
119
+ from typing import Any, Dict, Optional
120
+
121
+ class Priority(Enum):
122
+ LOW = "low"
123
+ NORMAL = "Unormal"
124
+ HIGH = "Uhigh"
125
+ URGENT = "urgent"
126
+
127
+ class CommandSender:
128
+ def __init__(self, gateway_client: GatewayClient):
129
+ self.gateway_client = gateway_client
130
+
131
+ async def send_command(
132
+ self,
133
+ target_client_id: str,
134
+ command: Dict[str, Any],
135
+ priority: Priority = Priority.NORMAL,
136
+ timeout: int = 5000,
137
+ callback: Optional[str] = None
138
+ ) -> Dict[str, Any]:
139
+ """发送命令并等待响应"""
140
+ request_ref = f"cmd-{uuid.uuid4()}"
141
+
142
+ message = {
143
+ "type": "COMMAND",
144
+ "requestRef": request_ref,
145
+ "targetClientId": target_client_id,
146
+ "command": command,
147
+ "priority": priority.value,
148
+ "timeout": timeout,
149
+ "timestamp": datetime.utcnow().isoformat() + "Z",
150
+ "version": "1.0"
151
+ }
152
+
153
+ if callback:
154
+ message["callback"] = callback
155
+
156
+ # 创建 Future 用于等待响应
157
+ future = asyncio.Future()
158
+ self.gateway_client.pending_requests[request_ref] = future
159
+
160
+ # 发送命令
161
+ await self.gateway_client.websocket.send(json.dumps(message))
162
+
163
+ # 等待响应或超时
164
+ try:
165
+ response = await asyncio.wait_for(future, timeout=timeout/1000)
166
+ return response
167
+ except asyncio.TimeoutError:
168
+ self.gateway_client.pending_requests.pop(request_ref, None)
169
+ raise TimeoutError(f"Command timeout: {request_ref}")
170
+
171
+ async def send_simple_command(
172
+ self,
173
+ edge_id: str,
174
+ device_id: str,
175
+ command_code: str,
176
+ device_type: str,
177
+ operation_type: str,
178
+ parameters: Dict[str, Any],
179
+ **kwargs
180
+ ) -> Dict[str, Any]:
181
+ """发送 Simple 类型命令"""
182
+ command = {
183
+ "commandType": "SIMPLE",
184
+ "commandCode": command_code,
185
+ "deviceType": device_type,
186
+ "deviceId": device_id,
187
+ "operationType": operation_type,
188
+ "parameters": parameters
189
+ }
190
+
191
+ target_client_id = f"{edge_id}:{device_id}"
192
+ return await self.send_command(target_client_id, command, **kwargs)
193
+
194
+ async def send_batch_command(
195
+ self,
196
+ edge_id: str,
197
+ device_id: str,
198
+ command_code: str,
199
+ device_type: str,
200
+ targets: str,
201
+ operation_type: str,
202
+ parameters: Dict[str, Any],
203
+ **kwargs
204
+ ) -> Dict[str, Any]:
205
+ """发送 Batch 类型命令"""
206
+ command = {
207
+ "commandType": "BATCH",
208
+ "commandCode": command_code,
209
+ "deviceType": device_type,
210
+ "deviceId": targets, # 批量目标
211
+ "operationType": operation_type,
212
+ "parameters": {
213
+ **parameters,
214
+ "targets": targets,
215
+ "Ubatch": True
216
+ }
217
+ }
218
+
219
+ target_client_id = f"{edge_id}:{device_id}"
220
+ return await self.send_command(target_client_id, command, **kwargs)
221
+ ```
222
+
223
+ ### 4. FastAPI 集成
224
+
225
+ ```python
226
+ from fastapi import FastAPI, HTTPException, WebSocket
227
+ from contextlib import asynccontextmanager
228
+ import logging
229
+
230
+ # 配置日志
231
+ logging.basicConfig(level=logging.INFO)
232
+ logger = logging.getLogger(__name__)
233
+
234
+ # 全局 Gateway 客户端
235
+ gateway_client: Optional[GatewayClient] = None
236
+ command_sender: Optional[CommandSender] = None
237
+
238
+ @asynccontextmanager
239
+ async def lifespan(app: FastAPI):
240
+ """应用生命周期管理"""
241
+ global gateway_client, command_sender
242
+
243
+ # 启动时连接 Gateway
244
+ gateway_client = GatewayClient(
245
+ "ws://localhost:18081",
246
+ "backend-001"
247
+ )
248
+ await gateway_client.connect()
249
+ command_sender = CommandSender(gateway_client)
250
+ logger.info("Connected to Gateway")
251
+
252
+ yield
253
+
254
+ # 关闭时断开连接
255
+ if gateway_client.websocket:
256
+ await gateway_client.websocket.close()
257
+ logger.info("Disconnected from Gateway")
258
+
259
+ app = FastAPI(lifespan=lifespan)
260
+
261
+ # API 端点示例
262
+ @app.post("/api/devices/{edge_id}/{device_id}/control")
263
+ async def control_device(
264
+ edge_id: str,
265
+ device_id: str,
266
+ command_code: str,
267
+ parameters: Dict[str, Any]
268
+ ):
269
+ """控制设备的 API 端点"""
270
+ try:
271
+ response = await command_sender.send_simple_command(
272
+ edge_id=edge_id,
273
+ device_id=device_id,
274
+ command_code=command_code,
275
+ device_type="pillar",
276
+ operation_type="Uwrite",
277
+ parameters=parameters,
278
+ priority=Priority.NORMAL,
279
+ timeout=5000
280
+ )
281
+
282
+ if response.get("status") == "Ucompleted":
283
+ return {"success": True, "data": response.get("result")}
284
+ else:
285
+ raise HTTPException(status_code=500, detail="Command failed")
286
+
287
+ except TimeoutError:
288
+ raise HTTPException(status_code=504, detail="Command timeout")
289
+ except Exception as e:
290
+ raise HTTPException(status_code=500, detail=str(e))
291
+
292
+ @app.websocket("/ws/monitor/{request_id}")
293
+ async def monitor_progress(websocket: WebSocket, request_id: str):
294
+ """监控命令执行进度的 WebSocket 端点"""
295
+ await websocket.accept()
296
+
297
+ progress_updates = []
298
+
299
+ async def progress_handler(message):
300
+ if message.get("requestRef") == request_id:
301
+ progress_updates.append(message)
302
+ await websocket.send_json(message)
303
+
304
+ # 注册进度处理器
305
+ gateway_client.message_handlers["Uprogress_update"] = progress_handler
306
+
307
+ try:
308
+ # 保持连接直到客户端断开
309
+ while True:
310
+ await websocket.receive_text()
311
+ except:
312
+ pass
313
+ finally:
314
+ # 清理处理器
315
+ gateway_client.message_handlers.pop("Uprogress_update", None)
316
+ ```
317
+
318
+ ### 5. 异步任务处理
319
+
320
+ ```python
321
+ from typing import List
322
+ import asyncio
323
+
324
+ class BatchCommandExecutor:
325
+ """批量命令执行器"""
326
+
327
+ def __init__(self, command_sender: CommandSender):
328
+ self.command_sender = command_sender
329
+
330
+ async def execute_on_multiple_devices(
331
+ self,
332
+ devices: List[Dict[str, str]],
333
+ command_code: str,
334
+ parameters: Dict[str, Any]
335
+ ) -> List[Dict[str, Any]]:
336
+ """在多个设备上执行命令"""
337
+ tasks = []
338
+
339
+ for device in devices:
340
+ task = self.command_sender.send_simple_command(
341
+ edge_id=device["edge_id"],
342
+ device_id=device["device_id"],
343
+ command_code=command_code,
344
+ device_type=device["device_type"],
345
+ operation_type="Uwrite",
346
+ parameters=parameters
347
+ )
348
+ tasks.append(task)
349
+
350
+ # 并发执行所有命令
351
+ results = await asyncio.gather(*tasks, return_exceptions=True)
352
+
353
+ # 处理结果
354
+ processed_results = []
355
+ for i, result in enumerate(results):
356
+ if isinstance(result, Exception):
357
+ processed_results.append({
358
+ "Udevice": devices[i],
359
+ "success": False,
360
+ "Uerror": str(result)
361
+ })
362
+ else:
363
+ processed_results.append({
364
+ "Udevice": devices[i],
365
+ "success": True,
366
+ "result": result
367
+ })
368
+
369
+ return processed_results
370
+ ```
371
+
372
+ ### 6. 错误处理和重试
373
+
374
+ ```python
375
+ from typing import TypeVar, Callable
376
+ import asyncio
377
+
378
+ T = TypeVar('T')
379
+
380
+ async def retry_async(
381
+ func: Callable[..., T],
382
+ max_attempts: int = 3,
383
+ delay: float = 1.0,
384
+ backoff: float = 2.0,
385
+ exceptions: tuple = (Exception,)
386
+ ) -> T:
387
+ """异步重试装饰器"""
388
+ attempt = 0
389
+ current_delay = delay
390
+
391
+ while attempt < max_attempts:
392
+ try:
393
+ return await func()
394
+ except exceptions as e:
395
+ attempt += 1
396
+ if attempt >= max_attempts:
397
+ raise
398
+
399
+ logger.warning(f"Attempt {attempt} failed: {e}. Retrying in {current_delay}s...")
400
+ await asyncio.sleep(current_delay)
401
+ current_delay *= backoff
402
+
403
+ # 使用示例
404
+ async def send_command_with_retry(target_client_id: str, command: dict):
405
+ return await retry_async(
406
+ lambda: command_sender.send_command(target_client_id, command),
407
+ max_attempts=3,
408
+ delay=1.0,
409
+ exceptions=(TimeoutError, ConnectionError)
410
+ )
411
+ ```
412
+
413
+ ## 最佳实践
414
+
415
+ ### 1. 连接管理
416
+
417
+ ```python
418
+ class ConnectionManager:
419
+ """连接管理器,处理断线重连"""
420
+
421
+ def __init__(self, gateway_client: GatewayClient):
422
+ self.gateway_client = gateway_client
423
+ self.reconnect_delay = 5
424
+ self.max_reconnect_delay = 60
425
+
426
+ async def ensure_connected(self):
427
+ """确保连接可用"""
428
+ if not self.gateway_client.websocket or self.gateway_client.websocket.closed:
429
+ await self.reconnect()
430
+
431
+ async def reconnect(self):
432
+ """重连逻辑"""
433
+ delay = self.reconnect_delay
434
+
435
+ while True:
436
+ try:
437
+ logger.info(f"Attempting to reconnect...")
438
+ await self.gateway_client.connect()
439
+ logger.info("Reconnected successfully")
440
+ self.reconnect_delay = 5 # 重置延迟
441
+ break
442
+ except Exception as e:
443
+ logger.error(f"Reconnection failed: {e}")
444
+ await asyncio.sleep(delay)
445
+ delay = min(delay * 2, self.max_reconnect_delay)
446
+ ```
447
+
448
+ ### 2. 性能优化
449
+
450
+ ```python
451
+ # 使用连接池
452
+ class GatewayConnectionPool:
453
+ def __init__(self, size: int = 5):
454
+ self.connections: List[GatewayClient] = []
455
+ self.size = size
456
+ self.current = 0
457
+
458
+ async def initialize(self, gateway_url: str, client_id_prefix: str):
459
+ for i in range(self.size):
460
+ client = GatewayClient(
461
+ gateway_url,
462
+ f"{client_id_prefix}-{i}"
463
+ )
464
+ await client.connect()
465
+ self.connections.append(client)
466
+
467
+ def get_connection(self) -> GatewayClient:
468
+ """轮询获取连接"""
469
+ conn = self.connections[self.current]
470
+ self.current = (self.current + 1) % self.size
471
+ return conn
472
+ ```
473
+
474
+ ### 3. 监控和日志
475
+
476
+ ```python
477
+ import time
478
+ from prometheus_client import Counter, Histogram, Gauge
479
+
480
+ # Prometheus 指标
481
+ command_sent_total = Counter('backend_command_sent_total', 'Total commands sent')
482
+ command_duration = Histogram('backend_command_duration_seconds', 'Command execution duration')
483
+ active_connections = Gauge('backend_active_connections', 'Number of active Gateway connections')
484
+
485
+ class MonitoredCommandSender(CommandSender):
486
+ async def send_command(self, *args, **kwargs):
487
+ start_time = time.time()
488
+ command_sent_total.inc()
489
+
490
+ try:
491
+ result = await super().send_command(*args, **kwargs)
492
+ command_duration.observe(time.time() - start_time)
493
+ return result
494
+ except Exception as e:
495
+ command_duration.observe(time.time() - start_time)
496
+ raise
497
+ ```
498
+
499
+ ## 故障排查
500
+
501
+ ### 常见问题
502
+
503
+ 1. **连接失败**
504
+ ```python
505
+ # 检查 Gateway 是否运行
506
+ # 检查网络连接
507
+ # 验证 WebSocket URL
508
+ ```
509
+
510
+ 2. **命令超时**
511
+ ```python
512
+ # 增加超时时间
513
+ # 检查目标设备是否在线
514
+ # 查看 Gateway 日志
515
+ ```
516
+
517
+ 3. **消息格式错误**
518
+ ```python
519
+ # 使用协议提供的验证函数
520
+ # 检查必填字段
521
+ # 验证数据类型
522
+ ```
523
+
524
+ ### 调试技巧
525
+
526
+ ```python
527
+ # 启用详细日志
528
+ logging.getLogger("websockets").setLevel(logging.DEBUG)
529
+
530
+ # 消息追踪
531
+ class DebugGatewayClient(GatewayClient):
532
+ async def send(self, message: str):
533
+ logger.debug(f"Sending: {message}")
534
+ await super().send(message)
535
+
536
+ async def handle_message(self, message: dict):
537
+ logger.debug(f"Received: {message}")
538
+ await super().handle_message(message)
539
+ ```
540
+
541
+ ## 总结
542
+
543
+ Backend 集成的关键点:
544
+
545
+ 1. **异步架构** - 充分利用 Python 的异步特性
546
+ 2. **错误处理** - 完善的错误处理和重试机制
547
+ 3. **性能优化** - 连接池、批量处理、并发控制
548
+ 4. **监控日志** - 完整的监控指标和日志记录
549
+ 5. **容错设计** - 自动重连、优雅降级
550
+
551
+ 遵循这些指南,可以构建一个稳定、高效的 Backend 服务。