feat: 把 A 端的 Session/KCP/视频/控制 都收口到一个本地 daemon 进程里,Django 和输入发送端都改成通过本机 UDS HTTP 去访问它,同时补齐了观测、性能和可用性上的几个关键问题。

This commit is contained in:
2026-04-01 15:48:13 +08:00
parent b6105450a1
commit aca23e91d7
12 changed files with 242 additions and 325 deletions

View File

@@ -0,0 +1 @@
default_app_config = "control.apps.ControlConfig"

6
backend/control/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ControlConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "control"

View File

@@ -0,0 +1,51 @@
from __future__ import annotations
import sys
from pathlib import Path
from typing import Any
PROJECT_ROOT = Path(__file__).resolve().parents[2]
WORKSPACE_ROOT = PROJECT_ROOT.parent
def _load_client_api():
try:
from omnisocket_a_side.client import OmniDaemonClient, OmniDaemonError
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, OmniDaemonError
return OmniDaemonClient, OmniDaemonError
_OmniDaemonClient, OmniDaemonError = _load_client_api()
_daemon_client = _OmniDaemonClient()
def get_daemon_client():
return _daemon_client
class ControlProxyService:
def get_status(self) -> dict[str, Any]:
return get_daemon_client().get_control_status()
def send_event(
self,
*,
event_code: str,
drive_value: float = 1.0,
source: str = "django-api",
client_time_ms: int | None = None,
) -> dict[str, Any]:
return get_daemon_client().send_control_event(
source=source,
event_code=event_code,
drive_value=drive_value,
client_time_ms=client_time_ms,
)
control_service = ControlProxyService()

9
backend/control/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
urlpatterns = [
path("event/", views.control_event, name="control-event"),
path("status/", views.control_status, name="control-status"),
]

59
backend/control/views.py Normal file
View File

@@ -0,0 +1,59 @@
from __future__ import annotations
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .services import OmniDaemonError, control_service
@api_view(["GET"])
def control_status(request):
try:
return Response(control_service.get_status())
except OmniDaemonError as error:
return Response(
{
"connected": False,
"queue_depth": 0,
"last_seq_id": None,
"last_error": str(error),
"peer_id": "",
"target_peer": "",
},
status=503,
)
@api_view(["POST"])
def control_event(request):
event_code = str(request.data.get("event_code", "")).strip()
if not event_code:
return Response({"error": "event_code is required"}, status=400)
try:
drive_value = float(request.data.get("drive_value", 1.0))
except (TypeError, ValueError):
return Response({"error": "drive_value must be numeric"}, status=400)
raw_client_time_ms = request.data.get("client_time_ms")
if raw_client_time_ms in (None, ""):
client_time_ms = None
else:
try:
client_time_ms = int(raw_client_time_ms)
except (TypeError, ValueError):
return Response({"error": "client_time_ms must be an integer"}, status=400)
source = str(request.data.get("source", "django-api")).strip() or "django-api"
try:
payload = control_service.send_event(
event_code=event_code,
drive_value=drive_value,
source=source,
client_time_ms=client_time_ms,
)
except OmniDaemonError as error:
return Response({"error": str(error)}, status=503)
return Response(payload, status=200 if payload.get("accepted") else 503)