# 项目架构说明

## 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 是地形几何通视，不包含建筑、植被、天线方向图、信号强度、菲涅耳区和大气折射；这些能力当前源码未实现。
