#0048 图像算子流程文件,保存文件后缀 .imw
This commit is contained in:
@@ -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))
|
if (!Directory.Exists(directory))
|
||||||
return Array.Empty<PipelineModel>();
|
return Array.Empty<PipelineModel>();
|
||||||
|
|
||||||
var files = Directory.GetFiles(directory, "*.pipeline.json");
|
var files = Directory.GetFiles(directory, "*.imw");
|
||||||
var results = new List<PipelineModel>();
|
var results = new List<PipelineModel>();
|
||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
var dialog = new SaveFileDialog
|
var dialog = new SaveFileDialog
|
||||||
{
|
{
|
||||||
Filter = "流水线文件 (*.pipeline.json)|*.pipeline.json",
|
Filter = "图像处理流水线 (*.imw)|*.imw",
|
||||||
FileName = PipelineName,
|
FileName = PipelineName,
|
||||||
InitialDirectory = GetPipelineDirectory()
|
InitialDirectory = GetPipelineDirectory()
|
||||||
};
|
};
|
||||||
@@ -423,7 +423,7 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
var dialog = new OpenFileDialog
|
var dialog = new OpenFileDialog
|
||||||
{
|
{
|
||||||
Filter = "流水线文件 (*.pipeline.json)|*.pipeline.json",
|
Filter = "图像处理流水线 (*.imw)|*.imw",
|
||||||
InitialDirectory = GetPipelineDirectory()
|
InitialDirectory = GetPipelineDirectory()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@
|
|||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="340" />
|
<RowDefinition Height="340" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<views:RaySourcePanelView Grid.Row="0" Grid.ColumnSpan="2" />
|
<views:RaySourceOperateView Grid.Row="0" Grid.ColumnSpan="2" />
|
||||||
<views:MotionControlPanelView
|
<views:MotionControlPanelView
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
|
|||||||
+14
-1
@@ -45,11 +45,24 @@
|
|||||||
----------------------
|
----------------------
|
||||||
1、各窗体间数据流的传递,全局数据结构的设计(包括一个基本的说明文档)√
|
1、各窗体间数据流的传递,全局数据结构的设计(包括一个基本的说明文档)√
|
||||||
2、将telerik 升级到 2024.1.408.310;调整界面和主题;引入 硬件层依赖 √
|
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相关的编排工具,如插入节点,插入位置,图像模块,等
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user