44 KiB
项目技术难点说明
1. 技术难点总览
| 编号 | 技术点 | 难度 | 核心模块 | 主要风险 | 关键源码 |
|---|---|---|---|---|---|
| 1 | 多执行器数据源协程和有序解析 | 高 | Local_Server、Asio | 乱序、积压、退出等待 | Data_Source::loop_coro() |
| 2 | ADS-B/CPR 三维位置和基站范围过滤 | 高 | SSR、Data_Source | 坐标/高度错误造成误过滤 | CPR.cpp |
| 3 | WebSocket 版本增量与轨迹监控状态 | 高 | Database、Data_Source.tsx | 漏更新、重复轨迹、慢连接 | Database.cpp |
| 4 | Cesium 生命周期和大对象增量同步 | 高 | Cesium_Map | 主线程长任务、资源泄漏 | Cesium_Map |
| 5 | Terrarium 自定义地形和 Worker 解码 | 高 | Terrain Provider/Worker | 层级翻转、主线程阻塞、缓存失控 | Terrarium_Terrain_Provider |
| 6 | ECEF 地形 LOS 与 Worker 池 | 高 | Los_Worker、Worker_Pool | 任务积压、旧结果、基准误差 | calculate_los() |
| 7 | 统一 2D/3D 业务状态和双渲染适配 | 中高 | Data_Source、Aircraft_Model | 重复状态、显示语义漂移 | Aircraft_Model |
| 8 | 基站、设备和飞机的三维坐标/姿态 | 中高 | Cesium_Map、Flight_VTO | 轴向、原点、世界/屏幕尺寸错误 | sync_base_station() |
| 9 | 多目录瓦片异步读取和 HTTP 缓存 | 中高 | tiles.cpp | 阻塞网络线程、重复回调、缓存失效 | Tile_Source |
| 10 | Cesium 相机模式和视图持久化 | 中 | Camera Control、Map View | 输入冲突、跟随状态恢复错误 | Cesium_Camera_Control |
| 11 | 后端/前端/Cesium 静态资源组合构建 | 中 | CMake、Vite | 开发可用而生产资源 404 | vite.config.js |
2. 技术难点依赖关系
flowchart LR
AsyncSource[1 多执行器数据源] --> CPR[2 ADS-B/CPR]
CPR --> Stream[3 WebSocket增量]
Stream --> Shared[7 统一业务状态]
Shared --> CesiumSync[4 Cesium增量同步]
Tiles[9 瓦片服务] --> Terrain[5 Terrarium地形]
Terrain --> LOS[6 LOS Worker池]
Shared --> Spatial[8 三维坐标姿态]
Terrain --> Spatial
Spatial --> CesiumSync
Camera[10 相机模式] --> CesiumSync
Build[11 组合构建] --> CesiumSync
Build --> Tiles
3. 技术难点详情
3.1 多执行器数据源协程和有序解析
难度等级: 高
复杂度来源: 并发、生命周期、性能
所属模块: module/Local_Server/server、module/Local_Server/Data_Source
当前状态: 已实现
解决的问题
TCP、串口、文件、DLL 和共享内存等输入不能因报文解析阻塞网络循环,同时同一数据源中的报文顺序不能被线程池并发破坏。
为什么困难
读取、解析和输出跨越不同 executor。等待处理模式要求读协程等待解析完成;不等待模式需要独立 strand 保序。停止过程中还必须停止新读取、等待处理线程、关闭底层资源并终止 io_context。
当前架构
Coro 持有一个 Asio io_context 和一个 process_data 线程池。每个 Data_Source 在网络 executor 上读取,按配置切到处理池或 strand,完成后回到网络 executor。
flowchart LR
IO[io_context读取] --> Queue[数据源本地处理路径]
Queue --> Pool[process_data线程池]
Pool --> SSR[SSR解析]
SSR --> IO
核心执行流程
Coro::start()创建网络线程和处理池。Data_Source::loop_coro()调用输入源read_coro()。- 数据切包后切换或投递到
process_data。 Data_Source_Handler解析并分发。- 协程回到网络 executor,继续读取。
Coro::stop()停止源、等待线程池并释放。
核心数据结构
Coro::ioCoro::process_data- 每个 Data Source 的
asio::strand With_Loop_Coro_Data状态
并发和异步语义
co_await 本身不创建线程;真正的线程切换来自 executor。strand 保证同一数据源投递任务不并行。不同数据源可以在处理池并行。当前没有统一的任务队列上限或背压指标。
算法和数值语义
不涉及数值算法。复杂度主要随报文数线性增长;空间成本取决于待处理消息积压。
关键源码
Coro::start()/stop()module/Local_Server/server/io_coro.cpp:47-131
Data_Source::loop_coro()module/Local_Server/server/io_coro.cpp:206-299
With_Loop_Coro::run_loop_coro()module/Local_Server/server/With_Loop_Coro.cpp:34-73
process_mode_acs_data()module/Local_Server/Data_Source/Data_Source_Handler.cpp:83-153
性能成本
CPU 成本来自切包和协议解析;并发成本来自 executor 切换、队列与 strand;积压会增加内存和实时延迟。
容易出现的问题
- 不等待模式中的消息顺序被破坏;
- 停止时仍有任务访问已关闭数据源;
- 解析速度低于输入速度;
- 在网络 executor 执行阻塞解析。
修改注意事项
不能改变 Data_Source::loop_coro() 的读取顺序语义。新增数据源必须实现相同的打开、读取、关闭生命周期;修改处理池切换时必须同时检查等待和不等待两个分支。
可验证方式
用多数据源高频报文压力测试,记录每源序号、队列等待、处理耗时和退出时间;使用 ThreadSanitizer 可用平台或日志验证顺序与停止边界。
可改进方向
增加每数据源有界队列、队列深度、最大等待时间和按业务允许的丢弃/合并策略。
3.2 ADS-B/CPR 三维位置和基站范围过滤
难度等级: 高
复杂度来源: 算法、坐标系统、数值精度
所属模块: third_party/SSR/SSR、Data_Source_Handler
当前状态: 已实现,实测精度待验证
解决的问题
从奇偶 CPR 报文或参考位置恢复飞机坐标,并利用有效基站位置、目标高度和宽松系数过滤超出理论无线电视距的异常点。
为什么困难
地面 CPR 有位置象限歧义,需要基站参考位置;距离计算必须使用球面距离;范围同时取决于基站和目标高度。高度单位、负值、地面/空中轨迹和报文时序都会影响结果。
当前架构
宿主通过 Data_Source_Interface 将基站三维位置和空中/地面约束传给 SSR。CPR 模块集中实现地面位置、haversine 距离、理论视距和范围判断,其他模块复用而不重复实现距离公式。
核心执行流程
- 解析 ADS-B 位置报文。
- 按空中或地面选择轨迹与约束。
- 用 CPR 全局或局部算法得到候选经纬度。
- 有基站有效位置时计算球面距离。
- 根据两端高度计算理论视距并乘过滤系数。
- 合格位置进入轨迹列表。
核心数据结构
CPR::PositionPosition_3DADS_B_T::ConstraintAircraft_Info::airborne_pos_track_listAircraft_Info::surface_pos_track_list
并发和异步语义
算法本身同步执行,由数据源处理线程池调用。单飞机状态的并发安全依赖上层数据源保序。
算法和数值语义
- haversine 输入经纬度,输出米;
- 无线电视距使用地球半径与两端高度;
distance <= range * factor;- 地面 CPR 以基站经纬度消除 90 度象限歧义。
关键源码
surface_position()third_party/SSR/SSR/CPR/CPR.cpp:247-265
haversine()third_party/SSR/SSR/CPR/CPR.cpp:275-290
radio_line_of_sight_range_meters()third_party/SSR/SSR/CPR/CPR.cpp:292-303
parse_mode_s_bin()调用module/Local_Server/Data_Source/Data_Source_Handler.cpp:155-189
性能成本
单次计算是常数级;主要成本来自高频位置报文和轨迹容器更新。错误过滤的业务成本远高于 CPU 成本。
容易出现的问题
- 将经纬度欧氏距离当成米;
- 高度单位或基准不一致;
- 地面 CPR 参考位置无效;
- 过滤系数方向理解反了;
- 奇偶报文时间窗口错误。
修改注意事项
距离和视距算法只能保留 CPR 中的一份实现。修改函数签名时必须同步 export.h、宿主调用和测试;不能把空中和地面轨迹重新合并成无类型列表。
可验证方式
用已知 ADS-B 报文和参考站位置做对照;测试赤道、日期变更线、高纬度、零高度、负高度和过滤边界;对照地理库距离结果。
可改进方向
明确所有高度的基准和单位,在配置与接口中携带高度类型;为范围过滤增加拒绝原因统计。
3.3 WebSocket 版本增量与轨迹监控状态
难度等级: 高
复杂度来源: 协议、状态机、并发、缓存一致性
所属模块: Database.cpp、Data_Source.tsx
当前状态: 已实现
解决的问题
用 WebSocket 主动推送飞机变化和被监控轨迹,避免 /aircraft_change_list 式周期全量列表。
为什么困难
服务端必须维护每个连接、每个数据源、每个 ICAO 的版本;前端重连时要提交本地版本和轨迹游标;全部监控模式与手动监控集合不能混为一套状态。
当前架构
后端保存连接状态并每秒构造增量。前端 Aircraft_Stream_Client 统一维护连接、重连和延迟订阅;每个 Data_Source 保留飞机版本、监控模式和手动 ICAO。
核心执行流程
- WebSocket 建立。
- 前端发送数据源 key、监控模式、ICAO、飞机版本和轨迹大小。
- 后端保存订阅状态并立即响应。
- 周期任务生成变化、删除和轨迹增量。
- 前端原地更新或删除飞机,再请求当前地图适配器同步。
核心数据结构
Aircraft_Stream_Client_StateAircraft_Stream_Source_Stateaircraft_change_versionsmanual_track_icao_setmonitor_all_aircraft_mode
并发和异步语义
后端状态由全局 mutex 保护;当前周期推送在持锁期间遍历并调用发送。浏览器使用事件循环和定时器合并订阅请求。没有 Cookie Session,连接对象就是实时状态边界。
算法和数值语义
按 ICAO 比较版本,生成 change_list 和 removed_icaos;轨迹按最后大小/序号截取增量。时间复杂度与当前可见飞机和客户端订阅规模相关。
关键源码
Aircraft_Stream_Client_Statemodule/Local_Server/Data_Source/Database.cpp:92-103
aircraft_stream_source_update_json()module/Local_Server/Data_Source/Database.cpp:156-207
register_aircraft_stream_ws()module/Local_Server/Data_Source/Database.cpp:283-289
Aircraft_Stream_Clientthird_party/eacp_webapp/src/Data_Source/Data_Source.tsx:483-573
性能成本
后端周期快照、版本比较、JSON 序列化和多客户端发送;前端增量解析、对象更新和地图同步。
容易出现的问题
- 重连时版本状态与服务端不一致;
- 删除消息遗漏;
- 全部监控和手动监控互相覆盖;
- 慢连接放大锁持有时间;
- 轨迹游标回退导致重复点。
修改注意事项
协议字段必须前后端一起修改。不能把全部监控实现为向手动集合加入所有 ICAO;它们有不同的生命周期语义。飞机删除必须同步清除版本和轨迹显示。
可验证方式
模拟重连、数据源启停、ICAO 删除/重现、模式切换和多慢客户端;抓取 WebSocket 帧确认只有增量。
可改进方向
构造不可变待发送消息后锁外发送;增加 per-client backlog、消息大小和版本重同步机制。
3.4 Cesium 生命周期和大对象增量同步
难度等级: 高
复杂度来源: 生命周期、性能、GPU
所属模块: Cesium_Map.tsx
当前状态: 已实现,目标规模待性能验证
解决的问题
在进入 3D 时按需创建 Cesium,在实时数据更新时复用对象,并在离开页面时彻底释放 Viewer、Worker、监听器和计时器。
为什么困难
Cesium Viewer、Entity、Primitive、Provider、Worker 和 React 组件有不同生命周期。一次全量创建大量对象会阻塞主线程;异步结果还可能在组件卸载后返回。
当前架构
App 动态导入三维页面。Cesium_Map 保存按业务 ID 索引的飞机和基站 record。同步先发现存活对象,再生成飞机任务,用数量和耗时双阈值分片处理。Viewer 使用 request-render mode。
核心执行流程
/map3d动态加载模块。load_map()加载配置并创建 Viewer。- WebSocket 更新调用
request_sync_data_sources()。 - 同步请求被定时器合并。
- 基站立即同步,飞机任务分片消费。
- 删除不再存活的 records。
- 卸载时递增 generation、清定时器、销毁 Worker/Provider/Viewer。
核心数据结构
aircraft_entity_mapbase_station_entity_mapsync_aircraft_taskssync_generationPointPrimitiveCollection
并发和异步语义
Cesium API 操作都在浏览器主线程。分片不是多线程,而是把工作拆到多个事件循环/渲染时机。地形和 LOS 的 CPU 工作才由 Worker 执行。
算法和数值语义
每轮发现阶段是 O(数据源 + 飞机),记录更新是按 key 查找。轨迹按每架飞机最大点数裁剪最旧点。
关键源码
load_map()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:247-298
request_sync_data_sources()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:707-713
sync_data_sources()和分片消费third_party/eacp_webapp/src/Map/Cesium_Map.tsx:714-767
on_un_mount()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:190-245
性能成本
主线程对象同步、标签布局、模型 Draw Call、Polyline 和 PointPrimitive 更新;GPU 成本取决于模型、分辨率和可见标签。
容易出现的问题
- Viewer 重复创建;
- 卸载后异步回调访问旧 Viewer;
- 每个普通轨迹点创建 GLB;
- 更新一架飞机却重建全部 Entity;
- 分片生产速度持续高于消费。
修改注意事项
不能把普通 TypeScript 业务模型改成 Cesium 类型。新增 Entity 必须加入 record 并在删除/卸载路径释放;任何异步路径都必须检查 generation 或 destroyed 状态。
可验证方式
Chrome Performance、Memory、React Profiler 和 Cesium Inspector;循环进入退出 3D 100 次;记录 Entity/Primitive/Worker 数量和长任务。
可改进方向
当目标规模达到数千时,将飞机适配层切换为共享模型/Primitive 或 3D Tiles,不改变统一业务模型。
3.5 Terrarium 自定义地形和 Worker 解码
难度等级: 高
复杂度来源: 算法、Worker、缓存、第三方约束
所属模块: Terrarium_Terrain_Provider.tsx、Terrarium_Terrain_Worker.ts
当前状态: 已实现
解决的问题
把 AWS Terrarium PNG 转为 Cesium 可消费的 Heightmap 数据,并避免在主线程执行 getImageData()。
为什么困难
需要遵循瓦片 Y 轴、Web Mercator、层级上限和 Cesium 高度数组顺序;请求可能缺失或被取消;缓存必须有限。
当前架构
CustomHeightmapTerrainProvider 的 requestTileGeometry 被适配到 Worker 请求。Worker fetch PNG,使用 createImageBitmap 和 OffscreenCanvas 解码,返回可转移 Float32Array。
核心执行流程
- Cesium 请求 z/x/y。
- Provider 应用配置层级和 Y 轴。
- Worker fetch 后端地形 URL。
- PNG 解码为 RGBA。
- 按 Terrarium 公式生成北到南、从西到东的高度数组。
- 结果进入有界缓存并返回 Cesium。
- 404 返回缺失,让 Cesium 使用父级。
核心数据结构
CustomHeightmapTerrainProvider- pending request Map
- 高度瓦片 LRU/容量缓存
Float32Array
并发和异步语义
一个 Provider 对应一个 Worker。fetch 和解码都不在 React render 或浏览器主线程;destroy() 终止 Worker 并拒绝/清理 pending。
算法和数值语义
height = R * 256 + G + B / 256 - 32768,单位米。时间和空间复杂度均为 O(tile_size²)。
关键源码
Terrarium_Terrain_Providerthird_party/eacp_webapp/src/Map/Terrarium_Terrain_Provider.tsx:22-61
decode_terrarium_png()third_party/eacp_webapp/src/Map/Terrarium_Terrain_Worker.ts:53-72
url_template_for_y_axis()third_party/eacp_webapp/src/Map/Map_Resources.tsx:42-45
性能成本
网络、PNG 解码、Float32 内存和 Cesium 网格生成。缓存越大,回看越快但内存越高。
容易出现的问题
- XYZ/TMS 上下颠倒;
- 高度数组行顺序错误;
- 404 被当成零高;
- Worker 不支持 OffscreenCanvas;
- 卸载后 pending 回调泄漏。
修改注意事项
不能把 Terrarium 当 quantized-mesh。修改公开元数据时必须同步后端 /map/resources 和两个前端消费者。
可验证方式
对照已知山峰和海平面;检查瓦片接缝、南北方向、层级切换、404 父级回退、Worker 请求不超过最大层级。
可改进方向
补充更准确 availability;对低层级和热点瓦片预取需基于实测,不能无界预取。
3.6 ECEF 地形 LOS 与 Worker 池
难度等级: 高
复杂度来源: 算法、并发、坐标系统、缓存
所属模块: Los_Worker.ts、Worker_Pool.ts、Cesium_Map.tsx
当前状态: 已实现,精度和压力待验证
解决的问题
判断基站设备到被监控飞机的直线是否被 Terrarium 地形遮挡,并在多个飞机间有限并行。
为什么困难
长距离视线必须在 ECEF 中插值,不能线性插值经纬高。每个采样点需要映射到瓦片和像素;飞机移动会造成旧任务晚于新任务返回。
当前架构
主线程生成纯数字请求,固定 Worker Pool 调度。Worker 执行 WGS84 ECEF 转换、分段采样、Terrarium 缓存和双线性插值。主线程用 generation/request ID 丢弃旧结果。
核心执行流程
- 检查 LOS 开关、有效基站、监控状态和范围。
- 生成基站/飞机经纬高请求。
- 提交 Worker Pool。
- Worker 沿 ECEF 线采样并查询高程。
- 返回最小净空和首次遮挡位置。
- 当前 generation 匹配时才应用。
核心数据结构
Los_RequestLos_ResultWorker_Slot- FIFO pending queue
- Worker 本地
tile_cache - per-aircraft generation
并发和异步语义
Worker 数量有上限,任务超出后排队。Promise.all 不创建线程;并行度来自 Worker 数量。当前没有队列容量、同实体待处理任务替换或强制取消。
算法和数值语义
输入为 WGS84 经纬度和高度;转换 ECEF 后做线性插值,再转回 Cartographic 查询地形。瓦片像素用双线性插值。复杂度约为 O(视线长度/采样间距)。
关键源码
Worker_Pool.run()/dispatch()third_party/eacp_webapp/src/Map/Worker_Pool.ts:38-97
calculate_los()third_party/eacp_webapp/src/Map/Los_Worker.ts:45-91
ECEF 转换和插值third_party/eacp_webapp/src/Map/Los_Worker.ts:93-138
update_aircraft_occlusion()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:1320
性能成本
每条 LOS 产生 CPU 采样、瓦片请求、PNG 解码和缓存。多个 Worker 会重复保存相同瓦片高度。
容易出现的问题
- 主线程一次提交全部飞机;
- 旧位置结果覆盖新位置;
- 任务队列无限增长;
- 气压高度和地形高度基准不同;
- 采样过稀漏掉山脊,过密浪费计算。
修改注意事项
不能向 Worker 传 Cesium Viewer、Cartesian3 类实例或 TerrainProvider。协议必须是可结构化克隆的普通数据;结果应用前必须保留 generation 校验。
可验证方式
构造无遮挡、单峰遮挡、瓦片边缘、远距离和缺失瓦片场景;显示 Worker 队列、缓存命中率、任务年龄和旧结果丢弃率。
可改进方向
为同一飞机只保留最新待处理任务;按选中/手动监控/全部监控设置优先级和速率;增加粗到细两阶段采样。
3.7 统一 2D/3D 业务状态和双渲染适配
难度等级: 中高
复杂度来源: 架构、状态一致性、生命周期
所属模块: Data_Source.tsx、Aircraft_Model.tsx、Leaflet、Cesium
当前状态: 已实现
解决的问题
二维和三维显示同一飞机、轨迹、监控和子数据源数据,同时允许各自独立显示样式。
为什么困难
业务状态必须共享,但渲染对象、尺寸语义、点击和生命周期不同。错误抽象会产生两套解析和订阅,或把 Cesium 类型泄漏到通用模型。
当前架构
Data_Source 和 Aircraft_Model 是统一状态;Leaflet Aircraft 和 Cesium record 是适配层。map2d 与 map3d 配置隔离,监控状态共享。
核心执行流程
WebSocket 更新统一对象;当前活动地图收到同步请求;切换页面不重新建立另一套业务订阅;各适配器按自身配置更新显示。
核心数据结构
Data_Source.aircraftMapAircraft_Model.trackPointsData_Source_Map_Display_Data的 2D/3D 实例- 共享监控集合
并发和异步语义
业务更新在浏览器事件循环。Cesium 动态模块只在 3D 加载;统一模型没有运行时 Cesium import。
算法和数值语义
不涉及复杂算法;难点是状态所有权和增量语义。
关键源码
Data_Sourcethird_party/eacp_webapp/src/Data_Source/Data_Source.tsx:68-145
Aircraft_Modelthird_party/eacp_webapp/src/Map/Aircraft_Model.tsx:22-75
React.lazy(Cesium_Map)third_party/eacp_webapp/src/App.tsx:23-26
性能成本
统一状态降低重复解析,但当前适配器仍需扫描可见对象。React 刷新和 Cesium 同步频率需要保持解耦。
容易出现的问题
- 2D/3D 样式互相覆盖;
- 切换地图重复订阅;
- Cesium 类型进入通用模型导致 2D 下载 Cesium chunk;
- 删除飞机只清理一个渲染器。
修改注意事项
新增业务字段先加统一模型,再由适配器读取。只有纯显示字段放 2D/3D 配置。监控模式和轨迹数据不能复制到独立 Cesium 状态。
可验证方式
在同一 WebSocket 数据下往返切换 2D/3D,检查对象数量、监控集合、轨迹长度和 Network chunk。
可改进方向
将 WebSocket delta 协议和统一 Store 再独立为无 UI 模块,增加纯 TypeScript 单元测试。
3.8 基站、设备和飞机的三维坐标与姿态
难度等级: 中高
复杂度来源: 坐标系统、模型资产、显示语义
所属模块: Cesium_Map.tsx、Flight_VTO、模型配置
当前状态: 已实现,替换模型需重新标定
解决的问题
让飞机沿轨迹三维方向显示;让基站从地形地面延伸到配置设备高度,并把设备放在顶部;支持模型自身初始角度和世界/屏幕尺寸。
为什么困难
GLB 的机头轴、上轴、原点和单位不统一;Cesium 使用地固坐标和局部 ENU。固定屏幕尺寸与世界尺寸不能混用。地形异步返回会改变基站底部。
当前架构
后端 VTO 提供轨迹方向,前端结合每类模型初始 heading/pitch/roll 生成四元数。基站模型位置在地形高程,统一 scale 使纵向达到设备高度;横向节点变换应用显示大小。设备位于顶部并承担 picking。
核心执行流程
- 从最后两个三维轨迹点得到方向。
- 选择涡轮/机型模型配置。
- 在飞机位置构造 ENU/HPR 姿态。
- 异步采样基站地面。
- 计算
antenna_height / model_original_height。 - 基站放地面、设备放顶部,更新标签和范围。
核心数据结构
Flight_OrientationMap_Model_Item_ConfigBase_Station_Entity_RecordAircraft_Entity_Record
并发和异步语义
地形高度异步采样,结果以位置和地形签名缓存;完成后请求下一次 Cesium 同步。模型加载由 Cesium 管理。
算法和数值语义
位置使用 WGS84 经纬高;姿态使用 heading/pitch/roll 和四元数;基站纵向世界比例由米制高度差与 GLB 原始高度比计算。
关键源码
Flight_Orientation/VTOmodule/Local_Server/Aircraft/Flight_VTO.h:3-22
sync_base_station()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:768-801
create_base_station_record()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:802-893
base_station_heights()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:1084-1092
base_station_node_transformations()third_party/eacp_webapp/src/Map/Cesium_Map.tsx:1163-1169
性能成本
模型 Draw Call、标签、地形采样和每次位置变化的矩阵/四元数更新。
容易出现的问题
- 飞机头朝下或翅膀竖直;
- 模型原点不在底部/挂点;
- 基站和设备高度互换;
- 显示缩放改变物理顶部;
- 辅助范围几何抢占 picking。
修改注意事项
模型颜色保持 GLB 原色,配置颜色只用于标注。替换模型必须设置原始尺寸和初始角度。基站底座不可绑定完整业务交互;设备是选择入口。
可验证方式
从四个方位和俯视检查同一基站;使用已知东西南北和爬升/下降轨迹检查机头;切换固定屏幕尺寸观察近远行为。
可改进方向
上传模型时解析 bounds、根节点、单位和挂点,保存显式 mount_offset 与水平尺寸节点,而不是依赖固定节点名。
3.9 多目录瓦片异步读取和 HTTP 缓存
难度等级: 中高
复杂度来源: 异步、文件系统、HTTP 语义
所属模块: tiles.cpp、Ucoro_Drogon_Glue.h
当前状态: 已实现
解决的问题
按配置目录顺序提供本地影像/地形瓦片,且文件系统查找和读取不阻塞 Drogon 网络线程。
为什么困难
查找、stat、打开、长度和读取都可能阻塞;GET/HEAD、304、404、读取失败和客户端生命周期必须只完成一次回调。缓存验证还要随文件变化。
当前架构
Tile_Path_Resolver 按目录顺序查找。Tile_Source::load() 在专用 Asio executor 完成所有文件操作,返回拥有字节数据和缓存元数据的结果;Drogon 协程桥回事件循环构造响应。
核心执行流程
- 解析路由 z/x/y。
- 专用执行器按目录查找。
- 获取文件大小、修改时间和 ETag。
- 命中条件请求则返回 304 元数据。
- HEAD 不读正文,GET 异步读取文件。
- 回 Drogon 事件循环构造唯一响应。
核心数据结构
Tile_ConfigTile_Path_ResolverTile_Load_ResultTile_Cache_Request
并发和异步语义
文件操作在独立 Asio 池,不在 Drogon 网络线程。当前该桥接池大小为 1,受控但可能成为高并发串行瓶颈。
算法和数值语义
目录查找 O(根目录数),不扫描目录树。单文件上限 32 MB,长度转换前有边界检查。
关键源码
Tile_Path_Resolvermodule/Local_Server/server/tiles.cpp:162-200
Tile_Source::load()module/Local_Server/server/tiles.cpp:202-295
register_tile_handler()module/Local_Server/server/tiles.cpp:297-324
to_drogon()module/Local_Server/server/Ucoro_Drogon_Glue.h:102-110
性能成本
每请求若干次文件存在性/stat 检查、一次打开读取和响应内存。依赖操作系统文件缓存和浏览器 HTTP 缓存。
容易出现的问题
- 只把最后的 read 投递线程池,前面的 exists/stat 仍阻塞网络线程;
- 条件请求和 HEAD 仍读文件;
- 多目录顺序错误;
- 回调重复;
- ETag 不随文件变化。
修改注意事项
影像和地形必须复用同一流程。新增格式差异只能放配置/MIME 层;不能为路由复制一套文件读取。
可验证方式
测试两个目录层级、文件哈希、404、HEAD、304 和文件修改后 ETag;并发请求并采样 Drogon 线程。
可改进方向
根据磁盘和并发测试调整专用池大小;保持有限并发,不增加应用层全量预加载。
3.10 Cesium 相机模式和视图持久化
难度等级: 中
复杂度来源: 状态机、输入、生命周期
所属模块: Cesium_Camera_Control.ts、Map_View.tsx
当前状态: 已实现
解决的问题
提供地表导航、地心轨道和自由观察三种显式操作方式,并保存/恢复 3D 相机位置。
为什么困难
自定义事件处理必须禁用 Cesium 默认输入并在模式退出时完整恢复;跟随飞机、飞行动画、参考坐标系和历史视角会互相影响。
当前架构
一个控制器保存 Cesium 默认输入状态。切换模式先销毁旧 handler;地表模式恢复默认控制,另外两种模式创建独立 ScreenSpaceEventHandler。
核心执行流程
选择模式 → 取消跟随/飞行 → 配置输入 → 鼠标事件更新相机 → 离开模式销毁 handler → 恢复默认控制。
核心数据结构
Camera_Control_Mode- 默认 controller state
- free-look position/heading/pitch/roll
Map_Camera_View
并发和异步语义
全部在主线程事件循环。相机保存使用 HTTP 异步接口,但相机操作本身同步。
算法和数值语义
地心轨道以 ECEF 原点为旋转中心;自由观察固定相机位置,只改变 heading/pitch;俯仰角限制在极点以内。
关键源码
Cesium_Camera_Control.set_mode()third_party/eacp_webapp/src/Map/Cesium_Camera_Control.ts:32-55
enable_earth_orbit()third_party/eacp_webapp/src/Map/Cesium_Camera_Control.ts:64-87
enable_free_look()third_party/eacp_webapp/src/Map/Cesium_Camera_Control.ts:88-121
load/save_map_view_config()third_party/eacp_webapp/src/Map/Map_View.tsx:83-90
性能成本
鼠标移动时相机矩阵更新和 requestRender,成本较低;主要风险是 handler 重复。
容易出现的问题
- 多个 handler 同时处理输入;
- 切回地表模式仍禁用缩放;
- trackedEntity 下一帧覆盖手动视角;
- 保存飞行中间状态。
修改注意事项
每个新增模式必须有对称销毁和默认状态恢复。改变相机前要明确是否取消跟随、是否保存上一视角。
可验证方式
反复切换模式、跟随和页面;检查每种鼠标语义、相机位置不变条件、刷新后恢复。
可改进方向
给相机状态增加明确版本和保存节流,避免拖动过程中高频持久化。
3.11 后端/前端/Cesium 静态资源组合构建
难度等级: 中
复杂度来源: 构建、部署、第三方约束
所属模块: CMake、Vite、Drogon 静态服务
当前状态: 已实现
解决的问题
让按需拆分的 Cesium JS、Workers、Assets、ThirdParty、Widgets 和本地 GLB 在开发代理和生产后端托管下使用相同 URL。
为什么困难
Cesium runtime 资源不是普通 JS import;开发服务器和生产输出目录不同;SPA 直接刷新还需要服务端 fallback。
当前架构
Vite 定义固定 CESIUM_BASE_URL,插件开发期挂载静态中间件、构建期复制 Cesium 和模型目录;React 对三维模块动态 import;Drogon 托管生产资源。
核心执行流程
npm build → Vite 拆 chunk → copy Cesium/model → 生成 wwwroot → 后端静态路由提供 /ui/*。
核心数据结构
不涉及业务结构;关键是输出目录和 URL 合同。
并发和异步语义
浏览器按需下载 Cesium chunk 和 Workers;2D 路由不执行三维动态模块。
算法和数值语义
不涉及。
关键源码
React.lazy()third_party/eacp_webapp/src/App.tsx:23-26
cesiumAssetsPlugin()third_party/eacp_webapp/vite.config.js:21-53
CESIUM_BASE_URLthird_party/eacp_webapp/vite.config.js:99-101
ecap_server构建目标main.cmake:40-59
性能成本
首次进入 3D 下载较大 Cesium chunk;2D 初始加载不承担该成本。
容易出现的问题
- Workers 404;
- 开发可用、生产 base URL 错误;
- 2D 公共模块运行时 import Cesium;
- 直接刷新
/ui/map3d返回 404。
修改注意事项
Cesium CSS 和渲染适配器不能移到公共入口;修改 /ui base 必须同时修改 Vite、后端静态路由和部署路径。
可验证方式
检查生产输出、刷新 2D/3D、Network 中 Cesium chunk 和 Workers;离线运行确认无 ion/CDN 请求。
可改进方向
在 CI 增加生产静态资源链接检查和 Playwright 网络断言。
4. 技术难点分级
P0:系统核心且修改风险极高
- 多执行器数据源协程和有序解析:决定数据是否能持续、按序、可停止地进入系统。
- ADS-B/CPR 三维位置和基站范围过滤:错误会直接删除正确位置或保留异常位置。
- WebSocket 版本增量与轨迹监控状态:连接级状态错误会造成前端长期不一致。
- Cesium 生命周期和大对象增量同步:错误通常表现为页面卡死、GPU/内存泄漏或交互失效。
- Terrarium + LOS Worker 管线:跨坐标、跨线程、跨缓存,结果过期与高度基准都影响正确性。
P1:重要且需要专门设计
- 统一 2D/3D 业务状态:决定两套地图是否长期保持同一业务语义。
- 基站、设备和飞机三维坐标与姿态:需要模型资产、位置和配置同时正确。
- 多目录瓦片异步读取和 HTTP 缓存:直接影响网络线程稳定性和地图加载。
P2:局部复杂但边界清晰
- Cesium 相机模式和视图持久化:局限在相机控制器和视图配置。
- 组合构建和静态资源部署:边界是构建输出与 URL 合同,可通过集成测试覆盖。
5. 面试表达摘要
多执行器数据源协程
系统需要同时接入网络、串口、文件和 DLL 数据源,协议解析不能阻塞网络线程,同一数据源又必须保持消息顺序。项目用 standalone Asio 协程统一输入生命周期,在 io_context 上读取,再切到受控处理线程池;不等待模式为每个数据源使用 strand 保序。难点不在 co_await 语法,而在 executor 所有权、退出等待和任务积压。当前实现已分离网络与解析,后续重点是增加有界队列和可观测背压。
ADS-B/CPR 和基站过滤
位置解码同时涉及奇偶 CPR、地面参考站、球面距离、三维高度和理论无线电视距。项目把 haversine、视距和范围判断集中在 CPR 模块,宿主只传有效基站三维位置和约束,避免多份距离算法漂移。取舍是先采用地球几何视距与宽松系数,不把完整传播模型混入位置解码。正确性通过已知报文、边界经纬度和过滤阈值对照验证。
WebSocket 版本增量
原先轮询全量飞机会随目标数快速增加带宽和前端处理。项目改为连接级 WebSocket 状态:前端提交每架飞机版本和轨迹游标,后端返回变化、删除和被监控轨迹。全部监控是独立模式,不等同于手动集合。主要取舍是后端维护连接状态换取小消息和低重复计算;当前仍需把锁内发送优化为锁外发送,并加入慢客户端背压指标。
Cesium 增量同步与生命周期
三维页面的难点是不能把实时消息直接等同于 React 重渲染和 Cesium 全量重建。项目动态加载 Cesium,用业务 ID Map 复用 Entity/Primitive,普通轨迹点采用 PointPrimitive,并把飞机同步拆成数量和时间双阈值的片段。卸载路径集中销毁 Viewer、Worker、Provider、监听器和定时器。该方案保持业务模型独立,未来可将渲染器替换为 Primitive/实例化而不改变数据协议。
Terrarium 与 LOS Worker
地形使用 Terrarium PNG,但 Cesium 需要高度数组;LOS 又要求沿地球曲率下的真实三维直线采样。项目把 PNG fetch、OffscreenCanvas 解码和 ECEF LOS 放入 Worker,主线程只传普通数字协议。固定 Worker Pool 限制并行,实体 generation 丢弃过期结果。取舍是每个 Worker 保留独立 LRU,代码简单但可能重复内存;后续应做同实体任务合并和队列背压。
统一 2D/3D 状态
Leaflet 与 Cesium 的显示对象完全不同,但飞机、轨迹和监控是同一业务。项目用普通 TypeScript Aircraft_Model 和 Data_Source 作为唯一状态,二维和三维只在适配层分叉;显示配置分别存储,监控状态共享。这样 2D 页面不会因模型类型导入 Cesium chunk,也避免两套 WebSocket 解析。修改业务字段时必须先进入统一模型,再分别适配显示。
三维模型坐标和姿态
飞机 GLB 可能有不同机头轴和初始角,基站还需要地形底部、配置顶部和设备挂点一致。项目用每模型初始 HPR 配置修正姿态,用最后两个三维轨迹点提供方向;基站纵向按真实高度缩放,横向显示大小独立,设备位于顶部并承担交互。关键取舍是保持 GLB 原色,配置颜色只用于标注。替换模型时必须重新标定 bounds、原点和角度。
异步瓦片服务
本地瓦片可能分布在多个目录,浏览器还要求 GET、HEAD、304 和缓存语义。项目将目录查找、stat、打开、长度和读取整体放到专用 Asio executor,再回 Drogon 事件循环构造唯一响应;目录按配置顺序短路,不扫描全盘。方案依赖浏览器和操作系统缓存,不引入复杂应用 LRU。后续优化应基于并发吞吐测试调整有限文件 I/O 并行度。
6. 源码导航索引
| 技术点 | 核心入口 | 辅助实现 | 调用方 | 测试或验证代码 |
|---|---|---|---|---|
| 数据源协程 | Data_Source::loop_coro() |
With_Loop_Coro |
Coro::coro_thread() |
运行压力测试,现无专用单测 |
| CPR/范围 | CPR.cpp |
BaseStation.cpp |
Data_Source_Handler.cpp |
CPR_TEST.cpp |
| WebSocket 增量 | register_aircraft_stream_ws() |
aircraft_stream_source_update_json() |
Aircraft_Stream_Client |
浏览器 WebSocket 集成测试待补 |
| Cesium 同步 | sync_data_sources() |
sync_aircraft() |
request_sync_data_sources() |
Chrome Performance/Memory |
| Terrarium | Terrarium_Terrain_Provider |
Terrarium_Terrain_Worker |
Cesium_Map.load_map() |
已知高程点对照待自动化 |
| LOS | calculate_los() |
Worker_Pool |
update_aircraft_occlusion() |
遮挡场景集成测试待补 |
| 统一模型 | Aircraft_Model |
Data_Source |
Leaflet/Cesium | npm run typecheck/lint:src |
| 模型姿态 | sync_base_station() |
Flight_VTO |
Cesium records | 多方位视觉检查 |
| 瓦片 | Tile_Source |
to_drogon() |
init_tiles() |
HTTP GET/HEAD/304/并发 |
| 相机 | Cesium_Camera_Control |
Map_View |
子数据源控制面板 | 页面往返和输入检查 |
| 组合构建 | vite.config.js |
main.cmake |
Drogon 静态服务 | npm run build、生产刷新 |