# 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
基座"] 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 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 启动即可生效** 的全局初始化入口之一。