Files
ECAP_Server/arch_doc/ARCHITECTURE.md
T
2026-07-27 18:28:21 +08:00

672 lines
41 KiB
Markdown
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.
# 项目架构说明
## 1. 项目概述
ECAP_Server 是一个面向 Mode A/C/S、ADS-B 与相关设备数据的数据接入、解析、存储、转发和地图显示组合系统。后端以 C++ 可执行程序运行,接入 TCP、串口、文件、DLL 和共享内存等数据源,将报文交给 SSR 协议模块解析,并通过 HTTP、WebSocket 和数据转发服务对外提供结果。前端提供 Leaflet 二维地图与 Cesium 三维地图,共用飞机、轨迹、基站和子数据源业务状态。
当前源码可确认的主要运行平台是 Windows;构建脚本也保留 Linux 分支。最终产物包括:
- `ecap_server` 后端可执行程序;
- `dll_source` 等动态数据源组件;
- Vite 生产构建生成的静态前端;
- 本地影像、Terrarium 地形和 GLB 模型资源。
最核心的子系统是:
1. 数据源和数据转发协程;
2. SSR/ADS-B/CPR 协议解析与飞机状态;
3. Drogon HTTP/WebSocket 服务;
4. Leaflet/Cesium 地图渲染;
5. 地形解码、LOS 通视分析和 Worker 调度。
关键入口:
- [`psc_main()`](../module/Local_Server_main.cpp#L56-L175)
- `module/Local_Server_main.cpp:56-175`
- [`Global::Global()`](../module/Local_Server/server/Global.cpp#L58-L145)
- `module/Local_Server/server/Global.cpp:58-145`
- [`App`](../third_party/eacp_webapp/src/App.tsx#L36-L293)
- `third_party/eacp_webapp/src/App.tsx:36-293`
## 2. 技术栈
| 层级 | 技术 | 用途 | 项目封装位置 | 关键源码 |
| -- | -- | -- | -- | -- |
| 语言 | C++20、TypeScript/TSX、JavaScript | 后端、算法、前端 | `module``third_party/SSR``third_party/eacp_webapp/src` | [`CMakeLists.txt`](../CMakeLists.txt#L5), [`package.json`](../third_party/eacp_webapp/package.json#L6) |
| 构建 | CMake、自定义 `_create/_depend/_attach_*` | 后端目标和依赖装配 | `third_party/build_infra``main.cmake` | [`main.cmake`](../main.cmake#L40-L59) |
| 后端 Web | Drogon/Trantor | HTTP、WebSocket、静态 UI | `module/Local_Server/server` | [`Global::init_web_server()`](../module/Local_Server/server/server.cpp#L136-L232) |
| 并发 | standalone Asio 1.38、C++ 协程、线程池 | 网络循环、数据处理、文件 I/O | `io_coro.*``Ucoro_Drogon_Glue.h` | [`Coro::start()`](../module/Local_Server/server/io_coro.cpp#L47-L80), [`ASIO_VERSION`](../third_party/CPP_Core/3rd/asio/asio-asio-1-38-0/include/asio/version.hpp#L21) |
| 协议 | SSR 自有 Mode A/C/S、ADS-B、CPR 实现 | 报文解析、位置和距离算法 | `third_party/SSR/SSR` | [`parse_mode_s_bin()`](../third_party/SSR/SSR/Aircraft_Info.cpp#L279), [`CPR`](../third_party/SSR/SSR/CPR/CPR.h#L12-L132) |
| 数据格式 | Psc JSON | 配置、接口、业务序列化 | `Config.h`、各模块 `toJson/fromJson` | [`Config::fromJson()`](../module/Local_Server/server/Config.h#L299-L313) |
| 存储 | SQLiteCpp、文件 | 飞机/轨迹数据和配置 | `Database.*`、配置文件 | [`database_server()`](../module/Local_Server/Data_Source/Database.cpp#L516-L522) |
| 前端 | React 18、Ant Design、Vite 6 | SPA、设置和业务面板 | `third_party/eacp_webapp` | [`package.json`](../third_party/eacp_webapp/package.json#L19-L49) |
| 二维地图 | Leaflet 1.9 | 二维地图、飞机和轨迹 | `Leaflet_Map.tsx``Aircraft.tsx` | [`Leaflet_Map`](../third_party/eacp_webapp/src/Map/Leaflet_Map.tsx#L42) |
| 三维地图 | CesiumJS 1.143 | 地球、模型、轨迹、地形和相机 | `src/Map/Cesium_*` | [`Cesium_Map`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L117) |
| 地形 | Terrarium PNG、CustomHeightmapTerrainProvider | 本地高程地形 | `Terrarium_Terrain_*` | [`Terrarium_Terrain_Provider`](../third_party/eacp_webapp/src/Map/Terrarium_Terrain_Provider.tsx#L22-L61) |
| 浏览器并发 | Web Worker、固定 Worker 池 | PNG 解码和 LOS | `Worker_Pool.ts``Los_Worker.ts` | [`Worker_Pool`](../third_party/eacp_webapp/src/Map/Worker_Pool.ts#L22-L97) |
| 日志 | Core 日志封装 | 后端运行和协议日志 | `third_party/CPP_Core``Global.cpp` | [`Global::Global()`](../module/Local_Server/server/Global.cpp#L58) |
| 测试 | GoogleTest、TypeScript、ESLint、Vite build | 后端单测与前端静态验证 | SSR 测试、前端 scripts | [`CPR_TEST.cpp`](../third_party/SSR/SSR/CPR/CPR_TEST.cpp#L1), [`package.json`](../third_party/eacp_webapp/package.json#L6-L12) |
| 部署 | Drogon 静态资源、Cesium runtime copy | 后端托管生产 UI | `vite.config.js``server.cpp` | [`cesiumAssetsPlugin()`](../third_party/eacp_webapp/vite.config.js#L21-L53) |
## 3. 仓库目录结构
| 目录 | 职责 | 类型与关系 |
| -- | -- | -- |
| `module/Local_Server` | 主服务、配置、数据源、数据转发、飞机 VTO、HTTP/WebSocket | 项目核心源码,依赖 SSR、Core、Drogon、Asio |
| `module/dll_source` | 可由主服务加载的数据源 DLL | 独立构建目标,主程序构建依赖它 |
| `module/mlat_source``module/Radarcape_Core` | MLAT 和设备相关能力 | 由主服务按配置调用 |
| `third_party/SSR` | Mode A/C/S、ADS-B、CPR 算法和状态对象 | 深度集成的第三方/项目内算法库 |
| `third_party/CPP_Core` | 日志、网络、协程、JSON 等基础能力 | 多模块基础依赖 |
| `third_party/eacp_webapp` | React、Leaflet、Cesium 前端及模型 | 生产构建由后端静态托管 |
| `third_party/build_infra` | CMake 封装、工具链和依赖装配 | 顶层构建基础设施 |
| `config` | 源配置模板 | 构建后复制到目标目录 |
| `data` | 运行数据和数据库 | 后端运行期资源 |
| `arch_doc` | 架构和技术难点文档 | 本文档输出目录 |
构建输出、`node_modules`、IDE 缓存和生成文件不属于架构源码;它们只在验证生产资源和实际运行配置时使用。
## 4. 构建与启动结构
顶层 CMake 先加载构建基础设施、Core 和 SSR,再装配 ECAP 目标。`ecap_server` 关联整个 `Local_Server` 源目录、SSR、Drogon、SQLiteCpp、zlib 和 libarchive,并在构建后复制配置。
```mermaid
flowchart LR
CMake[CMakeLists.txt] --> Infra[build_infra]
CMake --> Core[CPP_Core]
CMake --> SSR[SSR interface target]
CMake --> Main[main.cmake]
Main --> Server[ecap_server]
Main --> DLL[dll_source]
SSR --> Core
Server --> SSR
Server --> Drogon[Drogon]
Server --> SQLite[SQLiteCpp]
Server --> Archive[zlib/libarchive]
Server --> DLL
Vite[Vite build] --> WebRoot[wwwroot]
WebRoot --> Server
```
证据:
- [`CMakeLists.txt` 的包含顺序](../CMakeLists.txt#L8-L19)
- `CMakeLists.txt:8-19`
- [`ecap_server` 目标装配](../main.cmake#L40-L59)
- `main.cmake:40-59`
- [`ecap_server` 对 `dll_source` 的依赖](../main.cmake#L132)
- `main.cmake:132`
- [`Vite` 生产输出和 Cesium 资源复制](../third_party/eacp_webapp/vite.config.js#L21-L53)
- `third_party/eacp_webapp/vite.config.js:21-53`
后端启动顺序是:
1. `main()` 进入 `psc_main()`
2. 创建 `Global`,加载配置和模块;
3. 初始化 Drogon 路由;
4. 启动 Web 服务线程;
5. 启动 `Coro` 的 Asio 网络线程和数据处理线程池;
6. 主循环清理超时飞机并维持设备状态;
7. 退出时先停协程和设备,再销毁全局对象。
开发模式由 Vite 代理后端 `/api``/map``/tiles``/ws`;生产模式由 Drogon 提供 Vite 输出及 `/ui/cesium``/ui/model` 静态资源。
## 5. 总体架构
```mermaid
flowchart LR
Input[TCP/串口/文件/DLL/共享内存] --> Source[Data_Source]
Source --> Parse[Data_Source_Handler]
Parse --> SSR[SSR/ADS-B/CPR]
SSR --> Aircraft[Aircraft_Info/轨迹]
Parse --> Feed[Data_Feed]
Aircraft --> DB[DataBase]
DB --> WS[Aircraft WebSocket]
Config[Config JSON] --> Source
Config --> HTTP[Drogon HTTP]
Tiles[本地影像/地形] --> HTTP
HTTP --> Frontend[React 业务状态]
WS --> Frontend
Frontend --> Leaflet[Leaflet 2D]
Frontend --> Cesium[Cesium 3D]
Tiles --> Terrain[Terrarium Worker]
Terrain --> Cesium
Frontend --> LOS[LOS Worker Pool]
LOS --> Cesium
```
依赖方向以数据接入、协议域对象、服务接口、前端业务模型、渲染适配器为主。Leaflet 和 Cesium 不应成为后端业务对象的依赖;统一飞机模型不运行时导入 Cesium。Cesium 专属坐标、Entity、Worker 和相机状态停留在三维适配层。
## 6. 核心模块说明
### 6.1 程序和全局运行时
#### 职责
创建全局配置、服务、设备和数据源对象,控制后端启动与退出。
#### 核心类型和函数
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `psc_main()` | 函数 | 后端主流程 | 进程全生命周期 | [`psc_main()`](../module/Local_Server_main.cpp#L56-L175) |
| `Global` | 单例类 | 配置和主要模块所有者 | 主流程创建、退出销毁 | [`Global`](../module/Local_Server/server/Global.h#L148-L168) |
| `Global::init_web_server()` | 函数 | 注册路由并启动 Web 配置 | `Global` 初始化期 | [`init_web_server()`](../module/Local_Server/server/server.cpp#L136-L232) |
输入是配置文件和运行参数,输出是持续运行的服务与模块集合。异常处理包含顶层退出路径和日志;部分初始化失败会阻止 `init_ok`
### 6.2 配置系统
#### 职责
以一个 JSON 配置对象管理数据源、地图资源、二维/三维视图、Cesium 画质和模型元数据。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `Map_Tile_Config` | 结构体 | 瓦片目录、布局、Y 轴、投影和层级 | 配置生命周期 | [`Map_Tile_Config`](../module/Local_Server/server/Config.h#L114-L135) |
| `Map_View_Config` | 结构体 | 2D/3D 图层、层级和相机 | 配置生命周期 | [`Map_View_Config`](../module/Local_Server/server/Config.h#L164-L172) |
| `Cesium_Graphics_Config` | 结构体 | 画质、地形、日照和 LOS | 配置生命周期 | [`Cesium_Graphics_Config`](../module/Local_Server/server/Config.h#L173-L195) |
| `Map_Model_Config` | 结构体 | 飞机、基站、设备、轨迹点模型 | 配置生命周期 | [`Map_Model_Config`](../module/Local_Server/server/Config.h#L208-L262) |
| `Config::fromJson/toJson` | 函数 | 单一配置解析和序列化 | 启动加载、接口保存 | [`fromJson()`](../module/Local_Server/server/Config.h#L299-L313), [`toJson()`](../module/Local_Server/server/Config.h#L315-L333) |
地图接口只公开前端所需元数据,不公开本机目录。配置更新由后端接口写回同一配置对象和文件。
### 6.3 数据源与数据转发
#### 职责
抽象不同输入介质,读取原始数据,在线程池解析,再向数据库、飞机状态和数据转发模块发布。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `Data_Source` | 基类 | 输入源、状态和显示配置 | 配置创建到停用/退出 | [`Data_Source`](../module/Local_Server/Data_Source/Data_Source.h#L121) |
| `Data_Source_Handler` | 基类 | 切包、解析、业务分发 | 随 Data Source | [`Data_Source_Handler`](../module/Local_Server/Data_Source/Data_Source_Handler.h#L14-L19) |
| `Data_Feed` | 基类 | 向 TCP/UDP 等客户端转发 | 配置创建到退出 | [`Data_Feed`](../module/Local_Server/Data_Feed/Data_Feed.h#L16) |
| `Coro` | 单例类 | Asio 网络循环和处理线程池 | 服务运行期 | [`Coro`](../module/Local_Server/server/io_coro.h#L17-L26) |
异步模型见第 10 节。主要失败路径包括输入断开、协议切包失败、解析异常和输出连接失败。`With_Loop_Coro` 状态机负责打开、循环和关闭。
### 6.4 SSR、ADS-B、CPR 和飞机状态
#### 职责
解析 Mode S 报文,维护每架飞机的消息、空中/地面轨迹和派生属性;CPR 负责位置解码、球面距离和无线电视距范围。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `Data_Source_Interface` | 接口 | SSR 对宿主数据源的最小依赖 | 随数据源 | [`Data_Source_Interface`](../third_party/SSR/SSR/export.h#L38-L45) |
| `parse_mode_s_bin()` | 函数 | Mode S 主解析入口 | 每条报文 | [`parse_mode_s_bin()`](../third_party/SSR/SSR/Aircraft_Info.cpp#L279-L303) |
| `Aircraft_Info` | 类 | 飞机状态和轨迹所有者 | ICAO 出现到超时删除 | [`Aircraft_Info`](../third_party/SSR/SSR/Aircraft_Info.h#L44-L101) |
| `CPR::surface_position()` | 函数 | 基于参考站的地面 CPR 解码 | 地面位置报文 | [`surface_position()`](../third_party/SSR/SSR/CPR/CPR.cpp#L247-L265) |
| `CPR::in_radio_line_of_sight_range()` | 函数 | 球面距离与理论视距过滤 | 位置候选校验 | [`in_radio_line_of_sight_range()`](../third_party/SSR/SSR/CPR/CPR.cpp#L299-L303) |
`Data_Source_Handler` 将数据源中的基站位置和空中/地面约束传入 SSR,而不是让 SSR 反向依赖服务配置。
### 6.5 数据库与实时接口
#### 职责
提供飞机/轨迹查询,并维护按 WebSocket 连接隔离的版本和轨迹订阅状态。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `Aircraft_Stream_Client_State` | 连接状态 | 保存各数据源版本和订阅 | WebSocket 连接期 | [`Aircraft_Stream_Client_State`](../module/Local_Server/Data_Source/Database.cpp#L98-L103) |
| `aircraft_stream_source_update_json()` | 函数 | 构造单数据源增量 | 每次推送 | [`aircraft_stream_source_update_json()`](../module/Local_Server/Data_Source/Database.cpp#L156-L207) |
| `get_visible_aircraft_snapshot()` | 函数 | 点数和基站范围过滤 | 查询/推送时 | [`get_visible_aircraft_snapshot()`](../module/Local_Server/Data_Source/Database.cpp#L360-L407) |
| `register_aircraft_stream_ws()` | 函数 | 注册流和周期推送 | 服务生命周期 | [`register_aircraft_stream_ws()`](../module/Local_Server/Data_Source/Database.cpp#L283-L289) |
前端按 ICAO 返回 `change_version` 和轨迹位置,后端只发送变化、删除和被监控轨迹。全部监控与手动 ICAO 集合是不同状态。
### 6.6 HTTP、瓦片与资源配置
#### 职责
按有序目录查找影像和地形瓦片,通过异步文件读取返回 GET/HEAD、缓存头和条件响应,同时提供公开资源元数据。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `Tile_Path_Resolver` | 类 | 按布局和目录优先级定位文件 | 服务生命周期 | [`Tile_Path_Resolver`](../module/Local_Server/server/tiles.cpp#L162-L200) |
| `Tile_Source` | 类 | 查找、状态、读取和响应元数据 | 服务生命周期 | [`Tile_Source`](../module/Local_Server/server/tiles.cpp#L202-L295) |
| `register_tile_handler()` | 函数 | GET/HEAD、304、404 和回调桥接 | 路由注册期 | [`register_tile_handler()`](../module/Local_Server/server/tiles.cpp#L297-L324) |
| `Global::init_tiles()` | 函数 | 创建影像、Google 影像、地形源 | 服务初始化期 | [`init_tiles()`](../module/Local_Server/server/tiles.cpp#L589-L599) |
多目录严格按列表顺序查询,第一个普通文件获胜;不扫描全目录、不预加载瓦片。文件查找、`status`、打开和读取整体通过 `to_drogon()` 运行在专用 Asio 执行器,再回到 Drogon 事件循环完成一次回调。
### 6.7 前端统一业务状态
#### 职责
`Data_Source` 保存子数据源、基站、飞机集合、2D/3D 显示配置和监控模式;`Aircraft_Model` 保存与渲染框架无关的飞机/轨迹状态。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `Data_Source` | 类 | 单数据源前端状态所有者 | 配置加载到页面卸载 | [`Data_Source`](../third_party/eacp_webapp/src/Data_Source/Data_Source.tsx#L68-L145) |
| `Aircraft_Model` | 类 | 统一飞机位置、姿态、属性和轨迹 | 飞机出现到删除 | [`Aircraft_Model`](../third_party/eacp_webapp/src/Map/Aircraft_Model.tsx#L22-L75) |
| `Aircraft_Stream_Client` | 类 | WebSocket 重连、订阅和增量应用 | SPA 生命周期 | [`Aircraft_Stream_Client`](../third_party/eacp_webapp/src/Data_Source/Data_Source.tsx#L483-L573) |
Leaflet 的 `Aircraft` 包装统一模型,Cesium 直接读取同一对象。Cesium 依赖只存在于三维适配器。
### 6.8 Leaflet 二维渲染
#### 职责
维持原有二维地图、瓦片层、飞机图标、轨迹线和右键交互。
核心入口是 [`Leaflet_Map`](../third_party/eacp_webapp/src/Map/Leaflet_Map.tsx#L42-L252) 和 [`Aircraft`](../third_party/eacp_webapp/src/Map/Aircraft.tsx#L196-L580)。二维对象由 `Data_Source.aircraftMap` 中的同一飞机实例驱动;轨迹监控状态来自共享数据源状态。
### 6.9 Cesium 三维渲染
#### 职责
创建/销毁 Viewer,增量映射数据源、飞机、轨迹、基站和设备,接入影像、地形、模型、相机、LOS 与交互。
核心入口:
- [`Cesium_Map.load_map()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L247-L298)
- `third_party/eacp_webapp/src/Map/Cesium_Map.tsx:247-298`
- [`sync_data_sources()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L714-L767)
- `third_party/eacp_webapp/src/Map/Cesium_Map.tsx:714-767`
- [`sync_base_station()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L768-L801)
- `third_party/eacp_webapp/src/Map/Cesium_Map.tsx:768-801`
- [`sync_aircraft()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L1188-L1209)
- `third_party/eacp_webapp/src/Map/Cesium_Map.tsx:1188-1209`
- [`on_un_mount()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L190-L245)
- `third_party/eacp_webapp/src/Map/Cesium_Map.tsx:190-245`
飞机使用 Model Entity,轨迹线使用 Polyline Entity,普通轨迹点使用 `PointPrimitiveCollection`,选中特殊点才使用模型 Entity。基站底座是地形到设备高度的世界空间模型;设备位于顶部,承担选择和完整信息。基站纵向比例由真实架设高度确定,横向显示大小通过模型节点变换控制。
### 6.10 地形、LOS 与相机
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
| -- | -- | -- | -- | -- |
| `Terrarium_Terrain_Provider` | 适配器 | Cesium Heightmap Provider 和缓存 | Viewer 生命周期 | [`Terrarium_Terrain_Provider`](../third_party/eacp_webapp/src/Map/Terrarium_Terrain_Provider.tsx#L22-L61) |
| `Terrarium_Terrain_Worker` | Worker | fetch、PNG 解码、RGB 转高度 | Provider 生命周期 | [`decode_terrarium_png()`](../third_party/eacp_webapp/src/Map/Terrarium_Terrain_Worker.ts#L53-L72) |
| `Worker_Pool` | 调度器 | 固定并行度和 FIFO 任务 | Cesium 生命周期 | [`Worker_Pool`](../third_party/eacp_webapp/src/Map/Worker_Pool.ts#L22-L97) |
| `Los_Worker` | Worker | ECEF 视线和地形采样 | LOS Pool 生命周期 | [`calculate_los()`](../third_party/eacp_webapp/src/Map/Los_Worker.ts#L45-L91) |
| `Cesium_Camera_Control` | 控制器 | 地表、地心轨道、自由观察 | Viewer 生命周期 | [`Cesium_Camera_Control`](../third_party/eacp_webapp/src/Map/Cesium_Camera_Control.ts#L12-L177) |
## 7. 核心业务对象关系
```mermaid
classDiagram
class Data_Source {
Base_Station base_station
Map aircraftMap
Set manual_track_icao_set
bool monitor_all_aircraft_mode
map2d
map3d
}
class Aircraft {
Aircraft_Model data_model
LeafletMarker marker
TrackPath track
}
class Aircraft_Model {
id
longitude
latitude
altitude
orientation
trackPoints
}
class Cesium_Aircraft_Record {
Model Entity
Polyline Entity
PointPrimitive records
LOS Entity
}
class Base_Station {
position
range
maxAircraft
}
class Cesium_Base_Record {
Base world model
Device Entity
Range polylines
}
Data_Source *-- Base_Station
Data_Source *-- Aircraft
Aircraft *-- Aircraft_Model
Aircraft_Model --> Cesium_Aircraft_Record
Base_Station --> Cesium_Base_Record
```
业务主体是 `Data_Source`、飞机和基站状态;Cesium Entity、Primitive 和 Leaflet Layer 只是显示对象。一个飞机对应一个统一模型,同时可映射为 Leaflet Marker/Polyline 和 Cesium Model/Polyline/PointPrimitive。设备 Entity 是三维中可点击的基站业务入口,底部基站模型只表现物理支撑,不重复持有业务状态。
创建和销毁关系:
- WebSocket 增量创建或删除 `Data_Source.aircraftMap` 中的飞机;
- Leaflet 和 Cesium 按当前页面适配这些对象;
- Cesium 用 key 到 record 的 Map 复用 Entity
- 数据源或飞机删除时,渲染适配器同步删除关联轨迹、LOS 和模型;
- Viewer 卸载时统一销毁 Worker、监听器、定时器和 Cesium 资源。
## 8. 运行时生命周期
```mermaid
sequenceDiagram
participant Main as psc_main
participant Global
participant Coro
participant Source as Data_Source
participant SSR
participant DB
participant WS
participant React
participant Map as Leaflet/Cesium
Main->>Global: 创建并加载配置
Global->>DB: 注册 HTTP/WS
Main->>Coro: start()
Coro->>Source: 启动输入协程
Source->>SSR: parse_mode_s_bin
SSR-->>DB: 更新飞机/轨迹
DB-->>WS: 版本增量
WS-->>React: source delta
React->>Map: 请求增量同步
alt 进入 3D
React->>Map: lazy import + load_map
Map->>Map: 创建 Viewer/Worker/Provider
else 离开 3D
React->>Map: on_un_mount
Map->>Map: 清理 Viewer/Worker/监听器
end
Main->>Coro: stop()
Main->>Global: destroy()
```
`/map3d` 使用动态导入,未进入三维页面时不会执行 Viewer 初始化。WebGL 预检查失败会禁用三维入口并返回二维页面。对应源码:
- [`React.lazy()` 和 WebGL 检查](../third_party/eacp_webapp/src/App.tsx#L23-L26)
- `third_party/eacp_webapp/src/App.tsx:23-26`
- [`/map` 与 `/map3d` 路由](../third_party/eacp_webapp/src/App.tsx#L224-L268)
- `third_party/eacp_webapp/src/App.tsx:224-268`
## 9. 数据流
### 实时数据流
```text
输入字节
→ Data_Source 异步读取
→ process_data 线程池切包/解析
→ SSR Aircraft_Info 和轨迹
→ DataBase 可见性过滤和版本比较
→ /ws/aircraft_stream 增量
→ Data_Source.aircraftMap
→ Leaflet/Cesium 渲染记录
→ 屏幕
```
WebSocket 消息是 JSON;前端保留每架飞机版本和最后轨迹大小。业务状态在前端对象中原地增量更新,Cesium 对象按 ID 复用,不在每次消息后重建 Viewer。
### 用户交互流
```text
点击飞机/设备/轨迹点
→ Cesium/Leaflet picking
→ 找到 Data_Source 或 Aircraft_Model
→ 更新 active_aircraft/监控状态
→ 显示信息、轨迹或相机行为
```
基站范围辅助几何设置为忽略 picking,设备 Entity 才绑定完整基站信息。相关代码见 [`create_base_station_record()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L802-L892)。
### 地形与通视分析流
```text
基站设备位置 + 飞机位置
→ LOS generation/request ID
→ Worker Pool
→ ECEF 直线采样
→ Terrarium 瓦片 fetch/cache/decode
→ 净空和首次遮挡结果
→ 丢弃过期 generation
→ 更新飞机 LOS 状态和连线
```
LOS Worker 接收普通数字结构,不传递 Cesium Viewer 或类实例。高度缓存位于各 Worker 内,返回结果会检查当前 generation,避免旧位置覆盖新位置。
### 配置更新流
```text
设置表单临时值
→ HTTP PUT/POST
→ Global 中单一配置对象
→ config.json
→ 前端事件通知
→ 当前渲染器应用
```
地图资源接口只返回 URL、层级、投影、Y 轴和编码。模型上传由后端保存并更新模型 URL 和内置尺寸/初始角度配置。
## 10. 并发、异步和调度模型
```mermaid
flowchart TB
Main[主线程/进程控制] --> Asio[Asio io_context 单网络线程]
Asio --> Pool[process_data 线程池]
Drogon[Drogon/Trantor 事件循环] --> TilePool[Ucoro Asio 专用线程]
Browser[浏览器主线程] --> CesiumWorkers[Cesium 内置 Workers]
Browser --> TerrainWorker[Terrarium Decode Worker]
Browser --> LosPool[LOS Worker Pool]
WS[WebSocket 事件] --> Browser
Browser --> RAF[Cesium requestRender/postRender]
```
后端:
- `Coro::start()` 创建一个 `io_context` 运行线程和按硬件并发度计算的 `process_data` 线程池;
- 数据源在 `io_context` 读取,随后切换到 `process_data` 解析,再返回网络执行器;
- 无等待模式使用每个数据源自己的 strand 串行化解析;
- 瓦片 HTTP 桥接使用独立 Asio 线程池,整个文件系统查找和读取不在 Drogon 网络线程执行;
- `co_await` 只表达挂起点,实际线程由当前 executor 决定。
浏览器:
- React、WebSocket 消息处理、Cesium API 调用和 UI 在主线程;
- Terrarium PNG 解码在独立 Worker
- LOS 使用有上限的 Worker 池,`Promise` 只是调用和结果接口;
- Cesium 自身还会使用其 runtime Workers
- `sync_data_sources()` 先生成任务,再以最多 10 项或约 8 ms 的片段消费;
- `requestRenderMode` 降低静止场景持续渲染成本;
- Worker Pool 队列当前没有最大长度和任务取消,旧结果由实体 generation 淘汰。
关键证据:
- [`Data_Source::loop_coro()`](../module/Local_Server/server/io_coro.cpp#L206-L299)
- `module/Local_Server/server/io_coro.cpp:206-299`
- [`to_drogon()`](../module/Local_Server/server/Ucoro_Drogon_Glue.h#L102-L110)
- `module/Local_Server/server/Ucoro_Drogon_Glue.h:102-110`
- [`Cesium` 分片同步](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L707-L767)
- `third_party/eacp_webapp/src/Map/Cesium_Map.tsx:707-767`
- [`Worker_Pool.dispatch()`](../third_party/eacp_webapp/src/Map/Worker_Pool.ts#L67-L97)
- `third_party/eacp_webapp/src/Map/Worker_Pool.ts:67-97`
## 11. Cesium 渲染架构
Viewer 在组件挂载后创建,在卸载时销毁。生产构建通过 `CESIUM_BASE_URL=/ui/cesium/` 加载 Workers、Assets、ThirdParty 和 Widgets;模型来自 `/ui/model`
渲染对象选择:
| 业务对象 | Cesium 对象 | 更新方式 |
| -- | -- | -- |
| 飞机 | Model Entity + Label | ID Map 增量更新 |
| 轨迹线 | Polyline Entity | 更新位置数组 |
| 普通轨迹点 | PointPrimitiveCollection | 按轨迹序号增量增加/裁剪 |
| 选中/特殊轨迹点 | Model Entity | 只为特殊状态创建 |
| 基站底座 | Model Entity | 地形底部、世界空间高度 |
| 设备 | Model Entity + Label | 顶部位置、可点击 |
| 探测范围 | 预创建 Polyline Entity 集合 | 更新位置和 show |
| LOS | Polyline/状态显示 | Worker 结果更新 |
`sync_data_sources()` 会遍历当前启用数据源和飞机,这是已确认的全量发现阶段;已有记录由 Map 复用,重任务由分片队列消费。是否在目标规模下仍有长任务,需要 Chrome Performance 验证。
地形 Provider 使用 Web Mercator、配置层级和 XYZ/TMS 规则。2D 页面不加载 Cesium3D 页面根据设置启用 Terrarium。关闭地形或卸载 Viewer 时会销毁 Provider 和 Worker。
基站/设备当前空间语义:
- 地形采样值是基站模型底部;
- 配置设备高度是设备模型位置;
- 基站模型纵向比例由两者高度差与模型原始高度计算;
- “基站显示大小”只调整横向尺寸,不破坏顶部高度;
- 设备可按子数据源选择固定屏幕尺寸或普通世界尺寸;
- 完整基站信息绑定到设备,底座忽略 picking。
实现见 [`sync_base_station()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L768-L801)、[`create_base_station_record()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L802-L892) 和 [`base_station_heights()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L1082-L1092)。
## 12. 真正有难度的技术点
### 多执行器数据源协程
**难度等级:**
**所属模块:** Local_Server 数据源
**解决的问题:** 网络读取不能被协议解析阻塞,同时单数据源消息顺序必须稳定。
**为什么困难:** 同一逻辑跨 `io_context`、线程池和 strand,退出时还要等待未完成任务。
**当前实现:** 输入协程读取后切到 `process_data`;等待模式直接 `co_await`,无等待模式向 strand 投递;再回网络 executor。
**关键算法或状态机:** `With_Loop_Coro` 的打开、循环、关闭状态。
**数据流:** 输入字节 → 切包 → SSR → Feed/DB。
**线程与异步边界:** [`Data_Source::loop_coro()`](../module/Local_Server/server/io_coro.cpp#L206-L299)。
**性能成本:** executor 切换、任务排队和 JSON/协议解析。
**边界条件:** 数据源断开、退出、处理速度落后。
**失败方式:** 顺序破坏、停止等待、线程池积压。
**现有风险:** 队列长度没有统一可观测指标。
**可改进方向:** 增加每源队列深度、耗时和丢弃策略指标。
### WebSocket 版本增量与轨迹订阅
**难度等级:**
**所属模块:** Database、前端 Data_Source
**解决的问题:** 避免周期发送全部飞机和全部轨迹。
**为什么困难:** 每个连接、数据源、ICAO 都有版本和轨迹游标;还需区分全部监控和手动监控。
**当前实现:** 连接状态保存版本,前端订阅回传版本和轨迹大小,后端生成变化与删除。
**关键算法或状态机:** 连接状态 + per-source delta。
**数据流:** Snapshot → version compare → delta → apply/delete。
**线程与异步边界:** Drogon 事件循环和浏览器 WebSocket。
**关键源码:** [`Database.cpp`](../module/Local_Server/Data_Source/Database.cpp#L92-L289)、[`Aircraft_Stream_Client`](../third_party/eacp_webapp/src/Data_Source/Data_Source.tsx#L483-L573)。
**性能成本:** 周期快照、版本 Map 比较和 JSON 序列化。
**边界条件:** 重连、飞机删除、监控模式切换。
**失败方式:** 版本失配造成漏更新或重复轨迹。
**现有风险:** 后端持锁遍历并发送连接,需要压力测试。
**可改进方向:** 锁内只构造待发送项,锁外发送;增加队列和消息尺寸指标。
### Terrarium 地形与 LOS Worker
**难度等级:**
**所属模块:** Cesium 地形、LOS
**解决的问题:** 在不阻塞 UI 的情况下解码 PNG 高程并计算基站到飞机的地形通视。
**为什么困难:** 涉及 Web Mercator、XYZ/TMS、ECEF、地球曲率、异步瓦片、缓存和过期结果。
**当前实现:** Terrain Worker 解码 HeightmapLOS Worker 自行 fetch/cache/decode 并沿 ECEF 线采样。
**关键算法或状态机:** Terrarium `R*256+G+B/256-32768`generation 淘汰旧结果。
**数据流:** 普通数字协议 → Worker → Float32Array/LOS 结果。
**线程与异步边界:** 主线程只调度,解码和 LOS 在 Worker。
**关键源码:** [`Terrarium_Terrain_Worker`](../third_party/eacp_webapp/src/Map/Terrarium_Terrain_Worker.ts#L18-L72)、[`Los_Worker`](../third_party/eacp_webapp/src/Map/Los_Worker.ts#L36-L234)。
**性能成本:** 网络请求、PNG 解码、每 Worker 独立缓存和采样次数。
**边界条件:** 404、缺少 OffscreenCanvas、层级上限、卸载。
**失败方式:** 队列积压、缓存重复、旧 LOS 覆盖新位置。
**现有风险:** Worker Pool 队列无硬上限。
**可改进方向:** 按飞机合并待处理任务并增加队列背压。
更多难点详见 [项目技术难点说明](./TECHNICAL_HARD_POINTS.md)。
## 13. 性能架构
| 热点 | 状态 | 源码证据 | 验证方法 |
| -- | -- | -- | -- |
| `sync_data_sources()` 遍历全部当前对象 | 已确认存在,但对象记录复用且任务分片 | [`sync_data_sources()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L714-L767) | Chrome Performance,记录单次任务数和耗时 |
| 飞机 Entity 每次重建 | 已确认正常更新路径不存在;Map 复用记录 | [`sync_aircraft()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L1188-L1209) | 统计 Entity add/remove |
| 普通轨迹点全部使用 GLB | 已确认不存在;使用 PointPrimitive | [`create_aircraft_record()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L1211) | Cesium Inspector、Primitive 数量 |
| React 消息导致 Viewer 重建 | 已确认正常增量消息不会重建 Viewer | [`request_sync_data_sources()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L707-L713) | React Profiler |
| WebSocket 全量飞机 | 已确认已改为版本增量 | [`aircraft_stream_source_update_json()`](../module/Local_Server/Data_Source/Database.cpp#L156-L207) | 抓取消息尺寸和 change_list |
| LOS 对所有飞机无条件执行 | 需要结合运行配置和监控模式采样验证 | [`update_aircraft_occlusion()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L1320) | Worker 任务计数/秒 |
| 地形 PNG 主线程解码 | 已确认不存在于 Provider/LOS 路径 | [`Terrarium_Terrain_Worker`](../third_party/eacp_webapp/src/Map/Terrarium_Terrain_Worker.ts#L53) | Performance 主线程长任务 |
| Worker 队列无界 | 已确认 | [`Worker_Pool.run()`](../third_party/eacp_webapp/src/Map/Worker_Pool.ts#L38-L50) | 暴露 queue length 和等待时间 |
| 瓦片文件读取阻塞 Drogon 线程 | 已确认不存在;整体投递专用 Asio | [`register_tile_handler()`](../module/Local_Server/server/tiles.cpp#L297-L324) | 并发 HTTP 延迟和线程采样 |
| 瓦片读取明显串行 | 源码推断:专用池大小为 1,需要压力测试确认影响 | [`Ucoro_Drogon_Glue`](../module/Local_Server/server/Ucoro_Drogon_Glue.h#L13-L17) | 32/64 并发瓦片吞吐 |
| 后端 WebSocket 锁内发送 | 已确认调用位置,实际阻塞程度待验证 | [`push_aircraft_stream_updates()`](../module/Local_Server/Data_Source/Database.cpp#L227-L230) | 慢客户端压力测试和锁等待 |
| 每帧高成本回调 | 未发现每帧地形解码或全量同步;性能面板 postRender 仍需 Profile | [`postRender` 注册](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L525) | Performance flame chart |
当前项目重点核查项:
| 核查项 | 状态 |
| -- | -- |
| 进入三维模式完整调用链 | 已确认 |
| `sync_data_sources()` 全量发现 | 已确认存在,后续分片 |
| 飞机、基站、轨迹和模型重复创建 | 正常更新路径已确认不存在;边界重连需运行验证 |
| 高频位置按实体合并最新状态 | WebSocket 版本增量已确认;Cesium generation 会替换待同步批次 |
| 分片队列生产超过消费 | generation 替换旧批次;LOS 队列仍可能积压 |
| 离开三维后的清理 | 已确认有显式清理 |
| LOS 是否对所有飞机执行 | 需运行配置和任务计数确认 |
| 高程采样线程 | 已确认在自定义 Worker |
| Promise 是否等同多线程 | 已确认没有混淆;真正并行来自 Worker |
| Worker 池并行和旧任务 | 固定并行和 generation 已确认;无队列上限 |
| Entity/Primitive 选择 | 已确认普通轨迹点采用 PointPrimitive |
| 基站与设备信息所有权 | 已确认设备承担交互,基站底座是显示对象 |
| 屏幕尺寸和世界尺寸 | 基站世界高度;设备可选固定屏幕尺寸 |
| React 导致 Cesium 重建 | 正常消息路径已确认不会 |
| 每帧分配/全量遍历 | 未发现 LOS/地形全量每帧执行,仍需 Performance 确认 |
## 14. 配置与扩展点
- 配置来源:`config/config.json`,由 `Config::fromJson()` 加载并由 `toJson()` 保存。
- 业务配置:数据源协议、基站位置、点数限制、CPR 约束、理论范围过滤。
- 显示配置:2D/3D 分层样式、瓦片源、相机、Cesium 画质、地形、LOS、日照和模型。
- 新设备类型:扩展 `Data_Source` 子类和工厂注册,并提供 JSON 配置。
- 新地图图层:扩展 `Map_Resources_Config``/map/resources` 公开元数据,再在 2D/3D 适配器接入。
- 新数据源:实现 `Data_Source` 的打开、读取、关闭协程,复用 `Data_Source_Handler`
- 新算法:优先放在 SSR 或独立算法模块,通过小接口接入,避免渲染层反向依赖。
- 新相机模式:扩展 `Camera_Control_Mode``Cesium_Camera_Control.set_mode()`,必须实现输入恢复和 `destroy()`
- 新 Worker 任务:定义纯数据协议,通过 `Worker_Pool` 调度,明确缓存、过期和退出语义。
前端配置入口:
- [`load_map_resources()`](../third_party/eacp_webapp/src/Map/Map_Resources.tsx#L28-L45)
- [`load_map_view_config()`](../third_party/eacp_webapp/src/Map/Map_View.tsx#L83-L90)
- [`load_map_model_config()`](../third_party/eacp_webapp/src/Map/Map_Models.tsx#L117-L137)
## 15. 架构风险
| 等级 | 风险 | 影响 | 证据 | 建议验证方式 |
| -- | -- | -- | -- | -- |
| 高 | LOS Worker Pool 队列无上限、不能取消执行中任务 | 飞机多且更新快时任务积压和无效请求 | [`Worker_Pool.run()`](../third_party/eacp_webapp/src/Map/Worker_Pool.ts#L38-L50) | 记录 queue length、等待时间、旧结果比例 |
| 高 | WebSocket 周期推送在全局客户端锁内构造并发送 | 慢连接可能扩大锁持有时间 | [`push_aircraft_stream_updates()`](../module/Local_Server/Data_Source/Database.cpp#L227-L230) | 多慢客户端压力测试 |
| 中高 | 三维同步仍有全量发现阶段 | 目标数增大后主线程扫描成本增长 | [`sync_data_sources()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L714-L767) | 1k/10k 目标 Performance |
| 中高 | LOS 每 Worker 各自缓存 Terrarium | 并行度增加会重复占用内存 | [`load_height_tile()`](../third_party/eacp_webapp/src/Map/Los_Worker.ts#L186-L234) | Worker heap 和缓存命中率 |
| 中 | 瓦片专用 Asio 池为单线程 | 高并发磁盘瓦片吞吐可能串行 | [`Ucoro_Drogon_Glue`](../module/Local_Server/server/Ucoro_Drogon_Glue.h#L13-L17) | 并发请求吞吐、网络线程采样 |
| 中 | 上传替换基站 GLB 时横向节点名依赖 `Node` | 新模型节点命名不一致会失去横向显示比例 | [`base_station_node_transformations()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L1163) | 上传不同节点名 GLB 验证 |
| 中 | 气压高度、Terrarium 正高和 Cesium 椭球高基准可能不同 | LOS 和设备高度产生系统偏差 | [`base_station_heights()`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L1082-L1092) | 使用已知测量点对照 |
| 中 | 前端没有独立单元测试脚本 | 增量、缓存和生命周期回归主要依赖集成测试 | [`package.json`](../third_party/eacp_webapp/package.json#L6-L12) | 增加 Vitest/Playwright 关键路径测试 |
## 16. 源码索引
| 子系统 | 入口类型或函数 | 文件位置 | 说明 |
| -- | -- | -- | -- |
| 进程 | `psc_main()` | [`module/Local_Server_main.cpp`](../module/Local_Server_main.cpp#L56) | 后端启动和退出 |
| 全局对象 | `Global` | [`Global.h`](../module/Local_Server/server/Global.h#L148) | 配置和模块所有者 |
| Web 服务 | `init_web_server()` | [`server.cpp`](../module/Local_Server/server/server.cpp#L136) | HTTP/WS 注册 |
| 配置 | `Config` | [`Config.h`](../module/Local_Server/server/Config.h#L263) | 单一 JSON 配置 |
| 协程 | `Coro` | [`io_coro.cpp`](../module/Local_Server/server/io_coro.cpp#L47) | 网络和处理线程 |
| 数据源 | `Data_Source` | [`Data_Source.h`](../module/Local_Server/Data_Source/Data_Source.h#L121) | 输入抽象 |
| 协议处理 | `Data_Source_Handler` | [`Data_Source_Handler.cpp`](../module/Local_Server/Data_Source/Data_Source_Handler.cpp#L83) | 切包和业务分发 |
| SSR | `parse_mode_s_bin()` | [`Aircraft_Info.cpp`](../third_party/SSR/SSR/Aircraft_Info.cpp#L279) | Mode S 主入口 |
| CPR | `surface_position()` | [`CPR.cpp`](../third_party/SSR/SSR/CPR/CPR.cpp#L247) | 位置和距离 |
| 实时流 | `register_aircraft_stream_ws()` | [`Database.cpp`](../module/Local_Server/Data_Source/Database.cpp#L283) | 版本增量 WebSocket |
| 瓦片 | `init_tiles()` | [`tiles.cpp`](../module/Local_Server/server/tiles.cpp#L589) | 影像/地形 HTTP |
| SPA | `App` | [`App.tsx`](../third_party/eacp_webapp/src/App.tsx#L36) | 路由和延迟加载 |
| 统一模型 | `Aircraft_Model` | [`Aircraft_Model.tsx`](../third_party/eacp_webapp/src/Map/Aircraft_Model.tsx#L22) | 2D/3D 共用 |
| 二维地图 | `Leaflet_Map` | [`Leaflet_Map.tsx`](../third_party/eacp_webapp/src/Map/Leaflet_Map.tsx#L42) | Leaflet 渲染 |
| 三维地图 | `Cesium_Map` | [`Cesium_Map.tsx`](../third_party/eacp_webapp/src/Map/Cesium_Map.tsx#L117) | Cesium 适配器 |
| 地形 | `Terrarium_Terrain_Provider` | [`Terrarium_Terrain_Provider.tsx`](../third_party/eacp_webapp/src/Map/Terrarium_Terrain_Provider.tsx#L22) | Heightmap Provider |
| LOS | `calculate_los()` | [`Los_Worker.ts`](../third_party/eacp_webapp/src/Map/Los_Worker.ts#L45) | Worker 通视计算 |
| 相机 | `Cesium_Camera_Control` | [`Cesium_Camera_Control.ts`](../third_party/eacp_webapp/src/Map/Cesium_Camera_Control.ts#L12) | 三种控制模式 |
## 17. 无法确认的问题
1. 没有运行目标规模压力测试,无法确认 1,000 架飞机、10,000 个轨迹点时的主线程、GPU 和 Worker 队列上限。
2. 当前源码没有给出所有实际部署设备的高度基准,无法确认气压高度、正高和椭球高是否已经统一。
3. 未获得生产数据库规模和索引统计,无法确认历史轨迹查询成本。
4. 未看到浏览器端自动化测试,无法从源码确认重复进入/退出 3D、WebGL 初始化失败和 Worker 销毁的长期稳定性。
5. GLB 上传接口允许替换模型,但无法从配置确认每个外部模型的根节点命名、坐标轴、原点和单位。
6. Cesium、WebView2 和 Intel 集成显卡的实际驱动组合只能通过目标机器运行验证,源码不能证明 WebGL 初始化一定成功。
7. LOS 是地形几何通视,不包含建筑、植被、天线方向图、信号强度、菲涅耳区和大气折射;这些能力当前源码未实现。