dg-lab-mcp-sse-server 1.0.3 → 1.1.3
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 +21 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/cli.js +127 -18
- package/dist/cli.js.map +4 -4
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.js +127 -18
- package/dist/index.js.map +4 -4
- package/dist/session-manager.d.ts +38 -1
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/tools/control-tools.d.ts.map +1 -1
- package/dist/tools/device-tools.d.ts.map +1 -1
- package/dist/ws-server.d.ts +7 -1
- package/dist/ws-server.d.ts.map +1 -1
- package/package.json +63 -63
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
- **波形管理**: 支持解析、保存、发送 DG-LAB 波形数据
|
|
11
11
|
- **持续播放**: 支持波形持续循环播放
|
|
12
12
|
- **会话管理**: 支持设备别名、多设备管理
|
|
13
|
+
- **断线重连**: 设备断开后保留会话,支持在超时时间内重连而不丢失设置
|
|
13
14
|
|
|
14
15
|
## 安装
|
|
15
16
|
|
|
@@ -69,7 +70,8 @@ PORT=8080 npx dg-lab-mcp-sse-server
|
|
|
69
70
|
"env": {
|
|
70
71
|
"PUBLIC_IP": "your.public.ip",
|
|
71
72
|
"PORT": "3323",
|
|
72
|
-
"CONNECTION_TIMEOUT_MINUTES": "10"
|
|
73
|
+
"CONNECTION_TIMEOUT_MINUTES": "10",
|
|
74
|
+
"RECONNECTION_TIMEOUT_MINUTES": "5"
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -87,10 +89,28 @@ PORT=8080 npx dg-lab-mcp-sse-server
|
|
|
87
89
|
| `SSE_PATH` | /sse | SSE 端点路径 |
|
|
88
90
|
| `POST_PATH` | /message | POST 端点路径 |
|
|
89
91
|
| `CONNECTION_TIMEOUT_MINUTES` | 5 | 未绑定设备的超时时间(分钟) |
|
|
92
|
+
| `RECONNECTION_TIMEOUT_MINUTES` | 5 | 已绑定设备断开后的重连等待时间(分钟),超时后会话将被删除 |
|
|
90
93
|
| `HEARTBEAT_INTERVAL` | 30000 | WebSocket 心跳间隔 (ms) |
|
|
91
94
|
| `STALE_DEVICE_TIMEOUT` | 3600000 | 设备活跃超时 (ms),默认 1 小时 |
|
|
92
95
|
| `WAVEFORM_STORE_PATH` | ./data/waveforms.json | 波形存储路径 |
|
|
93
96
|
|
|
97
|
+
## 会话管理机制
|
|
98
|
+
|
|
99
|
+
### 连接超时 (CONNECTION_TIMEOUT_MINUTES)
|
|
100
|
+
- 创建设备后,如果在指定时间内未完成 APP 绑定,会话将自动销毁
|
|
101
|
+
- 默认 5 分钟,可通过环境变量配置
|
|
102
|
+
|
|
103
|
+
### 重连超时 (RECONNECTION_TIMEOUT_MINUTES)
|
|
104
|
+
- 已绑定的设备断开连接后,会话会保留一段时间等待重连
|
|
105
|
+
- 在此期间设备可以重新连接而不丢失设置(强度、波形等)
|
|
106
|
+
- 超时后会话将被自动删除
|
|
107
|
+
- 默认 5 分钟,可通过环境变量配置
|
|
108
|
+
- **注意**: 未绑定的设备断开后会立即删除,不会等待重连
|
|
109
|
+
|
|
110
|
+
### 状态查询
|
|
111
|
+
- 使用 `dg_list_devices` 可以看到设备的连接状态和剩余重连时间
|
|
112
|
+
- 使用 `dg_get_device_status` 可以获取详细的连接信息,包括 `disconnectedAt` 和 `reconnectionTimeRemaining`
|
|
113
|
+
|
|
94
114
|
## 使用流程
|
|
95
115
|
|
|
96
116
|
1. **创建设备**: 调用 `dg_create_device` 获取二维码内容
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAwB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAI5C,OAAO,EAAE,eAAe,EAAiB,MAAM,oBAAoB,CAAC;AAGpE;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,aAAa,CAAC;IACxB,eAAe,EAAE,eAAe,CAAC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAwB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAI5C,OAAO,EAAE,eAAe,EAAiB,MAAM,oBAAoB,CAAC;AAGpE;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,aAAa,CAAC;IACxB,eAAe,EAAE,eAAe,CAAC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,IAAI,GAAG,CAkD/B;AA4HD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBtD"}
|
package/dist/cli.js
CHANGED
|
@@ -91,7 +91,8 @@ function loadConfig() {
|
|
|
91
91
|
waveformStorePath: getEnvString("WAVEFORM_STORE_PATH", "./data/waveforms.json"),
|
|
92
92
|
heartbeatInterval: getEnvNumber("HEARTBEAT_INTERVAL", 3e4),
|
|
93
93
|
staleDeviceTimeout: getEnvNumber("STALE_DEVICE_TIMEOUT", 36e5),
|
|
94
|
-
connectionTimeoutMinutes: getEnvNumber("CONNECTION_TIMEOUT_MINUTES", 5)
|
|
94
|
+
connectionTimeoutMinutes: getEnvNumber("CONNECTION_TIMEOUT_MINUTES", 5),
|
|
95
|
+
reconnectionTimeoutMinutes: getEnvNumber("RECONNECTION_TIMEOUT_MINUTES", 5)
|
|
95
96
|
};
|
|
96
97
|
validateConfig(config);
|
|
97
98
|
return config;
|
|
@@ -146,6 +147,12 @@ function validateConfig(config) {
|
|
|
146
147
|
context: { connectionTimeoutMinutes: config.connectionTimeoutMinutes }
|
|
147
148
|
});
|
|
148
149
|
}
|
|
150
|
+
if (config.reconnectionTimeoutMinutes < 1 || config.reconnectionTimeoutMinutes > 60) {
|
|
151
|
+
throw new ConfigError(`\u91CD\u8FDE\u8D85\u65F6\u65F6\u95F4\u65E0\u6548: ${config.reconnectionTimeoutMinutes}\uFF0C\u5FC5\u987B\u5728 1-60 \u5206\u949F\u8303\u56F4\u5185`, {
|
|
152
|
+
code: "CONFIG_LOAD_FAILED" /* CONFIG_LOAD_FAILED */,
|
|
153
|
+
context: { reconnectionTimeoutMinutes: config.reconnectionTimeoutMinutes }
|
|
154
|
+
});
|
|
155
|
+
}
|
|
149
156
|
}
|
|
150
157
|
var configInstance = null;
|
|
151
158
|
function getConfig() {
|
|
@@ -754,10 +761,14 @@ var SessionManager = class {
|
|
|
754
761
|
cleanupTimer = null;
|
|
755
762
|
/** 连接超时时间(毫秒) */
|
|
756
763
|
connectionTimeoutMs;
|
|
757
|
-
|
|
764
|
+
/** 重连超时时间(毫秒) */
|
|
765
|
+
reconnectionTimeoutMs;
|
|
766
|
+
constructor(connectionTimeoutMinutes = 5, reconnectionTimeoutMinutes = 5) {
|
|
758
767
|
this.connectionTimeoutMs = connectionTimeoutMinutes * 60 * 1e3;
|
|
768
|
+
this.reconnectionTimeoutMs = reconnectionTimeoutMinutes * 60 * 1e3;
|
|
759
769
|
this.startCleanupTimer();
|
|
760
770
|
console.log(`[\u4F1A\u8BDD] \u8FDE\u63A5\u8D85\u65F6\u8BBE\u7F6E: ${connectionTimeoutMinutes} \u5206\u949F`);
|
|
771
|
+
console.log(`[\u4F1A\u8BDD] \u91CD\u8FDE\u8D85\u65F6\u8BBE\u7F6E: ${reconnectionTimeoutMinutes} \u5206\u949F`);
|
|
761
772
|
}
|
|
762
773
|
/**
|
|
763
774
|
* 创建新的设备会话
|
|
@@ -785,7 +796,9 @@ var SessionManager = class {
|
|
|
785
796
|
strengthLimitB: 200,
|
|
786
797
|
lastActive: now,
|
|
787
798
|
createdAt: now,
|
|
788
|
-
connectionTimeoutId: null
|
|
799
|
+
connectionTimeoutId: null,
|
|
800
|
+
reconnectionTimeoutId: null,
|
|
801
|
+
disconnectedAt: null
|
|
789
802
|
};
|
|
790
803
|
session.connectionTimeoutId = setTimeout(() => {
|
|
791
804
|
const currentSession = this.sessions.get(deviceId);
|
|
@@ -858,6 +871,10 @@ var SessionManager = class {
|
|
|
858
871
|
clearTimeout(session.connectionTimeoutId);
|
|
859
872
|
session.connectionTimeoutId = null;
|
|
860
873
|
}
|
|
874
|
+
if (session.reconnectionTimeoutId) {
|
|
875
|
+
clearTimeout(session.reconnectionTimeoutId);
|
|
876
|
+
session.reconnectionTimeoutId = null;
|
|
877
|
+
}
|
|
861
878
|
if (session.ws) {
|
|
862
879
|
try {
|
|
863
880
|
session.ws.close();
|
|
@@ -985,6 +1002,83 @@ var SessionManager = class {
|
|
|
985
1002
|
}
|
|
986
1003
|
return false;
|
|
987
1004
|
}
|
|
1005
|
+
/**
|
|
1006
|
+
* 处理设备断开连接
|
|
1007
|
+
*
|
|
1008
|
+
* 如果设备已绑定,启动重连超时计时器并保留会话。
|
|
1009
|
+
* 如果设备未绑定,立即删除会话。
|
|
1010
|
+
*
|
|
1011
|
+
* @param deviceId - 设备 ID
|
|
1012
|
+
* @returns 是否保留会话等待重连(true=保留,false=已删除)
|
|
1013
|
+
*/
|
|
1014
|
+
handleDisconnection(deviceId) {
|
|
1015
|
+
const session = this.sessions.get(deviceId);
|
|
1016
|
+
if (!session) return false;
|
|
1017
|
+
if (session.connectionTimeoutId) {
|
|
1018
|
+
clearTimeout(session.connectionTimeoutId);
|
|
1019
|
+
session.connectionTimeoutId = null;
|
|
1020
|
+
}
|
|
1021
|
+
if (!session.boundToApp) {
|
|
1022
|
+
console.log(`[\u4F1A\u8BDD] \u672A\u7ED1\u5B9A\u8BBE\u5907\u65AD\u5F00: ${deviceId}\uFF0C\u7ACB\u5373\u5220\u9664`);
|
|
1023
|
+
this.deleteSession(deviceId);
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1026
|
+
session.connected = false;
|
|
1027
|
+
session.disconnectedAt = /* @__PURE__ */ new Date();
|
|
1028
|
+
session.ws = null;
|
|
1029
|
+
session.reconnectionTimeoutId = setTimeout(() => {
|
|
1030
|
+
const currentSession = this.sessions.get(deviceId);
|
|
1031
|
+
if (currentSession && !currentSession.connected) {
|
|
1032
|
+
console.log(`[\u4F1A\u8BDD] \u91CD\u8FDE\u8D85\u65F6: ${deviceId} (${this.reconnectionTimeoutMs / 6e4} \u5206\u949F\u5185\u672A\u91CD\u8FDE)`);
|
|
1033
|
+
this.deleteSession(deviceId);
|
|
1034
|
+
}
|
|
1035
|
+
}, this.reconnectionTimeoutMs);
|
|
1036
|
+
console.log(`[\u4F1A\u8BDD] \u8BBE\u5907\u65AD\u5F00: ${deviceId}\uFF0C\u7B49\u5F85\u91CD\u8FDE (${this.reconnectionTimeoutMs / 6e4} \u5206\u949F)`);
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* 处理设备重新连接
|
|
1041
|
+
*
|
|
1042
|
+
* 恢复会话到已连接状态,取消重连超时计时器。
|
|
1043
|
+
* 保留所有会话数据(别名、强度等)。
|
|
1044
|
+
*
|
|
1045
|
+
* @param deviceId - 设备 ID
|
|
1046
|
+
* @param ws - 新的 WebSocket 连接
|
|
1047
|
+
* @param clientId - WebSocket 服务器分配的新 clientId
|
|
1048
|
+
* @returns 是否成功重连
|
|
1049
|
+
*/
|
|
1050
|
+
handleReconnection(deviceId, ws, clientId) {
|
|
1051
|
+
const session = this.sessions.get(deviceId);
|
|
1052
|
+
if (!session) return false;
|
|
1053
|
+
if (session.reconnectionTimeoutId) {
|
|
1054
|
+
clearTimeout(session.reconnectionTimeoutId);
|
|
1055
|
+
session.reconnectionTimeoutId = null;
|
|
1056
|
+
}
|
|
1057
|
+
session.ws = ws;
|
|
1058
|
+
session.clientId = clientId;
|
|
1059
|
+
session.connected = true;
|
|
1060
|
+
session.disconnectedAt = null;
|
|
1061
|
+
session.lastActive = /* @__PURE__ */ new Date();
|
|
1062
|
+
console.log(`[\u4F1A\u8BDD] \u8BBE\u5907\u91CD\u8FDE\u6210\u529F: ${deviceId}`);
|
|
1063
|
+
return true;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* 获取剩余重连时间
|
|
1067
|
+
*
|
|
1068
|
+
* 计算设备断开后还有多少时间可以重连。
|
|
1069
|
+
*
|
|
1070
|
+
* @param deviceId - 设备 ID
|
|
1071
|
+
* @returns 剩余时间(毫秒),如果设备已连接或不存在则返回 null
|
|
1072
|
+
*/
|
|
1073
|
+
getReconnectionTimeRemaining(deviceId) {
|
|
1074
|
+
const session = this.sessions.get(deviceId);
|
|
1075
|
+
if (!session || session.connected || !session.disconnectedAt) {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
const elapsed = Date.now() - session.disconnectedAt.getTime();
|
|
1079
|
+
const remaining = this.reconnectionTimeoutMs - elapsed;
|
|
1080
|
+
return remaining > 0 ? remaining : 0;
|
|
1081
|
+
}
|
|
988
1082
|
/**
|
|
989
1083
|
* 获取当前会话数量
|
|
990
1084
|
*/
|
|
@@ -1059,6 +1153,9 @@ var SessionManager = class {
|
|
|
1059
1153
|
if (session.connectionTimeoutId) {
|
|
1060
1154
|
clearTimeout(session.connectionTimeoutId);
|
|
1061
1155
|
}
|
|
1156
|
+
if (session.reconnectionTimeoutId) {
|
|
1157
|
+
clearTimeout(session.reconnectionTimeoutId);
|
|
1158
|
+
}
|
|
1062
1159
|
if (session.ws) {
|
|
1063
1160
|
try {
|
|
1064
1161
|
session.ws.close();
|
|
@@ -1071,13 +1168,8 @@ var SessionManager = class {
|
|
|
1071
1168
|
};
|
|
1072
1169
|
|
|
1073
1170
|
// src/ws-server.ts
|
|
1074
|
-
import { WebSocketServer, WebSocket
|
|
1171
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
1075
1172
|
import { v4 as uuidv43 } from "uuid";
|
|
1076
|
-
|
|
1077
|
-
// src/ws-bridge.ts
|
|
1078
|
-
import WebSocket from "ws";
|
|
1079
|
-
|
|
1080
|
-
// src/ws-server.ts
|
|
1081
1173
|
var DGLabWSServer = class {
|
|
1082
1174
|
wss = null;
|
|
1083
1175
|
clients = /* @__PURE__ */ new Map();
|
|
@@ -1385,7 +1477,7 @@ var DGLabWSServer = class {
|
|
|
1385
1477
|
}
|
|
1386
1478
|
/** 发送消息到 WebSocket */
|
|
1387
1479
|
send(ws, msg) {
|
|
1388
|
-
if (ws.readyState ===
|
|
1480
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
1389
1481
|
ws.send(JSON.stringify(msg));
|
|
1390
1482
|
}
|
|
1391
1483
|
}
|
|
@@ -1408,7 +1500,7 @@ var DGLabWSServer = class {
|
|
|
1408
1500
|
/** 创建内部控制器的模拟 WebSocket */
|
|
1409
1501
|
createMockWebSocket(clientId) {
|
|
1410
1502
|
return {
|
|
1411
|
-
readyState:
|
|
1503
|
+
readyState: WebSocket.OPEN,
|
|
1412
1504
|
send: (data) => {
|
|
1413
1505
|
console.log(`[WS \u670D\u52A1\u5668] \u53D1\u9001\u7ED9\u63A7\u5236\u5668 ${clientId}: ${data}`);
|
|
1414
1506
|
},
|
|
@@ -1475,7 +1567,7 @@ var DGLabWSServer = class {
|
|
|
1475
1567
|
}
|
|
1476
1568
|
if (client.boundTo) {
|
|
1477
1569
|
const appClient = this.clients.get(client.boundTo);
|
|
1478
|
-
if (appClient && appClient.ws.readyState ===
|
|
1570
|
+
if (appClient && appClient.ws.readyState === WebSocket.OPEN) {
|
|
1479
1571
|
this.send(appClient.ws, {
|
|
1480
1572
|
type: "break",
|
|
1481
1573
|
clientId: controllerId,
|
|
@@ -1491,7 +1583,7 @@ var DGLabWSServer = class {
|
|
|
1491
1583
|
this.options.onBindChange(controllerId, null);
|
|
1492
1584
|
}
|
|
1493
1585
|
}
|
|
1494
|
-
if (client.ws.readyState ===
|
|
1586
|
+
if (client.ws.readyState === WebSocket.OPEN) {
|
|
1495
1587
|
client.ws.close(1e3, "Disconnected by user");
|
|
1496
1588
|
}
|
|
1497
1589
|
this.clients.delete(controllerId);
|
|
@@ -1720,6 +1812,7 @@ function registerDeviceTools(toolManager, sessionManager, wsServer, publicIp) {
|
|
|
1720
1812
|
- boundToApp: APP\u662F\u5426\u5DF2\u626B\u7801\u7ED1\u5B9A\uFF08\u5FC5\u987B\u4E3Atrue\u624D\u80FD\u63A7\u5236\u8BBE\u5907\uFF09
|
|
1721
1813
|
- strengthA/B: \u5F53\u524DA/B\u901A\u9053\u5F3A\u5EA6(0-200)
|
|
1722
1814
|
- strengthLimitA/B: A/B\u901A\u9053\u5F3A\u5EA6\u4E0A\u9650\uFF08\u7531APP\u8BBE\u7F6E\uFF09
|
|
1815
|
+
- reconnectionTimeRemaining: \u5269\u4F59\u91CD\u8FDE\u65F6\u95F4\uFF08\u79D2\uFF09\uFF0C\u4EC5\u5728\u8BBE\u5907\u65AD\u5F00\u65F6\u663E\u793A\uFF0Cnull\u8868\u793A\u8BBE\u5907\u5DF2\u8FDE\u63A5
|
|
1723
1816
|
\u53EF\u9009\u53C2\u6570alias\u7528\u4E8E\u6309\u522B\u540D\u8FC7\u6EE4\u8BBE\u5907\u3002`,
|
|
1724
1817
|
{
|
|
1725
1818
|
type: "object",
|
|
@@ -1739,6 +1832,8 @@ function registerDeviceTools(toolManager, sessionManager, wsServer, publicIp) {
|
|
|
1739
1832
|
}
|
|
1740
1833
|
const devices = sessions.map((s) => {
|
|
1741
1834
|
const isBound = s.clientId ? wsServer.isControllerBound(s.clientId) : false;
|
|
1835
|
+
const reconnectionTimeRemaining = sessionManager.getReconnectionTimeRemaining(s.deviceId);
|
|
1836
|
+
const reconnectionTimeRemainingSeconds = reconnectionTimeRemaining !== null ? Math.ceil(reconnectionTimeRemaining / 1e3) : null;
|
|
1742
1837
|
return {
|
|
1743
1838
|
deviceId: s.deviceId,
|
|
1744
1839
|
alias: s.alias,
|
|
@@ -1747,7 +1842,8 @@ function registerDeviceTools(toolManager, sessionManager, wsServer, publicIp) {
|
|
|
1747
1842
|
strengthA: s.strengthA,
|
|
1748
1843
|
strengthB: s.strengthB,
|
|
1749
1844
|
strengthLimitA: s.strengthLimitA,
|
|
1750
|
-
strengthLimitB: s.strengthLimitB
|
|
1845
|
+
strengthLimitB: s.strengthLimitB,
|
|
1846
|
+
reconnectionTimeRemaining: reconnectionTimeRemainingSeconds
|
|
1751
1847
|
};
|
|
1752
1848
|
});
|
|
1753
1849
|
return createToolResult(JSON.stringify({ devices, count: devices.length }));
|
|
@@ -1818,6 +1914,8 @@ function registerDeviceTools(toolManager, sessionManager, wsServer, publicIp) {
|
|
|
1818
1914
|
const sessions = sessionManager.findByAlias(alias);
|
|
1819
1915
|
const devices = sessions.map((s) => {
|
|
1820
1916
|
const isBound = s.clientId ? wsServer.isControllerBound(s.clientId) : false;
|
|
1917
|
+
const reconnectionTimeRemaining = sessionManager.getReconnectionTimeRemaining(s.deviceId);
|
|
1918
|
+
const reconnectionTimeRemainingSeconds = reconnectionTimeRemaining !== null ? Math.ceil(reconnectionTimeRemaining / 1e3) : null;
|
|
1821
1919
|
return {
|
|
1822
1920
|
deviceId: s.deviceId,
|
|
1823
1921
|
alias: s.alias,
|
|
@@ -1826,7 +1924,8 @@ function registerDeviceTools(toolManager, sessionManager, wsServer, publicIp) {
|
|
|
1826
1924
|
strengthA: s.strengthA,
|
|
1827
1925
|
strengthB: s.strengthB,
|
|
1828
1926
|
strengthLimitA: s.strengthLimitA,
|
|
1829
|
-
strengthLimitB: s.strengthLimitB
|
|
1927
|
+
strengthLimitB: s.strengthLimitB,
|
|
1928
|
+
reconnectionTimeRemaining: reconnectionTimeRemainingSeconds
|
|
1830
1929
|
};
|
|
1831
1930
|
});
|
|
1832
1931
|
return createToolResult(
|
|
@@ -2785,8 +2884,11 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
|
|
|
2785
2884
|
`\u83B7\u53D6\u8BBE\u5907\u5B8C\u6574\u72B6\u6001\u4FE1\u606F\u3002
|
|
2786
2885
|
\u5173\u952E\u5B57\u6BB5\uFF1A
|
|
2787
2886
|
- boundToApp: \u662F\u5426\u5DF2\u7ED1\u5B9AAPP\uFF08\u5FC5\u987B\u4E3Atrue\u624D\u80FD\u63A7\u5236\u8BBE\u5907\uFF09
|
|
2887
|
+
- connected: \u8BBE\u5907\u662F\u5426\u5DF2\u8FDE\u63A5
|
|
2788
2888
|
- strengthA/B: \u5F53\u524DA/B\u901A\u9053\u5F3A\u5EA6
|
|
2789
2889
|
- strengthLimitA/B: A/B\u901A\u9053\u5F3A\u5EA6\u4E0A\u9650\uFF08\u7531APP\u8BBE\u7F6E\uFF0C\u4E0D\u53EF\u8D85\u8FC7\uFF09
|
|
2890
|
+
- disconnectedAt: \u8BBE\u5907\u65AD\u5F00\u8FDE\u63A5\u7684\u65F6\u95F4\u6233\uFF08\u4EC5\u5728\u65AD\u5F00\u65F6\u663E\u793A\uFF09
|
|
2891
|
+
- reconnectionTimeRemaining: \u5269\u4F59\u91CD\u8FDE\u65F6\u95F4\uFF08\u79D2\uFF09\uFF0C\u4EC5\u5728\u8BBE\u5907\u65AD\u5F00\u65F6\u663E\u793A
|
|
2790
2892
|
\u5EFA\u8BAE\u5728dg_connect\u540E\u5728\u7528\u6237\u8BF4\u5DF2\u5B8C\u6210\u540E\u4F7F\u7528\u6B64\u63A5\u53E3\u68C0\u67E5boundToApp\u72B6\u6001\u3002`,
|
|
2791
2893
|
{
|
|
2792
2894
|
type: "object",
|
|
@@ -2805,6 +2907,8 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
|
|
|
2805
2907
|
if ("error" in deviceResult) return createToolError(deviceResult.error);
|
|
2806
2908
|
const session = deviceResult.session;
|
|
2807
2909
|
const isBound = session.clientId ? wsServer.isControllerBound(session.clientId) : false;
|
|
2910
|
+
const reconnectionTimeRemaining = sessionManager.getReconnectionTimeRemaining(session.deviceId);
|
|
2911
|
+
const reconnectionTimeRemainingSeconds = reconnectionTimeRemaining !== null ? Math.ceil(reconnectionTimeRemaining / 1e3) : null;
|
|
2808
2912
|
return createToolResult(
|
|
2809
2913
|
JSON.stringify({
|
|
2810
2914
|
deviceId: session.deviceId,
|
|
@@ -2814,7 +2918,9 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
|
|
|
2814
2918
|
strengthA: session.strengthA,
|
|
2815
2919
|
strengthB: session.strengthB,
|
|
2816
2920
|
strengthLimitA: session.strengthLimitA,
|
|
2817
|
-
strengthLimitB: session.strengthLimitB
|
|
2921
|
+
strengthLimitB: session.strengthLimitB,
|
|
2922
|
+
disconnectedAt: session.disconnectedAt ? session.disconnectedAt.toISOString() : null,
|
|
2923
|
+
reconnectionTimeRemaining: reconnectionTimeRemainingSeconds
|
|
2818
2924
|
})
|
|
2819
2925
|
);
|
|
2820
2926
|
}
|
|
@@ -3017,8 +3123,11 @@ function createApp() {
|
|
|
3017
3123
|
const toolManager = new ToolManager(() => {
|
|
3018
3124
|
broadcastNotification(server, "notifications/tools/list_changed");
|
|
3019
3125
|
});
|
|
3020
|
-
const sessionManager = new SessionManager(
|
|
3021
|
-
|
|
3126
|
+
const sessionManager = new SessionManager(
|
|
3127
|
+
config.connectionTimeoutMinutes,
|
|
3128
|
+
config.reconnectionTimeoutMinutes
|
|
3129
|
+
);
|
|
3130
|
+
console.log(`[\u4F1A\u8BDD] \u4EC5\u5185\u5B58\u6A21\u5F0F\uFF08\u8FDE\u63A5\u8D85\u65F6: ${config.connectionTimeoutMinutes} \u5206\u949F\uFF0C\u91CD\u8FDE\u8D85\u65F6: ${config.reconnectionTimeoutMinutes} \u5206\u949F\uFF0C\u6D3B\u8DC3\u8D85\u65F6: 1 \u5C0F\u65F6\uFF09`);
|
|
3022
3131
|
const wsServer = createWSServer(config, sessionManager);
|
|
3023
3132
|
const waveformStorage2 = initWaveforms(config);
|
|
3024
3133
|
registerProtocolAndTools(server, toolManager, sessionManager, wsServer, config);
|