feat: integrate calibration extension and self-repair modules
This commit is contained in:
@@ -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]},
|
||||
},
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user