dg-lab-mcp-sse-server 1.1.3 → 1.2.0

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/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,CAkD/B;AA4HD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBtD"}
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;AA2HD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBtD"}
package/dist/cli.js CHANGED
@@ -1238,7 +1238,7 @@ var DGLabWSServer = class {
1238
1238
  this.waveformTimers.clear();
1239
1239
  for (const playback of this.continuousPlaybacks.values()) {
1240
1240
  if (playback.timerId) {
1241
- clearInterval(playback.timerId);
1241
+ clearTimeout(playback.timerId);
1242
1242
  }
1243
1243
  }
1244
1244
  this.continuousPlaybacks.clear();
@@ -1401,7 +1401,7 @@ var DGLabWSServer = class {
1401
1401
  for (const [key, playback] of this.continuousPlaybacks.entries()) {
1402
1402
  if (playback.controllerId === clientId) {
1403
1403
  if (playback.timerId) {
1404
- clearInterval(playback.timerId);
1404
+ clearTimeout(playback.timerId);
1405
1405
  }
1406
1406
  this.continuousPlaybacks.delete(key);
1407
1407
  console.log(`[WS \u670D\u52A1\u5668] \u5DF2\u505C\u6B62\u6301\u7EED\u64AD\u653E: ${key}`);
@@ -1559,7 +1559,7 @@ var DGLabWSServer = class {
1559
1559
  const state = this.continuousPlaybacks.get(key);
1560
1560
  if (state) {
1561
1561
  if (state.timerId) {
1562
- clearInterval(state.timerId);
1562
+ clearTimeout(state.timerId);
1563
1563
  }
1564
1564
  this.continuousPlaybacks.delete(key);
1565
1565
  console.log(`[WS \u670D\u52A1\u5668] \u5DF2\u505C\u6B62\u6301\u7EED\u64AD\u653E: ${key}`);
@@ -1630,16 +1630,16 @@ var DGLabWSServer = class {
1630
1630
  * 启动持续播放
1631
1631
  *
1632
1632
  * 循环发送波形数据到指定通道,直到手动停止。
1633
- * 每次发送一批波形,按间隔循环发送。
1633
+ * 使用动态等待机制,根据实际播放时长和发送耗时计算等待时间。
1634
1634
  *
1635
1635
  * @param controllerId - 控制器 ID
1636
1636
  * @param channel - 目标通道 A 或 B
1637
1637
  * @param waveforms - 要循环播放的波形数据
1638
- * @param interval - 发送间隔(毫秒),默认 100ms
1639
1638
  * @param batchSize - 每次发送的波形数量,默认 5
1639
+ * @param bufferRatio - 缓冲比例(0.5-1.0),默认 0.9,用于计算等待时间
1640
1640
  * @returns 是否成功启动
1641
1641
  */
1642
- startContinuousPlayback(controllerId, channel, waveforms, interval = 100, batchSize = 5) {
1642
+ startContinuousPlayback(controllerId, channel, waveforms, batchSize = 5, bufferRatio = 0.9) {
1643
1643
  if (!this.isControllerBound(controllerId)) {
1644
1644
  console.log(`[WS \u670D\u52A1\u5668] \u6301\u7EED\u64AD\u653E\u5931\u8D25: \u63A7\u5236\u5668 ${controllerId} \u672A\u7ED1\u5B9A APP`);
1645
1645
  return false;
@@ -1652,35 +1652,68 @@ var DGLabWSServer = class {
1652
1652
  if (this.continuousPlaybacks.has(key)) {
1653
1653
  this.stopContinuousPlayback(controllerId, channel);
1654
1654
  }
1655
+ const validBufferRatio = bufferRatio >= 0.5 && bufferRatio <= 1 ? bufferRatio : 0.9;
1656
+ const playbackDuration = batchSize * 100;
1655
1657
  const state = {
1656
1658
  controllerId,
1657
1659
  channel,
1658
1660
  waveforms,
1659
1661
  currentIndex: 0,
1660
- interval,
1661
1662
  batchSize,
1663
+ bufferRatio: validBufferRatio,
1664
+ playbackDuration,
1662
1665
  timerId: null,
1663
- active: true
1664
- };
1665
- state.timerId = setInterval(() => {
1666
- if (!state.active) {
1667
- return;
1668
- }
1669
- const batch = [];
1670
- for (let i = 0; i < state.batchSize; i++) {
1671
- batch.push(state.waveforms[state.currentIndex]);
1672
- state.currentIndex = (state.currentIndex + 1) % state.waveforms.length;
1673
- }
1674
- const success = this.sendWaveform(controllerId, channel, batch);
1675
- if (!success) {
1676
- console.log(`[WS \u670D\u52A1\u5668] \u6301\u7EED\u64AD\u653E\u53D1\u9001\u5931\u8D25\uFF0C\u505C\u6B62\u64AD\u653E: ${key}`);
1677
- this.stopContinuousPlayback(controllerId, channel);
1666
+ active: true,
1667
+ stats: {
1668
+ sendCount: 0,
1669
+ totalElapsedTime: 0,
1670
+ lastSendTime: 0
1678
1671
  }
1679
- }, interval);
1672
+ };
1680
1673
  this.continuousPlaybacks.set(key, state);
1681
- console.log(`[WS \u670D\u52A1\u5668] \u5DF2\u542F\u52A8\u6301\u7EED\u64AD\u653E: ${key}\uFF0C\u6CE2\u5F62\u6570: ${waveforms.length}\uFF0C\u95F4\u9694: ${interval}ms`);
1674
+ console.log(`[WS \u670D\u52A1\u5668] \u5DF2\u542F\u52A8\u6301\u7EED\u64AD\u653E: ${key}\uFF0C\u6CE2\u5F62\u6570: ${waveforms.length}\uFF0C\u6279\u6B21\u5927\u5C0F: ${batchSize}\uFF0C\u64AD\u653E\u65F6\u957F: ${playbackDuration}ms\uFF0C\u7F13\u51B2\u6BD4\u4F8B: ${validBufferRatio}`);
1675
+ this.scheduleSend(state);
1682
1676
  return true;
1683
1677
  }
1678
+ /**
1679
+ * 调度发送波形(内部方法)
1680
+ *
1681
+ * 使用递归 setTimeout 实现动态等待机制:
1682
+ * 1. 记录发送开始时间
1683
+ * 2. 发送波形批次
1684
+ * 3. 计算发送耗时
1685
+ * 4. 计算等待时间 = 播放时长 × 缓冲比例 - 发送耗时
1686
+ * 5. 调度下次发送
1687
+ *
1688
+ * @param state - 持续播放状态
1689
+ */
1690
+ scheduleSend(state) {
1691
+ if (!state.active) {
1692
+ return;
1693
+ }
1694
+ const startTime = Date.now();
1695
+ const batch = [];
1696
+ for (let i = 0; i < state.batchSize; i++) {
1697
+ batch.push(state.waveforms[state.currentIndex]);
1698
+ state.currentIndex = (state.currentIndex + 1) % state.waveforms.length;
1699
+ }
1700
+ const success = this.sendWaveform(state.controllerId, state.channel, batch);
1701
+ if (!success) {
1702
+ console.log(`[WS \u670D\u52A1\u5668] \u6301\u7EED\u64AD\u653E\u53D1\u9001\u5931\u8D25\uFF0C\u505C\u6B62\u64AD\u653E: ${state.controllerId}-${state.channel}`);
1703
+ this.stopContinuousPlayback(state.controllerId, state.channel);
1704
+ return;
1705
+ }
1706
+ const elapsedTime = Date.now() - startTime;
1707
+ state.stats.sendCount++;
1708
+ state.stats.totalElapsedTime += elapsedTime;
1709
+ state.stats.lastSendTime = startTime;
1710
+ const targetWaitTime = state.playbackDuration * state.bufferRatio - elapsedTime;
1711
+ const actualWaitTime = Math.max(10, targetWaitTime);
1712
+ if (targetWaitTime < 0) {
1713
+ console.warn(`[WS \u670D\u52A1\u5668] \u6301\u7EED\u64AD\u653E\u53D1\u9001\u592A\u6162: \u8017\u65F6 ${elapsedTime}ms > \u76EE\u6807\u65F6\u95F4 ${state.playbackDuration * state.bufferRatio}ms`);
1714
+ }
1715
+ state.timerId = setTimeout(() => this.scheduleSend(state), actualWaitTime);
1716
+ }
1684
1717
  /**
1685
1718
  * 停止持续播放
1686
1719
  *
@@ -1697,9 +1730,13 @@ var DGLabWSServer = class {
1697
1730
  console.log(`[WS \u670D\u52A1\u5668] \u505C\u6B62\u6301\u7EED\u64AD\u653E\u5931\u8D25: ${key} \u4E0D\u5B58\u5728`);
1698
1731
  return false;
1699
1732
  }
1733
+ if (state.stats.sendCount > 0) {
1734
+ const avgElapsedTime = state.stats.totalElapsedTime / state.stats.sendCount;
1735
+ console.log(`[WS \u670D\u52A1\u5668] \u6301\u7EED\u64AD\u653E\u7EDF\u8BA1: ${key}\uFF0C\u53D1\u9001\u6B21\u6570: ${state.stats.sendCount}\uFF0C\u5E73\u5747\u8017\u65F6: ${avgElapsedTime.toFixed(2)}ms`);
1736
+ }
1700
1737
  state.active = false;
1701
1738
  if (state.timerId) {
1702
- clearInterval(state.timerId);
1739
+ clearTimeout(state.timerId);
1703
1740
  state.timerId = null;
1704
1741
  }
1705
1742
  this.clearWaveform(controllerId, channel);
@@ -1732,9 +1769,15 @@ var DGLabWSServer = class {
1732
1769
  if (!state) return null;
1733
1770
  return {
1734
1771
  waveformCount: state.waveforms.length,
1735
- interval: state.interval,
1736
1772
  batchSize: state.batchSize,
1737
- active: state.active
1773
+ bufferRatio: state.bufferRatio,
1774
+ playbackDuration: state.playbackDuration,
1775
+ active: state.active,
1776
+ stats: {
1777
+ sendCount: state.stats.sendCount,
1778
+ totalElapsedTime: state.stats.totalElapsedTime,
1779
+ avgElapsedTime: state.stats.sendCount > 0 ? state.stats.totalElapsedTime / state.stats.sendCount : 0
1780
+ }
1738
1781
  };
1739
1782
  }
1740
1783
  /** 获取 APP 扫描的二维码 URL */
@@ -2481,10 +2524,37 @@ function createToolSuccess(data) {
2481
2524
  }
2482
2525
  var dgParseWaveformTool = {
2483
2526
  name: "dg_parse_waveform",
2484
- description: `\u89E3\u6790\u5E76\u4FDD\u5B58\u6CE2\u5F62\u6570\u636E\u3002
2485
- \u8F93\u5165\u683C\u5F0F\uFF1ADungeonlab+pulse:\u5F00\u5934\u7684Base64\u7F16\u7801\u5B57\u7B26\u4E32\uFF08\u4ECEDG-LAB APP\u5BFC\u51FA\uFF09\u3002
2486
- \u89E3\u6790\u540E\u4FDD\u5B58\u4E3AhexWaveforms\u6570\u7EC4\uFF0C\u53EF\u901A\u8FC7dg_get_waveform\u83B7\u53D6\u5E76\u7528dg_send_waveform\u53D1\u9001\u3002
2487
- \u5982\u679Cname\u5DF2\u5B58\u5728\u4F1A\u8986\u76D6\u539F\u6709\u6CE2\u5F62\u3002`,
2527
+ description: `\u89E3\u6790 DG-LAB APP \u5BFC\u51FA\u7684\u6CE2\u5F62\u6570\u636E\u3002
2528
+
2529
+ \u529F\u80FD\uFF1A
2530
+ - \u89E3\u6790 Dungeonlab+pulse: \u683C\u5F0F\u7684\u6CE2\u5F62\u6570\u636E
2531
+ - \u8F6C\u6362\u4E3A\u8BBE\u5907\u53EF\u7528\u7684 hexWaveforms \u6570\u7EC4
2532
+ - \u53EF\u9009\u62E9\u662F\u5426\u4FDD\u5B58\u5230\u5B58\u50A8\u4F9B\u540E\u7EED\u4F7F\u7528
2533
+
2534
+ \u53C2\u6570\uFF1A
2535
+ - hexData (\u5FC5\u9700): \u6CE2\u5F62\u6570\u636E\u5B57\u7B26\u4E32\uFF0C\u5FC5\u987B\u4EE5 "Dungeonlab+pulse:" \u5F00\u5934
2536
+ - name (save=true\u65F6\u5FC5\u9700): \u6CE2\u5F62\u540D\u79F0\uFF0C\u7528\u4E8E\u4FDD\u5B58\u548C\u540E\u7EED\u5F15\u7528
2537
+ - save (\u53EF\u9009): \u662F\u5426\u4FDD\u5B58\u5230\u5B58\u50A8\uFF0C\u9ED8\u8BA4 false
2538
+
2539
+ \u4F7F\u7528\u573A\u666F\uFF1A
2540
+ 1. \u4E34\u65F6\u89E3\u6790\uFF1A\u53EA\u9700\u8981 hexWaveforms\uFF0C\u4E0D\u4FDD\u5B58
2541
+ \u2192 \u53EA\u4F20 hexData\uFF0C\u8FD4\u56DE\u7ED3\u679C\u5305\u542B hexWaveforms
2542
+ 2. \u4FDD\u5B58\u590D\u7528\uFF1A\u89E3\u6790\u5E76\u4FDD\u5B58\uFF0C\u540E\u7EED\u901A\u8FC7 dg_get_waveform \u83B7\u53D6
2543
+ \u2192 \u4F20 hexData\u3001name\u3001save=true
2544
+
2545
+ \u8FD4\u56DE\u503C\uFF1A
2546
+ - success: \u662F\u5426\u6210\u529F
2547
+ - name: \u6CE2\u5F62\u540D\u79F0
2548
+ - saved: \u662F\u5426\u5DF2\u4FDD\u5B58
2549
+ - hexWaveformCount: hexWaveforms \u6570\u91CF
2550
+ - hexWaveforms: \u6CE2\u5F62\u6570\u636E\u6570\u7EC4\uFF08\u4EC5\u5F53 save=false \u65F6\u8FD4\u56DE\uFF09
2551
+ - metadata: \u5143\u6570\u636E\uFF08sectionCount, totalDuration\uFF09
2552
+ - overwritten: \u662F\u5426\u8986\u76D6\u4E86\u5DF2\u5B58\u5728\u7684\u6CE2\u5F62\uFF08\u4EC5\u5F53\u8986\u76D6\u65F6\u8FD4\u56DE\uFF09
2553
+
2554
+ \u6CE8\u610F\u4E8B\u9879\uFF1A
2555
+ - \u6CE2\u5F62\u6570\u636E\u4ECE DG-LAB APP \u7684"\u5206\u4EAB\u6CE2\u5F62"\u529F\u80FD\u5BFC\u51FA
2556
+ - \u6BCF\u4E2A hexWaveform \u4EE3\u8868 100ms \u7684\u64AD\u653E\u65F6\u95F4
2557
+ - \u76F8\u540C\u540D\u79F0\u4F1A\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6CE2\u5F62`,
2488
2558
  inputSchema: {
2489
2559
  type: "object",
2490
2560
  properties: {
@@ -2494,32 +2564,58 @@ var dgParseWaveformTool = {
2494
2564
  },
2495
2565
  name: {
2496
2566
  type: "string",
2497
- description: "\u6CE2\u5F62\u540D\u79F0\uFF0C\u7528\u4E8E\u4FDD\u5B58\u548C\u540E\u7EED\u5F15\u7528"
2567
+ description: "\u6CE2\u5F62\u540D\u79F0\uFF0C\u7528\u4E8E\u4FDD\u5B58\u548C\u540E\u7EED\u5F15\u7528\u3002\u5F53 save=true \u65F6\u5FC5\u9700"
2568
+ },
2569
+ save: {
2570
+ type: "boolean",
2571
+ description: "\u662F\u5426\u4FDD\u5B58\u6CE2\u5F62\u5230\u5B58\u50A8\uFF08\u9ED8\u8BA4 false\uFF09\u3002\u8BBE\u4E3A true \u65F6\u9700\u8981\u63D0\u4F9B name \u53C2\u6570"
2498
2572
  }
2499
2573
  },
2500
- required: ["hexData", "name"]
2574
+ required: ["hexData"]
2501
2575
  },
2502
2576
  handler: async (params) => {
2503
2577
  const hexData = params.hexData;
2504
2578
  const name = params.name;
2579
+ const save = params.save;
2505
2580
  if (!hexData || typeof hexData !== "string") {
2506
2581
  return createToolError2("hexData \u662F\u5FC5\u9700\u7684\u4E14\u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
2507
2582
  }
2508
- if (!name || typeof name !== "string" || name.trim().length === 0) {
2509
- return createToolError2("name \u662F\u5FC5\u9700\u7684\u4E14\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
2583
+ if (save !== void 0 && typeof save !== "boolean") {
2584
+ return createToolError2("save \u53C2\u6570\u5FC5\u987B\u662F boolean \u7C7B\u578B");
2510
2585
  }
2586
+ const shouldSave = save === true;
2587
+ if (shouldSave && (!name || typeof name !== "string" || name.trim().length === 0)) {
2588
+ return createToolError2("\u5F53 save=true \u65F6\uFF0Cname \u662F\u5FC5\u9700\u7684\u4E14\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
2589
+ }
2590
+ const waveformName = name?.trim() || "unnamed";
2511
2591
  try {
2512
- const waveform = parseWaveform(hexData, name.trim());
2513
- const storage = getWaveformStorage();
2514
- const existed = storage.has(name.trim());
2515
- storage.save(waveform);
2516
- persistWaveforms(storage, storagePath);
2517
- return createToolSuccess({
2592
+ const waveform = parseWaveform(hexData, waveformName);
2593
+ let overwritten = false;
2594
+ if (shouldSave) {
2595
+ const storage = getWaveformStorage();
2596
+ overwritten = storage.has(waveformName);
2597
+ storage.save(waveform);
2598
+ persistWaveforms(storage, storagePath);
2599
+ }
2600
+ const metadata = {
2601
+ sectionCount: waveform.sections.length,
2602
+ totalDuration: waveform.hexWaveforms.length * 100
2603
+ // 每个 hexWaveform 代表 100ms
2604
+ };
2605
+ const result = {
2518
2606
  success: true,
2519
2607
  name: waveform.name,
2520
- overwritten: existed,
2521
- hexWaveformCount: waveform.hexWaveforms.length
2522
- });
2608
+ saved: shouldSave,
2609
+ hexWaveformCount: waveform.hexWaveforms.length,
2610
+ metadata
2611
+ };
2612
+ if (shouldSave && overwritten) {
2613
+ result.overwritten = true;
2614
+ }
2615
+ if (!shouldSave) {
2616
+ result.hexWaveforms = waveform.hexWaveforms;
2617
+ }
2618
+ return createToolSuccess(result);
2523
2619
  } catch (error) {
2524
2620
  if (error instanceof Error) {
2525
2621
  return createToolError2(error.message);
@@ -2530,9 +2626,26 @@ var dgParseWaveformTool = {
2530
2626
  };
2531
2627
  var dgListWaveformsTool = {
2532
2628
  name: "dg_list_waveforms",
2533
- description: `\u5217\u51FA\u6240\u6709\u5DF2\u4FDD\u5B58\u7684\u6CE2\u5F62\u540D\u79F0\u548C\u6570\u636E\u91CF\u3002
2534
- \u8FD4\u56DE\u6BCF\u4E2A\u6CE2\u5F62\u7684name\u548ChexWaveformCount\uFF08\u6CE2\u5F62\u6570\u636E\u6761\u6570\uFF09\u3002
2535
- \u7528\u4E8E\u67E5\u770B\u53EF\u7528\u6CE2\u5F62\uFF0C\u7136\u540E\u901A\u8FC7dg_get_waveform\u83B7\u53D6\u5177\u4F53\u6570\u636E\u3002`,
2629
+ description: `\u5217\u51FA\u6240\u6709\u5DF2\u4FDD\u5B58\u7684\u6CE2\u5F62\u3002
2630
+
2631
+ \u529F\u80FD\uFF1A
2632
+ - \u83B7\u53D6\u5B58\u50A8\u4E2D\u6240\u6709\u6CE2\u5F62\u7684\u6982\u89C8\u4FE1\u606F
2633
+ - \u663E\u793A\u6BCF\u4E2A\u6CE2\u5F62\u7684\u540D\u79F0\u548C\u6570\u636E\u91CF
2634
+
2635
+ \u8FD4\u56DE\u503C\uFF1A
2636
+ - count: \u6CE2\u5F62\u603B\u6570
2637
+ - waveforms: \u6CE2\u5F62\u5217\u8868\u6570\u7EC4
2638
+ - name: \u6CE2\u5F62\u540D\u79F0
2639
+ - hexWaveformCount: hexWaveforms \u6570\u91CF\uFF08\u6BCF\u4E2A\u4EE3\u8868 100ms\uFF09
2640
+
2641
+ \u5178\u578B\u5DE5\u4F5C\u6D41\u7A0B\uFF1A
2642
+ 1. dg_list_waveforms \u67E5\u770B\u53EF\u7528\u6CE2\u5F62
2643
+ 2. dg_get_waveform \u83B7\u53D6\u5177\u4F53\u6CE2\u5F62\u6570\u636E
2644
+ 3. dg_send_waveform \u53D1\u9001\u5230\u8BBE\u5907
2645
+
2646
+ \u6CE8\u610F\u4E8B\u9879\uFF1A
2647
+ - \u53EA\u663E\u793A\u901A\u8FC7 dg_parse_waveform (save=true) \u4FDD\u5B58\u7684\u6CE2\u5F62
2648
+ - hexWaveformCount \xD7 100ms = \u6CE2\u5F62\u603B\u65F6\u957F`,
2536
2649
  inputSchema: {
2537
2650
  type: "object",
2538
2651
  properties: {},
@@ -2553,9 +2666,31 @@ var dgListWaveformsTool = {
2553
2666
  };
2554
2667
  var dgGetWaveformTool = {
2555
2668
  name: "dg_get_waveform",
2556
- description: `\u6309\u540D\u79F0\u83B7\u53D6\u6CE2\u5F62\u7684hexWaveforms\u6570\u7EC4\u3002
2557
- \u8FD4\u56DE\u7684hexWaveforms\u53EF\u76F4\u63A5\u4F20\u7ED9dg_send_waveform\u7684waveforms\u53C2\u6570\u4F7F\u7528\u3002
2558
- \u5178\u578B\u6D41\u7A0B\uFF1Adg_list_waveforms\u67E5\u770B\u53EF\u7528\u6CE2\u5F62 \u2192 dg_get_waveform\u83B7\u53D6\u6570\u636E \u2192 dg_send_waveform\u53D1\u9001\u5230\u8BBE\u5907\u3002`,
2669
+ description: `\u6309\u540D\u79F0\u83B7\u53D6\u5DF2\u4FDD\u5B58\u7684\u6CE2\u5F62\u6570\u636E\u3002
2670
+
2671
+ \u529F\u80FD\uFF1A
2672
+ - \u4ECE\u5B58\u50A8\u4E2D\u83B7\u53D6\u6307\u5B9A\u540D\u79F0\u7684\u6CE2\u5F62
2673
+ - \u8FD4\u56DE\u5B8C\u6574\u7684 hexWaveforms \u6570\u7EC4
2674
+
2675
+ \u53C2\u6570\uFF1A
2676
+ - name (\u5FC5\u9700): \u6CE2\u5F62\u540D\u79F0
2677
+
2678
+ \u8FD4\u56DE\u503C\uFF1A
2679
+ - name: \u6CE2\u5F62\u540D\u79F0
2680
+ - hexWaveforms: \u6CE2\u5F62\u6570\u636E\u6570\u7EC4\uFF0C\u53EF\u76F4\u63A5\u7528\u4E8E dg_send_waveform
2681
+
2682
+ \u5178\u578B\u5DE5\u4F5C\u6D41\u7A0B\uFF1A
2683
+ 1. dg_list_waveforms \u67E5\u770B\u53EF\u7528\u6CE2\u5F62
2684
+ 2. dg_get_waveform \u83B7\u53D6\u5177\u4F53\u6CE2\u5F62\u6570\u636E
2685
+ 3. dg_send_waveform \u6216 dg_start_continuous_playback \u53D1\u9001\u5230\u8BBE\u5907
2686
+
2687
+ \u4E0E\u5176\u4ED6\u5DE5\u5177\u914D\u5408\uFF1A
2688
+ - dg_send_waveform: \u4E00\u6B21\u6027\u53D1\u9001\u6CE2\u5F62
2689
+ - dg_start_continuous_playback: \u6301\u7EED\u5FAA\u73AF\u64AD\u653E\u6CE2\u5F62
2690
+
2691
+ \u6CE8\u610F\u4E8B\u9879\uFF1A
2692
+ - \u6CE2\u5F62\u5FC5\u987B\u5148\u901A\u8FC7 dg_parse_waveform (save=true) \u4FDD\u5B58
2693
+ - \u5982\u679C\u6CE2\u5F62\u4E0D\u5B58\u5728\u4F1A\u8FD4\u56DE\u9519\u8BEF`,
2559
2694
  inputSchema: {
2560
2695
  type: "object",
2561
2696
  properties: {
@@ -2585,7 +2720,26 @@ var dgGetWaveformTool = {
2585
2720
  var dgDeleteWaveformTool = {
2586
2721
  name: "dg_delete_waveform",
2587
2722
  description: `\u6309\u540D\u79F0\u5220\u9664\u5DF2\u4FDD\u5B58\u7684\u6CE2\u5F62\u3002
2588
- \u5220\u9664\u540E\u65E0\u6CD5\u6062\u590D\uFF0C\u9700\u8981\u91CD\u65B0\u7528dg_parse_waveform\u89E3\u6790\u4FDD\u5B58\u3002`,
2723
+
2724
+ \u529F\u80FD\uFF1A
2725
+ - \u4ECE\u5B58\u50A8\u4E2D\u6C38\u4E45\u5220\u9664\u6307\u5B9A\u6CE2\u5F62
2726
+ - \u540C\u65F6\u4ECE\u78C1\u76D8\u6301\u4E45\u5316\u6587\u4EF6\u4E2D\u79FB\u9664
2727
+
2728
+ \u53C2\u6570\uFF1A
2729
+ - name (\u5FC5\u9700): \u8981\u5220\u9664\u7684\u6CE2\u5F62\u540D\u79F0
2730
+
2731
+ \u8FD4\u56DE\u503C\uFF1A
2732
+ - success: \u662F\u5426\u6210\u529F
2733
+ - deleted: \u88AB\u5220\u9664\u7684\u6CE2\u5F62\u540D\u79F0
2734
+
2735
+ \u26A0\uFE0F \u8B66\u544A\uFF1A
2736
+ - \u5220\u9664\u64CD\u4F5C\u4E0D\u53EF\u9006\uFF01
2737
+ - \u5220\u9664\u540E\u9700\u8981\u91CD\u65B0\u7528 dg_parse_waveform \u89E3\u6790\u4FDD\u5B58
2738
+ - \u5EFA\u8BAE\u5220\u9664\u524D\u786E\u8BA4\u6CE2\u5F62\u540D\u79F0
2739
+
2740
+ \u6CE8\u610F\u4E8B\u9879\uFF1A
2741
+ - \u5982\u679C\u6CE2\u5F62\u4E0D\u5B58\u5728\u4F1A\u8FD4\u56DE\u9519\u8BEF
2742
+ - \u5220\u9664\u4E0D\u4F1A\u5F71\u54CD\u6B63\u5728\u8FDB\u884C\u7684\u6301\u7EED\u64AD\u653E`,
2589
2743
  inputSchema: {
2590
2744
  type: "object",
2591
2745
  properties: {
@@ -2928,13 +3082,27 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
2928
3082
  toolManager.registerTool(
2929
3083
  "dg_start_continuous_playback",
2930
3084
  `\u542F\u52A8\u6301\u7EED\u64AD\u653E\u6A21\u5F0F\uFF0C\u5FAA\u73AF\u53D1\u9001\u6CE2\u5F62\u6570\u636E\u76F4\u5230\u624B\u52A8\u505C\u6B62\u3002
2931
- \u4E0Edg_send_waveform\u4E0D\u540C\uFF0C\u6301\u7EED\u64AD\u653E\u4F1A\u81EA\u52A8\u5FAA\u73AF\u53D1\u9001\u6CE2\u5F62\uFF0C\u9002\u5408\u9700\u8981\u6301\u7EED\u8F93\u51FA\u7684\u573A\u666F\u3002
2932
- \u652F\u6301\u4E24\u79CD\u65B9\u5F0F\u63D0\u4F9B\u6CE2\u5F62\uFF1A
2933
- 1. \u76F4\u63A5\u63D0\u4F9Bwaveforms\u6570\u7EC4
2934
- 2. \u63D0\u4F9BwaveformName\u5F15\u7528\u5DF2\u4FDD\u5B58\u7684\u6CE2\u5F62
2935
- \u53EF\u9009\u53C2\u6570\uFF1A
2936
- - interval: \u53D1\u9001\u95F4\u9694\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4100ms
2937
- - batchSize: \u6BCF\u6B21\u53D1\u9001\u7684\u6CE2\u5F62\u6570\u91CF\uFF0C\u9ED8\u8BA45`,
3085
+
3086
+ \u529F\u80FD\uFF1A
3087
+ - \u81EA\u52A8\u5FAA\u73AF\u53D1\u9001\u6CE2\u5F62\uFF0C\u9002\u5408\u9700\u8981\u6301\u7EED\u8F93\u51FA\u7684\u573A\u666F
3088
+ - \u4F7F\u7528\u52A8\u6001\u7B49\u5F85\u673A\u5236\uFF0C\u6839\u636E\u5B9E\u9645\u64AD\u653E\u65F6\u957F\u548C\u53D1\u9001\u8017\u65F6\u8BA1\u7B97\u7B49\u5F85\u65F6\u95F4
3089
+ - \u6BCF\u4E2A hexWaveform \u4EE3\u8868 100ms \u7684\u64AD\u653E\u65F6\u95F4
3090
+
3091
+ \u53C2\u6570\uFF1A
3092
+ - deviceId \u6216 alias: \u8BBE\u5907\u6807\u8BC6\uFF08\u4E8C\u9009\u4E00\uFF0CdeviceId\u4F18\u5148\uFF09
3093
+ - channel: A\u6216B\u901A\u9053
3094
+ - waveforms \u6216 waveformName: \u6CE2\u5F62\u6570\u636E\u6765\u6E90\uFF08\u4E8C\u9009\u4E00\uFF09
3095
+ - batchSize: \u6BCF\u6B21\u53D1\u9001\u7684\u6CE2\u5F62\u6570\u91CF\uFF0C\u9ED8\u8BA45\uFF08\u64AD\u653E\u65F6\u957F = batchSize \xD7 100ms\uFF09
3096
+ - bufferRatio: \u7F13\u51B2\u6BD4\u4F8B\uFF080.5-1.0\uFF09\uFF0C\u9ED8\u8BA40.9\uFF0C\u7528\u4E8E\u8BA1\u7B97\u7B49\u5F85\u65F6\u95F4
3097
+
3098
+ \u65F6\u5E8F\u673A\u5236\uFF1A
3099
+ - \u64AD\u653E\u65F6\u957F = batchSize \xD7 100ms
3100
+ - \u7B49\u5F85\u65F6\u95F4 = \u64AD\u653E\u65F6\u957F \xD7 bufferRatio - \u53D1\u9001\u8017\u65F6
3101
+ - \u6700\u5C0F\u7B49\u5F85\u65F6\u95F4 = 10ms
3102
+
3103
+ \u4E0E dg_send_waveform \u7684\u533A\u522B\uFF1A
3104
+ - dg_send_waveform: \u4E00\u6B21\u6027\u53D1\u9001\uFF0C\u64AD\u653E\u5B8C\u6BD5\u540E\u505C\u6B62
3105
+ - dg_start_continuous_playback: \u5FAA\u73AF\u53D1\u9001\uFF0C\u76F4\u5230\u624B\u52A8\u505C\u6B62`,
2938
3106
  {
2939
3107
  type: "object",
2940
3108
  properties: {
@@ -2951,17 +3119,17 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
2951
3119
  type: "string",
2952
3120
  description: "\u5DF2\u4FDD\u5B58\u7684\u6CE2\u5F62\u540D\u79F0\u3002\u4E0Ewaveforms\u4E8C\u9009\u4E00"
2953
3121
  },
2954
- interval: {
2955
- type: "number",
2956
- minimum: 50,
2957
- maximum: 5e3,
2958
- description: "\u53D1\u9001\u95F4\u9694\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4100"
2959
- },
2960
3122
  batchSize: {
2961
3123
  type: "number",
2962
3124
  minimum: 1,
2963
3125
  maximum: 20,
2964
- description: "\u6BCF\u6B21\u53D1\u9001\u7684\u6CE2\u5F62\u6570\u91CF\uFF0C\u9ED8\u8BA45"
3126
+ description: "\u6BCF\u6B21\u53D1\u9001\u7684\u6CE2\u5F62\u6570\u91CF\uFF0C\u9ED8\u8BA45\uFF08\u64AD\u653E\u65F6\u957F = batchSize \xD7 100ms\uFF09"
3127
+ },
3128
+ bufferRatio: {
3129
+ type: "number",
3130
+ minimum: 0.5,
3131
+ maximum: 1,
3132
+ description: "\u7F13\u51B2\u6BD4\u4F8B\uFF080.5-1.0\uFF09\uFF0C\u9ED8\u8BA40.9\uFF0C\u7528\u4E8E\u8BA1\u7B97\u7B49\u5F85\u65F6\u95F4"
2965
3133
  }
2966
3134
  },
2967
3135
  required: ["channel"]
@@ -3002,27 +3170,29 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
3002
3170
  if (!isBound) {
3003
3171
  return createToolError("\u8BBE\u5907\u672A\u7ED1\u5B9AAPP");
3004
3172
  }
3005
- const interval = typeof params.interval === "number" ? params.interval : 100;
3006
3173
  const batchSize = typeof params.batchSize === "number" ? params.batchSize : 5;
3174
+ const bufferRatio = typeof params.bufferRatio === "number" ? params.bufferRatio : 0.9;
3007
3175
  const success = wsServer.startContinuousPlayback(
3008
3176
  session.clientId,
3009
3177
  channel,
3010
3178
  waveforms,
3011
- interval,
3012
- batchSize
3179
+ batchSize,
3180
+ bufferRatio
3013
3181
  );
3014
3182
  if (!success) {
3015
3183
  return createToolError("\u542F\u52A8\u6301\u7EED\u64AD\u653E\u5931\u8D25");
3016
3184
  }
3017
3185
  sessionManager.touchSession(session.deviceId);
3186
+ const playbackDuration = batchSize * 100;
3018
3187
  return createToolResult(
3019
3188
  JSON.stringify({
3020
3189
  success: true,
3021
3190
  deviceId: session.deviceId,
3022
3191
  channel,
3023
3192
  waveformCount: waveforms.length,
3024
- interval,
3025
3193
  batchSize,
3194
+ bufferRatio,
3195
+ playbackDuration,
3026
3196
  source: rawWaveforms ? "direct" : `waveform:${waveformName}`
3027
3197
  })
3028
3198
  );
@@ -3072,7 +3242,14 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
3072
3242
  toolManager.registerTool(
3073
3243
  "dg_get_playback_status",
3074
3244
  `\u83B7\u53D6\u8BBE\u5907\u7684\u6301\u7EED\u64AD\u653E\u72B6\u6001\u3002
3075
- \u8FD4\u56DEA\u548CB\u901A\u9053\u7684\u64AD\u653E\u72B6\u6001\uFF0C\u5305\u62EC\u662F\u5426\u6B63\u5728\u64AD\u653E\u3001\u6CE2\u5F62\u6570\u91CF\u3001\u53D1\u9001\u95F4\u9694\u7B49\u4FE1\u606F\u3002`,
3245
+
3246
+ \u8FD4\u56DE A \u548C B \u901A\u9053\u7684\u64AD\u653E\u72B6\u6001\uFF0C\u5305\u62EC\uFF1A
3247
+ - playing: \u662F\u5426\u6B63\u5728\u64AD\u653E
3248
+ - waveformCount: \u6CE2\u5F62\u6570\u91CF
3249
+ - batchSize: \u6BCF\u6B21\u53D1\u9001\u7684\u6CE2\u5F62\u6570\u91CF
3250
+ - bufferRatio: \u7F13\u51B2\u6BD4\u4F8B
3251
+ - playbackDuration: \u64AD\u653E\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09
3252
+ - stats: \u7EDF\u8BA1\u4FE1\u606F\uFF08\u53D1\u9001\u6B21\u6570\u3001\u5E73\u5747\u8017\u65F6\uFF09`,
3076
3253
  {
3077
3254
  type: "object",
3078
3255
  properties: {
@@ -3100,14 +3277,18 @@ function registerControlTools(toolManager, sessionManager, wsServer) {
3100
3277
  channelA: statusA ? {
3101
3278
  playing: statusA.active,
3102
3279
  waveformCount: statusA.waveformCount,
3103
- interval: statusA.interval,
3104
- batchSize: statusA.batchSize
3280
+ batchSize: statusA.batchSize,
3281
+ bufferRatio: statusA.bufferRatio,
3282
+ playbackDuration: statusA.playbackDuration,
3283
+ stats: statusA.stats
3105
3284
  } : { playing: false },
3106
3285
  channelB: statusB ? {
3107
3286
  playing: statusB.active,
3108
3287
  waveformCount: statusB.waveformCount,
3109
- interval: statusB.interval,
3110
- batchSize: statusB.batchSize
3288
+ batchSize: statusB.batchSize,
3289
+ bufferRatio: statusB.bufferRatio,
3290
+ playbackDuration: statusB.playbackDuration,
3291
+ stats: statusB.stats
3111
3292
  } : { playing: false }
3112
3293
  })
3113
3294
  );
@@ -3192,12 +3373,7 @@ function createWSServer(config, sessionManager) {
3192
3373
  console.log(`[WS] \u63A7\u5236\u5668\u65AD\u5F00: ${controllerId}`);
3193
3374
  const session = sessionManager.getSessionByClientId(controllerId);
3194
3375
  if (session) {
3195
- sessionManager.updateConnectionState(session.deviceId, {
3196
- connected: false,
3197
- boundToApp: false,
3198
- clientId: null,
3199
- targetId: null
3200
- });
3376
+ sessionManager.handleDisconnection(session.deviceId);
3201
3377
  }
3202
3378
  },
3203
3379
  onAppDisconnect: (appId) => {