ec482bda51
- 删除 app/park/vendor/s2s-cloud(201 文件)与 s2s_bridge.py - 清理 app.py/config.py/routers.py/rag.py/tools.py 中 s2s 相关代码与配置 - park_config 智能体提示词配置改名 PARK_AGENT_INSTRUCTIONS(原 S2S_INSTRUCTIONS,仍被园区 AI 问答使用) - pyproject 移除 torch/transformers/scipy/soundfile/nltk/openai(均仅 s2s 使用,Linux 下 torch CUDA 依赖曾致镜像构建下载 4-6GB、耗时 300s+) - 重新生成 uv.lock(移除 torch 等 40+ 传递依赖) - 修复 app/api/schemas/admin.py 缺失 Database import(3.14 延迟注解掩盖的既有 bug,3.12 下启动必崩) - .python-version 同步为 3.12(与镜像一致)
85 lines
2.8 KiB
Python
85 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""园区子应用(app/park)—— FastAPI 子应用组装。
|
||
|
||
由 server-core/dispatcher.py 以 `/park` 前缀路由对外;独立 lifespan:
|
||
启动 MQTT hub、数据 tick 线程(sim_engine → publish_tick)。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import asyncio
|
||
import logging
|
||
import threading
|
||
from contextlib import asynccontextmanager
|
||
from pathlib import Path
|
||
|
||
from fastapi import FastAPI
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
from fastapi.staticfiles import StaticFiles
|
||
|
||
from .config import settings
|
||
from .event_bus import bus
|
||
from .mqtt import hub
|
||
from .routers import router
|
||
from .sim_engine import get_engine, sim_engine
|
||
from . import tenants
|
||
|
||
log = logging.getLogger("dpm.park")
|
||
|
||
settings.MEDIA_DIR.mkdir(parents=True, exist_ok=True)
|
||
|
||
|
||
@asynccontextmanager
|
||
async def lifespan(app: FastAPI):
|
||
# 园区表/种子由 alembic + scripts/db/seed.py 非运行态完成;此处仅启动运行时组件
|
||
bus.set_loop(asyncio.get_running_loop())
|
||
hub.start()
|
||
|
||
stop = threading.Event()
|
||
|
||
def tick_loop():
|
||
while not stop.is_set():
|
||
try:
|
||
# 多租户:逐园区 tick + 按租户发布(同步读租户列表)
|
||
for t in tenants.list_tenants_sync():
|
||
try:
|
||
snap = get_engine(t["id"]).tick()
|
||
hub.publish_tick(snap, t["id"])
|
||
except Exception as e: # noqa: BLE001
|
||
log.warning("园区 %s tick 异常: %s", t.get("id"), e)
|
||
except Exception as e: # noqa: BLE001
|
||
log.warning("园区数据 tick 异常: %s", e)
|
||
stop.wait(settings.MQTT_TICK_INTERVAL)
|
||
|
||
threading.Thread(target=tick_loop, daemon=True).start()
|
||
log.info("园区子应用 /park 启动(MQTT %s:%s 系 %s)",
|
||
settings.MQTT_HOST, settings.MQTT_PORT,
|
||
"已启用" if settings.MQTT_ENABLED else "已禁用")
|
||
try:
|
||
yield
|
||
finally:
|
||
stop.set()
|
||
hub.stop()
|
||
|
||
|
||
app = FastAPI(title="OPC 智能园区子应用", version="1.0.0", lifespan=lifespan)
|
||
|
||
# 跨域:大屏(Vite dev :1420 / Tauri tauri.localhost)与 server-core(:8090) 不同源,需放行
|
||
app.add_middleware(
|
||
CORSMiddleware,
|
||
allow_origins=["*"],
|
||
allow_methods=["*"],
|
||
allow_headers=["*"],
|
||
allow_credentials=True,
|
||
)
|
||
|
||
# 关键:内部路由本为 /api/*,挂载到 /park 下 → 外部地址 /park/api/*
|
||
app.include_router(router, prefix="/park")
|
||
|
||
# 媒体静态服务(上传/播放文件)
|
||
app.mount("/park/file", StaticFiles(directory=str(settings.MEDIA_DIR)), name="park-media")
|
||
|
||
# 管理后台静态资源(如有)
|
||
_static_dir = Path(__file__).resolve().parent / "static"
|
||
if _static_dir.exists():
|
||
app.mount("/park/static", StaticFiles(directory=str(_static_dir)), name="park-static")
|