@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
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # PixelHue Event Control
2
+
3
+ Node service for PixelHue Event Control device.
4
+
5
+ ## 项目结构
6
+
7
+ ```
8
+ pixelhue-event-controller-sdk/
9
+ ├── src/ # 源代码目录
10
+ │ └── index.ts # 主文件
11
+ ├── dist/ # 编译输出目录 (自动生成)
12
+ ├── package.json
13
+ ├── tsconfig.json # TypeScript 配置
14
+ ├── jest.config.js # Jest 测试配置
15
+ ├── .eslintrc.js # ESLint 配置
16
+ ├── .prettierrc.json # Prettier 配置
17
+ ├── .editorconfig # 编辑器配置
18
+ ├── .npmrc # npm 配置
19
+ └── README.md
20
+ ```
21
+
22
+ ## 安装依赖
23
+
24
+ ```bash
25
+ yarn install
26
+ # 或简写
27
+ yarn
28
+ ```
29
+
30
+ ## 开发
31
+
32
+ ```bash
33
+ yarn start:dev
34
+
35
+ yarn dev
36
+
37
+ yarn build
38
+
39
+ yarn start
40
+ ```
41
+
42
+ ## 测试
43
+
44
+ ```bash
45
+ # 运行测试
46
+ yarn test
47
+
48
+ # 监听模式测试
49
+ yarn test:watch
50
+
51
+ # 生成覆盖率报告
52
+ yarn test:coverage
53
+ ```
54
+
55
+ ## 技术栈
56
+
57
+ - **TypeScript**: 类型安全的 JavaScript
58
+ - **ES Modules**: 现代模块系统
59
+ - **Jest**: 测试框架
60
+ - **ESLint**: 代码检查
61
+ - **Prettier**: 代码格式化
62
+
63
+ ## 配置文件说明
64
+
65
+ 项目包含以下配置文件,参考了 umi/dumi 项目的最佳实践:
66
+
67
+ - `.editorconfig`: 统一编辑器配置(缩进、换行符等)
68
+ - `.gitattributes`: Git 文件处理配置(文本/二进制文件处理)
69
+ - `.npmrc`: npm 配置(可配置 registry、lockfile 版本等)
70
+ - `jest.config.js`: Jest 测试框架配置
71
+ - `.eslintrc.js`: ESLint 代码检查配置
72
+ - `.prettierrc.json`: Prettier 代码格式化配置
73
+
74
+ ## License
75
+
76
+ MIT
@@ -0,0 +1,17 @@
1
+ /**
2
+ * PixelHue U5 Mini Event Controller SDK
3
+ *
4
+ * 提供 PixelHue U5 Mini 设备的连接管理、设备发现、协议处理等功能
5
+ */
6
+ export { MiniConnectionManager } from './packages/tcp/ConnectManager.js';
7
+ export { MiniDiscoveryService } from './packages/tcp/discoveryService.js';
8
+ export { ProtocolHandler } from './packages/tcp/protocolHandler.js';
9
+ export { QueueManager, DEFAULT_QUEUE_OPTIONS } from './packages/tcp/QueueManager.js';
10
+ export { SocketWrapper } from './packages/tcp/SocketWrapper.js';
11
+ export { TcpWrapper } from './packages/tcp/tcpWrapper.js';
12
+ export type { SocketConnectOptions, QueueOptions, QueueMetrics, ProtocolHandlerOptions, ProtocolSendPayload, SocketWrapperEvents, ISocketWrapper, ResponseQueueItem, SocketWrapperInstance, RequestIdExtractor, HeartbeatBuilder, HeartbeatResponseChecker, ConnectionManagerEvents, DiscoveryEvents, MiniDeviceInfo, MiniDiscoveredDevice, DiscoveryServiceOptions, } from './packages/tcp/types.js';
13
+ export { QueueOverflowStrategy } from './packages/tcp/types.js';
14
+ export { SUPPORTED_MODELS, DEFAULT_TCP_PORT, DEFAULT_ENABLE_HEARTBEAT, DEFAULT_AUTO_RECONNECT, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_RECONNECT_INTERVAL, DEFAULT_HEARTBEAT_TIMEOUT, DEFAULT_MAX_RECONNECT_INTERVAL, DEFAULT_ENABLE_EXPONENTIAL_BACKOFF, DEFAULT_MDNS_QUERY_INTERVAL, DEFAULT_MAX_RECONNECT_ATTEMPTS, } from './packages/tcp/constants.js';
15
+ export { parseHost, parsePort } from './packages/tcp/utils.js';
16
+ export type { ProtocolParseResult } from './packages/tcp/protocolHandler.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAG1D,YAAY,EACX,oBAAoB,EACpB,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,uBAAuB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,0BAA0B,EAC1B,yBAAyB,EACzB,8BAA8B,EAC9B,kCAAkC,EAClC,2BAA2B,EAC3B,8BAA8B,GAC9B,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAG/D,YAAY,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * PixelHue U5 Mini Event Controller SDK
3
+ *
4
+ * 提供 PixelHue U5 Mini 设备的连接管理、设备发现、协议处理等功能
5
+ */
6
+ // 主要类导出
7
+ export { MiniConnectionManager } from './packages/tcp/ConnectManager.js';
8
+ export { MiniDiscoveryService } from './packages/tcp/discoveryService.js';
9
+ export { ProtocolHandler } from './packages/tcp/protocolHandler.js';
10
+ export { QueueManager, DEFAULT_QUEUE_OPTIONS } from './packages/tcp/QueueManager.js';
11
+ export { SocketWrapper } from './packages/tcp/SocketWrapper.js';
12
+ export { TcpWrapper } from './packages/tcp/tcpWrapper.js';
13
+ export { QueueOverflowStrategy } from './packages/tcp/types.js';
14
+ // 常量导出
15
+ export { SUPPORTED_MODELS, DEFAULT_TCP_PORT, DEFAULT_ENABLE_HEARTBEAT, DEFAULT_AUTO_RECONNECT, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_RECONNECT_INTERVAL, DEFAULT_HEARTBEAT_TIMEOUT, DEFAULT_MAX_RECONNECT_INTERVAL, DEFAULT_ENABLE_EXPONENTIAL_BACKOFF, DEFAULT_MDNS_QUERY_INTERVAL, DEFAULT_MAX_RECONNECT_ATTEMPTS, } from './packages/tcp/constants.js';
16
+ // 工具函数导出
17
+ export { parseHost, parsePort } from './packages/tcp/utils.js';
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAuB1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO;AACP,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,0BAA0B,EAC1B,yBAAyB,EACzB,8BAA8B,EAC9B,kCAAkC,EAClC,2BAA2B,EAC3B,8BAA8B,GAC9B,MAAM,6BAA6B,CAAC;AAErC,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC","sourcesContent":["/**\r\n * PixelHue U5 Mini Event Controller SDK\r\n *\r\n * 提供 PixelHue U5 Mini 设备的连接管理、设备发现、协议处理等功能\r\n */\r\n\r\n// 主要类导出\r\nexport { MiniConnectionManager } from './packages/tcp/ConnectManager.js';\r\nexport { MiniDiscoveryService } from './packages/tcp/discoveryService.js';\r\nexport { ProtocolHandler } from './packages/tcp/protocolHandler.js';\r\nexport { QueueManager, DEFAULT_QUEUE_OPTIONS } from './packages/tcp/QueueManager.js';\r\nexport { SocketWrapper } from './packages/tcp/SocketWrapper.js';\r\nexport { TcpWrapper } from './packages/tcp/tcpWrapper.js';\r\n\r\n// 类型导出\r\nexport type {\r\n\tSocketConnectOptions,\r\n\tQueueOptions,\r\n\tQueueMetrics,\r\n\tProtocolHandlerOptions,\r\n\tProtocolSendPayload,\r\n\tSocketWrapperEvents,\r\n\tISocketWrapper,\r\n\tResponseQueueItem,\r\n\tSocketWrapperInstance,\r\n\tRequestIdExtractor,\r\n\tHeartbeatBuilder,\r\n\tHeartbeatResponseChecker,\r\n\tConnectionManagerEvents,\r\n\tDiscoveryEvents,\r\n\tMiniDeviceInfo,\r\n\tMiniDiscoveredDevice,\r\n\tDiscoveryServiceOptions,\r\n} from './packages/tcp/types.js';\r\n\r\nexport { QueueOverflowStrategy } from './packages/tcp/types.js';\r\n\r\n// 常量导出\r\nexport {\r\n\tSUPPORTED_MODELS,\r\n\tDEFAULT_TCP_PORT,\r\n\tDEFAULT_ENABLE_HEARTBEAT,\r\n\tDEFAULT_AUTO_RECONNECT,\r\n\tDEFAULT_HEARTBEAT_INTERVAL,\r\n\tDEFAULT_RECONNECT_INTERVAL,\r\n\tDEFAULT_HEARTBEAT_TIMEOUT,\r\n\tDEFAULT_MAX_RECONNECT_INTERVAL,\r\n\tDEFAULT_ENABLE_EXPONENTIAL_BACKOFF,\r\n\tDEFAULT_MDNS_QUERY_INTERVAL,\r\n\tDEFAULT_MAX_RECONNECT_ATTEMPTS,\r\n} from './packages/tcp/constants.js';\r\n\r\n// 工具函数导出\r\nexport { parseHost, parsePort } from './packages/tcp/utils.js';\r\n\r\n// 协议处理结果类型导出\r\nexport type { ProtocolParseResult } from './packages/tcp/protocolHandler.js';\r\n"]}
@@ -0,0 +1,66 @@
1
+ import { EventEmitter } from 'eventemitter3';
2
+ import type { ConnectionManagerEvents, SocketConnectOptions } from './types.js';
3
+ import { SocketWrapper } from './SocketWrapper.js';
4
+ /**
5
+ * MINI连接管理器
6
+ */
7
+ export declare class MiniConnectionManager extends EventEmitter<ConnectionManagerEvents> {
8
+ /** 连接池 - 每个地址只维护一个连接 */
9
+ private readonly connections;
10
+ /** 默认连接选项 */
11
+ private readonly defaultOptions;
12
+ /** 标记是否正在销毁,避免销毁过程中触发自动重连 */
13
+ private isShuttingDown;
14
+ constructor(options?: Partial<SocketConnectOptions>);
15
+ /**
16
+ * 连接到指定设备
17
+ * @param address 设备地址
18
+ * @param port 设备端口
19
+ * @param options 连接选项
20
+ * @returns Promise<SocketWrapper> Socket包装器
21
+ */
22
+ connectTo(address: string, port?: number, options?: Partial<SocketConnectOptions>): Promise<SocketWrapper>;
23
+ /**
24
+ * 断开指定设备的连接
25
+ * @param address 设备地址
26
+ * @param port 设备端口
27
+ */
28
+ disconnectFrom(address: string, port?: number, options?: {
29
+ autoReconnect?: boolean;
30
+ }): Promise<void>;
31
+ /**
32
+ * 断开所有连接
33
+ */
34
+ disconnectAll(): void;
35
+ /**
36
+ * 生成连接ID
37
+ * @param address 设备地址
38
+ * @param port 设备端口
39
+ * @returns string
40
+ */
41
+ private getConnectionId;
42
+ /**
43
+ * 设置Socket事件处理器
44
+ * @param socketWrapper Socket包装器
45
+ * @param connectionId 连接ID
46
+ */
47
+ private setupSocketEventHandlers;
48
+ /**
49
+ * 在发起新连接前,断开现有的其他设备连接,确保同一时间仅保留一个MINI设备连接
50
+ * 仅当目标设备与已连接设备的地址不同(不同MINI)时,才会执行挤占
51
+ * @param activeConnectionId 新的连接ID
52
+ * @param activeAddress 新设备的地址
53
+ */
54
+ private preemptOtherConnections;
55
+ /**
56
+ * 判断是否为内置设备(127.0.0.1)
57
+ * @param address 设备地址
58
+ * @returns boolean
59
+ */
60
+ private isBuiltInDevice;
61
+ /**
62
+ * 销毁连接管理器
63
+ */
64
+ destroy(): Promise<void>;
65
+ }
66
+ //# sourceMappingURL=ConnectManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectManager.d.ts","sourceRoot":"","sources":["../../../src/packages/tcp/ConnectManager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY,CAAC,uBAAuB,CAAC;IAC/E,wBAAwB;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAEhE,aAAa;IACb,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgC;IAE/D,6BAA6B;IAC7B,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC;IAMnD;;;;;;OAMG;IACG,SAAS,CACd,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAyB,EAC/B,OAAO,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACrC,OAAO,CAAC,aAAa,CAAC;IA4CzB;;;;OAIG;IACG,cAAc,CACnB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAyB,EAC/B,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC,OAAO,CAAC,IAAI,CAAC;IAqBhB;;OAEG;IACH,aAAa,IAAI,IAAI;IAQrB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAIvB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IA+BhC;;;;;OAKG;YACW,uBAAuB;IA4BrC;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9B"}
@@ -0,0 +1,176 @@
1
+ // ALL_CODE_LINE: 330
2
+ // AI_CODE_LINE: 151
3
+ import { EventEmitter } from 'eventemitter3';
4
+ import { DEFAULT_TCP_PORT } from './constants.js';
5
+ import { SocketWrapper } from './SocketWrapper.js';
6
+ /**
7
+ * MINI连接管理器
8
+ */
9
+ export class MiniConnectionManager extends EventEmitter {
10
+ /** 连接池 - 每个地址只维护一个连接 */
11
+ connections = new Map();
12
+ /** 默认连接选项 */
13
+ defaultOptions;
14
+ /** 标记是否正在销毁,避免销毁过程中触发自动重连 */
15
+ isShuttingDown = false;
16
+ constructor(options) {
17
+ super();
18
+ this.defaultOptions = { ...options };
19
+ }
20
+ /**
21
+ * 连接到指定设备
22
+ * @param address 设备地址
23
+ * @param port 设备端口
24
+ * @param options 连接选项
25
+ * @returns Promise<SocketWrapper> Socket包装器
26
+ */
27
+ async connectTo(address, port = DEFAULT_TCP_PORT, options) {
28
+ const connectionId = this.getConnectionId(address, port);
29
+ // 检查是否已存在连接(可能是自己之前的连接)
30
+ if (this.connections.has(connectionId)) {
31
+ const existingSocketWrapper = this.connections.get(connectionId);
32
+ if (existingSocketWrapper) {
33
+ return existingSocketWrapper;
34
+ }
35
+ }
36
+ // 在建立新连接前,主动断开其他设备的连接,实现后来的连接挤占前一个连接
37
+ await this.preemptOtherConnections(connectionId, address);
38
+ // 创建Socket包装器
39
+ const mergedOptions = {
40
+ ...this.defaultOptions,
41
+ ...options,
42
+ };
43
+ if (this.isBuiltInDevice(address)) {
44
+ mergedOptions.autoReconnect = true;
45
+ mergedOptions.maxReconnectAttempts = 0;
46
+ }
47
+ // 合并选项,直接使用 SocketConnectOptions
48
+ const socketOptions = {
49
+ ...mergedOptions,
50
+ };
51
+ const socketWrapper = new SocketWrapper();
52
+ // 存储连接(在连接前存储,确保事件触发时可获取)
53
+ this.connections.set(connectionId, socketWrapper);
54
+ // 设置事件监听
55
+ this.setupSocketEventHandlers(socketWrapper, connectionId);
56
+ socketWrapper.connect(address, port, socketOptions);
57
+ // 返回 SocketWrapper 实例
58
+ return socketWrapper;
59
+ }
60
+ /**
61
+ * 断开指定设备的连接
62
+ * @param address 设备地址
63
+ * @param port 设备端口
64
+ */
65
+ async disconnectFrom(address, port = DEFAULT_TCP_PORT, options) {
66
+ const _autoReconnect = options?.autoReconnect ?? false;
67
+ // UI 场景下不允许删除内置设备连接,除非明确要求关闭自动重连
68
+ if (!this.isShuttingDown && !_autoReconnect && this.isBuiltInDevice(address)) {
69
+ return;
70
+ }
71
+ const connectionId = this.getConnectionId(address, port);
72
+ const socketWrapper = this.connections.get(connectionId);
73
+ if (socketWrapper) {
74
+ await socketWrapper.disconnect();
75
+ this.connections.delete(connectionId);
76
+ // 触发断开事件,传递 SocketWrapper
77
+ this.emit('disconnected', socketWrapper);
78
+ }
79
+ }
80
+ /**
81
+ * 断开所有连接
82
+ */
83
+ disconnectAll() {
84
+ for (const [, socketWrapper] of this.connections) {
85
+ socketWrapper.disconnect();
86
+ }
87
+ this.connections.clear();
88
+ }
89
+ /**
90
+ * 生成连接ID
91
+ * @param address 设备地址
92
+ * @param port 设备端口
93
+ * @returns string
94
+ */
95
+ getConnectionId(address, port) {
96
+ return `${address}:${port}`;
97
+ }
98
+ /**
99
+ * 设置Socket事件处理器
100
+ * @param socketWrapper Socket包装器
101
+ * @param connectionId 连接ID
102
+ */
103
+ setupSocketEventHandlers(socketWrapper, connectionId) {
104
+ const tcpWrapper = socketWrapper.getTcpWrapper();
105
+ socketWrapper.on('connected', () => {
106
+ // 触发连接事件(包括首次连接和自动重连),传递 SocketWrapper
107
+ this.emit('connected', socketWrapper);
108
+ });
109
+ socketWrapper.on('disconnected', () => {
110
+ this.emit('disconnected', socketWrapper);
111
+ // 内置设备:保留实例等待自动重连
112
+ // 外置设备:删除实例(即使显式启用了自动重连,被动断连时也应该删除)
113
+ const address = tcpWrapper.getAddress();
114
+ if (address && !this.isBuiltInDevice(address)) {
115
+ this.connections.delete(connectionId);
116
+ }
117
+ });
118
+ socketWrapper.on('error', (error) => {
119
+ const errorMessage = error instanceof Error ? error.message : String(error);
120
+ this.emit('error', errorMessage, socketWrapper);
121
+ // 如果重连失败(达到最大次数)或非内置设备连接失败,清理实例
122
+ // 对于首次连接失败,如果不是内置设备,也会清理
123
+ const address = tcpWrapper.getAddress();
124
+ if (address && !this.isBuiltInDevice(address)) {
125
+ this.connections.delete(connectionId);
126
+ }
127
+ });
128
+ }
129
+ /**
130
+ * 在发起新连接前,断开现有的其他设备连接,确保同一时间仅保留一个MINI设备连接
131
+ * 仅当目标设备与已连接设备的地址不同(不同MINI)时,才会执行挤占
132
+ * @param activeConnectionId 新的连接ID
133
+ * @param activeAddress 新设备的地址
134
+ */
135
+ async preemptOtherConnections(activeConnectionId, activeAddress) {
136
+ const targets = Array.from(this.connections.entries()).filter(([connectionId, socketWrapper]) => {
137
+ const tcpWrapper = socketWrapper.getTcpWrapper();
138
+ const address = tcpWrapper.getAddress();
139
+ return connectionId !== activeConnectionId && address !== null && address !== activeAddress;
140
+ });
141
+ if (targets.length === 0) {
142
+ return;
143
+ }
144
+ await Promise.all(targets.map(async ([, socketWrapper]) => {
145
+ const tcpWrapper = socketWrapper.getTcpWrapper();
146
+ const address = tcpWrapper.getAddress();
147
+ const port = tcpWrapper.getPort();
148
+ if (address && port) {
149
+ try {
150
+ await this.disconnectFrom(address, port, { autoReconnect: true });
151
+ }
152
+ catch (error) {
153
+ const message = error instanceof Error ? error.message : 'Unknown error';
154
+ this.emit('error', `主动断开旧连接失败(${address}:${port}): ${message}`);
155
+ }
156
+ }
157
+ }));
158
+ }
159
+ /**
160
+ * 判断是否为内置设备(127.0.0.1)
161
+ * @param address 设备地址
162
+ * @returns boolean
163
+ */
164
+ isBuiltInDevice(address) {
165
+ return address === '127.0.0.1' || address === '::1' || address === 'localhost';
166
+ }
167
+ /**
168
+ * 销毁连接管理器
169
+ */
170
+ async destroy() {
171
+ this.isShuttingDown = true;
172
+ await this.disconnectAll();
173
+ this.removeAllListeners();
174
+ }
175
+ }
176
+ //# sourceMappingURL=ConnectManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectManager.js","sourceRoot":"","sources":["../../../src/packages/tcp/ConnectManager.ts"],"names":[],"mappings":"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","sourcesContent":["// ALL_CODE_LINE: 330\r\n// AI_CODE_LINE: 151\r\n\r\nimport { EventEmitter } from 'eventemitter3';\r\nimport type { ConnectionManagerEvents, SocketConnectOptions } from './types.js';\r\nimport { DEFAULT_TCP_PORT } from './constants.js';\r\nimport { SocketWrapper } from './SocketWrapper.js';\r\n\r\n/**\r\n * MINI连接管理器\r\n */\r\nexport class MiniConnectionManager extends EventEmitter<ConnectionManagerEvents> {\r\n\t/** 连接池 - 每个地址只维护一个连接 */\r\n\tprivate readonly connections = new Map<string, SocketWrapper>();\r\n\r\n\t/** 默认连接选项 */\r\n\tprivate readonly defaultOptions: Partial<SocketConnectOptions>;\r\n\r\n\t/** 标记是否正在销毁,避免销毁过程中触发自动重连 */\r\n\tprivate isShuttingDown = false;\r\n\r\n\tconstructor(options?: Partial<SocketConnectOptions>) {\r\n\t\tsuper();\r\n\r\n\t\tthis.defaultOptions = { ...options };\r\n\t}\r\n\r\n\t/**\r\n\t * 连接到指定设备\r\n\t * @param address 设备地址\r\n\t * @param port 设备端口\r\n\t * @param options 连接选项\r\n\t * @returns Promise<SocketWrapper> Socket包装器\r\n\t */\r\n\tasync connectTo(\r\n\t\taddress: string,\r\n\t\tport: number = DEFAULT_TCP_PORT,\r\n\t\toptions?: Partial<SocketConnectOptions>\r\n\t): Promise<SocketWrapper> {\r\n\t\tconst connectionId = this.getConnectionId(address, port);\r\n\r\n\t\t// 检查是否已存在连接(可能是自己之前的连接)\r\n\t\tif (this.connections.has(connectionId)) {\r\n\t\t\tconst existingSocketWrapper = this.connections.get(connectionId);\r\n\t\t\tif (existingSocketWrapper) {\r\n\t\t\t\treturn existingSocketWrapper;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// 在建立新连接前,主动断开其他设备的连接,实现后来的连接挤占前一个连接\r\n\t\tawait this.preemptOtherConnections(connectionId, address);\r\n\r\n\t\t// 创建Socket包装器\r\n\t\tconst mergedOptions = {\r\n\t\t\t...this.defaultOptions,\r\n\t\t\t...options,\r\n\t\t};\r\n\r\n\t\tif (this.isBuiltInDevice(address)) {\r\n\t\t\tmergedOptions.autoReconnect = true;\r\n\t\t\tmergedOptions.maxReconnectAttempts = 0;\r\n\t\t}\r\n\r\n\t\t// 合并选项,直接使用 SocketConnectOptions\r\n\t\tconst socketOptions: SocketConnectOptions = {\r\n\t\t\t...mergedOptions,\r\n\t\t};\r\n\r\n\t\tconst socketWrapper = new SocketWrapper();\r\n\r\n\t\t// 存储连接(在连接前存储,确保事件触发时可获取)\r\n\t\tthis.connections.set(connectionId, socketWrapper);\r\n\r\n\t\t// 设置事件监听\r\n\t\tthis.setupSocketEventHandlers(socketWrapper, connectionId);\r\n\r\n\t\tsocketWrapper.connect(address, port, socketOptions);\r\n\r\n\t\t// 返回 SocketWrapper 实例\r\n\t\treturn socketWrapper;\r\n\t}\r\n\r\n\t/**\r\n\t * 断开指定设备的连接\r\n\t * @param address 设备地址\r\n\t * @param port 设备端口\r\n\t */\r\n\tasync disconnectFrom(\r\n\t\taddress: string,\r\n\t\tport: number = DEFAULT_TCP_PORT,\r\n\t\toptions?: { autoReconnect?: boolean }\r\n\t): Promise<void> {\r\n\t\tconst _autoReconnect = options?.autoReconnect ?? false;\r\n\r\n\t\t// UI 场景下不允许删除内置设备连接,除非明确要求关闭自动重连\r\n\t\tif (!this.isShuttingDown && !_autoReconnect && this.isBuiltInDevice(address)) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst connectionId = this.getConnectionId(address, port);\r\n\r\n\t\tconst socketWrapper = this.connections.get(connectionId);\r\n\r\n\t\tif (socketWrapper) {\r\n\t\t\tawait socketWrapper.disconnect();\r\n\t\t\tthis.connections.delete(connectionId);\r\n\r\n\t\t\t// 触发断开事件,传递 SocketWrapper\r\n\t\t\tthis.emit('disconnected', socketWrapper);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 断开所有连接\r\n\t */\r\n\tdisconnectAll(): void {\r\n\t\tfor (const [, socketWrapper] of this.connections) {\r\n\t\t\tsocketWrapper.disconnect();\r\n\t\t}\r\n\r\n\t\tthis.connections.clear();\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 * @returns string\r\n\t */\r\n\tprivate getConnectionId(address: string, port: number): string {\r\n\t\treturn `${address}:${port}`;\r\n\t}\r\n\r\n\t/**\r\n\t * 设置Socket事件处理器\r\n\t * @param socketWrapper Socket包装器\r\n\t * @param connectionId 连接ID\r\n\t */\r\n\tprivate setupSocketEventHandlers(socketWrapper: SocketWrapper, connectionId: string): void {\r\n\t\tconst tcpWrapper = socketWrapper.getTcpWrapper();\r\n\t\tsocketWrapper.on('connected', () => {\r\n\t\t\t// 触发连接事件(包括首次连接和自动重连),传递 SocketWrapper\r\n\t\t\tthis.emit('connected', socketWrapper);\r\n\t\t});\r\n\r\n\t\tsocketWrapper.on('disconnected', () => {\r\n\t\t\tthis.emit('disconnected', socketWrapper);\r\n\r\n\t\t\t// 内置设备:保留实例等待自动重连\r\n\t\t\t// 外置设备:删除实例(即使显式启用了自动重连,被动断连时也应该删除)\r\n\t\t\tconst address = tcpWrapper.getAddress();\r\n\t\t\tif (address && !this.isBuiltInDevice(address)) {\r\n\t\t\t\tthis.connections.delete(connectionId);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tsocketWrapper.on('error', (error: Error) => {\r\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\r\n\t\t\tthis.emit('error', errorMessage, socketWrapper);\r\n\r\n\t\t\t// 如果重连失败(达到最大次数)或非内置设备连接失败,清理实例\r\n\t\t\t// 对于首次连接失败,如果不是内置设备,也会清理\r\n\t\t\tconst address = tcpWrapper.getAddress();\r\n\t\t\tif (address && !this.isBuiltInDevice(address)) {\r\n\t\t\t\tthis.connections.delete(connectionId);\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * 在发起新连接前,断开现有的其他设备连接,确保同一时间仅保留一个MINI设备连接\r\n\t * 仅当目标设备与已连接设备的地址不同(不同MINI)时,才会执行挤占\r\n\t * @param activeConnectionId 新的连接ID\r\n\t * @param activeAddress 新设备的地址\r\n\t */\r\n\tprivate async preemptOtherConnections(activeConnectionId: string, activeAddress: string): Promise<void> {\r\n\t\tconst targets = Array.from(this.connections.entries()).filter(([connectionId, socketWrapper]) => {\r\n\t\t\tconst tcpWrapper = socketWrapper.getTcpWrapper();\r\n\t\t\tconst address = tcpWrapper.getAddress();\r\n\t\t\treturn connectionId !== activeConnectionId && address !== null && address !== activeAddress;\r\n\t\t});\r\n\r\n\t\tif (targets.length === 0) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tawait Promise.all(\r\n\t\t\ttargets.map(async ([, socketWrapper]) => {\r\n\t\t\t\tconst tcpWrapper = socketWrapper.getTcpWrapper();\r\n\t\t\t\tconst address = tcpWrapper.getAddress();\r\n\t\t\t\tconst port = tcpWrapper.getPort();\r\n\t\t\t\tif (address && port) {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tawait this.disconnectFrom(address, port, { autoReconnect: true });\r\n\t\t\t\t\t} catch (error) {\r\n\t\t\t\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error';\r\n\t\t\t\t\t\tthis.emit('error', `主动断开旧连接失败(${address}:${port}): ${message}`);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * 判断是否为内置设备(127.0.0.1)\r\n\t * @param address 设备地址\r\n\t * @returns boolean\r\n\t */\r\n\tprivate isBuiltInDevice(address: string): boolean {\r\n\t\treturn address === '127.0.0.1' || address === '::1' || address === 'localhost';\r\n\t}\r\n\r\n\t/**\r\n\t * 销毁连接管理器\r\n\t */\r\n\tasync destroy(): Promise<void> {\r\n\t\tthis.isShuttingDown = true;\r\n\t\tawait this.disconnectAll();\r\n\t\tthis.removeAllListeners();\r\n\t}\r\n}\r\n"]}
@@ -0,0 +1,41 @@
1
+ import type { QueueOptions } from './types.js';
2
+ /**
3
+ * 默认队列配置
4
+ */
5
+ export declare const DEFAULT_QUEUE_OPTIONS: Required<QueueOptions>;
6
+ /**
7
+ * 队列管理器
8
+ * 负责处理数据队列、去重等功能
9
+ */
10
+ export declare class QueueManager {
11
+ private queue;
12
+ private queueOptions;
13
+ private processedRequestIds;
14
+ constructor(queueOptions: Required<QueueOptions>);
15
+ /**
16
+ * 添加任务到队列
17
+ * @param requestId 请求ID
18
+ * @param data 数据
19
+ * @param processor 处理函数
20
+ * @param onError 错误处理函数
21
+ * @param cachedParseResult 已解析的结果(可选,用于避免重复解析)
22
+ */
23
+ addToQueue(requestId: number, data: Buffer, processor: (requestId: number, data: Buffer, cachedParseResult?: any) => Promise<void>, onError?: (error: Error, requestId: number) => void, cachedParseResult?: any): void;
24
+ /**
25
+ * 检查是否可以添加到队列
26
+ */
27
+ canAdd(): boolean;
28
+ /**
29
+ * 检查是否已处理过(去重)
30
+ */
31
+ isProcessed(requestId: number): boolean;
32
+ /**
33
+ * 处理队列溢出
34
+ */
35
+ handleOverflow(requestId: number, data: Buffer, processor: (requestId: number, data: Buffer, cachedParseResult?: any) => Promise<void>, onError: (error: Error, requestId: number) => void, cachedParseResult?: any): void;
36
+ /**
37
+ * 清空队列
38
+ */
39
+ clear(): void;
40
+ }
41
+ //# sourceMappingURL=QueueManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueueManager.d.ts","sourceRoot":"","sources":["../../../src/packages/tcp/QueueManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,YAAY,CAOxD,CAAC;AAEF;;;GAGG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,mBAAmB,CAA0B;gBAEzC,YAAY,EAAE,QAAQ,CAAC,YAAY,CAAC;IAUhD;;;;;;;OAOG;IACH,UAAU,CACT,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,EACtF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,EACnD,iBAAiB,CAAC,EAAE,GAAG,GACrB,IAAI;IAuBP;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIvC;;OAEG;IACH,cAAc,CACb,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,EACtF,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,EAClD,iBAAiB,CAAC,EAAE,GAAG,GACrB,IAAI;IAkBP;;OAEG;IACH,KAAK,IAAI,IAAI;CAIb"}
@@ -0,0 +1,102 @@
1
+ // ALL_CODE_LINE: 120
2
+ // AI_CODE_LINE: 2
3
+ import PQueue from 'p-queue';
4
+ import { QueueOverflowStrategy } from './types.js';
5
+ /**
6
+ * 默认队列配置
7
+ */
8
+ export const DEFAULT_QUEUE_OPTIONS = {
9
+ /** 最大队列长度,默认 1000 */
10
+ maxLength: 1000,
11
+ /** 溢出策略*/
12
+ overflowStrategy: QueueOverflowStrategy.DROP_OLDEST,
13
+ /** 单条数据处理超时时间(毫秒),默认 5000 */
14
+ processTimeout: 5000,
15
+ };
16
+ /**
17
+ * 队列管理器
18
+ * 负责处理数据队列、去重等功能
19
+ */
20
+ export class QueueManager {
21
+ queue;
22
+ queueOptions;
23
+ processedRequestIds = new Set();
24
+ constructor(queueOptions) {
25
+ this.queueOptions = queueOptions;
26
+ // 初始化 p-queue
27
+ this.queue = new PQueue({
28
+ concurrency: 1, // 串行处理,保证顺序
29
+ timeout: this.queueOptions.processTimeout,
30
+ });
31
+ }
32
+ /**
33
+ * 添加任务到队列
34
+ * @param requestId 请求ID
35
+ * @param data 数据
36
+ * @param processor 处理函数
37
+ * @param onError 错误处理函数
38
+ * @param cachedParseResult 已解析的结果(可选,用于避免重复解析)
39
+ */
40
+ addToQueue(requestId, data, processor, onError, cachedParseResult) {
41
+ this.queue
42
+ .add(async () => {
43
+ try {
44
+ // 处理队列项
45
+ await processor(requestId, data, cachedParseResult);
46
+ // 去重标记
47
+ this.processedRequestIds.add(requestId);
48
+ // 限制去重集合大小,防止内存泄漏
49
+ if (this.processedRequestIds.size > this.queueOptions.maxLength * 2) {
50
+ this.processedRequestIds.clear();
51
+ }
52
+ }
53
+ catch (error) {
54
+ throw error; // 重新抛出,让 p-queue 处理
55
+ }
56
+ })
57
+ .catch((error) => {
58
+ // p-queue 的超时错误会在这里捕获
59
+ onError?.(error, requestId);
60
+ });
61
+ }
62
+ /**
63
+ * 检查是否可以添加到队列
64
+ */
65
+ canAdd() {
66
+ return this.queue.size < this.queueOptions.maxLength;
67
+ }
68
+ /**
69
+ * 检查是否已处理过(去重)
70
+ */
71
+ isProcessed(requestId) {
72
+ return this.processedRequestIds.has(requestId);
73
+ }
74
+ /**
75
+ * 处理队列溢出
76
+ */
77
+ handleOverflow(requestId, data, processor, onError, cachedParseResult) {
78
+ switch (this.queueOptions.overflowStrategy) {
79
+ case QueueOverflowStrategy.DROP_NEWEST:
80
+ // 丢弃最新数据
81
+ break;
82
+ case QueueOverflowStrategy.REJECT:
83
+ // 拒绝新数据并触发错误
84
+ const error = new Error(`Queue overflow: max length ${this.queueOptions.maxLength} reached`);
85
+ onError(error, requestId);
86
+ break;
87
+ case QueueOverflowStrategy.DROP_OLDEST:
88
+ default:
89
+ // p-queue 不支持直接丢弃最旧的任务
90
+ this.addToQueue(requestId, data, processor, onError, cachedParseResult);
91
+ break;
92
+ }
93
+ }
94
+ /**
95
+ * 清空队列
96
+ */
97
+ clear() {
98
+ this.queue.clear();
99
+ this.processedRequestIds.clear();
100
+ }
101
+ }
102
+ //# sourceMappingURL=QueueManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueueManager.js","sourceRoot":"","sources":["../../../src/packages/tcp/QueueManager.ts"],"names":[],"mappings":"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","sourcesContent":["// ALL_CODE_LINE: 120\r\n// AI_CODE_LINE: 2\r\nimport PQueue from 'p-queue';\r\nimport type { QueueOptions } from './types.js';\r\nimport { QueueOverflowStrategy } from './types.js';\r\n\r\n/**\r\n * 默认队列配置\r\n */\r\nexport const DEFAULT_QUEUE_OPTIONS: Required<QueueOptions> = {\r\n\t/** 最大队列长度,默认 1000 */\r\n\tmaxLength: 1000,\r\n\t/** 溢出策略*/\r\n\toverflowStrategy: QueueOverflowStrategy.DROP_OLDEST,\r\n\t/** 单条数据处理超时时间(毫秒),默认 5000 */\r\n\tprocessTimeout: 5000,\r\n};\r\n\r\n/**\r\n * 队列管理器\r\n * 负责处理数据队列、去重等功能\r\n */\r\nexport class QueueManager {\r\n\tprivate queue: PQueue;\r\n\tprivate queueOptions: Required<QueueOptions>;\r\n\tprivate processedRequestIds: Set<number> = new Set();\r\n\r\n\tconstructor(queueOptions: Required<QueueOptions>) {\r\n\t\tthis.queueOptions = queueOptions;\r\n\r\n\t\t// 初始化 p-queue\r\n\t\tthis.queue = new PQueue({\r\n\t\t\tconcurrency: 1, // 串行处理,保证顺序\r\n\t\t\ttimeout: this.queueOptions.processTimeout,\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * 添加任务到队列\r\n\t * @param requestId 请求ID\r\n\t * @param data 数据\r\n\t * @param processor 处理函数\r\n\t * @param onError 错误处理函数\r\n\t * @param cachedParseResult 已解析的结果(可选,用于避免重复解析)\r\n\t */\r\n\taddToQueue(\r\n\t\trequestId: number,\r\n\t\tdata: Buffer,\r\n\t\tprocessor: (requestId: number, data: Buffer, cachedParseResult?: any) => Promise<void>,\r\n\t\tonError?: (error: Error, requestId: number) => void,\r\n\t\tcachedParseResult?: any\r\n\t): void {\r\n\t\tthis.queue\r\n\t\t\t.add(async () => {\r\n\t\t\t\ttry {\r\n\t\t\t\t\t// 处理队列项\r\n\t\t\t\t\tawait processor(requestId, data, cachedParseResult);\r\n\r\n\t\t\t\t\t// 去重标记\r\n\t\t\t\t\tthis.processedRequestIds.add(requestId);\r\n\t\t\t\t\t// 限制去重集合大小,防止内存泄漏\r\n\t\t\t\t\tif (this.processedRequestIds.size > this.queueOptions.maxLength * 2) {\r\n\t\t\t\t\t\tthis.processedRequestIds.clear();\r\n\t\t\t\t\t}\r\n\t\t\t\t} catch (error) {\r\n\t\t\t\t\tthrow error; // 重新抛出,让 p-queue 处理\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.catch((error) => {\r\n\t\t\t\t// p-queue 的超时错误会在这里捕获\r\n\t\t\t\tonError?.(error, requestId);\r\n\t\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * 检查是否可以添加到队列\r\n\t */\r\n\tcanAdd(): boolean {\r\n\t\treturn this.queue.size < this.queueOptions.maxLength;\r\n\t}\r\n\r\n\t/**\r\n\t * 检查是否已处理过(去重)\r\n\t */\r\n\tisProcessed(requestId: number): boolean {\r\n\t\treturn this.processedRequestIds.has(requestId);\r\n\t}\r\n\r\n\t/**\r\n\t * 处理队列溢出\r\n\t */\r\n\thandleOverflow(\r\n\t\trequestId: number,\r\n\t\tdata: Buffer,\r\n\t\tprocessor: (requestId: number, data: Buffer, cachedParseResult?: any) => Promise<void>,\r\n\t\tonError: (error: Error, requestId: number) => void,\r\n\t\tcachedParseResult?: any\r\n\t): void {\r\n\t\tswitch (this.queueOptions.overflowStrategy) {\r\n\t\t\tcase QueueOverflowStrategy.DROP_NEWEST:\r\n\t\t\t\t// 丢弃最新数据\r\n\t\t\t\tbreak;\r\n\t\t\tcase QueueOverflowStrategy.REJECT:\r\n\t\t\t\t// 拒绝新数据并触发错误\r\n\t\t\t\tconst error = new Error(`Queue overflow: max length ${this.queueOptions.maxLength} reached`);\r\n\t\t\t\tonError(error, requestId);\r\n\t\t\t\tbreak;\r\n\t\t\tcase QueueOverflowStrategy.DROP_OLDEST:\r\n\t\t\tdefault:\r\n\t\t\t\t// p-queue 不支持直接丢弃最旧的任务\r\n\t\t\t\tthis.addToQueue(requestId, data, processor, onError, cachedParseResult);\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 清空队列\r\n\t */\r\n\tclear(): void {\r\n\t\tthis.queue.clear();\r\n\t\tthis.processedRequestIds.clear();\r\n\t}\r\n}\r\n"]}