@pixelhue/event-controller-sdk 0.0.1

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 (62) hide show
  1. package/README.md +76 -0
  2. package/dist/index.d.ts +17 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +18 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/packages/tcp/ConnectManager.d.ts +66 -0
  7. package/dist/packages/tcp/ConnectManager.d.ts.map +1 -0
  8. package/dist/packages/tcp/ConnectManager.js +176 -0
  9. package/dist/packages/tcp/ConnectManager.js.map +1 -0
  10. package/dist/packages/tcp/QueueManager.d.ts +41 -0
  11. package/dist/packages/tcp/QueueManager.d.ts.map +1 -0
  12. package/dist/packages/tcp/QueueManager.js +102 -0
  13. package/dist/packages/tcp/QueueManager.js.map +1 -0
  14. package/dist/packages/tcp/SocketWrapper.d.ts +106 -0
  15. package/dist/packages/tcp/SocketWrapper.d.ts.map +1 -0
  16. package/dist/packages/tcp/SocketWrapper.js +281 -0
  17. package/dist/packages/tcp/SocketWrapper.js.map +1 -0
  18. package/dist/packages/tcp/constants.d.ts +25 -0
  19. package/dist/packages/tcp/constants.d.ts.map +1 -0
  20. package/dist/packages/tcp/constants.js +27 -0
  21. package/dist/packages/tcp/constants.js.map +1 -0
  22. package/dist/packages/tcp/demo.d.ts +3 -0
  23. package/dist/packages/tcp/demo.d.ts.map +1 -0
  24. package/dist/packages/tcp/demo.js +164 -0
  25. package/dist/packages/tcp/demo.js.map +1 -0
  26. package/dist/packages/tcp/discoveryService.d.ts +42 -0
  27. package/dist/packages/tcp/discoveryService.d.ts.map +1 -0
  28. package/dist/packages/tcp/discoveryService.js +166 -0
  29. package/dist/packages/tcp/discoveryService.js.map +1 -0
  30. package/dist/packages/tcp/protocolHandler.d.ts +64 -0
  31. package/dist/packages/tcp/protocolHandler.d.ts.map +1 -0
  32. package/dist/packages/tcp/protocolHandler.js +96 -0
  33. package/dist/packages/tcp/protocolHandler.js.map +1 -0
  34. package/dist/packages/tcp/tcpWrapper.d.ts +168 -0
  35. package/dist/packages/tcp/tcpWrapper.d.ts.map +1 -0
  36. package/dist/packages/tcp/tcpWrapper.js +472 -0
  37. package/dist/packages/tcp/tcpWrapper.js.map +1 -0
  38. package/dist/packages/tcp/test-build-parse-debug.d.ts +2 -0
  39. package/dist/packages/tcp/test-build-parse-debug.d.ts.map +1 -0
  40. package/dist/packages/tcp/test-build-parse-debug.js +73 -0
  41. package/dist/packages/tcp/test-build-parse-debug.js.map +1 -0
  42. package/dist/packages/tcp/test-packet-split.d.ts +43 -0
  43. package/dist/packages/tcp/test-packet-split.d.ts.map +1 -0
  44. package/dist/packages/tcp/test-packet-split.js +417 -0
  45. package/dist/packages/tcp/test-packet-split.js.map +1 -0
  46. package/dist/packages/tcp/test-protocol-handler.d.ts +6 -0
  47. package/dist/packages/tcp/test-protocol-handler.d.ts.map +1 -0
  48. package/dist/packages/tcp/test-protocol-handler.js +225 -0
  49. package/dist/packages/tcp/test-protocol-handler.js.map +1 -0
  50. package/dist/packages/tcp/test-server.d.ts +54 -0
  51. package/dist/packages/tcp/test-server.d.ts.map +1 -0
  52. package/dist/packages/tcp/test-server.js +412 -0
  53. package/dist/packages/tcp/test-server.js.map +1 -0
  54. package/dist/packages/tcp/types.d.ts +232 -0
  55. package/dist/packages/tcp/types.d.ts.map +1 -0
  56. package/dist/packages/tcp/types.js +13 -0
  57. package/dist/packages/tcp/types.js.map +1 -0
  58. package/dist/packages/tcp/utils.d.ts +14 -0
  59. package/dist/packages/tcp/utils.d.ts.map +1 -0
  60. package/dist/packages/tcp/utils.js +47 -0
  61. package/dist/packages/tcp/utils.js.map +1 -0
  62. package/package.json +82 -0
@@ -0,0 +1,164 @@
1
+ // ALL_CODE_LINE: 161
2
+ // AI_CODE_LINE: 161
3
+ import { SocketWrapper } from './SocketWrapper.js';
4
+ import { QueueOverflowStrategy } from './types.js';
5
+ async function demo() {
6
+ console.log('🚀 Starting SocketWrapper Demo...\n');
7
+ const socketWrapper = new SocketWrapper({
8
+ maxLength: 1000,
9
+ overflowStrategy: QueueOverflowStrategy.DROP_NEWEST,
10
+ processTimeout: 5000,
11
+ });
12
+ socketWrapper.on('connected', () => {
13
+ console.log('✅ Socket connected');
14
+ console.log(' - Connection established successfully');
15
+ console.log(' - Heartbeat mechanism started');
16
+ console.log(' - Reconnect counter reset\n');
17
+ });
18
+ socketWrapper.on('disconnected', () => {
19
+ console.log('⚠️ Socket disconnected, will retry...');
20
+ console.log(' - Connection lost but will attempt to reconnect');
21
+ console.log(' - Auto-reconnect is enabled\n');
22
+ });
23
+ socketWrapper.on('data', (data, requestId) => {
24
+ console.log(`📨 Received data from server (RequestId: ${requestId}):`);
25
+ console.log(` Data:`, JSON.stringify(data, null, 2));
26
+ console.log('');
27
+ });
28
+ socketWrapper.on('error', (error, requestId) => {
29
+ console.error(`❌ Socket error: ${error.message}`);
30
+ if (requestId !== undefined) {
31
+ console.error(` - RequestId: ${requestId}`);
32
+ }
33
+ console.error(` - Error stack: ${error.stack || 'N/A'}\n`);
34
+ });
35
+ socketWrapper.on('close', () => {
36
+ console.log('🔌 Socket closed');
37
+ console.log(' - Connection permanently closed');
38
+ console.log(' - No more reconnection attempts will be made\n');
39
+ });
40
+ // 连接配置
41
+ // 默认使用本地测试服务器(先运行 npm run test-server 或 npx tsx src/packages/tcp/test-server.ts)
42
+ const connectOptions = {
43
+ host: process.env.TEST_HOST || '127.0.0.1', // 默认本地测试服务器
44
+ port: parseInt(process.env.TEST_PORT || '8080', 10), // 默认端口 8080
45
+ // 自动重连配置
46
+ autoReconnect: true, // 启用自动重连
47
+ reconnectInterval: 5000, // 重连间隔 5 秒
48
+ maxReconnectAttempts: 10, // 最大重连次数
49
+ // 重连限制配置
50
+ enableExponentialBackoff: true, // 启用指数退避(重连间隔逐渐增加:5s -> 10s -> 20s -> 40s -> 60s...)
51
+ maxReconnectInterval: 60000, // 最大重连间隔 60 秒
52
+ // 心跳配置(默认使用内置 ping/pong)
53
+ enableHeartbeat: true, // 启用心跳
54
+ heartbeatInterval: 5000, // 心跳间隔 5 秒
55
+ heartbeatTimeout: 5000, // 心跳超时 5 秒(无响应则关闭连接)
56
+ };
57
+ console.log('🔌 Connecting to server...');
58
+ console.log(` Host: ${connectOptions.host}`);
59
+ console.log(` Port: ${connectOptions.port}\n`);
60
+ // 连接到设备
61
+ socketWrapper.connect(connectOptions.host || '127.0.0.1', connectOptions.port || 8080, connectOptions);
62
+ // 等待连接建立
63
+ await new Promise((resolve) => {
64
+ socketWrapper.once('connected', resolve);
65
+ setTimeout(resolve, 10000); // 最多等待 10 秒
66
+ });
67
+ if (!socketWrapper.isConnected()) {
68
+ console.error('❌ Failed to connect\n');
69
+ return;
70
+ }
71
+ // ============================================
72
+ // 示例:发送测试数据
73
+ // ============================================
74
+ console.log('📤 Sending test data...');
75
+ const testPayload = {
76
+ data: { row: 1, col: 3, type: 0 },
77
+ header: { seq: 0, sn: '', source: '' },
78
+ options: {},
79
+ };
80
+ const requestId = socketWrapper.send(testPayload);
81
+ console.log(` - RequestId: ${requestId}`);
82
+ console.log(` - Payload:`, testPayload);
83
+ console.log('');
84
+ // 等待接收响应
85
+ console.log('⏳ Waiting for response...\n');
86
+ // 保持连接一段时间以观察自动重连(如果断开)
87
+ console.log('⏳ Keeping connection alive...');
88
+ if (connectOptions.enableExponentialBackoff) {
89
+ console.log(` - Base interval: ${connectOptions.reconnectInterval}ms`);
90
+ console.log(` - Max interval: ${connectOptions.maxReconnectInterval}ms`);
91
+ }
92
+ else {
93
+ console.log(` - Fixed interval: ${connectOptions.reconnectInterval}ms\n`);
94
+ }
95
+ console.log(' 💓 Heartbeat Configuration:');
96
+ if (connectOptions.enableHeartbeat) {
97
+ console.log(` - Interval: ${connectOptions.heartbeatInterval}ms`);
98
+ console.log(` - Timeout: ${connectOptions.heartbeatTimeout}ms`);
99
+ console.log(` - Behavior: send 'ping' every ${connectOptions.heartbeatInterval}ms and expect 'pong' within ${connectOptions.heartbeatTimeout}ms\n`);
100
+ }
101
+ else {
102
+ console.log(` - Enabled: No\n`);
103
+ }
104
+ // 等待用户中断或保持运行
105
+ await new Promise((resolve) => {
106
+ let isExiting = false;
107
+ let reconnectCheckTimeout = null;
108
+ // 处理 Ctrl+C 优雅退出
109
+ const exitHandler = () => {
110
+ if (isExiting)
111
+ return;
112
+ isExiting = true;
113
+ if (reconnectCheckTimeout) {
114
+ clearTimeout(reconnectCheckTimeout);
115
+ }
116
+ console.log('\n\n🛑 Shutting down...');
117
+ socketWrapper.disconnect();
118
+ console.log(' Disconnected\n');
119
+ resolve();
120
+ };
121
+ process.on('SIGINT', exitHandler);
122
+ process.on('SIGTERM', exitHandler);
123
+ // 监听重连事件,如果重连成功就不显示失败消息
124
+ let reconnectAttempted = false;
125
+ socketWrapper.on('disconnected', () => {
126
+ reconnectAttempted = true;
127
+ // 如果启用了自动重连,等待重连,不立即显示失败
128
+ });
129
+ socketWrapper.on('connected', () => {
130
+ // 重连成功,清除之前的失败检查
131
+ if (reconnectAttempted) {
132
+ reconnectAttempted = false;
133
+ if (reconnectCheckTimeout) {
134
+ clearTimeout(reconnectCheckTimeout);
135
+ reconnectCheckTimeout = null;
136
+ }
137
+ }
138
+ });
139
+ // 延迟检查连接状态,给自动重连一些时间
140
+ reconnectCheckTimeout = setTimeout(() => {
141
+ if (!socketWrapper.isConnected() && !reconnectAttempted) {
142
+ // 只有在没有重连尝试的情况下才显示失败
143
+ console.log('⚠️ Connection failed. Will exit in 10 seconds...');
144
+ console.log(' (Press Ctrl+C to exit immediately)\n');
145
+ setTimeout(() => {
146
+ if (!socketWrapper.isConnected() && !isExiting) {
147
+ console.log('⏰ Auto-exiting due to connection failure...\n');
148
+ exitHandler();
149
+ }
150
+ }, 10000);
151
+ }
152
+ }, 2000); // 等待2秒,给自动重连时间
153
+ });
154
+ }
155
+ export { demo };
156
+ // 直接执行 demo(当文件被直接运行时)
157
+ // 使用方式: npm run demo 或 npx tsx src/packages/tcp/demo.ts
158
+ if (import.meta.url === `file://${process.argv[1]?.replace(/\\/g, '/')}` || process.argv[1]?.includes('demo.ts')) {
159
+ demo().catch((error) => {
160
+ console.error('Demo error:', error);
161
+ process.exit(1);
162
+ });
163
+ }
164
+ //# sourceMappingURL=demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo.js","sourceRoot":"","sources":["../../../src/packages/tcp/demo.ts"],"names":[],"mappings":"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","sourcesContent":["// ALL_CODE_LINE: 161\r\n// AI_CODE_LINE: 161\r\n\r\nimport { SocketWrapper } from './SocketWrapper.js';\r\nimport type { SocketConnectOptions } from './types.js';\r\nimport { QueueOverflowStrategy } from './types.js';\r\n\r\nasync function demo() {\r\n\tconsole.log('🚀 Starting SocketWrapper Demo...\\n');\r\n\r\n\tconst socketWrapper = new SocketWrapper({\r\n\t\tmaxLength: 1000,\r\n\t\toverflowStrategy: QueueOverflowStrategy.DROP_NEWEST,\r\n\t\tprocessTimeout: 5000,\r\n\t});\r\n\r\n\tsocketWrapper.on('connected', () => {\r\n\t\tconsole.log('✅ Socket connected');\r\n\t\tconsole.log(' - Connection established successfully');\r\n\t\tconsole.log(' - Heartbeat mechanism started');\r\n\t\tconsole.log(' - Reconnect counter reset\\n');\r\n\t});\r\n\r\n\tsocketWrapper.on('disconnected', () => {\r\n\t\tconsole.log('⚠️ Socket disconnected, will retry...');\r\n\t\tconsole.log(' - Connection lost but will attempt to reconnect');\r\n\t\tconsole.log(' - Auto-reconnect is enabled\\n');\r\n\t});\r\n\r\n\tsocketWrapper.on('data', (data: any, requestId: number) => {\r\n\t\tconsole.log(`📨 Received data from server (RequestId: ${requestId}):`);\r\n\t\tconsole.log(` Data:`, JSON.stringify(data, null, 2));\r\n\t\tconsole.log('');\r\n\t});\r\n\r\n\tsocketWrapper.on('error', (error: Error, requestId?: number) => {\r\n\t\tconsole.error(`❌ Socket error: ${error.message}`);\r\n\t\tif (requestId !== undefined) {\r\n\t\t\tconsole.error(` - RequestId: ${requestId}`);\r\n\t\t}\r\n\t\tconsole.error(` - Error stack: ${error.stack || 'N/A'}\\n`);\r\n\t});\r\n\r\n\tsocketWrapper.on('close', () => {\r\n\t\tconsole.log('🔌 Socket closed');\r\n\t\tconsole.log(' - Connection permanently closed');\r\n\t\tconsole.log(' - No more reconnection attempts will be made\\n');\r\n\t});\r\n\r\n\t// 连接配置\r\n\t// 默认使用本地测试服务器(先运行 npm run test-server 或 npx tsx src/packages/tcp/test-server.ts)\r\n\tconst connectOptions: SocketConnectOptions = {\r\n\t\thost: process.env.TEST_HOST || '127.0.0.1', // 默认本地测试服务器\r\n\t\tport: parseInt(process.env.TEST_PORT || '8080', 10), // 默认端口 8080\r\n\t\t// 自动重连配置\r\n\t\tautoReconnect: true, // 启用自动重连\r\n\t\treconnectInterval: 5000, // 重连间隔 5 秒\r\n\t\tmaxReconnectAttempts: 10, // 最大重连次数\r\n\t\t// 重连限制配置\r\n\t\tenableExponentialBackoff: true, // 启用指数退避(重连间隔逐渐增加:5s -> 10s -> 20s -> 40s -> 60s...)\r\n\t\tmaxReconnectInterval: 60000, // 最大重连间隔 60 秒\r\n\t\t// 心跳配置(默认使用内置 ping/pong)\r\n\t\tenableHeartbeat: true, // 启用心跳\r\n\t\theartbeatInterval: 5000, // 心跳间隔 5 秒\r\n\t\theartbeatTimeout: 5000, // 心跳超时 5 秒(无响应则关闭连接)\r\n\t};\r\n\r\n\tconsole.log('🔌 Connecting to server...');\r\n\tconsole.log(` Host: ${connectOptions.host}`);\r\n\tconsole.log(` Port: ${connectOptions.port}\\n`);\r\n\r\n\t// 连接到设备\r\n\tsocketWrapper.connect(connectOptions.host || '127.0.0.1', connectOptions.port || 8080, connectOptions);\r\n\r\n\t// 等待连接建立\r\n\tawait new Promise((resolve) => {\r\n\t\tsocketWrapper.once('connected', resolve);\r\n\t\tsetTimeout(resolve, 10000); // 最多等待 10 秒\r\n\t});\r\n\r\n\tif (!socketWrapper.isConnected()) {\r\n\t\tconsole.error('❌ Failed to connect\\n');\r\n\t\treturn;\r\n\t}\r\n\r\n\t// ============================================\r\n\t// 示例:发送测试数据\r\n\t// ============================================\r\n\tconsole.log('📤 Sending test data...');\r\n\tconst testPayload = {\r\n\t\tdata: { row: 1, col: 3, type: 0 },\r\n\t\theader: { seq: 0, sn: '', source: '' },\r\n\t\toptions: {},\r\n\t};\r\n\tconst requestId = socketWrapper.send(testPayload);\r\n\tconsole.log(` - RequestId: ${requestId}`);\r\n\tconsole.log(` - Payload:`, testPayload);\r\n\tconsole.log('');\r\n\r\n\t// 等待接收响应\r\n\tconsole.log('⏳ Waiting for response...\\n');\r\n\r\n\t// 保持连接一段时间以观察自动重连(如果断开)\r\n\tconsole.log('⏳ Keeping connection alive...');\r\n\tif (connectOptions.enableExponentialBackoff) {\r\n\t\tconsole.log(` - Base interval: ${connectOptions.reconnectInterval}ms`);\r\n\t\tconsole.log(` - Max interval: ${connectOptions.maxReconnectInterval}ms`);\r\n\t} else {\r\n\t\tconsole.log(` - Fixed interval: ${connectOptions.reconnectInterval}ms\\n`);\r\n\t}\r\n\r\n\tconsole.log(' 💓 Heartbeat Configuration:');\r\n\tif (connectOptions.enableHeartbeat) {\r\n\t\tconsole.log(` - Interval: ${connectOptions.heartbeatInterval}ms`);\r\n\t\tconsole.log(` - Timeout: ${connectOptions.heartbeatTimeout}ms`);\r\n\t\tconsole.log(\r\n\t\t\t` - Behavior: send 'ping' every ${connectOptions.heartbeatInterval}ms and expect 'pong' within ${connectOptions.heartbeatTimeout}ms\\n`\r\n\t\t);\r\n\t} else {\r\n\t\tconsole.log(` - Enabled: No\\n`);\r\n\t}\r\n\r\n\t// 等待用户中断或保持运行\r\n\tawait new Promise<void>((resolve) => {\r\n\t\tlet isExiting = false;\r\n\t\tlet reconnectCheckTimeout: NodeJS.Timeout | null = null;\r\n\r\n\t\t// 处理 Ctrl+C 优雅退出\r\n\t\tconst exitHandler = () => {\r\n\t\t\tif (isExiting) return;\r\n\t\t\tisExiting = true;\r\n\t\t\tif (reconnectCheckTimeout) {\r\n\t\t\t\tclearTimeout(reconnectCheckTimeout);\r\n\t\t\t}\r\n\t\t\tconsole.log('\\n\\n🛑 Shutting down...');\r\n\t\t\tsocketWrapper.disconnect();\r\n\t\t\tconsole.log(' Disconnected\\n');\r\n\t\t\tresolve();\r\n\t\t};\r\n\r\n\t\tprocess.on('SIGINT', exitHandler);\r\n\t\tprocess.on('SIGTERM', exitHandler);\r\n\r\n\t\t// 监听重连事件,如果重连成功就不显示失败消息\r\n\t\tlet reconnectAttempted = false;\r\n\t\tsocketWrapper.on('disconnected', () => {\r\n\t\t\treconnectAttempted = true;\r\n\t\t\t// 如果启用了自动重连,等待重连,不立即显示失败\r\n\t\t});\r\n\r\n\t\tsocketWrapper.on('connected', () => {\r\n\t\t\t// 重连成功,清除之前的失败检查\r\n\t\t\tif (reconnectAttempted) {\r\n\t\t\t\treconnectAttempted = false;\r\n\t\t\t\tif (reconnectCheckTimeout) {\r\n\t\t\t\t\tclearTimeout(reconnectCheckTimeout);\r\n\t\t\t\t\treconnectCheckTimeout = null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\t// 延迟检查连接状态,给自动重连一些时间\r\n\t\treconnectCheckTimeout = setTimeout(() => {\r\n\t\t\tif (!socketWrapper.isConnected() && !reconnectAttempted) {\r\n\t\t\t\t// 只有在没有重连尝试的情况下才显示失败\r\n\t\t\t\tconsole.log('⚠️ Connection failed. Will exit in 10 seconds...');\r\n\t\t\t\tconsole.log(' (Press Ctrl+C to exit immediately)\\n');\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tif (!socketWrapper.isConnected() && !isExiting) {\r\n\t\t\t\t\t\tconsole.log('⏰ Auto-exiting due to connection failure...\\n');\r\n\t\t\t\t\t\texitHandler();\r\n\t\t\t\t\t}\r\n\t\t\t\t}, 10000);\r\n\t\t\t}\r\n\t\t}, 2000); // 等待2秒,给自动重连时间\r\n\t});\r\n}\r\n\r\nexport { demo };\r\n\r\n// 直接执行 demo(当文件被直接运行时)\r\n// 使用方式: npm run demo 或 npx tsx src/packages/tcp/demo.ts\r\nif (import.meta.url === `file://${process.argv[1]?.replace(/\\\\/g, '/')}` || process.argv[1]?.includes('demo.ts')) {\r\n\tdemo().catch((error) => {\r\n\t\tconsole.error('Demo error:', error);\r\n\t\tprocess.exit(1);\r\n\t});\r\n}\r\n"]}
@@ -0,0 +1,42 @@
1
+ import { EventEmitter } from 'eventemitter3';
2
+ import type { DiscoveryEvents, DiscoveryServiceOptions, MiniDiscoveredDevice } from './types.js';
3
+ /**
4
+ * MINI设备发现服务
5
+ * 使用Bonjour/mDNS进行设备自动发现
6
+ */
7
+ export declare class MiniDiscoveryService extends EventEmitter<DiscoveryEvents> {
8
+ private readonly options;
9
+ private readonly discoveredDevices;
10
+ private queryInterval;
11
+ private isDestroyed;
12
+ private bonjour;
13
+ private browser;
14
+ constructor(options?: DiscoveryServiceOptions);
15
+ /**
16
+ * 开始设备发现
17
+ */
18
+ private startDiscovery;
19
+ private emitUp;
20
+ private emitDown;
21
+ private convertService;
22
+ /**
23
+ * 手动查询设备(马上刷新服务列表,同时清理过期的服务)
24
+ */
25
+ query(): void;
26
+ /**
27
+ * 获取设备ID
28
+ * @param address 设备地址
29
+ * @param port 设备端口
30
+ */
31
+ private getDeviceId;
32
+ /**
33
+ * 获取所有已发现的设备
34
+ * @returns 已发现的设备数组
35
+ */
36
+ get knownDevices(): MiniDiscoveredDevice[];
37
+ /**
38
+ * 销毁发现服务
39
+ */
40
+ destroy(): void;
41
+ }
42
+ //# sourceMappingURL=discoveryService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discoveryService.d.ts","sourceRoot":"","sources":["../../../src/packages/tcp/discoveryService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGjG;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,YAAY,CAAC,eAAe,CAAC;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA2C;IAC7E,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,OAAO,CAAC,EAAE,uBAAuB;IAW7C;;OAEG;IACH,OAAO,CAAC,cAAc;IA6BtB,OAAO,CAAC,MAAM;IAcd,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,cAAc;IA2BtB;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAInB;;;OAGG;IACH,IAAI,YAAY,IAAI,oBAAoB,EAAE,CAEzC;IAED;;OAEG;IACH,OAAO,IAAI,IAAI;CAqCf"}
@@ -0,0 +1,166 @@
1
+ // ALL_CODE_LINE: 197
2
+ // AI_CODE_LINE: 38
3
+ import { EventEmitter } from 'eventemitter3';
4
+ import { Bonjour } from '@julusian/bonjour-service';
5
+ import { DEFAULT_MDNS_QUERY_INTERVAL, SUPPORTED_MODELS } from './constants.js';
6
+ /**
7
+ * MINI设备发现服务
8
+ * 使用Bonjour/mDNS进行设备自动发现
9
+ */
10
+ export class MiniDiscoveryService extends EventEmitter {
11
+ options;
12
+ discoveredDevices = new Map();
13
+ queryInterval = null;
14
+ isDestroyed = false;
15
+ bonjour = null;
16
+ browser = null;
17
+ constructor(options) {
18
+ super();
19
+ this.options = {
20
+ queryInterval: options?.queryInterval ?? DEFAULT_MDNS_QUERY_INTERVAL, // 10秒
21
+ type: options?.type ?? 'pixelhue',
22
+ protocol: options?.protocol ?? 'tcp',
23
+ };
24
+ this.startDiscovery();
25
+ }
26
+ /**
27
+ * 开始设备发现
28
+ */
29
+ startDiscovery() {
30
+ if (this.isDestroyed)
31
+ return;
32
+ // 初始化 mDNS 浏览器
33
+ this.bonjour = new Bonjour();
34
+ const type = this.options.type || 'pixelhue';
35
+ const protocol = (this.options.protocol || 'tcp');
36
+ this.browser = this.bonjour.find({ type, protocol });
37
+ if (this.browser) {
38
+ this.browser.on('up', (service) => this.emitUp(service));
39
+ this.browser.on('down', (service) => this.emitDown(service));
40
+ this.browser.on('srv-update', (newService, existingService) => {
41
+ this.emitDown(existingService);
42
+ this.emitUp(newService);
43
+ });
44
+ }
45
+ // 立即触发一次查询
46
+ this.query();
47
+ // 设置定期查询
48
+ if (this.options.queryInterval > 0) {
49
+ this.queryInterval = setInterval(() => {
50
+ this.query();
51
+ }, this.options.queryInterval);
52
+ }
53
+ }
54
+ emitUp(service) {
55
+ const device = this.convertService(service);
56
+ if (!device)
57
+ return;
58
+ const deviceId = this.getDeviceId(device.address, device.port);
59
+ const existingDevice = this.discoveredDevices.get(deviceId);
60
+ if (!existingDevice) {
61
+ this.discoveredDevices.set(deviceId, device);
62
+ }
63
+ else {
64
+ existingDevice.lastSeen = new Date();
65
+ }
66
+ this.emit('up', device);
67
+ }
68
+ emitDown(service) {
69
+ const device = this.convertService(service);
70
+ if (!device)
71
+ return;
72
+ const deviceId = this.getDeviceId(device.address, device.port);
73
+ const existingDevice = this.discoveredDevices.get(deviceId);
74
+ if (existingDevice) {
75
+ this.discoveredDevices.delete(deviceId);
76
+ this.emit('down', existingDevice);
77
+ }
78
+ }
79
+ convertService(service) {
80
+ // 如果没有地址,返回 null
81
+ if (!service.host) {
82
+ return null;
83
+ }
84
+ // 从 txt 记录提取元数据(字段名需与MINI固件一致,如无则使用默认)
85
+ const txt = service.txt || {};
86
+ const serialNumber = txt.sn || 'UNKNOWN';
87
+ const _version = txt.version || '1.0.0';
88
+ const model = txt.model || SUPPORTED_MODELS.MINI;
89
+ const displayName = txt.displayName || 'Pixelhue U5 MINI';
90
+ const keyRows = Number(txt.rows) || 4;
91
+ const keyCols = Number(txt.cols) || 10;
92
+ return {
93
+ address: service.host,
94
+ port: service.port, // 设备端口
95
+ name: displayName, // 设备名称
96
+ serialNumber, // 设备序列号,唯一标识设备
97
+ version: _version, // 固件版本号
98
+ modelName: model, // 设备型号名称
99
+ keyLayout: { rows: keyRows, columns: keyCols }, // 按键布局(行数和列数)
100
+ lastSeen: new Date(),
101
+ };
102
+ }
103
+ /**
104
+ * 手动查询设备(马上刷新服务列表,同时清理过期的服务)
105
+ */
106
+ query() {
107
+ if (this.isDestroyed) {
108
+ return;
109
+ }
110
+ // 告诉浏览器发送查询,并过期旧服务
111
+ this.browser?.update();
112
+ this.browser?.expire();
113
+ }
114
+ /**
115
+ * 获取设备ID
116
+ * @param address 设备地址
117
+ * @param port 设备端口
118
+ */
119
+ getDeviceId(address, port) {
120
+ return `${address}:${port}`;
121
+ }
122
+ /**
123
+ * 获取所有已发现的设备
124
+ * @returns 已发现的设备数组
125
+ */
126
+ get knownDevices() {
127
+ return Array.from(this.discoveredDevices.values());
128
+ }
129
+ /**
130
+ * 销毁发现服务
131
+ */
132
+ destroy() {
133
+ this.isDestroyed = true;
134
+ // 清理定时器
135
+ if (this.queryInterval) {
136
+ clearInterval(this.queryInterval);
137
+ this.queryInterval = null;
138
+ }
139
+ // 清空所有发现的设备并触发下线事件
140
+ for (const device of this.discoveredDevices.values()) {
141
+ this.emit('down', device);
142
+ }
143
+ this.discoveredDevices.clear();
144
+ // 停止浏览器
145
+ if (this.browser) {
146
+ try {
147
+ this.browser.stop();
148
+ }
149
+ catch (error) {
150
+ // 忽略停止浏览器时的错误,避免影响销毁流程
151
+ const errorMessage = error instanceof Error ? error.message : String(error);
152
+ console.warn('停止浏览器时发生错误:', errorMessage);
153
+ }
154
+ this.browser.removeAllListeners(); // 避免继续响应网络事件,比如up、down、srv-update等
155
+ this.browser = null;
156
+ }
157
+ // 销毁 Bonjour 实例
158
+ if (this.bonjour) {
159
+ this.bonjour.destroy();
160
+ this.bonjour = null;
161
+ }
162
+ // 移除所有事件监听器
163
+ this.removeAllListeners(); // 清理当前DiscoveryService(继承自EventEmitter)的所有事件监听器,避免内存泄漏
164
+ }
165
+ }
166
+ //# sourceMappingURL=discoveryService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discoveryService.js","sourceRoot":"","sources":["../../../src/packages/tcp/discoveryService.ts"],"names":[],"mappings":"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","sourcesContent":["// ALL_CODE_LINE: 197\r\n// AI_CODE_LINE: 38\r\n\r\nimport { EventEmitter } from 'eventemitter3';\r\nimport type { Browser, DiscoveredService as BonjourService } from '@julusian/bonjour-service';\r\nimport { Bonjour } from '@julusian/bonjour-service';\r\nimport type { DiscoveryEvents, DiscoveryServiceOptions, MiniDiscoveredDevice } from './types.js';\r\nimport { DEFAULT_MDNS_QUERY_INTERVAL, SUPPORTED_MODELS } from './constants.js';\r\n\r\n/**\r\n * MINI设备发现服务\r\n * 使用Bonjour/mDNS进行设备自动发现\r\n */\r\nexport class MiniDiscoveryService extends EventEmitter<DiscoveryEvents> {\r\n\tprivate readonly options: Required<DiscoveryServiceOptions>;\r\n\tprivate readonly discoveredDevices = new Map<string, MiniDiscoveredDevice>();\r\n\tprivate queryInterval: NodeJS.Timeout | null = null;\r\n\tprivate isDestroyed = false;\r\n\tprivate bonjour: Bonjour | null = null;\r\n\tprivate browser: Browser | null = null;\r\n\r\n\tconstructor(options?: DiscoveryServiceOptions) {\r\n\t\tsuper();\r\n\r\n\t\tthis.options = {\r\n\t\t\tqueryInterval: options?.queryInterval ?? DEFAULT_MDNS_QUERY_INTERVAL, // 10秒\r\n\t\t\ttype: options?.type ?? 'pixelhue',\r\n\t\t\tprotocol: options?.protocol ?? 'tcp',\r\n\t\t};\r\n\r\n\t\tthis.startDiscovery();\r\n\t}\r\n\t/**\r\n\t * 开始设备发现\r\n\t */\r\n\tprivate startDiscovery(): void {\r\n\t\tif (this.isDestroyed) return;\r\n\r\n\t\t// 初始化 mDNS 浏览器\r\n\t\tthis.bonjour = new Bonjour();\r\n\t\tconst type = this.options.type || 'pixelhue';\r\n\t\tconst protocol = (this.options.protocol || 'tcp') as 'tcp' | 'udp';\r\n\t\tthis.browser = this.bonjour.find({ type, protocol });\r\n\r\n\t\tif (this.browser) {\r\n\t\t\tthis.browser.on('up', (service: BonjourService) => this.emitUp(service));\r\n\t\t\tthis.browser.on('down', (service: BonjourService) => this.emitDown(service));\r\n\t\t\tthis.browser.on('srv-update', (newService: BonjourService, existingService: BonjourService) => {\r\n\t\t\t\tthis.emitDown(existingService);\r\n\t\t\t\tthis.emitUp(newService);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\t// 立即触发一次查询\r\n\t\tthis.query();\r\n\r\n\t\t// 设置定期查询\r\n\t\tif (this.options.queryInterval > 0) {\r\n\t\t\tthis.queryInterval = setInterval(() => {\r\n\t\t\t\tthis.query();\r\n\t\t\t}, this.options.queryInterval);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate emitUp(service: BonjourService): void {\r\n\t\tconst device = this.convertService(service);\r\n\t\tif (!device) return;\r\n\r\n\t\tconst deviceId = this.getDeviceId(device.address, device.port);\r\n\t\tconst existingDevice = this.discoveredDevices.get(deviceId);\r\n\t\tif (!existingDevice) {\r\n\t\t\tthis.discoveredDevices.set(deviceId, device);\r\n\t\t} else {\r\n\t\t\texistingDevice.lastSeen = new Date();\r\n\t\t}\r\n\t\tthis.emit('up', device);\r\n\t}\r\n\r\n\tprivate emitDown(service: BonjourService): void {\r\n\t\tconst device = this.convertService(service);\r\n\t\tif (!device) return;\r\n\r\n\t\tconst deviceId = this.getDeviceId(device.address, device.port);\r\n\t\tconst existingDevice = this.discoveredDevices.get(deviceId);\r\n\t\tif (existingDevice) {\r\n\t\t\tthis.discoveredDevices.delete(deviceId);\r\n\t\t\tthis.emit('down', existingDevice);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate convertService(service: BonjourService): MiniDiscoveredDevice | null {\r\n\t\t// 如果没有地址,返回 null\r\n\t\tif (!service.host) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\t// 从 txt 记录提取元数据(字段名需与MINI固件一致,如无则使用默认)\r\n\t\tconst txt: Record<string, string> = service.txt || {};\r\n\t\tconst serialNumber: string = txt.sn || 'UNKNOWN';\r\n\t\tconst _version: string = txt.version || '1.0.0';\r\n\t\tconst model: string = txt.model || SUPPORTED_MODELS.MINI;\r\n\t\tconst displayName: string = txt.displayName || 'Pixelhue U5 MINI';\r\n\t\tconst keyRows = Number(txt.rows) || 4;\r\n\t\tconst keyCols = Number(txt.cols) || 10;\r\n\r\n\t\treturn {\r\n\t\t\taddress: service.host,\r\n\t\t\tport: service.port, // 设备端口\r\n\t\t\tname: displayName, // 设备名称\r\n\t\t\tserialNumber, // 设备序列号,唯一标识设备\r\n\t\t\tversion: _version, // 固件版本号\r\n\t\t\tmodelName: model, // 设备型号名称\r\n\t\t\tkeyLayout: { rows: keyRows, columns: keyCols }, // 按键布局(行数和列数)\r\n\t\t\tlastSeen: new Date(),\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * 手动查询设备(马上刷新服务列表,同时清理过期的服务)\r\n\t */\r\n\tquery(): void {\r\n\t\tif (this.isDestroyed) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// 告诉浏览器发送查询,并过期旧服务\r\n\t\tthis.browser?.update();\r\n\t\tthis.browser?.expire();\r\n\t}\r\n\r\n\t/**\r\n\t * 获取设备ID\r\n\t * @param address 设备地址\r\n\t * @param port 设备端口\r\n\t */\r\n\tprivate getDeviceId(address: string, port: number): string {\r\n\t\treturn `${address}:${port}`;\r\n\t}\r\n\r\n\t/**\r\n\t * 获取所有已发现的设备\r\n\t * @returns 已发现的设备数组\r\n\t */\r\n\tget knownDevices(): MiniDiscoveredDevice[] {\r\n\t\treturn Array.from(this.discoveredDevices.values());\r\n\t}\r\n\r\n\t/**\r\n\t * 销毁发现服务\r\n\t */\r\n\tdestroy(): void {\r\n\t\tthis.isDestroyed = true;\r\n\r\n\t\t// 清理定时器\r\n\t\tif (this.queryInterval) {\r\n\t\t\tclearInterval(this.queryInterval);\r\n\t\t\tthis.queryInterval = null;\r\n\t\t}\r\n\r\n\t\t// 清空所有发现的设备并触发下线事件\r\n\t\tfor (const device of this.discoveredDevices.values()) {\r\n\t\t\tthis.emit('down', device);\r\n\t\t}\r\n\t\tthis.discoveredDevices.clear();\r\n\r\n\t\t// 停止浏览器\r\n\t\tif (this.browser) {\r\n\t\t\ttry {\r\n\t\t\t\tthis.browser.stop();\r\n\t\t\t} catch (error) {\r\n\t\t\t\t// 忽略停止浏览器时的错误,避免影响销毁流程\r\n\t\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\r\n\t\t\t\tconsole.warn('停止浏览器时发生错误:', errorMessage);\r\n\t\t\t}\r\n\t\t\tthis.browser.removeAllListeners(); // 避免继续响应网络事件,比如up、down、srv-update等\r\n\t\t\tthis.browser = null;\r\n\t\t}\r\n\r\n\t\t// 销毁 Bonjour 实例\r\n\t\tif (this.bonjour) {\r\n\t\t\tthis.bonjour.destroy();\r\n\t\t\tthis.bonjour = null;\r\n\t\t}\r\n\r\n\t\t// 移除所有事件监听器\r\n\t\tthis.removeAllListeners(); // 清理当前DiscoveryService(继承自EventEmitter)的所有事件监听器,避免内存泄漏\r\n\t}\r\n}\r\n"]}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * 协议处理结果
3
+ */
4
+ export interface ProtocolParseResult {
5
+ /** 解析结果 */
6
+ data: any;
7
+ /** 头部数据 */
8
+ header: any;
9
+ /** 请求ID(seqId) */
10
+ requestId: number;
11
+ /** 是否为响应包 */
12
+ isResponse: boolean;
13
+ }
14
+ /**
15
+ * Uniform Protocol 协议处理器
16
+ */
17
+ export declare class ProtocolHandler {
18
+ private parser;
19
+ private builder;
20
+ /**
21
+ * 构造函数
22
+ * @param options 协议处理选项
23
+ */
24
+ constructor();
25
+ /**
26
+ * 解析协议数据包
27
+ * @param buffer 原始数据缓冲区
28
+ * @returns 解析结果和请求ID
29
+ */
30
+ parse(buffer: Buffer): ProtocolParseResult;
31
+ /**
32
+ * 构建协议数据包
33
+ * @param data 业务数据(TLV2 的数据部分,通常是 JSON 对象)
34
+ * @param header TLV1 的头部数据(通常是 JSON 对象)
35
+ * @param options 构建选项(会与默认选项合并)
36
+ * @returns 构建好的数据包 Buffer
37
+ */
38
+ build(data: any, header?: {
39
+ seq: number;
40
+ sn: string;
41
+ source: string;
42
+ }, options?: {}): Buffer;
43
+ /**
44
+ * 生成 seqId
45
+ * @returns 序列ID(3字节)
46
+ */
47
+ generateSeqId(): number;
48
+ /**
49
+ * 从缓冲区中提取完整协议包(处理半包/粘包)
50
+ */
51
+ extractCompletePacket(buffer: Buffer): {
52
+ packet: Buffer;
53
+ remaining: Buffer | null;
54
+ } | null;
55
+ /**
56
+ * 从缓冲区中提取所有完整协议包(处理粘包)
57
+ */
58
+ extractAllCompletePackets(buffer: Buffer): {
59
+ packets: Buffer[];
60
+ remaining: Buffer | null;
61
+ invalidPackets: Buffer[];
62
+ };
63
+ }
64
+ //# sourceMappingURL=protocolHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocolHandler.d.ts","sourceRoot":"","sources":["../../../src/packages/tcp/protocolHandler.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,WAAW;IACX,IAAI,EAAE,GAAG,CAAC;IACV,WAAW;IACX,MAAM,EAAE,GAAG,CAAC;IACZ,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAAM;IAErB;;;OAGG;;IAMH;;;;OAIG;IACI,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB;IAuBjD;;;;;;OAMG;IACI,KAAK,CACX,IAAI,EAAE,GAAG,EACT,MAAM;;;;KAIL,EACD,OAAO,KAAK,GACV,MAAM;IAKT;;;OAGG;IACI,aAAa,IAAI,MAAM;IAI9B;;OAEG;IACI,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAYjG;;OAEG;IACI,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG;QACjD,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,cAAc,EAAE,MAAM,EAAE,CAAC;KACzB;CAUD"}
@@ -0,0 +1,96 @@
1
+ // ALL_CODE_LINE: 142
2
+ // AI_CODE_LINE: 1
3
+ /**
4
+ * Uniform Protocol 协议处理模块
5
+ * 封装协议解析、校验和构建功能
6
+ */
7
+ import { UniformProtocolParser } from '@unico/uniform-protocol-parser';
8
+ import { UniformProtocolBuilder } from '@unico/uniform-protocol-builder';
9
+ /**
10
+ * Uniform Protocol 协议处理器
11
+ */
12
+ export class ProtocolHandler {
13
+ parser;
14
+ builder;
15
+ /**
16
+ * 构造函数
17
+ * @param options 协议处理选项
18
+ */
19
+ constructor() {
20
+ this.parser = new UniformProtocolParser({ enableChecksum: false });
21
+ this.builder = new UniformProtocolBuilder();
22
+ }
23
+ /**
24
+ * 解析协议数据包
25
+ * @param buffer 原始数据缓冲区
26
+ * @returns 解析结果和请求ID
27
+ */
28
+ parse(buffer) {
29
+ try {
30
+ // parser.parse 内部需要 ArrayBuffer,需要转换
31
+ const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
32
+ const result = this.parser.parse(arrayBuffer);
33
+ return {
34
+ data: result.tlv2.jsonData !== undefined
35
+ ? result.tlv2.jsonData
36
+ : JSON.parse(new TextDecoder().decode(new Uint8Array(result.tlv2.data))),
37
+ header: result.tlv1.jsonData !== undefined
38
+ ? result.tlv1.jsonData
39
+ : JSON.parse(new TextDecoder().decode(new Uint8Array(result.tlv1.data))),
40
+ requestId: result.requestHead.seqId,
41
+ isResponse: result.requestHead.isResponse,
42
+ };
43
+ }
44
+ catch (error) {
45
+ throw error;
46
+ }
47
+ }
48
+ /**
49
+ * 构建协议数据包
50
+ * @param data 业务数据(TLV2 的数据部分,通常是 JSON 对象)
51
+ * @param header TLV1 的头部数据(通常是 JSON 对象)
52
+ * @param options 构建选项(会与默认选项合并)
53
+ * @returns 构建好的数据包 Buffer
54
+ */
55
+ build(data, header = {
56
+ seq: 0,
57
+ sn: '',
58
+ source: '',
59
+ }, options = {}) {
60
+ const result = this.builder.build(data, header, options);
61
+ return Buffer.from(result);
62
+ }
63
+ /**
64
+ * 生成 seqId
65
+ * @returns 序列ID(3字节)
66
+ */
67
+ generateSeqId() {
68
+ return this.builder.generateSeqId();
69
+ }
70
+ /**
71
+ * 从缓冲区中提取完整协议包(处理半包/粘包)
72
+ */
73
+ extractCompletePacket(buffer) {
74
+ const result = this.parser.extractCompletePacket(buffer);
75
+ if (!result) {
76
+ return null;
77
+ }
78
+ return {
79
+ packet: result.packet,
80
+ remaining: result.remaining || null,
81
+ };
82
+ }
83
+ /**
84
+ * 从缓冲区中提取所有完整协议包(处理粘包)
85
+ */
86
+ extractAllCompletePackets(buffer) {
87
+ const result = this.parser.extractAllCompletePackets(buffer);
88
+ // parser 已优化,直接返回 Buffer 数组
89
+ return {
90
+ packets: result.packets,
91
+ remaining: result.remaining || null,
92
+ invalidPackets: result.invalidPackets,
93
+ };
94
+ }
95
+ }
96
+ //# sourceMappingURL=protocolHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocolHandler.js","sourceRoot":"","sources":["../../../src/packages/tcp/protocolHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// ALL_CODE_LINE: 142\n// AI_CODE_LINE: 1\n\n/**\n * Uniform Protocol 协议处理模块\n * 封装协议解析、校验和构建功能\n */\n\nimport { UniformProtocolParser } from '@unico/uniform-protocol-parser';\nimport { UniformProtocolBuilder } from '@unico/uniform-protocol-builder';\n\n/**\n * 协议处理结果\n */\nexport interface ProtocolParseResult {\n\t/** 解析结果 */\n\tdata: any;\n\t/** 头部数据 */\n\theader: any;\n\t/** 请求ID(seqId) */\n\trequestId: number;\n\t/** 是否为响应包 */\n\tisResponse: boolean;\n}\n\n/**\n * Uniform Protocol 协议处理器\n */\nexport class ProtocolHandler {\n\tprivate parser: any;\n\tprivate builder: any;\n\n\t/**\n\t * 构造函数\n\t * @param options 协议处理选项\n\t */\n\tconstructor() {\n\t\tthis.parser = new UniformProtocolParser({ enableChecksum: false });\n\t\tthis.builder = new UniformProtocolBuilder();\n\t}\n\n\t/**\n\t * 解析协议数据包\n\t * @param buffer 原始数据缓冲区\n\t * @returns 解析结果和请求ID\n\t */\n\tpublic parse(buffer: Buffer): ProtocolParseResult {\n\t\ttry {\n\t\t\t// parser.parse 内部需要 ArrayBuffer,需要转换\n\t\t\tconst arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n\t\t\tconst result = this.parser.parse(arrayBuffer);\n\n\t\t\treturn {\n\t\t\t\tdata:\n\t\t\t\t\tresult.tlv2.jsonData !== undefined\n\t\t\t\t\t\t? result.tlv2.jsonData\n\t\t\t\t\t\t: JSON.parse(new TextDecoder().decode(new Uint8Array(result.tlv2.data))),\n\t\t\t\theader:\n\t\t\t\t\tresult.tlv1.jsonData !== undefined\n\t\t\t\t\t\t? result.tlv1.jsonData\n\t\t\t\t\t\t: JSON.parse(new TextDecoder().decode(new Uint8Array(result.tlv1.data))),\n\t\t\t\trequestId: result.requestHead.seqId,\n\t\t\t\tisResponse: result.requestHead.isResponse,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * 构建协议数据包\n\t * @param data 业务数据(TLV2 的数据部分,通常是 JSON 对象)\n\t * @param header TLV1 的头部数据(通常是 JSON 对象)\n\t * @param options 构建选项(会与默认选项合并)\n\t * @returns 构建好的数据包 Buffer\n\t */\n\tpublic build(\n\t\tdata: any,\n\t\theader = {\n\t\t\tseq: 0,\n\t\t\tsn: '',\n\t\t\tsource: '',\n\t\t},\n\t\toptions = {}\n\t): Buffer {\n\t\tconst result = this.builder.build(data, header, options);\n\t\treturn Buffer.from(result);\n\t}\n\n\t/**\n\t * 生成 seqId\n\t * @returns 序列ID(3字节)\n\t */\n\tpublic generateSeqId(): number {\n\t\treturn this.builder.generateSeqId();\n\t}\n\n\t/**\n\t * 从缓冲区中提取完整协议包(处理半包/粘包)\n\t */\n\tpublic extractCompletePacket(buffer: Buffer): { packet: Buffer; remaining: Buffer | null } | null {\n\t\tconst result = this.parser.extractCompletePacket(buffer);\n\n\t\tif (!result) {\n\t\t\treturn null;\n\t\t}\n\t\treturn {\n\t\t\tpacket: result.packet,\n\t\t\tremaining: result.remaining || null,\n\t\t};\n\t}\n\n\t/**\n\t * 从缓冲区中提取所有完整协议包(处理粘包)\n\t */\n\tpublic extractAllCompletePackets(buffer: Buffer): {\n\t\tpackets: Buffer[];\n\t\tremaining: Buffer | null;\n\t\tinvalidPackets: Buffer[];\n\t} {\n\t\tconst result = this.parser.extractAllCompletePackets(buffer);\n\n\t\t// parser 已优化,直接返回 Buffer 数组\n\t\treturn {\n\t\t\tpackets: result.packets,\n\t\t\tremaining: result.remaining || null,\n\t\t\tinvalidPackets: result.invalidPackets,\n\t\t};\n\t}\n}\n"]}