Files
server-core/dispatcher.py
T
Pine 4d4d38fe0f feat(park): 园区子应用 app/park 骨架迁入 — dispatcher /park 前缀分流
迁自 park-desktop/backend/app:config(数据目录→serverdata/park)/storage/sim_engine/
mqtt/event_bus + 精简 routers(health/settings/config/dashboard/park/companies/display/
events) + app.py 子应用(lifespan 起 MQTT hub+tick 线程, include_router prefix=/park)。
dispatcher.py 增 ROUTE_PARK_PREFIX + _prefix_app,与 app.training(/api)、app.main 分流。
pyproject 增 paho-mqtt(MQTT 必需)。AI/知识库/语音/视觉重模块延后全量迁入。
2026-08-24 16:57:27 +08:00

76 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""server-core 统一入口(dispatcher)—— 对外生产服务 opc.pinesound.cn 单入口。
按「端口专属前缀」把请求分流到对应子应用,每端口自带前缀子路由、互不冲突:
/park/* → 园区子应用(app.park,大屏/MQTT/园区数据)
/api/*、/uploads/*、/SpXvScDiDT.txt → 培训子应用(app.training,独立 data/opc.db
/auth /opc /admin /government /investor …→ 平台应用(app.main,身份 + 业务域,各端口子路由)
启动(server-core 目录):
uv run uvicorn dispatcher:app --host 0.0.0.0 --port 8090 --reload
"""
from __future__ import annotations
import logging
from app.main import app as core_app
from app.park.app import app as park_app
from app.training.main import app as training_app
logger = logging.getLogger(__name__)
ROUTE_TRAINING_PREFIXES = ("/api", "/uploads", "/SpXvScDiDT.txt")
ROUTE_PARK_PREFIX = "/park"
def _prefix_app(path: str):
"""按路径前缀匹配目标子应用;未命中返回 None 交由平台应用(app.main)。"""
if path.startswith(ROUTE_PARK_PREFIX):
return park_app
if path.startswith(ROUTE_TRAINING_PREFIXES):
return training_app
return None
class Dispatcher:
"""极简 ASGI 分发器:驱动子应用 lifespan(核心库初始化)+ 按路径路由。"""
def __init__(self, apps: list):
self._apps = apps
async def _run_lifespan(self, receive, send):
"""依次进入各子应用 lifespan(核心应用负责建表/种子),按启动/停止消息配对退出。"""
started: list = []
try:
while True:
message = await receive()
if message["type"] == "lifespan.startup":
for a in self._apps:
cm = a.router.lifespan_context(app=a)
await cm.__aenter__()
started.append(cm)
await send({"type": "lifespan.startup.complete"})
elif message["type"] == "lifespan.shutdown":
for cm in reversed(started):
await cm.__aexit__(None, None, None)
started = []
await send({"type": "lifespan.shutdown.complete"})
return
except BaseException:
for cm in reversed(started):
try:
await cm.__aexit__(None, None, None)
except Exception: # noqa: BLE001
pass
raise
async def __call__(self, scope, receive, send):
if scope["type"] == "lifespan":
await self._run_lifespan(receive, send)
return
path = scope.get("path", "")
target = _prefix_app(path) or core_app
await target(scope, receive, send)
app = Dispatcher([core_app, training_app, park_app])