Files

118 lines
5.7 KiB
Markdown
Raw Permalink 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.
# OPC 智能园区后端(FastAPI + MQTT
> 📍 工作区位置:`code/dpm/backend/`DPM 园区大屏后端,独立仓库)。权威说明以本文档为准;系统/运维文档见 `design/dpm/`,前端见 `code/dpm/src/`。
独立后端服务:**所有数据通过 REST API 提供**,**页面/媒体控制通过 MQTT 下发**,媒体资源由后端统一存储与返回。
## 架构
```
┌──────────────┐ REST(全部数据/媒体/设置) ┌────────────────┐
│ Tauri 大屏 │ ◄────────────────────────── │ FastAPI :10085 │
│ (React 前端) │ └───────┬────────┘
│ │ MQTT 订阅 opc/display/command │ MQTT 发布
│ 账号: dpm │ ◄────────────────────────────────────│ 账号: dpmserver
└──────┬───────┘ MQTT Broker (EMQX) │
│ 192.168.1.3:1883 │
└───────────────────────────────────────────────┘
```
- **数据**`GET /api/dashboard/snapshot`(每 2.2s 刷新,模拟引擎在后端运行)等
- **控制**`POST /api/display/command` → MQTT `opc/display/command` → 所有大屏订阅同步响应
- **媒体**:上传/列表/播放列表/`/file` 静态返回(图片/视频统一由后端托管)
- **AI**:通义千问(DashScope)+ 工具调用(切页/控制/卡片/通知,经 MQTT 广播);语音识别用阿里云 paraformer
## 启动
```bash
cd backend
uv sync # 安装依赖(fastapi/uvicorn/paho-mqtt/dashscope/jinja2 等)
cp .env.example .env # 按需修改(含 DashScope Key、MQTT 账号)
uv run python main.py # 或 .venv/bin/python main.py
```
启动后:API 与前端页面均在 `http://0.0.0.0:10085`(浏览器直接打开即大屏)。
## 环境变量(backend/.env
| 变量 | 说明 |
|---|---|
| `DPM_HOST` / `DPM_PORT`(或 `FASTAPI_HOST`/`FASTAPI_PORT` | 监听地址,默认 `0.0.0.0:10085` |
| `DPM_MQTT_HOST` / `DPM_MQTT_PORT` | MQTT broker 地址,默认读 `MQTT_BROKER_HOST/PORT` |
| `MQTT_USERNAME` / `MQTT_PASSWORD` | **服务端发布账号:dpmserver / 123456** |
| `DASHSCOPE_API_KEY` | 阿里云 DashScopeLLM + 语音识别) |
| `DPM_LLM_MODEL` | 默认 `qwen-plus` |
| `DPM_ASR_MODEL` | 默认 `paraformer-realtime-v2` |
> 前端连接配置在项目根 `.env.local`(已 gitignore):`VITE_API_BASE`、`VITE_MQTT_URL`、`VITE_MQTT_USERNAME=dpm`、`VITE_MQTT_PASSWORD=123456`。
## MQTT 主题
| 主题 | 方向 | 说明 |
|---|---|---|
| `opc/display/command` | 后端→前端 | `{cmd_id, ts, action, params}`action: `navigate` / `navigate_rel` / `play` / `pause` / `next` / `prev` / `alert` / `show_card` / `set_mode` |
| `opc/dashboard/tick` | 后端→前端 | 数据快照(`{ts, snapshot}` |
| `opc/display/ack` | 前端→后端 | 指令回执(预留) |
## 控制示例
```bash
# 切换页面(数据大屏 / 数字孪生 / AI 助手 / 媒体轮播)
curl -X POST http://localhost:10085/api/display/command \
-H "Content-Type: application/json" \
-d '{"action":"navigate","params":{"page":"/twin"}}'
# 媒体控制
curl -X POST http://localhost:10085/api/display/command \
-d '{"action":"next","params":{}}'
# 大屏通知
curl -X POST http://localhost:10085/api/display/command \
-d '{"action":"alert","params":{"title":"园区通知","content":"..."}}'
```
## 关键接口
- `GET /api/dashboard/snapshot` — 大屏全量数据快照
- `GET /api/park/companies` · `GET /api/park/zones` — 企业分布
- `POST /api/ai/chat` — AI 对话(`{messages:[{role,content}]}``{reply, tools, mqtt_published}`
- `POST /api/ai/asr` — 语音识别(multipart 上传录音 + `format` 参数)
- `GET/POST /api/playlist` · `POST /upload` · `GET /media` — 媒体与播放列表
- `POST /api/display/command` — 管理端控制指令(转 MQTT 广播)
- `GET /api/events` — SSE 兼容通道(MQTT 不可用时的前端回退)
## 说明
- 大屏数据由后端模拟引擎产生(`sim_engine.py`,与原前端 `parkData.js` 逻辑一致);前端离线时也有本地兜底
- 媒体文件存放于 `backend/media/``/file/...` 直接返回
- 安全:`backend/.env` 与根 `.env.local` 已加入 `.gitignore`,含阿里云密钥与 MQTT 凭据,勿提交
## Docker 部署(推荐)
一键构建并启动 **后端 + EMQX Broker**
```bash
# 1) (可选)配置部署参数
export MQTT_PUBLIC_HOST=192.168.1.50 # 展播端可访问的服务器局域网 IPBroker WebSocket
export EMQX_USER=dpmserver
export EMQX_PASS=你的密码
export DASHSCOPE_API_KEY=sk-xxx # AI/语音密钥(未配置时 AI 走本地规则引擎)
export DPM_S2S_ENABLED=1 # 实时语音对话开关
# 2) 构建并启动
docker compose up -d --build
# 3) 验证
curl http://192.168.1.9:10085/api/health # {"ok":true,...}
docker compose logs -f backend
```
- 端口:`10085`API/后台/媒体)、`8765`s2s 语音)、`1883/8083/18083`EMQX
- 数据卷:`dpm_media`(媒体)、`dpm_knowledge`(知识库 park.md,可热更新)、`dpm_data`(设置/播放列表)
- 展播端(Tauri 大屏)启动时经 `GET /api/config` 自动获取 MQTT WebSocket 地址
- 常用命令:`docker compose down` 停止;`docker compose up -d --build backend` 仅重建后端;
`docker compose ps` 查看状态
> 单独构建镜像:`docker build -f backend/Dockerfile -t dpm-backend .`