Files
StandardSence/StandardScene代码审查报告.md
T
2026-06-14 11:19:15 +08:00

16 KiB
Raw Blame History

StandardScene 代码审查报告

审查对象:E:\Work\Core\Simple-FR\StandardSenceStandardScene.Core / StandardScene.Devices / StandardScene.Protocol.VDA5050 审查方式:全量反模式扫描(ripgrep)+ 关键文件精读 + 编译告警分类(dotnet build --no-incremental 目标框架:net8.0-windows(最终目标 net8.0 报告日期:2026-06-09


一、审查范围与方法

  • 代码规模113 个 .cs 文件;最大文件 WebApi.cs2661 行)、AbstractLoopMission.cs1858 行)。
  • 扫描维度:并发/线程、异常处理、报文解析边界、资源管理、配置硬编码、日志、UI/业务耦合、安全、编译告警。
  • 证据标注
    • 精读确认:已逐行读取、行号精确。
    • 扫描命中:ripgrep 命中文件级,行号待整改时逐一核对。

二、总体评价与子系统评分

子系统 评分 说明
设备驱动(StandardScene.Devices·新) ★★★★☆ ModbusDoorController 工程化优秀,可作团队样板
TCP 基础设施(AsyncTcpClient ★★★★☆ 锁/陈旧回调防护/重连完善,少量瑕疵
Coders(重构后) ★★★★☆ 本轮已去重,结构清晰
CarTypes 车型族 ★★☆☆☆ 巨类、async void、空 catch、硬编码集中
任务族 Missions ★★☆☆☆ Thread.Abort、while(true)+Sleep、状态机分散
充电 Charge ★★☆☆☆ 实时报文路径越界风险、UDP 线程模型粗放
VDA5050 协议 ★★☆☆☆ 硬编码 IP、async void、throw ex、Console 日志
WebApi(老 Nancy ★☆☆☆☆ 2661 行上帝文件、无鉴权反射调用、模板代码爆炸
Commons / 公共层 ★★☆☆☆ 上帝工具类、重复实现、隐性 bug

核心判断:技术债集中在旧模块CarTypes / Missions / Charge / VDA5050 / WebApi / Commons);新写模块Devices、AsyncTcpClient)质量明显更高,说明团队具备写好代码的能力,债务主要是历史遗留。整改应"以新模块为范本、按子系统收敛旧债"。


三、问题严重级别汇总

级别 含义 主要条目
P0 阻断 .NET8 下会崩溃/失效,或存在安全风险 Thread.Abort 被吞、WebApi 无鉴权反射、实时报文越界
P1 高危 生产环境易触发故障/难排障 硬编码 IP、async void+throw、空 catch、HttpClient 滥用
P2 结构 可维护性/扩展性差 上帝类、重复代码、Console 日志、UI/业务耦合
P3 整洁 编译告警与代码卫生 45 项告警(重复 using、未用字段、隐藏成员等)

四、P0 阻断级问题

P0-1 Thread.Abort() 在 .NET8 必抛异常且被静默吞掉(线程停不掉)

Thread.Abort() 在 .NET8 抛 PlatformNotSupportedException(告警 SYSLIB0006,共 14 条/双配置)。多处 Abort() 外层是空 catch{},导致异常被吞、线程实际未停止——任务"停止"后后台线程仍在跑,造成重复下发、资源泄漏、状态错乱。

精读确认命中点:

文件
StandardScene.Core/Chained/AbstractLoopMission.cs 1314 _strategyThread?.Abort()、1329 _logicThread?.Abort()
StandardScene.Core/Charge/StandardChargeMission.cs 681 myThread?.Abort()、684 ChargeThread?.Abort()
StandardScene.Core/Scheduler/SecuritySignalMission.cs 176 myThread?.Abort()
StandardScene.Core/Scheduler/NodeIsEnableMission.cs 121 myThread?.Abort()
StandardScene.Core/Scheduler/HeartBeatMission.cs 68 _myThread?.Abort()
StandardScene.Core/Chained/AbstractChainedDeliveryMission.cs 1606 myThread.Abort()

典型代码(AbstractLoopMission.cs:1309精读确认):

        public virtual void StopLoop()
        {
            try
            {
                _strategyRunning = false;
                _strategyThread?.Abort();
                _strategyThread = null;
                ...
            }
            catch { }
        }

修复方向:改为协作式停止——已有 _strategyRunning/_logicRunning 布尔标志,循环体应周期检查该标志退出;线程创建用 IsBackground=true,停止时 flag=falseJoin(timeout)。删除所有 Abort()。对阻塞型循环用 CancellationToken + 可中断等待(Task.Delay(token) / ManualResetEventSlim.Wait(token))替代 Thread.Sleep。参考 ModbusDoorControllerCancellationTokenSource 范式。


P0-2 WebApi 反射接口:无鉴权远程调用任意方法(RCE 级风险)

StandardScene.Core/WebApi.cs精读确认272314 / 332368)暴露:

            Get("/car_reflection/execute/{id}/{method}", parameters =>
            {
                ...
                    methodInfo = car.GetType().GetMethod((string)parameters.method);
                ...
                    return ExecuteMethod(methodInfo, car, queryParams);
            });

mission_reflection/execute/{id}/{method} 同款。问题:通过 HTTP 即可按名反射调用 Car/Mission 上任意 public 方法,无身份校验、无方法白名单、无危险方法拦截。结合 GET 语义,配置不当会暴露在内网甚至外网,构成命令执行级攻击面。

修复方向

  1. 最小化:加访问令牌/来源 IP 限制(中间件统一校验)。
  2. 方法白名单:仅允许标注了 [MethodMember](或新增 [WebInvokable])的方法被反射调用;拒绝其余。
  3. 语义化:执行类操作改 POST;统一错误响应封装(见 P2-2)。

P0-3 实时报文解析按固定下标取值、缺长度校验(IndexOutOfRange 崩溃)

对比发现两条解析路径风险等级不同

  • 正确范本StandardScene.Core/Charge/CommunicationMessageService.cs精读确认)在取下标前先校验 parts.Length < 30 / < 10198、276 行),再访问 bytes[28] 等,越界已被防住。
  • 风险路径:实时 UDP/回调路径按固定下标直接取值,未见等价长度校验(扫描命中):
    • StandardScene.Core/ChargeUdpService.csmessage[28] 等)
    • StandardScene.Devices/Charge/PCBChargeStation.csOnUdpMessagemessage[1]
    • StandardScene.Core/ChargeStationType/MuXingChargeStation.cs

设备掉线/半包/异常帧时,直接 IndexOutOfRangeException,且若发生在 UDP 接收线程会拖垮整条接收链路。

修复方向:所有报文解析入口统一"先校验长度(与帧头/类型匹配)再取值",越界返回 null 并 Diagnosis.Log 记录原始帧;抽出 ChargeFrameParser 复用 CommunicationMessageService 的安全解析。


五、P1 高危问题

P1-1 配置硬编码(IP/URL/路径),多 AGV/换环境必失效(扫描命中 14 文件)

最典型(精读确认StandardScene.Protocol.VDA5050/VDACar/VDA5050Car.cs:150

        public async void GetVDA5050StateFromC()
        {
            try
            {
                //string jsonResponse1 = await hc.GetStringAsync($"http://{this.address}:8008/getStat");
                string jsonResponse1 = await hc.GetStringAsync($"http://192.168.2.1:8008/getStat");
                ...
            }
            catch (Exception ex)
            {
            }
        }

把按车 this.address 的写法注释掉、写死 192.168.2.1——多 AGV 场景必然全部打到同一地址;外加空 catch 吞错,故障"静默"。其余命中:Kiva.cs/Forklift.cs192.168.2.1:8008)、Map.cs127.0.0.1:4321)、LoopMission.cs(西门子 PLC IP)、TransportDeliveryCallbacks.cs(回调 URL)、StandardCADTool.csWindows 盘符路径)等。

修复方向:抽 scene.json/配置中心统一注入;禁止业务代码内联端点;StandardCADTool 路径改相对/可配置。

P1-2 async void + 在其中 throw(CA2200 失栈)→ 进程级崩溃风险

  • Kiva.cs:619 public new async void ForceStop()精读确认):async voidcatch 内 MessageBox.ShowUI 耦合),最外层 Console.WriteLine(e); throw;——async void 抛出的异常无法被调用方捕获,直接打到 SynchronizationContext/线程池,可致进程崩溃。new 还隐藏基类 ForceStopCS0114)。
  • VDA5050Car.cs:240 throw ex;精读确认)破坏原始堆栈(CA2200,全仓 4 条)。DummyCar.cs 同款。
  • async void 在 CarTypes/Charge/VDA5050/CAD 等多文件广泛存在(扫描命中)。

修复方向:事件处理器之外一律 async Task;确需 async void 的入口用 try/catch 兜底并 Diagnosis.Log,不得外抛;throw ex;throw;

P1-3 空 catch{} 静默吞异常(扫描命中 10 文件)

Kiva.csVDA5050Car.csFLChargeStation.csAbstractLoopMission.csCommons.csAsyncTcpClient.csLoopViewer.cs 等。Kiva.cs:606VDA5050Car.cs:165 为典型业务路径空 catch。

注:AsyncTcpClient / ModbusDoorController 中环绕 Close()/Dispose() 的空 catch 属可接受的清理兜底,应保留但加注释;业务路径空 catch 必须改为记录日志。

P1-4 同步阻塞调用 .Result/.Wait()/GetAwaiter().GetResult()扫描命中 7 文件)

ButtonMission.csDoorMission.csVDA5050Car.cs 等。Kiva.cs:635 status.programs.task.Wait()精读确认)在 UI/线程上下文易死锁。修复:异步链路打通,避免 sync-over-async。

P1-5 new HttpClient() 反复实例化(Socket 耗尽)(扫描命中

Forklift.cs/Kiva.cs/VDA5050Car.cs/WebApi.cs 等频繁 new HttpClient修复:单例或 IHttpClientFactory/SocketsHttpHandler(设 PooledConnectionLifetime)。


六、P2 结构 / 质量问题

P2-1 Commons.cs 上帝工具类(精读确认

  • AddOrUpdateCarField/SiteField/MissionField "ContainsKey→Remove+Add / else Add" 模板重复 4 份,应为 dict[key]=value
  • AddOrUpdateTag 名为 update 实为 add(更新逻辑被注释),重复键会抛错,命名误导。
  • CarValue:存在 electricCurrent忽略入参 key 直接返回,疑似 bug。
  • GoSite catch 内 Console.WriteLine + Thread.Sleep(3000),注释写"重新执行"但并未真正重试
  • 调度 NearestTask(约 398–538)巨函数、深嵌套、大段 #region ObsoleteCode 注释代码、魔法优先级 Priority=50

P2-2 WebApi.cs 2661 行上帝文件(精读确认

错误响应 new { Success=false, Code=500, Data="null", Message=... } 模板复制几十处;路由全堆一个文件。修复:抽 ApiResult.Fail/Ok 帮助器;按资源拆分路由模块;老接口归入 WebApi.Core(deprecated) 并规划迁移。

P2-3 日志体系不统一(扫描命中 22 文件 Console.WriteLine

Diagnosis.Log 混用。ModbusDoorController 已全程 Diagnosis.Log,应作为统一范式推广;禁用 Console.WriteLine 于业务代码。

P2-4 UI 与业务耦合(扫描命中

MessageBox.Show 出现在 Commons.csTrafficControl.OnDeadLock)、Kiva.cs:648DoorManager.csButtonBoxManager.cs 等业务/管理类中。修复:业务层只发事件/日志,弹窗交由 UI 层(后续 migu 平台)。

P2-5 while(true)+Thread.Sleep 忙等/阻塞(扫描命中 13 文件)

Forklift.csStandardChargeMission.csChargeUdpService.csVDA5050Car.cs 等。修复:改 CancellationToken+可中断等待或定时器;与 P0-1 协作式停止一并处理。

P2-6 AsyncTcpClient 细节(精读确认

  • Send 失败抛 InvalidProgramException(异常类型不当,应 InvalidOperationException/自定义)。
  • HandleDatagramWrittenEndWrite 无 try,写失败异常落到线程池无人观测。
  • uint on = 1; 未使用字段(CS0414)。

七、编译告警分类(全量,双配置合计 90 实例 ≈ 45/配置)

告警码 数量 含义 处置
CS0108 16 隐藏继承成员未加 new 显式 new/override 或改名
CS4014 14 调用未 await(即发即弃) 显式 await_ = 并说明
CS0414 14 私有字段赋值但从未使用 删除
SYSLIB0006 14 Thread.Abort 已弃用 P0-1
CS0105 6 重复 using 删除
CS0162 6 不可达代码 清理
CS0168 6 变量声明未用 删除
CA2200 4 throw ex 失栈 throw;
CS8321 2 局部函数未用 删除(如 VDA5050Car monitor()
CS0169 2 字段从未使用 删除
CS0114 2 隐藏继承成员(如 ForceStop override/new
CS0219 2 变量赋值未用 删除
CS0649 2 字段从未赋值 初始化或删除

八、分子系统评述(摘要,详版见架构方案)

  • CarTypesBasicCarFields/SiteFields/TrackFields/PlanFields 字段袋设计合理(BasicFields.csCarLength/CarWidth 默认 -1 作"未配置"哨兵,已被避障 Coder 正确利用);但具体车型(Kiva/Forklift/VDA5050Car)是巨类,混杂通信、状态、UI、调度,async void/空 catch/硬编码集中。
  • Coders:本轮已完成磁导航统一与避障去重(AvoidanceParamCoder 4 参 + AvoidanceParamLWCoder 2 参),结构清晰,建议继续把车型内联 Coder 收敛到 CommonTrackCoders
  • MissionsChained/InterLock/Scheduler 线程模型粗放(裸 new Thread+Abort+while(true)),状态机散落字符串 status.status。建议统一 MissionRunnerBaseCancellationToken + 状态枚举)。
  • Charge:解析存在"安全范本"与"风险路径"并存(见 P0-3);UDP 服务与 Mission 线程耦合。
  • Devices(新)ModbusDoorController 优秀;唯一瑕疵是用析构函数兜底 Disconnect(在 GC 线程取锁+Wait,有风险),应实现 IDisposable 显式释放。
  • VDA5050MQTT/HTTP 异步用法不规范(async void、throw ex、硬编码 IP、Console 日志),状态处理 ProcessCacheAndSendOrderMessage 内大量 Console。
  • WebApi:见 P0-2 / P2-2,最高优先级重构对象。

九、正面样板(建议作为团队基线)

  1. StandardScene.Devices/Door/ModbusDoorController.csCancellationTokenSource 协作式停止、lock 线程安全、变更检测(_lastSentControl)、重连节流、统一 Diagnosis.Log、资源清理。
  2. StandardScene.Core/TCP/AsyncTcpClient.cs:锁保护、陈旧回调防护、自动重连。
  3. StandardScene.Core/Charge/CommunicationMessageService.cs:报文解析前置长度校验(安全解析范本)。

十、整改路线图

第 1 批(P0,本次执行)

  1. Thread.Abort → 协作式停止(6 文件 8 处)。
  2. WebApi 反射 execute 加来源校验 + [MethodMember] 白名单。
  3. 充电实时报文路径补长度校验(复用安全解析)。

第 2 批(P1 4. 硬编码端点配置化(VDA5050/Kiva/Forklift/Map/Loop/CAD)。 5. async voidTaskthrow exthrow、业务空 catch 加日志。 6. HttpClient 单例化;sync-over-async 拆解。

第 3 批(P2/结构) 7. ApiResult 帮助器 + WebApi 拆分;Commons 拆服务、修 CarValue/GoSite/AddOrUpdate。 8. 统一 Diagnosis.LogUI 解耦;MissionRunnerBase 统一线程/状态机。

第 4 批(P3 卫生) 9. 清 45 项告警(重复 using、未用字段、不可达代码、隐藏成员)。


附录:扫描方法

  • 反模式:rg 扫描 Thread.Abort / catch\s*\{\s*\} / throw ex; / async void / \.Result|\.Wait\(\) / 硬编码 IP / Console.WriteLine / while\s*\(\s*true\s*\) / new HttpClient / MessageBox.Show / 魔法下标。
  • 告警:dotnet build --no-incremental_buildwarnings.txt → 按告警码聚合计数。
  • 精读:AsyncTcpClient、Commons、CommunicationMessageService、VDA5050Car、WebApi、Kiva、AbstractLoopMission、ModbusDoorController、BasicFields 等。