# StandardScene 代码架构文档

> **目标读者**：接手本仓库的工程师、AI Agent、架构审查人员  
> **最后更新**：2026-06-25  
> **配套文档**：`DEVELOPMENT_GUIDE.md`（开发实操）、`QUICK_REFERENCE.md`（速查）、`StandardScene架构重构方案.md`（演进方向）

---

## 1. 项目定位

StandardScene 是一套 **AGV/AMR 场内调度与联动控制** 的场景插件库，不是独立可执行程序。

| 维度 | 说明 |
| --- | --- |
| 工程类型 | 类库插件（1 基座 + 4 卫星 = 5 个 DLL） |
| 宿主 | `SimpleLite.exe`（CycleGUI 桌面应用） |
| 内核 | `SimpleCore.dll`（Mission、交通、路径编译、导航契约） |
| 目标框架 | `net8.0-windows`（x64） |
| 语言 | C# |
| UI 演进 | WinForms → CycleGUI（`DeliveryViewer` 等已迁移） |

典型业务能力：搬运任务、环线任务、区域交通管制、充电编排、门禁/按钮盒联动、HTTP / MQTT / Modbus 对外接口。

---

## 2. 解决方案结构

```
StandardScene.sln
├── StandardScene.Core          → 输出 StandardScene.dll（基座，始终加载）
├── StandardScene.Magnetic      → 输出 StandardScene.Magnetic.dll（scene.mag）
├── StandardScene.QrLidar       → 输出 StandardScene.QrLidar.dll（scene.qrlidar）
├── StandardScene.Devices       → 输出 StandardScene.Devices.dll（scene.device）
└── StandardScene.Protocol.VDA5050 → 输出 StandardScene.Protocol.VDA5050.dll（scene.vda5050）
```

### 2.1 依赖关系（星型拓扑）

```mermaid
graph TD
    Host["SimpleLite.exe"]
    SC["SimpleCore.dll"]
    SL["SimpleLite.dll"]

    Core["StandardScene.dll<br/>基座"]
    Mag["StandardScene.Magnetic.dll"]
    Qr["StandardScene.QrLidar.dll"]
    Dev["StandardScene.Devices.dll"]
    VDA["StandardScene.Protocol.VDA5050.dll"]

    Host --> Core
    Host --> Mag
    Host --> Qr
    Host --> Dev
    Host --> VDA

    Core --> SC
    Core --> SL
    Mag --> Core
    Qr --> Core
    Dev --> Core
    VDA --> Core
```

**规则**：
- 卫星 **只能** 引用基座，禁止循环引用
- 所有插件 **运行时** 依赖 `SimpleCore` + `SimpleLite`
- 卫星通过 `InternalsVisibleTo` 访问 Core 的 `internal` 字段袋类型，避免大规模 `public` 暴露

### 2.2 插件清单对照表

| DLL | scene id | 始终加载 | 职责 |
| --- | --- | --- | --- |
| `StandardScene.dll` | — | 是 | 任务调度、交通互锁、充电编排、门禁抽象、HTTP API、数据模型 |
| `StandardScene.Magnetic.dll` | `scene.mag` | 否 | 磁导航车型 + 磁循迹 Coder |
| `StandardScene.QrLidar.dll` | `scene.qrlidar` | 否 | 激光 SLAM + 二维码导航，多种车型 |
| `StandardScene.Devices.dll` | `scene.device` | 否 | Modbus 门控、充电桩、按钮盒具体驱动 |
| `StandardScene.Protocol.VDA5050.dll` | `scene.vda5050` | 否 | VDA5050（MQTT）协议栈与标准车型 |

---

## 3. 运行时架构

### 3.1 启动与加载流程

```mermaid
sequenceDiagram
    participant Host as SimpleLite.exe
    participant PM as PluginManager
    participant Core as StandardScene.dll
    participant Sat as 卫星 DLL

    Host->>PM: 扫描 plugins\ 目录
    PM->>Core: 加载 requiresCore 指向的基座（不可回收）
    PM->>PM: 读取 active-scenes.json / CLI --scenes
    PM->>Sat: 按场景 id 加载选中的卫星 DLL
    Sat->>Core: 引用已加载的基座类型
    PM->>PM: UiTypeDiscovery 反射 [MissionType]/[CarType]/[DoorType] 等
    alt 导航插件
        PM->>Sat: 实例化 NavigationProfileBase → OnActivate
    end
    Host->>Core: CustomOperationsBeforeLoading.Set()（死锁回调等）
```

**场景选择优先级**（高 → 低）：
1. CLI 参数 `--scenes`
2. `active-scenes.json`
3. `simple.json` 中的 `scenes` 字段
4. 加载全部已发现的卫星

> **注意**：若 `active-scenes.json` 仅包含 `scene.mag`，则 `scene.device` / `scene.vda5050` 不会加载，门控/充电桩/VDA5050 车型将不可用。生产部署需显式包含所需场景 id。

### 3.2 构建与部署

`Directory.Build.targets` 在编译后自动将产物复制到 `build\plugins\`：

- 各插件 DLL + PDB
- 各卫星的 `*.scene.json`
- Devices 插件附带的 `leegKeys-sdk.dll`

部署步骤：
1. 先构建宿主：`dotnet build Simple\SimpleLite\SimpleLite.csproj`
2. 构建本方案：`dotnet build StandardScene.sln`
3. 将 `build\plugins\` 内容复制到 `SimpleLite.exe` 工作目录的 `plugins\`
4. 配置 `active-scenes.json` 后启动 `SimpleLite.exe`

### 3.3 业务运行时链路

```
外部系统 (HTTP/MQTT/Modbus)
        ↓
WebApi / 设备驱动 / 车型通信
        ↓
Mission（后台进程：搬运/环线/充电/门控/心跳…）
        ↓
SimpleLib / TrafficControl / SegmentPlan（SimpleCore）
        ↓
GhostCar / Car 实例（车辆状态机 + 下位机通信）
        ↓
地图站点 / 轨道 / fields & tags 配置
```

---

## 4. 核心概念

### 4.1 Mission（场景进程）

Mission 是本系统的 **可启动后台业务单元**，继承自 `SimpleCore.Mission`，由宿主 UI 或 HTTP 反射 API 启停。

**生命周期模式**（以 `HeartBeatMission` 为范本）：

| 阶段 | 典型实现 |
| --- | --- |
| 注册 | `[MissionType(Name="...", editor=typeof(...))]` |
| 工厂 | `public static Mission Create()` |
| 启动 | `[MethodMember(Name="启动进程")] public override void Execute()` |
| 后台 | `Thread` / `Task.Run` / `async` + `CancellationTokenSource` |
| 状态 | 更新 `status.status` 或自定义 `MissionStatus` 子类 |
| 停止 | `[MethodMember(Name="关闭进程")] public void Stop()` |

**Mission 继承树**：

```
Mission (SimpleCore)
├── HeartBeatMission, RegionalTrafficControlMission, SecuritySignalMission
├── NodeIsEnableMission
├── DoorMission, ButtonMission
├── AbstractInterlockMission
│   ├── TrafficInterlockMission
│   └── AbstractChargeLogicMission → StandardChargeMission
├── ChainedDeliveryMission → TransportMission（搬运调度）
└── AbstractLoopMission → LoopMission（环线任务）
```

### 4.2 CarType（车型）

车型代表一种 AGV 下位机协议与行为模型，继承自 `SimpleLite.RCS.CarTypes.GhostCar` 或 `Car`。

**注册三要素**：

1. **类型特性**：`[CarType(Name="显示名", editor=typeof(XxxCar))]`
2. **轨迹编码器**：`[ProgramTrackCoderSettings(priority=N, program=typeof(XxxCoder))]`
3. **场景清单**：`scene.json` 的 `provides.carTypes` +（导航插件）`NavigationProfileBase.CarTypes`

**Coder（轨迹编码器）** 实现 `SimpleCore.Compiler.ITrackCoder`，在路径规划时将站点/轨道上的 `fields` 编译为 Topaz 脚本片段（如 `agv.MagGo`、`agv.BasicGo`）。

### 4.3 设备驱动（门 / 充电桩 / 按钮盒）

设备采用 **抽象在 Core、实现在 Devices** 的模式：

| 设备 | Core 抽象 | Devices 实现 | 发现机制 |
| --- | --- | --- | --- |
| 门控 | `BasicDoorController` | `ModbusDoorController` | `[DoorType("...")]` + `UiTypeDiscovery` |
| 充电桩 | `AbstractChargeStation` | `FL/PCB/MuXingChargeStation` | 类型全名 + `UiTypeDiscovery` |
| 按钮盒 | `BasicButtonBox` | `Leeg/AzowieButtonBox` | 类名 + `UiTypeDiscovery` |

### 4.4 Fields & Tags（轻量配置）

除 JSON 配置文件外，系统大量使用地图上的 **fields**（键值对）和 **tags** 驱动行为，例如：
- 站点 `Region1=1` → 区域流控
- 轨道 `Magnet=1` → 磁导航段
- 车辆 `address` → 下位机 IP

通用读写入口：`Commons.cs` 中的 `GetXxxField` / `AddOrUpdateXxxField` 系列方法。

### 4.5 NavigationProfile（导航场景画像）

仅 **Magnetic** 与 **QrLidar** 两个导航卫星实现 `SimpleCore.Navigation.NavigationProfileBase`：

```csharp
public sealed class MagneticSceneProfile : NavigationProfileBase
{
    public override NavKind Kind => NavKind.Magnetic;
    public override string SceneId => "scene.mag";
    public override IReadOnlyList<Type> CarTypes => new[] { typeof(MagCar) };
    public override void OnActivate(ISceneContext context) { ... }
}
```

`scene.json` 与 `NavigationProfile` 中的 `CarTypes` **必须保持一致**。

---

## 5. 模块地图（StandardScene.Core）

基座约 110+ 源文件，按目录职责划分如下：

| 目录 | 职责 | 关键类型 |
| --- | --- | --- |
| `Scheduler/` | 辅助后台 Mission（心跳、区域流控、安全信号） | `HeartBeatMission`, `RegionalTrafficControlMission` |
| `Chained/` | 搬运与环线任务主流程 | `ChainedDeliveryMission`, `TransportMission`, `AbstractLoopMission`, `LoopMission` |
| `Chained/Loop/` | 环线规则策略接口 | `IEnterRule`, `IExitRule`, `IJoinRule`, `IBranchRule`, `ITaskStrategy` |
| `Charge/` | 充电策略、站点管理、UDP 通信、配置 UI | `StandardChargeMission`, `AbstractChargeLogicMission` |
| `ChargeStationType/` | 充电桩抽象基类 | `AbstractChargeStation` |
| `InterLock/` | 区域互锁与交通管制 | `AbstractInterlockMission`, `TrafficInterlockMission` |
| `ExtendDevice/Door/` | 门控抽象、管理器、Mission | `BasicDoorController`, `DoorMission`, `DoorManager` |
| `ExtendDevice/ButtonBox/` | 按钮盒抽象与管理 | `BasicButtonBox`, `ButtonMission`, `ButtonBoxManager` |
| `CarTypes/` | 共享字段袋、模拟车 | `BasicFields`, `DummyCar`, `KivaFields` |
| `Coders/` | 导航无关的通用轨迹编码器 | `CommonTrackCoders`, `LidarAreaSwitchCoder` |
| `Model/` | 任务、地图、配置数据模型 | `TaskModel`, `LoopTask`, `Map`, `ChargingSetting` |
| `TCP/` | 异步 TCP 客户端 | `AsyncTcpClient` |
| `Utils/` | JSON、Modbus、WebAPI、CycleGUI 辅助 | `WebAPIHelper`, `CycleUiHelper`, `CarRemoteHelper` |
| `CommonTools/` | 原子文件写入、雪花 ID | `AtomicFileUpdateHelper`, `SnowflakeIdGenerator` |
| 根目录 | 全局工具与 HTTP 入口 | `Commons.cs`, `WebApi.cs`, `Heuristic.cs`, `LadderLogic.cs` |

### 5.1 卫星插件内容

**StandardScene.Magnetic**（5 文件）
- `CarTypes/MagCar.cs` — 磁导航 UDP 车型
- `Coders/MagneticTrackCoder.cs` — `agv.MagGo` / `agv.NaiveMagGo`
- `MagneticSceneProfile.cs` + `StandardScene.Magnetic.scene.json`

**StandardScene.QrLidar**（12 文件）
- `CarTypes/` — Forklift, Kiva, ArmCar, DualLiftingCar, MultiVehicleCar, MultiWheelForkLifter, MultiWheelLifterCar
- `Cad/SyncQrMap.cs` — 二维码地图同步 CAD 工具
- `QrLidarSceneProfile.cs` + `StandardScene.QrLidar.scene.json`

**StandardScene.Devices**（8 文件）
- `Door/ModbusDoorController.cs`
- `Charge/FLChargeStation.cs`, `PCBChargeStation.cs`, `MuXingChargeStation.cs`
- `ButtonBox/LeegButtonBox.cs`, `AzowieButtonBox.cs`
- `StandardScene.Devices.scene.json`

**StandardScene.Protocol.VDA5050**（13 文件）
- `VDACar/VDA5050Car.cs` — MQTT VDA5050 标准车
- `VDACar/MasterMQTTCommunication.cs` — MQTT 通信层
- `StandardScene.Protocol.VDA5050.scene.json`

### 5.2 已注册车型一览

| 类名 | 所属插件 | 显示名 |
| --- | --- | --- |
| `DummyCar` | Core | 模拟车-包络 |
| `MagCar` | Magnetic | 磁导航车 |
| `Forklift` | QrLidar | 叉车 |
| `MultiWheelForkLifter` | QrLidar | 多舵轮叉车 |
| `DualLiftingCar` | QrLidar | 锂电双举升 |
| `MultiVehicleCar` | QrLidar | 多车联动 AGV |
| `ArmCar` | QrLidar | ArmCar |
| `Kiva` | QrLidar | Kiva |
| `MultiWheelLifterCar` | QrLidar | 多舵轮顶升车 |
| `VDA5050Car` | Protocol.VDA5050 | VDA5050 标准车 |

### 5.3 已注册 Mission 一览

| 类名 | 显示名 | 目录 |
| --- | --- | --- |
| `HeartBeatMission` | 调度心跳进程 | Scheduler |
| `RegionalTrafficControlMission` | 区域流量监控 | Scheduler |
| `SecuritySignalMission` | 安全信号交互 | Scheduler |
| `NodeIsEnableMission` | 锁点上传迷毂 | Scheduler |
| `TransportMission` | 搬运任务进程 | Chained |
| `LoopMission` | 环线进程 | Chained |
| `StandardChargeMission` | 充电进程 | Charge |
| `TrafficInterlockMission` | 交通管制 | InterLock |
| `DoorMission` | 门控进程 | ExtendDevice/Door |
| `ButtonMission` | 按钮进程 | ExtendDevice/ButtonBox |

---

## 6. 扩展点指南

接手后最常见的三类扩展：

### 6.1 新增 Mission

1. 在 `StandardScene.Core` 合适目录新建类，继承 `Mission`（或现有抽象基类）
2. 添加 `[MissionType(Name="...", editor=typeof(...))]`
3. 实现 `Create()`、`Execute()`、`Stop()` 三件套
4. **参考**：`Scheduler/HeartBeatMission.cs`（最小）、`Scheduler/RegionalTrafficControlMission.cs`（事件订阅）

### 6.2 新增车型（导航卫星）

1. 确定目标卫星（Magnetic / QrLidar / VDA5050）
2. 新建 `CarTypes/XxxCar.cs`，继承 `GhostCar`，标注 `[CarType]` + `[ProgramTrackCoderSettings]`
3. 如需新 Coder，在同插件 `Coders/` 实现 `ITrackCoder`
4. 更新 `XxxSceneProfile.CarTypes` 与 `*.scene.json` 的 `provides.carTypes`
5. 字段袋扩展放在 Core `CarTypes/`（`internal`，卫星通过 `InternalsVisibleTo` 访问）

### 6.3 新增设备驱动

1. 在 `StandardScene.Devices` 实现 Core 抽象（如 `BasicDoorController`）
2. 添加类型特性（如 `[DoorType("MyDoor")]`）
3. 更新 `StandardScene.Devices.scene.json` 的 `provides` 列表
4. 确保 `active-scenes.json` 包含 `scene.device`

### 6.4 新增 HTTP 接口

入口在 `StandardScene.Core/WebApi.cs`（`ApiController`，Nancy 框架）。常见路由前缀：
- `/car/*` — 车辆与任务
- `/map/*` — 地图
- `/task/*` — 任务查询
- `/mission_reflection/*` — Mission 反射调用

> 长期计划是将 WebApi 从 Core 拆出并迁移到 SimpleLite EmbedIO，当前仍以 `WebApi.cs` 为唯一活跃 HTTP 入口。

---

## 7. 配置文件

| 文件 | 位置 | 用途 |
| --- | --- | --- |
| `active-scenes.json` | 宿主 `plugins\` | 选择加载哪些卫星场景 |
| `simple.json` | 宿主工作目录 | 宿主基础配置（含 scenes 备选） |
| `Config/traffic.json` | 运行时 | 交通互锁 / 区域配置 |
| `Config/ChargeStations.json` | 运行时 | 充电桩定义 |
| `Config/ChargeStrategyConfig.json` | 运行时 | 充电策略 |
| `Config/AlarmConfigs.json` | 运行时 | 报警配置 |
| `DoorConfig.json` | 运行时 | 门禁配置 |
| `tasklist.json` | 运行时 | 环线任务配置 |

---

## 8. 外部依赖

### 8.1 程序集引用（HintPath，需本机构建）

| DLL | 用途 |
| --- | --- |
| `SimpleLite.dll` | 宿主框架：RCS、CAD、UI、`UiTypeDiscovery`、特性标注 |
| `SimpleCore.dll` | 内核：Mission、Car、交通、路径编译、导航契约 |
| `CommonUsage.dll` | 数学、VDA5050 消息类型 |
| `Topaz.dll` | Coder 脚本模板引擎 |
| `CycleGUI.dll` | 3D / 立即模式 UI（`Private=false`，由宿主加载） |
| `MDCSToolBox.dll` | 运动学 / 数学（Core、QrLidar） |
| `LessokajiWeaverUtilities.dll` | i18n、诊断工具 |
| `leegKeys-sdk.dll` | Leeg 按钮盒 SDK |

### 8.2 NuGet（Core 最重）

`Nancy`, `MQTTnet`, `EasyModbusTCP`, `IoTClient`, `Jint`, `DocumentFormat.OpenXml`, `Newtonsoft.Json`

Protocol.VDA5050 额外使用 `MQTTnet`；Magnetic / QrLidar 仅 `Newtonsoft.Json`。

---

## 9. 关键设计决策与现状问题

### 9.1 当前设计的合理之处

- **星型插件拓扑**：基座承载全部 Mission 与设备抽象，卫星按场景热插拔
- **`scene.json` 清单**：声明式描述插件能力，宿主无需硬编码
- **`NavigationProfileBase`**：导航平台与车型注册的标准契约
- **`Chained/Loop/` 规则接口**：环线策略可插拔（`IEnterRule` 等）
- **`InternalsVisibleTo` 拆分策略**：在不破坏封装的前提下外移车型代码

### 9.2 已知架构债务（详见 `StandardScene架构重构方案.md`）

| 编号 | 问题 | 影响 |
| --- | --- | --- |
| S1 | Core 背负 MQTT/Modbus/Nancy/OpenXml 等协议依赖 | 基座臃肿，卫星无法独立瘦身 |
| S2 | WinForms 与 CycleGUI 并存 | 阻塞纯 `net8.0` 跨平台 |
| S3 | 多个 600~2700 行 God-class（WebApi、AbstractLoopMission 等） | 改动风险高、难测试 |
| S8 | Coder 注册表限定内核程序集反射 | 导航 Coder 热插拔受限 |
| S9 | csproj 绝对路径 HintPath | CI / 换机构建需手动适配 |

### 9.3 演进方向（摘要）

1. 抽离 `StandardScene.Abstractions`（纯契约 + 模型）
2. 充电子系统独立为卫星 `StandardScene.Charge`
3. WebApi 按资源拆模块并迁出 Core
4. UI 全部迁至 CycleGUI 或独立 UI 程序集
5. 目标 TFM 从 `net8.0-windows` 过渡到 `net8.0`（基础设施层）

---

## 10. AI / 新工程师接手清单

### 10.1 第一天：建立全局认知

1. 阅读 `README.md` → 本文档 → `DEVELOPMENT_GUIDE.md`
2. 打开 `DocumentHub.html` 浏览模块导航
3. 本地构建：先 SimpleLite，再 StandardScene，部署到 `plugins\`
4. 启动宿主，确认 `active-scenes.json` 包含所需场景

### 10.2 第二天：跟踪一条完整链路

**搬运任务链路**（推荐）：
```
WebApi /car/createTask
  → TransportMission（ChainedDeliveryMission.LoopAsync）
    → Commons.NearestTask / 选车逻辑
      → GhostCar 下发路径（SegmentPlan + Coder 脚本）
        → TrafficControl 互锁
          → 下位机 HTTP/MQTT/UDP 通信
```

**充电链路**：
```
StandardChargeMission
  → AbstractChargeLogicMission（电量策略）
    → AbstractInterlockMission（站点互锁）
      → AbstractChargeStation 实例（Devices 插件驱动）
```

### 10.3 按任务类型定位文件

| 我要做… | 先看 |
| --- | --- |
| 最小 Mission 样板 | `Scheduler/HeartBeatMission.cs` |
| 搬运调度 | `Chained/ChainedDeliveryMission.cs`, `Chained/TransportMission.cs` |
| 环线任务 | `Chained/AbstractLoopMission.cs`, `Chained/LoopMission.cs` |
| 区域流控 | `Scheduler/RegionalTrafficControlMission.cs` |
| 充电 | `Charge/StandardChargeMission.cs`, `Charge/AbstractChargeLogicMission.cs` |
| 门控 | `ExtendDevice/Door/DoorMission.cs`, `Devices/Door/ModbusDoorController.cs` |
| HTTP API | `WebApi.cs` |
| 新车型 | 对应卫星 `CarTypes/` + `Coders/` + `*SceneProfile.cs` |
| 字段/选车/路径辅助 | `Commons.cs` |
| 地图与任务模型 | `Model/` |

### 10.4 修改前的安全检查

- [ ] 确认目标变更属于 Core 还是卫星（避免在 Core 放平台专有逻辑）
- [ ] 若改车型，同步 `scene.json` + `NavigationProfile.CarTypes`
- [ ] 若改设备驱动，确认 `scene.device` 在 `active-scenes.json` 中
- [ ] 若改 Coder，注意 `priority` 与字段袋哨兵值（`-1` 语义）
- [ ] 编译后检查 `build\plugins\` 产物是否完整

---

## 11. 文档索引

| 文档 | 用途 |
| --- | --- |
| **本文档 `ARCHITECTURE.md`** | 整体架构、模块地图、扩展点、接手清单 |
| `DEVELOPMENT_GUIDE.md` | 开发环境、案例教程、调试建议 |
| `QUICK_REFERENCE.md` | 高频入口与配置速查 |
| `StandardScene架构重构方案.md` | 架构演进与分层目标 |
| `StandardScene拆分计划.md` | 程序集拆分进度 |
| `StandardScene代码审查报告.md` | 质量缺陷与修复记录 |
| `StandardScene.Core/Docs/` | 充电、门控、Coder 专题手册 |

---

## 附录 A：scene.json 完整示例

**导航插件（scene.mag）**：
```json
{
  "id": "scene.mag",
  "displayName": "磁导航平台",
  "navKind": "magnetic",
  "assembly": "StandardScene.Magnetic.dll",
  "coreVersion": ">=1.0.0",
  "requiresCore": "StandardScene.dll",
  "provides": {
    "carTypes": ["MagCar"],
    "missionTypes": []
  }
}
```

**设备插件（scene.device）**：
```json
{
  "id": "scene.device",
  "displayName": "设备驱动（门 / 充电桩 / 按钮盒）",
  "assembly": "StandardScene.Devices.dll",
  "coreVersion": ">=1.0.0",
  "requiresCore": "StandardScene.dll",
  "provides": {
    "doorControllers": ["ModbusDoorController"],
    "chargeStations": ["FLChargeStation", "PCBChargeStation", "MuXingChargeStation"],
    "buttonBoxes": ["LeegButtonBox", "AzowieButtonBox"]
  }
}
```

## 附录 B：插件引导钩子

基座在宿主加载早期通过反射调用：

```csharp
// StandardScene.Core/Commons.cs
public class CustomOperationsBeforeLoading
{
    public static void Set()
    {
        TrafficControl.OnDeadLock = (loopingCar) => { /* 死锁告警 */ };
    }
}
```

这是少数几个 **无 Mission 启动即可生效** 的全局初始化入口之一。
