修复CNC结束时导航合成截图归档

This commit is contained in:
zhengxuan.zhang
2026-08-11 16:34:22 +08:00
parent 7bfd1e1085
commit bf574ad55b
4 changed files with 80 additions and 13 deletions
+14 -6
View File
@@ -7,7 +7,11 @@ XplorePlane 系统用于控制平面 CT 设备的各个子系统(射线源、
### 总体架构 ### 总体架构
- 客户端框架: WPF + Prism MVVM(目标框架 net8.0-windows - 客户端框架: WPF + Prism MVVM(目标框架 net8.0-windows
- 图像处理内核: ImageProcessing.Core(算子基类)+ ImageProcessing.Processors(具体算子),基于 EmguCV - 图像处理内核: `XP.ImageProcessing.Processors`,基于 Emgu.CV
- `Core/`:包含 `ImageProcessorBase``ProcessorParameter` 及 ROI/对齐等核心模型和算子基类
- 各功能目录:包含图像变换、滤波、增强、边缘检测、检测分析和模板匹配等具体算子
- `ImageProcessingService`:负责算子程序集扫描、反射发现、注册及执行
- 统一使用 16 位图像处理通道,并通过适配器兼容 8 位算子
- 相机控制: XP.CameraBasler pylon SDK 封装,支持软件触发、参数读写) - 相机控制: XP.CameraBasler pylon SDK 封装,支持软件触发、参数读写)
- 硬件抽象: XP.Common + XP.Hardware.RaySource(射线源控制) - 硬件抽象: XP.Common + XP.Hardware.RaySource(射线源控制)
- 日志: Serilog - 日志: Serilog
@@ -27,11 +31,15 @@ XplorePlane.sln
├── XP.Hardware.RaySource/ # 射线源控制模块 ├── XP.Hardware.RaySource/ # 射线源控制模块
├── XP.Hardware.RaySource.Comet.Host/ # Comet 射线源独立 Host 进程 ├── XP.Hardware.RaySource.Comet.Host/ # Comet 射线源独立 Host 进程
├── XP.Hardware.RaySource.Comet.Messages/# Comet Host 通信消息定义 ├── XP.Hardware.RaySource.Comet.Messages/# Comet Host 通信消息定义
├── XP.ImageProcessing.Core/ # 图像处理算子基类与核心模型 ├── XP.ImageProcessing.Processors/ # 图像处理核心与具体算子实现
├── XP.ImageProcessing.Processors/ # 具体图像处理算子实现 │ ├── Core/ # 算子基类、参数模型、ROI/对齐模型
├── XP.ImageProcessing.CfgControl/ # 图像处理配置控件 │ ├── 图像变换/ # 灰度、旋转、镜像、阈值等
├── XP.ImageProcessing.RoiControl/ # ROI 绘制与测量控件 │ ├── 滤波处理/ # 滤波算子
├── XP.ImageProcessing.SmokeTest/ # 图像处理冒烟测试程序 │ ├── 图像增强/ # 增强算子
│ ├── 边缘检测/ # 边缘检测算子
│ ├── 检测分析/ # BGA、QFN、空洞率及测量算子
│ └── 定位识别/ # 模板匹配和定位算子
├── XP.ImageProcessing.RoiControl/ # ROI 绘制与测量控件
├── XP.Scan/ # 扫描模式相关模块 ├── XP.Scan/ # 扫描模式相关模块
├── XP.Calibration/ # 校准模块 ├── XP.Calibration/ # 校准模块
├── XP.ReportEngine/ # 报告生成模块 ├── XP.ReportEngine/ # 报告生成模块
@@ -779,10 +779,19 @@ namespace XplorePlane.Services.Cnc
/// 注意:与 HomeAll()(机械回零,驱动轴到机械零点建立坐标系)不同,MoveToSafePosition 是移动到用户配置的坐标。 /// 注意:与 HomeAll()(机械回零,驱动轴到机械零点建立坐标系)不同,MoveToSafePosition 是移动到用户配置的坐标。
/// 运动服务不可用时优雅降级(仿真模式直接返回)。 /// 运动服务不可用时优雅降级(仿真模式直接返回)。
/// </summary> /// </summary>
private BitmapSource GetInspectionMapOverviewImage(BitmapSource fallbackImage) private BitmapSource GetInspectionMapOverviewImage(BitmapSource fallbackImage)
{ {
if (_navigationFrameProvider?.TryGetLatestBitmapSource(out var navigationOverview) == true) if (_navigationFrameProvider?.TryCaptureCompositeBitmapSource(out var compositeOverview) == true)
{ {
_logger.ForModule<CncExecutionService>().Info(
"检查标记图 overview 使用导航窗体合成截图:{0}x{1}",
compositeOverview.PixelWidth,
compositeOverview.PixelHeight);
return compositeOverview;
}
if (_navigationFrameProvider?.TryGetLatestBitmapSource(out var navigationOverview) == true)
{
_logger.ForModule<CncExecutionService>().Info( _logger.ForModule<CncExecutionService>().Info(
"检查标记图 overview 使用导航相机大视野图:{0}x{1}", "检查标记图 overview 使用导航相机大视野图:{0}x{1}",
navigationOverview.PixelWidth, navigationOverview.PixelWidth,
@@ -10,8 +10,14 @@ namespace XplorePlane.Services.Hardware.Camera
void UpdateFrame(byte[] pixelData, int width, int height, string pixelFormat); void UpdateFrame(byte[] pixelData, int width, int height, string pixelFormat);
void UpdateFrame(BitmapSource bitmap); void UpdateFrame(BitmapSource bitmap);
bool TryGetLatestBitmapSource(out BitmapSource bitmap); /// <summary>注册导航窗体的合成截图回调(相机图像 + 标记叠加层)。</summary>
void SetCompositeCapture(Func<BitmapSource> capture);
/// <summary>获取导航窗体当前实际渲染的合成图像。</summary>
bool TryCaptureCompositeBitmapSource(out BitmapSource bitmap);
bool TryGetLatestBitmapSource(out BitmapSource bitmap);
} }
/// <summary>导航摄像头帧提供器</summary> /// <summary>导航摄像头帧提供器</summary>
@@ -26,8 +32,37 @@ namespace XplorePlane.Services.Hardware.Camera
private int _height; private int _height;
private string _pixelFormat = string.Empty; private string _pixelFormat = string.Empty;
private BitmapSource _bitmap; private BitmapSource _bitmap;
private Func<BitmapSource> _compositeCapture;
public DateTime? LatestCapturedAtUtc { get; private set; } public DateTime? LatestCapturedAtUtc { get; private set; }
public void SetCompositeCapture(Func<BitmapSource> capture)
{
lock (_syncRoot)
{
_compositeCapture = capture;
}
}
public bool TryCaptureCompositeBitmapSource(out BitmapSource bitmap)
{
Func<BitmapSource> capture;
lock (_syncRoot)
{
capture = _compositeCapture;
}
try
{
bitmap = capture?.Invoke();
return bitmap != null && bitmap.PixelWidth > 0 && bitmap.PixelHeight > 0;
}
catch
{
bitmap = null;
return false;
}
}
public void UpdateFrame(byte[] pixelData, int width, int height, string pixelFormat) public void UpdateFrame(byte[] pixelData, int width, int height, string pixelFormat)
{ {
@@ -7,6 +7,7 @@ using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using XplorePlane.Services.Hardware.Camera;
using XplorePlane.Services.Hardware.Coordinate; using XplorePlane.Services.Hardware.Coordinate;
using XplorePlane.Views.Main.Overlays; using XplorePlane.Views.Main.Overlays;
@@ -54,6 +55,17 @@ namespace XplorePlane.Views.Main
ResolveViewModel(); ResolveViewModel();
AttachMarkOverlay(); AttachMarkOverlay();
// 将导航窗体实际渲染结果注册给 CNC 归档服务,包含标记叠加层。
try
{
var frameProvider = AppBootstrapper.Instance?.Container.Resolve<INavigationCameraFrameProvider>();
frameProvider?.SetCompositeCapture(CaptureCompositeImage);
}
catch (Exception ex)
{
_diag.Warning(ex, "注册导航合成截图回调失败");
}
// 订阅 ViewModel 属性变化,驱动红十字线位置更新 // 订阅 ViewModel 属性变化,驱动红十字线位置更新
if (_viewModel != null) if (_viewModel != null)
{ {
@@ -71,6 +83,9 @@ namespace XplorePlane.Views.Main
/// </summary> /// </summary>
private BitmapSource? CaptureCompositeImage() private BitmapSource? CaptureCompositeImage()
{ {
if (!Dispatcher.CheckAccess())
return Dispatcher.Invoke(CaptureCompositeImage);
var target = cameraDisplayHost; var target = cameraDisplayHost;
if (target == null) if (target == null)
return null; return null;