20 KiB
StandardScene 架构重构方案
配套文档:《StandardScene代码审查报告.md》(质量/缺陷)、《StandardScene拆分计划.md》(拆分进度) 本文聚焦结构与架构合理性:现状全景 → 逐模块深度分析 → 目标分层 → 程序集边界 → net8.0 去 Windows → 分阶段迁移路径。 日期:2026-06-09
一、现状架构全景
1.1 程序集与依赖
| 程序集 | AssemblyName | TFM | 角色 | 依赖 |
|---|---|---|---|---|
| StandardScene.Core | StandardScene |
net8.0-windows | 基座:抽象+业务+任务+调度+WebApi+UI | SimpleLite/SimpleCore/CommonUsage/MDCSToolBox/Topaz + NuGet(MQTTnet/Nancy/Jint/EasyModbus/IoTClient/OpenXml/Newtonsoft) |
| StandardScene.Devices | StandardScene.Devices |
net8.0-windows | 具体设备驱动(门/充电桩/按钮盒) | →Core + SimpleLite/SimpleCore/Topaz + leegKeys-sdk |
| StandardScene.Protocol.VDA5050 | StandardScene.Protocol.VDA5050 |
net8.0-windows | VDA5050 协议车型 | →Core + SimpleLite/SimpleCore + NuGet(MQTTnet/Nancy/Jint/Newtonsoft) |
插件清单 scene.json(Devices / VDA5050 各一份):
{ "id":"devices", "assembly":"StandardScene.Devices.dll", "requiresCore":"StandardScene.dll",
"provides": { "doorControllers":[...], "chargeStations":[...], "buttonBoxes":[...] } }
发现机制:SimpleLite.Utils.UiTypeDiscovery.AllTypes() 跨程序集扫描 + 类型特性([DoorType]/[ChargeType]/[ButtonBox...]/[CarType])。
1.2 当前依赖方向(问题版)
graph TD
Devices --> Core
VDA5050 --> Core
Core -->|NuGet| MQTTnet
Core -->|NuGet| Nancy
Core -->|NuGet| EasyModbus
Core -->|NuGet| IoTClient
Core -->|NuGet| OpenXml
Core --> WinForms[WinForms 12+ 窗体]
Core --> SimpleLite
subgraph 卫星
Devices
VDA5050
end
核心结构问题:Core 是"万能基座"——既是抽象基座,又塞满了具体协议依赖(MQTT 属 VDA5050、Modbus/IoTClient 属设备、Nancy 属 WebApi、OpenXml 属报表),还内置 12+ WinForms 窗体。卫星只能依赖这个臃肿 Core,无法独立演进。
1.3 体量分布(非 Designer,前列)
WebApi 2686 / AbstractLoopMission 1858 / VehicleMonitor 1502(UI) / AbstractChainedDeliveryMission 1407(已 Compile Remove) / ChainedDeliveryMission 1365 / ChargeStationManagementForm 1250(UI) / AbstractChargeLogicMission 1143 / VDA5050Car 938 / DoorMission 932 / ButtonBoxManager 913 / DoorManager 848 / Kiva 827 / ButtonMission 743 / StandardChargeMission 671 / Commons 653 …
12+ 个 600~2700 行巨类,是可维护性的主要矛盾。
二、结构性问题诊断(按影响排序)
| # | 问题 | 证据 | 影响 |
|---|---|---|---|
| S1 | 分层污染:Core 背负协议/设备/报表专有依赖 | Core.csproj 引 MQTTnet/EasyModbus/IoTClient/Nancy/OpenXml | 卫星无法瘦身;Core 编译/部署重;职责不清 |
| S2 | WinForms 全模块渗透,阻塞纯 net8.0 | 12+ *.Designer.cs(Charge 4 个 Form、VehicleMonitor、各 Manager/Viewer);三 csproj 均 UseWindowsForms=true + net8.0-windows |
无法 net8.0 跨平台/瘦运行;与"UI 迁 migu"目标冲突 |
| S3 | God-class 泛滥 | WebApi 2686 / AbstractLoopMission 1858 / ChainedDeliveryMission 1365 / AbstractChargeLogicMission 1143 … | 改动风险高、测试困难、并发态难推理 |
| S4 | 充电子系统未独立,却已自成体系(20 文件) | Charge/ 任务+站点+配置+数据服务+通信+4 表单 |
应为独立卫星,却深埋 Core |
| S5 | 抽象与实现同居 Core | 设备基类/特性/Mission/Manager 在 Core,仅具体驱动在 Devices | 卫星仍强依赖 Core 内部;热插拔受限 |
| S6 | 干净抽象反向耦合到巨类 | Loop/ILoopRules.cs 顶部 using static AbstractLoopMission;(依赖其嵌套 LoopPoint) |
好接口被巨类绑架,无法独立复用 |
| S7 | 全局可变静态 | DeliveryCallbackRegistry(static 字典)、Commons 静态工具、各 static HttpClient |
隐式耦合、测试隔离难、生命周期不可控 |
| S8 | 内核 Coder 注册表限定本程序集反射 | ProgramCoderSet/SegmentPlan.Coder 特性驱动、按内核程序集反射 |
导航类卫星(磁/二维码/激光)无法热插拔(C2 阻塞) |
| S9 | 构建可移植性差 | csproj 多处绝对 HintPath(E:\Work…、D:\MDCS…) |
换机/CI 无法直接构建 |
| S10 | 死文件/弃用并存 | AbstractChainedDeliveryMission.cs(1407) 被 Compile Remove 仍在树 |
认知噪音、误改风险 |
| S11 | 命名空间与程序集名不一致 | 三程序集 RootNamespace=StandardScene;类型散落 StandardScene.* 子命名空间 |
物理边界与逻辑边界错位,难判断"谁属于谁" |
三、逐模块深度分析
每个模块:职责 / 结构 / 依赖与耦合 / 主要问题 / 目标处置。
M1 CarTypes(车型族,11 文件)
- 职责:定义各 AGV 车型(Kiva 827、Forklift 398、MultiWheel*、MultiVehicle、DualLifting、ArmCar、DummyCar 666)+ 字段袋
BasicFields(50) + 车辆监控 UIVehicleMonitor(1502, WinForms)。 - 结构:字段袋
BasicCarFields/SiteFields/TrackFields/PlanFields设计合理(-1哨兵语义被 Coder 正确利用);车型类承载通信(HttpClient)、状态机、调度、UI、强制控制等多职责。 - 耦合:车型直引
Commons、HttpClient、MessageBox、内核类型;VehicleMonitor把 UI 与车辆模型绑定。 - 问题:巨类(Kiva 827)、
async void+throw(Kiva.ForceStop 619/659)、空 catch、硬编码 IP(192.168.2.1:8008)。 - 目标处置:车型保留在领域层;剥离"通信/HTTP/UI"为协作者(
ICarTransport/ICarStatusView);VehicleMonitor进 UI 程序集。
M2 Coders(轨迹编码器,1 文件 + 车型内联)
- 职责:
CommonTrackCoders通用ITrackCoder(磁导航统一MagneticTrackCoder、避障AvoidanceParamCoder4参/AvoidanceParamLWCoder2参)。 - 结构:本轮已去重、结构清晰;
CommonTemplateTrackCoder提供SiteFieldsType扩展点。 - 耦合:受内核
ProgramTrackCoderSettings/TemplateTrackCoderSettings特性约束(S8)。 - 目标处置:继续把车型内联 Coder 收敛至此;待内核放开注册表后,导航类 Coder 可下沉到导航卫星。
M3 Chained(链式/循环任务族,12+2 文件)
- 职责:
AbstractLoopMission(1858)、ChainedDeliveryMission(1365)、TransportMission(549)、LoopMission、回调注册表/附着器、Loop/规则接口、若干 Viewer(UI)。 - 结构:两面性——
Loop/ILoopRules(IEnter/IExit/IJoin/IBranch/ITaskStrategy) 与DeliveryCallbackRegistry是规范的策略/注册表模式(亮点);但AbstractLoopMission是 1858 行巨类,且接口using static AbstractLoopMission(S6)反向耦合其嵌套类型。 - 耦合:裸
new Thread+while+Thread.Sleep(P0-1 已改协作式停止);UI Viewer 混入。 - 问题:巨类、状态用字符串
status.status、AbstractChainedDeliveryMission(1407) 死文件(S10)。 - 目标处置:把
LoopPoint/LoopTask等领域模型从巨类上提到 Model/Abstractions,让Loop接口独立;巨类按"调度循环/任务编排/显示"拆分;删除/归档死文件。
M4 InterLock(互锁,4 文件)
- 职责:
AbstractInterlockMission(367)、TrafficInterlockMission、Viewer(UI)。交通互锁逻辑。 - 耦合:与调度/交通控制耦合;含 Viewer。
- 目标处置:归入"交通/调度"领域子模块;UI 外提。
M5 Scheduler(调度后台任务,4 文件)
- 职责:
HeartBeatMission、NodeIsEnableMission、SecuritySignalMission、RegionalTrafficControlMission(377)。周期性后台任务(心跳/站点禁用/安全信号上传/区域交通)。 - 耦合:直发 HTTP(硬编码端点)、
Console.WriteLine、原while(true)+Thread.Abort(P0-1 已修为while(started)/协作式)。 - 问题:每个任务各写一套线程循环(重复),无统一基类。
- 目标处置:抽
MissionRunnerBase(CancellationToken + 状态枚举 + 统一日志),所有周期任务复用。
M6 Charge(充电子系统,20 文件 + ChargeStationType)★需独立
- 职责:任务(
StandardChargeMission671/AbstractChargeLogicMission1143)、站点(AbstractChargeStation/ChargeStation)、配置(ChargeStrategyConfig/AlarmConfig/ChargingSetting)、数据服务(3 个 *DataService)、通信(ChargeUdpService/CommunicationMessage(Service))、Helper、4 个 WinForms 表单。 - 结构:自成完整子系统(任务+设备+配置+持久化+通信+UI),但全埋 Core。
CommunicationMessageService是安全解析范本(先校验长度)。 - 耦合:与具体充电桩驱动(Devices/Charge)双向(Core 持任务/抽象,Devices 持 PCB/FL/MuXing 驱动);实时 UDP 路径越界(P0-3 已修)。
- 目标处置:升级为独立卫星
StandardScene.Charge(含 Mission/抽象/配置/通信),具体桩驱动留Devices或并入;4 表单进 UI 程序集;通过 Abstractions 与 Core 解耦。
M7 ExtendDevice + Devices(设备:门/按钮盒,Core 侧 13 + Devices 侧 6)
- 职责:Core 侧 = 基类(
BasicDoorController/BasicButtonBox)+特性(DoorTypeAttribute等)+管理器(DoorManager848/ButtonBoxManager913, 含 UI)+任务(DoorMission932/ButtonMission743)+模型/Monitor(UI);Devices 侧 = 具体驱动(ModbusDoorController/Azowie/Leeg/3 充电桩)。 - 结构:新驱动质量优秀(
ModbusDoorController:CTS 协作停止/锁/变更检测/重连节流/统一日志);DoorTypeAttribute特性发现规范。 - 耦合:抽象+业务在 Core、驱动在 Devices(S5);Manager 含 WinForms。
- 问题:
ModbusDoorController用析构函数兜底Disconnect(GC 线程取锁+Wait,风险)→应实现IDisposable;Manager 巨类含 UI。 - 目标处置:把设备抽象+特性下沉到
StandardScene.Abstractions;Manager 拆"设备生命周期服务 + UI";驱动统一IDisposable。
M8 Protocol.VDA5050(协议卫星,9 文件)
- 职责:
VDA5050Car(938)、MasterMQTTCommunication、VDA5050Interface/Segment/Helper/Commons、VDA5050WebApi、TextViewer(UI)。 - 结构:已是独立卫星(好);自带 WebApi 与 MQTT 栈。
- 问题:
async void+throw ex(240)、硬编码192.168.2.1:8008且注释掉按车地址(150)、空 catch、Console.WriteLine满布、monitor()死局部函数。 - 目标处置:作为协议卫星范本;端点配置化(按车
address)、异步规范化、日志统一、VDA5050WebApi与 Core WebApi 走统一ApiResult/路由约定。
M9 WebApi(老 Nancy 接口,2686 行)★最高优先重构
- 职责:车辆/任务/地图/配置等 HTTP 接口(Nancy 2.0)。
- 结构:单文件上帝路由;错误响应
new{Success=false,Code=500,...}复制几十处;反射 execute 端点(P0-2 已加白名单)。 - 目标处置:抽
ApiResult.Ok/Fail+ 按资源拆模块(CarApi/MissionApi/MapApi…);统一鉴权中间件(来源/令牌,配置驱动);老接口归WebApi.Core(deprecated)规划迁移;最终独立StandardScene.WebApi程序集(隔离 Nancy 依赖)。
M10 Model(领域/配置模型,12 文件)
- 职责:
Map/SimpleMap/MapStructure、SimpleConfig、TaskModel/LoopTask/MissionState、VehicleStatus、各*Setting。 - 结构:领域模型与配置混居;
Map.cs含硬编码127.0.0.1:4321。 - 目标处置:拆"纯领域模型(→Abstractions)"与"配置(→Configuration)";端点配置化。
M11 基础设施(TCP 5 / Utils 4 / CommonTools 2)
- 职责:
AsyncTcpClient(432, 质量较好)+事件args;JsonParser/JsonTool/ModbusClass/WebAPIHelper;AtomicFileUpdateHelper/SnowflakeIdGenerator。 - 问题:
AsyncTcpClient.Send抛InvalidProgramException(类型不当)、EndWrite无异常处理、uint on未用;ModbusClass与设备 Modbus 重复关注点。 - 目标处置:归入
StandardScene.Infrastructure(net8.0 纯净,无 Windows);TCP/IO/序列化/ID 通用化。
M12 Core 根 God-files(Commons 653 / Heuristic / LadderLogic / StandardCADTool / WebApi)
- 职责:
Commons万能工具+调度(NearestTask)、Heuristic启发式、LadderLogic梯形逻辑、StandardCADToolCAD(硬编码盘符路径、async void)。 - 问题:
Commons.AddOrUpdateXxxField重复 4 份、CarValue忽略 key、GoSite假重试(详见审查报告 P2-1);根目录堆放无归属大文件。 - 目标处置:
Commons按职责拆(字段服务/调度服务/控制台辅助);StandardCADTool路径配置化、归 CAD 子模块。
四、目标架构
4.1 分层与程序集边界(目标)
graph TD
subgraph L0[抽象层 net8.0 纯净]
Abstractions[StandardScene.Abstractions<br/>接口/特性/字段袋/领域模型/ITrackCoder/设备契约]
Infra[StandardScene.Infrastructure<br/>TCP/序列化/ID/IO/ILogger]
Config[StandardScene.Configuration<br/>配置模型+读写]
end
subgraph L1[领域层 net8.0]
Core2[StandardScene.Core<br/>车型/任务族/调度/交通/Coders]
end
subgraph L2[卫星 net8.0]
Charge2[StandardScene.Charge]
Devices2[StandardScene.Devices]
VDA[StandardScene.Protocol.VDA5050]
Nav[StandardScene.Nav.*(磁/二维码/激光,待内核放开)]
end
subgraph L3[宿主/接入 net8.0-windows]
Web[StandardScene.WebApi(Nancy 隔离)]
UI[StandardScene.UI.WinForms(临时·弃用,待 migu)]
end
Core2 --> Abstractions
Core2 --> Infra
Core2 --> Config
Charge2 --> Abstractions
Devices2 --> Abstractions
VDA --> Abstractions
Nav --> Abstractions
Charge2 -. 受限 .-> Core2
Web --> Core2
UI --> Core2
Devices2 -. NuGet .-> Modbus
VDA -. NuGet .-> MQTT
Web -. NuGet .-> Nancy
关键规则:
- 依赖只向下:卫星/宿主 → Abstractions(+受限 Core);Core 不得依赖任何具体协议/设备 NuGet(MQTT/Modbus/IoTClient/OpenXml/Nancy 全部下放到对应卫星/宿主)。
- 抽象先行:接口、注册特性、字段袋、纯领域模型、
ITrackCoder/设备契约统一进Abstractions(net8.0,无 Windows),卫星只认 Abstractions。 - UI 与协议 = 边缘:WinForms 全部收口到
UI.WinForms(标 deprecated,仅过渡,迁 migu 后删);Nancy 收口到WebApi。如此 Abstractions/Infrastructure/Core/卫星可去-windows,回到纯net8.0。 - 发现统一:设备/车型/协议统一"
[XxxType]特性 +UiTypeDiscovery.AllTypes()",scene.json声明provides。
4.2 边界接口(最小集)
ILogger(取代Console.WriteLine/直连 Diagnosis):业务只依赖抽象。IDeviceDriver/IDoorController/IChargeStation/IButtonBox(下沉 Abstractions),IDisposable释放。MissionRunnerBase(CancellationToken + 状态枚举 + 统一异常/日志):统一所有后台任务线程模型。ApiResult<T>(统一 HTTP 响应),HTTP 鉴权中间件。IEndpointProvider/配置注入:消灭硬编码 IP/URL/路径。ICarTransport(车辆通信抽象):把 HttpClient 从车型类剥离,单例化。
五、net8.0 去 Windows 依赖路径(解 S2)
- 隔离 UI:所有
*Form/*Viewer/*Monitor + *.Designer.cs(12+)迁StandardScene.UI.WinForms(唯一net8.0-windows+UseWindowsForms)。 - 去 MessageBox:业务层
MessageBox.Show(Commons/Kiva/各 Manager)改为事件/ILogger,弹窗交 UI 层。 - 核对 Windows-only API:去掉 CA1416 抑制后逐项消解(P/Invoke 控制台显隐等收口到宿主)。
- 切 TFM:Abstractions/Infrastructure/Core/卫星改
net8.0;仅 UI 与(如需)宿主保留-windows。
六、分阶段迁移路径(低风险·每阶段 build-green·可回归)
延续既有"路线乙":先结构移动(无逻辑变更)→ 再去重/解耦,每步可独立验证。
- A 抽象层奠基:建
StandardScene.Abstractions,纯移动接口/特性/字段袋/纯模型(ILoopRules、*TypeAttribute、BasicFields、LoopPoint/LoopTask等)。解 S6/S11。 - B 基础设施收口:建
Infrastructure(TCP/Utils/CommonTools)+ILogger;修AsyncTcpClient异常类型/EndWrite。解 S1(部分)。 - C UI 隔离:建
UI.WinForms,移走全部窗体;业务去MessageBox。解 S2,打通去-windows。 - D 协议/设备依赖下放:MQTT→VDA5050、Modbus/IoTClient→Devices、Nancy→WebApi、OpenXml→报表所在卫星;Core.csproj 清空专有 NuGet。解 S1。
- E 充电独立:抽
StandardScene.Charge卫星(Mission/抽象/配置/通信),表单已在 UI 层。解 S4。 - F 任务线程统一:落地
MissionRunnerBase,迁移 Scheduler/Chained/Charge 后台循环(在 P0-1 协作式停止基础上)。解 S3(并发面)。 - G WebApi 重构:
ApiResult+按资源拆分+鉴权中间件;老接口归 deprecated。解 S3/S9(WebApi)。 - H 巨类拆分:AbstractLoopMission/ChainedDeliveryMission/AbstractChargeLogicMission/Kiva 按职责拆分。解 S3。
- I 切 net8.0 + 清死文件/绝对路径:TFM 收敛;删
AbstractChainedDeliveryMission等死文件;HintPath改相对/包变量。解 S2/S9/S10。 - J 导航卫星(依赖内核):待内核放开 Coder 注册表(S8),抽磁/二维码/激光导航卫星。
每阶段出口标准:dotnet build 0 错误、警告不增、关键路径冒烟可过。
七、模块处置矩阵(速查)
| 模块 | 现位置 | 目标位置 | 关键动作 |
|---|---|---|---|
| 接口/特性/字段袋/纯模型 | Core 各处 | Abstractions | 纯移动 |
| TCP/Utils/CommonTools | Core | Infrastructure | 移动+ILogger+修 TCP |
| 全部窗体/Viewer/Monitor | 各模块 | UI.WinForms(deprecated) | 移动+去 MessageBox |
| 充电(任务/抽象/配置/通信) | Core/Charge | StandardScene.Charge | 卫星化 |
| 充电桩具体驱动 | Devices | Devices 或并入 Charge | 统一 IDisposable |
| 门/按钮盒 抽象+特性 | Core | Abstractions | 下沉 |
| 门/按钮盒 驱动 | Devices | Devices | IDisposable |
| MQTT/Modbus/IoTClient/Nancy/OpenXml | Core NuGet | 各卫星/WebApi | 依赖下放 |
| WebApi | Core 单文件 | StandardScene.WebApi | 拆分+鉴权+ApiResult |
| 车型 | Core/CarTypes | Core(领域) | 剥离通信/UI 协作者 |
| Coders | Core/Coders | Core(→导航卫星 J 阶段) | 继续收敛 |
| Commons/Heuristic/LadderLogic/CAD | Core 根 | 按职责归子模块 | 拆分+配置化 |
八、风险与约束
- 内核耦合(SimpleLite/SimpleCore):Coder 注册表按内核程序集反射(S8)→导航卫星热插拔需内核改造;
UiTypeDiscovery.AllTypes()已支持跨程序集发现(设备/车型可行)。 - 无 Git 基线:大量文件未纳入版本控制 → 强烈建议先建 Git 基线再执行 A~J,保证可回滚。
- 行为等价:车型/充电/VDA5050 含设备协议时序,移动需保持时序与字段语义(沿用"先移动后去重")。
- 绝对 HintPath/本地 dll:迁移期保持 HintPath 可用,I 阶段统一相对化,避免中途断链。
- migu 平台 UI:UI.WinForms 仅过渡;接口层(Abstractions/WebApi)应面向 migu 提供稳定契约,UI 迁移后整包删除。
九、近期可立即执行(已具备条件,低风险)
- 删除/归档死文件
AbstractChainedDeliveryMission.cs(已 Compile Remove)。 - 新建
StandardScene.Abstractions,先迁BasicFields、*TypeAttribute、Loop/ILoopRules(解 S6/S11,且不改逻辑)。 MissionRunnerBase抽取(承接 P0-1 协作式停止成果,统一 Scheduler 四任务)。ApiResult帮助器(先在 WebApi 内消重,零行为变化)。