# 项目架构说明 ## 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 页面不加载 Cesium;3D 页面根据设置启用 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 解码 Heightmap;LOS 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 是地形几何通视,不包含建筑、植被、天线方向图、信号强度、菲涅耳区和大气折射;这些能力当前源码未实现。