修复流水线属性面板实时预览缺失叠加显示
CncInspectionModulePipelineViewModel.ExecutePreviewAsync 只推送了结果图, 未像 PipelineEditorViewModel 一样调用 PushDetectionOverlay, 导致调参实时预览时不会叠加显示 BGA 检测的圆形标注。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -685,9 +685,15 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
try
|
||||
{
|
||||
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
||||
var execResult = await _executionService.ExecutePipelineAsync(GetNodesInExecutionScope(), sourceImage, null, token);
|
||||
var executionNodes = GetNodesInExecutionScope().ToList();
|
||||
var execResult = await _executionService.ExecutePipelineAsync(executionNodes, sourceImage, null, token);
|
||||
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:执行完成,推送结果图像");
|
||||
_mainViewportService.SetManualImage(execResult.Image, string.Empty);
|
||||
if (execResult.LastStepOutputData != null)
|
||||
{
|
||||
var operatorKey = executionNodes.LastOrDefault()?.OperatorKey ?? string.Empty;
|
||||
_mainViewportService.PushDetectionOverlay(execResult.LastStepOutputData, operatorKey);
|
||||
}
|
||||
_eventAggregator?.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||
.Publish(new PipelinePreviewUpdatedPayload(execResult.Image, StatusMessage));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# AppState 与事件流问题审计
|
||||
|
||||
> 审计日期:2026-08-03
|
||||
> 审计范围:硬件事件、`AppStateService`、调试面板、探测器帧队列、BGA 向导及窗口生命周期。
|
||||
> 本文用于记录代码审阅结论,不代表问题已经修复。
|
||||
|
||||
## 1. 审计结论
|
||||
|
||||
整体架构判断成立:硬件层通过 Prism 事件进入 `AppStateService`,再由状态事件通知 ViewModel。当前发现的问题主要集中在事件去重、资源生命周期和未完成的回退路径,不属于架构需要整体重写的问题。
|
||||
|
||||
审阅材料中的结论需要按下面的状态理解:
|
||||
|
||||
| 编号 | 问题 | 当前判断 | 证据 |
|
||||
|---|---|---|---|
|
||||
| P0-1 | BGA 检测方法存在无条件 `return`,流水线结果缺失时静默退出 | **确认存在** | [`InspectionTaskWizardViewModel.cs:638`](../../XplorePlane/ViewModels/Inspection/TaskWizard/InspectionTaskWizardViewModel.cs#L638);后续代码已被编译器报 `CS0162` |
|
||||
| P0-2 | `MotionState` 事件可能无变化洪泛 | **确认存在** | `BuildMotionStateSnapshot` 每次生成新的 record;[`AppStateService.cs:790-794`](../../XplorePlane/Services/AppState/AppStateService.cs#L790) 使用 `ReferenceEquals` 去重 |
|
||||
| P1-1 | `_acquireQueue` 长期保留大尺寸帧 | **确认存在,需先验证运行时占用** | [`DetectorFramePipelineService.cs:29`](../../XplorePlane/Services/Main/Viewport/DetectorFramePipelineService.cs#L29) 入队后没有对应出队;处理队列 `_processQueue` 则有后台消费者,不应把两者混为一谈 |
|
||||
| P1-2 | 调试面板 `PerformanceMonitorViewModel` 事件订阅无法解除 | **确认存在** | [`PerformanceMonitorViewModel.cs:84-90`](../../XplorePlane/ViewModels/Debug/PerformanceMonitorViewModel.cs#L84) 使用匿名 lambda;[`Dispose`](../../XplorePlane/ViewModels/Debug/PerformanceMonitorViewModel.cs#L96) 未反订阅 |
|
||||
| P1-3 | `_latestGeometry` 跨线程读写缺少明确同步 | **确认存在,风险待量化** | [`AppStateService.cs:161`](../../XplorePlane/Services/AppState/AppStateService.cs#L161)、[`AppStateService.cs:566-571`](../../XplorePlane/Services/AppState/AppStateService.cs#L566) |
|
||||
| P1-4 | 探测器断连状态判断不是单一原子事务 | **部分确认** | [`AppStateService.cs:579-585`](../../XplorePlane/Services/AppState/AppStateService.cs#L579) 在后台线程读取 `_detectorState`;最终状态更新仍通过原子替换,但 `wasConnected` 与后续更新之间可能交错 |
|
||||
| P1-5 | `CncExecutionStateChanged` 没有主项目订阅方 | **确认存在** | `AppStateService` 有发布事件,但当前主项目未发现 `+=` 订阅;部分界面改读 `MainViewportService.IsCncRunning`,形成双来源风险 |
|
||||
| P1-6 | 硬件错误事件没有进入统一 `SystemState` | **确认存在** | `MotionErrorEvent`、探测器 `ErrorOccurredEvent`、射线源 `ErrorOccurredEvent` 在硬件层有发布,但主项目未发现对应统一订阅;射线源自身 ViewModel 的局部订阅不等于全局故障状态闭环 |
|
||||
| P1-7 | `OperationMode` 始终为 `Idle`,权限守卫可能失效 | **确认存在** | 当前唯一写入点 [`RecipeService.cs:225`](../../XplorePlane/Services/Recipe/RecipeService.cs#L225) 仍写入 `OperationMode.Idle`;[`PermissionService.cs:159`](../../XplorePlane/Services/Security/PermissionService.cs#L159) 依赖该字段 |
|
||||
| P2-1 | 标定矩阵/画面联动没有外部调用方 | **基本确认** | `UpdateCalibrationMatrix`、`RequestLinkedView`、`UpdateLinkedViewState` 的调用主要位于 `AppStateService` 自身,未发现业务入口;需结合实际 UI 需求决定是接入还是删除 |
|
||||
| P2-2 | `WindowLauncherService.ShowOrActivateVisible` 关闭后保留窗口引用 | **确认存在** | [`WindowLauncherService.cs:91-106`](../../XplorePlane/Services/Main/WindowLauncherService.cs#L91) 没有像 `ShowOrActivate` 一样挂载 `Closed` 清理逻辑;接口注释也明确记录了该历史行为 |
|
||||
| P2-3 | 事件投递顺序与最新属性值可能不一致 | **待验证** | 目前代码确实存在 `Interlocked.Exchange` 与 `Dispatcher.BeginInvoke` 的组合,但是否造成实际错序需要并发测试或日志证据,不能仅凭静态审阅定性为已发生故障 |
|
||||
| P2-4 | `CncRoiEditRequestedPayload` 携带 `Action` 委托 | **设计风险确认** | [`CncEvents.cs:54-60`](../../XplorePlane/Events/CncEvents.cs#L54) 将回调放入全局事件 Payload,存在生命周期、重入和异常传播风险 |
|
||||
| P2-5 | 探测器每帧复制大数组并被多处缓存 | **部分确认** | [`DetectorFramePipelineService.cs:99-114`](../../XplorePlane/Services/Main/Viewport/DetectorFramePipelineService.cs#L99) 每帧复制原始数组并生成 BitmapSource;是否达到约 600 MB 取决于实际队列深度、图像尺寸和 GC 回收,需运行时采样确认 |
|
||||
|
||||
## 2. 审阅材料中的修正项
|
||||
|
||||
### 2.1 “`_acquireQueue` 和 `ProcessFrameDequeued` 都没有消费者”不准确
|
||||
|
||||
当前实现中:
|
||||
|
||||
- `_acquireQueue` 确实只有入队和容量淘汰,没有业务消费方;这是需要处理的资源保留问题。
|
||||
- `_processQueue` 由 `ProcessLoopAsync` 消费,并发布 `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 执行状态的唯一事实源,统一 `CncExecutionState` 与 `MainViewportService.IsCncRunning` 的读写路径。
|
||||
2. 将硬件错误事件统一映射到 `SystemState`,并明确 UI、日志和权限层的消费方。
|
||||
3. 为 `ShowOrActivateVisible` 增加 `Closed` 清理;补充窗口重复打开、关闭后重开测试。
|
||||
4. 评估标定/画面联动 API 和带回调的 CNC 事件是否应改成专用服务或请求 ID。
|
||||
|
||||
## 4. 验收标准
|
||||
|
||||
- 静止设备不再因无变化的 `MotionState` 每 100 ms 触发完整 UI 更新。
|
||||
- 调试面板关闭后,旧 `PerformanceMonitorViewModel` 不再接收 AppState 事件。
|
||||
- `_acquireQueue` 有明确消费者或明确的淘汰/禁用策略,运行时内存曲线可解释。
|
||||
- BGA 流水线输出缺失时有可见错误,不再静默返回;编译器不再报告该方法的不可达代码。
|
||||
- CNC 执行中权限判断、主界面状态和执行服务使用同一个状态来源。
|
||||
- 硬件故障能够进入统一状态、日志和 UI 告警链路。
|
||||
|
||||
## 5. 关联文档
|
||||
|
||||
- [`doc/评估.md`](../评估.md):项目整体评估和既有死代码/占位实现清单。
|
||||
- [`doc/archive/状态与事件流架构.md`](../archive/状态与事件流架构.md):AppState 与事件流架构说明。
|
||||
- [`doc/fix/调试面板无限弹窗修复说明.md`](调试面板无限弹窗修复说明.md):调试面板窗口生命周期背景。
|
||||
Reference in New Issue
Block a user