1
0

4 Коммиты b60fc19a18 ... c4197b0ad0

Автор SHA1 Сообщение Дата
  liulinag c4197b0ad0 Improve GPS streaming and risk alert delivery 1 месяц назад
  liulinag 91dbbbc3d5 server: ignore xingxing-server runtime data 1 месяц назад
  liulinag 2072025779 server: add xingxing-server tree 1 месяц назад
  liulinag 39a3728f07 server: rename to xingxing-server and unify gps coord handling 1 месяц назад
100 измененных файлов с 574 добавлено и 36441 удалено
  1. 2 1
      .gitignore
  2. 0 162
      main/xiaozhi-server/core/api/app_gps_handler.py
  3. 0 56
      main/xiaozhi-server/mcp_server_settings.json
  4. 0 14
      main/xiaozhi-server/models/SenseVoiceSmall/configuration.json
  5. 0 331
      main/xiaozhi-server/plugins_func/functions/nav_copilot.py
  6. 0 58
      main/xiaozhi-server/plugins_func/nav_copilot/geo.py
  7. 0 991
      main/xiaozhi-server/plugins_func/nav_copilot/service.py
  8. 0 396
      main/xiaozhi-server/plugins_func/nav_copilot/storage.py
  9. 0 90
      main/xiaozhi-server/test/js/config/default-mcp-tools.json
  10. 0 533
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.cdi3.json
  11. 0 82
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json
  12. 0 1569
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.physics3.json
  13. 0 15
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.pose3.json
  14. 0 1063
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m01.motion3.json
  15. 0 1428
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m02.motion3.json
  16. 0 1295
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m03.motion3.json
  17. 0 860
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m04.motion3.json
  18. 0 1543
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m05.motion3.json
  19. 0 1947
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m06.motion3.json
  20. 0 927
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m07.motion3.json
  21. 0 1023
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m08.motion3.json
  22. 0 1113
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m09.motion3.json
  23. 0 924
      main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m10.motion3.json
  24. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Angry.exp3.json
  25. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Blushing.exp3.json
  26. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Normal.exp3.json
  27. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Sad.exp3.json
  28. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Smile.exp3.json
  29. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Surprised.exp3.json
  30. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_01.exp3.json
  31. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_02.exp3.json
  32. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_03.exp3.json
  33. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_04.exp3.json
  34. 0 120
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_05.exp3.json
  35. 0 1432
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_00.motion3.json
  36. 0 1971
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_01.motion3.json
  37. 0 2182
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_02.motion3.json
  38. 0 2596
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_03.motion3.json
  39. 0 2316
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_04.motion3.json
  40. 0 2345
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_05.motion3.json
  41. 0 2082
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_06.motion3.json
  42. 0 1907
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_07.motion3.json
  43. 0 44
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori.pose3.json
  44. 0 711
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori_pro_t06.cdi3.json
  45. 0 123
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori_pro_t06.model3.json
  46. 0 966
      main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori_pro_t06.physics3.json
  47. 0 0
      main/xingxing-server/.dockerignore
  48. 0 0
      main/xingxing-server/Dockerfile
  49. 0 0
      main/xingxing-server/agent-base-prompt.txt
  50. 0 0
      main/xingxing-server/app.py
  51. 1 15
      main/xiaozhi-server/config.yaml
  52. 0 0
      main/xingxing-server/config/assets/bind_code.wav
  53. 0 0
      main/xingxing-server/config/assets/bind_code/0.wav
  54. 0 0
      main/xingxing-server/config/assets/bind_code/1.wav
  55. 0 0
      main/xingxing-server/config/assets/bind_code/2.wav
  56. 0 0
      main/xingxing-server/config/assets/bind_code/3.wav
  57. 0 0
      main/xingxing-server/config/assets/bind_code/4.wav
  58. 0 0
      main/xingxing-server/config/assets/bind_code/5.wav
  59. 0 0
      main/xingxing-server/config/assets/bind_code/6.wav
  60. 0 0
      main/xingxing-server/config/assets/bind_code/7.wav
  61. 0 0
      main/xingxing-server/config/assets/bind_code/8.wav
  62. 0 0
      main/xingxing-server/config/assets/bind_code/9.wav
  63. 0 0
      main/xingxing-server/config/assets/bind_not_found.wav
  64. 0 0
      main/xingxing-server/config/assets/max_output_size.wav
  65. 0 0
      main/xingxing-server/config/assets/tts_notify.mp3
  66. 0 0
      main/xingxing-server/config/assets/wakeup_words.wav
  67. 0 0
      main/xingxing-server/config/assets/wakeup_words/ed76d459636c2481aec828516c1b4f54.wav
  68. 0 0
      main/xingxing-server/config/assets/wakeup_words_short.wav
  69. 0 0
      main/xingxing-server/config/config_loader.py
  70. 0 0
      main/xingxing-server/config/logger.py
  71. 0 0
      main/xingxing-server/config/manage_api_client.py
  72. 0 0
      main/xingxing-server/config/settings.py
  73. 0 0
      main/xingxing-server/config_from_api.yaml
  74. 455 0
      main/xingxing-server/core/api/app_gps_handler.py
  75. 2 4
      main/xiaozhi-server/core/api/base_handler.py
  76. 0 0
      main/xingxing-server/core/api/ota_handler.py
  77. 0 0
      main/xingxing-server/core/api/vision_handler.py
  78. 0 0
      main/xingxing-server/core/auth.py
  79. 0 0
      main/xingxing-server/core/connection.py
  80. 0 0
      main/xingxing-server/core/handle/abortHandle.py
  81. 0 0
      main/xingxing-server/core/handle/helloHandle.py
  82. 23 3
      main/xiaozhi-server/core/handle/intentHandler.py
  83. 0 0
      main/xingxing-server/core/handle/receiveAudioHandle.py
  84. 0 0
      main/xingxing-server/core/handle/reportHandle.py
  85. 0 0
      main/xingxing-server/core/handle/sendAudioHandle.py
  86. 0 0
      main/xingxing-server/core/handle/textHandle.py
  87. 0 0
      main/xingxing-server/core/handle/textHandler/abortMessageHandler.py
  88. 0 0
      main/xingxing-server/core/handle/textHandler/helloMessageHandler.py
  89. 0 0
      main/xingxing-server/core/handle/textHandler/iotMessageHandler.py
  90. 0 0
      main/xingxing-server/core/handle/textHandler/listenMessageHandler.py
  91. 0 0
      main/xingxing-server/core/handle/textHandler/mcpMessageHandler.py
  92. 0 0
      main/xingxing-server/core/handle/textHandler/pingMessageHandler.py
  93. 0 0
      main/xingxing-server/core/handle/textHandler/serverMessageHandler.py
  94. 77 0
      main/xingxing-server/core/handle/textHandler/telemetryMessageHandler.py
  95. 0 0
      main/xingxing-server/core/handle/textMessageHandler.py
  96. 2 0
      main/xiaozhi-server/core/handle/textMessageHandlerRegistry.py
  97. 6 3
      main/xiaozhi-server/core/handle/textMessageProcessor.py
  98. 1 0
      main/xiaozhi-server/core/handle/textMessageType.py
  99. 5 0
      main/xiaozhi-server/core/http_server.py
  100. 0 0
      main/xiaozhi-server/core/providers/asr/aliyun.py

+ 2 - 1
.gitignore

@@ -149,6 +149,7 @@ tmp
 .history
 .DS_Store
 main/xiaozhi-server/data
+main/xingxing-server/data
 
 main/manager-web/node_modules
 .config.yaml
@@ -186,4 +187,4 @@ uploadfile
 !main/manager-mobile/**/env/
 !main/manager-mobile/**/.env*
 !main/manager-mobile/**/*.json
-!main/xiaozhi-server/**/*.json
+!main/xiaozhi-server/**/*.json

+ 0 - 162
main/xiaozhi-server/core/api/app_gps_handler.py

@@ -1,162 +0,0 @@
-import asyncio
-import time
-from aiohttp import web
-
-from config.logger import setup_logging
-from core.api.base_handler import BaseHandler
-from core.providers.tools.device_mcp.mcp_handler import call_mcp_tool
-from plugins_func.nav_copilot.service import _parse_fix, _pick_gnss_tool_name
-
-TAG = __name__
-
-
-class AppGpsHandler(BaseHandler):
-    def __init__(self, config: dict, ws_server):
-        super().__init__(config)
-        self.ws_server = ws_server
-        self.logger = setup_logging()
-        self._inflight_lock = asyncio.Lock()
-        self._inflight_tasks: dict[str, asyncio.Task] = {}
-        self._latest_payloads: dict[str, tuple[float, dict]] = {}
-        self._cache_ttl_s = 1.5
-        self._tool_timeout_s = 8
-
-    async def _resolve_conn(self, request):
-        device_id = request.query.get("device_id")
-        conn = await self.ws_server.get_connection(device_id=device_id)
-        if conn is None:
-            return None, "no_active_device"
-        if not hasattr(conn, "mcp_client") or conn.mcp_client is None:
-            return None, "device_mcp_not_initialized"
-        if not await conn.mcp_client.is_ready():
-            return None, "device_mcp_not_ready"
-        tool_name = _pick_gnss_tool_name(conn)
-        if not tool_name:
-            return None, "device_gnss_tool_not_found"
-        return (conn, tool_name), None
-
-    @staticmethod
-    def _device_key(conn) -> str:
-        return str(getattr(conn, "device_id", "") or "default")
-
-    async def _fetch_fix_payload(self, conn, tool_name: str) -> dict:
-        raw = await call_mcp_tool(
-            conn,
-            conn.mcp_client,
-            tool_name,
-            "{}",
-            timeout=self._tool_timeout_s,
-        )
-
-        fix = _parse_fix(raw)
-        if not fix.ok:
-            return {
-                "ok": False,
-                "device_id": getattr(conn, "device_id", None),
-                "reason": "no_fix",
-            }
-
-        payload = {
-            "ok": True,
-            "device_id": getattr(conn, "device_id", None),
-            "lat": fix.lat,
-            "lon": fix.lon,
-            "speed_mps": fix.speed_mps,
-            "heading_deg": fix.heading_deg,
-            "timestamp": fix.t,
-            "source": "device_mcp_gnss",
-        }
-        self._latest_payloads[self._device_key(conn)] = (time.monotonic(), payload)
-        return payload
-
-    async def _get_or_start_inflight(self, device_key: str, conn, tool_name: str):
-        async with self._inflight_lock:
-            task = self._inflight_tasks.get(device_key)
-            if task is None or task.done():
-                task = asyncio.create_task(self._fetch_fix_payload(conn, tool_name))
-                self._inflight_tasks[device_key] = task
-            return task
-
-    async def _get_fix_payload(self, request):
-        resolved, error_code = await self._resolve_conn(request)
-        if error_code:
-            return None, error_code
-
-        conn, tool_name = resolved
-        device_key = self._device_key(conn)
-
-        cached = self._latest_payloads.get(device_key)
-        now = time.monotonic()
-        if cached and (now - cached[0]) <= self._cache_ttl_s:
-            return cached[1], None
-
-        try:
-            task = await self._get_or_start_inflight(device_key, conn, tool_name)
-            payload = await task
-            return payload, None
-        except Exception as exc:
-            self.logger.bind(tag=TAG).warning(f"app gps tool call failed: {exc}")
-            cached = self._latest_payloads.get(device_key)
-            if cached:
-                stale_payload = dict(cached[1])
-                stale_payload["stale"] = True
-                return stale_payload, None
-            return None, "device_gnss_call_failed"
-        finally:
-            async with self._inflight_lock:
-                task = self._inflight_tasks.get(device_key)
-                if task is not None and task.done():
-                    self._inflight_tasks.pop(device_key, None)
-
-    def _json_response(self, payload: dict, status: int = 200):
-        response = web.json_response(payload, status=status)
-        self._add_cors_headers(response)
-        return response
-
-    async def handle_get_latest(self, request):
-        payload, error_code = await self._get_fix_payload(request)
-        if error_code:
-            return self._json_response(
-                {
-                    "ok": False,
-                    "error": error_code,
-                    "active_devices": await self.ws_server.list_active_device_ids(),
-                },
-                status=503,
-            )
-        return self._json_response(payload)
-
-    async def handle_ws(self, request):
-        ws = web.WebSocketResponse(heartbeat=30)
-        await ws.prepare(request)
-
-        interval_s = 1.0
-        interval_arg = request.query.get("interval")
-        if interval_arg:
-            try:
-                interval_s = max(0.2, float(interval_arg))
-            except ValueError:
-                interval_s = 1.0
-
-        try:
-            while True:
-                payload, error_code = await self._get_fix_payload(request)
-                if error_code:
-                    await ws.send_json(
-                        {
-                            "ok": False,
-                            "error": error_code,
-                            "active_devices": await self.ws_server.list_active_device_ids(),
-                        }
-                    )
-                else:
-                    await ws.send_json(payload)
-                await asyncio.sleep(interval_s)
-        except asyncio.CancelledError:
-            raise
-        except Exception as exc:
-            self.logger.bind(tag=TAG).warning(f"app gps websocket closed: {exc}")
-        finally:
-            await ws.close()
-
-        return ws

+ 0 - 56
main/xiaozhi-server/mcp_server_settings.json

@@ -1,56 +0,0 @@
-{
-  "des": [
-    "在data目录下创建.mcp_server_settings.json文件,可以选择下面的MCP服务,也可以自行添加新的MCP服务。",
-    "后面不断测试补充好用的mcp服务,欢迎大家一起补充。",
-    "记得删除注释行,des属性仅为说明,不会被解析。",
-    "des和link属性,仅为说明安装方式,方便大家查看原始链接,不是必须项。",
-    "当前支持三种传输模式:stdio(标准输入输出), sse(Server-Sent Events), streamable-http(流式HTTP)。"
-  ],
-  "mcpServers": {
-    "Home Assistant": {
-      "command": "mcp-proxy",
-      "args": [
-        "http://YOUR_HA_HOST/mcp_server/sse"
-      ],
-      "env": {
-        "API_ACCESS_TOKEN": "YOUR_API_ACCESS_TOKEN"
-      }
-    },
-    "filesystem": {
-      "command": "npx",
-      "args": [
-        "-y",
-        "@modelcontextprotocol/server-filesystem",
-        "/Users/username/Desktop",
-        "/path/to/other/allowed/dir"
-      ],
-      "link":"https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem"
-    },
-    "playwright": {
-      "command": "npx",
-      "args": ["-y", "@executeautomation/playwright-mcp-server"],
-      "des" : "run 'npx playwright install' first",
-      "link": "https://github.com/executeautomation/mcp-playwright"
-    },
-    "windows-cli": {
-      "command": "npx",
-      "args": ["-y", "@simonb97/server-win-cli"],
-      "link": "https://github.com/SimonB97/win-cli-mcp-server"
-    },
-    "sse-mcp-server": {
-      "url": "http://localhost:8080/sse",
-      "headers": {
-        "Authorization": "Bearer YOUR TOKEN"
-      },
-      "des": "使用SSE传输模式(默认)"
-    },
-    "streamable-http-mcp-server": {
-      "url": "http://localhost:8000/mcp",
-      "transport": "streamable-http",
-      "headers": {
-        "Authorization": "Bearer YOUR TOKEN"
-      },
-      "des": "使用Streamable HTTP传输模式,适用于生产环境的Web部署"
-    }
-  }
-}

+ 0 - 14
main/xiaozhi-server/models/SenseVoiceSmall/configuration.json

@@ -1,14 +0,0 @@
-{
-  "framework": "pytorch",
-  "task" : "auto-speech-recognition",
-  "model": {"type" : "funasr"},
-  "pipeline": {"type":"funasr-pipeline"},
-  "model_name_in_hub": {
-    "ms":"", 
-    "hf":""},
-  "file_path_metas": {
-    "init_param":"model.pt", 
-    "config":"config.yaml",
-    "tokenizer_conf": {"bpemodel": "chn_jpn_yue_eng_ko_spectok.bpe.model"},
-    "frontend_conf":{"cmvn_file": "am.mvn"}}
-}

+ 0 - 331
main/xiaozhi-server/plugins_func/functions/nav_copilot.py

@@ -1,331 +0,0 @@
-from __future__ import annotations
-
-from plugins_func.register import Action, ActionResponse, ToolType, register_function
-
-from plugins_func.nav_copilot.service import get_service
-
-
-nav_start_record_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_start_record",
-        "description": (
-            "开始记录路线。GPS 模式下,标记风险点会保存当前 GPS 位置;"
-            "结束录制后会进入预警状态。"
-            "当用户说“开始记录路线”“开始录制路线”“开始记录”时,优先调用这个工具。"
-        ),
-        "parameters": {
-            "type": "object",
-            "properties": {
-                "route_name": {
-                    "type": "string",
-                    "description": "路线名称,可选",
-                },
-                "sample_hz": {
-                    "type": "integer",
-                    "description": "采样频率,默认 10Hz",
-                },
-                "mode": {
-                    "type": "string",
-                    "description": "路线模式。通常使用 gps",
-                    "enum": ["auto", "counter", "gps"],
-                },
-            },
-            "required": [],
-        },
-    },
-}
-
-
-@register_function("nav_start_record", nav_start_record_desc, ToolType.SYSTEM_CTL)
-async def nav_start_record(
-    conn,
-    route_name: str | None = None,
-    sample_hz: int = 10,
-    mode: str = "gps",
-):
-    svc = get_service(conn)
-    route_id = await svc.start_recording(
-        route_name=route_name,
-        sample_hz=sample_hz,
-        mode=mode,
-    )
-    route_mode = svc.record_mode or "auto"
-    if route_mode == "counter":
-        text = (
-            f"已经进入计数路线录制准备状态,路线 ID 是 {route_id}。"
-            "到风险点时直接说“标记急转弯”或“标记障碍”,说“结束录制”后会进入预警。"
-        )
-    else:
-        text = (
-            f"已经开始记录 GPS 路线,路线 ID 是 {route_id}。"
-            "到风险点时可以直接说“标记水坑”“标记急转弯”或“标记障碍”。"
-        )
-    return ActionResponse(action=Action.RESPONSE, result=str(route_id), response=text)
-
-
-nav_stop_record_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_stop_record",
-        "description": "结束当前路线记录并保存。如果已经标记过风险点,则自动进入预警状态。",
-        "parameters": {"type": "object", "properties": {}, "required": []},
-    },
-}
-
-
-@register_function("nav_stop_record", nav_stop_record_desc, ToolType.SYSTEM_CTL)
-async def nav_stop_record(conn):
-    svc = get_service(conn)
-    result = await svc.stop_recording()
-    if result is None:
-        return ActionResponse(action=Action.RESPONSE, response="当前没有正在记录的路线。")
-
-    if result.monitor_started:
-        if result.mode == "counter":
-            response = (
-                f"路线录制已结束,路线 ID 是 {result.route_id}。"
-                f"已记录 {result.hazard_count} 个风险点,并且已经开始监听预警。"
-            )
-        else:
-            response = (
-                f"路线记录已结束,路线 ID 是 {result.route_id}。"
-                f"已记录 {result.hazard_count} 个风险点,并且已经进入预警状态。"
-            )
-    else:
-        response = (
-            f"路线记录已结束,路线 ID 是 {result.route_id}。"
-            "但你还没有标记风险点,所以还没有进入预警状态。"
-        )
-
-    return ActionResponse(
-        action=Action.RESPONSE,
-        result=str(result.route_id),
-        response=response,
-    )
-
-
-nav_mark_hazard_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_mark_hazard",
-        "description": (
-            "在当前位置标记一个风险点。"
-            "GPS 模式下会保存当前 GPS 数据,后续进入该风险点范围内时触发预警。"
-            "典型口令包括“标记水坑”“标记急转弯”“标记障碍”。"
-        ),
-        "parameters": {
-            "type": "object",
-            "properties": {
-                "hazard_type": {
-                    "type": "string",
-                    "description": "风险点类型",
-                    "enum": [
-                        "sharp_turn",
-                        "bump",
-                        "jump",
-                        "slope",
-                        "water",
-                        "obstacle",
-                        "other",
-                    ],
-                },
-                "note": {
-                    "type": "string",
-                    "description": "补充说明,可选",
-                },
-            },
-            "required": ["hazard_type"],
-        },
-    },
-}
-
-
-@register_function("nav_mark_hazard", nav_mark_hazard_desc, ToolType.SYSTEM_CTL)
-async def nav_mark_hazard(conn, hazard_type: str, note: str | None = None):
-    svc = get_service(conn)
-    result = await svc.mark_hazard(hazard_type=hazard_type, note=note or "")
-    if result.mode == "counter" and result.counter_value is not None:
-        response = f"已标记风险点,当前串口值是 {result.counter_value}。"
-    elif result.mode == "gps" and result.lat is not None and result.lon is not None:
-        response = f"已标记风险点,GPS 数据是 {result.lat:.6f},{result.lon:.6f}。"
-    else:
-        response = "已标记风险点。"
-    return ActionResponse(
-        action=Action.RESPONSE,
-        result=str(result.hazard_id),
-        response=response,
-    )
-
-
-nav_mark_hazard_reject_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_mark_hazard_reject",
-        "description": "当用户只说标记风险点但没有说具体类型时,提示其重新说清楚。",
-        "parameters": {"type": "object", "properties": {}, "required": []},
-    },
-}
-
-
-@register_function("nav_mark_hazard_reject", nav_mark_hazard_reject_desc, ToolType.SYSTEM_CTL)
-async def nav_mark_hazard_reject(conn):
-    return ActionResponse(
-        action=Action.RESPONSE,
-        response="请直接说要标记的风险类型,例如:标记水坑、标记急转弯、标记障碍。",
-    )
-
-
-nav_start_run_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_start_run",
-        "description": (
-            "开始预警播报。通常结束录制后会自动进入预警;"
-            "这个工具用于手动补启。"
-        ),
-        "parameters": {
-            "type": "object",
-            "properties": {
-                "route_id": {
-                    "type": "integer",
-                    "description": "指定路线 ID,可选",
-                },
-                "tick_hz": {
-                    "type": "integer",
-                    "description": "刷新频率,默认 10Hz",
-                },
-                "mode": {
-                    "type": "string",
-                    "description": "路线模式,通常不需要手动指定",
-                    "enum": ["auto", "counter", "gps"],
-                },
-            },
-            "required": [],
-        },
-    },
-}
-
-
-@register_function("nav_start_run", nav_start_run_desc, ToolType.SYSTEM_CTL)
-async def nav_start_run(
-    conn,
-    route_id: int | None = None,
-    tick_hz: int = 10,
-    mode: str = "auto",
-):
-    svc = get_service(conn)
-    active_route_id = await svc.start_run(route_id=route_id, tick_hz=tick_hz, mode=mode)
-    if svc.run_mode == "counter":
-        response = f"已经开始监听预警,使用路线 ID={active_route_id}。"
-    else:
-        response = f"已经开始预警,使用路线 ID={active_route_id}。"
-    return ActionResponse(
-        action=Action.RESPONSE,
-        result=str(active_route_id),
-        response=response,
-    )
-
-
-nav_stop_run_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_stop_run",
-        "description": "停止当前预警播报。",
-        "parameters": {"type": "object", "properties": {}, "required": []},
-    },
-}
-
-
-@register_function("nav_stop_run", nav_stop_run_desc, ToolType.SYSTEM_CTL)
-async def nav_stop_run(conn):
-    svc = get_service(conn)
-    route_id = await svc.stop_run()
-    if route_id is None:
-        return ActionResponse(action=Action.RESPONSE, response="当前没有正在运行的预警。")
-    return ActionResponse(
-        action=Action.RESPONSE,
-        result=str(route_id),
-        response="预警已停止。",
-    )
-
-
-nav_status_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_status",
-        "description": "查询路线记录和预警状态。",
-        "parameters": {"type": "object", "properties": {}, "required": []},
-    },
-}
-
-
-@register_function("nav_status", nav_status_desc, ToolType.SYSTEM_CTL)
-async def nav_status(conn):
-    svc = get_service(conn)
-    return ActionResponse(action=Action.RESPONSE, response=svc.status_text())
-
-
-nav_list_routes_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_list_routes",
-        "description": "列出最近保存的路线。",
-        "parameters": {
-            "type": "object",
-            "properties": {
-                "limit": {
-                    "type": "integer",
-                    "description": "返回数量,默认 10",
-                }
-            },
-            "required": [],
-        },
-    },
-}
-
-
-@register_function("nav_list_routes", nav_list_routes_desc, ToolType.SYSTEM_CTL)
-async def nav_list_routes(conn, limit: int = 10):
-    svc = get_service(conn)
-    routes = svc.db.list_routes(limit=int(limit or 10))
-    if not routes:
-        return ActionResponse(action=Action.RESPONSE, response="暂无路线,请先开始记录路线。")
-
-    lines = []
-    for route in routes:
-        active = "(激活)" if route.active else ""
-        monitoring = "(预警中)" if route.monitoring else ""
-        lines.append(
-            f"路线 {route.id}{active}{monitoring}:{route.name},模式 {route.mode},点数 {route.points},风险点 {route.hazards}"
-        )
-    return ActionResponse(action=Action.RESPONSE, response="\n".join(lines))
-
-
-nav_set_active_route_desc = {
-    "type": "function",
-    "function": {
-        "name": "nav_set_active_route",
-        "description": "将某条路线设置为当前激活路线。",
-        "parameters": {
-            "type": "object",
-            "properties": {
-                "route_id": {
-                    "type": "integer",
-                    "description": "路线 ID",
-                }
-            },
-            "required": ["route_id"],
-        },
-    },
-}
-
-
-@register_function("nav_set_active_route", nav_set_active_route_desc, ToolType.SYSTEM_CTL)
-async def nav_set_active_route(conn, route_id: int):
-    svc = get_service(conn)
-    svc.db.set_active_route(int(route_id))
-    return ActionResponse(
-        action=Action.RESPONSE,
-        response=f"已激活路线 ID={int(route_id)}。",
-    )

+ 0 - 58
main/xiaozhi-server/plugins_func/nav_copilot/geo.py

@@ -1,58 +0,0 @@
-from __future__ import annotations
-
-import math
-from typing import Iterable, Optional, Tuple
-
-
-def haversine_m(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
-    """Great-circle distance in meters."""
-    r = 6371000.0
-    phi1 = math.radians(lat1)
-    phi2 = math.radians(lat2)
-    d_phi = math.radians(lat2 - lat1)
-    d_lam = math.radians(lon2 - lon1)
-    a = (
-        math.sin(d_phi / 2.0) ** 2
-        + math.cos(phi1) * math.cos(phi2) * math.sin(d_lam / 2.0) ** 2
-    )
-    c = 2.0 * math.atan2(math.sqrt(a), math.sqrt(1.0 - a))
-    return r * c
-
-
-def clamp(n: int, lo: int, hi: int) -> int:
-    return max(lo, min(hi, n))
-
-
-def nearest_point_index(
-    points_latlon: Iterable[Tuple[float, float]],
-    lat: float,
-    lon: float,
-    *,
-    start_idx: Optional[int] = None,
-    window: int = 80,
-) -> Tuple[int, float]:
-    """Return (idx, distance_m) of the nearest point.
-
-    If start_idx is given, search a window around it for speed.
-    """
-    pts = list(points_latlon)
-    if not pts:
-        raise ValueError("empty points")
-
-    if start_idx is None:
-        search_lo = 0
-        search_hi = len(pts) - 1
-    else:
-        search_lo = clamp(start_idx - window, 0, len(pts) - 1)
-        search_hi = clamp(start_idx + window, 0, len(pts) - 1)
-
-    best_i = search_lo
-    best_d = float("inf")
-    for i in range(search_lo, search_hi + 1):
-        p_lat, p_lon = pts[i]
-        d = haversine_m(lat, lon, p_lat, p_lon)
-        if d < best_d:
-            best_d = d
-            best_i = i
-    return best_i, best_d
-

+ 0 - 991
main/xiaozhi-server/plugins_func/nav_copilot/service.py

@@ -1,991 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-import json
-import time
-from dataclasses import dataclass
-from typing import Any, Dict, List, Optional, Set, Tuple
-
-from config.logger import setup_logging
-from core.providers.tools.device_mcp.mcp_handler import call_mcp_tool
-from core.providers.tts.dto.dto import ContentType
-
-from .geo import haversine_m, nearest_point_index
-from .storage import Hazard, NavDB, RoutePoint
-
-
-TAG = __name__
-logger = setup_logging()
-
-COUNTER_MAX_VALUE = 256
-COUNTER_WRAP_THRESHOLD = 128
-
-
-_HAZARD_LABELS: Dict[str, str] = {
-    "sharp_turn": "急转弯",
-    "bump": "大坑",
-    "jump": "跳台",
-    "slope": "陡坡",
-    "water": "涉水",
-    "obstacle": "障碍",
-    "other": "风险点",
-}
-
-_HAZARD_SYNONYMS: Dict[str, str] = {
-    "急转弯": "sharp_turn",
-    "急弯": "sharp_turn",
-    "大坑": "bump",
-    "坑": "bump",
-    "跳台": "jump",
-    "陡坡": "slope",
-    "涉水": "water",
-    "障碍": "obstacle",
-}
-
-
-@dataclass
-class GnssFix:
-    ok: bool
-    lat: float
-    lon: float
-    speed_mps: float
-    heading_deg: Optional[float] = None
-    t: Optional[int] = None
-
-
-@dataclass
-class CounterState:
-    ok: bool
-    value: int
-    absolute: int
-    cycle: int
-    t: Optional[int] = None
-
-
-@dataclass(frozen=True)
-class HazardMarkResult:
-    hazard_id: int
-    route_id: int
-    mode: str
-    counter_value: Optional[int] = None
-    counter_absolute: Optional[int] = None
-    lat: Optional[float] = None
-    lon: Optional[float] = None
-
-
-@dataclass(frozen=True)
-class StopRecordingResult:
-    route_id: int
-    mode: str
-    hazard_count: int
-    monitor_started: bool
-
-
-def _parse_jsonish(raw: Any) -> Optional[dict]:
-    if raw is None:
-        return None
-    if isinstance(raw, str):
-        raw = raw.strip()
-        if not raw:
-            return None
-        try:
-            raw = json.loads(raw)
-        except Exception:
-            return None
-    if not isinstance(raw, dict):
-        return None
-    return raw
-
-
-def _parse_fix(raw: Any) -> GnssFix:
-    payload = _parse_jsonish(raw)
-    if payload is None:
-        return GnssFix(False, 0.0, 0.0, 0.0)
-
-    if "gnss" in payload and isinstance(payload.get("gnss"), dict):
-        payload = payload["gnss"]
-
-    ok = bool(payload.get("ok", True))
-    has_fix = payload.get("has_fix")
-    if has_fix is not None:
-        ok = ok and bool(has_fix)
-
-    lat = payload.get("lat", payload.get("latitude"))
-    lon = payload.get("lon", payload.get("longitude"))
-    try:
-        lat_f = float(lat)
-        lon_f = float(lon)
-    except Exception:
-        return GnssFix(False, 0.0, 0.0, 0.0)
-
-    speed_mps = payload.get("speed_mps")
-    if speed_mps is None:
-        speed_knots = payload.get("speed_knots")
-        if speed_knots is not None:
-            try:
-                speed_mps = float(speed_knots) * 0.514444
-            except Exception:
-                speed_mps = 0.0
-        else:
-            speed_kmh = payload.get("speed_kmh")
-            if speed_kmh is not None:
-                try:
-                    speed_mps = float(speed_kmh) / 3.6
-                except Exception:
-                    speed_mps = 0.0
-            else:
-                speed_mps = payload.get("speed") or 0.0
-    try:
-        speed_mps_f = float(speed_mps)
-    except Exception:
-        speed_mps_f = 0.0
-
-    heading = payload.get("heading_deg", payload.get("course_deg"))
-    heading_f = None
-    if heading is not None:
-        try:
-            heading_f = float(heading)
-        except Exception:
-            heading_f = None
-
-    t = payload.get("t") or payload.get("timestamp") or payload.get("time")
-    t_i = None
-    if t is not None:
-        try:
-            t_i = int(t)
-        except Exception:
-            t_i = None
-
-    fix = payload.get("fix") or payload.get("fix_quality") or payload.get("quality")
-    if fix is not None:
-        try:
-            ok = ok and int(fix) > 0
-        except Exception:
-            pass
-
-    return GnssFix(ok, lat_f, lon_f, max(0.0, speed_mps_f), heading_f, t_i)
-
-
-def _parse_counter_state(raw: Any) -> CounterState:
-    payload = _parse_jsonish(raw)
-    if payload is None:
-        return CounterState(False, 0, 0, 0)
-
-    if "counter" in payload and isinstance(payload.get("counter"), dict):
-        payload = payload["counter"]
-
-    ok = bool(payload.get("ok", True))
-    has_value = payload.get("has_value")
-    if has_value is not None:
-        ok = ok and bool(has_value)
-
-    value = (
-        payload.get("value")
-        or payload.get("counter_value")
-        or payload.get("counter")
-        or payload.get("raw")
-    )
-    absolute = payload.get("absolute") or payload.get("counter_absolute")
-    cycle = payload.get("cycle", 0)
-    t = payload.get("t") or payload.get("timestamp") or payload.get("time")
-
-    try:
-        value_i = int(value)
-    except Exception:
-        return CounterState(False, 0, 0, 0)
-    if value_i < 1 or value_i > COUNTER_MAX_VALUE:
-        return CounterState(False, 0, 0, 0)
-
-    try:
-        absolute_i = int(absolute) if absolute is not None else value_i
-    except Exception:
-        absolute_i = value_i
-    try:
-        cycle_i = int(cycle)
-    except Exception:
-        cycle_i = 0
-    try:
-        t_i = int(t) if t is not None else None
-    except Exception:
-        t_i = None
-
-    return CounterState(ok, value_i, max(value_i, absolute_i), max(0, cycle_i), t_i)
-
-
-def _pick_tool_name(conn, preferred: str, keywords: Tuple[str, ...]) -> Optional[str]:
-    if not hasattr(conn, "mcp_client") or not conn.mcp_client:
-        return None
-    if conn.mcp_client.has_tool(preferred):
-        return preferred
-    for name in conn.mcp_client.tools.keys():
-        lowered = name.lower()
-        if all(keyword in lowered for keyword in keywords):
-            return name
-    return None
-
-
-def _pick_gnss_tool_name(conn) -> Optional[str]:
-    return _pick_tool_name(conn, "self_gnss_get_fix", ("gnss", "fix"))
-
-
-def _pick_counter_tool_name(conn) -> Optional[str]:
-    return _pick_tool_name(conn, "self_nav_counter_get_state", ("counter", "state"))
-
-
-def _pick_nav_alert_tool_name(conn) -> Optional[str]:
-    return _pick_tool_name(conn, "self_nav_alert_play", ("nav", "alert", "play"))
-
-
-class NavCopilotService:
-    def __init__(self, conn, db: NavDB):
-        self.conn = conn
-        self.db = db
-
-        self.record_task: Optional[asyncio.Task] = None
-        self.record_route_id: Optional[int] = None
-        self.record_mode: Optional[str] = None
-        self._record_seq = 0
-        self._record_cum_m = 0.0
-        self._record_last_latlon: Optional[Tuple[float, float]] = None
-        self._record_last_t = 0
-        self._record_interval_s = 0.1
-        self._record_counter_start_absolute: Optional[int] = None
-        self._record_counter_last_absolute: Optional[int] = None
-        self._record_counter_hazards: List[Hazard] = []
-        self._record_counter_announced: Set[Tuple[int, int, str]] = set()
-        self._record_last_announce_at = 0.0
-
-        self.run_task: Optional[asyncio.Task] = None
-        self.run_route_id: Optional[int] = None
-        self.run_mode: Optional[str] = None
-        self._run_interval_s = 0.1
-        self._run_points: List[RoutePoint] = []
-        self._run_hazards: List[Hazard] = []
-        self._run_last_idx: Optional[int] = None
-        self._run_announced: Set[int] = set()
-        self._run_gps_last_announce_at: Dict[int, float] = {}
-        self._run_counter_announced: Set[Tuple[int, int, str]] = set()
-        self._run_last_announce_at = 0.0
-
-        self.gps_alert_radius_m = 3.0
-        self.gps_alert_repeat_cooldown_s = 2.0
-        self.announce_at_m = 50.0
-        self.announce_window_m = 18.0
-        self.announce_cooldown_s = 5.0
-        self.counter_announce_ahead = 6
-        self.counter_announce_cooldown_s = 1.5
-
-    async def recover_monitoring_if_needed(self) -> Optional[int]:
-        if self.run_route_id is not None:
-            return self.run_route_id
-        if not _pick_gnss_tool_name(self.conn):
-            return None
-
-        route_id = self.db.get_monitoring_route_id()
-        if not route_id:
-            return None
-
-        hazards = self.db.load_hazards(route_id)
-        if not hazards:
-            logger.bind(tag=TAG).warning(
-                f"Route {route_id} was marked as monitoring but has no hazards; clearing monitoring state"
-            )
-            self.db.set_monitoring_route(None)
-            return None
-
-        route_mode = self.db.get_route_mode(route_id)
-        if route_mode != "gps":
-            logger.bind(tag=TAG).warning(
-                f"Route {route_id} monitoring recovery only supports gps mode, current mode={route_mode}; clearing monitoring state"
-            )
-            self.db.set_monitoring_route(None)
-            return None
-
-        try:
-            await self.start_run(route_id=route_id, tick_hz=10, mode=route_mode)
-        except RuntimeError as exc:
-            logger.bind(tag=TAG).warning(
-                f"Failed to recover monitoring route {route_id}: {exc}"
-            )
-            return None
-
-        logger.bind(tag=TAG).info(
-            f"Recovered GPS monitoring after reconnect, route_id={route_id}, hazards={len(hazards)}"
-        )
-        return route_id
-
-    def _counter_input_hint(self) -> str:
-        return (
-            "请确认 STM32 已接到 RXD1/GND,波特率 115200,"
-            "并且按带分隔符的文本格式发送,例如 1\\n 2\\n 3\\n ... 256\\n。"
-        )
-
-    def _speak(self, text: str) -> None:
-        try:
-            self.conn.tts.tts_one_sentence(
-                self.conn,
-                ContentType.TEXT,
-                content_detail=text,
-            )
-        except Exception as exc:
-            logger.bind(tag=TAG).warning(f"TTS speak failed: {exc}")
-
-    async def _play_local_hazard_alert(self, hazard: Hazard) -> bool:
-        tool_name = _pick_nav_alert_tool_name(self.conn)
-        if not tool_name:
-            return False
-        try:
-            await call_mcp_tool(
-                self.conn,
-                self.conn.mcp_client,
-                tool_name,
-                json.dumps(
-                    {
-                        "hazard_type": hazard.type,
-                        "note": hazard.note or "",
-                    },
-                    ensure_ascii=False,
-                ),
-                timeout=5,
-            )
-            return True
-        except Exception as exc:
-            logger.bind(tag=TAG).warning(f"Local hazard alert failed: {exc}")
-            return False
-
-    async def _announce_gps_hazard(self, hazard: Hazard) -> None:
-        if await self._play_local_hazard_alert(hazard):
-            return
-        label = _HAZARD_LABELS.get(hazard.type, _HAZARD_LABELS["other"])
-        text = f"注意{label}"
-        if hazard.note:
-            text = f"{text},{hazard.note}"
-        self._speak(text)
-
-    def _normalize_mode(self, mode: str | None) -> str:
-        normalized = (mode or "gps").strip().lower()
-        if normalized not in {"auto", "gps", "counter"}:
-            return "gps"
-        return normalized
-
-    def _resolve_mode(self, requested_mode: str | None, route_id: int | None = None) -> str:
-        mode = self._normalize_mode(requested_mode)
-        if route_id:
-            stored_mode = self.db.get_route_mode(route_id)
-            if stored_mode in {"gps", "counter"}:
-                return stored_mode
-        if mode == "gps":
-            if not _pick_gnss_tool_name(self.conn):
-                raise RuntimeError("设备端没有可用的 GNSS 定位工具")
-            return "gps"
-        if _pick_gnss_tool_name(self.conn):
-            return "gps"
-        raise RuntimeError("设备端没有可用的 GNSS 定位工具")
-
-    async def _get_fix(self) -> GnssFix:
-        tool_name = _pick_gnss_tool_name(self.conn)
-        if not tool_name:
-            return GnssFix(False, 0.0, 0.0, 0.0)
-        try:
-            raw = await call_mcp_tool(
-                self.conn,
-                self.conn.mcp_client,
-                tool_name,
-                "{}",
-                timeout=5,
-            )
-            return _parse_fix(raw)
-        except Exception as exc:
-            logger.bind(tag=TAG).warning(f"GNSS fix failed: {exc}")
-            return GnssFix(False, 0.0, 0.0, 0.0)
-
-    async def _get_counter_state(self) -> CounterState:
-        tool_name = _pick_counter_tool_name(self.conn)
-        if not tool_name:
-            return CounterState(False, 0, 0, 0)
-        try:
-            raw = await call_mcp_tool(
-                self.conn,
-                self.conn.mcp_client,
-                tool_name,
-                "{}",
-                timeout=5,
-            )
-            return _parse_counter_state(raw)
-        except Exception as exc:
-            logger.bind(tag=TAG).warning(f"Counter state failed: {exc}")
-            return CounterState(False, 0, 0, 0)
-
-    def _counter_distance(self, current_value: int, target_value: int) -> int:
-        delta = target_value - current_value
-        if delta < 0:
-            delta += COUNTER_MAX_VALUE
-        return delta
-
-    def _prune_counter_announced(
-        self,
-        announced: Set[Tuple[int, int, str]],
-        current_cycle: int,
-    ) -> None:
-        stale = {
-            item for item in announced if item[1] < max(0, current_cycle - 1)
-        }
-        announced.difference_update(stale)
-
-    def _maybe_announce_counter_hazard(
-        self,
-        sample: CounterState,
-        hazards: List[Hazard],
-        announced: Set[Tuple[int, int, str]],
-        *,
-        last_announce_at: float,
-    ) -> float:
-        if not sample.ok or not hazards:
-            return last_announce_at
-
-        self._prune_counter_announced(announced, sample.cycle)
-
-        for hazard in hazards:
-            if hazard.counter_value <= 0:
-                continue
-            arrive_key = (hazard.id, sample.cycle, "arrived")
-            if sample.value != hazard.counter_value or arrive_key in announced:
-                continue
-
-            label = _HAZARD_LABELS.get(hazard.type, _HAZARD_LABELS["other"])
-            text = f"当前{label}"
-            if hazard.note:
-                text = f"{text},{hazard.note}"
-            self._speak(text)
-            announced.add(arrive_key)
-            return time.time()
-
-        next_hazard: Optional[Hazard] = None
-        next_distance: Optional[int] = None
-        for hazard in hazards:
-            if hazard.counter_value <= 0:
-                continue
-            distance = self._counter_distance(sample.value, hazard.counter_value)
-            if distance == 0:
-                continue
-            ahead_key = (hazard.id, sample.cycle, "ahead")
-            if ahead_key in announced:
-                continue
-            if next_distance is None or distance < next_distance:
-                next_hazard = hazard
-                next_distance = distance
-
-        if next_hazard is None or next_distance is None:
-            return last_announce_at
-        if not (1 <= next_distance <= self.counter_announce_ahead):
-            return last_announce_at
-
-        now = time.time()
-        if now - last_announce_at < self.counter_announce_cooldown_s:
-            return last_announce_at
-
-        label = _HAZARD_LABELS.get(next_hazard.type, _HAZARD_LABELS["other"])
-        text = f"注意{label}"
-        if next_hazard.counter_value > 0:
-            text = f"{text},目标点 {next_hazard.counter_value}"
-        if next_hazard.note:
-            text = f"{text},{next_hazard.note}"
-        self._speak(text)
-        announced.add((next_hazard.id, sample.cycle, "ahead"))
-        return now
-
-    def _pick_counter_point(self, points: List[RoutePoint], sample: CounterState) -> RoutePoint:
-        if not points:
-            raise RuntimeError("路线里还没有计数点")
-        return min(
-            points,
-            key=lambda point: min(
-                self._counter_distance(sample.value, point.counter_value or 1),
-                self._counter_distance(point.counter_value or 1, sample.value),
-            ),
-        )
-
-    async def start_recording(
-        self,
-        route_name: Optional[str] = None,
-        sample_hz: int = 10,
-        mode: str | None = None,
-    ) -> int:
-        if self.record_route_id is not None:
-            raise RuntimeError("当前已经在记录路线")
-        if self.run_route_id is not None:
-            raise RuntimeError("当前正在预警,不能同时开始记录")
-
-        route_mode = self._resolve_mode(mode)
-        name = (route_name or "").strip() or f"路线 {time.strftime('%Y-%m-%d %H:%M:%S')}"
-        route_id = self.db.create_route(name, route_mode)
-
-        self.record_route_id = route_id
-        self.record_mode = route_mode
-        self._record_seq = 0
-        self._record_cum_m = 0.0
-        self._record_last_latlon = None
-        self._record_last_t = 0
-        self._record_counter_start_absolute = None
-        self._record_counter_last_absolute = None
-        self._record_counter_hazards = []
-        self._record_counter_announced = set()
-        self._record_last_announce_at = 0.0
-        hz = max(1, int(sample_hz or 10))
-        if route_mode == "counter":
-            hz = max(5, hz)
-        self._record_interval_s = 1.0 / hz
-
-        if route_mode == "counter":
-            self.record_task = None
-        else:
-            self.record_task = asyncio.create_task(
-                self._record_gps_loop(),
-                name=f"NavRecordGps:{route_id}",
-            )
-        return route_id
-
-    async def stop_recording(self) -> Optional[StopRecordingResult]:
-        route_id = self.record_route_id
-        if route_id is None:
-            return None
-
-        route_mode = self.record_mode or self.db.get_route_mode(route_id)
-        if self.record_task and not self.record_task.done():
-            self.record_task.cancel()
-            try:
-                await self.record_task
-            except BaseException:
-                pass
-
-        self.record_task = None
-        self.record_route_id = None
-        self.record_mode = None
-        self._record_counter_hazards = []
-        self._record_counter_announced = set()
-        self.db.finish_route(route_id)
-
-        hazard_count = len(self.db.load_hazards(route_id))
-        monitor_started = False
-        logger.bind(tag=TAG).info(
-            f"Stop recording route_id={route_id}, mode={route_mode}, hazards={hazard_count}"
-        )
-        if hazard_count > 0:
-            await self.start_run(route_id=route_id, tick_hz=10, mode=route_mode)
-            monitor_started = True
-
-        return StopRecordingResult(
-            route_id=route_id,
-            mode=route_mode,
-            hazard_count=hazard_count,
-            monitor_started=monitor_started,
-        )
-
-    async def _record_gps_loop(self) -> None:
-        assert self.record_route_id is not None
-        route_id = self.record_route_id
-        logger.bind(tag=TAG).info(f"GPS record loop started, route_id={route_id}")
-        try:
-            while not self.conn.stop_event.is_set():
-                fix = await self._get_fix()
-                now_t = int(fix.t or time.time())
-                if fix.ok:
-                    latlon = (fix.lat, fix.lon)
-                    should_add = False
-                    if self._record_last_latlon is None:
-                        should_add = True
-                    else:
-                        moved_m = haversine_m(
-                            self._record_last_latlon[0],
-                            self._record_last_latlon[1],
-                            latlon[0],
-                            latlon[1],
-                        )
-                        if moved_m >= 1.0:
-                            should_add = True
-                        if (now_t - self._record_last_t) >= 2:
-                            should_add = True
-
-                    if should_add:
-                        if self._record_last_latlon is not None:
-                            self._record_cum_m += haversine_m(
-                                self._record_last_latlon[0],
-                                self._record_last_latlon[1],
-                                latlon[0],
-                                latlon[1],
-                            )
-                        self._record_last_latlon = latlon
-                        self._record_last_t = now_t
-                        self._record_seq += 1
-
-                        self.db.add_point(
-                            route_id=route_id,
-                            seq=self._record_seq,
-                            t=now_t,
-                            lat=latlon[0],
-                            lon=latlon[1],
-                            speed_mps=fix.speed_mps,
-                            cum_dist_m=self._record_cum_m,
-                        )
-
-                await asyncio.sleep(self._record_interval_s)
-        finally:
-            if self.record_route_id == route_id:
-                try:
-                    self.db.finish_route(route_id)
-                except Exception:
-                    pass
-                self.record_route_id = None
-                self.record_mode = None
-            self.record_task = None
-
-    async def _record_counter_loop(self) -> None:
-        assert self.record_route_id is not None
-        route_id = self.record_route_id
-        logger.bind(tag=TAG).info(f"Counter record loop started, route_id={route_id}")
-        try:
-            while not self.conn.stop_event.is_set():
-                sample = await self._get_counter_state()
-                now_t = int(sample.t or time.time())
-                if sample.ok:
-                    if self._record_counter_start_absolute is None:
-                        self._record_counter_start_absolute = sample.absolute
-
-                    should_add = (
-                        self._record_counter_last_absolute is None
-                        or sample.absolute != self._record_counter_last_absolute
-                    )
-                    if should_add:
-                        self._record_seq += 1
-                        self._record_cum_m = float(
-                            max(0, sample.absolute - self._record_counter_start_absolute)
-                        )
-                        self._record_counter_last_absolute = sample.absolute
-                        self._record_last_t = now_t
-                        self.db.add_point(
-                            route_id=route_id,
-                            seq=self._record_seq,
-                            t=now_t,
-                            lat=0.0,
-                            lon=0.0,
-                            speed_mps=0.0,
-                            cum_dist_m=self._record_cum_m,
-                            counter_value=sample.value,
-                            counter_absolute=sample.absolute,
-                        )
-
-                    self._record_last_announce_at = self._maybe_announce_counter_hazard(
-                        sample,
-                        self._record_counter_hazards,
-                        self._record_counter_announced,
-                        last_announce_at=self._record_last_announce_at,
-                    )
-
-                await asyncio.sleep(self._record_interval_s)
-        finally:
-            if self.record_route_id == route_id:
-                try:
-                    self.db.finish_route(route_id)
-                except Exception:
-                    pass
-                self.record_route_id = None
-                self.record_mode = None
-            self.record_task = None
-            self._record_counter_hazards = []
-            self._record_counter_announced = set()
-
-    async def mark_hazard(self, hazard_type: str, note: str = "") -> HazardMarkResult:
-        hazard_type = (hazard_type or "").strip() or "other"
-        if hazard_type in _HAZARD_SYNONYMS:
-            hazard_type = _HAZARD_SYNONYMS[hazard_type]
-        if hazard_type not in _HAZARD_LABELS:
-            hazard_type = "other"
-
-        route_id = self.record_route_id or self.run_route_id or self.db.get_active_route_id()
-        if not route_id:
-            raise RuntimeError("没有可用路线,请先开始记录")
-        route_mode = "gps"
-        now_t = int(time.time())
-
-        if False and route_mode == "counter":
-            sample = await self._get_counter_state()
-            if not sample.ok:
-                fix = await self._get_fix()
-                if fix.ok:
-                    logger.bind(tag=TAG).warning(
-                        f"Counter route {route_id} has no live counter sample but GPS is available; switching route to gps mode"
-                    )
-                    self.db.update_route_mode(route_id, "gps")
-                    route_mode = "gps"
-                    if self.record_route_id == route_id:
-                        self.record_mode = "gps"
-                        if self.record_task is None or self.record_task.done():
-                            self._record_interval_s = 0.1
-                            self.record_task = asyncio.create_task(
-                                self._record_gps_loop(),
-                                name=f"NavRecordGps:{route_id}",
-                            )
-                    if self.run_route_id == route_id:
-                        self.run_mode = "gps"
-            if route_mode == "counter" and not sample.ok:
-                raise RuntimeError(
-                    "当前没有可用的串口计数值,无法标记风险点。"
-                    + self._counter_input_hint()
-                )
-
-            if route_mode == "counter":
-                points = self.db.load_route_points(route_id)
-                if points:
-                    point = self._pick_counter_point(points, sample)
-                    seq = point.seq
-                    cum = point.cum_dist_m
-                else:
-                    seq = len(self.db.load_hazards(route_id)) + 1
-                    cum = float(max(0, seq - 1))
-
-                hazard_id = self.db.add_hazard(
-                    route_id=route_id,
-                    seq=seq,
-                    t=int(sample.t or now_t),
-                    lat=0.0,
-                    lon=0.0,
-                    cum_dist_m=cum,
-                    hazard_type=hazard_type,
-                    note=note or "",
-                    counter_value=sample.value,
-                    counter_absolute=sample.absolute,
-                )
-
-                if self.record_route_id == route_id and self.record_mode == "counter":
-                    self._record_counter_hazards = self.db.load_hazards(route_id)
-                if self.run_route_id == route_id and self.run_mode == "counter":
-                    self._run_hazards = self.db.load_hazards(route_id)
-                return HazardMarkResult(
-                    hazard_id=hazard_id,
-                    route_id=route_id,
-                    mode="counter",
-                    counter_value=sample.value,
-                    counter_absolute=sample.absolute,
-                )
-
-        fix = await self._get_fix()
-        if not fix.ok:
-            raise RuntimeError("当前没有有效 GPS 定位,无法标记风险点")
-
-        if self.record_route_id == route_id and self._record_last_latlon is not None:
-            seq = self._record_seq
-            cum = self._record_cum_m
-        else:
-            points = self.db.load_route_points(route_id)
-            if points:
-                idx, _ = nearest_point_index([(point.lat, point.lon) for point in points], fix.lat, fix.lon)
-                seq = points[idx].seq
-                cum = points[idx].cum_dist_m
-            else:
-                seq = self._record_seq if self._record_seq > 0 else len(self.db.load_hazards(route_id)) + 1
-                cum = self._record_cum_m
-
-        hazard_id = self.db.add_hazard(
-            route_id=route_id,
-            seq=seq,
-            t=int(fix.t or now_t),
-            lat=fix.lat,
-            lon=fix.lon,
-            cum_dist_m=cum,
-            hazard_type=hazard_type,
-            note=note or "",
-        )
-
-        if self.run_route_id == route_id and self.run_mode == "gps":
-            self._run_hazards = self.db.load_hazards(route_id)
-        return HazardMarkResult(
-            hazard_id=hazard_id,
-            route_id=route_id,
-            mode="gps",
-            lat=fix.lat,
-            lon=fix.lon,
-        )
-
-    async def start_run(
-        self,
-        route_id: Optional[int] = None,
-        tick_hz: int = 10,
-        mode: str | None = None,
-    ) -> int:
-        if self.run_route_id is not None:
-            raise RuntimeError("当前已经在预警")
-        if self.record_route_id is not None:
-            raise RuntimeError("当前正在记录路线,不能同时开始预警")
-
-        route_id = route_id or self.db.get_active_route_id()
-        if not route_id:
-            raise RuntimeError("没有可用路线,请先开始记录路线")
-
-        route_mode = self._resolve_mode(mode, route_id=route_id)
-        points = self.db.load_route_points(route_id)
-        hazards = self.db.load_hazards(route_id)
-        if route_mode == "gps" and not hazards:
-            raise RuntimeError("这条 GPS 路线还没有任何标记点")
-        if route_mode == "counter" and not hazards:
-            raise RuntimeError("这条计数路线还没有任何标记点")
-
-        logger.bind(tag=TAG).info(
-            f"Start run requested, route_id={route_id}, mode={route_mode}, hazards={len(hazards)}, points={len(points)}"
-        )
-        self.run_route_id = route_id
-        self.run_mode = route_mode
-        self.db.set_active_route(route_id)
-        self.db.set_monitoring_route(route_id)
-        self._run_points = points
-        self._run_hazards = hazards
-        self._run_last_idx = None
-        self._run_announced = set()
-        self._run_gps_last_announce_at = {}
-        self._run_counter_announced = set()
-        self._run_last_announce_at = 0.0
-        hz = max(1, int(tick_hz or 10))
-        if route_mode == "counter":
-            hz = max(5, hz)
-        self._run_interval_s = 1.0 / hz
-
-        if route_mode == "counter":
-            self.run_task = asyncio.create_task(
-                self._run_counter_loop(),
-                name=f"NavRunCounter:{route_id}",
-            )
-        else:
-            self.run_task = asyncio.create_task(
-                self._run_gps_loop(),
-                name=f"NavRunGps:{route_id}",
-            )
-        return route_id
-
-    async def stop_run(self) -> Optional[int]:
-        route_id = self.run_route_id
-        if self.run_task and not self.run_task.done():
-            self.run_task.cancel()
-            try:
-                await self.run_task
-            except BaseException:
-                pass
-        self.run_task = None
-        self.run_route_id = None
-        self.run_mode = None
-        self.db.set_monitoring_route(None)
-        self._run_points = []
-        self._run_hazards = []
-        self._run_last_idx = None
-        self._run_announced = set()
-        self._run_gps_last_announce_at = {}
-        self._run_counter_announced = set()
-        return route_id
-
-    async def _run_gps_loop(self) -> None:
-        assert self.run_route_id is not None
-        route_id = self.run_route_id
-        logger.bind(tag=TAG).info(
-            f"GPS run loop started, route_id={route_id}, hazards={len(self._run_hazards)}, radius_m={self.gps_alert_radius_m}, repeat_cooldown_s={self.gps_alert_repeat_cooldown_s}"
-        )
-
-        try:
-            while not self.conn.stop_event.is_set():
-                fix = await self._get_fix()
-                if not fix.ok:
-                    await asyncio.sleep(1.0)
-                    continue
-
-                for hazard in self._run_hazards:
-                    if hazard.lat == 0.0 and hazard.lon == 0.0:
-                        continue
-                    distance = haversine_m(fix.lat, fix.lon, hazard.lat, hazard.lon)
-                    if distance <= self.gps_alert_radius_m:
-                        now = time.monotonic()
-                        last_announce_at = self._run_gps_last_announce_at.get(hazard.id, 0.0)
-                        if now - last_announce_at < self.gps_alert_repeat_cooldown_s:
-                            continue
-                        logger.bind(tag=TAG).info(
-                            f"GPS hazard triggered: route_id={route_id}, hazard_id={hazard.id}, distance_m={distance:.3f}, fix=({fix.lat:.6f},{fix.lon:.6f}), hazard=({hazard.lat:.6f},{hazard.lon:.6f})"
-                        )
-                        await self._announce_gps_hazard(hazard)
-                        self._run_gps_last_announce_at[hazard.id] = now
-
-                await asyncio.sleep(self._run_interval_s)
-        finally:
-            if self.run_route_id == route_id:
-                self.run_route_id = None
-                self.run_mode = None
-                self.run_task = None
-                self._run_points = []
-                self._run_hazards = []
-                self._run_last_idx = None
-                self._run_announced = set()
-                self._run_gps_last_announce_at = {}
-                self._run_counter_announced = set()
-
-    async def _run_counter_loop(self) -> None:
-        assert self.run_route_id is not None
-        route_id = self.run_route_id
-        logger.bind(tag=TAG).info(f"Counter run loop started, route_id={route_id}")
-        try:
-            while not self.conn.stop_event.is_set():
-                sample = await self._get_counter_state()
-                if sample.ok:
-                    self._run_last_announce_at = self._maybe_announce_counter_hazard(
-                        sample,
-                        self._run_hazards,
-                        self._run_counter_announced,
-                        last_announce_at=self._run_last_announce_at,
-                    )
-                await asyncio.sleep(self._run_interval_s)
-        finally:
-            if self.run_route_id == route_id:
-                self.run_route_id = None
-                self.run_mode = None
-                self.run_task = None
-                self._run_points = []
-                self._run_hazards = []
-                self._run_last_idx = None
-                self._run_announced = set()
-                self._run_gps_last_announce_at = {}
-                self._run_counter_announced = set()
-
-    def status_text(self) -> str:
-        parts: List[str] = []
-        if self.record_route_id:
-            if self.record_mode == "counter":
-                parts.append(
-                    f"正在准备录制计数路线 ID={self.record_route_id},已标记风险点={len(self.db.load_hazards(self.record_route_id))}"
-                )
-            else:
-                parts.append(
-                    f"正在记录路线 ID={self.record_route_id},模式={self.record_mode or 'unknown'},点数={self._record_seq}"
-                )
-        if self.run_route_id:
-            if self.run_mode == "counter":
-                parts.append(
-                    f"正在监听预警路线 ID={self.run_route_id},风险点={len(self._run_hazards)}"
-                )
-            else:
-                parts.append(
-                    f"正在预警路线 ID={self.run_route_id},模式={self.run_mode or 'unknown'},风险点={len(self._run_hazards)}"
-                )
-        if not parts:
-            route_id = self.db.get_monitoring_route_id()
-            if route_id:
-                parts.append(
-                    f"数据库显示正在预警路线 ID={route_id},模式={self.db.get_route_mode(route_id)},等待本次连接恢复执行"
-                )
-            route_id = self.db.get_active_route_id()
-            if route_id:
-                parts.append(
-                    f"当前激活路线 ID={route_id},模式={self.db.get_route_mode(route_id)}"
-                )
-            else:
-                parts.append("当前还没有路线,请先开始记录")
-        return ";".join(parts)
-
-
-def get_service(conn) -> NavCopilotService:
-    if not hasattr(conn, "nav_copilot_service") or conn.nav_copilot_service is None:
-        conn.nav_copilot_service = NavCopilotService(conn, NavDB.default())
-    return conn.nav_copilot_service

+ 0 - 396
main/xiaozhi-server/plugins_func/nav_copilot/storage.py

@@ -1,396 +0,0 @@
-from __future__ import annotations
-
-import os
-import sqlite3
-import threading
-import time
-from dataclasses import dataclass
-from pathlib import Path
-from typing import List, Optional
-
-
-@dataclass(frozen=True)
-class RouteInfo:
-    id: int
-    name: str
-    mode: str
-    created_at: int
-    finished_at: Optional[int]
-    points: int
-    hazards: int
-    active: bool
-    monitoring: bool
-
-
-@dataclass(frozen=True)
-class RoutePoint:
-    seq: int
-    t: int
-    lat: float
-    lon: float
-    speed_mps: float
-    cum_dist_m: float
-    counter_value: int = 0
-    counter_absolute: int = 0
-
-
-@dataclass(frozen=True)
-class Hazard:
-    id: int
-    route_id: int
-    seq: int
-    t: int
-    lat: float
-    lon: float
-    cum_dist_m: float
-    type: str
-    note: str
-    counter_value: int = 0
-    counter_absolute: int = 0
-
-
-class NavDB:
-    def __init__(self, db_path: str):
-        self.db_path = db_path
-        os.makedirs(os.path.dirname(db_path), exist_ok=True)
-        self._lock = threading.Lock()
-        self._conn = sqlite3.connect(db_path, check_same_thread=False)
-        self._conn.row_factory = sqlite3.Row
-        self._init_schema()
-
-    @classmethod
-    def default(cls) -> "NavDB":
-        base = Path(__file__).resolve().parents[2]
-        return cls(str(base / "data" / "nav_copilot.sqlite3"))
-
-    def _init_schema(self) -> None:
-        with self._lock:
-            cur = self._conn.cursor()
-            cur.execute(
-                """
-                CREATE TABLE IF NOT EXISTS routes (
-                    id INTEGER PRIMARY KEY AUTOINCREMENT,
-                    name TEXT NOT NULL,
-                    mode TEXT NOT NULL DEFAULT 'gps',
-                    created_at INTEGER NOT NULL,
-                    finished_at INTEGER,
-                    active INTEGER NOT NULL DEFAULT 0,
-                    monitoring INTEGER NOT NULL DEFAULT 0
-                )
-                """
-            )
-            cur.execute(
-                """
-                CREATE TABLE IF NOT EXISTS route_points (
-                    route_id INTEGER NOT NULL,
-                    seq INTEGER NOT NULL,
-                    t INTEGER NOT NULL,
-                    lat REAL NOT NULL,
-                    lon REAL NOT NULL,
-                    speed_mps REAL NOT NULL,
-                    cum_dist_m REAL NOT NULL,
-                    counter_value INTEGER NOT NULL DEFAULT 0,
-                    counter_absolute INTEGER NOT NULL DEFAULT 0,
-                    PRIMARY KEY(route_id, seq)
-                )
-                """
-            )
-            cur.execute(
-                """
-                CREATE TABLE IF NOT EXISTS hazards (
-                    id INTEGER PRIMARY KEY AUTOINCREMENT,
-                    route_id INTEGER NOT NULL,
-                    seq INTEGER NOT NULL,
-                    t INTEGER NOT NULL,
-                    lat REAL NOT NULL,
-                    lon REAL NOT NULL,
-                    cum_dist_m REAL NOT NULL,
-                    type TEXT NOT NULL,
-                    note TEXT NOT NULL DEFAULT '',
-                    counter_value INTEGER NOT NULL DEFAULT 0,
-                    counter_absolute INTEGER NOT NULL DEFAULT 0
-                )
-                """
-            )
-
-            self._ensure_column(cur, "routes", "mode", "TEXT NOT NULL DEFAULT 'gps'")
-            self._ensure_column(
-                cur,
-                "routes",
-                "monitoring",
-                "INTEGER NOT NULL DEFAULT 0",
-            )
-            self._ensure_column(
-                cur,
-                "route_points",
-                "counter_value",
-                "INTEGER NOT NULL DEFAULT 0",
-            )
-            self._ensure_column(
-                cur,
-                "route_points",
-                "counter_absolute",
-                "INTEGER NOT NULL DEFAULT 0",
-            )
-            self._ensure_column(
-                cur,
-                "hazards",
-                "counter_value",
-                "INTEGER NOT NULL DEFAULT 0",
-            )
-            self._ensure_column(
-                cur,
-                "hazards",
-                "counter_absolute",
-                "INTEGER NOT NULL DEFAULT 0",
-            )
-            self._conn.commit()
-
-    def _ensure_column(
-        self,
-        cur: sqlite3.Cursor,
-        table_name: str,
-        column_name: str,
-        column_ddl: str,
-    ) -> None:
-        columns = {
-            str(row["name"])
-            for row in cur.execute(f"PRAGMA table_info({table_name})").fetchall()
-        }
-        if column_name not in columns:
-            cur.execute(
-                f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_ddl}"
-            )
-
-    def create_route(self, name: str, mode: str = "gps") -> int:
-        now = int(time.time())
-        route_mode = (mode or "gps").strip() or "gps"
-        with self._lock:
-            self._conn.execute("UPDATE routes SET active=0, monitoring=0")
-            cur = self._conn.execute(
-                "INSERT INTO routes(name, mode, created_at, active, monitoring) VALUES(?, ?, ?, 1, 0)",
-                (name, route_mode, now),
-            )
-            self._conn.commit()
-            return int(cur.lastrowid)
-
-    def finish_route(self, route_id: int) -> None:
-        now = int(time.time())
-        with self._lock:
-            self._conn.execute(
-                "UPDATE routes SET finished_at=? WHERE id=?",
-                (now, route_id),
-            )
-            self._conn.commit()
-
-    def set_active_route(self, route_id: int) -> None:
-        with self._lock:
-            self._conn.execute("UPDATE routes SET active=0")
-            self._conn.execute("UPDATE routes SET active=1 WHERE id=?", (route_id,))
-            self._conn.commit()
-
-    def get_active_route_id(self) -> Optional[int]:
-        with self._lock:
-            row = self._conn.execute(
-                "SELECT id FROM routes WHERE active=1 ORDER BY id DESC LIMIT 1"
-            ).fetchone()
-            return int(row["id"]) if row else None
-
-    def set_monitoring_route(self, route_id: Optional[int]) -> None:
-        with self._lock:
-            self._conn.execute("UPDATE routes SET monitoring=0")
-            if route_id is not None:
-                self._conn.execute(
-                    "UPDATE routes SET monitoring=1 WHERE id=?",
-                    (int(route_id),),
-                )
-            self._conn.commit()
-
-    def get_monitoring_route_id(self) -> Optional[int]:
-        with self._lock:
-            row = self._conn.execute(
-                "SELECT id FROM routes WHERE monitoring=1 ORDER BY id DESC LIMIT 1"
-            ).fetchone()
-            return int(row["id"]) if row else None
-
-    # Backward-compatible aliases for older call sites that used sanitized names.
-    def setmonitoringroute(self, route_id: Optional[int]) -> None:
-        self.set_monitoring_route(route_id)
-
-    def getmonitoringrouteid(self) -> Optional[int]:
-        return self.get_monitoring_route_id()
-
-    def get_route_mode(self, route_id: int) -> str:
-        with self._lock:
-            row = self._conn.execute(
-                "SELECT mode FROM routes WHERE id=?",
-                (route_id,),
-            ).fetchone()
-        if not row:
-            return "gps"
-        return str(row["mode"] or "gps")
-
-    def update_route_mode(self, route_id: int, mode: str) -> None:
-        route_mode = (mode or "gps").strip() or "gps"
-        with self._lock:
-            self._conn.execute(
-                "UPDATE routes SET mode=? WHERE id=?",
-                (route_mode, route_id),
-            )
-            self._conn.commit()
-
-    def add_point(
-        self,
-        *,
-        route_id: int,
-        seq: int,
-        t: int,
-        lat: float,
-        lon: float,
-        speed_mps: float,
-        cum_dist_m: float,
-        counter_value: int = 0,
-        counter_absolute: int = 0,
-    ) -> None:
-        with self._lock:
-            self._conn.execute(
-                """
-                INSERT OR REPLACE INTO route_points(
-                    route_id, seq, t, lat, lon, speed_mps, cum_dist_m, counter_value, counter_absolute
-                )
-                VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
-                """,
-                (
-                    route_id,
-                    seq,
-                    t,
-                    lat,
-                    lon,
-                    speed_mps,
-                    cum_dist_m,
-                    int(counter_value or 0),
-                    int(counter_absolute or 0),
-                ),
-            )
-            self._conn.commit()
-
-    def add_hazard(
-        self,
-        *,
-        route_id: int,
-        seq: int,
-        t: int,
-        lat: float,
-        lon: float,
-        cum_dist_m: float,
-        hazard_type: str,
-        note: str = "",
-        counter_value: int = 0,
-        counter_absolute: int = 0,
-    ) -> int:
-        with self._lock:
-            cur = self._conn.execute(
-                """
-                INSERT INTO hazards(
-                    route_id, seq, t, lat, lon, cum_dist_m, type, note, counter_value, counter_absolute
-                )
-                VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
-                """,
-                (
-                    route_id,
-                    seq,
-                    t,
-                    lat,
-                    lon,
-                    cum_dist_m,
-                    hazard_type,
-                    note or "",
-                    int(counter_value or 0),
-                    int(counter_absolute or 0),
-                ),
-            )
-            self._conn.commit()
-            return int(cur.lastrowid)
-
-    def list_routes(self, limit: int = 20) -> List[RouteInfo]:
-        with self._lock:
-            rows = self._conn.execute(
-                """
-                SELECT
-                    r.id, r.name, r.mode, r.created_at, r.finished_at, r.active, r.monitoring,
-                    (SELECT COUNT(1) FROM route_points p WHERE p.route_id=r.id) AS points,
-                    (SELECT COUNT(1) FROM hazards h WHERE h.route_id=r.id) AS hazards
-                FROM routes r
-                ORDER BY r.id DESC
-                LIMIT ?
-                """,
-                (int(limit),),
-            ).fetchall()
-        return [
-            RouteInfo(
-                id=int(row["id"]),
-                name=str(row["name"]),
-                mode=str(row["mode"] or "gps"),
-                created_at=int(row["created_at"]),
-                finished_at=int(row["finished_at"]) if row["finished_at"] else None,
-                points=int(row["points"]),
-                hazards=int(row["hazards"]),
-                active=bool(int(row["active"])),
-                monitoring=bool(int(row["monitoring"] or 0)),
-            )
-            for row in rows
-        ]
-
-    def load_route_points(self, route_id: int) -> List[RoutePoint]:
-        with self._lock:
-            rows = self._conn.execute(
-                """
-                SELECT seq, t, lat, lon, speed_mps, cum_dist_m, counter_value, counter_absolute
-                FROM route_points
-                WHERE route_id=?
-                ORDER BY seq ASC
-                """,
-                (route_id,),
-            ).fetchall()
-        return [
-            RoutePoint(
-                seq=int(row["seq"]),
-                t=int(row["t"]),
-                lat=float(row["lat"]),
-                lon=float(row["lon"]),
-                speed_mps=float(row["speed_mps"]),
-                cum_dist_m=float(row["cum_dist_m"]),
-                counter_value=int(row["counter_value"] or 0),
-                counter_absolute=int(row["counter_absolute"] or 0),
-            )
-            for row in rows
-        ]
-
-    def load_hazards(self, route_id: int) -> List[Hazard]:
-        with self._lock:
-            rows = self._conn.execute(
-                """
-                SELECT
-                    id, route_id, seq, t, lat, lon, cum_dist_m, type, note, counter_value, counter_absolute
-                FROM hazards
-                WHERE route_id=?
-                ORDER BY cum_dist_m ASC, id ASC
-                """,
-                (route_id,),
-            ).fetchall()
-        return [
-            Hazard(
-                id=int(row["id"]),
-                route_id=int(row["route_id"]),
-                seq=int(row["seq"]),
-                t=int(row["t"]),
-                lat=float(row["lat"]),
-                lon=float(row["lon"]),
-                cum_dist_m=float(row["cum_dist_m"]),
-                type=str(row["type"]),
-                note=str(row["note"] or ""),
-                counter_value=int(row["counter_value"] or 0),
-                counter_absolute=int(row["counter_absolute"] or 0),
-            )
-            for row in rows
-        ]

+ 0 - 90
main/xiaozhi-server/test/js/config/default-mcp-tools.json

@@ -1,90 +0,0 @@
-[
-    {
-        "name": "self_camera_take_photo",
-        "description": "Take a photo using the device's camera. This tool captures the current camera frame and returns the image data. You MUST call this tool when user asks to 'take a photo', 'what do you see', 'describe what you see', or similar requests. After getting the photo data, describe what you see in the image. Parameter 'question' is optional, defaults to 'describe objects seen'.",
-        "inputSchema": {
-            "type": "object",
-            "properties": {
-                "question": {
-                    "type": "string",
-                    "description": "Question to guide the photo analysis, e.g., 'describe the objects seen'. Can be empty."
-                }
-            }
-        },
-        "mockResponse": {
-            "success": true,
-            "photo_data": "base64_image_data",
-            "response": "Please describe what you see based on the returned photo_data"
-        }
-    },
-    {
-        "name": "self.get_device_status",
-        "description": "Provides the real-time information of the device, including the current status of the audio speaker, screen, battery, network, etc.\nUse this tool for: \n1. Answering questions about current condition (e.g. what is the current volume of the audio speaker?)\n2. As the first step to control the device (e.g. turn up / down the volume of the audio speaker, etc.)",
-        "inputSchema": {
-            "type": "object",
-            "properties": {}
-        },
-        "mockResponse": {
-            "audio_speaker": {
-                "volume": 50,
-                "muted": false
-            },
-            "screen": {
-                "brightness": 80,
-                "theme": "light"
-            },
-            "battery": {
-                "level": 85,
-                "charging": false
-            },
-            "network": {
-                "connected": true,
-                "type": "wifi"
-            }
-        }
-    },
-    {
-        "name": "self.audio_speaker.set_volume",
-        "description": "Set the volume of the audio speaker. If the current volume is unknown, you must call `self.get_device_status` tool first and then call this tool.",
-        "inputSchema": {
-            "type": "object",
-            "properties": {
-                "volume": {
-                    "type": "integer",
-                    "minimum": 0,
-                    "maximum": 100
-                }
-            },
-            "required": [
-                "volume"
-            ]
-        },
-        "mockResponse": {
-            "success": true,
-            "volume": "${volume}",
-            "message": "音量已设置为 ${volume}"
-        }
-    },
-    {
-        "name": "self.screen.set_brightness",
-        "description": "Set the brightness of the screen.",
-        "inputSchema": {
-            "type": "object",
-            "properties": {
-                "brightness": {
-                    "type": "integer",
-                    "minimum": 0,
-                    "maximum": 100
-                }
-            },
-            "required": [
-                "brightness"
-            ]
-        },
-        "mockResponse": {
-            "success": true,
-            "brightness": "${brightness}",
-            "message": "亮度已设置为 ${brightness}"
-        }
-    }
-]

+ 0 - 533
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.cdi3.json

@@ -1,533 +0,0 @@
-{
-	"Version": 3,
-	"Parameters": [
-		{
-			"Id": "ParamAngleX",
-			"GroupId": "ParamGroupFace",
-			"Name": "角度 X"
-		},
-		{
-			"Id": "ParamAngleY",
-			"GroupId": "ParamGroupFace",
-			"Name": "角度 Y"
-		},
-		{
-			"Id": "ParamAngleZ",
-			"GroupId": "ParamGroupFace",
-			"Name": "角度 Z"
-		},
-		{
-			"Id": "ParamCheek",
-			"GroupId": "ParamGroupFace",
-			"Name": "脸颊泛红"
-		},
-		{
-			"Id": "ParamEyeLOpen",
-			"GroupId": "ParamGroupEyes",
-			"Name": "左眼 开闭"
-		},
-		{
-			"Id": "ParamEyeLSmile",
-			"GroupId": "ParamGroupEyes",
-			"Name": "左眼 微笑"
-		},
-		{
-			"Id": "ParamEyeROpen",
-			"GroupId": "ParamGroupEyes",
-			"Name": "右眼 开闭"
-		},
-		{
-			"Id": "ParamEyeRSmile",
-			"GroupId": "ParamGroupEyes",
-			"Name": "右眼 微笑"
-		},
-		{
-			"Id": "ParamEyeBallX",
-			"GroupId": "ParamGroupEyeballs",
-			"Name": "眼珠 X"
-		},
-		{
-			"Id": "ParamEyeBallY",
-			"GroupId": "ParamGroupEyeballs",
-			"Name": "眼珠 Y"
-		},
-		{
-			"Id": "ParamBrowLY",
-			"GroupId": "ParamGroupBrows",
-			"Name": "左眉 上下"
-		},
-		{
-			"Id": "ParamBrowRY",
-			"GroupId": "ParamGroupBrows",
-			"Name": "右眉 上下"
-		},
-		{
-			"Id": "ParamBrowLX",
-			"GroupId": "ParamGroupBrows",
-			"Name": "左眉 左右"
-		},
-		{
-			"Id": "ParamBrowRX",
-			"GroupId": "ParamGroupBrows",
-			"Name": "右眉 左右"
-		},
-		{
-			"Id": "ParamBrowLAngle",
-			"GroupId": "ParamGroupBrows",
-			"Name": "左眉 角度"
-		},
-		{
-			"Id": "ParamBrowRAngle",
-			"GroupId": "ParamGroupBrows",
-			"Name": "右眉 角度"
-		},
-		{
-			"Id": "ParamBrowLForm",
-			"GroupId": "ParamGroupBrows",
-			"Name": "左眉 变形"
-		},
-		{
-			"Id": "ParamBrowRForm",
-			"GroupId": "ParamGroupBrows",
-			"Name": "右眉 变形"
-		},
-		{
-			"Id": "ParamMouthForm",
-			"GroupId": "ParamGroupMouth",
-			"Name": "嘴 变形"
-		},
-		{
-			"Id": "ParamMouthOpenY",
-			"GroupId": "ParamGroupMouth",
-			"Name": "嘴 开闭"
-		},
-		{
-			"Id": "ParamBodyAngleX",
-			"GroupId": "ParamGroupBody",
-			"Name": "身体的旋转 X"
-		},
-		{
-			"Id": "ParamBodyAngleY",
-			"GroupId": "ParamGroupBody",
-			"Name": "身体的旋转 Y"
-		},
-		{
-			"Id": "ParamBodyAngleZ",
-			"GroupId": "ParamGroupBody",
-			"Name": "身体的旋转 Z"
-		},
-		{
-			"Id": "ParamBreath",
-			"GroupId": "ParamGroupBody",
-			"Name": "呼吸"
-		},
-		{
-			"Id": "ParamShoulder",
-			"GroupId": "ParamGroupBody",
-			"Name": "肩"
-		},
-		{
-			"Id": "ParamLeg",
-			"GroupId": "ParamGroupBody",
-			"Name": "腿"
-		},
-		{
-			"Id": "ParamArmLA",
-			"GroupId": "ParamGroupArms",
-			"Name": "左臂 A"
-		},
-		{
-			"Id": "ParamArmRA",
-			"GroupId": "ParamGroupArms",
-			"Name": "右臂 A"
-		},
-		{
-			"Id": "ParamArmLB",
-			"GroupId": "ParamGroupArms",
-			"Name": "左臂 B"
-		},
-		{
-			"Id": "ParamArmRB",
-			"GroupId": "ParamGroupArms",
-			"Name": "右臂 B"
-		},
-		{
-			"Id": "ParamHandLB",
-			"GroupId": "ParamGroupArms",
-			"Name": "左手B 旋转"
-		},
-		{
-			"Id": "ParamHandRB",
-			"GroupId": "ParamGroupArms",
-			"Name": "右手B 旋转"
-		},
-		{
-			"Id": "ParamHandL",
-			"GroupId": "ParamGroupArms",
-			"Name": "左手"
-		},
-		{
-			"Id": "ParamHandR",
-			"GroupId": "ParamGroupArms",
-			"Name": "右手"
-		},
-		{
-			"Id": "ParamBustY",
-			"GroupId": "ParamGroupSway",
-			"Name": "胸部 摇动"
-		},
-		{
-			"Id": "ParamHairAhoge",
-			"GroupId": "ParamGroupSway",
-			"Name": "头发摇动 呆毛"
-		},
-		{
-			"Id": "ParamHairFront",
-			"GroupId": "ParamGroupSway",
-			"Name": "头发摇动 前"
-		},
-		{
-			"Id": "ParamHairBack",
-			"GroupId": "ParamGroupSway",
-			"Name": "头发摇动 后"
-		},
-		{
-			"Id": "ParamSideupRibbon",
-			"GroupId": "ParamGroupSway",
-			"Name": "发饰的摇动"
-		},
-		{
-			"Id": "ParamRibbon",
-			"GroupId": "ParamGroupSway",
-			"Name": "蝴蝶结的摇动"
-		},
-		{
-			"Id": "ParamSkirt",
-			"GroupId": "ParamGroupSway",
-			"Name": "短裙的摇动"
-		},
-		{
-			"Id": "ParamSkirt2",
-			"GroupId": "ParamGroupSway",
-			"Name": "短裙的上卷"
-		},
-		{
-			"Id": "Param_Angle_Rotation_1_ArtMesh62",
-			"GroupId": "ParamGroup2",
-			"Name": "[0]辫子左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_2_ArtMesh62",
-			"GroupId": "ParamGroup2",
-			"Name": "[1]辫子左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_3_ArtMesh62",
-			"GroupId": "ParamGroup2",
-			"Name": "[2]辫子左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_4_ArtMesh62",
-			"GroupId": "ParamGroup2",
-			"Name": "[3]辫子左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_5_ArtMesh62",
-			"GroupId": "ParamGroup2",
-			"Name": "[4]辫子左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_6_ArtMesh62",
-			"GroupId": "ParamGroup2",
-			"Name": "[5]辫子左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_7_ArtMesh62",
-			"GroupId": "ParamGroup2",
-			"Name": "[6]辫子左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_1_ArtMesh61",
-			"GroupId": "ParamGroup",
-			"Name": "[0]辫子右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_2_ArtMesh61",
-			"GroupId": "ParamGroup",
-			"Name": "[1]辫子右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_3_ArtMesh61",
-			"GroupId": "ParamGroup",
-			"Name": "[2]辫子右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_4_ArtMesh61",
-			"GroupId": "ParamGroup",
-			"Name": "[3]辫子右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_5_ArtMesh61",
-			"GroupId": "ParamGroup",
-			"Name": "[4]辫子右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_6_ArtMesh61",
-			"GroupId": "ParamGroup",
-			"Name": "[5]辫子右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_7_ArtMesh61",
-			"GroupId": "ParamGroup",
-			"Name": "[6]辫子右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_1_ArtMesh55",
-			"GroupId": "ParamGroup4",
-			"Name": "[0]侧发左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_2_ArtMesh55",
-			"GroupId": "ParamGroup4",
-			"Name": "[1]侧发左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_3_ArtMesh55",
-			"GroupId": "ParamGroup4",
-			"Name": "[2]侧发左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_4_ArtMesh55",
-			"GroupId": "ParamGroup4",
-			"Name": "[3]侧发左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_5_ArtMesh55",
-			"GroupId": "ParamGroup4",
-			"Name": "[4]侧发左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_6_ArtMesh55",
-			"GroupId": "ParamGroup4",
-			"Name": "[5]侧发左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_7_ArtMesh55",
-			"GroupId": "ParamGroup4",
-			"Name": "[6]侧发左"
-		},
-		{
-			"Id": "Param_Angle_Rotation_1_ArtMesh54",
-			"GroupId": "ParamGroup3",
-			"Name": "[0]侧发右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_2_ArtMesh54",
-			"GroupId": "ParamGroup3",
-			"Name": "[1]侧发右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_3_ArtMesh54",
-			"GroupId": "ParamGroup3",
-			"Name": "[2]侧发右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_4_ArtMesh54",
-			"GroupId": "ParamGroup3",
-			"Name": "[3]侧发右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_5_ArtMesh54",
-			"GroupId": "ParamGroup3",
-			"Name": "[4]侧发右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_6_ArtMesh54",
-			"GroupId": "ParamGroup3",
-			"Name": "[5]侧发右"
-		},
-		{
-			"Id": "Param_Angle_Rotation_7_ArtMesh54",
-			"GroupId": "ParamGroup3",
-			"Name": "[6]侧发右"
-		}
-	],
-	"ParameterGroups": [
-		{
-			"Id": "ParamGroupFace",
-			"GroupId": "",
-			"Name": "脸"
-		},
-		{
-			"Id": "ParamGroupEyes",
-			"GroupId": "",
-			"Name": "眼睛"
-		},
-		{
-			"Id": "ParamGroupEyeballs",
-			"GroupId": "",
-			"Name": "眼珠"
-		},
-		{
-			"Id": "ParamGroupBrows",
-			"GroupId": "",
-			"Name": "眉毛"
-		},
-		{
-			"Id": "ParamGroupMouth",
-			"GroupId": "",
-			"Name": "嘴"
-		},
-		{
-			"Id": "ParamGroupBody",
-			"GroupId": "",
-			"Name": "身体"
-		},
-		{
-			"Id": "ParamGroupArms",
-			"GroupId": "",
-			"Name": "手臂"
-		},
-		{
-			"Id": "ParamGroupSway",
-			"GroupId": "",
-			"Name": "摇动"
-		},
-		{
-			"Id": "ParamGroup2",
-			"GroupId": "",
-			"Name": "摇动 辫子左"
-		},
-		{
-			"Id": "ParamGroup",
-			"GroupId": "",
-			"Name": "摇动 辫子右"
-		},
-		{
-			"Id": "ParamGroup4",
-			"GroupId": "",
-			"Name": "摇动 侧发左"
-		},
-		{
-			"Id": "ParamGroup3",
-			"GroupId": "",
-			"Name": "摇动 侧发右"
-		}
-	],
-	"Parts": [
-		{
-			"Id": "PartCore",
-			"Name": "Core"
-		},
-		{
-			"Id": "PartCheek",
-			"Name": "脸颊"
-		},
-		{
-			"Id": "PartBrow",
-			"Name": "眉毛"
-		},
-		{
-			"Id": "PartEye",
-			"Name": "眼睛"
-		},
-		{
-			"Id": "PartNose",
-			"Name": "鼻子"
-		},
-		{
-			"Id": "PartMouth",
-			"Name": "嘴"
-		},
-		{
-			"Id": "PartFace",
-			"Name": "脸"
-		},
-		{
-			"Id": "PartEar",
-			"Name": "耳朵"
-		},
-		{
-			"Id": "PartHairSide",
-			"Name": "侧发"
-		},
-		{
-			"Id": "PartHairFront",
-			"Name": "前发"
-		},
-		{
-			"Id": "PartHairBack",
-			"Name": "后发"
-		},
-		{
-			"Id": "PartNeck",
-			"Name": "脖子"
-		},
-		{
-			"Id": "PartArmA",
-			"Name": "手臂A"
-		},
-		{
-			"Id": "PartArmB",
-			"Name": "手臂B"
-		},
-		{
-			"Id": "PartBody",
-			"Name": "身体"
-		},
-		{
-			"Id": "PartBackground",
-			"Name": "背景"
-		},
-		{
-			"Id": "PartSketch",
-			"Name": "[参考图]"
-		},
-		{
-			"Id": "PartEyeBall",
-			"Name": "眼珠"
-		},
-		{
-			"Id": "ArtMesh55_Skinning",
-			"Name": "侧发右"
-		},
-		{
-			"Id": "Part4",
-			"Name": "侧发左(旋转)"
-		},
-		{
-			"Id": "ArtMesh54_Skinning",
-			"Name": "侧发右(蒙皮)"
-		},
-		{
-			"Id": "Part3",
-			"Name": "侧发右(旋转)"
-		},
-		{
-			"Id": "ArtMesh61_Skinning",
-			"Name": "辫子右(蒙皮)"
-		},
-		{
-			"Id": "Part",
-			"Name": "辫子右(旋转)"
-		},
-		{
-			"Id": "ArtMesh62_Skinning",
-			"Name": "辫子左(蒙皮)"
-		},
-		{
-			"Id": "Part2",
-			"Name": "辫子左(旋转)"
-		}
-	],
-	"CombinedParameters": [
-		[
-			"ParamAngleX",
-			"ParamAngleY"
-		],
-		[
-			"ParamMouthForm",
-			"ParamMouthOpenY"
-		]
-	]
-}

+ 0 - 82
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json

@@ -1,82 +0,0 @@
-{
-	"Version": 3,
-	"FileReferences": {
-		"Moc": "hiyori_pro_t11.moc3",
-		"Textures": [
-			"hiyori_pro_t11.2048/texture_00.png",
-			"hiyori_pro_t11.2048/texture_01.png"
-		],
-		"Physics": "hiyori_pro_t11.physics3.json",
-		"Pose": "hiyori_pro_t11.pose3.json",
-		"DisplayInfo": "hiyori_pro_t11.cdi3.json",
-		"Motions": {
-			"Idle": [
-				{
-					"File": "motion/hiyori_m01.motion3.json"
-				},
-				{
-					"File": "motion/hiyori_m02.motion3.json"
-				},
-				{
-					"File": "motion/hiyori_m05.motion3.json"
-				}
-			],
-			"Flick": [
-				{
-					"File": "motion/hiyori_m03.motion3.json"
-				}
-			],
-			"FlickDown": [
-				{
-					"File": "motion/hiyori_m04.motion3.json"
-				}
-			],
-			"FlickUp": [
-				{
-					"File": "motion/hiyori_m06.motion3.json"
-				}
-			],
-			"Tap": [
-				{
-					"File": "motion/hiyori_m07.motion3.json"
-				},
-				{
-					"File": "motion/hiyori_m08.motion3.json"
-				}
-			],
-			"Tap@Body": [
-				{
-					"File": "motion/hiyori_m09.motion3.json"
-				}
-			],
-			"Flick@Body": [
-				{
-					"File": "motion/hiyori_m10.motion3.json"
-				}
-			]
-		}
-	},
-	"Groups": [
-		{
-			"Target": "Parameter",
-			"Name": "LipSync",
-			"Ids": [
-				"ParamMouthOpenY"
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Name": "EyeBlink",
-			"Ids": [
-				"ParamEyeLOpen",
-				"ParamEyeROpen"
-			]
-		}
-	],
-	"HitAreas": [
-		{
-			"Id": "HitArea",
-			"Name": "Body"
-		}
-	]
-}

Разница между файлами не показана из-за своего большого размера
+ 0 - 1569
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.physics3.json


+ 0 - 15
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/hiyori_pro_t11.pose3.json

@@ -1,15 +0,0 @@
-{
-	"Type": "Live2D Pose",
-	"Groups": [
-		[
-			{
-				"Id": "PartArmA",
-				"Link": []
-			},
-			{
-				"Id": "PartArmB",
-				"Link": []
-			}
-		]
-	]
-}

Разница между файлами не показана из-за своего большого размера
+ 0 - 1063
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m01.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 1428
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m02.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 1295
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m03.motion3.json


+ 0 - 860
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m04.motion3.json

@@ -1,860 +0,0 @@
-{
-	"Version": 3,
-	"Meta": {
-		"Duration": 4.43,
-		"Fps": 30.0,
-		"Loop": true,
-		"AreBeziersRestricted": false,
-		"CurveCount": 29,
-		"TotalSegmentCount": 106,
-		"TotalPointCount": 287,
-		"UserDataCount": 0,
-		"TotalUserDataSize": 0
-	},
-	"Curves": [
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleX",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.211,
-				1,
-				0.422,
-				0,
-				0.633,
-				0,
-				1,
-				0.911,
-				0,
-				1.189,
-				5,
-				1.467,
-				5,
-				1,
-				1.689,
-				5,
-				1.911,
-				-16,
-				2.133,
-				-16,
-				1,
-				2.356,
-				-16,
-				2.578,
-				13.871,
-				2.8,
-				13.871,
-				1,
-				2.956,
-				13.871,
-				3.111,
-				0,
-				3.267,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.211,
-				0,
-				0.422,
-				0,
-				0.633,
-				0,
-				1,
-				0.911,
-				0,
-				1.189,
-				-25,
-				1.467,
-				-25,
-				1,
-				1.689,
-				-25,
-				1.911,
-				-15.225,
-				2.133,
-				-11,
-				1,
-				2.356,
-				-6.775,
-				2.578,
-				-5.127,
-				2.8,
-				-2.5,
-				1,
-				2.956,
-				-0.661,
-				3.111,
-				0,
-				3.267,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleZ",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.222,
-				0,
-				0.444,
-				0,
-				0.667,
-				0,
-				1,
-				0.756,
-				0,
-				0.844,
-				-4,
-				0.933,
-				-4,
-				1,
-				1.122,
-				-4,
-				1.311,
-				18,
-				1.5,
-				18,
-				1,
-				1.722,
-				18,
-				1.944,
-				-14,
-				2.167,
-				-14,
-				1,
-				2.567,
-				-14,
-				2.967,
-				-14,
-				3.367,
-				-14,
-				1,
-				3.511,
-				-14,
-				3.656,
-				-12,
-				3.8,
-				-12,
-				0,
-				4.433,
-				-12
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamCheek",
-			"Segments": [
-				0,
-				1,
-				0,
-				4.433,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeLOpen",
-			"Segments": [
-				0,
-				1.2,
-				1,
-				0.389,
-				1.2,
-				0.778,
-				1.148,
-				1.167,
-				1,
-				1,
-				1.211,
-				0.983,
-				1.256,
-				0,
-				1.3,
-				0,
-				1,
-				1.322,
-				0,
-				1.344,
-				0,
-				1.367,
-				0,
-				1,
-				1.422,
-				0,
-				1.478,
-				1,
-				1.533,
-				1,
-				1,
-				1.944,
-				1,
-				2.356,
-				1,
-				2.767,
-				1,
-				1,
-				2.811,
-				1,
-				2.856,
-				0,
-				2.9,
-				0,
-				1,
-				2.922,
-				0,
-				2.944,
-				0,
-				2.967,
-				0,
-				1,
-				3.022,
-				0,
-				3.078,
-				1,
-				3.133,
-				1,
-				0,
-				4.433,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeLSmile",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeROpen",
-			"Segments": [
-				0,
-				1.2,
-				1,
-				0.389,
-				1.2,
-				0.778,
-				1.148,
-				1.167,
-				1,
-				1,
-				1.211,
-				0.983,
-				1.256,
-				0,
-				1.3,
-				0,
-				1,
-				1.322,
-				0,
-				1.344,
-				0,
-				1.367,
-				0,
-				1,
-				1.422,
-				0,
-				1.478,
-				1,
-				1.533,
-				1,
-				1,
-				1.944,
-				1,
-				2.356,
-				1,
-				2.767,
-				1,
-				1,
-				2.811,
-				1,
-				2.856,
-				0,
-				2.9,
-				0,
-				1,
-				2.922,
-				0,
-				2.944,
-				0,
-				2.967,
-				0,
-				1,
-				3.022,
-				0,
-				3.078,
-				1,
-				3.133,
-				1,
-				0,
-				4.433,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeRSmile",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeBallX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.211,
-				0,
-				0.422,
-				0,
-				0.633,
-				0,
-				1,
-				0.911,
-				0,
-				1.189,
-				-0.44,
-				1.467,
-				-0.44,
-				1,
-				1.689,
-				-0.44,
-				1.911,
-				0.79,
-				2.133,
-				0.79,
-				1,
-				2.511,
-				0.79,
-				2.889,
-				0,
-				3.267,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeBallY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.211,
-				0,
-				0.422,
-				0,
-				0.633,
-				0,
-				1,
-				0.911,
-				0,
-				1.189,
-				-1,
-				1.467,
-				-1,
-				1,
-				1.689,
-				-1,
-				1.911,
-				-1,
-				2.133,
-				-1,
-				1,
-				2.511,
-				-1,
-				2.889,
-				0,
-				3.267,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLY",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.544,
-				1,
-				1.089,
-				1,
-				1.633,
-				1,
-				1,
-				1.856,
-				1,
-				2.078,
-				0,
-				2.3,
-				0,
-				1,
-				2.5,
-				0,
-				2.7,
-				1,
-				2.9,
-				1,
-				0,
-				4.433,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRY",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.544,
-				1,
-				1.089,
-				1,
-				1.633,
-				1,
-				1,
-				1.856,
-				1,
-				2.078,
-				0,
-				2.3,
-				0,
-				1,
-				2.5,
-				0,
-				2.7,
-				1,
-				2.9,
-				1,
-				0,
-				4.433,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLX",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRX",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLAngle",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRAngle",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLForm",
-			"Segments": [
-				0,
-				-1,
-				0,
-				4.433,
-				-1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRForm",
-			"Segments": [
-				0,
-				-1,
-				0,
-				4.433,
-				-1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.244,
-				0,
-				0.489,
-				0,
-				0.733,
-				0,
-				1,
-				0.933,
-				0,
-				1.133,
-				-7,
-				1.333,
-				-7,
-				1,
-				1.644,
-				-7,
-				1.956,
-				0,
-				2.267,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.244,
-				0,
-				0.489,
-				0,
-				0.733,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleZ",
-			"Segments": [
-				0,
-				2,
-				1,
-				0.233,
-				2,
-				0.467,
-				0,
-				0.7,
-				0,
-				1,
-				0.733,
-				0,
-				0.767,
-				0,
-				0.8,
-				0,
-				1,
-				1,
-				0,
-				1.2,
-				-4,
-				1.4,
-				-4,
-				1,
-				1.711,
-				-4,
-				2.022,
-				5,
-				2.333,
-				5,
-				1,
-				2.567,
-				5,
-				2.8,
-				3.64,
-				3.033,
-				0,
-				1,
-				3.133,
-				-1.56,
-				3.233,
-				-3,
-				3.333,
-				-3,
-				1,
-				3.467,
-				-3,
-				3.6,
-				-2,
-				3.733,
-				-2,
-				0,
-				4.433,
-				-2
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBreath",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.189,
-				0,
-				0.378,
-				1,
-				0.567,
-				1,
-				1,
-				0.711,
-				1,
-				0.856,
-				0,
-				1,
-				0,
-				1,
-				1.222,
-				0,
-				1.444,
-				1,
-				1.667,
-				1,
-				1,
-				1.889,
-				1,
-				2.111,
-				0,
-				2.333,
-				0,
-				1,
-				2.544,
-				0,
-				2.756,
-				1,
-				2.967,
-				1,
-				1,
-				3.167,
-				1,
-				3.367,
-				0,
-				3.567,
-				0,
-				0,
-				4.433,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamShoulder",
-			"Segments": [
-				0,
-				0.1,
-				1,
-				0.467,
-				0.1,
-				0.933,
-				1,
-				1.4,
-				1,
-				1,
-				1.844,
-				1,
-				2.289,
-				1,
-				2.733,
-				1,
-				1,
-				2.967,
-				1,
-				3.2,
-				-1,
-				3.433,
-				-1,
-				0,
-				4.433,
-				-1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamLeg",
-			"Segments": [
-				0,
-				1,
-				0,
-				4.433,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmLA",
-			"Segments": [
-				0,
-				-10,
-				0,
-				4.433,
-				-10
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmRA",
-			"Segments": [
-				0,
-				-10,
-				0,
-				4.433,
-				-10
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamHairAhoge",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.3,
-				0,
-				0.6,
-				0,
-				0.9,
-				-0.012,
-				1,
-				1.067,
-				-0.019,
-				1.233,
-				-6.827,
-				1.4,
-				-6.827,
-				1,
-				1.511,
-				-6.827,
-				1.622,
-				7.958,
-				1.733,
-				7.958,
-				1,
-				1.944,
-				7.958,
-				2.156,
-				-7.565,
-				2.367,
-				-7.565,
-				1,
-				2.5,
-				-7.565,
-				2.633,
-				9.434,
-				2.767,
-				9.434,
-				1,
-				2.978,
-				9.434,
-				3.189,
-				-8.871,
-				3.4,
-				-8.871,
-				1,
-				3.5,
-				-8.871,
-				3.6,
-				7.588,
-				3.7,
-				7.588,
-				1,
-				3.789,
-				7.588,
-				3.878,
-				-3.904,
-				3.967,
-				-3.904,
-				1,
-				4.011,
-				-3.904,
-				4.056,
-				-0.032,
-				4.1,
-				-0.032,
-				0,
-				4.433,
-				-0.032
-			]
-		},
-		{
-			"Target": "PartOpacity",
-			"Id": "PartArmA",
-			"Segments": [
-				0,
-				1,
-				0,
-				4.43,
-				1
-			]
-		},
-		{
-			"Target": "PartOpacity",
-			"Id": "PartArmB",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.43,
-				0
-			]
-		}
-	]
-}

Разница между файлами не показана из-за своего большого размера
+ 0 - 1543
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m05.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 1947
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m06.motion3.json


+ 0 - 927
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m07.motion3.json

@@ -1,927 +0,0 @@
-{
-	"Version": 3,
-	"Meta": {
-		"Duration": 1.9,
-		"Fps": 30.0,
-		"Loop": true,
-		"AreBeziersRestricted": false,
-		"CurveCount": 30,
-		"TotalSegmentCount": 121,
-		"TotalPointCount": 331,
-		"UserDataCount": 0,
-		"TotalUserDataSize": 0
-	},
-	"Curves": [
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleZ",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.444,
-				0,
-				0.556,
-				8,
-				0.667,
-				8,
-				0,
-				1.9,
-				8
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamCheek",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeLOpen",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.111,
-				1,
-				0.222,
-				1,
-				0.333,
-				1,
-				1,
-				0.378,
-				1,
-				0.422,
-				0,
-				0.467,
-				0,
-				1,
-				0.522,
-				0,
-				0.578,
-				1.2,
-				0.633,
-				1.2,
-				1,
-				0.744,
-				1.2,
-				0.856,
-				1.2,
-				0.967,
-				1.2,
-				1,
-				0.989,
-				1.2,
-				1.011,
-				0,
-				1.033,
-				0,
-				1,
-				1.067,
-				0,
-				1.1,
-				1.2,
-				1.133,
-				1.2,
-				1,
-				1.167,
-				1.2,
-				1.2,
-				1.2,
-				1.233,
-				1.2,
-				1,
-				1.267,
-				1.2,
-				1.3,
-				0,
-				1.333,
-				0,
-				1,
-				1.356,
-				0,
-				1.378,
-				1.2,
-				1.4,
-				1.2,
-				0,
-				1.9,
-				1.2
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeLSmile",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeROpen",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.111,
-				1,
-				0.222,
-				1,
-				0.333,
-				1,
-				1,
-				0.378,
-				1,
-				0.422,
-				0,
-				0.467,
-				0,
-				1,
-				0.522,
-				0,
-				0.578,
-				1.2,
-				0.633,
-				1.2,
-				1,
-				0.744,
-				1.2,
-				0.856,
-				1.2,
-				0.967,
-				1.2,
-				1,
-				0.989,
-				1.2,
-				1.011,
-				0,
-				1.033,
-				0,
-				1,
-				1.067,
-				0,
-				1.1,
-				1.2,
-				1.133,
-				1.2,
-				1,
-				1.167,
-				1.2,
-				1.2,
-				1.2,
-				1.233,
-				1.2,
-				1,
-				1.267,
-				1.2,
-				1.3,
-				0,
-				1.333,
-				0,
-				1,
-				1.356,
-				0,
-				1.378,
-				1.2,
-				1.4,
-				1.2,
-				0,
-				1.9,
-				1.2
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeRSmile",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0,
-				0.667,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeBallX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0,
-				0.667,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeBallY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0,
-				0.667,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0.26,
-				0.667,
-				0.26,
-				0,
-				1.9,
-				0.26
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0.36,
-				0.667,
-				0.36,
-				0,
-				1.9,
-				0.36
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0,
-				0.667,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				-0.27,
-				0.667,
-				-0.27,
-				0,
-				1.9,
-				-0.27
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLAngle",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0.26,
-				0.667,
-				0.26,
-				0,
-				1.9,
-				0.26
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRAngle",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				-0.03,
-				0.667,
-				-0.03,
-				0,
-				1.9,
-				-0.03
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLForm",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0.33,
-				0.667,
-				0.33,
-				0,
-				1.9,
-				0.33
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRForm",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.356,
-				0,
-				0.378,
-				0,
-				0.4,
-				0,
-				1,
-				0.489,
-				0,
-				0.578,
-				0.21,
-				0.667,
-				0.21,
-				0,
-				1.9,
-				0.21
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.444,
-				0,
-				0.556,
-				-6,
-				0.667,
-				-6,
-				0,
-				1.9,
-				-6
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.422,
-				0,
-				0.511,
-				10,
-				0.6,
-				10,
-				1,
-				0.667,
-				10,
-				0.733,
-				-6,
-				0.8,
-				-6,
-				1,
-				0.833,
-				-6,
-				0.867,
-				5,
-				0.9,
-				5,
-				1,
-				1.011,
-				5,
-				1.122,
-				0,
-				1.233,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleZ",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.444,
-				0,
-				0.556,
-				-3,
-				0.667,
-				-3,
-				0,
-				1.9,
-				-3
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBreath",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamShoulder",
-			"Segments": [
-				0,
-				-0.062,
-				1,
-				0.111,
-				-0.062,
-				0.222,
-				-0.103,
-				0.333,
-				0,
-				1,
-				0.589,
-				0.238,
-				0.844,
-				1,
-				1.1,
-				1,
-				0,
-				1.9,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmLA",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.478,
-				0,
-				0.622,
-				-10,
-				0.767,
-				-10,
-				1,
-				0.811,
-				-10,
-				0.856,
-				-8.2,
-				0.9,
-				-8.2,
-				0,
-				1.9,
-				-8.2
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmRA",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				1,
-				0.478,
-				0,
-				0.622,
-				-10,
-				0.767,
-				-10,
-				1,
-				0.811,
-				-10,
-				0.856,
-				-7.2,
-				0.9,
-				-7.2,
-				0,
-				1.9,
-				-7.2
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmLB",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmRB",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				0,
-				0.333,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamHairAhoge",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.111,
-				0,
-				0.222,
-				1.9,
-				0.333,
-				5.2,
-				1,
-				0.444,
-				8.5,
-				0.556,
-				9.926,
-				0.667,
-				9.926,
-				1,
-				0.744,
-				9.926,
-				0.822,
-				-10,
-				0.9,
-				-10,
-				1,
-				0.956,
-				-10,
-				1.011,
-				6,
-				1.067,
-				6,
-				1,
-				1.144,
-				6,
-				1.222,
-				-4,
-				1.3,
-				-4,
-				1,
-				1.367,
-				-4,
-				1.433,
-				0,
-				1.5,
-				0,
-				0,
-				1.9,
-				0
-			]
-		},
-		{
-			"Target": "PartOpacity",
-			"Id": "PartArmA",
-			"Segments": [
-				0,
-				1,
-				0,
-				1.9,
-				1
-			]
-		},
-		{
-			"Target": "PartOpacity",
-			"Id": "PartArmB",
-			"Segments": [
-				0,
-				0,
-				0,
-				1.9,
-				0
-			]
-		}
-	]
-}

Разница между файлами не показана из-за своего большого размера
+ 0 - 1023
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m08.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 1113
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m09.motion3.json


+ 0 - 924
main/xiaozhi-server/test/resources/hiyori_pro_zh/runtime/motion/hiyori_m10.motion3.json

@@ -1,924 +0,0 @@
-{
-	"Version": 3,
-	"Meta": {
-		"Duration": 4.17,
-		"Fps": 30.0,
-		"Loop": true,
-		"AreBeziersRestricted": false,
-		"CurveCount": 31,
-		"TotalSegmentCount": 118,
-		"TotalPointCount": 321,
-		"UserDataCount": 0,
-		"TotalUserDataSize": 0
-	},
-	"Curves": [
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.4,
-				0,
-				0.6,
-				0,
-				0.8,
-				0,
-				1,
-				1.067,
-				0,
-				1.333,
-				1.041,
-				1.6,
-				1.041,
-				1,
-				1.844,
-				1.041,
-				2.089,
-				-8,
-				2.333,
-				-8,
-				1,
-				2.656,
-				-8,
-				2.978,
-				6,
-				3.3,
-				6,
-				0,
-				4.167,
-				6
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.344,
-				0,
-				0.489,
-				-30,
-				0.633,
-				-30,
-				0,
-				4.167,
-				-30
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamAngleZ",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamCheek",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeLOpen",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.067,
-				1,
-				0.133,
-				1,
-				0.2,
-				1,
-				1,
-				0.311,
-				1,
-				0.422,
-				0.988,
-				0.533,
-				0.8,
-				1,
-				0.589,
-				0.706,
-				0.644,
-				0,
-				0.7,
-				0,
-				1,
-				0.722,
-				0,
-				0.744,
-				0,
-				0.767,
-				0,
-				1,
-				0.822,
-				0,
-				0.878,
-				0.8,
-				0.933,
-				0.8,
-				1,
-				1.422,
-				0.8,
-				1.911,
-				0.8,
-				2.4,
-				0.8,
-				1,
-				2.456,
-				0.8,
-				2.511,
-				0,
-				2.567,
-				0,
-				1,
-				2.589,
-				0,
-				2.611,
-				0,
-				2.633,
-				0,
-				1,
-				2.689,
-				0,
-				2.744,
-				0.8,
-				2.8,
-				0.8,
-				0,
-				4.167,
-				0.8
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeLSmile",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeROpen",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.067,
-				1,
-				0.133,
-				1,
-				0.2,
-				1,
-				1,
-				0.311,
-				1,
-				0.422,
-				0.988,
-				0.533,
-				0.8,
-				1,
-				0.589,
-				0.706,
-				0.644,
-				0,
-				0.7,
-				0,
-				1,
-				0.722,
-				0,
-				0.744,
-				0,
-				0.767,
-				0,
-				1,
-				0.822,
-				0,
-				0.878,
-				0.8,
-				0.933,
-				0.8,
-				1,
-				1.422,
-				0.8,
-				1.911,
-				0.8,
-				2.4,
-				0.8,
-				1,
-				2.456,
-				0.8,
-				2.511,
-				0,
-				2.567,
-				0,
-				1,
-				2.589,
-				0,
-				2.611,
-				0,
-				2.633,
-				0,
-				1,
-				2.689,
-				0,
-				2.744,
-				0.8,
-				2.8,
-				0.8,
-				0,
-				4.167,
-				0.8
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeRSmile",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				0,
-				0.433,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeBallX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				0,
-				0.433,
-				0,
-				1,
-				0.667,
-				0,
-				0.9,
-				0.004,
-				1.133,
-				-0.01,
-				1,
-				1.4,
-				-0.025,
-				1.667,
-				-0.43,
-				1.933,
-				-0.43,
-				1,
-				2.211,
-				-0.43,
-				2.489,
-				0.283,
-				2.767,
-				0.283,
-				0,
-				4.167,
-				0.283
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamEyeBallY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				-1,
-				0.433,
-				-1,
-				0,
-				4.167,
-				-1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				0.19,
-				0.433,
-				0.19,
-				0,
-				4.167,
-				0.19
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				0.11,
-				0.433,
-				0.11,
-				0,
-				4.167,
-				0.11
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				-0.48,
-				0.433,
-				-0.48,
-				0,
-				4.167,
-				-0.48
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				-0.29,
-				0.433,
-				-0.29,
-				0,
-				4.167,
-				-0.29
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLAngle",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				1,
-				0.433,
-				1,
-				0,
-				4.167,
-				1
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRAngle",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				0.85,
-				0.433,
-				0.85,
-				0,
-				4.167,
-				0.85
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowLForm",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				-0.75,
-				0.433,
-				-0.75,
-				0,
-				4.167,
-				-0.75
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBrowRForm",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.278,
-				0,
-				0.356,
-				-0.87,
-				0.433,
-				-0.87,
-				0,
-				4.167,
-				-0.87
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleX",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.444,
-				0,
-				0.689,
-				0,
-				0.933,
-				0,
-				1,
-				1.211,
-				0,
-				1.489,
-				0,
-				1.767,
-				0,
-				1,
-				2.056,
-				0,
-				2.344,
-				-6,
-				2.633,
-				-6,
-				1,
-				3.033,
-				-6,
-				3.433,
-				10,
-				3.833,
-				10,
-				0,
-				4.167,
-				10
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleY",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBodyAngleZ",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.8,
-				0,
-				1.4,
-				-2,
-				2,
-				-2,
-				1,
-				2.456,
-				-2,
-				2.911,
-				8.125,
-				3.367,
-				8.125,
-				0,
-				4.167,
-				8.125
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamBreath",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamShoulder",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamLeg",
-			"Segments": [
-				0,
-				1,
-				1,
-				0.667,
-				1,
-				1.333,
-				1,
-				2,
-				1,
-				1,
-				2.267,
-				1,
-				2.533,
-				0.948,
-				2.8,
-				0.948,
-				0,
-				4.167,
-				0.948
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmLA",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.233,
-				0,
-				0.267,
-				0,
-				0.3,
-				0,
-				1,
-				0.478,
-				0,
-				0.656,
-				-10,
-				0.833,
-				-10,
-				1,
-				0.922,
-				-10,
-				1.011,
-				-8.846,
-				1.1,
-				-8.846,
-				1,
-				1.467,
-				-8.846,
-				1.833,
-				-8.835,
-				2.2,
-				-9.1,
-				1,
-				2.622,
-				-9.405,
-				3.044,
-				-10,
-				3.467,
-				-10,
-				0,
-				4.167,
-				-10
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmRA",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.233,
-				0,
-				0.267,
-				0,
-				0.3,
-				0,
-				1,
-				0.478,
-				0,
-				0.656,
-				-10,
-				0.833,
-				-10,
-				1,
-				0.922,
-				-10,
-				1.011,
-				-8.972,
-				1.1,
-				-8.846,
-				1,
-				1.467,
-				-8.328,
-				1.833,
-				-8.2,
-				2.2,
-				-8.2,
-				1,
-				2.622,
-				-8.2,
-				3.044,
-				-10,
-				3.467,
-				-10,
-				0,
-				4.167,
-				-10
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmLB",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamArmRB",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.167,
-				0
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Id": "ParamHairAhoge",
-			"Segments": [
-				0,
-				0,
-				1,
-				0.067,
-				0,
-				0.133,
-				0,
-				0.2,
-				0,
-				1,
-				0.233,
-				0,
-				0.267,
-				-5,
-				0.3,
-				-5,
-				1,
-				0.378,
-				-5,
-				0.456,
-				10,
-				0.533,
-				10,
-				1,
-				0.633,
-				10,
-				0.733,
-				4,
-				0.833,
-				4,
-				0,
-				4.167,
-				4
-			]
-		},
-		{
-			"Target": "PartOpacity",
-			"Id": "PartArmA",
-			"Segments": [
-				0,
-				1,
-				0,
-				4.17,
-				1
-			]
-		},
-		{
-			"Target": "PartOpacity",
-			"Id": "PartArmB",
-			"Segments": [
-				0,
-				0,
-				0,
-				4.17,
-				0
-			]
-		}
-	]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Angry.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  -2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  -2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0.3,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0.3,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  -0.4,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  -0.4,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Blushing.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Normal.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Sad.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  -2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  -2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Smile.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/Surprised.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0.3,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0.3,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0.2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0.2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  -0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  -0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_01.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  3,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  3,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  -0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  -0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_02.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_03.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_04.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  0.2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  0.2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0.2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0.2,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  -0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  -0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0.1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

+ 0 - 120
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/exp/exp_05.exp3.json

@@ -1,120 +0,0 @@
-{
-    "Type":  "Live2D Expression",
-    "Parameters":  [
-                       {
-                           "Id":  "ParamEyeLOpen",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeLForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeROpen",
-                           "Value":  -1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRSmile",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeRForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamEyeBallForm",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRY",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRX",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRAngle",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowLForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm",
-                           "Value":  1,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamBrowRForm2",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamTeethOn",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamCheek",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGlassUD",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassWhite",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlight",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       },
-                       {
-                           "Id":  "ParamGrassHighlightMove",
-                           "Value":  0,
-                           "Blend":  "Add"
-                       }
-                   ]
-}

Разница между файлами не показана из-за своего большого размера
+ 0 - 1432
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_00.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 1971
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_01.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 2182
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_02.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 2596
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_03.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 2316
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_04.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 2345
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_05.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 2082
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_06.motion3.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 1907
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/motions/mtn_07.motion3.json


+ 0 - 44
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori.pose3.json

@@ -1,44 +0,0 @@
-{
-	"Type": "Live2D Pose",
-	"FadeInTime": 0.2,
-	"Groups": [
-		[
-			{
-				"Id": "PartArmAL",
-				"Link": []
-			},
-			{
-				"Id": "PartArmCL",
-				"Link": []
-			},
-			{
-				"Id": "PartArmDL",
-				"Link": []
-			}
-		],
-		[
-			{
-				"Id": "PartArmAR",
-				"Link": []
-			},
-			{
-				"Id": "PartArmBR",
-				"Link": []
-			},
-			{
-				"Id": "PartArmER",
-				"Link": []
-			}
-		],
-		[
-			{
-				"Id": "PartWatchA",
-				"Link": []
-			},
-			{
-				"Id": "PartWatchB",
-				"Link": []
-			}
-		]
-	]
-}

+ 0 - 711
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori_pro_t06.cdi3.json

@@ -1,711 +0,0 @@
-{
-	"Version": 3,
-	"Parameters": [
-		{
-			"Id": "ParamAngleX",
-			"GroupId": "",
-			"Name": "角度 X"
-		},
-		{
-			"Id": "ParamAngleY",
-			"GroupId": "",
-			"Name": "角度 Y"
-		},
-		{
-			"Id": "ParamAngleZ",
-			"GroupId": "",
-			"Name": "角度 Z"
-		},
-		{
-			"Id": "ParamEyeLOpen",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眼 开闭"
-		},
-		{
-			"Id": "ParamEyeLSmile",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眼 微笑"
-		},
-		{
-			"Id": "ParamEyeLForm",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眼 变形"
-		},
-		{
-			"Id": "ParamEyeROpen",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眼 开闭"
-		},
-		{
-			"Id": "ParamEyeRSmile",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眼 微笑"
-		},
-		{
-			"Id": "ParamEyeRForm",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眼 变形"
-		},
-		{
-			"Id": "ParamEyeBallX",
-			"GroupId": "ParamGroupExpression",
-			"Name": "眼珠 X"
-		},
-		{
-			"Id": "ParamEyeBallY",
-			"GroupId": "ParamGroupExpression",
-			"Name": "眼珠 Y"
-		},
-		{
-			"Id": "ParamEyeBallForm",
-			"GroupId": "ParamGroupExpression",
-			"Name": "眼珠 缩放"
-		},
-		{
-			"Id": "ParamBrowLY",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眉 上下"
-		},
-		{
-			"Id": "ParamBrowRY",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眉 上下"
-		},
-		{
-			"Id": "ParamBrowLX",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眉 左右"
-		},
-		{
-			"Id": "ParamBrowRX",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眉 左右"
-		},
-		{
-			"Id": "ParamBrowLAngle",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眉 角度"
-		},
-		{
-			"Id": "ParamBrowRAngle",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眉 角度"
-		},
-		{
-			"Id": "ParamBrowLForm",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眉 变形"
-		},
-		{
-			"Id": "ParamBrowLForm2",
-			"GroupId": "ParamGroupExpression",
-			"Name": "左眉 变形2"
-		},
-		{
-			"Id": "ParamBrowRForm",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眉 变形"
-		},
-		{
-			"Id": "ParamBrowRForm2",
-			"GroupId": "ParamGroupExpression",
-			"Name": "右眉 变形2"
-		},
-		{
-			"Id": "ParamMouthForm",
-			"GroupId": "ParamGroupExpression",
-			"Name": "嘴 变形"
-		},
-		{
-			"Id": "ParamMouthOpenY",
-			"GroupId": "ParamGroupExpression",
-			"Name": "嘴 开闭"
-		},
-		{
-			"Id": "ParamMouthForm2",
-			"GroupId": "ParamGroupExpression",
-			"Name": "嘴 变形2"
-		},
-		{
-			"Id": "ParamTeethOn",
-			"GroupId": "ParamGroupExpression",
-			"Name": "牙齿的显示"
-		},
-		{
-			"Id": "ParamCheek",
-			"GroupId": "ParamGroupExpression",
-			"Name": "害羞"
-		},
-		{
-			"Id": "ParamGlassUD",
-			"GroupId": "ParamGroupExpression",
-			"Name": "眼镜 上下"
-		},
-		{
-			"Id": "ParamGrassWhite",
-			"GroupId": "ParamGroupExpression",
-			"Name": "镜片 白"
-		},
-		{
-			"Id": "ParamGrassHighlight",
-			"GroupId": "ParamGroupExpression",
-			"Name": "镜片 反光显示"
-		},
-		{
-			"Id": "ParamGrassHighlightMove",
-			"GroupId": "ParamGroupExpression",
-			"Name": "镜片 反光移动"
-		},
-		{
-			"Id": "ParamBodyAngleX",
-			"GroupId": "ParamGroupBody",
-			"Name": "身体的旋转 X"
-		},
-		{
-			"Id": "ParamBodyAngleY",
-			"GroupId": "ParamGroupBody",
-			"Name": "身体的旋转 Y"
-		},
-		{
-			"Id": "ParamBodyAngleZ",
-			"GroupId": "ParamGroupBody",
-			"Name": "身体的旋转 Z"
-		},
-		{
-			"Id": "ParamWaistAngleZ",
-			"GroupId": "ParamGroupBody",
-			"Name": "腰的旋转 Z"
-		},
-		{
-			"Id": "ParamBodyPosition",
-			"GroupId": "ParamGroupBody",
-			"Name": "身体的前后"
-		},
-		{
-			"Id": "ParamBreath",
-			"GroupId": "ParamGroupBody",
-			"Name": "呼吸"
-		},
-		{
-			"Id": "ParamLeftShoulderUp",
-			"GroupId": "ParamGroupBody",
-			"Name": "左肩的上下"
-		},
-		{
-			"Id": "ParamRightShoulderUp",
-			"GroupId": "ParamGroupBody",
-			"Name": "右肩的上下"
-		},
-		{
-			"Id": "ParamAllX",
-			"GroupId": "ParamGroup",
-			"Name": "整体的移动 X"
-		},
-		{
-			"Id": "ParamAllY",
-			"GroupId": "ParamGroup",
-			"Name": "整体的移动 Y"
-		},
-		{
-			"Id": "ParamAllRotate",
-			"GroupId": "ParamGroup",
-			"Name": "整体的旋转"
-		},
-		{
-			"Id": "ParamHairFront",
-			"GroupId": "ParamGroupSway",
-			"Name": "头发摇动 前"
-		},
-		{
-			"Id": "ParamHairSide",
-			"GroupId": "ParamGroupSway",
-			"Name": "头发摇动 侧"
-		},
-		{
-			"Id": "ParamHairBack",
-			"GroupId": "ParamGroupSway",
-			"Name": "头发摇动 后"
-		},
-		{
-			"Id": "ParamHairFrontFuwa",
-			"GroupId": "ParamGroupSway",
-			"Name": "前发 蓬松"
-		},
-		{
-			"Id": "ParamHairSideFuwa",
-			"GroupId": "ParamGroupSway",
-			"Name": "侧发 蓬松"
-		},
-		{
-			"Id": "ParamHairBackFuwa",
-			"GroupId": "ParamGroupSway",
-			"Name": "后发 蓬松"
-		},
-		{
-			"Id": "ParamJacket",
-			"GroupId": "ParamGroupSway",
-			"Name": "燕尾的摇动"
-		},
-		{
-			"Id": "ParamChainWaist",
-			"GroupId": "ParamGroupSway",
-			"Name": "表链A的摇动"
-		},
-		{
-			"Id": "ParamWatchSwingA1",
-			"GroupId": "ParamGroupSway",
-			"Name": "怀表A 摇动1"
-		},
-		{
-			"Id": "ParamWatchSwingA2",
-			"GroupId": "ParamGroupSway",
-			"Name": "怀表A 摇动2"
-		},
-		{
-			"Id": "ParamWatchBChain",
-			"GroupId": "ParamGroupSway",
-			"Name": "怀表B 表链的摇动"
-		},
-		{
-			"Id": "ParamWatchAX",
-			"GroupId": "ParamGroup8",
-			"Name": "怀表A 横向旋转"
-		},
-		{
-			"Id": "ParamWatchBSwitch",
-			"GroupId": "ParamGroup9",
-			"Name": "怀表B 开关"
-		},
-		{
-			"Id": "ParamWatchBOpen",
-			"GroupId": "ParamGroup9",
-			"Name": "怀表B 表盖的开闭"
-		},
-		{
-			"Id": "ParamWatchBOpen2",
-			"GroupId": "ParamGroup9",
-			"Name": "怀表B 表盘的开闭"
-		},
-		{
-			"Id": "ParamWatchBX",
-			"GroupId": "ParamGroup9",
-			"Name": "怀表B 横向旋转"
-		},
-		{
-			"Id": "ParamWatchBRoll",
-			"GroupId": "ParamGroup9",
-			"Name": "怀表B 旋转"
-		},
-		{
-			"Id": "ParamWatchBLR",
-			"GroupId": "ParamGroup9",
-			"Name": "怀表B 左右"
-		},
-		{
-			"Id": "ParamWatchBUD",
-			"GroupId": "ParamGroup9",
-			"Name": "怀表B 上下"
-		},
-		{
-			"Id": "ParamArmAL01",
-			"GroupId": "ParamGroup3",
-			"Name": "左手臂A 肩"
-		},
-		{
-			"Id": "ParamArmAL02",
-			"GroupId": "ParamGroup3",
-			"Name": "左肩A 手肘旋转"
-		},
-		{
-			"Id": "ParamArmAL03",
-			"GroupId": "ParamGroup3",
-			"Name": "左手臂A 手腕"
-		},
-		{
-			"Id": "ParamArmAL04",
-			"GroupId": "ParamGroup3",
-			"Name": "左手臂A 前臂的前后"
-		},
-		{
-			"Id": "ParamArmAR01",
-			"GroupId": "ParamGroup2",
-			"Name": "右手臂A 肩的旋转"
-		},
-		{
-			"Id": "ParamArmAR02",
-			"GroupId": "ParamGroup2",
-			"Name": "右手臂A 手肘的旋转"
-		},
-		{
-			"Id": "ParamArmAR03",
-			"GroupId": "ParamGroup2",
-			"Name": "右手臂A 手腕的旋转"
-		},
-		{
-			"Id": "ParamArmAR04",
-			"GroupId": "ParamGroup2",
-			"Name": "右手臂A 前臂的前后"
-		},
-		{
-			"Id": "ParamArmBR01",
-			"GroupId": "ParamGroup4",
-			"Name": "右手臂B 肩的旋转"
-		},
-		{
-			"Id": "ParamArmBR02",
-			"GroupId": "ParamGroup4",
-			"Name": "右手臂B 手肘的旋转"
-		},
-		{
-			"Id": "ParamArmBR03",
-			"GroupId": "ParamGroup4",
-			"Name": "右手臂B 手腕的旋转"
-		},
-		{
-			"Id": "ParamArmBRHand01",
-			"GroupId": "ParamGroup4",
-			"Name": "右手01 显示"
-		},
-		{
-			"Id": "ParamArmBRHand01Roll",
-			"GroupId": "ParamGroup4",
-			"Name": "右手01 手指弯曲"
-		},
-		{
-			"Id": "ParamArmBRHand05",
-			"GroupId": "ParamGroup4",
-			"Name": "右手05 显示"
-		},
-		{
-			"Id": "ParamArmBRHand05Roll1",
-			"GroupId": "ParamGroup4",
-			"Name": "右手05 手指弯曲1"
-		},
-		{
-			"Id": "ParamArmBRHand05Roll2",
-			"GroupId": "ParamGroup4",
-			"Name": "右手05 手指弯曲2"
-		},
-		{
-			"Id": "ParamArmBRHand05Roll3",
-			"GroupId": "ParamGroup4",
-			"Name": "右手05 手指弯曲3"
-		},
-		{
-			"Id": "ParamArmCR01",
-			"GroupId": "ParamGroup5",
-			"Name": "左手臂C 肩的旋转"
-		},
-		{
-			"Id": "ParamArmCR02",
-			"GroupId": "ParamGroup5",
-			"Name": "左手臂C 手肘的旋转"
-		},
-		{
-			"Id": "ParamArmCR03",
-			"GroupId": "ParamGroup5",
-			"Name": "左手臂C 手腕的旋转"
-		},
-		{
-			"Id": "ParamArmCLHandRoll1",
-			"GroupId": "ParamGroup5",
-			"Name": "左手C 手指弯曲1"
-		},
-		{
-			"Id": "ParamArmDL01",
-			"GroupId": "ParamGroup6",
-			"Name": "左手臂D 肩的旋转"
-		},
-		{
-			"Id": "ParamArmDL02",
-			"GroupId": "ParamGroup6",
-			"Name": "左手臂D 手肘的旋转"
-		},
-		{
-			"Id": "ParamArmDL03",
-			"GroupId": "ParamGroup6",
-			"Name": "左手臂D 手腕的旋转"
-		},
-		{
-			"Id": "ParamArmDLHand03Roll",
-			"GroupId": "ParamGroup6",
-			"Name": "左手03 手指弯曲"
-		},
-		{
-			"Id": "ParamArmER01",
-			"GroupId": "ParamGroup7",
-			"Name": "右手臂E 肩的旋转"
-		},
-		{
-			"Id": "ParamArmER02",
-			"GroupId": "ParamGroup7",
-			"Name": "右手臂E 手肘的旋转"
-		},
-		{
-			"Id": "ParamArmER03",
-			"GroupId": "ParamGroup7",
-			"Name": "右手臂E 手腕的旋转"
-		},
-		{
-			"Id": "ParamArmER04",
-			"GroupId": "ParamGroup7",
-			"Name": "右手臂E 上臂的长度"
-		},
-		{
-			"Id": "ParamArmERHand04",
-			"GroupId": "ParamGroup7",
-			"Name": "右手04 显示"
-		},
-		{
-			"Id": "ParamArmERHand04Roll1",
-			"GroupId": "ParamGroup7",
-			"Name": "右手04 手指弯曲1"
-		},
-		{
-			"Id": "ParamArmERHand04Roll2",
-			"GroupId": "ParamGroup7",
-			"Name": "右手04 手指弯曲2"
-		},
-		{
-			"Id": "ParamArmERHand06",
-			"GroupId": "ParamGroup7",
-			"Name": "右手06 显示"
-		},
-		{
-			"Id": "ParamArmERHand06Roll1",
-			"GroupId": "ParamGroup7",
-			"Name": "右手06 手指弯曲1"
-		},
-		{
-			"Id": "ParamArmERHand06Roll2",
-			"GroupId": "ParamGroup7",
-			"Name": "右手06 手指弯曲2"
-		}
-	],
-	"ParameterGroups": [
-		{
-			"Id": "ParamGroupExpression",
-			"GroupId": "",
-			"Name": "表情"
-		},
-		{
-			"Id": "ParamGroupBody",
-			"GroupId": "",
-			"Name": "身体"
-		},
-		{
-			"Id": "ParamGroup",
-			"GroupId": "",
-			"Name": "整体移动"
-		},
-		{
-			"Id": "ParamGroupSway",
-			"GroupId": "",
-			"Name": "摇动"
-		},
-		{
-			"Id": "ParamGroup8",
-			"GroupId": "",
-			"Name": "怀表A"
-		},
-		{
-			"Id": "ParamGroup9",
-			"GroupId": "",
-			"Name": "怀表B"
-		},
-		{
-			"Id": "ParamGroup3",
-			"GroupId": "",
-			"Name": "左手臂A"
-		},
-		{
-			"Id": "ParamGroup2",
-			"GroupId": "",
-			"Name": "右手臂A"
-		},
-		{
-			"Id": "ParamGroup4",
-			"GroupId": "",
-			"Name": "右手臂B"
-		},
-		{
-			"Id": "ParamGroup5",
-			"GroupId": "",
-			"Name": "左手臂C"
-		},
-		{
-			"Id": "ParamGroup6",
-			"GroupId": "",
-			"Name": "左手臂D"
-		},
-		{
-			"Id": "ParamGroup7",
-			"GroupId": "",
-			"Name": "右手臂E"
-		}
-	],
-	"Parts": [
-		{
-			"Id": "PartCredit",
-			"Name": "名牌"
-		},
-		{
-			"Id": "PartCore",
-			"Name": "CORE"
-		},
-		{
-			"Id": "PartGlass",
-			"Name": "眼镜"
-		},
-		{
-			"Id": "PartWatchA",
-			"Name": "怀表A"
-		},
-		{
-			"Id": "PartWatchB",
-			"Name": "怀表B"
-		},
-		{
-			"Id": "PartHairFront",
-			"Name": "前发"
-		},
-		{
-			"Id": "PartHead",
-			"Name": "头"
-		},
-		{
-			"Id": "PartUpperBody",
-			"Name": "上半身"
-		},
-		{
-			"Id": "PartHairBack",
-			"Name": "后发"
-		},
-		{
-			"Id": "PartLowerBody",
-			"Name": "下半身"
-		},
-		{
-			"Id": "PartArmAL",
-			"Name": "左手臂A"
-		},
-		{
-			"Id": "PartArmAR",
-			"Name": "右手臂A"
-		},
-		{
-			"Id": "PartArmBR",
-			"Name": "右手臂B"
-		},
-		{
-			"Id": "PartArmCL",
-			"Name": "左手臂C"
-		},
-		{
-			"Id": "PartArmDL",
-			"Name": "左手臂D"
-		},
-		{
-			"Id": "PartArmER",
-			"Name": "右手臂E"
-		},
-		{
-			"Id": "PartEyeBlow",
-			"Name": "眉毛"
-		},
-		{
-			"Id": "PartEyeR",
-			"Name": "右眼"
-		},
-		{
-			"Id": "PartEyeL",
-			"Name": "左眼"
-		},
-		{
-			"Id": "PartHairLine",
-			"Name": "发际线"
-		},
-		{
-			"Id": "PartHairShadow",
-			"Name": "头发阴影"
-		},
-		{
-			"Id": "PartNose",
-			"Name": "鼻子"
-		},
-		{
-			"Id": "PartMouth",
-			"Name": "嘴"
-		},
-		{
-			"Id": "PartJacket",
-			"Name": "燕尾服"
-		},
-		{
-			"Id": "PartArmALFore",
-			"Name": "左手臂A 前臂"
-		},
-		{
-			"Id": "PartArmARFore",
-			"Name": "右手臂A 前臂"
-		},
-		{
-			"Id": "PartHand11",
-			"Name": "手套_1"
-		},
-		{
-			"Id": "PartHand51",
-			"Name": "手套_5"
-		},
-		{
-			"Id": "PartHand21",
-			"Name": "手套_2"
-		},
-		{
-			"Id": "PartHand31",
-			"Name": "手套_3"
-		},
-		{
-			"Id": "PartHand41",
-			"Name": "手套_4"
-		},
-		{
-			"Id": "PartHand61",
-			"Name": "手套_6"
-		}
-	],
-	"CombinedParameters": [
-		[
-			"ParamAngleX",
-			"ParamAngleY"
-		],
-		[
-			"ParamEyeBallX",
-			"ParamEyeBallY"
-		],
-		[
-			"ParamMouthForm",
-			"ParamMouthOpenY"
-		],
-		[
-			"ParamBodyAngleX",
-			"ParamBodyAngleY"
-		],
-		[
-			"ParamLeftShoulderUp",
-			"ParamRightShoulderUp"
-		],
-		[
-			"ParamAllX",
-			"ParamAllY"
-		],
-		[
-			"ParamWatchSwingA1",
-			"ParamWatchSwingA2"
-		],
-		[
-			"ParamWatchBLR",
-			"ParamWatchBUD"
-		]
-	]
-}

+ 0 - 123
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori_pro_t06.model3.json

@@ -1,123 +0,0 @@
-{
-	"Version": 3,
-	"FileReferences": {
-		"Moc": "natori_pro_t06.moc3",
-		"Textures": [
-			"natori_pro_t06.4096/texture_00.png"
-		],
-		"Physics": "natori_pro_t06.physics3.json",
-		"Pose": "natori.pose3.json",
-		"DisplayInfo": "natori_pro_t06.cdi3.json",
-		"Expressions": [
-			{
-				"Name": "Angry",
-				"File": "exp/Angry.exp3.json"
-			},
-			{
-				"Name": "Blushing",
-				"File": "exp/Blushing.exp3.json"
-			},
-			{
-				"Name": "Normal",
-				"File": "exp/Normal.exp3.json"
-			},
-			{
-				"Name": "Sad",
-				"File": "exp/Sad.exp3.json"
-			},
-			{
-				"Name": "Smile",
-				"File": "exp/Smile.exp3.json"
-			},
-			{
-				"Name": "Surprised",
-				"File": "exp/Surprised.exp3.json"
-			},
-			{
-				"Name": "exp_01",
-				"File": "exp/exp_01.exp3.json"
-			},
-			{
-				"Name": "exp_02",
-				"File": "exp/exp_02.exp3.json"
-			},
-			{
-				"Name": "exp_03",
-				"File": "exp/exp_03.exp3.json"
-			},
-			{
-				"Name": "exp_04",
-				"File": "exp/exp_04.exp3.json"
-			},
-			{
-				"Name": "exp_05",
-				"File": "exp/exp_05.exp3.json"
-			}
-		],
-		"Motions": {
-			"Idle": [
-				{
-					"File": "motions/mtn_00.motion3.json"
-				},
-				{
-					"File": "motions/mtn_01.motion3.json"
-				},
-				{
-					"File": "motions/mtn_02.motion3.json"
-				}
-			],
-			"Tap": [
-				{
-					"File": "motions/mtn_03.motion3.json"
-				}
-			],
-			"FlickUp@Head": [
-				{
-					"File": "motions/mtn_04.motion3.json"
-				}
-			],
-			"Flick@Body": [
-				{
-					"File": "motions/mtn_05.motion3.json"
-				}
-			],
-			"FlickDown@Body": [
-				{
-					"File": "motions/mtn_06.motion3.json"
-				}
-			],
-			"Tap@Head": [
-				{
-					"File": "motions/mtn_07.motion3.json"
-				}
-			]
-		}
-	},
-	"Groups": [
-		{
-			"Target": "Parameter",
-			"Name": "LipSync",
-			"Ids": [
-				"ParamMouthOpenY"
-			]
-		},
-		{
-			"Target": "Parameter",
-			"Name": "EyeBlink",
-			"Ids": [
-				"ParamEyeLOpen",
-				"ParamEyeROpen"
-			]
-		}
-	],
-	"HitAreas": [
-		{
-			"Id": "HitAreaHead",
-			"Name": ""
-		},
-		{
-			"Id": "HitAreaBody",
-			"Name": ""
-		}
-	]
-}

+ 0 - 966
main/xiaozhi-server/test/resources/natori_pro_zh/runtime/natori_pro_t06.physics3.json

@@ -1,966 +0,0 @@
-{
-	"Version": 3,
-	"Meta": {
-		"PhysicsSettingCount": 11,
-		"TotalInputCount": 34,
-		"TotalOutputCount": 12,
-		"VertexCount": 23,
-		"EffectiveForces": {
-			"Gravity": {
-				"X": 0,
-				"Y": -1
-			},
-			"Wind": {
-				"X": 0,
-				"Y": 0
-			}
-		},
-		"PhysicsDictionary": [
-			{
-				"Id": "PhysicsSetting1",
-				"Name": "前髪 揺れ"
-			},
-			{
-				"Id": "PhysicsSetting2",
-				"Name": "横髪 揺れ"
-			},
-			{
-				"Id": "PhysicsSetting3",
-				"Name": "後ろ髪 揺れ"
-			},
-			{
-				"Id": "PhysicsSetting4",
-				"Name": "前髪ふわ"
-			},
-			{
-				"Id": "PhysicsSetting5",
-				"Name": "横髪ふわ"
-			},
-			{
-				"Id": "PhysicsSetting6",
-				"Name": "後ろ髪ふわ"
-			},
-			{
-				"Id": "PhysicsSetting7",
-				"Name": "燕尾揺れ"
-			},
-			{
-				"Id": "PhysicsSetting8",
-				"Name": "懐中時計腰 揺れ"
-			},
-			{
-				"Id": "PhysicsSetting9",
-				"Name": "腰のチェーン揺れ"
-			},
-			{
-				"Id": "PhysicsSetting10",
-				"Name": "懐中時計腰 横回転"
-			},
-			{
-				"Id": "PhysicsSetting11",
-				"Name": "懐中時計B チェーン揺れ"
-			}
-		]
-	},
-	"PhysicsSettings": [
-		{
-			"Id": "PhysicsSetting1",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleX"
-					},
-					"Weight": 60,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleZ"
-					},
-					"Weight": 60,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleX"
-					},
-					"Weight": 40,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleZ"
-					},
-					"Weight": 40,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamHairFront"
-					},
-					"VertexIndex": 1,
-					"Scale": 1.824,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 12.7
-					},
-					"Mobility": 0.95,
-					"Delay": 0.9,
-					"Acceleration": 1,
-					"Radius": 12.7
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting2",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleX"
-					},
-					"Weight": 60,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleZ"
-					},
-					"Weight": 60,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleX"
-					},
-					"Weight": 40,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleZ"
-					},
-					"Weight": 40,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamHairSide"
-					},
-					"VertexIndex": 1,
-					"Scale": 2,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 18.2
-					},
-					"Mobility": 0.95,
-					"Delay": 0.9,
-					"Acceleration": 1,
-					"Radius": 18.2
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting3",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleX"
-					},
-					"Weight": 60,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleZ"
-					},
-					"Weight": 60,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleX"
-					},
-					"Weight": 40,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleZ"
-					},
-					"Weight": 40,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamHairBack"
-					},
-					"VertexIndex": 1,
-					"Scale": 2,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 14.3
-					},
-					"Mobility": 1,
-					"Delay": 0.9,
-					"Acceleration": 1.42,
-					"Radius": 14.3
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting4",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleY"
-					},
-					"Weight": 35,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleY"
-					},
-					"Weight": 30,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyPosition"
-					},
-					"Weight": 35,
-					"Type": "X",
-					"Reflect": true
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamHairFrontFuwa"
-					},
-					"VertexIndex": 1,
-					"Scale": 3,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": true
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 11.9
-					},
-					"Mobility": 0.79,
-					"Delay": 0.9,
-					"Acceleration": 1,
-					"Radius": 11.9
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting5",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleY"
-					},
-					"Weight": 35,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleY"
-					},
-					"Weight": 30,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyPosition"
-					},
-					"Weight": 35,
-					"Type": "X",
-					"Reflect": true
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamHairSideFuwa"
-					},
-					"VertexIndex": 1,
-					"Scale": 3.5,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": true
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 14.3
-					},
-					"Mobility": 0.79,
-					"Delay": 0.9,
-					"Acceleration": 1.1,
-					"Radius": 14.3
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting6",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamAngleY"
-					},
-					"Weight": 35,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleY"
-					},
-					"Weight": 30,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyPosition"
-					},
-					"Weight": 35,
-					"Type": "X",
-					"Reflect": true
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamHairBackFuwa"
-					},
-					"VertexIndex": 1,
-					"Scale": 5,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": true
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 11.3
-					},
-					"Mobility": 0.79,
-					"Delay": 0.9,
-					"Acceleration": 1.16,
-					"Radius": 11.3
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting7",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleX"
-					},
-					"Weight": 100,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleZ"
-					},
-					"Weight": 70,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamWaistAngleZ"
-					},
-					"Weight": 30,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamJacket"
-					},
-					"VertexIndex": 1,
-					"Scale": 1,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 31.9
-					},
-					"Mobility": 0.95,
-					"Delay": 0.8,
-					"Acceleration": 0.8,
-					"Radius": 31.9
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting8",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleX"
-					},
-					"Weight": 100,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleZ"
-					},
-					"Weight": 70,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamWaistAngleZ"
-					},
-					"Weight": 30,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamWatchSwingA1"
-					},
-					"VertexIndex": 1,
-					"Scale": 1,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamWatchSwingA2"
-					},
-					"VertexIndex": 2,
-					"Scale": 1,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 15.4
-					},
-					"Mobility": 0.95,
-					"Delay": 1,
-					"Acceleration": 0.8,
-					"Radius": 15.4
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 31.9
-					},
-					"Mobility": 0.9,
-					"Delay": 1,
-					"Acceleration": 0.6,
-					"Radius": 16.5
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting9",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleX"
-					},
-					"Weight": 100,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleZ"
-					},
-					"Weight": 70,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamWaistAngleZ"
-					},
-					"Weight": 30,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamChainWaist"
-					},
-					"VertexIndex": 1,
-					"Scale": 1,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 11.6
-					},
-					"Mobility": 0.95,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 11.6
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting10",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleX"
-					},
-					"Weight": 100,
-					"Type": "X",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamBodyAngleZ"
-					},
-					"Weight": 70,
-					"Type": "Angle",
-					"Reflect": false
-				},
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamWaistAngleZ"
-					},
-					"Weight": 30,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamWatchAX"
-					},
-					"VertexIndex": 1,
-					"Scale": 2,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 72.1
-					},
-					"Mobility": 0.95,
-					"Delay": 1,
-					"Acceleration": 0.2,
-					"Radius": 72.1
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		},
-		{
-			"Id": "PhysicsSetting11",
-			"Input": [
-				{
-					"Source": {
-						"Target": "Parameter",
-						"Id": "ParamArmBR03"
-					},
-					"Weight": 100,
-					"Type": "X",
-					"Reflect": false
-				}
-			],
-			"Output": [
-				{
-					"Destination": {
-						"Target": "Parameter",
-						"Id": "ParamWatchBChain"
-					},
-					"VertexIndex": 1,
-					"Scale": 2,
-					"Weight": 100,
-					"Type": "Angle",
-					"Reflect": false
-				}
-			],
-			"Vertices": [
-				{
-					"Position": {
-						"X": 0,
-						"Y": 0
-					},
-					"Mobility": 1,
-					"Delay": 1,
-					"Acceleration": 1,
-					"Radius": 0
-				},
-				{
-					"Position": {
-						"X": 0,
-						"Y": 13.1
-					},
-					"Mobility": 0.95,
-					"Delay": 1,
-					"Acceleration": 0.66,
-					"Radius": 13.1
-				}
-			],
-			"Normalization": {
-				"Position": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				},
-				"Angle": {
-					"Minimum": -10,
-					"Default": 0,
-					"Maximum": 10
-				}
-			}
-		}
-	]
-}

main/xiaozhi-server/.dockerignore → main/xingxing-server/.dockerignore


main/xiaozhi-server/Dockerfile → main/xingxing-server/Dockerfile


main/xiaozhi-server/agent-base-prompt.txt → main/xingxing-server/agent-base-prompt.txt


main/xiaozhi-server/app.py → main/xingxing-server/app.py


+ 1 - 15
main/xiaozhi-server/config.yaml

@@ -169,7 +169,7 @@ voiceprint: {}
 # ################################以下是角色模型配置######################################
 
 prompt: |
-  你是爱驰尔,运行在 xingxing-server 上的车载 AI 模块。
+  你是爱驰尔,运行在 FWT_Ren 上的车载 AI 模块。
   你的职责是语音问答、路线录制与风险点预警、天气查询、音乐播放、Home Assistant 设备控制、知识库检索和车辆相关信息播报。
   所有回复都只说一句话,优先直接给结论,简短、自然、可靠,不说多余寒暄。
   涉及行车、路线、故障码、氢系统时,先保证安全,再给出明确建议。
@@ -226,13 +226,6 @@ Intent:
     functions:
       - get_weather
       - search_from_ragflow
-      - nav_start_record
-      - nav_stop_record
-      - nav_mark_hazard
-      - nav_start_run
-      - nav_stop_run
-      - nav_list_routes
-      - nav_set_active_route
       - play_music
       - hass_get_state
       - hass_set_state
@@ -247,13 +240,6 @@ Intent:
       - get_weather
       - search_from_ragflow
       # 越野/赛道领航播报(路线录制 + 风险点预警)
-      - nav_start_record
-      - nav_stop_record
-      - nav_mark_hazard
-      - nav_start_run
-      - nav_stop_run
-      - nav_list_routes
-      - nav_set_active_route
       - play_music
       - hass_get_state
       - hass_set_state

main/xiaozhi-server/config/assets/bind_code.wav → main/xingxing-server/config/assets/bind_code.wav


main/xiaozhi-server/config/assets/bind_code/0.wav → main/xingxing-server/config/assets/bind_code/0.wav


main/xiaozhi-server/config/assets/bind_code/1.wav → main/xingxing-server/config/assets/bind_code/1.wav


main/xiaozhi-server/config/assets/bind_code/2.wav → main/xingxing-server/config/assets/bind_code/2.wav


main/xiaozhi-server/config/assets/bind_code/3.wav → main/xingxing-server/config/assets/bind_code/3.wav


main/xiaozhi-server/config/assets/bind_code/4.wav → main/xingxing-server/config/assets/bind_code/4.wav


main/xiaozhi-server/config/assets/bind_code/5.wav → main/xingxing-server/config/assets/bind_code/5.wav


main/xiaozhi-server/config/assets/bind_code/6.wav → main/xingxing-server/config/assets/bind_code/6.wav


main/xiaozhi-server/config/assets/bind_code/7.wav → main/xingxing-server/config/assets/bind_code/7.wav


main/xiaozhi-server/config/assets/bind_code/8.wav → main/xingxing-server/config/assets/bind_code/8.wav


main/xiaozhi-server/config/assets/bind_code/9.wav → main/xingxing-server/config/assets/bind_code/9.wav


main/xiaozhi-server/config/assets/bind_not_found.wav → main/xingxing-server/config/assets/bind_not_found.wav


main/xiaozhi-server/config/assets/max_output_size.wav → main/xingxing-server/config/assets/max_output_size.wav


main/xiaozhi-server/config/assets/tts_notify.mp3 → main/xingxing-server/config/assets/tts_notify.mp3


main/xiaozhi-server/config/assets/wakeup_words.wav → main/xingxing-server/config/assets/wakeup_words.wav


main/xiaozhi-server/config/assets/wakeup_words/ed76d459636c2481aec828516c1b4f54.wav → main/xingxing-server/config/assets/wakeup_words/ed76d459636c2481aec828516c1b4f54.wav


main/xiaozhi-server/config/assets/wakeup_words_short.wav → main/xingxing-server/config/assets/wakeup_words_short.wav


main/xiaozhi-server/config/config_loader.py → main/xingxing-server/config/config_loader.py


main/xiaozhi-server/config/logger.py → main/xingxing-server/config/logger.py


main/xiaozhi-server/config/manage_api_client.py → main/xingxing-server/config/manage_api_client.py


main/xiaozhi-server/config/settings.py → main/xingxing-server/config/settings.py


main/xiaozhi-server/config_from_api.yaml → main/xingxing-server/config_from_api.yaml


+ 455 - 0
main/xingxing-server/core/api/app_gps_handler.py

@@ -0,0 +1,455 @@
+import asyncio
+import time
+
+from aiohttp import web
+
+from config.logger import setup_logging
+from core.api.base_handler import BaseHandler
+from core.providers.tools.device_mcp.mcp_handler import call_mcp_tool
+from plugins_func.nav_copilot.geo import haversine_m
+from plugins_func.nav_copilot.service import (
+    _parse_fix,
+    _pick_gnss_tool_name,
+    get_service,
+    normalize_latlon,
+    normalize_note,
+    normalize_coord_type,
+)
+from plugins_func.nav_copilot.storage import LineAlert, NavDB
+
+TAG = __name__
+
+
+class AppGpsHandler(BaseHandler):
+    def __init__(self, config: dict, ws_server):
+        super().__init__(config)
+        self.ws_server = ws_server
+        self.logger = setup_logging()
+        self._inflight_lock = asyncio.Lock()
+        self._inflight_tasks: dict[str, asyncio.Task] = {}
+        self._latest_payloads: dict[str, tuple[float, dict]] = {}
+        self._cache_ttl_s = 1.5
+        self._tool_timeout_s = 8
+        self._nav_db = NavDB.default()
+
+    async def _attach_recent_alert(self, device_id: str, payload: dict | None) -> dict | None:
+        if payload is None:
+            return None
+        merged = dict(payload)
+        try:
+            recent_alert = await self.ws_server.get_recent_device_alert(device_id)
+        except Exception:
+            recent_alert = None
+        if recent_alert is not None:
+            merged["risk_alert_event"] = recent_alert
+        return merged
+
+    async def _resolve_conn(self, request):
+        requested_device_id = request.query.get("device_id")
+        conn = await self.ws_server.get_connection(device_id=requested_device_id)
+        if conn is None:
+            return None, "no_active_device"
+        return conn, None
+
+    async def _resolve_target_device(self, request, body: dict | None = None):
+        requested_device_id = request.query.get("device_id")
+        if not requested_device_id and isinstance(body, dict):
+            requested_device_id = str(body.get("device_id") or "").strip()
+
+        if requested_device_id:
+            conn = await self.ws_server.get_connection(device_id=requested_device_id)
+            return requested_device_id, conn, None
+
+        conn, error_code = await self._resolve_conn(request)
+        if error_code:
+            return None, None, error_code
+        return self._device_key(conn), conn, None
+
+    async def _resolve_mcp_target(self, conn):
+        if not hasattr(conn, "mcp_client") or conn.mcp_client is None:
+            return None, "device_mcp_not_initialized"
+        if not await conn.mcp_client.is_ready():
+            return None, "device_mcp_not_ready"
+        tool_name = _pick_gnss_tool_name(conn)
+        if not tool_name:
+            return None, "device_gnss_tool_not_found"
+        return tool_name, None
+
+    @staticmethod
+    def _device_key(conn) -> str:
+        return str(getattr(conn, "device_id", "") or "default")
+
+    @staticmethod
+    def _serialize_alert(alert: LineAlert) -> dict:
+        return {
+            "id": alert.id,
+            "device_id": alert.device_id,
+            "start_lat": alert.start_lat,
+            "start_lon": alert.start_lon,
+            "end_lat": alert.end_lat,
+            "end_lon": alert.end_lon,
+            "coord_type": alert.coord_type,
+            "note": alert.note,
+            "created_at": alert.created_at,
+        }
+
+    def _parse_alert_payload(self, body: dict) -> tuple[dict | None, str | None]:
+        try:
+            start_lat, start_lon = normalize_latlon(
+                body.get("start_lat"),
+                body.get("start_lon"),
+            )
+            end_lat, end_lon = normalize_latlon(
+                body.get("end_lat"),
+                body.get("end_lon"),
+            )
+            coord_type = normalize_coord_type(body.get("coord_type"), default="BD09")
+            note = normalize_note(str(body.get("note") or ""))
+        except ValueError as exc:
+            return None, str(exc)
+
+        if haversine_m(start_lat, start_lon, end_lat, end_lon) < 1.0:
+            return None, "line_too_short"
+
+        return {
+            "start_lat": start_lat,
+            "start_lon": start_lon,
+            "end_lat": end_lat,
+            "end_lon": end_lon,
+            "coord_type": coord_type,
+            "note": note,
+        }, None
+
+    async def _fetch_fix_payload(self, conn, tool_name: str) -> dict:
+        raw = await call_mcp_tool(
+            conn,
+            conn.mcp_client,
+            tool_name,
+            "{}",
+            timeout=self._tool_timeout_s,
+        )
+
+        fix = _parse_fix(raw)
+        if not fix.ok:
+            return {
+                "ok": False,
+                "device_id": getattr(conn, "device_id", None),
+                "reason": "no_fix",
+            }
+
+        payload = {
+            "ok": True,
+            "device_id": getattr(conn, "device_id", None),
+            "lat": fix.lat,
+            "lon": fix.lon,
+            "speed_mps": fix.speed_mps,
+            "heading_deg": fix.heading_deg,
+            "timestamp": fix.t,
+            "coord_type": "WGS84",
+            "source": "device_mcp_gnss",
+        }
+        device_key = self._device_key(conn)
+        self._latest_payloads[device_key] = (time.monotonic(), payload)
+        try:
+            await self.ws_server.update_device_gps(device_key, payload)
+        except Exception:
+            pass
+        return payload
+
+    async def _get_or_start_inflight(self, device_key: str, conn, tool_name: str):
+        async with self._inflight_lock:
+            task = self._inflight_tasks.get(device_key)
+            if task is None or task.done():
+                task = asyncio.create_task(self._fetch_fix_payload(conn, tool_name))
+                self._inflight_tasks[device_key] = task
+            return task
+
+    async def _get_fix_payload(self, request):
+        conn, error_code = await self._resolve_conn(request)
+        if error_code:
+            return None, error_code
+
+        device_key = self._device_key(conn)
+
+        live_payload, _ = await self.ws_server.get_latest_gps(
+            device_key, max_age_s=3.0, allow_stale=False
+        )
+        if live_payload is not None:
+            return live_payload, None
+
+        try:
+            nav_service = get_service(conn)
+            if nav_service.is_monitoring_active():
+                cached_gps = nav_service.get_cached_gps()
+                if cached_gps is not None:
+                    return cached_gps, None
+        except Exception:
+            pass
+
+        cached = self._latest_payloads.get(device_key)
+        now = time.monotonic()
+        if cached and (now - cached[0]) <= self._cache_ttl_s:
+            return cached[1], None
+
+        try:
+            tool_name, error_code = await self._resolve_mcp_target(conn)
+            if error_code:
+                stale_payload, _ = await self.ws_server.get_latest_gps(
+                    device_key, max_age_s=3.0, allow_stale=True
+                )
+                if stale_payload is not None:
+                    return stale_payload, None
+                return None, error_code
+            task = await self._get_or_start_inflight(device_key, conn, tool_name)
+            payload = await task
+            return payload, None
+        except Exception as exc:
+            self.logger.bind(tag=TAG).warning(f"app gps tool call failed: {exc}")
+            stale_payload, _ = await self.ws_server.get_latest_gps(
+                device_key, max_age_s=3.0, allow_stale=True
+            )
+            if stale_payload is not None:
+                return stale_payload, None
+            cached = self._latest_payloads.get(device_key)
+            if cached:
+                stale_payload = dict(cached[1])
+                stale_payload["stale"] = True
+                return stale_payload, None
+            return None, "device_gnss_call_failed"
+        finally:
+            async with self._inflight_lock:
+                task = self._inflight_tasks.get(device_key)
+                if task is not None and task.done():
+                    self._inflight_tasks.pop(device_key, None)
+
+    def _json_response(self, payload: dict, status: int = 200):
+        response = web.json_response(payload, status=status)
+        self._add_cors_headers(response)
+        return response
+
+    async def handle_get_latest(self, request):
+        payload, error_code = await self._get_fix_payload(request)
+        if error_code:
+            return self._json_response(
+                {
+                    "ok": False,
+                    "error": error_code,
+                    "active_devices": await self.ws_server.list_active_device_ids(),
+                },
+                status=503,
+            )
+        conn, _ = await self._resolve_conn(request)
+        if conn is not None:
+            payload = await self._attach_recent_alert(self._device_key(conn), payload)
+        return self._json_response(payload)
+
+    async def handle_get_risk_alerts(self, request):
+        device_id, _, error_code = await self._resolve_target_device(request)
+        if error_code:
+            return self._json_response(
+                {
+                    "ok": False,
+                    "error": error_code,
+                    "active_devices": await self.ws_server.list_active_device_ids(),
+                },
+                status=503,
+            )
+
+        alerts = self._nav_db.list_line_alerts(device_id)
+        return self._json_response(
+            {
+                "ok": True,
+                "device_id": device_id,
+                "alerts": [self._serialize_alert(alert) for alert in alerts],
+            }
+        )
+
+    async def handle_post_risk_alerts(self, request):
+        try:
+            body = await request.json()
+        except Exception:
+            return self._json_response({"ok": False, "error": "invalid_json"}, status=400)
+
+        payload, error_code = self._parse_alert_payload(body)
+        if error_code:
+            return self._json_response({"ok": False, "error": error_code}, status=400)
+
+        device_id, conn, error_code = await self._resolve_target_device(request, body)
+        if error_code:
+            return self._json_response(
+                {
+                    "ok": False,
+                    "error": error_code,
+                    "active_devices": await self.ws_server.list_active_device_ids(),
+                },
+                status=503,
+            )
+
+        try:
+            if conn is not None:
+                service = get_service(conn)
+                alert = await service.create_line_alert(device_id=device_id, **payload)
+                monitoring_active = service.is_monitoring_active()
+            else:
+                alert_id = self._nav_db.create_line_alert(device_id=device_id, **payload)
+                alert = self._nav_db.get_line_alert(alert_id, device_id=device_id)
+                monitoring_active = False
+            assert alert is not None
+        except ValueError as exc:
+            return self._json_response({"ok": False, "error": str(exc)}, status=400)
+        except Exception as exc:
+            self.logger.bind(tag=TAG).warning(f"create risk alert failed: {exc}")
+            return self._json_response({"ok": False, "error": "create_failed"}, status=500)
+
+        return self._json_response(
+            {
+                "ok": True,
+                "device_id": device_id,
+                "monitoring_active": monitoring_active,
+                "alert": self._serialize_alert(alert),
+            },
+            status=201,
+        )
+
+    async def handle_delete_risk_alert(self, request):
+        alert_id_raw = request.match_info.get("alert_id", "")
+        try:
+            alert_id = int(alert_id_raw)
+        except ValueError:
+            return self._json_response({"ok": False, "error": "invalid_alert_id"}, status=400)
+
+        device_id, conn, error_code = await self._resolve_target_device(request)
+        if error_code:
+            return self._json_response(
+                {
+                    "ok": False,
+                    "error": error_code,
+                    "active_devices": await self.ws_server.list_active_device_ids(),
+                },
+                status=503,
+            )
+
+        try:
+            if conn is not None:
+                service = get_service(conn)
+                deleted = await service.delete_line_alert(device_id, alert_id)
+            else:
+                deleted = self._nav_db.delete_line_alert(alert_id, device_id=device_id)
+        except Exception as exc:
+            self.logger.bind(tag=TAG).warning(f"delete risk alert failed: {exc}")
+            return self._json_response({"ok": False, "error": "delete_failed"}, status=500)
+
+        if not deleted:
+            return self._json_response({"ok": False, "error": "not_found"}, status=404)
+
+        return self._json_response(
+            {
+                "ok": True,
+                "device_id": device_id,
+                "alert_id": alert_id,
+                "deleted": True,
+            }
+        )
+
+    async def handle_ws(self, request):
+        ws = web.WebSocketResponse(heartbeat=30)
+        await ws.prepare(request)
+
+        interval_s = 1.0
+        interval_arg = request.query.get("interval")
+        if interval_arg:
+            try:
+                interval_s = max(0.2, float(interval_arg))
+            except ValueError:
+                interval_s = 1.0
+
+        gps_queue = None
+        subscribed_device_key = None
+
+        try:
+            while True:
+                if gps_queue is None:
+                    conn, error_code = await self._resolve_conn(request)
+                    if error_code:
+                        await ws.send_json(
+                            {
+                                "ok": False,
+                                "error": error_code,
+                                "active_devices": await self.ws_server.list_active_device_ids(),
+                            }
+                        )
+                        await asyncio.sleep(1.0)
+                        continue
+
+                    subscribed_device_key = self._device_key(conn)
+                    gps_queue = await self.ws_server.subscribe_device_gps(subscribed_device_key)
+
+                    current_payload, _ = await self.ws_server.get_latest_gps(
+                        subscribed_device_key, max_age_s=3.0, allow_stale=True
+                    )
+                    if current_payload is not None:
+                        await ws.send_json(
+                            await self._attach_recent_alert(
+                                subscribed_device_key,
+                                current_payload,
+                            )
+                        )
+                    else:
+                        payload, error_code = await self._get_fix_payload(request)
+                        if error_code:
+                            await ws.send_json(
+                                {
+                                    "ok": False,
+                                    "error": error_code,
+                                    "active_devices": await self.ws_server.list_active_device_ids(),
+                                }
+                            )
+                        else:
+                            await ws.send_json(
+                                await self._attach_recent_alert(
+                                    subscribed_device_key,
+                                    payload,
+                                )
+                            )
+
+                try:
+                    payload = await asyncio.wait_for(gps_queue.get(), timeout=30.0)
+                    await ws.send_json(
+                        await self._attach_recent_alert(
+                            subscribed_device_key,
+                            payload,
+                        )
+                    )
+                except asyncio.TimeoutError:
+                    payload, error_code = await self._get_fix_payload(request)
+                    if error_code:
+                        await ws.send_json(
+                            {
+                                "ok": False,
+                                "error": error_code,
+                                "active_devices": await self.ws_server.list_active_device_ids(),
+                            }
+                        )
+                        if error_code == "no_active_device" and gps_queue is not None and subscribed_device_key is not None:
+                            await self.ws_server.unsubscribe_device_gps(subscribed_device_key, gps_queue)
+                            gps_queue = None
+                            subscribed_device_key = None
+                            await asyncio.sleep(interval_s)
+                    else:
+                        await ws.send_json(
+                            await self._attach_recent_alert(
+                                subscribed_device_key,
+                                payload,
+                            )
+                        )
+
+        except asyncio.CancelledError:
+            raise
+        except Exception as exc:
+            self.logger.bind(tag=TAG).warning(f"app gps websocket closed: {exc}")
+        finally:
+            if gps_queue is not None and subscribed_device_key is not None:
+                await self.ws_server.unsubscribe_device_gps(subscribed_device_key, gps_queue)
+            await ws.close()
+
+        return ws

+ 2 - 4
main/xiaozhi-server/core/api/base_handler.py

@@ -1,4 +1,5 @@
 from aiohttp import web
+
 from config.logger import setup_logging
 
 
@@ -8,7 +9,6 @@ class BaseHandler:
         self.logger = setup_logging()
 
     def _add_cors_headers(self, response):
-        """添加CORS头信息"""
         response.headers["Access-Control-Allow-Headers"] = (
             "client-id, content-type, device-id, authorization"
         )
@@ -16,9 +16,7 @@ class BaseHandler:
         response.headers["Access-Control-Allow-Origin"] = "*"
 
     async def handle_options(self, request):
-        """处理OPTIONS请求,添加CORS头信息"""
         response = web.Response(body=b"", content_type="text/plain")
         self._add_cors_headers(response)
-        # 添加允许的方法
-        response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
+        response.headers["Access-Control-Allow-Methods"] = "GET, POST, DELETE, OPTIONS"
         return response

main/xiaozhi-server/core/api/ota_handler.py → main/xingxing-server/core/api/ota_handler.py


main/xiaozhi-server/core/api/vision_handler.py → main/xingxing-server/core/api/vision_handler.py


main/xiaozhi-server/core/auth.py → main/xingxing-server/core/auth.py


main/xiaozhi-server/core/connection.py → main/xingxing-server/core/connection.py


main/xiaozhi-server/core/handle/abortHandle.py → main/xingxing-server/core/handle/abortHandle.py


main/xiaozhi-server/core/handle/helloHandle.py → main/xingxing-server/core/handle/helloHandle.py


+ 23 - 3
main/xiaozhi-server/core/handle/intentHandler.py

@@ -15,6 +15,7 @@ from core.utils.util import remove_punctuation_and_length
 from core.providers.tts.dto.dto import TTSMessageDTO, SentenceType
 
 TAG = __name__
+NAV_ALERT_ECHO_TTL_S = 12.0
 
 
 STRICT_HAZARD_MARK_PHRASES = {
@@ -72,7 +73,7 @@ async def handle_user_intent(conn: "ConnectionHandler", text):
     if await checkWakeupWords(conn, filtered_text):
         return True
 
-    if should_ignore_nav_alert_echo(filtered_text):
+    if should_ignore_nav_alert_echo(conn, filtered_text):
         conn.logger.bind(tag=TAG).info(f"忽略导航播报回采文本: {filtered_text}")
         return True
 
@@ -136,6 +137,7 @@ def match_direct_nav_command(filtered_text: str):
     text = (filtered_text or "").strip()
     if not text:
         return None
+    return None
 
     if text in {
         "开始录制路线",
@@ -179,11 +181,29 @@ def match_direct_nav_command(filtered_text: str):
     return None
 
 
-def should_ignore_nav_alert_echo(filtered_text: str) -> bool:
+def should_ignore_nav_alert_echo(conn: "ConnectionHandler", filtered_text: str) -> bool:
     text = (filtered_text or "").strip()
     if not text:
         return False
-    return text in NAV_ALERT_ECHO_PHRASES
+    recent = getattr(conn, "_recent_nav_alert_notes", None)
+    if not isinstance(recent, dict) or not recent:
+        return False
+
+    now = asyncio.get_running_loop().time()
+    matched = False
+    expired: list[str] = []
+    for raw_text, spoken_at in recent.items():
+        if now - float(spoken_at) > NAV_ALERT_ECHO_TTL_S:
+            expired.append(raw_text)
+            continue
+        _, normalized = remove_punctuation_and_length(str(raw_text))
+        if normalized == text:
+            matched = True
+
+    for raw_text in expired:
+        recent.pop(raw_text, None)
+
+    return matched
 
 
 async def dispatch_function_call(

main/xiaozhi-server/core/handle/receiveAudioHandle.py → main/xingxing-server/core/handle/receiveAudioHandle.py


main/xiaozhi-server/core/handle/reportHandle.py → main/xingxing-server/core/handle/reportHandle.py


main/xiaozhi-server/core/handle/sendAudioHandle.py → main/xingxing-server/core/handle/sendAudioHandle.py


main/xiaozhi-server/core/handle/textHandle.py → main/xingxing-server/core/handle/textHandle.py


main/xiaozhi-server/core/handle/textHandler/abortMessageHandler.py → main/xingxing-server/core/handle/textHandler/abortMessageHandler.py


main/xiaozhi-server/core/handle/textHandler/helloMessageHandler.py → main/xingxing-server/core/handle/textHandler/helloMessageHandler.py


main/xiaozhi-server/core/handle/textHandler/iotMessageHandler.py → main/xingxing-server/core/handle/textHandler/iotMessageHandler.py


main/xiaozhi-server/core/handle/textHandler/listenMessageHandler.py → main/xingxing-server/core/handle/textHandler/listenMessageHandler.py


main/xiaozhi-server/core/handle/textHandler/mcpMessageHandler.py → main/xingxing-server/core/handle/textHandler/mcpMessageHandler.py


main/xiaozhi-server/core/handle/textHandler/pingMessageHandler.py → main/xingxing-server/core/handle/textHandler/pingMessageHandler.py


main/xiaozhi-server/core/handle/textHandler/serverMessageHandler.py → main/xingxing-server/core/handle/textHandler/serverMessageHandler.py


+ 77 - 0
main/xingxing-server/core/handle/textHandler/telemetryMessageHandler.py

@@ -0,0 +1,77 @@
+import time
+from typing import Any, Dict
+
+from core.handle.textMessageHandler import TextMessageHandler
+from core.handle.textMessageType import TextMessageType
+from plugins_func.nav_copilot.geo import convert_to_wgs84, normalize_coord_type
+
+
+class TelemetryTextMessageHandler(TextMessageHandler):
+    @property
+    def message_type(self) -> TextMessageType:
+        return TextMessageType.TELEMETRY
+
+    async def handle(self, conn, msg_json: Dict[str, Any]) -> None:
+        payload = msg_json.get("payload")
+        if not isinstance(payload, dict):
+            return
+
+        gps = payload.get("gps")
+        if not isinstance(gps, dict):
+            return
+
+        try:
+            if not bool(gps.get("ok", True)):
+                return
+            lat = float(gps["lat"])
+            lon = float(gps["lon"])
+            coord_type = normalize_coord_type(gps.get("coord_type"), default="WGS84")
+            lat, lon = convert_to_wgs84(lat, lon, coord_type)
+        except (KeyError, TypeError, ValueError):
+            return
+
+        if not (-90.0 <= lat <= 90.0 and -180.0 <= lon <= 180.0):
+            return
+
+        device_id = getattr(conn, "device_id", None)
+        if not device_id:
+            return
+
+        speed_mps = gps.get("speed_mps")
+        try:
+            if speed_mps is None and gps.get("speed_kmh") is not None:
+                speed_mps = float(gps["speed_kmh"]) / 3.6
+            elif speed_mps is not None:
+                speed_mps = float(speed_mps)
+        except (TypeError, ValueError):
+            speed_mps = 0.0
+        if speed_mps is None:
+            speed_mps = 0.0
+
+        try:
+            heading_deg = float(gps.get("heading_deg", 0.0) or 0.0)
+        except (TypeError, ValueError):
+            heading_deg = 0.0
+
+        try:
+            timestamp = float(gps.get("timestamp", 0) or 0)
+        except (TypeError, ValueError):
+            timestamp = 0.0
+        if timestamp <= 0:
+            timestamp = time.time()
+
+        canonical_payload = {
+            "ok": True,
+            "device_id": device_id,
+            "lat": lat,
+            "lon": lon,
+            "speed_mps": speed_mps,
+            "heading_deg": heading_deg,
+            "timestamp": timestamp,
+            "coord_type": "WGS84",
+            "source": str(gps.get("source") or "device_ws_telemetry"),
+        }
+
+        server = getattr(conn, "server", None)
+        if server is not None:
+            await server.update_device_gps(device_id, canonical_payload)

main/xiaozhi-server/core/handle/textMessageHandler.py → main/xingxing-server/core/handle/textMessageHandler.py


+ 2 - 0
main/xiaozhi-server/core/handle/textMessageHandlerRegistry.py

@@ -5,6 +5,7 @@ from core.handle.textHandler.helloMessageHandler import HelloTextMessageHandler
 from core.handle.textHandler.iotMessageHandler import IotTextMessageHandler
 from core.handle.textHandler.listenMessageHandler import ListenTextMessageHandler
 from core.handle.textHandler.mcpMessageHandler import McpTextMessageHandler
+from core.handle.textHandler.telemetryMessageHandler import TelemetryTextMessageHandler
 from core.handle.textMessageHandler import TextMessageHandler
 from core.handle.textHandler.serverMessageHandler import ServerTextMessageHandler
 from core.handle.textHandler.pingMessageHandler import PingMessageHandler
@@ -27,6 +28,7 @@ class TextMessageHandlerRegistry:
             ListenTextMessageHandler(),
             IotTextMessageHandler(),
             McpTextMessageHandler(),
+            TelemetryTextMessageHandler(),
             ServerTextMessageHandler(),
             PingMessageHandler(),
         ]

+ 6 - 3
main/xiaozhi-server/core/handle/textMessageProcessor.py

@@ -19,15 +19,18 @@ class TextMessageProcessor:
     async def process_message(self, conn: "ConnectionHandler", message: str) -> None:
         """Route JSON text messages to the registered message handlers."""
         try:
-            # Count MCP replies and other text frames as live activity so the
-            # connection does not get closed while app-side GPS polling is active.
+            # Count MCP replies and telemetry frames as live activity so the
+            # connection does not get closed while app-side GPS streaming is active.
             conn.last_activity_time = time.time() * 1000
 
             msg_json = json.loads(message)
 
             if isinstance(msg_json, dict):
                 message_type = msg_json.get("type")
-                conn.logger.bind(tag=TAG).info(f"收到{message_type}消息:{message}")
+                if message_type == "telemetry":
+                    conn.logger.bind(tag=TAG).debug("收到 telemetry 消息")
+                else:
+                    conn.logger.bind(tag=TAG).info(f"收到{message_type}消息:{message}")
 
                 handler = self.registry.get_handler(message_type)
                 if handler:

+ 1 - 0
main/xiaozhi-server/core/handle/textMessageType.py

@@ -8,5 +8,6 @@ class TextMessageType(Enum):
     LISTEN = "listen"
     IOT = "iot"
     MCP = "mcp"
+    TELEMETRY = "telemetry"
     SERVER = "server"
     PING = "ping"

+ 5 - 0
main/xiaozhi-server/core/http_server.py

@@ -84,8 +84,13 @@ class SimpleHttpServer:
                         [
                             web.get("/app/gps/latest", self.app_gps_handler.handle_get_latest),
                             web.get("/app/gps/ws", self.app_gps_handler.handle_ws),
+                            web.get("/app/risk-alerts", self.app_gps_handler.handle_get_risk_alerts),
+                            web.post("/app/risk-alerts", self.app_gps_handler.handle_post_risk_alerts),
+                            web.delete("/app/risk-alerts/{alert_id}", self.app_gps_handler.handle_delete_risk_alert),
                             web.options("/app/gps/latest", self.app_gps_handler.handle_options),
                             web.options("/app/gps/ws", self.app_gps_handler.handle_options),
+                            web.options("/app/risk-alerts", self.app_gps_handler.handle_options),
+                            web.options("/app/risk-alerts/{alert_id}", self.app_gps_handler.handle_options),
                         ]
                     )
 

+ 0 - 0
main/xiaozhi-server/core/providers/asr/aliyun.py


Некоторые файлы не были показаны из-за большого количества измененных файлов