Files
StandardSence/StandardScene架构重构方案.md
T
2026-06-14 11:19:15 +08:00

20 KiB
Raw Blame History

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.jsonDevices / 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.csCharge 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 全局可变静态 DeliveryCallbackRegistrystatic 字典)、Commons 静态工具、各 static HttpClient 隐式耦合、测试隔离难、生命周期不可控
S8 内核 Coder 注册表限定本程序集反射 ProgramCoderSet/SegmentPlan.Coder 特性驱动、按内核程序集反射 导航类卫星(磁/二维码/激光)无法热插拔(C2 阻塞)
S9 构建可移植性差 csproj 多处绝对 HintPathE:\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) + 车辆监控 UI VehicleMonitor(1502, WinForms)。
  • 结构:字段袋 BasicCarFields/SiteFields/TrackFields/PlanFields 设计合理(-1 哨兵语义被 Coder 正确利用);车型类承载通信(HttpClient)、状态机、调度、UI、强制控制等多职责。
  • 耦合:车型直引 Commons、HttpClient、MessageBox、内核类型;VehicleMonitor 把 UI 与车辆模型绑定。
  • 问题:巨类(Kiva 827)、async void+throwKiva.ForceStop 619/659)、空 catch、硬编码 IP192.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 AbstractLoopMissionS6)反向耦合其嵌套类型。
  • 耦合:裸 new Thread+while+Thread.Sleep(P0-1 已改协作式停止);UI Viewer 混入。
  • 问题:巨类、状态用字符串 status.statusAbstractChainedDeliveryMission(1407) 死文件(S10)。
  • 目标处置:把 LoopPoint/LoopTask 等领域模型从巨类上提到 Model/Abstractions,让 Loop 接口独立;巨类按"调度循环/任务编排/显示"拆分;删除/归档死文件。

M4 InterLock(互锁,4 文件)

  • 职责AbstractInterlockMission(367)、TrafficInterlockMission、Viewer(UI)。交通互锁逻辑。
  • 耦合:与调度/交通控制耦合;含 Viewer。
  • 目标处置:归入"交通/调度"领域子模块;UI 外提。

M5 Scheduler(调度后台任务,4 文件)

  • 职责HeartBeatMissionNodeIsEnableMissionSecuritySignalMissionRegionalTrafficControlMission(377)。周期性后台任务(心跳/站点禁用/安全信号上传/区域交通)。
  • 耦合:直发 HTTP(硬编码端点)、Console.WriteLine、原 while(true)+Thread.AbortP0-1 已修为 while(started)/协作式)。
  • 问题:每个任务各写一套线程循环(重复),无统一基类。
  • 目标处置:抽 MissionRunnerBaseCancellationToken + 状态枚举 + 统一日志),所有周期任务复用。

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、驱动在 DevicesS5);Manager 含 WinForms。
  • 问题ModbusDoorController 用析构函数兜底 Disconnect(GC 线程取锁+Wait,风险)→应实现 IDisposableManager 巨类含 UI。
  • 目标处置:把设备抽象+特性下沉到 StandardScene.AbstractionsManager 拆"设备生命周期服务 + UI";驱动统一 IDisposable

M8 Protocol.VDA5050(协议卫星,9 文件)

  • 职责VDA5050Car(938)、MasterMQTTCommunicationVDA5050Interface/Segment/Helper/CommonsVDA5050WebApiTextViewer(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/MapStructureSimpleConfigTaskModel/LoopTask/MissionStateVehicleStatus、各 *Setting
  • 结构:领域模型与配置混居;Map.cs 含硬编码 127.0.0.1:4321
  • 目标处置:拆"纯领域模型(→Abstractions"与"配置(→Configuration";端点配置化。

M11 基础设施(TCP 5 / Utils 4 / CommonTools 2

  • 职责AsyncTcpClient(432, 质量较好)+事件argsJsonParser/JsonTool/ModbusClass/WebAPIHelperAtomicFileUpdateHelper/SnowflakeIdGenerator
  • 问题AsyncTcpClient.SendInvalidProgramException(类型不当)、EndWrite 无异常处理、uint on 未用;ModbusClass 与设备 Modbus 重复关注点。
  • 目标处置:归入 StandardScene.Infrastructurenet8.0 纯净,无 Windows);TCP/IO/序列化/ID 通用化。

M12 Core 根 God-filesCommons 653 / Heuristic / LadderLogic / StandardCADTool / WebApi

  • 职责Commons 万能工具+调度(NearestTask)、Heuristic 启发式、LadderLogic 梯形逻辑、StandardCADTool CAD(硬编码盘符路径、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.WebApiNancy 隔离)]
      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

关键规则

  1. 依赖只向下:卫星/宿主 → Abstractions(+受限 Core)Core 不得依赖任何具体协议/设备 NuGetMQTT/Modbus/IoTClient/OpenXml/Nancy 全部下放到对应卫星/宿主)。
  2. 抽象先行:接口、注册特性、字段袋、纯领域模型、ITrackCoder/设备契约统一进 Abstractionsnet8.0,无 Windows),卫星只认 Abstractions。
  3. UI 与协议 = 边缘WinForms 全部收口到 UI.WinForms(标 deprecated,仅过渡,迁 migu 后删);Nancy 收口到 WebApi。如此 Abstractions/Infrastructure/Core/卫星可去 -windows,回到纯 net8.0
  4. 发现统一:设备/车型/协议统一"[XxxType] 特性 + UiTypeDiscovery.AllTypes()"scene.json 声明 provides

4.2 边界接口(最小集)

  • ILogger(取代 Console.WriteLine/直连 Diagnosis):业务只依赖抽象。
  • IDeviceDriver/IDoorController/IChargeStation/IButtonBox(下沉 Abstractions),IDisposable 释放。
  • MissionRunnerBaseCancellationToken + 状态枚举 + 统一异常/日志):统一所有后台任务线程模型。
  • ApiResult<T>(统一 HTTP 响应),HTTP 鉴权中间件。
  • IEndpointProvider/配置注入:消灭硬编码 IP/URL/路径。
  • ICarTransport(车辆通信抽象):把 HttpClient 从车型类剥离,单例化。

五、net8.0 去 Windows 依赖路径(解 S2

  1. 隔离 UI:所有 *Form/*Viewer/*Monitor + *.Designer.cs12+)迁 StandardScene.UI.WinForms(唯一 net8.0-windows+UseWindowsForms)。
  2. 去 MessageBox:业务层 MessageBox.ShowCommons/Kiva/各 Manager)改为事件/ILogger,弹窗交 UI 层。
  3. 核对 Windows-only API:去掉 CA1416 抑制后逐项消解(P/Invoke 控制台显隐等收口到宿主)。
  4. 切 TFMAbstractions/Infrastructure/Core/卫星改 net8.0;仅 UI 与(如需)宿主保留 -windows

六、分阶段迁移路径(低风险·每阶段 build-green·可回归)

延续既有"路线乙"先结构移动(无逻辑变更)→ 再去重/解耦,每步可独立验证。

  • A 抽象层奠基:建 StandardScene.Abstractions纯移动接口/特性/字段袋/纯模型(ILoopRules*TypeAttributeBasicFieldsLoopPoint/LoopTask 等)。解 S6/S11。
  • B 基础设施收口:建 InfrastructureTCP/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/S9WebApi)。
  • 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 根 按职责归子模块 拆分+配置化

八、风险与约束

  1. 内核耦合(SimpleLite/SimpleCore:Coder 注册表按内核程序集反射(S8)→导航卫星热插拔需内核改造;UiTypeDiscovery.AllTypes() 已支持跨程序集发现(设备/车型可行)。
  2. 无 Git 基线:大量文件未纳入版本控制 → 强烈建议先建 Git 基线再执行 A~J,保证可回滚。
  3. 行为等价:车型/充电/VDA5050 含设备协议时序,移动需保持时序与字段语义(沿用"先移动后去重")。
  4. 绝对 HintPath/本地 dll:迁移期保持 HintPath 可用,I 阶段统一相对化,避免中途断链。
  5. migu 平台 UIUI.WinForms 仅过渡;接口层(Abstractions/WebApi)应面向 migu 提供稳定契约,UI 迁移后整包删除。

九、近期可立即执行(已具备条件,低风险)

  1. 删除/归档死文件 AbstractChainedDeliveryMission.cs(已 Compile Remove)。
  2. 新建 StandardScene.Abstractions,先迁 BasicFields*TypeAttributeLoop/ILoopRules(解 S6/S11,且不改逻辑)。
  3. MissionRunnerBase 抽取(承接 P0-1 协作式停止成果,统一 Scheduler 四任务)。
  4. ApiResult 帮助器(先在 WebApi 内消重,零行为变化)。