rol-websocket-channel 1.0.2 → 1.0.6

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.
@@ -1,119 +1,170 @@
1
- // MQTT 客户端类
2
- // 封装 MQTT 连接和消息处理逻辑(使用 MQTT.js 自带的重连功能)
3
-
4
- import type { MqttConfig } from "./types.js";
5
- import * as ConnectionManager from "./connection-manager.js";
6
-
7
- export interface MqttClientOptions {
8
- config: MqttConfig;
9
- abortSignal: AbortSignal;
10
- onMessage: (topic: string, payload: Buffer) => Promise<void>;
11
- onConnect?: () => void;
12
- onDisconnect?: () => void;
13
- onError?: (err: Error) => void;
14
- }
15
-
16
- export class MqttClient {
17
- private config: MqttConfig;
18
- private abortSignal: AbortSignal;
19
- private onMessage: (topic: string, payload: Buffer) => Promise<void>;
20
- private onConnect?: () => void;
21
- private onDisconnect?: () => void;
22
- private onError?: (err: Error) => void;
23
-
24
- constructor(options: MqttClientOptions) {
25
- this.config = options.config;
26
- this.abortSignal = options.abortSignal;
27
- this.onMessage = options.onMessage;
28
- this.onConnect = options.onConnect;
29
- this.onDisconnect = options.onDisconnect;
30
- this.onError = options.onError;
31
- }
32
-
33
- /**
34
- * 连接到 MQTT Broker
35
- * 使用 MQTT.js 自带的重连功能
36
- */
37
- async connect(): Promise<void> {
38
- const { accountId, mqttUrl, mqttTopic } = this.config;
39
-
40
- console.log(`[MQTT] MqttClient.connect(${accountId}): starting connection to ${mqttUrl}...`);
41
-
42
- // 检查是否已中止
43
- if (this.abortSignal.aborted) {
44
- console.log(`[MQTT] MqttClient.connect(${accountId}): aborted before starting`);
45
- return;
46
- }
47
-
48
- // 创建连接
49
- await ConnectionManager.createMqttConnection(
50
- this.config,
51
- {
52
- onConnect: () => {
53
- console.log(`[MQTT] MqttClient(${accountId}): onConnect callback`);
54
- this.onConnect?.();
55
- },
56
- onDisconnect: () => {
57
- console.log(`[MQTT] MqttClient(${accountId}): onDisconnect callback`);
58
- this.onDisconnect?.();
59
- },
60
- onError: (err) => {
61
- console.error(`[MQTT] MqttClient(${accountId}): onError callback - ${err.message}`);
62
- this.onError?.(err);
63
- },
64
- onClose: () => {
65
- console.log(`[MQTT] MqttClient(${accountId}): onClose callback`);
66
- // MQTT.js 会自动重连,不需要我们干预
67
- // 只有在 abortSignal 触发时才真正关闭
68
- if (this.abortSignal.aborted) {
69
- console.log(`[MQTT] MqttClient(${accountId}): abort signaled, cleaning up`);
70
- ConnectionManager.removeConnection(accountId);
71
- }
72
- },
73
- onMessage: this.onMessage,
74
- }
75
- );
76
-
77
- console.log(`[MQTT] MqttClient.connect(${accountId}): connection created, waiting for abort signal...`);
78
-
79
- // 等待中止信号
80
- await new Promise<void>((resolve) => {
81
- const handleAbort = () => {
82
- console.log(`[MQTT] MqttClient(${accountId}): abort signal received`);
83
- this.disconnect();
84
- resolve();
85
- };
86
-
87
- this.abortSignal.addEventListener("abort", handleAbort, { once: true });
88
- });
89
-
90
- console.log(`[MQTT] MqttClient.connect(${accountId}): connection loop ended`);
91
- }
92
-
93
- /**
94
- * 断开连接(手动停止时使用)
95
- */
96
- disconnect(): void {
97
- const { accountId } = this.config;
98
- console.log(`[MQTT] MqttClient.disconnect(${accountId}): disconnecting...`);
99
- ConnectionManager.closeConnection(accountId);
100
- console.log(`[MQTT] MqttClient.disconnect(${accountId}): disconnected`);
101
- }
102
-
103
- /**
104
- * 发布消息
105
- */
106
- publish(topic: string, message: string): boolean {
107
- const { accountId } = this.config;
108
- console.log(`[MQTT] MqttClient.publish(${accountId}): publishing to ${topic}`);
109
- return ConnectionManager.publishMessage(accountId, topic, message);
110
- }
111
-
112
- /**
113
- * 检查是否已连接
114
- */
115
- isConnected(): boolean {
116
- const { accountId } = this.config;
117
- return ConnectionManager.isConnected(accountId);
118
- }
119
- }
1
+ // MQTT 全局客户端
2
+ // 封装全局 MQTT 连接逻辑(使用 MQTT.js 自带的重连功能)
3
+ // 全进程共享单一连接,随机生成 clientId,从 topic 解析用户名
4
+
5
+ import * as ConnectionManager from "./connection-manager.js";
6
+
7
+ // ─────────────────────────────────────────────
8
+ // 选项接口
9
+ // ─────────────────────────────────────────────
10
+
11
+ export interface GlobalMqttClientOptions {
12
+ /** Broker 地址,如 ws://192.168.1.152:8083/mqtt */
13
+ mqttUrl: string;
14
+ /**
15
+ * 原始 topic,格式为 announcement/{user_name}/{agent_id}/...
16
+ * 用于解析用户名(作为 MQTT username)和作为消息发布目标
17
+ * 例:announcement/0b9a784d-e044-4c6f-9024-ef8799c131d1/ce627d3d-9e22-47f7-b1c6-a6ab69570fef/bot
18
+ */
19
+ mqttTopic: string;
20
+ /** AbortSignal,触发时断开连接并结束 connect() */
21
+ abortSignal: AbortSignal;
22
+ /** 收到消息时回调 */
23
+ onMessage: (topic: string, payload: Buffer) => Promise<void>;
24
+ /** 连接成功回调 */
25
+ onConnect?: () => void;
26
+ /** 连接断开回调 */
27
+ onDisconnect?: () => void;
28
+ /** 连接错误回调 */
29
+ onError?: (err: Error) => void;
30
+ }
31
+
32
+ // ─────────────────────────────────────────────
33
+ // GlobalMqttClient
34
+ // ─────────────────────────────────────────────
35
+
36
+ /**
37
+ * 全局共享 MQTT 客户端
38
+ *
39
+ * 全进程只维护一个 MQTT 连接:
40
+ * - clientId 每次启动随机生成
41
+ * - MQTT username 从 mqttTopic 中解析(announcement/{user_name}/...),默认 "default_name"
42
+ * - 订阅通配符 topic(announcement/{user_name}/#),不区分 agent_id
43
+ * - 多处调用 connect() 时复用同一连接
44
+ */
45
+ export class GlobalMqttClient {
46
+ private mqttUrl: string;
47
+ private mqttTopic: string;
48
+ private abortSignal: AbortSignal;
49
+ private onMessageCb: (topic: string, payload: Buffer) => Promise<void>;
50
+ private onConnectCb?: () => void;
51
+ private onDisconnectCb?: () => void;
52
+ private onErrorCb?: (err: Error) => void;
53
+
54
+ constructor(options: GlobalMqttClientOptions) {
55
+ this.mqttUrl = options.mqttUrl;
56
+ this.mqttTopic = options.mqttTopic;
57
+ this.abortSignal = options.abortSignal;
58
+ this.onMessageCb = options.onMessage;
59
+ this.onConnectCb = options.onConnect;
60
+ this.onDisconnectCb = options.onDisconnect;
61
+ this.onErrorCb = options.onError;
62
+ }
63
+
64
+ /**
65
+ * 建立全局 MQTT 连接并等待 AbortSignal
66
+ *
67
+ * 若全局连接已存在且已连接则直接复用。
68
+ * 方法持续挂起直到 abortSignal 触发,触发后自动断开并 resolve。
69
+ */
70
+ async connect(): Promise<void> {
71
+ const username = ConnectionManager.parseUsernameFromTopic(this.mqttTopic);
72
+ const subscribeTopic = ConnectionManager.getSubscribeTopic(this.mqttTopic);
73
+
74
+ console.log(
75
+ `[MQTT] GlobalMqttClient.connect(): url=${this.mqttUrl}, username=${username}, subscribeTopic=${subscribeTopic}`,
76
+ );
77
+
78
+ if (this.abortSignal.aborted) {
79
+ console.log("[MQTT] GlobalMqttClient.connect(): aborted before starting");
80
+ return;
81
+ }
82
+
83
+ await ConnectionManager.createGlobalMqttConnection(
84
+ this.mqttUrl,
85
+ this.mqttTopic,
86
+ {
87
+ onConnect: () => {
88
+ console.log("[MQTT] GlobalMqttClient: onConnect");
89
+ this.onConnectCb?.();
90
+ },
91
+ onDisconnect: () => {
92
+ console.log("[MQTT] GlobalMqttClient: onDisconnect");
93
+ this.onDisconnectCb?.();
94
+ },
95
+ onError: (err) => {
96
+ console.error(`[MQTT] GlobalMqttClient: onError - ${err.message}`);
97
+ this.onErrorCb?.(err);
98
+ },
99
+ onClose: () => {
100
+ console.log("[MQTT] GlobalMqttClient: onClose");
101
+ // MQTT.js 会自动重连;仅在 abort 触发时才真正清理全局连接
102
+ if (this.abortSignal.aborted) {
103
+ ConnectionManager.closeGlobalConnection();
104
+ }
105
+ },
106
+ onMessage: this.onMessageCb,
107
+ },
108
+ );
109
+
110
+ console.log(
111
+ "[MQTT] GlobalMqttClient.connect(): connection created, waiting for abort signal...",
112
+ );
113
+
114
+ // 挂起,直到 AbortSignal 触发
115
+ await new Promise<void>((resolve) => {
116
+ const handleAbort = () => {
117
+ console.log(
118
+ "[MQTT] GlobalMqttClient: abort signal received, disconnecting",
119
+ );
120
+ this.disconnect();
121
+ resolve();
122
+ };
123
+ this.abortSignal.addEventListener("abort", handleAbort, { once: true });
124
+ });
125
+
126
+ console.log("[MQTT] GlobalMqttClient.connect(): connection loop ended");
127
+ }
128
+
129
+ /**
130
+ * 手动断开全局连接
131
+ */
132
+ disconnect(): void {
133
+ console.log(
134
+ "[MQTT] GlobalMqttClient.disconnect(): closing global connection",
135
+ );
136
+ ConnectionManager.closeGlobalConnection();
137
+ }
138
+
139
+ /**
140
+ * 向指定 topic 发布消息
141
+ * @returns 发布成功返回 true,未连接返回 false
142
+ */
143
+ publish(topic: string, message: string): boolean {
144
+ console.log(`[MQTT] GlobalMqttClient.publish(): topic=${topic}`);
145
+ return ConnectionManager.publishGlobalMessage(topic, message);
146
+ }
147
+
148
+ /**
149
+ * 检查全局连接是否处于已连接状态
150
+ */
151
+ isConnected(): boolean {
152
+ return ConnectionManager.isGlobalConnected();
153
+ }
154
+
155
+ /**
156
+ * 获取当前解析的订阅 topic(通配符形式)
157
+ * 例:announcement/0b9a784d-e044-4c6f-9024-ef8799c131d1/#
158
+ */
159
+ getSubscribeTopic(): string {
160
+ return ConnectionManager.getSubscribeTopic(this.mqttTopic);
161
+ }
162
+
163
+ /**
164
+ * 获取从 topic 中解析的用户名
165
+ * 例:announcement/myuser/agent/bot → "myuser"
166
+ */
167
+ getUsername(): string {
168
+ return ConnectionManager.parseUsernameFromTopic(this.mqttTopic);
169
+ }
170
+ }