Revert "feat: 把 A 端的 Session/KCP/视频/控制 都收口到一个本地 daemon 进程里,Django 和输入发送端都改成通过本机 UDS HTTP 去访问它,同时补齐了观测、性能和可用性上的几个关键问题。"
This reverts commit 38991ca9d8.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
transport:
|
transport:
|
||||||
server_addr: ""
|
server_addr: 81.70.156.140:10909
|
||||||
relay_via: 106.55.173.235:10909
|
relay_via: 106.55.173.235:10909
|
||||||
bind_ip: ""
|
bind_ip: ""
|
||||||
bind_device: ""
|
bind_device: ""
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Keyboard sender that emits control events through OmniDaemon or OmniSocket."""
|
"""Keyboard sender that emits binary control packets over OmniSocket."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -9,9 +9,8 @@ import signal
|
|||||||
import sys
|
import sys
|
||||||
import termios
|
import termios
|
||||||
import threading
|
import threading
|
||||||
import time
|
|
||||||
import tty
|
import tty
|
||||||
from typing import Dict
|
from typing import Dict, Optional
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@@ -21,55 +20,32 @@ except ImportError: # pragma: no cover - direct script execution fallback
|
|||||||
from omnisocket_control import make_control_packet
|
from omnisocket_control import make_control_packet
|
||||||
|
|
||||||
|
|
||||||
WORKSPACE_ROOT = Path(__file__).resolve().parents[3]
|
|
||||||
DEFAULT_BACKEND = "daemon"
|
|
||||||
|
|
||||||
|
|
||||||
def _load_daemon_client_api():
|
|
||||||
try:
|
|
||||||
from omnisocket_a_side.client import OmniDaemonClient
|
|
||||||
except ImportError:
|
|
||||||
python_dir = WORKSPACE_ROOT / "OmniSocketGo" / "python"
|
|
||||||
if python_dir.exists():
|
|
||||||
sys.path.insert(0, str(python_dir))
|
|
||||||
from omnisocket_a_side.client import OmniDaemonClient
|
|
||||||
return OmniDaemonClient
|
|
||||||
|
|
||||||
|
|
||||||
def _load_omnisocket_api():
|
def _load_omnisocket_api():
|
||||||
try:
|
try:
|
||||||
from omnisocket import CONTROL_DEFAULTS, Session
|
from omnisocket import CONTROL_DEFAULTS, Session
|
||||||
except ImportError as exc: # pragma: no cover - environment dependent
|
except ImportError as exc: # pragma: no cover - environment dependent
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"omnisocket is not installed. Install it before using direct transport mode."
|
"omnisocket is not installed. Install it before using "
|
||||||
|
"omnisocket_keyboard_sender.py."
|
||||||
) from exc
|
) from exc
|
||||||
return CONTROL_DEFAULTS, Session
|
return CONTROL_DEFAULTS, Session
|
||||||
|
|
||||||
|
|
||||||
class OmniSocketKeyboardSender:
|
class OmniSocketKeyboardSender:
|
||||||
"""Standalone keyboard sender for A-side control-plane testing."""
|
"""Standalone keyboard sender for OmniSocket control-plane testing."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.config: Dict[str, object] = {}
|
self.config: Dict[str, object] = {}
|
||||||
self.backend = self._resolve_backend()
|
|
||||||
self.seq_id = 0
|
self.seq_id = 0
|
||||||
self.running = False
|
self.running = False
|
||||||
self.session = None
|
self.session = None
|
||||||
self.daemon_client = None
|
self.input_thread: Optional[threading.Thread] = None
|
||||||
self.input_thread: threading.Thread | None = None
|
|
||||||
self.original_terminal_settings = None
|
self.original_terminal_settings = None
|
||||||
|
|
||||||
self._load_config()
|
self._load_config()
|
||||||
self._init_transport()
|
self._init_session()
|
||||||
self._print_help()
|
self._print_help()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _resolve_backend() -> str:
|
|
||||||
backend = os.getenv("OMNI_TRANSPORT_BACKEND", DEFAULT_BACKEND).strip().lower()
|
|
||||||
if backend not in {"daemon", "direct"}:
|
|
||||||
return DEFAULT_BACKEND
|
|
||||||
return backend
|
|
||||||
|
|
||||||
def _load_config(self) -> None:
|
def _load_config(self) -> None:
|
||||||
config_path = Path(__file__).resolve().parent / "config" / "omnisocket_demo.yaml"
|
config_path = Path(__file__).resolve().parent / "config" / "omnisocket_demo.yaml"
|
||||||
if config_path.exists():
|
if config_path.exists():
|
||||||
@@ -88,12 +64,7 @@ class OmniSocketKeyboardSender:
|
|||||||
self.peer_id = str(sender_cfg.get("peer_id", "peer-a-ctrl"))
|
self.peer_id = str(sender_cfg.get("peer_id", "peer-a-ctrl"))
|
||||||
self.target_peer = str(sender_cfg.get("target_peer", "peer-b-ctrl"))
|
self.target_peer = str(sender_cfg.get("target_peer", "peer-b-ctrl"))
|
||||||
|
|
||||||
def _init_transport(self) -> None:
|
def _init_session(self) -> None:
|
||||||
if self.backend == "daemon":
|
|
||||||
daemon_client_cls = _load_daemon_client_api()
|
|
||||||
self.daemon_client = daemon_client_cls()
|
|
||||||
return
|
|
||||||
|
|
||||||
control_defaults, session_cls = _load_omnisocket_api()
|
control_defaults, session_cls = _load_omnisocket_api()
|
||||||
self.session = session_cls()
|
self.session = session_cls()
|
||||||
self.session.connect(
|
self.session.connect(
|
||||||
@@ -107,10 +78,6 @@ class OmniSocketKeyboardSender:
|
|||||||
|
|
||||||
def _print_help(self) -> None:
|
def _print_help(self) -> None:
|
||||||
print("OmniSocket keyboard sender ready")
|
print("OmniSocket keyboard sender ready")
|
||||||
print(f"Backend: {self.backend}")
|
|
||||||
if self.backend == "daemon":
|
|
||||||
print(f"Daemon socket: {os.getenv('OMNIDAEMON_SOCKET', '/tmp/omnisocket-a-side.sock')}")
|
|
||||||
else:
|
|
||||||
print(f"Peer: {self.peer_id} -> {self.target_peer} via {self.server_addr}")
|
print(f"Peer: {self.peer_id} -> {self.target_peer} via {self.server_addr}")
|
||||||
print("Keys:")
|
print("Keys:")
|
||||||
print(" z -> pose_home")
|
print(" z -> pose_home")
|
||||||
@@ -161,11 +128,6 @@ class OmniSocketKeyboardSender:
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self._handle_ctrl_c()
|
self._handle_ctrl_c()
|
||||||
finally:
|
finally:
|
||||||
if self.daemon_client is not None:
|
|
||||||
try:
|
|
||||||
self.daemon_client.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if self.original_terminal_settings is not None:
|
if self.original_terminal_settings is not None:
|
||||||
termios.tcsetattr(
|
termios.tcsetattr(
|
||||||
sys.stdin, termios.TCSADRAIN, self.original_terminal_settings
|
sys.stdin, termios.TCSADRAIN, self.original_terminal_settings
|
||||||
@@ -224,20 +186,6 @@ class OmniSocketKeyboardSender:
|
|||||||
def _send_event(
|
def _send_event(
|
||||||
self, event_code: str, key_name: str, drive_value: float = 1.0
|
self, event_code: str, key_name: str, drive_value: float = 1.0
|
||||||
) -> None:
|
) -> None:
|
||||||
try:
|
|
||||||
if self.backend == "daemon":
|
|
||||||
assert self.daemon_client is not None
|
|
||||||
result = self.daemon_client.send_control_event(
|
|
||||||
source="keyboard-sender",
|
|
||||||
event_code=event_code,
|
|
||||||
drive_value=drive_value,
|
|
||||||
client_time_ms=int(time.time() * 1000),
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
f"sent seq={result.get('assigned_seq_id')} event={event_code} "
|
|
||||||
f"key={key_name} backend=daemon"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if self.session is None:
|
if self.session is None:
|
||||||
return
|
return
|
||||||
packet = make_control_packet(self.seq_id, event_code, drive_value)
|
packet = make_control_packet(self.seq_id, event_code, drive_value)
|
||||||
@@ -246,12 +194,8 @@ class OmniSocketKeyboardSender:
|
|||||||
self.session.send(to=self.target_peer, data=payload)
|
self.session.send(to=self.target_peer, data=payload)
|
||||||
print(
|
print(
|
||||||
f"sent seq={packet.seq_id} event={event_code} key={key_name} "
|
f"sent seq={packet.seq_id} event={event_code} key={key_name} "
|
||||||
f"bytes={len(payload)} backend=direct"
|
f"bytes={len(payload)}"
|
||||||
)
|
)
|
||||||
except Exception as error:
|
|
||||||
print(f"send failed for {event_code}: {error}")
|
|
||||||
return
|
|
||||||
|
|
||||||
if event_code == "session_quit":
|
if event_code == "session_quit":
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
"""ROS2 Joy bridge for OmniDaemon or direct OmniSocket control."""
|
"""ROS2 Joy -> OmniSocket bridge for Xbox control."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
import rclpy
|
import rclpy
|
||||||
@@ -20,48 +17,30 @@ except ImportError: # pragma: no cover - direct script execution fallback
|
|||||||
from omnisocket_control import make_control_packet
|
from omnisocket_control import make_control_packet
|
||||||
|
|
||||||
|
|
||||||
WORKSPACE_ROOT = Path(__file__).resolve().parents[3]
|
|
||||||
DEFAULT_BACKEND = "daemon"
|
|
||||||
|
|
||||||
|
|
||||||
def _load_daemon_client_api():
|
|
||||||
try:
|
|
||||||
from omnisocket_a_side.client import OmniDaemonClient
|
|
||||||
except ImportError:
|
|
||||||
python_dir = WORKSPACE_ROOT / "OmniSocketGo" / "python"
|
|
||||||
if python_dir.exists():
|
|
||||||
sys.path.insert(0, str(python_dir))
|
|
||||||
from omnisocket_a_side.client import OmniDaemonClient
|
|
||||||
return OmniDaemonClient
|
|
||||||
|
|
||||||
|
|
||||||
def _load_omnisocket_api():
|
def _load_omnisocket_api():
|
||||||
try:
|
try:
|
||||||
from omnisocket import CONTROL_DEFAULTS, Session
|
from omnisocket import CONTROL_DEFAULTS, Session
|
||||||
except ImportError as exc: # pragma: no cover - environment dependent
|
except ImportError as exc: # pragma: no cover - environment dependent
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"omnisocket is not installed. Install it before using direct transport mode."
|
"omnisocket is not installed. Install it before using "
|
||||||
|
"omnisocket_xbox_sender.py."
|
||||||
) from exc
|
) from exc
|
||||||
return CONTROL_DEFAULTS, Session
|
return CONTROL_DEFAULTS, Session
|
||||||
|
|
||||||
|
|
||||||
class OmniSocketXboxSender(Node):
|
class OmniSocketXboxSender(Node):
|
||||||
"""Subscribe to Joy messages and forward them through the selected backend."""
|
"""Subscribe to Joy messages and forward them through OmniSocket."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__("omnisocket_xbox_sender")
|
super().__init__("omnisocket_xbox_sender")
|
||||||
self.config: Dict[str, object] = {}
|
self.config: Dict[str, object] = {}
|
||||||
self.backend = self._resolve_backend()
|
|
||||||
self.seq_id = 0
|
self.seq_id = 0
|
||||||
self.last_buttons: Dict[str, int] = {}
|
self.last_buttons: Dict[str, int] = {}
|
||||||
self.last_dpad_h = 0.0
|
self.last_dpad_h = 0.0
|
||||||
self.session = None
|
self.session = None
|
||||||
self.daemon_client = None
|
|
||||||
self._last_transport_error = ""
|
|
||||||
self._last_transport_error_at = 0.0
|
|
||||||
|
|
||||||
self._load_config()
|
self._load_config()
|
||||||
self._init_transport()
|
self._init_session()
|
||||||
|
|
||||||
qos_profile = QoSProfile(
|
qos_profile = QoSProfile(
|
||||||
reliability=ReliabilityPolicy.RELIABLE,
|
reliability=ReliabilityPolicy.RELIABLE,
|
||||||
@@ -73,25 +52,17 @@ class OmniSocketXboxSender(Node):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.get_logger().info(
|
self.get_logger().info(
|
||||||
f"Forwarding {self.joy_topic} via {self.backend} "
|
f"Forwarding {self.joy_topic} -> OmniSocket "
|
||||||
f"from {self.peer_id} to {self.target_peer}"
|
f"{self.peer_id} -> {self.target_peer}"
|
||||||
|
)
|
||||||
|
self.get_logger().info(
|
||||||
|
"Buttons: A=WALKAMP X=ZERO Y=STOP START=reset"
|
||||||
)
|
)
|
||||||
self.get_logger().info("Buttons: A=WALKAMP X=ZERO Y=STOP START=reset")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _resolve_backend() -> str:
|
|
||||||
backend = os.getenv("OMNI_TRANSPORT_BACKEND", DEFAULT_BACKEND).strip().lower()
|
|
||||||
if backend not in {"daemon", "direct"}:
|
|
||||||
return DEFAULT_BACKEND
|
|
||||||
return backend
|
|
||||||
|
|
||||||
def destroy_node(self) -> bool:
|
def destroy_node(self) -> bool:
|
||||||
if self.session is not None:
|
if self.session is not None:
|
||||||
self.session.close()
|
self.session.close()
|
||||||
self.session = None
|
self.session = None
|
||||||
if self.daemon_client is not None:
|
|
||||||
self.daemon_client.close()
|
|
||||||
self.daemon_client = None
|
|
||||||
return super().destroy_node()
|
return super().destroy_node()
|
||||||
|
|
||||||
def _load_config(self) -> None:
|
def _load_config(self) -> None:
|
||||||
@@ -184,12 +155,7 @@ class OmniSocketXboxSender(Node):
|
|||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _init_transport(self) -> None:
|
def _init_session(self) -> None:
|
||||||
if self.backend == "daemon":
|
|
||||||
daemon_client_cls = _load_daemon_client_api()
|
|
||||||
self.daemon_client = daemon_client_cls()
|
|
||||||
return
|
|
||||||
|
|
||||||
control_defaults, session_cls = _load_omnisocket_api()
|
control_defaults, session_cls = _load_omnisocket_api()
|
||||||
self.session = session_cls()
|
self.session = session_cls()
|
||||||
self.session.connect(
|
self.session.connect(
|
||||||
@@ -291,19 +257,6 @@ class OmniSocketXboxSender(Node):
|
|||||||
def _send_event(
|
def _send_event(
|
||||||
self, event_code: str, key_name: str, drive_value: float = 1.0
|
self, event_code: str, key_name: str, drive_value: float = 1.0
|
||||||
) -> None:
|
) -> None:
|
||||||
try:
|
|
||||||
if self.backend == "daemon":
|
|
||||||
assert self.daemon_client is not None
|
|
||||||
result = self.daemon_client.send_control_event(
|
|
||||||
source="xbox-sender",
|
|
||||||
event_code=event_code,
|
|
||||||
drive_value=drive_value,
|
|
||||||
client_time_ms=int(time.time() * 1000),
|
|
||||||
)
|
|
||||||
self.get_logger().debug(
|
|
||||||
f"sent seq={result.get('assigned_seq_id')} event={event_code} key={key_name}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if self.session is None:
|
if self.session is None:
|
||||||
return
|
return
|
||||||
packet = make_control_packet(self.seq_id, event_code, drive_value)
|
packet = make_control_packet(self.seq_id, event_code, drive_value)
|
||||||
@@ -312,19 +265,6 @@ class OmniSocketXboxSender(Node):
|
|||||||
self.get_logger().debug(
|
self.get_logger().debug(
|
||||||
f"sent seq={packet.seq_id} event={event_code} key={key_name}"
|
f"sent seq={packet.seq_id} event={event_code} key={key_name}"
|
||||||
)
|
)
|
||||||
self._last_transport_error = ""
|
|
||||||
except Exception as error:
|
|
||||||
self._report_transport_error(str(error))
|
|
||||||
|
|
||||||
def _report_transport_error(self, message: str) -> None:
|
|
||||||
now = time.monotonic()
|
|
||||||
if (
|
|
||||||
message != self._last_transport_error
|
|
||||||
or now - self._last_transport_error_at >= 2.0
|
|
||||||
):
|
|
||||||
self.get_logger().warning(f"control send failed via {self.backend}: {message}")
|
|
||||||
self._last_transport_error = message
|
|
||||||
self._last_transport_error_at = now
|
|
||||||
|
|
||||||
|
|
||||||
def main(args: list[str] | None = None) -> None:
|
def main(args: list[str] | None = None) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user