docs: 总体架构 V14 — FrameDataStore 内存回放双层存储设计

This commit is contained in:
tian 2026-06-17 16:08:58 +08:00
parent 6098186f34
commit bc8b401ef6

View File

@ -1,8 +1,9 @@
# 反无人机仿真系统 — 总体架构设计 # 反无人机仿真系统 — 总体架构设计
> **版本**V13 > **版本**V14
> **日期**2026-06-16 > **日期**2026-06-17
> **状态**:已实现(探测实时链路 + 3D 球冠待实现) > **状态**:已实现
> **变更**V14 新增 DataService 基础数据 CRUD + FrameDataStore 内存回放架构
> **Unity 版本**22.3.62 > **Unity 版本**22.3.62
> **.NET 版本**.NET Standard 2.1 > **.NET 版本**.NET Standard 2.1
> **变更**V13 探测行为决策固化——5 项规则确认(回退 Undetected / 融合取最早-同刻取精度高 / 不影响 FireSchedule / 盲区仅可视化 / 3D 球冠);新增 14.2.1 三维球冠几何IsInCoverage+ 14.7 数据模型变更清单EquipmentDeployment 表加 4 个 3D 探测字段DetectionSource 同步扩展 > **变更**V13 探测行为决策固化——5 项规则确认(回退 Undetected / 融合取最早-同刻取精度高 / 不影响 FireSchedule / 盲区仅可视化 / 3D 球冠);新增 14.2.1 三维球冠几何IsInCoverage+ 14.7 数据模型变更清单EquipmentDeployment 表加 4 个 3D 探测字段DetectionSource 同步扩展
@ -1257,38 +1258,46 @@ public interface IDefensePlanner
### 7.1 FrameDataStore 实现 ### 7.1 FrameDataStore 实现
`FrameDataStore` 是**具体类**(非接口),采用内存缓存 + 仿真结束批量落库策略,避免每帧磁盘 IO `FrameDataStore` 采用**双层存储**架构:仿真期间内存缓冲 → 仿真结束保留 LiveFrames 副本 + 批量写入 SQLite
```csharp ```csharp
/// <summary>帧数据存储 — 内存缓存,仿真结束时批量写入 SQLite</summary>
public class FrameDataStore public class FrameDataStore
{ {
void BeginRecording(string taskId); // 仿真启动:清空缓冲区 // 内存回放Flush 后保留副本,供回放直接读取(零磁盘 IO
void RecordFrame(int frameIdx, float timestamp, // 每 Tick仅写内存缓冲零字符串分配 public List<SimFrameRecord> LiveFrames { get; private set; }
List<EntitySnapshot> snapshots);
void Flush(); // 仿真结束:序列化 JSON + 单次事务批量入库 void BeginRecording(string taskId); // 清空 buffer + LiveFrames
void Discard(); // 丢弃未落库的缓冲 void RecordFrame(...); // 仅写内存 List<FrameRecord>
List<SimFrameRecord> ReadFrames(string taskId, int fromFrame, int toFrame); // 回放读 void Flush(); // 序列化 → LiveFrames 副本 + SQLite 入库
void DeleteTaskDb(string taskId); void Discard(); // 清空 buffer + LiveFrames
int CleanupExpired(); // TTL 清理(按 RetentionDays默认 180 天) List<SimFrameRecord> ReadFrames(...); // 从 SQLite 读(历史仿真回放)
int BufferedFrameCount { get; } // 已缓冲帧数
} }
``` ```
> **关键设计**`RecordFrame` 在仿真热路径上**只搬强类型标量字段**(位置/血量/云团半径等)到内存 `List<FrameRecord>`,不做任何 JSON 拼接。JSON 序列化推迟到 `Flush` 一次性完成(详见第十四章性能设计)。 **LiveFrames 生命周期**
```
仿真运行 → Flush() → LiveFrames = 当前仿真数据(同时写 SQLite
回放:优先取 LiveFrames内存
新仿真开始 → BeginRecording() → LiveFrames.Clear()
如需回放旧仿真 → 回退读 SQLite
```
### 7.2 录制 ### 7.2 录制
- 仿真启动时 `BeginRecording` 清空内存缓冲区 - 仿真启动时 `BeginRecording` 清空内存缓冲区和 LiveFrames
- 录制帧率 = 仿真 TickRate每想定可配置默认 20Hz - 录制帧率 = 仿真 TickRate每想定可配置默认 20Hz
- 每 Tick 调用 `RecordFrame()`,仅追加到内存 `List<FrameRecord>`,零磁盘 IO、零字符串分配 - 每 Tick 调用 `RecordFrame()`,仅追加到内存 `List<FrameRecord>`,零磁盘 IO、零字符串分配
- 仿真结束 `Flush()`:序列化为 `SimFrameRecord` + 单事务 `InsertAll` 批量写入 `frames/{taskId}.db` - 仿真结束 `Flush()`:序列化为 `SimFrameRecord` → 保留 `LiveFrames` 副本 → 单事务 `InsertAll` 写入 SQLite
- 仿真事件 `SimEvent` 直接写入主库 `main.db`(每任务 ~30 条,轻量) - 仿真事件 `SimEvent` 直接写入主库 `main.db`
### 7.3 回放 ### 7.3 回放
- ReplayController 协程驱动 - `ReplayController.LoadReplay(taskId, frameStore)` 双路径:
- 打开 `frames/{taskId}.db`,按 FrameIndex 范围查询 - 传入 `frameStore``LiveFrames` 非空 → 直接使用内存数据(仿真刚结束,零延迟)
- 否则 → 从 `frames/{taskId}.db` 读取(历史仿真)
- WHERE FrameIndex BETWEEN a AND b 命中索引,毫秒级 - WHERE FrameIndex BETWEEN a AND b 命中索引,毫秒级
- 回放帧率由前端控制(可与录制不同) - 回放帧率由前端控制(可与录制不同)
- 支持暂停 / 快进 / 后退 / 跳转 - 支持暂停 / 快进 / 后退 / 跳转