#0048 图像算子流程文件,保存文件后缀 .imw

This commit is contained in:
zhengxuan.zhang
2026-03-26 17:04:10 +08:00
parent 69af65644a
commit da43dfc595
5 changed files with 259 additions and 5 deletions
+241
View File
@@ -0,0 +1,241 @@
# AppStateService 使用指南
## 概述
`AppStateService` 是全局单例状态管理服务,聚合所有硬件和系统状态。核心特性:
- **不可变状态**:所有状态模型均为 `record` 类型,更新即替换
- **线程安全**:内部使用 `Interlocked.Exchange` 原子替换,可从任意线程调用 `Update`
- **UI 线程调度**:事件和 `PropertyChanged` 自动通过 `Dispatcher.BeginInvoke` 调度到 UI 线程
- **WPF 绑定友好**:继承 `BindableBase`,支持直接在 XAML 中绑定
## 1. 获取服务实例
通过 Prism 依赖注入获取(ViewModel 构造函数注入):
```csharp
public class MyViewModel : BindableBase
{
private readonly IAppStateService _appState;
public MyViewModel(IAppStateService appState)
{
_appState = appState;
}
}
```
## 2. 读取状态
所有状态属性均为只读,直接访问即可:
```csharp
// 读取射线源状态
RaySourceState ray = _appState.RaySourceState;
bool isOn = ray.IsOn; // 开关状态
double voltage = ray.Voltage; // 电压 (kV)
double power = ray.Power; // 功率 (W)
// 读取运动状态
MotionState motion = _appState.MotionState;
double xPos = motion.XM; // X 轴位置 (μm)
double yPos = motion.YM; // Y 轴位置 (μm)
// 读取探测器状态
DetectorState det = _appState.DetectorState;
bool connected = det.IsConnected;
double fps = det.FrameRate;
// 读取系统状态
SystemState sys = _appState.SystemState;
OperationMode mode = sys.OperationMode;
bool hasError = sys.HasError;
```
## 3. 更新状态
状态模型是不可变 record,更新时创建新实例或使用 `with` 表达式:
```csharp
// 方式一:创建全新实例
_appState.UpdateRaySourceState(new RaySourceState(
IsOn: true,
Voltage: 120.0,
Power: 50.0
));
// 方式二:基于当前状态用 with 修改部分字段(推荐)
var current = _appState.RaySourceState;
_appState.UpdateRaySourceState(current with { Voltage = 150.0 });
// 更新运动状态
_appState.UpdateMotionState(new MotionState(
XM: 1000.0, YM: 2000.0, ZT: 500.0, ZD: 300.0,
TiltD: 0, Dist: 0,
XMSpeed: 100, YMSpeed: 100, ZTSpeed: 50, ZDSpeed: 50,
TiltDSpeed: 0, DistSpeed: 0
));
// 更新系统状态(报告错误)
_appState.UpdateSystemState(_appState.SystemState with
{
HasError = true,
ErrorMessage = "探测器连接超时"
});
```
> **注意**:传入 `null` 会抛出 `ArgumentNullException``Dispose()` 后调用 `Update` 会被静默忽略。
## 4. 订阅状态变更事件
每种状态都有对应的类型化事件,回调自动在 UI 线程执行:
```csharp
public class RaySourceMonitorViewModel : BindableBase, IDisposable
{
private readonly IAppStateService _appState;
public RaySourceMonitorViewModel(IAppStateService appState)
{
_appState = appState;
// 订阅射线源状态变更
_appState.RaySourceStateChanged += OnRaySourceStateChanged;
// 订阅运动状态变更
_appState.MotionStateChanged += OnMotionStateChanged;
// 订阅系统状态变更
_appState.SystemStateChanged += OnSystemStateChanged;
}
private void OnRaySourceStateChanged(object sender, StateChangedEventArgs<RaySourceState> e)
{
// e.OldValue - 变更前的状态
// e.NewValue - 变更后的状态(已在 UI 线程)
if (!e.OldValue.IsOn && e.NewValue.IsOn)
{
// 射线刚刚开启
Console.WriteLine($"射线已开启,电压={e.NewValue.Voltage}kV");
}
if (e.OldValue.Voltage != e.NewValue.Voltage)
{
Console.WriteLine($"电压变化: {e.OldValue.Voltage}kV → {e.NewValue.Voltage}kV");
}
}
private void OnMotionStateChanged(object sender, StateChangedEventArgs<MotionState> e)
{
Console.WriteLine($"X轴: {e.OldValue.XM} → {e.NewValue.XM} μm");
}
private void OnSystemStateChanged(object sender, StateChangedEventArgs<SystemState> e)
{
if (e.NewValue.HasError)
{
Console.WriteLine($"系统错误: {e.NewValue.ErrorMessage}");
}
}
public void Dispose()
{
// 取消订阅,防止内存泄漏
_appState.RaySourceStateChanged -= OnRaySourceStateChanged;
_appState.MotionStateChanged -= OnMotionStateChanged;
_appState.SystemStateChanged -= OnSystemStateChanged;
}
}
```
## 5. XAML 数据绑定
直接绑定到 `IAppStateService` 的属性(需要将服务暴露为 ViewModel 属性):
```csharp
// ViewModel
public class DashboardViewModel : BindableBase
{
public IAppStateService AppState { get; }
public DashboardViewModel(IAppStateService appState)
{
AppState = appState;
}
}
```
```xml
<!-- XAML -->
<StackPanel DataContext="{Binding AppState}">
<!-- 射线源状态 -->
<TextBlock Text="{Binding RaySourceState.Voltage, StringFormat='电压: {0:F1} kV'}" />
<TextBlock Text="{Binding RaySourceState.Power, StringFormat='功率: {0:F1} W'}" />
<Ellipse Width="12" Height="12"
Fill="{Binding RaySourceState.IsOn, Converter={StaticResource BoolToColorConverter}}" />
<!-- 运动位置 -->
<TextBlock Text="{Binding MotionState.XM, StringFormat='X: {0:F2} μm'}" />
<TextBlock Text="{Binding MotionState.YM, StringFormat='Y: {0:F2} μm'}" />
<!-- 系统状态 -->
<TextBlock Text="{Binding SystemState.OperationMode}" />
<TextBlock Text="{Binding SystemState.ErrorMessage}" Foreground="Red"
Visibility="{Binding SystemState.HasError, Converter={StaticResource BoolToVisConverter}}" />
</StackPanel>
```
## 6. 完整场景示例:硬件状态轮询 + UI 更新
```csharp
public class HardwarePollingService : IDisposable
{
private readonly IAppStateService _appState;
private readonly IRaySourceService _raySource;
private readonly Timer _timer;
public HardwarePollingService(IAppStateService appState, IRaySourceService raySource)
{
_appState = appState;
_raySource = raySource;
// 每 500ms 轮询一次硬件状态
_timer = new Timer(PollHardware, null, 0, 500);
}
private async void PollHardware(object state)
{
// 从硬件读取(在后台线程执行)
var voltageResult = await _raySource.ReadVoltageAsync();
var currentResult = await _raySource.ReadCurrentAsync();
if (voltageResult.Success && currentResult.Success)
{
var voltage = (double)voltageResult.Value;
var current = (double)currentResult.Value;
// 线程安全更新,事件自动调度到 UI 线程
_appState.UpdateRaySourceState(new RaySourceState(
IsOn: _raySource.IsXRayOn,
Voltage: voltage,
Power: voltage * current / 1000.0
));
}
}
public void Dispose() => _timer?.Dispose();
}
```
## 可用状态一览
| 属性 | 类型 | 关键字段 |
|------|------|----------|
| `MotionState` | `MotionState` | XM, YM, ZT, ZD, TiltD, Dist 及各轴速度 |
| `RaySourceState` | `RaySourceState` | IsOn, Voltage, Power |
| `DetectorState` | `DetectorState` | IsConnected, IsAcquiring, FrameRate, Resolution |
| `SystemState` | `SystemState` | OperationMode, HasError, ErrorMessage |
| `CameraState` | `CameraState` | IsConnected, IsStreaming, CurrentFrame, Width, Height, FrameRate |
| `CalibrationMatrix` | `CalibrationMatrix` | 3×3 仿射矩阵,支持 Transform(px, py) |
| `LinkedViewState` | `LinkedViewState` | TargetPosition, IsExecuting, LastRequestTime |
| `RecipeExecutionState` | `RecipeExecutionState` | CurrentStepIndex, TotalSteps, Status, CurrentRecipeName |
@@ -74,7 +74,7 @@ namespace XplorePlane.Services
if (!Directory.Exists(directory))
return Array.Empty<PipelineModel>();
var files = Directory.GetFiles(directory, "*.pipeline.json");
var files = Directory.GetFiles(directory, "*.imw");
var results = new List<PipelineModel>();
foreach (var file in files)
@@ -374,7 +374,7 @@ namespace XplorePlane.ViewModels
var dialog = new SaveFileDialog
{
Filter = "流水线文件 (*.pipeline.json)|*.pipeline.json",
Filter = "图像处理流水线 (*.imw)|*.imw",
FileName = PipelineName,
InitialDirectory = GetPipelineDirectory()
};
@@ -423,7 +423,7 @@ namespace XplorePlane.ViewModels
{
var dialog = new OpenFileDialog
{
Filter = "流水线文件 (*.pipeline.json)|*.pipeline.json",
Filter = "图像处理流水线 (*.imw)|*.imw",
InitialDirectory = GetPipelineDirectory()
};
+1 -1
View File
@@ -457,7 +457,7 @@
<RowDefinition Height="*" />
<RowDefinition Height="340" />
</Grid.RowDefinitions>
<views:RaySourcePanelView Grid.Row="0" Grid.ColumnSpan="2" />
<views:RaySourceOperateView Grid.Row="0" Grid.ColumnSpan="2" />
<views:MotionControlPanelView
Grid.Row="1"
Grid.RowSpan="2"
+14 -1
View File
@@ -45,11 +45,24 @@
----------------------
1、各窗体间数据流的传递,全局数据结构的设计(包括一个基本的说明文档)√
2、将telerik 升级到 2024.1.408.310;调整界面和主题;引入 硬件层依赖 √
3、图像算子流程文件,保存文件后缀 .imw, image process workflow 缩写
3、图像算子流程文件,保存文件后缀 .imw, image process workflow 缩写
4、CNC保存文件后缀为.xp, 表示 XplorePlane CNC file 的缩写
5、硬件层射线源控件的集成
2026.3.27
----------------------
CNC及矩阵功能的设计与实现,包含以下功能:
1、CNC功能设计与实现,包含以下功能:
a. CNC状态的定义和管理
b. CNC界面设计与实现
c. CNC相关数据的传递和处理
2、CNC相关的编排工具,如插入节点,插入位置,图像模块,等