@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.
- package/README.md +76 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/packages/tcp/ConnectManager.d.ts +66 -0
- package/dist/packages/tcp/ConnectManager.d.ts.map +1 -0
- package/dist/packages/tcp/ConnectManager.js +176 -0
- package/dist/packages/tcp/ConnectManager.js.map +1 -0
- package/dist/packages/tcp/QueueManager.d.ts +41 -0
- package/dist/packages/tcp/QueueManager.d.ts.map +1 -0
- package/dist/packages/tcp/QueueManager.js +102 -0
- package/dist/packages/tcp/QueueManager.js.map +1 -0
- package/dist/packages/tcp/SocketWrapper.d.ts +106 -0
- package/dist/packages/tcp/SocketWrapper.d.ts.map +1 -0
- package/dist/packages/tcp/SocketWrapper.js +281 -0
- package/dist/packages/tcp/SocketWrapper.js.map +1 -0
- package/dist/packages/tcp/constants.d.ts +25 -0
- package/dist/packages/tcp/constants.d.ts.map +1 -0
- package/dist/packages/tcp/constants.js +27 -0
- package/dist/packages/tcp/constants.js.map +1 -0
- package/dist/packages/tcp/demo.d.ts +3 -0
- package/dist/packages/tcp/demo.d.ts.map +1 -0
- package/dist/packages/tcp/demo.js +164 -0
- package/dist/packages/tcp/demo.js.map +1 -0
- package/dist/packages/tcp/discoveryService.d.ts +42 -0
- package/dist/packages/tcp/discoveryService.d.ts.map +1 -0
- package/dist/packages/tcp/discoveryService.js +166 -0
- package/dist/packages/tcp/discoveryService.js.map +1 -0
- package/dist/packages/tcp/protocolHandler.d.ts +64 -0
- package/dist/packages/tcp/protocolHandler.d.ts.map +1 -0
- package/dist/packages/tcp/protocolHandler.js +96 -0
- package/dist/packages/tcp/protocolHandler.js.map +1 -0
- package/dist/packages/tcp/tcpWrapper.d.ts +168 -0
- package/dist/packages/tcp/tcpWrapper.d.ts.map +1 -0
- package/dist/packages/tcp/tcpWrapper.js +472 -0
- package/dist/packages/tcp/tcpWrapper.js.map +1 -0
- package/dist/packages/tcp/test-build-parse-debug.d.ts +2 -0
- package/dist/packages/tcp/test-build-parse-debug.d.ts.map +1 -0
- package/dist/packages/tcp/test-build-parse-debug.js +73 -0
- package/dist/packages/tcp/test-build-parse-debug.js.map +1 -0
- package/dist/packages/tcp/test-packet-split.d.ts +43 -0
- package/dist/packages/tcp/test-packet-split.d.ts.map +1 -0
- package/dist/packages/tcp/test-packet-split.js +417 -0
- package/dist/packages/tcp/test-packet-split.js.map +1 -0
- package/dist/packages/tcp/test-protocol-handler.d.ts +6 -0
- package/dist/packages/tcp/test-protocol-handler.d.ts.map +1 -0
- package/dist/packages/tcp/test-protocol-handler.js +225 -0
- package/dist/packages/tcp/test-protocol-handler.js.map +1 -0
- package/dist/packages/tcp/test-server.d.ts +54 -0
- package/dist/packages/tcp/test-server.d.ts.map +1 -0
- package/dist/packages/tcp/test-server.js +412 -0
- package/dist/packages/tcp/test-server.js.map +1 -0
- package/dist/packages/tcp/types.d.ts +232 -0
- package/dist/packages/tcp/types.d.ts.map +1 -0
- package/dist/packages/tcp/types.js +13 -0
- package/dist/packages/tcp/types.js.map +1 -0
- package/dist/packages/tcp/utils.d.ts +14 -0
- package/dist/packages/tcp/utils.d.ts.map +1 -0
- package/dist/packages/tcp/utils.js +47 -0
- package/dist/packages/tcp/utils.js.map +1 -0
- 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
|
package/dist/index.d.ts
ADDED
|
@@ -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"]}
|