41 KiB
项目架构说明
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 模型资源。
最核心的子系统是:
- 数据源和数据转发协程;
- SSR/ADS-B/CPR 协议解析与飞机状态;
- Drogon HTTP/WebSocket 服务;
- Leaflet/Cesium 地图渲染;
- 地形解码、LOS 通视分析和 Worker 调度。
关键入口:
psc_main()module/Local_Server_main.cpp:56-175
Global::Global()module/Local_Server/server/Global.cpp:58-145
Appthird_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, package.json |
| 构建 | CMake、自定义 _create/_depend/_attach_* |
后端目标和依赖装配 | third_party/build_infra、main.cmake |
main.cmake |
| 后端 Web | Drogon/Trantor | HTTP、WebSocket、静态 UI | module/Local_Server/server |
Global::init_web_server() |
| 并发 | standalone Asio 1.38、C++ 协程、线程池 | 网络循环、数据处理、文件 I/O | io_coro.*、Ucoro_Drogon_Glue.h |
Coro::start(), ASIO_VERSION |
| 协议 | SSR 自有 Mode A/C/S、ADS-B、CPR 实现 | 报文解析、位置和距离算法 | third_party/SSR/SSR |
parse_mode_s_bin(), CPR |
| 数据格式 | Psc JSON | 配置、接口、业务序列化 | Config.h、各模块 toJson/fromJson |
Config::fromJson() |
| 存储 | SQLiteCpp、文件 | 飞机/轨迹数据和配置 | Database.*、配置文件 |
database_server() |
| 前端 | React 18、Ant Design、Vite 6 | SPA、设置和业务面板 | third_party/eacp_webapp |
package.json |
| 二维地图 | Leaflet 1.9 | 二维地图、飞机和轨迹 | Leaflet_Map.tsx、Aircraft.tsx |
Leaflet_Map |
| 三维地图 | CesiumJS 1.143 | 地球、模型、轨迹、地形和相机 | src/Map/Cesium_* |
Cesium_Map |
| 地形 | Terrarium PNG、CustomHeightmapTerrainProvider | 本地高程地形 | Terrarium_Terrain_* |
Terrarium_Terrain_Provider |
| 浏览器并发 | Web Worker、固定 Worker 池 | PNG 解码和 LOS | Worker_Pool.ts、Los_Worker.ts |
Worker_Pool |
| 日志 | Core 日志封装 | 后端运行和协议日志 | third_party/CPP_Core、Global.cpp |
Global::Global() |
| 测试 | GoogleTest、TypeScript、ESLint、Vite build | 后端单测与前端静态验证 | SSR 测试、前端 scripts | CPR_TEST.cpp, package.json |
| 部署 | Drogon 静态资源、Cesium runtime copy | 后端托管生产 UI | vite.config.js、server.cpp |
cesiumAssetsPlugin() |
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,并在构建后复制配置。
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:8-19
ecap_server目标装配main.cmake:40-59
ecap_server对dll_source的依赖main.cmake:132
Vite生产输出和 Cesium 资源复制third_party/eacp_webapp/vite.config.js:21-53
后端启动顺序是:
main()进入psc_main();- 创建
Global,加载配置和模块; - 初始化 Drogon 路由;
- 启动 Web 服务线程;
- 启动
Coro的 Asio 网络线程和数据处理线程池; - 主循环清理超时飞机并维持设备状态;
- 退出时先停协程和设备,再销毁全局对象。
开发模式由 Vite 代理后端 /api、/map、/tiles 和 /ws;生产模式由 Drogon 提供 Vite 输出及 /ui/cesium、/ui/model 静态资源。
5. 总体架构
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() |
Global |
单例类 | 配置和主要模块所有者 | 主流程创建、退出销毁 | Global |
Global::init_web_server() |
函数 | 注册路由并启动 Web 配置 | Global 初始化期 |
init_web_server() |
输入是配置文件和运行参数,输出是持续运行的服务与模块集合。异常处理包含顶层退出路径和日志;部分初始化失败会阻止 init_ok。
6.2 配置系统
职责
以一个 JSON 配置对象管理数据源、地图资源、二维/三维视图、Cesium 画质和模型元数据。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
|---|---|---|---|---|
Map_Tile_Config |
结构体 | 瓦片目录、布局、Y 轴、投影和层级 | 配置生命周期 | Map_Tile_Config |
Map_View_Config |
结构体 | 2D/3D 图层、层级和相机 | 配置生命周期 | Map_View_Config |
Cesium_Graphics_Config |
结构体 | 画质、地形、日照和 LOS | 配置生命周期 | Cesium_Graphics_Config |
Map_Model_Config |
结构体 | 飞机、基站、设备、轨迹点模型 | 配置生命周期 | Map_Model_Config |
Config::fromJson/toJson |
函数 | 单一配置解析和序列化 | 启动加载、接口保存 | fromJson(), toJson() |
地图接口只公开前端所需元数据,不公开本机目录。配置更新由后端接口写回同一配置对象和文件。
6.3 数据源与数据转发
职责
抽象不同输入介质,读取原始数据,在线程池解析,再向数据库、飞机状态和数据转发模块发布。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
|---|---|---|---|---|
Data_Source |
基类 | 输入源、状态和显示配置 | 配置创建到停用/退出 | Data_Source |
Data_Source_Handler |
基类 | 切包、解析、业务分发 | 随 Data Source | Data_Source_Handler |
Data_Feed |
基类 | 向 TCP/UDP 等客户端转发 | 配置创建到退出 | Data_Feed |
Coro |
单例类 | Asio 网络循环和处理线程池 | 服务运行期 | Coro |
异步模型见第 10 节。主要失败路径包括输入断开、协议切包失败、解析异常和输出连接失败。With_Loop_Coro 状态机负责打开、循环和关闭。
6.4 SSR、ADS-B、CPR 和飞机状态
职责
解析 Mode S 报文,维护每架飞机的消息、空中/地面轨迹和派生属性;CPR 负责位置解码、球面距离和无线电视距范围。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
|---|---|---|---|---|
Data_Source_Interface |
接口 | SSR 对宿主数据源的最小依赖 | 随数据源 | Data_Source_Interface |
parse_mode_s_bin() |
函数 | Mode S 主解析入口 | 每条报文 | parse_mode_s_bin() |
Aircraft_Info |
类 | 飞机状态和轨迹所有者 | ICAO 出现到超时删除 | Aircraft_Info |
CPR::surface_position() |
函数 | 基于参考站的地面 CPR 解码 | 地面位置报文 | surface_position() |
CPR::in_radio_line_of_sight_range() |
函数 | 球面距离与理论视距过滤 | 位置候选校验 | in_radio_line_of_sight_range() |
Data_Source_Handler 将数据源中的基站位置和空中/地面约束传入 SSR,而不是让 SSR 反向依赖服务配置。
6.5 数据库与实时接口
职责
提供飞机/轨迹查询,并维护按 WebSocket 连接隔离的版本和轨迹订阅状态。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
|---|---|---|---|---|
Aircraft_Stream_Client_State |
连接状态 | 保存各数据源版本和订阅 | WebSocket 连接期 | Aircraft_Stream_Client_State |
aircraft_stream_source_update_json() |
函数 | 构造单数据源增量 | 每次推送 | aircraft_stream_source_update_json() |
get_visible_aircraft_snapshot() |
函数 | 点数和基站范围过滤 | 查询/推送时 | get_visible_aircraft_snapshot() |
register_aircraft_stream_ws() |
函数 | 注册流和周期推送 | 服务生命周期 | register_aircraft_stream_ws() |
前端按 ICAO 返回 change_version 和轨迹位置,后端只发送变化、删除和被监控轨迹。全部监控与手动 ICAO 集合是不同状态。
6.6 HTTP、瓦片与资源配置
职责
按有序目录查找影像和地形瓦片,通过异步文件读取返回 GET/HEAD、缓存头和条件响应,同时提供公开资源元数据。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
|---|---|---|---|---|
Tile_Path_Resolver |
类 | 按布局和目录优先级定位文件 | 服务生命周期 | Tile_Path_Resolver |
Tile_Source |
类 | 查找、状态、读取和响应元数据 | 服务生命周期 | Tile_Source |
register_tile_handler() |
函数 | GET/HEAD、304、404 和回调桥接 | 路由注册期 | register_tile_handler() |
Global::init_tiles() |
函数 | 创建影像、Google 影像、地形源 | 服务初始化期 | init_tiles() |
多目录严格按列表顺序查询,第一个普通文件获胜;不扫描全目录、不预加载瓦片。文件查找、status、打开和读取整体通过 to_drogon() 运行在专用 Asio 执行器,再回到 Drogon 事件循环完成一次回调。
6.7 前端统一业务状态
职责
Data_Source 保存子数据源、基站、飞机集合、2D/3D 显示配置和监控模式;Aircraft_Model 保存与渲染框架无关的飞机/轨迹状态。
| 名称 | 类型 | 职责 | 生命周期 | 源码 |
|---|---|---|---|---|
Data_Source |
类 | 单数据源前端状态所有者 | 配置加载到页面卸载 | Data_Source |
Aircraft_Model |
类 | 统一飞机位置、姿态、属性和轨迹 | 飞机出现到删除 | Aircraft_Model |
Aircraft_Stream_Client |
类 | WebSocket 重连、订阅和增量应用 | SPA 生命周期 | Aircraft_Stream_Client |
Leaflet 的 Aircraft 包装统一模型,Cesium 直接读取同一对象。Cesium 依赖只存在于三维适配器。
6.8 Leaflet 二维渲染
职责
维持原有二维地图、瓦片层、飞机图标、轨迹线和右键交互。
核心入口是 Leaflet_Map 和 Aircraft。二维对象由 Data_Source.aircraftMap 中的同一飞机实例驱动;轨迹监控状态来自共享数据源状态。
6.9 Cesium 三维渲染
职责
创建/销毁 Viewer,增量映射数据源、飞机、轨迹、基站和设备,接入影像、地形、模型、相机、LOS 与交互。
核心入口:
Cesium_Map.load_map()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:247-298
sync_data_sources()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:714-767
sync_base_station()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:768-801
sync_aircraft()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:1188-1209
on_un_mount()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 |
Terrarium_Terrain_Worker |
Worker | fetch、PNG 解码、RGB 转高度 | Provider 生命周期 | decode_terrarium_png() |
Worker_Pool |
调度器 | 固定并行度和 FIFO 任务 | Cesium 生命周期 | Worker_Pool |
Los_Worker |
Worker | ECEF 视线和地形采样 | LOS Pool 生命周期 | calculate_los() |
Cesium_Camera_Control |
控制器 | 地表、地心轨道、自由观察 | Viewer 生命周期 | Cesium_Camera_Control |
7. 核心业务对象关系
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. 运行时生命周期
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:23-26
/map与/map3d路由third_party/eacp_webapp/src/App.tsx:224-268
9. 数据流
实时数据流
输入字节
→ Data_Source 异步读取
→ process_data 线程池切包/解析
→ SSR Aircraft_Info 和轨迹
→ DataBase 可见性过滤和版本比较
→ /ws/aircraft_stream 增量
→ Data_Source.aircraftMap
→ Leaflet/Cesium 渲染记录
→ 屏幕
WebSocket 消息是 JSON;前端保留每架飞机版本和最后轨迹大小。业务状态在前端对象中原地增量更新,Cesium 对象按 ID 复用,不在每次消息后重建 Viewer。
用户交互流
点击飞机/设备/轨迹点
→ Cesium/Leaflet picking
→ 找到 Data_Source 或 Aircraft_Model
→ 更新 active_aircraft/监控状态
→ 显示信息、轨迹或相机行为
基站范围辅助几何设置为忽略 picking,设备 Entity 才绑定完整基站信息。相关代码见 create_base_station_record()。
地形与通视分析流
基站设备位置 + 飞机位置
→ LOS generation/request ID
→ Worker Pool
→ ECEF 直线采样
→ Terrarium 瓦片 fetch/cache/decode
→ 净空和首次遮挡结果
→ 丢弃过期 generation
→ 更新飞机 LOS 状态和连线
LOS Worker 接收普通数字结构,不传递 Cesium Viewer 或类实例。高度缓存位于各 Worker 内,返回结果会检查当前 generation,避免旧位置覆盖新位置。
配置更新流
设置表单临时值
→ HTTP PUT/POST
→ Global 中单一配置对象
→ config.json
→ 前端事件通知
→ 当前渲染器应用
地图资源接口只返回 URL、层级、投影、Y 轴和编码。模型上传由后端保存并更新模型 URL 和内置尺寸/初始角度配置。
10. 并发、异步和调度模型
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:206-299
to_drogon()module/Local_Server/server/Ucoro_Drogon_Glue.h:102-110
Cesium分片同步third_party/eacp_webapp/src/Map/Cesium_Map.tsx:707-767
Worker_Pool.dispatch()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 页面不加载 Cesium;3D 页面根据设置启用 Terrarium。关闭地形或卸载 Viewer 时会销毁 Provider 和 Worker。
基站/设备当前空间语义:
- 地形采样值是基站模型底部;
- 配置设备高度是设备模型位置;
- 基站模型纵向比例由两者高度差与模型原始高度计算;
- “基站显示大小”只调整横向尺寸,不破坏顶部高度;
- 设备可按子数据源选择固定屏幕尺寸或普通世界尺寸;
- 完整基站信息绑定到设备,底座忽略 picking。
实现见 sync_base_station()、create_base_station_record() 和 base_station_heights()。
12. 真正有难度的技术点
多执行器数据源协程
难度等级: 高
所属模块: Local_Server 数据源
解决的问题: 网络读取不能被协议解析阻塞,同时单数据源消息顺序必须稳定。
为什么困难: 同一逻辑跨 io_context、线程池和 strand,退出时还要等待未完成任务。
当前实现: 输入协程读取后切到 process_data;等待模式直接 co_await,无等待模式向 strand 投递;再回网络 executor。
关键算法或状态机: With_Loop_Coro 的打开、循环、关闭状态。
数据流: 输入字节 → 切包 → SSR → Feed/DB。
线程与异步边界: Data_Source::loop_coro()。
性能成本: executor 切换、任务排队和 JSON/协议解析。
边界条件: 数据源断开、退出、处理速度落后。
失败方式: 顺序破坏、停止等待、线程池积压。
现有风险: 队列长度没有统一可观测指标。
可改进方向: 增加每源队列深度、耗时和丢弃策略指标。
WebSocket 版本增量与轨迹订阅
难度等级: 高
所属模块: Database、前端 Data_Source
解决的问题: 避免周期发送全部飞机和全部轨迹。
为什么困难: 每个连接、数据源、ICAO 都有版本和轨迹游标;还需区分全部监控和手动监控。
当前实现: 连接状态保存版本,前端订阅回传版本和轨迹大小,后端生成变化与删除。
关键算法或状态机: 连接状态 + per-source delta。
数据流: Snapshot → version compare → delta → apply/delete。
线程与异步边界: Drogon 事件循环和浏览器 WebSocket。
关键源码: Database.cpp、Aircraft_Stream_Client。
性能成本: 周期快照、版本 Map 比较和 JSON 序列化。
边界条件: 重连、飞机删除、监控模式切换。
失败方式: 版本失配造成漏更新或重复轨迹。
现有风险: 后端持锁遍历并发送连接,需要压力测试。
可改进方向: 锁内只构造待发送项,锁外发送;增加队列和消息尺寸指标。
Terrarium 地形与 LOS Worker
难度等级: 高
所属模块: Cesium 地形、LOS
解决的问题: 在不阻塞 UI 的情况下解码 PNG 高程并计算基站到飞机的地形通视。
为什么困难: 涉及 Web Mercator、XYZ/TMS、ECEF、地球曲率、异步瓦片、缓存和过期结果。
当前实现: Terrain Worker 解码 Heightmap;LOS Worker 自行 fetch/cache/decode 并沿 ECEF 线采样。
关键算法或状态机: Terrarium R*256+G+B/256-32768;generation 淘汰旧结果。
数据流: 普通数字协议 → Worker → Float32Array/LOS 结果。
线程与异步边界: 主线程只调度,解码和 LOS 在 Worker。
关键源码: Terrarium_Terrain_Worker、Los_Worker。
性能成本: 网络请求、PNG 解码、每 Worker 独立缓存和采样次数。
边界条件: 404、缺少 OffscreenCanvas、层级上限、卸载。
失败方式: 队列积压、缓存重复、旧 LOS 覆盖新位置。
现有风险: Worker Pool 队列无硬上限。
可改进方向: 按飞机合并待处理任务并增加队列背压。
更多难点详见 项目技术难点说明。
13. 性能架构
| 热点 | 状态 | 源码证据 | 验证方法 |
|---|---|---|---|
sync_data_sources() 遍历全部当前对象 |
已确认存在,但对象记录复用且任务分片 | sync_data_sources() |
Chrome Performance,记录单次任务数和耗时 |
| 飞机 Entity 每次重建 | 已确认正常更新路径不存在;Map 复用记录 | sync_aircraft() |
统计 Entity add/remove |
| 普通轨迹点全部使用 GLB | 已确认不存在;使用 PointPrimitive | create_aircraft_record() |
Cesium Inspector、Primitive 数量 |
| React 消息导致 Viewer 重建 | 已确认正常增量消息不会重建 Viewer | request_sync_data_sources() |
React Profiler |
| WebSocket 全量飞机 | 已确认已改为版本增量 | aircraft_stream_source_update_json() |
抓取消息尺寸和 change_list |
| LOS 对所有飞机无条件执行 | 需要结合运行配置和监控模式采样验证 | update_aircraft_occlusion() |
Worker 任务计数/秒 |
| 地形 PNG 主线程解码 | 已确认不存在于 Provider/LOS 路径 | Terrarium_Terrain_Worker |
Performance 主线程长任务 |
| Worker 队列无界 | 已确认 | Worker_Pool.run() |
暴露 queue length 和等待时间 |
| 瓦片文件读取阻塞 Drogon 线程 | 已确认不存在;整体投递专用 Asio | register_tile_handler() |
并发 HTTP 延迟和线程采样 |
| 瓦片读取明显串行 | 源码推断:专用池大小为 1,需要压力测试确认影响 | Ucoro_Drogon_Glue |
32/64 并发瓦片吞吐 |
| 后端 WebSocket 锁内发送 | 已确认调用位置,实际阻塞程度待验证 | push_aircraft_stream_updates() |
慢客户端压力测试和锁等待 |
| 每帧高成本回调 | 未发现每帧地形解码或全量同步;性能面板 postRender 仍需 Profile | postRender 注册 |
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调度,明确缓存、过期和退出语义。
前端配置入口:
15. 架构风险
| 等级 | 风险 | 影响 | 证据 | 建议验证方式 |
|---|---|---|---|---|
| 高 | LOS Worker Pool 队列无上限、不能取消执行中任务 | 飞机多且更新快时任务积压和无效请求 | Worker_Pool.run() |
记录 queue length、等待时间、旧结果比例 |
| 高 | WebSocket 周期推送在全局客户端锁内构造并发送 | 慢连接可能扩大锁持有时间 | push_aircraft_stream_updates() |
多慢客户端压力测试 |
| 中高 | 三维同步仍有全量发现阶段 | 目标数增大后主线程扫描成本增长 | sync_data_sources() |
1k/10k 目标 Performance |
| 中高 | LOS 每 Worker 各自缓存 Terrarium | 并行度增加会重复占用内存 | load_height_tile() |
Worker heap 和缓存命中率 |
| 中 | 瓦片专用 Asio 池为单线程 | 高并发磁盘瓦片吞吐可能串行 | Ucoro_Drogon_Glue |
并发请求吞吐、网络线程采样 |
| 中 | 上传替换基站 GLB 时横向节点名依赖 Node |
新模型节点命名不一致会失去横向显示比例 | base_station_node_transformations() |
上传不同节点名 GLB 验证 |
| 中 | 气压高度、Terrarium 正高和 Cesium 椭球高基准可能不同 | LOS 和设备高度产生系统偏差 | base_station_heights() |
使用已知测量点对照 |
| 中 | 前端没有独立单元测试脚本 | 增量、缓存和生命周期回归主要依赖集成测试 | package.json |
增加 Vitest/Playwright 关键路径测试 |
16. 源码索引
| 子系统 | 入口类型或函数 | 文件位置 | 说明 |
|---|---|---|---|
| 进程 | psc_main() |
module/Local_Server_main.cpp |
后端启动和退出 |
| 全局对象 | Global |
Global.h |
配置和模块所有者 |
| Web 服务 | init_web_server() |
server.cpp |
HTTP/WS 注册 |
| 配置 | Config |
Config.h |
单一 JSON 配置 |
| 协程 | Coro |
io_coro.cpp |
网络和处理线程 |
| 数据源 | Data_Source |
Data_Source.h |
输入抽象 |
| 协议处理 | Data_Source_Handler |
Data_Source_Handler.cpp |
切包和业务分发 |
| SSR | parse_mode_s_bin() |
Aircraft_Info.cpp |
Mode S 主入口 |
| CPR | surface_position() |
CPR.cpp |
位置和距离 |
| 实时流 | register_aircraft_stream_ws() |
Database.cpp |
版本增量 WebSocket |
| 瓦片 | init_tiles() |
tiles.cpp |
影像/地形 HTTP |
| SPA | App |
App.tsx |
路由和延迟加载 |
| 统一模型 | Aircraft_Model |
Aircraft_Model.tsx |
2D/3D 共用 |
| 二维地图 | Leaflet_Map |
Leaflet_Map.tsx |
Leaflet 渲染 |
| 三维地图 | Cesium_Map |
Cesium_Map.tsx |
Cesium 适配器 |
| 地形 | Terrarium_Terrain_Provider |
Terrarium_Terrain_Provider.tsx |
Heightmap Provider |
| LOS | calculate_los() |
Los_Worker.ts |
Worker 通视计算 |
| 相机 | Cesium_Camera_Control |
Cesium_Camera_Control.ts |
三种控制模式 |
17. 无法确认的问题
- 没有运行目标规模压力测试,无法确认 1,000 架飞机、10,000 个轨迹点时的主线程、GPU 和 Worker 队列上限。
- 当前源码没有给出所有实际部署设备的高度基准,无法确认气压高度、正高和椭球高是否已经统一。
- 未获得生产数据库规模和索引统计,无法确认历史轨迹查询成本。
- 未看到浏览器端自动化测试,无法从源码确认重复进入/退出 3D、WebGL 初始化失败和 Worker 销毁的长期稳定性。
- GLB 上传接口允许替换模型,但无法从配置确认每个外部模型的根节点命名、坐标轴、原点和单位。
- Cesium、WebView2 和 Intel 集成显卡的实际驱动组合只能通过目标机器运行验证,源码不能证明 WebGL 初始化一定成功。
- LOS 是地形几何通视,不包含建筑、植被、天线方向图、信号强度、菲涅耳区和大气折射;这些能力当前源码未实现。