feat: integrate calibration extension and self-repair modules

This commit is contained in:
vee
2026-08-07 23:39:45 +08:00
parent 156b10fda1
commit 3bceb0d5a1
41 changed files with 21310 additions and 314 deletions
@@ -0,0 +1,130 @@
"""Optional MQTT 3.1.1 client used by the browser workstation."""
from __future__ import annotations
import json
import threading
import uuid
from collections.abc import Callable
from typing import Any
StateCallback = Callable[[str, str], None]
PayloadCallback = Callable[[str, str], None]
class MqttSeatPoseClient:
def __init__(
self,
host: str,
port: int,
topic: str,
on_state: StateCallback,
on_payload: PayloadCallback,
) -> None:
self.host = host
self.port = port
self.topic = topic
self.test_topic = f"n7calibration/seatpose_test/{uuid.uuid4().hex[:12]}"
self.on_state = on_state
self.on_payload = on_payload
self._client: Any = None
self._lock = threading.Lock()
def connect(self) -> None:
try:
import paho.mqtt.client as mqtt # type: ignore
except ImportError as error:
raise RuntimeError("缺少 paho-mqtt;请安装项目的 ir 可选依赖") from error
self.close()
try:
client = mqtt.Client(
callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
client_id=f"n7cal_{uuid.uuid4().hex[:12]}",
protocol=mqtt.MQTTv311,
)
except (AttributeError, TypeError):
client = mqtt.Client(client_id=f"n7cal_{uuid.uuid4().hex[:12]}")
client.on_connect = self._on_connect
client.on_disconnect = self._on_disconnect
client.on_message = self._on_message
with self._lock:
self._client = client
self.on_state("connecting", f"正在连接 {self.host}:{self.port}")
client.connect_async(self.host, self.port, keepalive=60)
client.loop_start()
def publish_test(self, payload: dict[str, Any] | None = None) -> str:
with self._lock:
client = self._client
if client is None or not client.is_connected():
raise RuntimeError("MQTT 尚未连接")
body = payload or sample_payload()
result = client.publish(self.test_topic, json.dumps(body, ensure_ascii=False), qos=0)
if result.rc != 0:
raise RuntimeError(f"MQTT 发送失败,返回码 {result.rc}")
return self.test_topic
def close(self) -> None:
with self._lock:
client, self._client = self._client, None
if client is not None:
try:
client.disconnect()
except Exception:
pass
try:
client.loop_stop()
except Exception:
pass
def _on_connect(
self,
client: Any,
_userdata: Any,
_flags: Any,
reason_code: Any,
*_: Any,
) -> None:
if int(reason_code) != 0:
self.on_state("error", f"MQTT 连接被拒绝:{reason_code}")
return
client.subscribe([(self.topic, 0), (self.test_topic, 0)])
self.on_state("subscribed", f"已订阅 {self.topic}")
def _on_disconnect(self, _client: Any, _userdata: Any, *args: Any) -> None:
reason = args[-2] if len(args) >= 2 else args[-1] if args else ""
self.on_state("disconnected", f"MQTT 已断开{f':{reason}' if reason else ''}")
def _on_message(self, _client: Any, _userdata: Any, message: Any) -> None:
try:
payload = message.payload.decode("utf-8")
except UnicodeDecodeError:
self.on_state("error", "MQTT 收到非 UTF-8 数据")
return
self.on_payload(message.topic, payload)
def sample_payload(frame: int = 1) -> dict[str, Any]:
return {
"type": "seatpose",
"frame": frame,
"timestamp_us": frame * 33_333,
"area_poses": [
{
"area": "Seat_cushion",
"axis_angle": {"valid": True, "degree": 0.0},
"current_frame_to_reference": {"valid": True, "T": [0.12, 0.04, 0.01]},
},
{
"area": "Seat_backrest",
"axis_angle": {"valid": True, "degree": 34.0},
"current_frame_to_reference": {"valid": True, "T": [0.0, 0.0, 0.0]},
},
{
"area": "Seat_leg_rest",
"axis_angle": {"valid": True, "degree": 22.0},
"current_frame_to_reference": {"valid": True, "T": [0.0, 0.0, 0.0]},
},
],
}