From d079e9357beb49879f3eb5acba78d58c4382df12 Mon Sep 17 00:00:00 2001 From: "zhengxuan.zhang" Date: Wed, 6 May 2026 23:26:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A1=AC=E4=BB=B6=E9=9B=86?= =?UTF-8?q?=E6=88=90=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- XplorePlane/Doc/硬件层及UI集成技术路线.md | Bin 24576 -> 0 bytes XplorePlane/Doc/硬件层及UI集成技术路线.txt | 430 +++++++++++++++++++++ 2 files changed, 430 insertions(+) delete mode 100644 XplorePlane/Doc/硬件层及UI集成技术路线.md create mode 100644 XplorePlane/Doc/硬件层及UI集成技术路线.txt diff --git a/XplorePlane/Doc/硬件层及UI集成技术路线.md b/XplorePlane/Doc/硬件层及UI集成技术路线.md deleted file mode 100644 index ede56ae53289c779150bf72f365098855669569c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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

Ufdo-;wgp;fYXvT@vOp?hDE$~e$O}9S{ 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 cNa^BhmD}n` 引用。 + +DLL 内部包含: + +| 类型 | 名称 | 说明 | +|------|------|------| +| UserControl | `RaySourceOperateView` | 射线源操作界面 | +| ViewModel | `RaySourceOperateViewModel` | 对应 ViewModel | +| 服务接口/实现 | `IRaySourceService` / `RaySourceService` | 射线源业务逻辑 | +| 工厂 | `IRaySourceFactory` / `RaySourceFactory` | 策略工厂,支持 Comet 160kv、Hamamatsu160kv | +| 服务 | `IFilamentLifetimeService` | 灯丝寿命管理 | +| 配置模型 | `RaySourceConfig` | 从 App.config 加载的配置 | + +--- + +### 2. DI 注册层(App.xaml.cs → AppBootstrapper) + +主项目在 `RegisterTypes()` 中**手动注册** DLL 内所有服务,未走 Prism 的 `ConfigureModuleCatalog` 自动模块加载,目的是避免模块加载顺序问题,确保 DryIoc 容器在 Shell 创建前已具备所有依赖。 + +```csharp +// 注册 ViewModel(供 ViewModelLocator 自动装配) +containerRegistry.Register(); + +// 注册配置(从 App.config 读取 RaySource:xxx 键值) +var raySourceConfig = XP.Hardware.RaySource.Config.ConfigLoader.LoadConfig(); +containerRegistry.RegisterInstance(raySourceConfig); + +// 注册核心服务(全部单例) +containerRegistry.RegisterSingleton(); +containerRegistry.RegisterSingleton(); +containerRegistry.RegisterSingleton(); +``` + +--- + +### 3. XAML 嵌入层(MainWindow.xaml) + +通过 XML 命名空间直接引用 DLL 中的 View: + +```xml +xmlns:views1="clr-namespace:XP.Hardware.RaySource.Views;assembly=XP.Hardware.RaySource" +``` + +在主窗口右侧面板顶部(Grid.Row="0",固定高度 250px)放置控件: + +```xml + +``` + +控件内部已设置 `prism:ViewModelLocator.AutoWireViewModel="True"`,Prism 按命名约定自动从 DI 容器解析 `RaySourceOperateViewModel` 并绑定为 DataContext。 + +--- + +### 4. 数据传递路线 + +数据流分四条路径: + +**路径 A:配置数据(启动时,单向下行)** + +``` +App.config (RaySource:xxx 键值) + → ConfigLoader.LoadConfig() + → RaySourceConfig 实例 + → 注入到 RaySourceService / RaySourceOperateViewModel +``` + +App.config 中的关键配置项: + +```xml + + + + + +``` + +**路径 B:用户操作(UI → DLL 服务层)** + +``` +RaySourceOperateView(按钮点击) + → RaySourceOperateViewModel(Command 绑定) + → IRaySourceService.SetVoltageAsync() / TurnOnAsync() / ... + → IXRaySource(具体策略实现,如 Comet225) + → 硬件通讯(B&R PVI / BR.AN.PviServices.dll) +``` + +**路径 C:状态回传(DLL 服务层 → UI)** + +``` +硬件状态轮询(StatusPollingInterval = 500ms) + → RaySourceService 内部更新 + → RaySourceOperateViewModel 属性变更(INotifyPropertyChanged) + → RaySourceOperateView 数据绑定自动刷新 + +同时: + → AppStateService 订阅 IRaySourceService 事件 + → 更新 RaySourceState(IsOn, Voltage, Power) + → Dispatcher.BeginInvoke 调度到 UI 线程 + → 其他 ViewModel 通过 IAppStateService 读取全局射线源状态 +``` + +`RaySourceState` 为不可变 record,定义于 `Models/StateModels.cs`: + +```csharp +public record RaySourceState( + bool IsOn, // 开关状态 + double Voltage, // 电压 (kV) + double Power // 功率 (W) +) +{ + public static readonly RaySourceState Default = new(false, 0, 0); +} +``` + +**路径 D:跨模块事件通讯(Prism EventAggregator)** + +``` +DLL 内部发布事件(XP.Hardware.RaySource.Abstractions.Events): + XrayStateChangedEvent — 射线开关状态变化 + StatusUpdatedEvent — 实时电压/电流数据 + ErrorOccurredEvent — 错误通知 + OperationResultEvent — 操作结果回调 + +主项目任意 ViewModel 订阅示例: + _eventAggregator.GetEvent().Subscribe(data => { ... }); +``` + +--- + +### 5. 完整依赖关系 + +``` +RaySourceOperateView(DLL 中的 UserControl) + └─ AutoWire → RaySourceOperateViewModel(DLL,主项目注册到 DI) + ├─ IRaySourceService ← 单例,DLL 实现 + ├─ RaySourceConfig ← App.config 加载 + ├─ IFilamentLifetimeService ← 单例,DLL 实现 + ├─ IEventAggregator ← Prism 内置 + ├─ ILoggerService ← 主项目 LoggerServiceAdapter 适配 Serilog + └─ ILocalizationService ← 主项目注册,DLL 消费 +``` + +--- + +### 6. 资源释放 + +`App.OnExit()` 中显式从容器解析并释放资源: + +```csharp +var appStateService = bootstrapper.Container.Resolve(); +appStateService?.Dispose(); + +var raySourceService = bootstrapper.Container.Resolve(); +raySourceService?.Dispose(); +``` + +确保硬件连接在应用退出时正确断开。 + +### 7. + +--- + +# 硬件层 → AppState → UI 状态同步机制 + +## 整体结论 + +| 硬件子系统 | 同步方式 | AppState 订阅 | 状态是否自动同步 | +|-----------|---------|--------------|----------------| +| 运动控制(Motion) | 事件驱动 + 轮询 | ✅ 已订阅 `GeometryUpdatedEvent` / `AxisStatusChangedEvent` | ✅ 自动同步 | +| 射线源(RaySource) | 事件定义存在 | ❌ 未订阅任何事件 | ⚠️ 需手动推送 | +| 探测器(Detector) | 事件定义存在 | ❌ 未订阅任何事件 | ⚠️ 需手动推送 | +| 相机(Camera) | ViewModel 直连 | ❌ 不经过 AppState | ⚠️ `CameraState` 为空壳 | + +--- + +## 一、运动控制 — 完整链路 + +### 1.1 数据流 + +``` +PLC 硬件(B&R) + └─ IPlcService.IsConnected(轮询前置检查) + └─ MotionControlService.OnPollingTick() [System.Threading.Timer,周期 = PollingInterval ms] + ├─ _motionSystem.UpdateAllStatus() ← 从 PLC 读取所有轴实际位置 + ├─ GetCurrentGeometry() ← 正算 FOD / FDD / Magnification + ├─ 发布 GeometryUpdatedEvent ─┐ + └─ 发布 AxisStatusChangedEvent ─┤ + ↓ + AppStateService(构造时订阅两个事件) + ├─ OnGeometryUpdated() + └─ OnAxisStatusChanged() + └─ TryRefreshMotionStateFromHardware() + └─ BuildMotionStateSnapshot() + ├─ 读取所有轴 ActualPosition / ActualAngle + └─ SetMotionState() + └─ 触发 MotionStateChanged 事件 + └─ ViewModel 绑定自动刷新 UI +``` + +### 1.2 关键实现细节 + +**轮询启动**:`MotionControlService.StartPolling()` 需在应用启动时显式调用,否则轮询不会运行。 + +**PLC 未连接时的保护**: +```csharp +// MotionControlService.OnPollingTick() +if (!_plcService.IsConnected) return; // 直接跳过,不报错 +``` + +**连续错误降频**: +```csharp +if (_pollErrorCount > 3) +{ + if (++_pollErrorCount % 50 != 0) return; // 每 50 次才尝试一次,防止日志刷屏 +} +``` + +**AppStateService 初始化时的首次刷新**: +```csharp +// AppStateService 构造函数末尾 +private void SubscribeToExistingServices() +{ + if (TryRefreshMotionStateFromHardware("initialization")) + _logger.Info("AppStateService subscribed to motion hardware state"); + else + _logger.Warn("AppStateService could not initialize motion state from hardware"); +} +``` +PLC 未连接时 warn 但不崩溃,等待后续轮询事件触发再同步。 + +**`UpdateMotionState()` 的特殊行为**: +```csharp +public void UpdateMotionState(MotionState newState) +{ + // 优先从硬件层拉取最新快照,忽略外部传入值 + if (TryRefreshMotionStateFromHardware("UpdateMotionState")) + return; + + // 硬件不可用时才使用传入值(降级路径) + SetMotionState(newState); +} +``` +硬件连接后,外部调用 `UpdateMotionState()` 实际上会被硬件快照覆盖,硬件层始终是 `MotionState` 的唯一真实来源。 + +### 1.3 事件清单 + +| 事件 | 发布方 | 订阅方 | 触发时机 | +|------|--------|--------|---------| +| `GeometryUpdatedEvent` | `MotionControlService` | `AppStateService`、`MotionControlViewModel`、`AxisControlViewModel` | 每次轮询 tick | +| `AxisStatusChangedEvent` | `MotionControlService` | `AppStateService`、`MotionControlViewModel`、`AxisControlViewModel` | 轴状态发生变化时 | +| `MotionErrorEvent` | `MotionControlService` | — | 轴状态变为 Error / Alarm | +| `DoorStatusChangedEvent` | `MotionControlService` | `MotionControlViewModel` | 安全门状态变化 | +| `DoorInterlockChangedEvent` | `MotionControlService` | — | 联锁状态变化 | +| `GeometryApplyRequestEvent` | DebugWindow | `MotionControlViewModel` | 调试窗口发起几何反算请求 | + +--- + +## 二、射线源 — 断链(待补全) + +### 2.1 现状 + +`AppStateService` 持有 `IRaySourceService` 引用,但**没有订阅任何射线源事件**。`RaySourceState` 只能通过外部显式调用 `UpdateRaySourceState()` 手动推送,硬件状态变化不会自动反映到 AppState。 + +``` +XP.Hardware.RaySource 发布的事件(均无人在 AppState 层订阅): + ├─ StatusUpdatedEvent ← 实时电压 / 电流 / 功率数据 + ├─ RaySourceStatusChangedEvent ← 三态状态(On / Off / Fault) + ├─ VariablesConnectedEvent ← PLC 变量连接状态 + └─ OperationResultEvent ← 操作执行结果 +``` + +### 2.2 当前 `RaySourceState` 的写入路径 + +目前只有两处会更新 `RaySourceState`: + +1. `CncProgramService.CreateReferencePointNode()` — 创建参考点时读取一次快照 +2. 任何直接调用 `appStateService.UpdateRaySourceState()` 的地方(目前代码中无此调用) + +### 2.3 修复方案 + +在 `AppStateService` 构造时订阅 `StatusUpdatedEvent`,将实时数据映射到 `RaySourceState`: + +```csharp +// AppStateService 构造函数中补充 +_raySourceStatusToken = _eventAggregator + .GetEvent() + .Subscribe(OnRaySourceStatusUpdated); + +// 处理方法 +private void OnRaySourceStatusUpdated(SystemStatusData data) +{ + if (_disposed) return; + UpdateRaySourceState(new RaySourceState( + IsOn: data.IsOn, + Voltage: data.Voltage, + Power: data.Power)); +} +``` + +同时在 `Dispose()` 中取消订阅,并在 `AppStateService` 的字段和 `Dispose` 方法中补充对应的 `SubscriptionToken`。 + +--- + +## 三、探测器 — 断链(待补全) + +### 3.1 现状 + +与射线源情况相同,`AppStateService` 没有订阅任何探测器事件,`DetectorState` 是静态默认值。 + +``` +XP.Hardware.Detector 发布的事件(均无人在 AppState 层订阅): + ├─ StatusChangedEvent ← 探测器连接 / 采集状态变化 + ├─ ImageCapturedEvent ← 图像采集完成 + ├─ CorrectionCompletedEvent ← 校正完成 + └─ ErrorOccurredEvent ← 错误发生 +``` + +### 3.2 修复方案 + +在 `AppStateService` 构造时订阅 `StatusChangedEvent`: + +```csharp +_detectorStatusToken = _eventAggregator + .GetEvent() + .Subscribe(OnDetectorStatusChanged); + +private void OnDetectorStatusChanged(DetectorStatus status) +{ + if (_disposed) return; + UpdateDetectorState(new DetectorState( + IsConnected: status.IsConnected, + IsAcquiring: status.IsAcquiring, + FrameRate: status.FrameRate, + Resolution: status.Resolution)); +} +``` + +--- + +## 四、相机 — 独立直连,不经过 AppState + +### 4.1 现状 + +相机完全绕过 `AppStateService`,由 `NavigationPropertyPanelViewModel` 直接持有 `ICamera` 引用并订阅硬件事件: + +``` +NavigationPropertyPanelViewModel + └─ 直接持有 ICamera 引用 + ├─ _camera.ImageGrabbed += OnCameraImageGrabbed + ├─ _camera.GrabError += OnCameraGrabError + └─ _camera.ConnectionLost += OnCameraConnectionLost + └─ IsCameraConnected 属性直接驱动 UI 命令可用性 +``` + +`AppStateService.CameraState` 字段目前是空壳,没有任何地方向它写入真实数据。 + +### 4.2 架构说明 + +相机的独立直连是有意为之的设计——相机图像流数据量大、帧率高,不适合经过 AppState 的不可变 record 替换机制。当前图像通过 `ManualImageLoadedEvent` 在模块间传递,与状态管理解耦。 + +如需将相机连接状态纳入 AppState 统一管理(例如供其他模块查询),可在 `OnCameraConnectionLost` / `ConnectCamera` 中补充调用 `_appStateService.UpdateCameraState()`,仅同步连接状态,不同步图像帧。 + +--- + +## 五、状态同步全景图 + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ 硬件层 │ +│ │ +│ PLC ──→ MotionControlService ──→ GeometryUpdatedEvent ─────┐ │ +│ └──→ AxisStatusChangedEvent ────┤ │ +│ │ │ +│ RaySource ──→ IRaySourceService ──→ StatusUpdatedEvent ─ ─ ┤ │ +│ └──→ RaySourceStatusChangedEvent ─┤│ +│ │ │ +│ Detector ──→ IDetectorService ──→ StatusChangedEvent ─ ─ ┤ │ +│ │ │ +│ Camera ──→ ICamera ──→ ImageGrabbed / ConnectionLost ─ ─ ┘ │ +└─────────────────────────────────────────────────────────────────────┘ + │ 已接通 ✅ │ 断链 ⚠️ + ↓ ↓ +┌─────────────────────────────────────────────────────────────────────┐ +│ AppStateService │ +│ │ +│ MotionState ← 自动同步(轮询 + 事件驱动) ✅ │ +│ RaySourceState ← 需手动推送 / 待补订阅 ⚠️ │ +│ DetectorState ← 需手动推送 / 待补订阅 ⚠️ │ +│ CameraState ← 空壳,相机走独立直连路径 ⚠️ │ +└─────────────────────────────────────────────────────────────────────┘ + │ + ↓ PropertyChanged / StateChangedEvent +┌─────────────────────────────────────────────────────────────────────┐ +│ ViewModel 层 │ +│ │ +│ MotionControlViewModel ← 同时订阅 GeometryUpdatedEvent(双路) │ +│ CncNodeViewModel ← 通过 IAppStateService 读取快照 │ +│ CncEditorViewModel ← 通过 IAppStateService 读取快照 │ +│ NavigationPropertyPanelViewModel ← 直连 ICamera(独立路径) │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + + +## 六、待办事项 + +| 优先级 | 项目 | 涉及文件 | +|--------|------|---------| +| 高 | 补充射线源事件订阅,打通 `RaySourceState` 自动同步 | `AppStateService.cs` | +| 高 | 补充探测器事件订阅,打通 `DetectorState` 自动同步 | `AppStateService.cs` | +| 中 | 确认 `StartPolling()` 在应用启动流程中被调用 | `App.xaml.cs` / `AppBootstrapper` | +| 低 | 评估是否将相机连接状态纳入 `CameraState` 统一管理 | `NavigationPropertyPanelViewModel.cs`、`AppStateService.cs` | + + +