Files
zhaowei.huang a0dc1e6cd0 refactor: 插件 UI 从 WinForms 迁移到 CycleGUI,并修复代码质量问题
将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。

同时修复代码审核中的问题:
- 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式)
- CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告
- DummyCar 移除已废弃的 rightClickAction()/SetPosition()
- CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch
- 重命名名不副实的 Mstsc()(现为打开网页)
- 统一弃元命名为 _
- TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引
- csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props

构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。
注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
2026-06-26 15:00:53 +08:00

20 KiB
Raw Permalink Blame History

StandardScene 代码架构文档

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


1. 项目定位

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

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

典型业务能力:搬运任务、环线任务、区域交通管制、充电编排、门禁/按钮盒联动、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 依赖关系(星型拓扑)

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 启动与加载流程

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.GhostCarCar

注册三要素

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

Coder(轨迹编码器) 实现 SimpleCore.Compiler.ITrackCoder,在路径规划时将站点/轨道上的 fields 编译为 Topaz 脚本片段(如 agv.MagGoagv.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(导航场景画像)

MagneticQrLidar 两个导航卫星实现 SimpleCore.Navigation.NavigationProfileBase

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.jsonNavigationProfile 中的 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.Magnetic5 文件)

  • CarTypes/MagCar.cs — 磁导航 UDP 车型
  • Coders/MagneticTrackCoder.csagv.MagGo / agv.NaiveMagGo
  • MagneticSceneProfile.cs + StandardScene.Magnetic.scene.json

StandardScene.QrLidar12 文件)

  • CarTypes/ — Forklift, Kiva, ArmCar, DualLiftingCar, MultiVehicleCar, MultiWheelForkLifter, MultiWheelLifterCar
  • Cad/SyncQrMap.cs — 二维码地图同步 CAD 工具
  • QrLidarSceneProfile.cs + StandardScene.QrLidar.scene.json

StandardScene.Devices8 文件)

  • Door/ModbusDoorController.cs
  • Charge/FLChargeStation.cs, PCBChargeStation.cs, MuXingChargeStation.cs
  • ButtonBox/LeegButtonBox.cs, AzowieButtonBox.cs
  • StandardScene.Devices.scene.json

StandardScene.Protocol.VDA505013 文件)

  • 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.jsonprovides.carTypes
  5. 字段袋扩展放在 Core CarTypes/internal,卫星通过 InternalsVisibleTo 访问)

6.3 新增设备驱动

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

6.4 新增 HTTP 接口

入口在 StandardScene.Core/WebApi.csApiControllerNancy 框架)。常见路由前缀:

  • /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 / 立即模式 UIPrivate=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 额外使用 MQTTnetMagnetic / 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.deviceactive-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

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

设备插件(scene.device

{
  "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:插件引导钩子

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

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

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