@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,715 @@
1
+ /**
2
+ * 完整的协议实现示例
3
+ * 展示如何基于规范实现 Gateway 核心功能
4
+ */
5
+
6
+ import {
7
+ MessageFactory,
8
+ MessageValidator,
9
+ MessageType,
10
+ ClientType,
11
+ Priority,
12
+ CommandType,
13
+ isCommandMessage,
14
+ isRegisterMessage,
15
+ CommandMessage,
16
+ RegisterMessage,
17
+ GatewayUtils,
18
+ GATEWAY_SITE_ID,
19
+ DeviceInfo,
20
+ CommandChainItem
21
+ } from '../src';
22
+
23
+ /**
24
+ * Gateway 核心实现
25
+ */
26
+ export class GatewayCore {
27
+ // 客户端连接管理
28
+ private clients = new Map<string, {
29
+ ws: any;
30
+ info: DeviceInfo;
31
+ capabilities: Set<string>;
32
+ }>();
33
+
34
+ // 命令路由表
35
+ private commandRoutes = new Map<string, string[]>();
36
+
37
+ // 待处理命令队列(按优先级)
38
+ private commandQueues = {
39
+ critical: [] as CommandMessage[],
40
+ high: [] as CommandMessage[],
41
+ normal: [] as CommandMessage[],
42
+ low: [] as CommandMessage[]
43
+ };
44
+
45
+ /**
46
+ * 处理客户端消息
47
+ */
48
+ async handleMessage(clientSiteId: string, rawMessage: string) {
49
+ try {
50
+ const message = JSON.parse(rawMessage);
51
+
52
+ // 验证基本消息格式
53
+ if (!MessageValidator.validateMessage(message)) {
54
+ this.sendError(clientSiteId, 'PROTOCOL_INVALID', 'Invalid message format');
55
+ return;
56
+ }
57
+
58
+ // 根据消息类型分发处理
59
+ switch (message.type) {
60
+ case 'register':
61
+ await this.handleRegister(clientSiteId, message);
62
+ break;
63
+
64
+ case 'command':
65
+ await this.handleCommand(clientSiteId, message);
66
+ break;
67
+
68
+ case 'command_response':
69
+ await this.handleCommandResponse(clientSiteId, message);
70
+ break;
71
+
72
+ case 'heartbeat':
73
+ await this.handleHeartbeat(clientSiteId, message);
74
+ break;
75
+
76
+ case 'batch':
77
+ await this.handleBatch(clientSiteId, message);
78
+ break;
79
+
80
+ default:
81
+ this.sendError(clientSiteId, 'PROTOCOL_INVALID', `Unknown message type: ${message.type}`);
82
+ }
83
+
84
+ } catch (error) {
85
+ console.error('Error handling message:', error);
86
+ this.sendError(clientSiteId, 'SYSTEM_ERROR', 'Internal error processing message');
87
+ }
88
+ }
89
+
90
+ /**
91
+ * 处理注册
92
+ */
93
+ private async handleRegister(clientSiteId: string, message: RegisterMessage) {
94
+ const client = this.clients.get(clientSiteId);
95
+ if (!client) return;
96
+
97
+ // 更新客户端信息
98
+ client.info = {
99
+ clientId: message.clientId,
100
+ clientType: message.clientType || 'device',
101
+ status: 'online',
102
+ connectedAt: new Date().toISOString(),
103
+ lastHeartbeat: new Date().toISOString(),
104
+ clientInfo: message.clientInfo
105
+ };
106
+
107
+ // 记录客户端能力
108
+ if (message.clientInfo?.capabilities) {
109
+ client.capabilities = new Set(message.clientInfo.capabilities);
110
+ }
111
+
112
+ // 如果是新协议,记录支持的特性
113
+ const extendedMsg = message as any;
114
+ if (extendedMsg.features) {
115
+ extendedMsg.features.forEach((feature: string) => {
116
+ client.capabilities.add(feature);
117
+ });
118
+ }
119
+
120
+ // 更新路由表
121
+ this.updateRoutes(message.clientId, clientSiteId);
122
+
123
+ // 发送确认
124
+ const ack = {
125
+ type: 'register_ack',
126
+ clientId: message.clientId,
127
+ success: true,
128
+ sessionId: this.generateSessionId(),
129
+ timestamp: new Date().toISOString(),
130
+ version: '1.0',
131
+ // 告知客户端 Gateway 支持的特性
132
+ features: ['batch', 'chain', 'filter', 'compression']
133
+ };
134
+
135
+ this.sendToClient(clientSiteId, ack);
136
+
137
+ console.log(`Client registered: ${message.clientId} (${message.clientType || 'device'})`);
138
+ }
139
+
140
+ /**
141
+ * 处理命令
142
+ */
143
+ private async handleCommand(clientSiteId: string, message: CommandMessage) {
144
+ // 验证命令消息
145
+ if (!MessageValidator.validateCommandMessage(message)) {
146
+ this.sendError(clientSiteId, 'COMMAND_INVALID', 'Invalid command format');
147
+ return;
148
+ }
149
+
150
+ // 检查是否为 Gateway 命令
151
+ if (GatewayUtils.isGatewayCommand(message)) {
152
+ await this.handleGatewayCommand(clientSiteId, message);
153
+ return;
154
+ }
155
+
156
+ // 检查是否为广播命令
157
+ if (GatewayUtils.isBroadcastCommand(message)) {
158
+ await this.handleBroadcastCommand(clientSiteId, message as ExtendedCommandMessage);
159
+ return;
160
+ }
161
+
162
+ // 检查是否为命令链
163
+ const extendedCmd = message as ExtendedCommandMessage;
164
+ if (extendedCmd.commandChain) {
165
+ await this.handleCommandChain(clientSiteId, extendedCmd);
166
+ return;
167
+ }
168
+
169
+ // 普通点对点命令
170
+ await this.routeCommand(clientSiteId, message);
171
+ }
172
+
173
+ /**
174
+ * 处理 Gateway 命令
175
+ */
176
+ private async handleGatewayCommand(clientSiteId: string, message: CommandMessage) {
177
+ const { command } = message;
178
+
179
+ switch (command.commandCode) {
180
+ case 'GET_DEVICE_STATUS':
181
+ const targetSiteId = command.parameters.targetSiteId;
182
+ const deviceInfo = this.getDeviceInfo(targetSiteId);
183
+
184
+ this.sendCommandResponse(clientSiteId, message.requestRef, {
185
+ success: !!deviceInfo,
186
+ data: deviceInfo || { error: 'Device not found' }
187
+ });
188
+ break;
189
+
190
+ case 'LIST_DEVICES':
191
+ const filter = command.parameters.filter as DeviceFilter;
192
+ const devices = this.listDevices(filter);
193
+
194
+ this.sendCommandResponse(clientSiteId, message.requestRef, {
195
+ success: true,
196
+ data: { devices, count: devices.length }
197
+ });
198
+ break;
199
+
200
+ case 'GET_STATS':
201
+ const stats = this.getGatewayStats();
202
+
203
+ this.sendCommandResponse(clientSiteId, message.requestRef, {
204
+ success: true,
205
+ data: stats
206
+ });
207
+ break;
208
+
209
+ default:
210
+ this.sendCommandResponse(clientSiteId, message.requestRef, {
211
+ success: false,
212
+ message: `Unknown Gateway command: ${command.commandCode}`
213
+ });
214
+ }
215
+ }
216
+
217
+ /**
218
+ * 处理广播命令
219
+ */
220
+ private async handleBroadcastCommand(clientSiteId: string, message: ExtendedCommandMessage) {
221
+ const filter = message.filter || {};
222
+ const targets = this.findDevicesByFilter(filter);
223
+
224
+ if (targets.length === 0) {
225
+ this.sendCommandResponse(clientSiteId, message.requestRef, {
226
+ success: false,
227
+ message: 'No devices match the filter criteria'
228
+ });
229
+ return;
230
+ }
231
+
232
+ console.log(`Broadcasting command to ${targets.length} devices`);
233
+
234
+ // 为每个目标创建单独的命令
235
+ const results: any[] = [];
236
+ for (const target of targets) {
237
+ const targetCommand = {
238
+ ...message,
239
+ clientId: target.clientId,
240
+ requestRef: `${message.requestRef}-${target.clientId}`
241
+ };
242
+
243
+ // 发送命令并收集结果
244
+ try {
245
+ await this.routeCommand(clientSiteId, targetCommand);
246
+ results.push({ clientId: target.clientId, status: 'sent' });
247
+ } catch (error) {
248
+ results.push({ clientId: target.clientId, status: 'failed', error: error.message });
249
+ }
250
+ }
251
+
252
+ // 返回批量执行结果
253
+ this.sendCommandResponse(clientSiteId, message.requestRef, {
254
+ success: true,
255
+ data: {
256
+ totalTargets: targets.length,
257
+ results
258
+ }
259
+ });
260
+ }
261
+
262
+ /**
263
+ * 处理命令链
264
+ */
265
+ private async handleCommandChain(clientSiteId: string, message: ExtendedCommandMessage) {
266
+ const { clientId, commandChain, requestRef } = message;
267
+
268
+ if (!commandChain || commandChain.length === 0) {
269
+ this.sendCommandResponse(clientSiteId, requestRef, {
270
+ success: false,
271
+ message: 'Command chain is empty'
272
+ });
273
+ return;
274
+ }
275
+
276
+ console.log(`Executing command chain with ${commandChain.length} commands for ${clientId}`);
277
+
278
+ // 顺序执行命令链
279
+ const results: any[] = [];
280
+ let chainSuccess = true;
281
+
282
+ for (let i = 0; i < commandChain.length; i++) {
283
+ const chainItem = commandChain[i];
284
+
285
+ // 如果有延迟,等待
286
+ if (chainItem.delay && chainItem.delay > 0) {
287
+ await this.delay(chainItem.delay);
288
+ }
289
+
290
+ // 创建单个命令
291
+ const command = MessageFactory.createCommandMessage(
292
+ `${requestRef}-step-${i}`,
293
+ clientId,
294
+ {
295
+ commandCode: chainItem.commandCode,
296
+ deviceType: message.command.deviceType,
297
+ operationType: 'write',
298
+ parameters: chainItem.parameters
299
+ },
300
+ {
301
+ priority: message.priority as Priority,
302
+ timeout: 10000
303
+ }
304
+ );
305
+
306
+ try {
307
+ // 发送命令
308
+ await this.routeCommand(clientSiteId, command);
309
+ results.push({
310
+ step: i,
311
+ commandCode: chainItem.commandCode,
312
+ status: 'completed'
313
+ });
314
+ } catch (error) {
315
+ results.push({
316
+ step: i,
317
+ commandCode: chainItem.commandCode,
318
+ status: 'failed',
319
+ error: error.message
320
+ });
321
+ chainSuccess = false;
322
+ break; // 链中某个命令失败,停止执行
323
+ }
324
+ }
325
+
326
+ // 返回链执行结果
327
+ this.sendCommandResponse(clientSiteId, requestRef, {
328
+ success: chainSuccess,
329
+ data: {
330
+ totalSteps: commandChain.length,
331
+ executedSteps: results.length,
332
+ results
333
+ }
334
+ });
335
+ }
336
+
337
+ /**
338
+ * 路由命令到目标设备
339
+ */
340
+ private async routeCommand(fromSiteId: string, message: CommandMessage) {
341
+ const targetClient = this.findClientBySiteId(message.clientId);
342
+
343
+ if (!targetClient) {
344
+ this.sendCommandResponse(fromSiteId, message.requestRef, {
345
+ success: false,
346
+ message: `Device ${message.clientId} is offline or not found`
347
+ });
348
+ return;
349
+ }
350
+
351
+ // 检查权限
352
+ if (!this.checkCommandPermission(fromSiteId, message.clientId)) {
353
+ this.sendCommandResponse(fromSiteId, message.requestRef, {
354
+ success: false,
355
+ message: 'Permission denied'
356
+ });
357
+ return;
358
+ }
359
+
360
+ // 根据优先级加入队列
361
+ const priority = message.priority || 'normal';
362
+ this.commandQueues[priority].push(message);
363
+
364
+ // 立即处理高优先级命令
365
+ if (priority === 'critical' || priority === 'high') {
366
+ await this.processCommandQueue(priority);
367
+ }
368
+ }
369
+
370
+ /**
371
+ * 处理命令队列
372
+ */
373
+ private async processCommandQueue(priority: string) {
374
+ const queue = this.commandQueues[priority as keyof typeof this.commandQueues];
375
+
376
+ while (queue.length > 0) {
377
+ const command = queue.shift();
378
+ if (!command) continue;
379
+
380
+ const targetClient = this.findClientBySiteId(command.clientId);
381
+ if (!targetClient) continue;
382
+
383
+ // 直接转发命令
384
+ this.sendToClient(targetClient.info.clientId, command);
385
+
386
+ // 设置超时处理
387
+ setTimeout(() => {
388
+ // 检查是否收到响应
389
+ // 如果没有,发送超时错误
390
+ }, command.timeout || 10000);
391
+ }
392
+ }
393
+
394
+ /**
395
+ * 处理心跳
396
+ */
397
+ private async handleHeartbeat(clientSiteId: string, message: any) {
398
+ const client = this.clients.get(clientSiteId);
399
+ if (!client) return;
400
+
401
+ // 更新最后心跳时间
402
+ client.info.lastHeartbeat = new Date().toISOString();
403
+
404
+ // 发送心跳确认
405
+ const ack = {
406
+ type: 'heartbeat_ack',
407
+ clientId: message.clientId,
408
+ sequence: message.sequence,
409
+ timestamp: new Date().toISOString()
410
+ };
411
+
412
+ this.sendToClient(clientSiteId, ack);
413
+ }
414
+
415
+ /**
416
+ * 处理批量消息
417
+ */
418
+ private async handleBatch(clientSiteId: string, message: any) {
419
+ if (!message.messages || !Array.isArray(message.messages)) {
420
+ this.sendError(clientSiteId, 'PROTOCOL_INVALID', 'Invalid batch message');
421
+ return;
422
+ }
423
+
424
+ console.log(`Processing batch of ${message.messages.length} messages`);
425
+
426
+ // 并行处理批量消息
427
+ const results = await Promise.allSettled(
428
+ message.messages.map(msg => this.handleMessage(clientSiteId, JSON.stringify(msg)))
429
+ );
430
+
431
+ // 返回批量处理结果
432
+ const response = {
433
+ type: 'batch_response',
434
+ results: results.map((result, index) => ({
435
+ index,
436
+ success: result.status === 'fulfilled',
437
+ error: result.status === 'rejected' ? result.reason : undefined
438
+ }))
439
+ };
440
+
441
+ this.sendToClient(clientSiteId, response);
442
+ }
443
+
444
+ // === 辅助方法 ===
445
+
446
+ /**
447
+ * 获取设备信息
448
+ */
449
+ private getDeviceInfo(clientId: string): DeviceInfo | null {
450
+ for (const [_, client] of this.clients) {
451
+ if (client.info.clientId === clientId) {
452
+ return {
453
+ ...client.info,
454
+ statistics: {
455
+ commandsSent: 0,
456
+ commandsReceived: 0,
457
+ errors: 0,
458
+ uptime: Date.now() - new Date(client.info.connectedAt).getTime()
459
+ }
460
+ };
461
+ }
462
+ }
463
+ return null;
464
+ }
465
+
466
+ /**
467
+ * 列出设备
468
+ */
469
+ private listDevices(filter?: DeviceFilter): DeviceInfo[] {
470
+ const devices: DeviceInfo[] = [];
471
+
472
+ for (const [_, client] of this.clients) {
473
+ const info = client.info;
474
+
475
+ // 应用过滤器
476
+ if (filter) {
477
+ if (filter.status && info.status !== filter.status) continue;
478
+ if (filter.clientType && info.clientType !== filter.clientType) continue;
479
+ if (filter.deviceType && info.clientInfo?.deviceType !== filter.deviceType) continue;
480
+ if (filter.tags && filter.tags.length > 0) {
481
+ const clientTags = info.clientInfo?.tags || [];
482
+ if (!filter.tags.every(tag => clientTags.includes(tag))) continue;
483
+ }
484
+ }
485
+
486
+ devices.push(info);
487
+ }
488
+
489
+ return devices;
490
+ }
491
+
492
+ /**
493
+ * 根据过滤器查找设备
494
+ */
495
+ private findDevicesByFilter(filter: DeviceFilter): DeviceInfo[] {
496
+ return this.listDevices(filter);
497
+ }
498
+
499
+ /**
500
+ * 根据 clientId 查找客户端
501
+ */
502
+ private findClientBySiteId(clientId: string) {
503
+ for (const [_, client] of this.clients) {
504
+ if (client.info.clientId === clientId) {
505
+ return client;
506
+ }
507
+ }
508
+ return null;
509
+ }
510
+
511
+ /**
512
+ * 检查命令权限
513
+ */
514
+ private checkCommandPermission(fromSiteId: string, toSiteId: string): boolean {
515
+ const fromClient = this.findClientBySiteId(fromSiteId);
516
+ if (!fromClient) return false;
517
+
518
+ // 基于客户端类型的权限控制
519
+ switch (fromClient.info.clientType) {
520
+ case 'backend':
521
+ case 'backend':
522
+ // 后端可以发送给任何 Edge 或其管理的设备
523
+ return true;
524
+
525
+ case 'edge':
526
+ // Edge 可以:
527
+ // 1. 回复来自 Backend 的命令
528
+ // 2. 上报其管理的设备状态
529
+ // 3. 不能主动向其他 Edge 或 Backend 发送命令
530
+ const toClient = this.findClientBySiteId(toSiteId);
531
+ return toClient?.info.clientType === 'backend';
532
+
533
+ case 'device':
534
+ // 设备不直接连接 Gateway
535
+ return false;
536
+
537
+ default:
538
+ return false;
539
+ }
540
+ }
541
+
542
+ /**
543
+ * 发送命令响应
544
+ */
545
+ private sendCommandResponse(clientSiteId: string, requestRef: string, result: any) {
546
+ const response = {
547
+ type: 'command_response',
548
+ requestRef,
549
+ status: result.success ? 'completed' : 'failed',
550
+ result,
551
+ timestamp: new Date().toISOString()
552
+ };
553
+
554
+ this.sendToClient(clientSiteId, response);
555
+ }
556
+
557
+ /**
558
+ * 发送错误消息
559
+ */
560
+ private sendError(clientSiteId: string, code: string, message: string) {
561
+ const error = {
562
+ type: 'error',
563
+ code,
564
+ message,
565
+ severity: 'medium',
566
+ timestamp: new Date().toISOString()
567
+ };
568
+
569
+ this.sendToClient(clientSiteId, error);
570
+ }
571
+
572
+ /**
573
+ * 发送消息到客户端
574
+ */
575
+ private sendToClient(clientSiteId: string, message: any) {
576
+ const client = this.clients.get(clientSiteId);
577
+ if (!client || !client.ws) return;
578
+
579
+ try {
580
+ client.ws.send(JSON.stringify(message));
581
+ } catch (error) {
582
+ console.error(`Error sending to client ${clientSiteId}:`, error);
583
+ }
584
+ }
585
+
586
+ /**
587
+ * 获取 Gateway 统计信息
588
+ */
589
+ private getGatewayStats() {
590
+ const clientsByType: Record<string, number> = {};
591
+ const clientsByStatus: Record<string, number> = {};
592
+
593
+ for (const [_, client] of this.clients) {
594
+ const type = client.info.clientType;
595
+ const status = client.info.status;
596
+
597
+ clientsByType[type] = (clientsByType[type] || 0) + 1;
598
+ clientsByStatus[status] = (clientsByStatus[status] || 0) + 1;
599
+ }
600
+
601
+ return {
602
+ uptime: process.uptime() * 1000,
603
+ connections: {
604
+ total: this.clients.size,
605
+ byType: clientsByType,
606
+ byStatus: clientsByStatus
607
+ },
608
+ messages: {
609
+ received: 0, // TODO: 实现计数器
610
+ sent: 0,
611
+ errors: 0,
612
+ byType: {}
613
+ },
614
+ performance: {
615
+ averageLatency: 0,
616
+ messageRate: 0,
617
+ errorRate: 0
618
+ }
619
+ };
620
+ }
621
+
622
+ /**
623
+ * 更新路由表
624
+ */
625
+ private updateRoutes(clientId: string, clientSiteId: string) {
626
+ this.commandRoutes.set(clientId, [clientSiteId]);
627
+ }
628
+
629
+ /**
630
+ * 生成会话 ID
631
+ */
632
+ private generateSessionId(): string {
633
+ return `sess-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
634
+ }
635
+
636
+ /**
637
+ * 延迟执行
638
+ */
639
+ private delay(ms: number): Promise<void> {
640
+ return new Promise(resolve => setTimeout(resolve, ms));
641
+ }
642
+
643
+ /**
644
+ * 处理命令响应
645
+ */
646
+ private async handleCommandResponse(clientSiteId: string, message: any) {
647
+ // TODO: 路由响应回原始请求者
648
+ console.log(`Received command response from ${clientSiteId}:`, message.requestRef);
649
+ }
650
+ }
651
+
652
+ // 使用示例
653
+ const gateway = new GatewayCore();
654
+
655
+ // 模拟客户端连接和消息处理
656
+ async function simulateGateway() {
657
+ // 1. 设备注册
658
+ await gateway.handleMessage('conn-1', JSON.stringify({
659
+ type: 'register',
660
+ clientId: 'device001'
661
+ }));
662
+
663
+ // 2. 后端注册(完整格式)
664
+ await gateway.handleMessage('conn-2', JSON.stringify({
665
+ type: 'register',
666
+ clientId: 'backend-server',
667
+ clientType: 'backend',
668
+ clientInfo: {
669
+ version: '1.0.0',
670
+ platform: 'nodejs',
671
+ capabilities: ['command', 'program', 'batch']
672
+ },
673
+ features: ['batch', 'chain'],
674
+ version: '1.0'
675
+ }));
676
+
677
+ // 3. 查询设备状态
678
+ await gateway.handleMessage('conn-2', JSON.stringify(
679
+ GatewayUtils.createDeviceStatusQuery('device001', 'query-001')
680
+ ));
681
+
682
+ // 4. 批量命令
683
+ await gateway.handleMessage('conn-2', JSON.stringify(
684
+ GatewayUtils.createBatchCommand(
685
+ { deviceType: 'controller', status: 'online' },
686
+ {
687
+ commandCode: 'RESET',
688
+ deviceType: 'controller',
689
+ operationType: 'write',
690
+ parameters: {}
691
+ },
692
+ { priority: Priority.HIGH }
693
+ )
694
+ ));
695
+
696
+ // 5. Complex类型命令(持续响应)
697
+ await gateway.handleMessage('conn-2', JSON.stringify(
698
+ GatewayUtils.createComplexCommand(
699
+ 'device001',
700
+ {
701
+ commandType: CommandType.COMPLEX,
702
+ commandCode: 'HealthCheck',
703
+ parameters: {
704
+ switchStatus: true,
705
+ switchConfigInformation: true,
706
+ synchronizerStatus: true,
707
+ pillarStatus: true
708
+ }
709
+ },
710
+ { priority: Priority.NORMAL }
711
+ )
712
+ ));
713
+ }
714
+
715
+ export { simulateGateway };