Files
XplorePlane/doc/fix/AppState与事件流问题审计.md
T
zhengxuan.zhang 0d1cda6403 修复流水线属性面板实时预览缺失叠加显示
CncInspectionModulePipelineViewModel.ExecutePreviewAsync 只推送了结果图,
未像 PipelineEditorViewModel 一样调用 PushDetectionOverlay,
导致调参实时预览时不会叠加显示 BGA 检测的圆形标注。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 09:35:13 +08:00

8.3 KiB
Raw Blame History

AppState 与事件流问题审计

审计日期:2026-08-03
审计范围:硬件事件、AppStateService、调试面板、探测器帧队列、BGA 向导及窗口生命周期。
本文用于记录代码审阅结论,不代表问题已经修复。

1. 审计结论

整体架构判断成立:硬件层通过 Prism 事件进入 AppStateService,再由状态事件通知 ViewModel。当前发现的问题主要集中在事件去重、资源生命周期和未完成的回退路径,不属于架构需要整体重写的问题。

审阅材料中的结论需要按下面的状态理解:

编号 问题 当前判断 证据
P0-1 BGA 检测方法存在无条件 return,流水线结果缺失时静默退出 确认存在 InspectionTaskWizardViewModel.cs:638;后续代码已被编译器报 CS0162
P0-2 MotionState 事件可能无变化洪泛 确认存在 BuildMotionStateSnapshot 每次生成新的 recordAppStateService.cs:790-794 使用 ReferenceEquals 去重
P1-1 _acquireQueue 长期保留大尺寸帧 确认存在,需先验证运行时占用 DetectorFramePipelineService.cs:29 入队后没有对应出队;处理队列 _processQueue 则有后台消费者,不应把两者混为一谈
P1-2 调试面板 PerformanceMonitorViewModel 事件订阅无法解除 确认存在 PerformanceMonitorViewModel.cs:84-90 使用匿名 lambdaDispose 未反订阅
P1-3 _latestGeometry 跨线程读写缺少明确同步 确认存在,风险待量化 AppStateService.cs:161AppStateService.cs:566-571
P1-4 探测器断连状态判断不是单一原子事务 部分确认 AppStateService.cs:579-585 在后台线程读取 _detectorState;最终状态更新仍通过原子替换,但 wasConnected 与后续更新之间可能交错
P1-5 CncExecutionStateChanged 没有主项目订阅方 确认存在 AppStateService 有发布事件,但当前主项目未发现 += 订阅;部分界面改读 MainViewportService.IsCncRunning,形成双来源风险
P1-6 硬件错误事件没有进入统一 SystemState 确认存在 MotionErrorEvent、探测器 ErrorOccurredEvent、射线源 ErrorOccurredEvent 在硬件层有发布,但主项目未发现对应统一订阅;射线源自身 ViewModel 的局部订阅不等于全局故障状态闭环
P1-7 OperationMode 始终为 Idle,权限守卫可能失效 确认存在 当前唯一写入点 RecipeService.cs:225 仍写入 OperationMode.IdlePermissionService.cs:159 依赖该字段
P2-1 标定矩阵/画面联动没有外部调用方 基本确认 UpdateCalibrationMatrixRequestLinkedViewUpdateLinkedViewState 的调用主要位于 AppStateService 自身,未发现业务入口;需结合实际 UI 需求决定是接入还是删除
P2-2 WindowLauncherService.ShowOrActivateVisible 关闭后保留窗口引用 确认存在 WindowLauncherService.cs:91-106 没有像 ShowOrActivate 一样挂载 Closed 清理逻辑;接口注释也明确记录了该历史行为
P2-3 事件投递顺序与最新属性值可能不一致 待验证 目前代码确实存在 Interlocked.ExchangeDispatcher.BeginInvoke 的组合,但是否造成实际错序需要并发测试或日志证据,不能仅凭静态审阅定性为已发生故障
P2-4 CncRoiEditRequestedPayload 携带 Action 委托 设计风险确认 CncEvents.cs:54-60 将回调放入全局事件 Payload,存在生命周期、重入和异常传播风险
P2-5 探测器每帧复制大数组并被多处缓存 部分确认 DetectorFramePipelineService.cs:99-114 每帧复制原始数组并生成 BitmapSource;是否达到约 600 MB 取决于实际队列深度、图像尺寸和 GC 回收,需运行时采样确认

2. 审阅材料中的修正项

2.1 “_acquireQueueProcessFrameDequeued 都没有消费者”不准确

当前实现中:

  • _acquireQueue 确实只有入队和容量淘汰,没有业务消费方;这是需要处理的资源保留问题。
  • _processQueueProcessLoopAsync 消费,并发布 ProcessFrameDequeued;因此处理队列本身不是“只写不读”。
  • ProcessFrameDequeued 当前没有主项目订阅方,但它仍由处理线程发布,不能据此推断 _processQueue 未消费。

2.2 “错误事件完全没有订阅方”需要限定范围

射线源的 ErrorOccurredEvent 在模块自身已有 ViewModel 订阅,用于局部显示;更准确的问题是:主项目没有把运动、探测器和射线源故障统一映射到 SystemState,因此主界面和权限逻辑无法依赖统一故障状态。

2.3 “内存恒定约 600 MB”应改为容量上界风险

在 3072×3060、16 位图像下,单帧原始数组约 18.8 MB,BitmapSource 也可能占用相近量级。由于 _acquireQueue 容量为 16,静态上界确实可能达到数百 MB;但实际占用还受 BitmapSource 内存布局、淘汰时机和 GC 影响,应通过运行时计数器确认后再作为验收数字。

3. 建议修复顺序

第一阶段:解除当前功能阻塞

  1. 处理 ExecuteRunBgaDetection 的流水线结果缺失分支:明确显示错误并允许回退到受控的真实检测路径;删除无条件死代码 return,不要静默结束。
  2. 为 BGA 流水线缺失输出增加测试,覆盖“成功复用、缺失输出、输出类型错误”三种情况。

第二阶段:降低事件与内存压力

  1. MotionState 去重从引用比较改为值比较,或在状态未变化时不创建新 record;同时增加事件频率测试。
  2. 明确 _acquireQueue 的职责:如果只是调试缓存,改为有限的轻量元数据/可配置采样;如果需要消费,则补齐消费者和释放策略。
  3. PerformanceMonitorViewModel 的匿名订阅改为具名处理器或保存 SubscriptionToken,在 Dispose 中完整解除订阅。

第三阶段:补齐状态闭环与生命周期

  1. 确定 CNC 执行状态的唯一事实源,统一 CncExecutionStateMainViewportService.IsCncRunning 的读写路径。
  2. 将硬件错误事件统一映射到 SystemState,并明确 UI、日志和权限层的消费方。
  3. ShowOrActivateVisible 增加 Closed 清理;补充窗口重复打开、关闭后重开测试。
  4. 评估标定/画面联动 API 和带回调的 CNC 事件是否应改成专用服务或请求 ID。

4. 验收标准

  • 静止设备不再因无变化的 MotionState 每 100 ms 触发完整 UI 更新。
  • 调试面板关闭后,旧 PerformanceMonitorViewModel 不再接收 AppState 事件。
  • _acquireQueue 有明确消费者或明确的淘汰/禁用策略,运行时内存曲线可解释。
  • BGA 流水线输出缺失时有可见错误,不再静默返回;编译器不再报告该方法的不可达代码。
  • CNC 执行中权限判断、主界面状态和执行服务使用同一个状态来源。
  • 硬件故障能够进入统一状态、日志和 UI 告警链路。

5. 关联文档