@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,279 @@
1
+ /**
2
+ * Backend 升级示例 - 使用新协议与Gateway通信
3
+ */
4
+
5
+ import WebSocket from 'ws';
6
+ import {
7
+ MessageFactory,
8
+ MessageValidator,
9
+ ClientType,
10
+ Priority,
11
+ isRegisterMessage,
12
+ isCommandResponseMessage,
13
+ RegisterAckMessage,
14
+ CommandResponseMessage
15
+ } from '@jrsoft/subway-protocol';
16
+
17
+ export class UpgradedGatewayClient {
18
+ private ws: WebSocket | null = null;
19
+ private clientId: string;
20
+ private sessionId?: string;
21
+ private reconnectAttempts = 0;
22
+ private readonly maxReconnectAttempts = 10;
23
+
24
+ constructor(
25
+ private gatewayUrl: string,
26
+ clientId?: string
27
+ ) {
28
+ this.clientId = clientId || 'backend-server';
29
+ }
30
+
31
+ /**
32
+ * 连接到Gateway
33
+ */
34
+ async connect(): Promise<void> {
35
+ return new Promise((resolve, reject) => {
36
+ try {
37
+ this.ws = new WebSocket(this.gatewayUrl);
38
+
39
+ this.ws.on('open', () => {
40
+ console.log('Connected to Gateway');
41
+ this.reconnectAttempts = 0;
42
+ this.register();
43
+ resolve();
44
+ });
45
+
46
+ this.ws.on('message', (data) => {
47
+ this.handleMessage(data.toString());
48
+ });
49
+
50
+ this.ws.on('close', () => {
51
+ console.log('Disconnected from Gateway');
52
+ this.sessionId = undefined;
53
+ this.attemptReconnect();
54
+ });
55
+
56
+ this.ws.on('error', (error) => {
57
+ console.error('WebSocket error:', error);
58
+ reject(error);
59
+ });
60
+
61
+ } catch (error) {
62
+ reject(error);
63
+ }
64
+ });
65
+ }
66
+
67
+ /**
68
+ * 注册到Gateway - 使用完整的新协议格式
69
+ */
70
+ private register() {
71
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
72
+ console.error('Cannot register: WebSocket not connected');
73
+ return;
74
+ }
75
+
76
+ // 使用新协议创建注册消息
77
+ const registerMsg = MessageFactory.createRegisterMessage(
78
+ this.clientId,
79
+ ClientType.BACKEND,
80
+ {
81
+ version: '1.0.0',
82
+ platform: 'nodejs',
83
+ capabilities: ['command', 'program', 'callback']
84
+ }
85
+ );
86
+
87
+ console.log('Sending registration:', registerMsg);
88
+ this.ws.send(JSON.stringify(registerMsg));
89
+ }
90
+
91
+ /**
92
+ * 处理来自Gateway的消息
93
+ */
94
+ private handleMessage(data: string) {
95
+ try {
96
+ const message = JSON.parse(data);
97
+
98
+ // 验证消息格式
99
+ if (!MessageValidator.validateMessage(message)) {
100
+ console.error('Invalid message format:', message);
101
+ return;
102
+ }
103
+
104
+ switch (message.type) {
105
+ case 'register_ack':
106
+ this.handleRegisterAck(message as RegisterAckMessage);
107
+ break;
108
+
109
+ case 'command_response':
110
+ if (isCommandResponseMessage(message)) {
111
+ this.handleCommandResponse(message);
112
+ }
113
+ break;
114
+
115
+ default:
116
+ console.log('Received message:', message.type);
117
+ }
118
+
119
+ } catch (error) {
120
+ console.error('Error handling message:', error);
121
+ }
122
+ }
123
+
124
+ /**
125
+ * 处理注册确认
126
+ */
127
+ private handleRegisterAck(message: RegisterAckMessage) {
128
+ if (message.success) {
129
+ console.log('Registration successful');
130
+
131
+ // 保存sessionId(如果Gateway支持)
132
+ if (message.sessionId) {
133
+ this.sessionId = message.sessionId;
134
+ console.log('Session ID:', this.sessionId);
135
+ }
136
+ } else {
137
+ console.error('Registration failed:', message.error);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * 处理命令响应
143
+ */
144
+ private handleCommandResponse(message: CommandResponseMessage) {
145
+ console.log('Command response received:', {
146
+ requestRef: message.requestRef,
147
+ status: message.status,
148
+ executionTime: message.executionTime
149
+ });
150
+
151
+ // 处理响应...
152
+ }
153
+
154
+ /**
155
+ * 发送命令到设备
156
+ */
157
+ async sendCommand(
158
+ deviceSiteId: string,
159
+ commandCode: string,
160
+ parameters: any,
161
+ options?: {
162
+ priority?: Priority;
163
+ timeout?: number;
164
+ callback?: string;
165
+ }
166
+ ): Promise<void> {
167
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
168
+ throw new Error('WebSocket not connected');
169
+ }
170
+
171
+ const command = MessageFactory.createCommandMessage(
172
+ this.generateRequestRef(),
173
+ deviceSiteId,
174
+ {
175
+ commandCode,
176
+ deviceType: 'controller',
177
+ operationType: 'write',
178
+ parameters
179
+ },
180
+ {
181
+ priority: options?.priority || Priority.NORMAL,
182
+ timeout: options?.timeout || 10000,
183
+ callback: options?.callback
184
+ }
185
+ );
186
+
187
+ console.log('Sending command:', command);
188
+ this.ws.send(JSON.stringify(command));
189
+ }
190
+
191
+ /**
192
+ * 发送心跳
193
+ */
194
+ sendHeartbeat() {
195
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
196
+ return;
197
+ }
198
+
199
+ const heartbeat = MessageFactory.createHeartbeatMessage(
200
+ this.clientId,
201
+ Date.now()
202
+ );
203
+
204
+ this.ws.send(JSON.stringify(heartbeat));
205
+ }
206
+
207
+ /**
208
+ * 尝试重连
209
+ */
210
+ private attemptReconnect() {
211
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
212
+ console.error('Max reconnection attempts reached');
213
+ return;
214
+ }
215
+
216
+ this.reconnectAttempts++;
217
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
218
+
219
+ console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
220
+
221
+ setTimeout(() => {
222
+ this.connect().catch(error => {
223
+ console.error('Reconnection failed:', error);
224
+ });
225
+ }, delay);
226
+ }
227
+
228
+ /**
229
+ * 生成请求引用
230
+ */
231
+ private generateRequestRef(): string {
232
+ return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
233
+ }
234
+
235
+ /**
236
+ * 断开连接
237
+ */
238
+ disconnect() {
239
+ if (this.ws) {
240
+ this.ws.close();
241
+ this.ws = null;
242
+ }
243
+ }
244
+ }
245
+
246
+ // 使用示例
247
+ async function example() {
248
+ const client = new UpgradedGatewayClient(
249
+ 'ws://localhost:3001',
250
+ 'backend-server'
251
+ );
252
+
253
+ try {
254
+ // 连接到Gateway
255
+ await client.connect();
256
+
257
+ // 发送命令
258
+ await client.sendCommand(
259
+ 'device001',
260
+ 'WRITE_REGISTER',
261
+ { register: 'R001', value: 100 },
262
+ {
263
+ priority: Priority.HIGH,
264
+ timeout: 5000,
265
+ callback: 'http://localhost:18082/api/callback'
266
+ }
267
+ );
268
+
269
+ // 定期发送心跳
270
+ setInterval(() => {
271
+ client.sendHeartbeat();
272
+ }, 30000);
273
+
274
+ } catch (error) {
275
+ console.error('Error:', error);
276
+ }
277
+ }
278
+
279
+ export default UpgradedGatewayClient;