Files
StandardSence/Doc/ARCHITECTURE.md
T

526 lines
20 KiB
Markdown
Raw Normal View History

# 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.dllscene.mag
├── StandardScene.QrLidar → 输出 StandardScene.QrLidar.dllscene.qrlidar
├── StandardScene.Devices → 输出 StandardScene.Devices.dllscene.device
└── StandardScene.Protocol.VDA5050 → 输出 StandardScene.Protocol.VDA5050.dllscene.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` | 否 | VDA5050MQTT)协议栈与标准车型 |
---
## 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 / SegmentPlanSimpleCore
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 NuGetCore 最重)
`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-classWebApi、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
→ TransportMissionChainedDeliveryMission.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 专题手册 |
---
## 附录 Ascene.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 启动即可生效** 的全局初始化入口之一。