@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
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import type { ISocketWrapper, SocketConnectOptions, QueueOptions, ProtocolSendPayload } from './types.js';
|
|
3
|
+
import { TcpWrapper } from './tcpWrapper.js';
|
|
4
|
+
/**
|
|
5
|
+
* SocketWrapper - TCP Socket 最小传输封装
|
|
6
|
+
*
|
|
7
|
+
* 职责:
|
|
8
|
+
* - 仅负责 TCP 连接的字节级可靠传输
|
|
9
|
+
* - 事件化接口:提供 connected/data/error/close 事件
|
|
10
|
+
* - 协议支持:集成 Uniform Protocol 协议解析和构建
|
|
11
|
+
* - 并发请求支持:通过 requestId 管理多个并发请求的发送和响应匹配
|
|
12
|
+
* - 缓冲管理:维护接收缓冲区并按 requestId 管理
|
|
13
|
+
* - 队列管理:支持队列长度限制、溢出策略、去重、监控等
|
|
14
|
+
*
|
|
15
|
+
* 事件:
|
|
16
|
+
* - 'connected': 连接成功时触发
|
|
17
|
+
* - 'disconnected': 连接彻底关闭时触发
|
|
18
|
+
* - 'data': 接收到协议数据时触发 (data: any, requestId: number)
|
|
19
|
+
* - 'error': 发生错误时触发 (error: Error, requestId?: number)
|
|
20
|
+
*/
|
|
21
|
+
export declare class SocketWrapper extends EventEmitter implements ISocketWrapper {
|
|
22
|
+
/** 连接管理器 */
|
|
23
|
+
private tcpWrapper;
|
|
24
|
+
/** 队列管理器 */
|
|
25
|
+
private queueManager;
|
|
26
|
+
/** 协议处理器 */
|
|
27
|
+
private protocolHandler;
|
|
28
|
+
/** 接收缓冲区,按 requestId 索引存储接收到的数据 */
|
|
29
|
+
private receiveBuffers;
|
|
30
|
+
/** 接收数据缓冲区,用于处理不完整的数据包 */
|
|
31
|
+
private receiveBuffer;
|
|
32
|
+
/**
|
|
33
|
+
* 构造函数
|
|
34
|
+
* @param queueOptions 队列配置选项
|
|
35
|
+
*/
|
|
36
|
+
constructor(queueOptions?: Partial<QueueOptions>);
|
|
37
|
+
/**
|
|
38
|
+
* 检查是否是心跳数据(ping/pong)
|
|
39
|
+
* 使用 Buffer 直接比较,避免字符串转换开销
|
|
40
|
+
*/
|
|
41
|
+
private isHeartbeatData;
|
|
42
|
+
/**
|
|
43
|
+
* 接收数据处理
|
|
44
|
+
* @param data 接收到的数据
|
|
45
|
+
*/
|
|
46
|
+
private handleReceiveData;
|
|
47
|
+
/**
|
|
48
|
+
* 处理协议数据(使用半包粘包处理)
|
|
49
|
+
* 使用循环替代递归,避免栈溢出风险
|
|
50
|
+
*/
|
|
51
|
+
private handleProtocolData;
|
|
52
|
+
/**
|
|
53
|
+
* 处理单个协议包
|
|
54
|
+
* @param packet 协议包数据
|
|
55
|
+
*/
|
|
56
|
+
private processPacket;
|
|
57
|
+
/**
|
|
58
|
+
* 处理单个队列项
|
|
59
|
+
* @param requestId 请求ID
|
|
60
|
+
* @param data 数据
|
|
61
|
+
* @param cachedParseResult 已解析的结果(如果已解析过,避免重复解析)
|
|
62
|
+
*/
|
|
63
|
+
private processQueueItem;
|
|
64
|
+
/**
|
|
65
|
+
* 清理资源
|
|
66
|
+
*/
|
|
67
|
+
private cleanup;
|
|
68
|
+
/**
|
|
69
|
+
* 写入 socket
|
|
70
|
+
* @param bytes 要写入的数据
|
|
71
|
+
*/
|
|
72
|
+
private writeToSocket;
|
|
73
|
+
/**
|
|
74
|
+
* 连接建立
|
|
75
|
+
* @param host 服务器主机地址
|
|
76
|
+
* @param port 服务器端口号
|
|
77
|
+
* @param options 连接选项
|
|
78
|
+
*/
|
|
79
|
+
connect(host: string, port: number, options: SocketConnectOptions): void;
|
|
80
|
+
/**
|
|
81
|
+
* 发送协议数据包
|
|
82
|
+
* @param payload 协议负载对象
|
|
83
|
+
* @returns 实际使用的 requestId(seqId,由系统自动生成)
|
|
84
|
+
*/
|
|
85
|
+
send(payload: ProtocolSendPayload): number;
|
|
86
|
+
/**
|
|
87
|
+
* 断开连接
|
|
88
|
+
*/
|
|
89
|
+
disconnect(): void;
|
|
90
|
+
/**
|
|
91
|
+
* 检查是否已连接
|
|
92
|
+
*/
|
|
93
|
+
isConnected(): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* 主动触发 error 事件
|
|
96
|
+
* 用于外部主动报告错误,例如业务逻辑错误、数据验证错误等
|
|
97
|
+
* @param error 错误对象
|
|
98
|
+
*/
|
|
99
|
+
emitError(error: Error): void;
|
|
100
|
+
/**
|
|
101
|
+
* 获取内部的 TcpWrapper 实例
|
|
102
|
+
* @returns TcpWrapper 实例
|
|
103
|
+
*/
|
|
104
|
+
getTcpWrapper(): TcpWrapper;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=SocketWrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SocketWrapper.d.ts","sourceRoot":"","sources":["../../../src/packages/tcp/SocketWrapper.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC1G,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAW7C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,SAAQ,YAAa,YAAW,cAAc;IACxE,YAAY;IACZ,OAAO,CAAC,UAAU,CAAa;IAE/B,YAAY;IACZ,OAAO,CAAC,YAAY,CAAe;IAEnC,YAAY;IACZ,OAAO,CAAC,eAAe,CAAkB;IAEzC,mCAAmC;IACnC,OAAO,CAAC,cAAc,CAAkC;IAExD,0BAA0B;IAC1B,OAAO,CAAC,aAAa,CAAuB;IAE5C;;;OAGG;gBACS,YAAY,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAmChD;;;OAGG;IACH,OAAO,CAAC,eAAe;IAOvB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAuC1B;;;OAGG;IACH,OAAO,CAAC,aAAa;IA6CrB;;;;;OAKG;YACW,gBAAgB;IAiB9B;;OAEG;IACH,OAAO,CAAC,OAAO;IAMf;;;OAGG;IACH,OAAO,CAAC,aAAa;IAcrB;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAIxE;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM;IAkB1C;;OAEG;IACH,UAAU,IAAI,IAAI;IAOlB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAI7B;;;OAGG;IACH,aAAa,IAAI,UAAU;CAG3B"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// ALL_CODE_LINE: 329
|
|
2
|
+
// AI_CODE_LINE: 53
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { TcpWrapper } from './tcpWrapper.js';
|
|
5
|
+
import { QueueManager, DEFAULT_QUEUE_OPTIONS } from './QueueManager.js';
|
|
6
|
+
import { ProtocolHandler } from './protocolHandler.js';
|
|
7
|
+
/**
|
|
8
|
+
* 心跳数据常量(预定义 Buffer,避免重复转换)
|
|
9
|
+
*/
|
|
10
|
+
const HEARTBEAT_PING = Buffer.from('ping', 'utf8');
|
|
11
|
+
const HEARTBEAT_PONG = Buffer.from('pong', 'utf8');
|
|
12
|
+
const HEARTBEAT_LENGTH = 4;
|
|
13
|
+
/**
|
|
14
|
+
* SocketWrapper - TCP Socket 最小传输封装
|
|
15
|
+
*
|
|
16
|
+
* 职责:
|
|
17
|
+
* - 仅负责 TCP 连接的字节级可靠传输
|
|
18
|
+
* - 事件化接口:提供 connected/data/error/close 事件
|
|
19
|
+
* - 协议支持:集成 Uniform Protocol 协议解析和构建
|
|
20
|
+
* - 并发请求支持:通过 requestId 管理多个并发请求的发送和响应匹配
|
|
21
|
+
* - 缓冲管理:维护接收缓冲区并按 requestId 管理
|
|
22
|
+
* - 队列管理:支持队列长度限制、溢出策略、去重、监控等
|
|
23
|
+
*
|
|
24
|
+
* 事件:
|
|
25
|
+
* - 'connected': 连接成功时触发
|
|
26
|
+
* - 'disconnected': 连接彻底关闭时触发
|
|
27
|
+
* - 'data': 接收到协议数据时触发 (data: any, requestId: number)
|
|
28
|
+
* - 'error': 发生错误时触发 (error: Error, requestId?: number)
|
|
29
|
+
*/
|
|
30
|
+
export class SocketWrapper extends EventEmitter {
|
|
31
|
+
/** 连接管理器 */
|
|
32
|
+
tcpWrapper;
|
|
33
|
+
/** 队列管理器 */
|
|
34
|
+
queueManager;
|
|
35
|
+
/** 协议处理器 */
|
|
36
|
+
protocolHandler;
|
|
37
|
+
/** 接收缓冲区,按 requestId 索引存储接收到的数据 */
|
|
38
|
+
receiveBuffers = new Map();
|
|
39
|
+
/** 接收数据缓冲区,用于处理不完整的数据包 */
|
|
40
|
+
receiveBuffer = null;
|
|
41
|
+
/**
|
|
42
|
+
* 构造函数
|
|
43
|
+
* @param queueOptions 队列配置选项
|
|
44
|
+
*/
|
|
45
|
+
constructor(queueOptions) {
|
|
46
|
+
super();
|
|
47
|
+
this.protocolHandler = new ProtocolHandler();
|
|
48
|
+
// 初始化连接管理器和队列管理器
|
|
49
|
+
this.tcpWrapper = new TcpWrapper();
|
|
50
|
+
// 合并默认配置和用户配置
|
|
51
|
+
this.queueManager = new QueueManager({
|
|
52
|
+
...DEFAULT_QUEUE_OPTIONS,
|
|
53
|
+
...queueOptions,
|
|
54
|
+
});
|
|
55
|
+
// 设置连接管理器的事件回调
|
|
56
|
+
this.tcpWrapper.setEventHandlers({
|
|
57
|
+
/** 连接成功回调:TCP Socket 成功建立连接时触发,同时启动心跳机制并重置重连计数器 */
|
|
58
|
+
onConnected: () => {
|
|
59
|
+
this.emit('connected');
|
|
60
|
+
},
|
|
61
|
+
/** 连接断开回调(自动重连场景):Socket 关闭且会重连,或心跳超时且启用自动重连时触发,表示临时断开 */
|
|
62
|
+
onDisconnected: () => { },
|
|
63
|
+
/** 接收数据回调:Socket 接收到非心跳数据时触发,心跳响应会被内部处理不传递给上层 */
|
|
64
|
+
onData: (data) => {
|
|
65
|
+
this.handleReceiveData(data);
|
|
66
|
+
},
|
|
67
|
+
/** 错误回调:连接参数无效、Socket 错误/超时、连接超时、重连异常等错误情况时触发 */
|
|
68
|
+
onError: (error) => {
|
|
69
|
+
this.emit('error', error);
|
|
70
|
+
},
|
|
71
|
+
/** 连接关闭回调:连接彻底关闭时触发(手动断开、达到最大重连次数、连接信息缺失),不会再有重连尝试 */
|
|
72
|
+
onClose: () => {
|
|
73
|
+
this.emit('disconnected');
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 检查是否是心跳数据(ping/pong)
|
|
79
|
+
* 使用 Buffer 直接比较,避免字符串转换开销
|
|
80
|
+
*/
|
|
81
|
+
isHeartbeatData(buffer) {
|
|
82
|
+
if (buffer.length !== HEARTBEAT_LENGTH) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return buffer.equals(HEARTBEAT_PING) || buffer.equals(HEARTBEAT_PONG);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 接收数据处理
|
|
89
|
+
* @param data 接收到的数据
|
|
90
|
+
*/
|
|
91
|
+
handleReceiveData(data) {
|
|
92
|
+
// 检查是否是心跳数据,如果是则直接跳过,不进入协议解析流程
|
|
93
|
+
if (this.isHeartbeatData(data)) {
|
|
94
|
+
return; // 心跳数据由 tcpWrapper 处理,不进入协议解析
|
|
95
|
+
}
|
|
96
|
+
// 将新数据追加到接收缓冲区(优化:避免不必要的 Buffer.concat)
|
|
97
|
+
if (!this.receiveBuffer || this.receiveBuffer.length === 0) {
|
|
98
|
+
this.receiveBuffer = data;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.receiveBuffer = Buffer.concat([this.receiveBuffer, data]);
|
|
102
|
+
}
|
|
103
|
+
// 处理协议数据
|
|
104
|
+
this.handleProtocolData();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 处理协议数据(使用半包粘包处理)
|
|
108
|
+
* 使用循环替代递归,避免栈溢出风险
|
|
109
|
+
*/
|
|
110
|
+
handleProtocolData() {
|
|
111
|
+
if (!this.receiveBuffer)
|
|
112
|
+
return;
|
|
113
|
+
// 循环处理,直到没有更多数据需要处理
|
|
114
|
+
while (this.receiveBuffer) {
|
|
115
|
+
// 检查并过滤心跳数据(处理粘包情况:心跳数据 + 协议数据)
|
|
116
|
+
if (this.receiveBuffer.length >= HEARTBEAT_LENGTH) {
|
|
117
|
+
const first4Bytes = this.receiveBuffer.slice(0, HEARTBEAT_LENGTH);
|
|
118
|
+
if (this.isHeartbeatData(first4Bytes)) {
|
|
119
|
+
// 移除前 4 字节的心跳数据,继续处理剩余数据
|
|
120
|
+
this.receiveBuffer =
|
|
121
|
+
this.receiveBuffer.length > HEARTBEAT_LENGTH ? this.receiveBuffer.slice(HEARTBEAT_LENGTH) : null;
|
|
122
|
+
continue; // 继续循环处理剩余数据
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// 提取所有完整协议包(自动校验)
|
|
126
|
+
const { packets, remaining, invalidPackets } = this.protocolHandler.extractAllCompletePackets(this.receiveBuffer);
|
|
127
|
+
// 处理校验失败的废包
|
|
128
|
+
if (invalidPackets.length > 0) {
|
|
129
|
+
this.emit('error', new Error(`Invalid packets detected: ${invalidPackets.length} packets dropped`));
|
|
130
|
+
}
|
|
131
|
+
// 处理每个有效完整包
|
|
132
|
+
for (const packet of packets) {
|
|
133
|
+
this.processPacket(packet);
|
|
134
|
+
}
|
|
135
|
+
// 更新接收缓冲区为剩余数据(半包数据保留)
|
|
136
|
+
this.receiveBuffer = remaining;
|
|
137
|
+
// 如果没有剩余数据,退出循环
|
|
138
|
+
if (!remaining || remaining.length === 0) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 处理单个协议包
|
|
145
|
+
* @param packet 协议包数据
|
|
146
|
+
*/
|
|
147
|
+
processPacket(packet) {
|
|
148
|
+
// 先解析获取 requestId(用于去重和队列管理)
|
|
149
|
+
let requestId;
|
|
150
|
+
let parseResult;
|
|
151
|
+
try {
|
|
152
|
+
parseResult = this.protocolHandler.parse(packet);
|
|
153
|
+
requestId = parseResult.requestId;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
this.emit('error', error instanceof Error ? error : new Error(String(error)));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// 去重检查(响应包允许使用相同的 requestId,跳过去重检查)
|
|
160
|
+
if (!parseResult.isResponse && this.queueManager.isProcessed(requestId)) {
|
|
161
|
+
this.emit('error', new Error(`Duplicate requestId detected, dropping: ${requestId}`), requestId);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// 队列处理回调函数(提取为常量,避免重复创建)
|
|
165
|
+
const processor = (reqId, data, cachedParseResult) => this.processQueueItem(reqId, data, cachedParseResult);
|
|
166
|
+
const errorHandler = (error, reqId) => this.emit('error', error, reqId);
|
|
167
|
+
// 检查队列长度限制
|
|
168
|
+
if (!this.queueManager.canAdd()) {
|
|
169
|
+
this.queueManager.handleOverflow(requestId, packet, processor, errorHandler, parseResult // 传递已解析的结果
|
|
170
|
+
);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// 使用队列管理器处理(传递已解析的结果,避免重复解析)
|
|
174
|
+
this.queueManager.addToQueue(requestId, packet, processor, errorHandler, parseResult // 传递已解析的结果
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 处理单个队列项
|
|
179
|
+
* @param requestId 请求ID
|
|
180
|
+
* @param data 数据
|
|
181
|
+
* @param cachedParseResult 已解析的结果(如果已解析过,避免重复解析)
|
|
182
|
+
*/
|
|
183
|
+
async processQueueItem(requestId, data, cachedParseResult) {
|
|
184
|
+
// 缓存原始数据(按 requestId 索引)
|
|
185
|
+
this.receiveBuffers.set(requestId, data);
|
|
186
|
+
try {
|
|
187
|
+
// 如果已有解析结果,直接使用;否则重新解析
|
|
188
|
+
const parseResult = cachedParseResult || this.protocolHandler.parse(data);
|
|
189
|
+
// 触发协议数据事件(使用parseResult.data,因为ProtocolParseResult中数据字段是data)
|
|
190
|
+
this.emit('data', parseResult.data, requestId);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
// 解析失败,触发错误事件
|
|
194
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
195
|
+
this.emit('error', errorObj, requestId);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* 清理资源
|
|
200
|
+
*/
|
|
201
|
+
cleanup() {
|
|
202
|
+
this.receiveBuffers.clear();
|
|
203
|
+
this.receiveBuffer = null;
|
|
204
|
+
this.queueManager.clear();
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 写入 socket
|
|
208
|
+
* @param bytes 要写入的数据
|
|
209
|
+
*/
|
|
210
|
+
writeToSocket(bytes) {
|
|
211
|
+
const socket = this.tcpWrapper.getSocket();
|
|
212
|
+
if (!socket?.writable) {
|
|
213
|
+
this.emit('error', new Error('Socket is not writable'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
socket.write(bytes, (err) => {
|
|
217
|
+
if (err) {
|
|
218
|
+
this.emit('error', err);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 连接建立
|
|
224
|
+
* @param host 服务器主机地址
|
|
225
|
+
* @param port 服务器端口号
|
|
226
|
+
* @param options 连接选项
|
|
227
|
+
*/
|
|
228
|
+
connect(host, port, options) {
|
|
229
|
+
this.tcpWrapper.connect(host, port, options);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 发送协议数据包
|
|
233
|
+
* @param payload 协议负载对象
|
|
234
|
+
* @returns 实际使用的 requestId(seqId,由系统自动生成)
|
|
235
|
+
*/
|
|
236
|
+
send(payload) {
|
|
237
|
+
const buildOptions = {
|
|
238
|
+
isResponse: false, // 默认请求包
|
|
239
|
+
...payload,
|
|
240
|
+
};
|
|
241
|
+
// 如果业务侧需要指定 seqId(如响应包需要匹配请求),可以通过 options.seqId 传入
|
|
242
|
+
if (typeof buildOptions.seqId !== 'number') {
|
|
243
|
+
buildOptions.seqId = this.protocolHandler.generateSeqId();
|
|
244
|
+
}
|
|
245
|
+
const buffer = this.protocolHandler.build(payload);
|
|
246
|
+
const resolvedRequestId = buildOptions.seqId;
|
|
247
|
+
this.writeToSocket(buffer);
|
|
248
|
+
return resolvedRequestId;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* 断开连接
|
|
252
|
+
*/
|
|
253
|
+
disconnect() {
|
|
254
|
+
// 先清理本地资源
|
|
255
|
+
this.cleanup();
|
|
256
|
+
// 再断开连接(会停止心跳和重连)
|
|
257
|
+
this.tcpWrapper.disconnect();
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* 检查是否已连接
|
|
261
|
+
*/
|
|
262
|
+
isConnected() {
|
|
263
|
+
return this.tcpWrapper.isConnected();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 主动触发 error 事件
|
|
267
|
+
* 用于外部主动报告错误,例如业务逻辑错误、数据验证错误等
|
|
268
|
+
* @param error 错误对象
|
|
269
|
+
*/
|
|
270
|
+
emitError(error) {
|
|
271
|
+
this.emit('error', error);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 获取内部的 TcpWrapper 实例
|
|
275
|
+
* @returns TcpWrapper 实例
|
|
276
|
+
*/
|
|
277
|
+
getTcpWrapper() {
|
|
278
|
+
return this.tcpWrapper;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=SocketWrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SocketWrapper.js","sourceRoot":"","sources":["../../../src/packages/tcp/SocketWrapper.ts"],"names":[],"mappings":"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","sourcesContent":["// ALL_CODE_LINE: 329\r\n// AI_CODE_LINE: 53\r\nimport { EventEmitter } from 'events';\r\nimport type { ISocketWrapper, SocketConnectOptions, QueueOptions, ProtocolSendPayload } from './types.js';\r\nimport { TcpWrapper } from './tcpWrapper.js';\r\nimport { QueueManager, DEFAULT_QUEUE_OPTIONS } from './QueueManager.js';\r\nimport { ProtocolHandler } from './protocolHandler.js';\r\n\r\n/**\r\n * 心跳数据常量(预定义 Buffer,避免重复转换)\r\n */\r\nconst HEARTBEAT_PING = Buffer.from('ping', 'utf8');\r\nconst HEARTBEAT_PONG = Buffer.from('pong', 'utf8');\r\nconst HEARTBEAT_LENGTH = 4;\r\n\r\n/**\r\n * SocketWrapper - TCP Socket 最小传输封装\r\n *\r\n * 职责:\r\n * - 仅负责 TCP 连接的字节级可靠传输\r\n * - 事件化接口:提供 connected/data/error/close 事件\r\n * - 协议支持:集成 Uniform Protocol 协议解析和构建\r\n * - 并发请求支持:通过 requestId 管理多个并发请求的发送和响应匹配\r\n * - 缓冲管理:维护接收缓冲区并按 requestId 管理\r\n * - 队列管理:支持队列长度限制、溢出策略、去重、监控等\r\n *\r\n * 事件:\r\n * - 'connected': 连接成功时触发\r\n * - 'disconnected': 连接彻底关闭时触发\r\n * - 'data': 接收到协议数据时触发 (data: any, requestId: number)\r\n * - 'error': 发生错误时触发 (error: Error, requestId?: number)\r\n */\r\nexport class SocketWrapper extends EventEmitter implements ISocketWrapper {\r\n\t/** 连接管理器 */\r\n\tprivate tcpWrapper: TcpWrapper;\r\n\r\n\t/** 队列管理器 */\r\n\tprivate queueManager: QueueManager;\r\n\r\n\t/** 协议处理器 */\r\n\tprivate protocolHandler: ProtocolHandler;\r\n\r\n\t/** 接收缓冲区,按 requestId 索引存储接收到的数据 */\r\n\tprivate receiveBuffers: Map<number, Buffer> = new Map();\r\n\r\n\t/** 接收数据缓冲区,用于处理不完整的数据包 */\r\n\tprivate receiveBuffer: Buffer | null = null;\r\n\r\n\t/**\r\n\t * 构造函数\r\n\t * @param queueOptions 队列配置选项\r\n\t */\r\n\tconstructor(queueOptions?: Partial<QueueOptions>) {\r\n\t\tsuper();\r\n\r\n\t\tthis.protocolHandler = new ProtocolHandler();\r\n\r\n\t\t// 初始化连接管理器和队列管理器\r\n\t\tthis.tcpWrapper = new TcpWrapper();\r\n\t\t// 合并默认配置和用户配置\r\n\t\tthis.queueManager = new QueueManager({\r\n\t\t\t...DEFAULT_QUEUE_OPTIONS,\r\n\t\t\t...queueOptions,\r\n\t\t});\r\n\r\n\t\t// 设置连接管理器的事件回调\r\n\t\tthis.tcpWrapper.setEventHandlers({\r\n\t\t\t/** 连接成功回调:TCP Socket 成功建立连接时触发,同时启动心跳机制并重置重连计数器 */\r\n\t\t\tonConnected: () => {\r\n\t\t\t\tthis.emit('connected');\r\n\t\t\t},\r\n\t\t\t/** 连接断开回调(自动重连场景):Socket 关闭且会重连,或心跳超时且启用自动重连时触发,表示临时断开 */\r\n\t\t\tonDisconnected: () => {},\r\n\t\t\t/** 接收数据回调:Socket 接收到非心跳数据时触发,心跳响应会被内部处理不传递给上层 */\r\n\t\t\tonData: (data: Buffer) => {\r\n\t\t\t\tthis.handleReceiveData(data);\r\n\t\t\t},\r\n\t\t\t/** 错误回调:连接参数无效、Socket 错误/超时、连接超时、重连异常等错误情况时触发 */\r\n\t\t\tonError: (error: Error) => {\r\n\t\t\t\tthis.emit('error', error);\r\n\t\t\t},\r\n\t\t\t/** 连接关闭回调:连接彻底关闭时触发(手动断开、达到最大重连次数、连接信息缺失),不会再有重连尝试 */\r\n\t\t\tonClose: () => {\r\n\t\t\t\tthis.emit('disconnected');\r\n\t\t\t},\r\n\t\t});\r\n\t}\r\n\t/**\r\n\t * 检查是否是心跳数据(ping/pong)\r\n\t * 使用 Buffer 直接比较,避免字符串转换开销\r\n\t */\r\n\tprivate isHeartbeatData(buffer: Buffer): boolean {\r\n\t\tif (buffer.length !== HEARTBEAT_LENGTH) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\treturn buffer.equals(HEARTBEAT_PING) || buffer.equals(HEARTBEAT_PONG);\r\n\t}\r\n\r\n\t/**\r\n\t * 接收数据处理\r\n\t * @param data 接收到的数据\r\n\t */\r\n\tprivate handleReceiveData(data: Buffer): void {\r\n\t\t// 检查是否是心跳数据,如果是则直接跳过,不进入协议解析流程\r\n\t\tif (this.isHeartbeatData(data)) {\r\n\t\t\treturn; // 心跳数据由 tcpWrapper 处理,不进入协议解析\r\n\t\t}\r\n\r\n\t\t// 将新数据追加到接收缓冲区(优化:避免不必要的 Buffer.concat)\r\n\t\tif (!this.receiveBuffer || this.receiveBuffer.length === 0) {\r\n\t\t\tthis.receiveBuffer = data;\r\n\t\t} else {\r\n\t\t\tthis.receiveBuffer = Buffer.concat([this.receiveBuffer, data]);\r\n\t\t}\r\n\r\n\t\t// 处理协议数据\r\n\t\tthis.handleProtocolData();\r\n\t}\r\n\r\n\t/**\r\n\t * 处理协议数据(使用半包粘包处理)\r\n\t * 使用循环替代递归,避免栈溢出风险\r\n\t */\r\n\tprivate handleProtocolData(): void {\r\n\t\tif (!this.receiveBuffer) return;\r\n\r\n\t\t// 循环处理,直到没有更多数据需要处理\r\n\t\twhile (this.receiveBuffer) {\r\n\t\t\t// 检查并过滤心跳数据(处理粘包情况:心跳数据 + 协议数据)\r\n\t\t\tif (this.receiveBuffer.length >= HEARTBEAT_LENGTH) {\r\n\t\t\t\tconst first4Bytes = this.receiveBuffer.slice(0, HEARTBEAT_LENGTH);\r\n\t\t\t\tif (this.isHeartbeatData(first4Bytes)) {\r\n\t\t\t\t\t// 移除前 4 字节的心跳数据,继续处理剩余数据\r\n\t\t\t\t\tthis.receiveBuffer =\r\n\t\t\t\t\t\tthis.receiveBuffer.length > HEARTBEAT_LENGTH ? this.receiveBuffer.slice(HEARTBEAT_LENGTH) : null;\r\n\t\t\t\t\tcontinue; // 继续循环处理剩余数据\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// 提取所有完整协议包(自动校验)\r\n\t\t\tconst { packets, remaining, invalidPackets } = this.protocolHandler.extractAllCompletePackets(this.receiveBuffer);\r\n\r\n\t\t\t// 处理校验失败的废包\r\n\t\t\tif (invalidPackets.length > 0) {\r\n\t\t\t\tthis.emit('error', new Error(`Invalid packets detected: ${invalidPackets.length} packets dropped`));\r\n\t\t\t}\r\n\r\n\t\t\t// 处理每个有效完整包\r\n\t\t\tfor (const packet of packets) {\r\n\t\t\t\tthis.processPacket(packet);\r\n\t\t\t}\r\n\r\n\t\t\t// 更新接收缓冲区为剩余数据(半包数据保留)\r\n\t\t\tthis.receiveBuffer = remaining;\r\n\r\n\t\t\t// 如果没有剩余数据,退出循环\r\n\t\t\tif (!remaining || remaining.length === 0) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 处理单个协议包\r\n\t * @param packet 协议包数据\r\n\t */\r\n\tprivate processPacket(packet: Buffer): void {\r\n\t\t// 先解析获取 requestId(用于去重和队列管理)\r\n\t\tlet requestId: number;\r\n\t\tlet parseResult: any;\r\n\t\ttry {\r\n\t\t\tparseResult = this.protocolHandler.parse(packet);\r\n\t\t\trequestId = parseResult.requestId;\r\n\t\t} catch (error) {\r\n\t\t\tthis.emit('error', error instanceof Error ? error : new Error(String(error)));\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// 去重检查(响应包允许使用相同的 requestId,跳过去重检查)\r\n\t\tif (!parseResult.isResponse && this.queueManager.isProcessed(requestId)) {\r\n\t\t\tthis.emit('error', new Error(`Duplicate requestId detected, dropping: ${requestId}`), requestId);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// 队列处理回调函数(提取为常量,避免重复创建)\r\n\t\tconst processor = (reqId: number, data: Buffer, cachedParseResult?: any) =>\r\n\t\t\tthis.processQueueItem(reqId, data, cachedParseResult);\r\n\t\tconst errorHandler = (error: Error, reqId: number) => this.emit('error', error, reqId);\r\n\r\n\t\t// 检查队列长度限制\r\n\t\tif (!this.queueManager.canAdd()) {\r\n\t\t\tthis.queueManager.handleOverflow(\r\n\t\t\t\trequestId,\r\n\t\t\t\tpacket,\r\n\t\t\t\tprocessor,\r\n\t\t\t\terrorHandler,\r\n\t\t\t\tparseResult // 传递已解析的结果\r\n\t\t\t);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// 使用队列管理器处理(传递已解析的结果,避免重复解析)\r\n\t\tthis.queueManager.addToQueue(\r\n\t\t\trequestId,\r\n\t\t\tpacket,\r\n\t\t\tprocessor,\r\n\t\t\terrorHandler,\r\n\t\t\tparseResult // 传递已解析的结果\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 cachedParseResult 已解析的结果(如果已解析过,避免重复解析)\r\n\t */\r\n\tprivate async processQueueItem(requestId: number, data: Buffer, cachedParseResult?: any): Promise<void> {\r\n\t\t// 缓存原始数据(按 requestId 索引)\r\n\t\tthis.receiveBuffers.set(requestId, data);\r\n\r\n\t\ttry {\r\n\t\t\t// 如果已有解析结果,直接使用;否则重新解析\r\n\t\t\tconst parseResult = cachedParseResult || this.protocolHandler.parse(data);\r\n\r\n\t\t\t// 触发协议数据事件(使用parseResult.data,因为ProtocolParseResult中数据字段是data)\r\n\t\t\tthis.emit('data', parseResult.data, requestId);\r\n\t\t} catch (error) {\r\n\t\t\t// 解析失败,触发错误事件\r\n\t\t\tconst errorObj = error instanceof Error ? error : new Error(String(error));\r\n\t\t\tthis.emit('error', errorObj, requestId);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * 清理资源\r\n\t */\r\n\tprivate cleanup(): void {\r\n\t\tthis.receiveBuffers.clear();\r\n\t\tthis.receiveBuffer = null;\r\n\t\tthis.queueManager.clear();\r\n\t}\r\n\r\n\t/**\r\n\t * 写入 socket\r\n\t * @param bytes 要写入的数据\r\n\t */\r\n\tprivate writeToSocket(bytes: Buffer): void {\r\n\t\tconst socket = this.tcpWrapper.getSocket();\r\n\t\tif (!socket?.writable) {\r\n\t\t\tthis.emit('error', new Error('Socket is not writable'));\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tsocket.write(bytes, (err?: Error | null) => {\r\n\t\t\tif (err) {\r\n\t\t\t\tthis.emit('error', err);\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * 连接建立\r\n\t * @param host 服务器主机地址\r\n\t * @param port 服务器端口号\r\n\t * @param options 连接选项\r\n\t */\r\n\tconnect(host: string, port: number, options: SocketConnectOptions): void {\r\n\t\tthis.tcpWrapper.connect(host, port, options);\r\n\t}\r\n\r\n\t/**\r\n\t * 发送协议数据包\r\n\t * @param payload 协议负载对象\r\n\t * @returns 实际使用的 requestId(seqId,由系统自动生成)\r\n\t */\r\n\tsend(payload: ProtocolSendPayload): number {\r\n\t\tconst buildOptions: any = {\r\n\t\t\tisResponse: false, // 默认请求包\r\n\t\t\t...payload,\r\n\t\t};\r\n\r\n\t\t// 如果业务侧需要指定 seqId(如响应包需要匹配请求),可以通过 options.seqId 传入\r\n\t\tif (typeof buildOptions.seqId !== 'number') {\r\n\t\t\tbuildOptions.seqId = this.protocolHandler.generateSeqId();\r\n\t\t}\r\n\r\n\t\tconst buffer = this.protocolHandler.build(payload);\r\n\t\tconst resolvedRequestId = buildOptions.seqId;\r\n\r\n\t\tthis.writeToSocket(buffer);\r\n\t\treturn resolvedRequestId;\r\n\t}\r\n\r\n\t/**\r\n\t * 断开连接\r\n\t */\r\n\tdisconnect(): void {\r\n\t\t// 先清理本地资源\r\n\t\tthis.cleanup();\r\n\t\t// 再断开连接(会停止心跳和重连)\r\n\t\tthis.tcpWrapper.disconnect();\r\n\t}\r\n\r\n\t/**\r\n\t * 检查是否已连接\r\n\t */\r\n\tisConnected(): boolean {\r\n\t\treturn this.tcpWrapper.isConnected();\r\n\t}\r\n\r\n\t/**\r\n\t * 主动触发 error 事件\r\n\t * 用于外部主动报告错误,例如业务逻辑错误、数据验证错误等\r\n\t * @param error 错误对象\r\n\t */\r\n\temitError(error: Error): void {\r\n\t\tthis.emit('error', error);\r\n\t}\r\n\r\n\t/**\r\n\t * 获取内部的 TcpWrapper 实例\r\n\t * @returns TcpWrapper 实例\r\n\t */\r\n\tgetTcpWrapper(): TcpWrapper {\r\n\t\treturn this.tcpWrapper;\r\n\t}\r\n}\r\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** 支持的设备型号 */
|
|
2
|
+
export declare const SUPPORTED_MODELS: {
|
|
3
|
+
readonly MINI: "U5 MINI Event Controller";
|
|
4
|
+
};
|
|
5
|
+
/** 默认TCP端口 */
|
|
6
|
+
export declare const DEFAULT_TCP_PORT = 17100;
|
|
7
|
+
/** 默认启用心跳 */
|
|
8
|
+
export declare const DEFAULT_ENABLE_HEARTBEAT = true;
|
|
9
|
+
/** 默认自动重连开关 */
|
|
10
|
+
export declare const DEFAULT_AUTO_RECONNECT = false;
|
|
11
|
+
/** 默认心跳间隔(毫秒) */
|
|
12
|
+
export declare const DEFAULT_HEARTBEAT_INTERVAL = 5000;
|
|
13
|
+
/** 默认重连间隔(毫秒) */
|
|
14
|
+
export declare const DEFAULT_RECONNECT_INTERVAL = 5000;
|
|
15
|
+
/** 默认心跳超时时间(毫秒) */
|
|
16
|
+
export declare const DEFAULT_HEARTBEAT_TIMEOUT = 5000;
|
|
17
|
+
/** 默认最大重连间隔(毫秒) */
|
|
18
|
+
export declare const DEFAULT_MAX_RECONNECT_INTERVAL = 60000;
|
|
19
|
+
/** 默认是否启用指数退避 */
|
|
20
|
+
export declare const DEFAULT_ENABLE_EXPONENTIAL_BACKOFF = true;
|
|
21
|
+
/** 默认mDNS查询间隔(毫秒) */
|
|
22
|
+
export declare const DEFAULT_MDNS_QUERY_INTERVAL = 10000;
|
|
23
|
+
/** 默认最大重连次数 */
|
|
24
|
+
export declare const DEFAULT_MAX_RECONNECT_ATTEMPTS = 0;
|
|
25
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/packages/tcp/constants.ts"],"names":[],"mappings":"AAGA,cAAc;AACd,eAAO,MAAM,gBAAgB;;CAEnB,CAAC;AAEX,cAAc;AACd,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,aAAa;AACb,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAe;AACf,eAAO,MAAM,sBAAsB,QAAQ,CAAC;AAE5C,iBAAiB;AACjB,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iBAAiB;AACjB,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,mBAAmB;AACnB,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,mBAAmB;AACnB,eAAO,MAAM,8BAA8B,QAAQ,CAAC;AAEpD,iBAAiB;AACjB,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAEvD,qBAAqB;AACrB,eAAO,MAAM,2BAA2B,QAAQ,CAAC;AAEjD,eAAe;AACf,eAAO,MAAM,8BAA8B,IAAI,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ALL_CODE_LINE: 50
|
|
2
|
+
// AI_CODE_LINE: 20
|
|
3
|
+
/** 支持的设备型号 */
|
|
4
|
+
export const SUPPORTED_MODELS = {
|
|
5
|
+
MINI: 'U5 MINI Event Controller',
|
|
6
|
+
};
|
|
7
|
+
/** 默认TCP端口 */
|
|
8
|
+
export const DEFAULT_TCP_PORT = 17100;
|
|
9
|
+
/** 默认启用心跳 */
|
|
10
|
+
export const DEFAULT_ENABLE_HEARTBEAT = true;
|
|
11
|
+
/** 默认自动重连开关 */
|
|
12
|
+
export const DEFAULT_AUTO_RECONNECT = false;
|
|
13
|
+
/** 默认心跳间隔(毫秒) */
|
|
14
|
+
export const DEFAULT_HEARTBEAT_INTERVAL = 5000;
|
|
15
|
+
/** 默认重连间隔(毫秒) */
|
|
16
|
+
export const DEFAULT_RECONNECT_INTERVAL = 5000;
|
|
17
|
+
/** 默认心跳超时时间(毫秒) */
|
|
18
|
+
export const DEFAULT_HEARTBEAT_TIMEOUT = 5000;
|
|
19
|
+
/** 默认最大重连间隔(毫秒) */
|
|
20
|
+
export const DEFAULT_MAX_RECONNECT_INTERVAL = 60000;
|
|
21
|
+
/** 默认是否启用指数退避 */
|
|
22
|
+
export const DEFAULT_ENABLE_EXPONENTIAL_BACKOFF = true;
|
|
23
|
+
/** 默认mDNS查询间隔(毫秒) */
|
|
24
|
+
export const DEFAULT_MDNS_QUERY_INTERVAL = 10000; // 10秒
|
|
25
|
+
/** 默认最大重连次数 */
|
|
26
|
+
export const DEFAULT_MAX_RECONNECT_ATTEMPTS = 0;
|
|
27
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/packages/tcp/constants.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,mBAAmB;AAEnB,cAAc;AACd,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,IAAI,EAAE,0BAA0B;CACvB,CAAC;AAEX,cAAc;AACd,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAEtC,aAAa;AACb,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C,eAAe;AACf,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAE5C,iBAAiB;AACjB,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAE/C,iBAAiB;AACjB,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAE/C,mBAAmB;AACnB,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C,mBAAmB;AACnB,MAAM,CAAC,MAAM,8BAA8B,GAAG,KAAK,CAAC;AAEpD,iBAAiB;AACjB,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;AAEvD,qBAAqB;AACrB,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC,CAAC,MAAM;AAExD,eAAe;AACf,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC","sourcesContent":["// ALL_CODE_LINE: 50\r\n// AI_CODE_LINE: 20\r\n\r\n/** 支持的设备型号 */\r\nexport const SUPPORTED_MODELS = {\r\n\tMINI: 'U5 MINI Event Controller',\r\n} as const;\r\n\r\n/** 默认TCP端口 */\r\nexport const DEFAULT_TCP_PORT = 17100;\r\n\r\n/** 默认启用心跳 */\r\nexport const DEFAULT_ENABLE_HEARTBEAT = true;\r\n\r\n/** 默认自动重连开关 */\r\nexport const DEFAULT_AUTO_RECONNECT = false;\r\n\r\n/** 默认心跳间隔(毫秒) */\r\nexport const DEFAULT_HEARTBEAT_INTERVAL = 5000;\r\n\r\n/** 默认重连间隔(毫秒) */\r\nexport const DEFAULT_RECONNECT_INTERVAL = 5000;\r\n\r\n/** 默认心跳超时时间(毫秒) */\r\nexport const DEFAULT_HEARTBEAT_TIMEOUT = 5000;\r\n\r\n/** 默认最大重连间隔(毫秒) */\r\nexport const DEFAULT_MAX_RECONNECT_INTERVAL = 60000;\r\n\r\n/** 默认是否启用指数退避 */\r\nexport const DEFAULT_ENABLE_EXPONENTIAL_BACKOFF = true;\r\n\r\n/** 默认mDNS查询间隔(毫秒) */\r\nexport const DEFAULT_MDNS_QUERY_INTERVAL = 10000; // 10秒\r\n\r\n/** 默认最大重连次数 */\r\nexport const DEFAULT_MAX_RECONNECT_ATTEMPTS = 0;\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../../src/packages/tcp/demo.ts"],"names":[],"mappings":"AAOA,iBAAe,IAAI,kBAyKlB;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
|