1175 lines
47 KiB
C#
1175 lines
47 KiB
C#
using Emgu.CV;
|
|
using Emgu.CV.Structure;
|
|
using Microsoft.Win32;
|
|
using Prism.Commands;
|
|
using Prism.Dialogs;
|
|
using Prism.Events;
|
|
using Prism.Ioc;
|
|
using Prism.Mvvm;
|
|
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.Configuration;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using XP.Camera.Calibration;
|
|
using XP.Common.GeneralForm.Views;
|
|
using XP.Common.Logging.Interfaces;
|
|
using XP.Common.PdfViewer.Interfaces;
|
|
using XP.Hardware.MotionControl.Abstractions;
|
|
using XP.Hardware.MotionControl.Services;
|
|
using XplorePlane.Events;
|
|
using XplorePlane.Services.MainViewport;
|
|
using XplorePlane.Services.Storage;
|
|
using XplorePlane.ViewModels.Cnc;
|
|
using XplorePlane.Views;
|
|
using XplorePlane.Views.Cnc;
|
|
|
|
namespace XplorePlane.ViewModels
|
|
{
|
|
public class MainViewModel : BindableBase
|
|
{
|
|
private const double CncEditorHostWidth = 452d;
|
|
private readonly ILoggerService _logger;
|
|
private readonly IContainerProvider _containerProvider;
|
|
private readonly IEventAggregator _eventAggregator;
|
|
private readonly IMainViewportService _mainViewportService;
|
|
private readonly IXpDataPathService _xpDataPathService;
|
|
private readonly CncEditorViewModel _cncEditorViewModel;
|
|
private readonly CncPageView _cncPageView;
|
|
|
|
public string LicenseInfo
|
|
{
|
|
get => _licenseInfo;
|
|
set => SetProperty(ref _licenseInfo, value);
|
|
}
|
|
|
|
private string _statusMessage = "就绪";
|
|
|
|
public string StatusMessage
|
|
{
|
|
get => _statusMessage;
|
|
set => SetProperty(ref _statusMessage, value);
|
|
}
|
|
|
|
public ObservableCollection<object> NavigationTree { get; set; }
|
|
|
|
// 导航命令
|
|
public DelegateCommand NavigateHomeCommand { get; set; }
|
|
|
|
public DelegateCommand NavigateInspectCommand { get; set; }
|
|
public DelegateCommand OpenFileCommand { get; set; }
|
|
public DelegateCommand ExportCommand { get; set; }
|
|
public DelegateCommand ClearCommand { get; set; }
|
|
public DelegateCommand EditPropertiesCommand { get; set; }
|
|
|
|
// 窗口打开命令
|
|
public DelegateCommand OpenImageProcessingCommand { get; }
|
|
|
|
public DelegateCommand LoadImageCommand { get; }
|
|
public DelegateCommand OpenPipelineEditorCommand { get; }
|
|
public DelegateCommand OpenCncEditorCommand { get; }
|
|
public DelegateCommand OpenMatrixEditorCommand { get; }
|
|
public DelegateCommand OpenToolboxCommand { get; }
|
|
public DelegateCommand OpenLibraryVersionsCommand { get; }
|
|
public DelegateCommand OpenUserManualCommand { get; }
|
|
public DelegateCommand OpenSettingsCommand { get; }
|
|
public DelegateCommand BrowseDataRootPathCommand { get; }
|
|
public DelegateCommand ResetDataRootPathCommand { get; }
|
|
public DelegateCommand SaveDataRootPathCommand { get; }
|
|
public DelegateCommand NewCncProgramCommand { get; }
|
|
public DelegateCommand SaveCncProgramCommand { get; }
|
|
public DelegateCommand LoadCncProgramCommand { get; }
|
|
public DelegateCommand InsertReferencePointCommand { get; }
|
|
public DelegateCommand InsertSavePositionCommand { get; }
|
|
public DelegateCommand InsertCompleteProgramCommand { get; }
|
|
public DelegateCommand InsertInspectionMarkerCommand { get; }
|
|
public DelegateCommand InsertInspectionModuleCommand { get; }
|
|
public DelegateCommand InsertBuiltInInspectionModuleCommand { get; }
|
|
public DelegateCommand InsertSaveNodeCommand { get; }
|
|
public DelegateCommand InsertPauseDialogCommand { get; }
|
|
public DelegateCommand InsertWaitDelayCommand { get; }
|
|
public DelegateCommand RunCncCommand { get; }
|
|
public DelegateCommand StopCncCommand { get; }
|
|
|
|
//导航相机
|
|
|
|
public DelegateCommand OpenCameraSettingsCommand { get; }
|
|
public DelegateCommand OpenCameraChessboardCalibrationCommand { get; }
|
|
public DelegateCommand OpenCameraCalibrationCommand { get; }
|
|
|
|
// 硬件命令
|
|
public DelegateCommand AxisResetCommand { get; }
|
|
|
|
public DelegateCommand OpenDoorCommand { get; }
|
|
public DelegateCommand CloseDoorCommand { get; }
|
|
public DelegateCommand OpenDetectorConfigCommand { get; }
|
|
public DelegateCommand OpenMotionDebugCommand { get; }
|
|
public DelegateCommand OpenPlcAddrConfigCommand { get; }
|
|
public DelegateCommand OpenRaySourceConfigCommand { get; }
|
|
public DelegateCommand WarmUpCommand { get; }
|
|
|
|
// 测量命令
|
|
public DelegateCommand PointDistanceMeasureCommand { get; }
|
|
|
|
public DelegateCommand PointLineDistanceMeasureCommand { get; }
|
|
public DelegateCommand AngleMeasureCommand { get; }
|
|
public DelegateCommand ThroughHoleFillRateMeasureCommand { get; }
|
|
public DelegateCommand BgaDetectionCommand { get; }
|
|
public DelegateCommand VoidDetectionCommand { get; }
|
|
public DelegateCommand BubbleMeasureCommand { get; }
|
|
|
|
private bool _isScaleBarVisible;
|
|
public bool IsScaleBarVisible
|
|
{
|
|
get => _isScaleBarVisible;
|
|
set => SetProperty(ref _isScaleBarVisible, value);
|
|
}
|
|
|
|
// 辅助线命令
|
|
public DelegateCommand ToggleCrosshairCommand { get; }
|
|
|
|
// 图像处理命令
|
|
public DelegateCommand WhiteBackgroundDetectionCommand { get; }
|
|
|
|
public DelegateCommand BlackBackgroundDetectionCommand { get; }
|
|
public DelegateCommand GrayscaleCommand { get; }
|
|
public DelegateCommand SharpenCommand { get; }
|
|
public DelegateCommand EnhanceCommand { get; }
|
|
|
|
// 设置命令
|
|
public DelegateCommand OpenLanguageSwitcherCommand { get; }
|
|
|
|
public DelegateCommand OpenRealTimeLogViewerCommand { get; }
|
|
public DelegateCommand UseLiveDetectorSourceCommand { get; }
|
|
|
|
public bool IsMainViewportRealtimeEnabled
|
|
{
|
|
get => _mainViewportService.IsRealtimeDisplayEnabled;
|
|
set
|
|
{
|
|
if (_mainViewportService.IsRealtimeDisplayEnabled == value)
|
|
return;
|
|
|
|
_mainViewportService.SetRealtimeDisplayEnabled(value);
|
|
RaisePropertyChanged();
|
|
RaisePropertyChanged(nameof(IsMeasurementToolsEnabled));
|
|
}
|
|
}
|
|
|
|
/// <summary>测量工具是否可用(实时模式关闭时启用)</summary>
|
|
public bool IsMeasurementToolsEnabled => !IsMainViewportRealtimeEnabled;
|
|
|
|
public bool IsUsingLiveDetectorSource => _mainViewportService.CurrentSourceMode == MainViewportSourceMode.LiveDetector;
|
|
|
|
public string DataRootPath
|
|
{
|
|
get => _dataRootPath;
|
|
set => SetProperty(ref _dataRootPath, value);
|
|
}
|
|
|
|
public string PlanRootPath => _xpDataPathService.PlanPath;
|
|
|
|
public string ToolsRootPath => _xpDataPathService.ToolsPath;
|
|
|
|
public string ResultsRootPath => _xpDataPathService.DataPath;
|
|
|
|
public string ReportRootPath => _xpDataPathService.ReportPath;
|
|
|
|
public ObservableCollection<BuiltInInspectionModuleItem> BuiltInInspectionModules { get; } = new();
|
|
|
|
public BuiltInInspectionModuleItem SelectedBuiltInInspectionModule
|
|
{
|
|
get => _selectedBuiltInInspectionModule;
|
|
set
|
|
{
|
|
if (SetProperty(ref _selectedBuiltInInspectionModule, value))
|
|
{
|
|
InsertBuiltInInspectionModuleCommand?.RaiseCanExecuteChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>右侧图像区域内容 | Right-side image panel content</summary>
|
|
public object ImagePanelContent
|
|
{
|
|
get => _imagePanelContent;
|
|
set => SetProperty(ref _imagePanelContent, value);
|
|
}
|
|
|
|
/// <summary>右侧图像区域宽度 | Right-side image panel width</summary>
|
|
public GridLength ImagePanelWidth
|
|
{
|
|
get => _imagePanelWidth;
|
|
set => SetProperty(ref _imagePanelWidth, value);
|
|
}
|
|
|
|
/// <summary>主视图区宽度 | Main viewport width</summary>
|
|
public GridLength ViewportPanelWidth
|
|
{
|
|
get => _viewportPanelWidth;
|
|
set => SetProperty(ref _viewportPanelWidth, value);
|
|
}
|
|
|
|
// 窗口引用(单例窗口防止重复打开)
|
|
private Window _motionDebugWindow;
|
|
|
|
private Window _detectorConfigWindow;
|
|
private Window _plcAddrConfigWindow;
|
|
private Window _realTimeLogViewerWindow;
|
|
private Window _settingsWindow;
|
|
private Window _toolboxWindow;
|
|
private Window _raySourceConfigWindow;
|
|
private object _imagePanelContent;
|
|
private GridLength _viewportPanelWidth = new(1, GridUnitType.Star);
|
|
private GridLength _imagePanelWidth = new(320);
|
|
private bool _isCncEditorMode;
|
|
private string _licenseInfo = "当前时间";
|
|
|
|
private string _dataRootPath = string.Empty;
|
|
private BuiltInInspectionModuleItem _selectedBuiltInInspectionModule;
|
|
|
|
public MainViewModel(
|
|
ILoggerService logger,
|
|
IContainerProvider containerProvider,
|
|
IEventAggregator eventAggregator,
|
|
IMainViewportService mainViewportService,
|
|
IXpDataPathService xpDataPathService)
|
|
{
|
|
_logger = logger?.ForModule<MainViewModel>() ?? throw new ArgumentNullException(nameof(logger));
|
|
_containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
|
|
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
|
_mainViewportService = mainViewportService ?? throw new ArgumentNullException(nameof(mainViewportService));
|
|
_xpDataPathService = xpDataPathService ?? throw new ArgumentNullException(nameof(xpDataPathService));
|
|
_cncEditorViewModel = _containerProvider.Resolve<CncEditorViewModel>();
|
|
_cncPageView = new CncPageView { DataContext = _cncEditorViewModel };
|
|
|
|
_mainViewportService.StateChanged += OnMainViewportStateChanged;
|
|
_cncEditorViewModel.PropertyChanged += (s, e) =>
|
|
{
|
|
if (e.PropertyName == nameof(CncEditorViewModel.StatusMessage))
|
|
RaisePropertyChanged(nameof(CncStatusMessage));
|
|
else if (e.PropertyName == nameof(CncEditorViewModel.HasExecutionError))
|
|
RaisePropertyChanged(nameof(CncHasExecutionError));
|
|
else if (e.PropertyName == nameof(CncEditorViewModel.IsRunning))
|
|
{
|
|
RunCncCommand.RaiseCanExecuteChanged();
|
|
StopCncCommand.RaiseCanExecuteChanged();
|
|
}
|
|
else if (e.PropertyName == nameof(CncEditorViewModel.SelectedNode))
|
|
{
|
|
var node = _cncEditorViewModel.SelectedNode;
|
|
if (node?.ResultImage != null)
|
|
{
|
|
_logger.Info("[Image] Switched to node [{Name}], showing cached result image.", node.Name);
|
|
_mainViewportService.SetManualImage(node.ResultImage, $"CNC node: {node.Name}");
|
|
}
|
|
}
|
|
};
|
|
_cncEditorViewModel.RunCncCommand.CanExecuteChanged += (s, e) => RunCncCommand.RaiseCanExecuteChanged();
|
|
_cncEditorViewModel.StopCncCommand.CanExecuteChanged += (s, e) => StopCncCommand.RaiseCanExecuteChanged();
|
|
|
|
_eventAggregator.GetEvent<PipelinePreviewUpdatedEvent>()
|
|
.Subscribe(OnPipelinePreviewUpdated, ThreadOption.UIThread);
|
|
|
|
NavigationTree = new ObservableCollection<object>();
|
|
|
|
NavigateHomeCommand = new DelegateCommand(OnNavigateHome);
|
|
NavigateInspectCommand = new DelegateCommand(OnNavigateInspect);
|
|
OpenFileCommand = new DelegateCommand(OnOpenFile);
|
|
ExportCommand = new DelegateCommand(OnExport);
|
|
ClearCommand = new DelegateCommand(OnClear);
|
|
EditPropertiesCommand = new DelegateCommand(OnEditProperties);
|
|
|
|
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
|
|
|
|
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
|
|
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编辑"));
|
|
OpenToolboxCommand = new DelegateCommand(ExecuteOpenToolbox);
|
|
OpenLibraryVersionsCommand = new DelegateCommand(() => ShowWindow(new Views.LibraryVersionsWindow(), "关于"));
|
|
OpenUserManualCommand = new DelegateCommand(ExecuteOpenUserManual);
|
|
OpenCameraSettingsCommand = new DelegateCommand(ExecuteOpenCameraSettings);
|
|
OpenCameraChessboardCalibrationCommand = new DelegateCommand(ExecuteOpenCameraChessboardCalibration);
|
|
OpenCameraCalibrationCommand = new DelegateCommand(ExecuteOpenCameraCalibration);
|
|
|
|
OpenSettingsCommand = new DelegateCommand(ExecuteOpenSettings);
|
|
BrowseDataRootPathCommand = new DelegateCommand(ExecuteBrowseDataRootPath);
|
|
ResetDataRootPathCommand = new DelegateCommand(ExecuteResetDataRootPath);
|
|
SaveDataRootPathCommand = new DelegateCommand(ExecuteSaveDataRootPath);
|
|
NewCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.NewProgramCommand.Execute()));
|
|
SaveCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.SaveProgramCommand.Execute()));
|
|
LoadCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.LoadProgramCommand.Execute()));
|
|
InsertReferencePointCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertReferencePointCommand.Execute()));
|
|
InsertSavePositionCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertSavePositionCommand.Execute()));
|
|
InsertCompleteProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertCompleteProgramCommand.Execute()));
|
|
InsertInspectionMarkerCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionMarkerCommand.Execute()));
|
|
InsertInspectionModuleCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionModuleCommand.Execute()));
|
|
InsertBuiltInInspectionModuleCommand = new DelegateCommand(
|
|
async () => await ExecuteInsertBuiltInInspectionModuleAsync(),
|
|
CanExecuteInsertBuiltInInspectionModule);
|
|
InsertSaveNodeCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertSaveNodeCommand.Execute()));
|
|
InsertPauseDialogCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertPauseDialogCommand.Execute()));
|
|
InsertWaitDelayCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertWaitDelayCommand.Execute()));
|
|
RunCncCommand = new DelegateCommand(
|
|
() => ExecuteCncEditorAction(vm => vm.RunCncCommand.Execute()),
|
|
() => _cncEditorViewModel.RunCncCommand.CanExecute());
|
|
StopCncCommand = new DelegateCommand(
|
|
() => ExecuteCncEditorAction(vm => vm.StopCncCommand.Execute()),
|
|
() => _cncEditorViewModel.StopCncCommand.CanExecute());
|
|
|
|
PointDistanceMeasureCommand = new DelegateCommand(ExecutePointDistanceMeasure);
|
|
PointLineDistanceMeasureCommand = new DelegateCommand(ExecutePointLineDistanceMeasure);
|
|
AngleMeasureCommand = new DelegateCommand(ExecuteAngleMeasure);
|
|
ThroughHoleFillRateMeasureCommand = new DelegateCommand(ExecuteThroughHoleFillRateMeasure);
|
|
BgaDetectionCommand = new DelegateCommand(ExecuteBgaDetection);
|
|
VoidDetectionCommand = new DelegateCommand(ExecuteVoidDetection);
|
|
BubbleMeasureCommand = new DelegateCommand(ExecuteBubbleMeasure);
|
|
|
|
// 辅助线命令
|
|
ToggleCrosshairCommand = new DelegateCommand(() =>
|
|
_eventAggregator.GetEvent<ToggleCrosshairEvent>().Publish());
|
|
|
|
// 图像处理命令
|
|
WhiteBackgroundDetectionCommand = new DelegateCommand(ExecuteWhiteBackgroundDetection);
|
|
BlackBackgroundDetectionCommand = new DelegateCommand(ExecuteBlackBackgroundDetection);
|
|
GrayscaleCommand = new DelegateCommand(ExecuteGrayscale);
|
|
SharpenCommand = new DelegateCommand(ExecuteSharpen);
|
|
EnhanceCommand = new DelegateCommand(ExecuteEnhance);
|
|
|
|
AxisResetCommand = new DelegateCommand(ExecuteAxisReset);
|
|
OpenDoorCommand = new DelegateCommand(ExecuteOpenDoor);
|
|
CloseDoorCommand = new DelegateCommand(ExecuteCloseDoor);
|
|
OpenDetectorConfigCommand = new DelegateCommand(ExecuteOpenDetectorConfig);
|
|
OpenMotionDebugCommand = new DelegateCommand(ExecuteOpenMotionDebug);
|
|
OpenPlcAddrConfigCommand = new DelegateCommand(ExecuteOpenPlcAddrConfig);
|
|
OpenRaySourceConfigCommand = new DelegateCommand(ExecuteOpenRaySourceConfig);
|
|
WarmUpCommand = new DelegateCommand(ExecuteWarmUp);
|
|
|
|
OpenLanguageSwitcherCommand = new DelegateCommand(ExecuteOpenLanguageSwitcher);
|
|
OpenRealTimeLogViewerCommand = new DelegateCommand(ExecuteOpenRealTimeLogViewer);
|
|
UseLiveDetectorSourceCommand = new DelegateCommand(ExecuteUseLiveDetectorSource);
|
|
|
|
ImagePanelContent = new PipelineEditorView();
|
|
ViewportPanelWidth = new GridLength(1, GridUnitType.Star);
|
|
ImagePanelWidth = new GridLength(320);
|
|
DataRootPath = _xpDataPathService.RootPath;
|
|
LoadBuiltInInspectionModules();
|
|
|
|
_logger.Info("MainViewModel 已初始化");
|
|
}
|
|
|
|
public string CncStatusMessage => _cncEditorViewModel.StatusMessage;
|
|
public bool CncHasExecutionError => _cncEditorViewModel.HasExecutionError;
|
|
|
|
private string _cursorInfoText = "X: -- Y: -- Gray: --";
|
|
|
|
public string CursorInfoText
|
|
{
|
|
get => _cursorInfoText;
|
|
set => SetProperty(ref _cursorInfoText, value);
|
|
}
|
|
|
|
private void ShowWindow(Window window, string name)
|
|
{
|
|
window.Owner = Application.Current.MainWindow;
|
|
window.Show();
|
|
_logger.Info("{Name} 窗口已打开", name);
|
|
}
|
|
|
|
private void ShowOrActivate(Window currentWindow, Action<Window> setWindow, Func<Window> factory, string name)
|
|
{
|
|
if (currentWindow != null && currentWindow.IsLoaded)
|
|
{
|
|
currentWindow.Activate();
|
|
return;
|
|
}
|
|
|
|
var window = factory();
|
|
window.Owner = Application.Current.MainWindow;
|
|
window.ShowInTaskbar = true;
|
|
window.Closed += (s, e) => setWindow(null);
|
|
window.Show();
|
|
setWindow(window);
|
|
_logger.Info("{Name} 窗口已打开", name);
|
|
}
|
|
|
|
private void ExecuteOpenToolbox()
|
|
{
|
|
ShowOrActivate(_toolboxWindow, w => _toolboxWindow = w, () => new Views.OperatorToolboxWindow(), "Operator Toolbox");
|
|
}
|
|
|
|
private void ExecuteOpenCncEditor()
|
|
{
|
|
if (_isCncEditorMode)
|
|
{
|
|
ImagePanelContent = new PipelineEditorView();
|
|
ViewportPanelWidth = new GridLength(1, GridUnitType.Star);
|
|
ImagePanelWidth = new GridLength(320);
|
|
_isCncEditorMode = false;
|
|
_logger.Info("已退出 CNC 编辑模式");
|
|
return;
|
|
}
|
|
|
|
ShowCncEditor();
|
|
}
|
|
|
|
private void ExecuteCncEditorAction(Action<CncEditorViewModel> action)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(action);
|
|
ShowCncEditor();
|
|
action(_cncEditorViewModel);
|
|
}
|
|
|
|
private void ShowCncEditor()
|
|
{
|
|
ImagePanelContent = _cncPageView;
|
|
ViewportPanelWidth = new GridLength(1, GridUnitType.Star);
|
|
ImagePanelWidth = new GridLength(CncEditorHostWidth);
|
|
_isCncEditorMode = true;
|
|
_logger.Info("CNC 编辑器已切换到主界面图像区域");
|
|
}
|
|
|
|
private void ExecuteOpenUserManual()
|
|
{
|
|
try
|
|
{
|
|
var manualPath = ConfigurationManager.AppSettings["UserManual"];
|
|
if (string.IsNullOrEmpty(manualPath))
|
|
{
|
|
_logger.Warn("User manual path is not configured.");
|
|
MessageBox.Show("User manual path is not configured. Please check the UserManual setting in App.config.",
|
|
"Info", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
return;
|
|
}
|
|
|
|
if (!File.Exists(manualPath))
|
|
{
|
|
_logger.Warn("User manual file not found: {Path}", manualPath);
|
|
MessageBox.Show($"User manual file not found:\n{manualPath}",
|
|
"Info", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
return;
|
|
}
|
|
|
|
var pdfViewerService = _containerProvider.Resolve<IPdfViewerService>();
|
|
var stream = File.OpenRead(manualPath);
|
|
var fileName = Path.GetFileName(manualPath);
|
|
pdfViewerService.OpenViewer(stream, fileName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to open user manual.");
|
|
MessageBox.Show($"Failed to open user manual: {ex.Message}",
|
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteOpenCameraSettings()
|
|
{
|
|
try
|
|
{
|
|
var vm = _containerProvider.Resolve<NavigationPropertyPanelViewModel>();
|
|
if (!vm.IsCameraConnected)
|
|
{
|
|
MessageBox.Show("Please connect the camera first", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
return;
|
|
}
|
|
|
|
var window = new Views.CameraSettingsWindow(vm) { Owner = Application.Current.MainWindow };
|
|
window.Show();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to open camera settings.");
|
|
}
|
|
}
|
|
|
|
private void ExecuteOpenCameraChessboardCalibration()
|
|
{
|
|
var chessboardWindow = new System.Windows.Window
|
|
{
|
|
Title = XP.Common.Resources.Resources.ChessboardToolTitle,
|
|
Width = 1600,
|
|
Height = 900,
|
|
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen,
|
|
Icon = System.Windows.Application.Current.MainWindow?.Icon
|
|
};
|
|
|
|
var calibrationDialogService = new XP.Camera.Calibration.DefaultCalibrationDialogService();
|
|
var chessboardViewModel = new XP.Camera.Calibration.ViewModels.ChessboardCalibrationViewModel(calibrationDialogService);
|
|
var chessboardControl = new XP.Camera.Calibration.Controls.ChessboardCalibrationControl
|
|
{
|
|
DataContext = chessboardViewModel
|
|
};
|
|
|
|
chessboardWindow.Content = chessboardControl;
|
|
chessboardWindow.ShowDialog();
|
|
}
|
|
|
|
private void ExecuteOpenCameraCalibration()
|
|
{
|
|
var calibrationWindow = new System.Windows.Window
|
|
{
|
|
Title = XP.Common.Resources.Resources.CalibrationToolTitle,
|
|
Width = 1400,
|
|
Height = 850,
|
|
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen,
|
|
Icon = System.Windows.Application.Current.MainWindow?.Icon
|
|
};
|
|
|
|
var calibrationDialogService = new XP.Camera.Calibration.DefaultCalibrationDialogService();
|
|
var calibrationViewModel = new XP.Camera.Calibration.ViewModels.CalibrationViewModel(calibrationDialogService);
|
|
var calibrationControl = new XP.Camera.Calibration.Controls.CalibrationControl
|
|
{
|
|
DataContext = calibrationViewModel
|
|
};
|
|
|
|
calibrationWindow.Content = calibrationControl;
|
|
calibrationWindow.ShowDialog();
|
|
}
|
|
|
|
private void ExecuteOpenSettings()
|
|
{
|
|
try
|
|
{
|
|
ShowOrActivate(_settingsWindow, w => _settingsWindow = w,
|
|
() => new Views.SettingsWindow(this), "Settings");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to open settings window");
|
|
MessageBox.Show($"Failed to open settings window: {ex.Message}",
|
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteBrowseDataRootPath()
|
|
{
|
|
try
|
|
{
|
|
var dialog = new OpenFolderDialog
|
|
{
|
|
Title = "选择 XP 数据根目录",
|
|
InitialDirectory = Directory.Exists(DataRootPath) ? DataRootPath : _xpDataPathService.RootPath
|
|
};
|
|
|
|
if (dialog.ShowDialog() == true)
|
|
{
|
|
DataRootPath = dialog.FolderName;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to browse XP data root.");
|
|
MessageBox.Show($"Failed to browse data root: {ex.Message}",
|
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteResetDataRootPath()
|
|
{
|
|
DataRootPath = _xpDataPathService.DefaultRootPath;
|
|
}
|
|
|
|
private void ExecuteSaveDataRootPath()
|
|
{
|
|
try
|
|
{
|
|
_xpDataPathService.SaveRootPath(DataRootPath);
|
|
DataRootPath = _xpDataPathService.RootPath;
|
|
RaisePropertyChanged(nameof(PlanRootPath));
|
|
RaisePropertyChanged(nameof(ToolsRootPath));
|
|
RaisePropertyChanged(nameof(ResultsRootPath));
|
|
RaisePropertyChanged(nameof(ReportRootPath));
|
|
LoadBuiltInInspectionModules();
|
|
|
|
MessageBox.Show("XP data root saved. New save/load dialogs will use the new path immediately.",
|
|
"Info", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to save XP data root.");
|
|
MessageBox.Show($"Failed to save data root: {ex.Message}",
|
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private bool CanExecuteInsertBuiltInInspectionModule()
|
|
{
|
|
return SelectedBuiltInInspectionModule != null;
|
|
}
|
|
|
|
private async Task ExecuteInsertBuiltInInspectionModuleAsync()
|
|
{
|
|
var module = SelectedBuiltInInspectionModule;
|
|
if (module == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
ShowCncEditor();
|
|
await _cncEditorViewModel.InsertInspectionModuleFromPipelineFileAsync(module.FilePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to insert built-in inspection module: {FilePath}", module.FilePath);
|
|
MessageBox.Show($"Failed to insert built-in inspection module: {ex.Message}",
|
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void LoadBuiltInInspectionModules()
|
|
{
|
|
BuiltInInspectionModules.Clear();
|
|
|
|
try
|
|
{
|
|
var toolsPath = _xpDataPathService.ToolsPath;
|
|
if (!Directory.Exists(toolsPath))
|
|
{
|
|
SelectedBuiltInInspectionModule = null;
|
|
return;
|
|
}
|
|
|
|
var files = Directory
|
|
.EnumerateFiles(toolsPath, "*.xpm", SearchOption.AllDirectories)
|
|
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
|
|
.Select(path => new BuiltInInspectionModuleItem(
|
|
GetBuiltInModuleDisplayName(toolsPath, path),
|
|
path))
|
|
.ToList();
|
|
|
|
foreach (var file in files)
|
|
{
|
|
BuiltInInspectionModules.Add(file);
|
|
}
|
|
|
|
SelectedBuiltInInspectionModule = BuiltInInspectionModules.FirstOrDefault();
|
|
_logger.Info("Loaded {Count} built-in inspection modules from {ToolsPath}", BuiltInInspectionModules.Count, toolsPath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
SelectedBuiltInInspectionModule = null;
|
|
_logger.Error(ex, "Failed to load built-in inspection modules.");
|
|
}
|
|
}
|
|
|
|
private static string GetBuiltInModuleDisplayName(string toolsPath, string filePath)
|
|
{
|
|
var relativePath = Path.GetRelativePath(toolsPath, filePath);
|
|
var withoutExtension = Path.ChangeExtension(relativePath, null) ?? relativePath;
|
|
return withoutExtension.Replace(Path.DirectorySeparatorChar, '/');
|
|
}
|
|
|
|
private void ExecuteAxisReset()
|
|
{
|
|
var result = MessageBox.Show("Confirm axis reset?", "Axis Reset",
|
|
MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
|
if (result != MessageBoxResult.OK)
|
|
return;
|
|
|
|
try
|
|
{
|
|
var motionSystem = _containerProvider.Resolve<IMotionSystem>();
|
|
var resetResult = motionSystem.AxisReset.Reset();
|
|
if (!resetResult.Success)
|
|
{
|
|
MessageBox.Show($"Axis reset failed: {resetResult.ErrorMessage}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Axis reset failed.");
|
|
MessageBox.Show($"Axis reset error: {ex.Message}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteOpenDoor()
|
|
{
|
|
try
|
|
{
|
|
var motionService = _containerProvider.Resolve<IMotionControlService>();
|
|
var result = motionService.OpenDoor();
|
|
if (!result.Success)
|
|
{
|
|
MessageBox.Show($"Open door failed: {result.ErrorMessage}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Open door failed.");
|
|
MessageBox.Show($"Open door error: {ex.Message}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteCloseDoor()
|
|
{
|
|
try
|
|
{
|
|
var motionService = _containerProvider.Resolve<IMotionControlService>();
|
|
var result = motionService.CloseDoor();
|
|
if (!result.Success)
|
|
{
|
|
MessageBox.Show($"Close door failed: {result.ErrorMessage}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Close door failed.");
|
|
MessageBox.Show($"Close door error: {ex.Message}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteOpenDetectorConfig()
|
|
{
|
|
try
|
|
{
|
|
ShowOrActivate(_detectorConfigWindow, w => _detectorConfigWindow = w,
|
|
() => new XP.Hardware.Detector.Views.DetectorConfigWindow(), "Detector Config");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to open detector config window.");
|
|
MessageBox.Show($"Failed to open detector config window:\n{ex.InnerException?.Message ?? ex.Message}",
|
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteOpenMotionDebug()
|
|
{
|
|
ShowOrActivate(_motionDebugWindow, w => _motionDebugWindow = w,
|
|
() => new XP.Hardware.MotionControl.Views.MotionDebugWindow(), "运动调试");
|
|
}
|
|
|
|
private void ExecuteOpenPlcAddrConfig()
|
|
{
|
|
ShowOrActivate(_plcAddrConfigWindow, w => _plcAddrConfigWindow = w,
|
|
() => _containerProvider.Resolve<XP.Hardware.PLC.Views.PlcAddrConfigEditorWindow>(), "PLC 地址配置");
|
|
}
|
|
|
|
private void ExecuteOpenRaySourceConfig()
|
|
{
|
|
ShowOrActivate(_raySourceConfigWindow, w => _raySourceConfigWindow = w,
|
|
() => new XP.Hardware.RaySource.Views.RaySourceConfigWindow(), "Ray Source Config");
|
|
}
|
|
|
|
private void ExecuteLoadImage()
|
|
{
|
|
var dialog = new OpenFileDialog
|
|
{
|
|
Title = "加载图像",
|
|
Filter = "图像文件|*.bmp;*.png;*.jpg;*.jpeg;*.tif;*.tiff|所有文件|*.*"
|
|
};
|
|
|
|
if (dialog.ShowDialog() != true)
|
|
return;
|
|
|
|
try
|
|
{
|
|
var bitmap = new BitmapImage();
|
|
bitmap.BeginInit();
|
|
bitmap.UriSource = new Uri(dialog.FileName, UriKind.Absolute);
|
|
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
|
bitmap.EndInit();
|
|
bitmap.Freeze();
|
|
|
|
_logger.Info("[Image] ExecuteLoadImage loaded image {Path} and will push it to MainViewportService and ManualImageLoadedEvent.", dialog.FileName);
|
|
_mainViewportService.SetManualImage(bitmap, dialog.FileName);
|
|
|
|
// Publish the image to the pipeline editor at the same time.
|
|
_eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
|
.Publish(new ManualImageLoadedPayload(bitmap, dialog.FileName));
|
|
_logger.Info("[Image] ManualImageLoadedEvent published.");
|
|
|
|
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to load image: {Path}", dialog.FileName);
|
|
MessageBox.Show($"Failed to load image: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteUseLiveDetectorSource()
|
|
{
|
|
_mainViewportService.SetSourceMode(MainViewportSourceMode.LiveDetector);
|
|
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
|
}
|
|
|
|
private void ExecuteWarmUp()
|
|
{
|
|
var messageBoxResult = MessageBox.Show("Confirm X-ray source warm-up?", "Warm-up",
|
|
MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
|
if (messageBoxResult != MessageBoxResult.OK)
|
|
return;
|
|
|
|
try
|
|
{
|
|
var raySourceService = _containerProvider.Resolve<XP.Hardware.RaySource.Services.IRaySourceService>();
|
|
var result = raySourceService.WarmUp();
|
|
if (!result.Success)
|
|
{
|
|
MessageBox.Show($"Warm-up failed: {result.ErrorMessage}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
}
|
|
else
|
|
{
|
|
_logger.Info("Warm-up command sent.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Warm-up failed.");
|
|
MessageBox.Show($"Warm-up error: {ex.Message}", "Error",
|
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
#region 测量命令实现
|
|
|
|
private bool CheckImageLoaded()
|
|
{
|
|
try
|
|
{
|
|
var viewportVm = _containerProvider.Resolve<ViewportPanelViewModel>();
|
|
if (viewportVm?.ImageSource != null) return true;
|
|
}
|
|
catch { }
|
|
HexMessageBox.Show("Please load an image first!", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
return false;
|
|
}
|
|
|
|
private void ExecutePointDistanceMeasure()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Point distance measurement triggered.");
|
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.PointDistance);
|
|
}
|
|
|
|
private void ExecutePointLineDistanceMeasure()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Point-line distance measurement triggered.");
|
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.PointLineDistance);
|
|
}
|
|
|
|
private void ExecuteAngleMeasure()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Angle measurement triggered.");
|
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.Angle);
|
|
}
|
|
|
|
private void ExecuteThroughHoleFillRateMeasure()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Through-hole fill-rate measurement triggered.");
|
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.ThroughHoleFillRate);
|
|
}
|
|
|
|
private Window _bgaDetectionPanel;
|
|
|
|
private void ExecuteBgaDetection()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("BGA检测功能已触发");
|
|
|
|
if (_bgaDetectionPanel != null && _bgaDetectionPanel.IsVisible)
|
|
{
|
|
_bgaDetectionPanel.Activate();
|
|
return;
|
|
}
|
|
|
|
_bgaDetectionPanel = new Views.ImageProcessing.BgaDetectionPanel
|
|
{
|
|
Owner = System.Windows.Application.Current.MainWindow
|
|
};
|
|
_bgaDetectionPanel.Show();
|
|
}
|
|
|
|
private Window _voidDetectionPanel;
|
|
|
|
private void ExecuteVoidDetection()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("空隙检测功能已触发");
|
|
|
|
if (_voidDetectionPanel != null && _voidDetectionPanel.IsVisible)
|
|
{
|
|
_voidDetectionPanel.Activate();
|
|
return;
|
|
}
|
|
|
|
_voidDetectionPanel = new Views.ImageProcessing.VoidDetectionPanel
|
|
{
|
|
Owner = System.Windows.Application.Current.MainWindow
|
|
};
|
|
_voidDetectionPanel.Show();
|
|
}
|
|
|
|
private Window _bubbleMeasurePanel;
|
|
|
|
private void ExecuteBubbleMeasure()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Bubble measurement triggered.");
|
|
|
|
// Enter bubble measurement mode.
|
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.BubbleMeasure);
|
|
|
|
// Open the tool panel.
|
|
if (_bubbleMeasurePanel != null && _bubbleMeasurePanel.IsVisible)
|
|
{
|
|
_bubbleMeasurePanel.Activate();
|
|
return;
|
|
}
|
|
|
|
_bubbleMeasurePanel = new Views.ImageProcessing.BubbleMeasurePanel
|
|
{
|
|
Owner = System.Windows.Application.Current.MainWindow
|
|
};
|
|
_bubbleMeasurePanel.Closed += (s, e) =>
|
|
{
|
|
// Exit bubble measurement mode when the panel closes.
|
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.None);
|
|
};
|
|
_bubbleMeasurePanel.Show();
|
|
}
|
|
|
|
private void ExecuteWhiteBackgroundDetection()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("White background detection triggered.");
|
|
// TODO: 实现白底检测逻辑
|
|
}
|
|
|
|
private void ExecuteBlackBackgroundDetection()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Black background detection triggered.");
|
|
// TODO: 实现黑底检测逻辑
|
|
}
|
|
|
|
private void ExecuteGrayscale()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Line profile toggled.");
|
|
_eventAggregator.GetEvent<ToggleLineProfileEvent>().Publish();
|
|
}
|
|
|
|
private void ExecuteSharpen()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Sharpen triggered.");
|
|
|
|
try
|
|
{
|
|
var viewportVm = _containerProvider.Resolve<ViewportPanelViewModel>();
|
|
var imageSource = viewportVm?.ImageSource as BitmapSource;
|
|
if (imageSource == null) return;
|
|
|
|
var inputImage = BitmapSourceToImage(imageSource);
|
|
if (inputImage == null) return;
|
|
|
|
var processor = new XP.ImageProcessing.Processors.SharpenProcessor();
|
|
processor.SetParameter("Method", "UnsharpMask");
|
|
processor.SetParameter("Strength", 0.2);
|
|
processor.SetParameter("KernelSize", 3);
|
|
|
|
var result = processor.Process(inputImage);
|
|
var resultBitmap = ImageToBitmapSource(result);
|
|
|
|
_mainViewportService.SetManualImage(resultBitmap, "Sharpen");
|
|
|
|
inputImage.Dispose();
|
|
result.Dispose();
|
|
|
|
_logger.Info("Sharpen completed.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Sharpen failed.");
|
|
HexMessageBox.Show($"Sharpen failed: {ex.Message}", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private void ExecuteEnhance()
|
|
{
|
|
if (!CheckImageLoaded()) return;
|
|
_logger.Info("Enhance triggered.");
|
|
|
|
try
|
|
{
|
|
var viewportVm = _containerProvider.Resolve<ViewportPanelViewModel>();
|
|
var imageSource = viewportVm?.ImageSource as BitmapSource;
|
|
if (imageSource == null) return;
|
|
|
|
var inputImage = BitmapSourceToImage(imageSource);
|
|
if (inputImage == null) return;
|
|
|
|
var processor = new XP.ImageProcessing.Processors.HistogramEqualizationProcessor();
|
|
|
|
var result = processor.Process(inputImage);
|
|
var resultBitmap = ImageToBitmapSource(result);
|
|
|
|
_mainViewportService.SetManualImage(resultBitmap, "Enhance");
|
|
|
|
inputImage.Dispose();
|
|
result.Dispose();
|
|
|
|
_logger.Info("Enhance completed.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Enhance failed.");
|
|
HexMessageBox.Show($"Enhance failed: {ex.Message}", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
|
|
private Image<Gray, byte>? BitmapSourceToImage(BitmapSource bitmapSource)
|
|
{
|
|
// 转换为可用的图像格式
|
|
BitmapSource source = bitmapSource;
|
|
|
|
// 如果不是 Gray8 格式,转换为 Gray8
|
|
if (bitmapSource.Format != PixelFormats.Gray8)
|
|
{
|
|
source = new FormatConvertedBitmap(bitmapSource, PixelFormats.Gray8, null, 0);
|
|
}
|
|
|
|
// 获取原始像素数据
|
|
int width = source.PixelWidth;
|
|
int height = source.PixelHeight;
|
|
int stride = width; // Gray8 每个像素 1 字节
|
|
byte[] pixels = new byte[width * height];
|
|
source.CopyPixels(pixels, stride, 0);
|
|
|
|
// 创建 Emgu CV Image
|
|
var image = new Image<Gray, byte>(width, height);
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
image.Data[y, x, 0] = pixels[y * stride + x];
|
|
}
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
private BitmapSource ImageToBitmapSource(Image<Gray, byte> image)
|
|
{
|
|
int width = image.Width;
|
|
int height = image.Height;
|
|
int stride = width;
|
|
byte[] pixels = new byte[width * height];
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
pixels[y * stride + x] = image.Data[y, x, 0];
|
|
}
|
|
}
|
|
|
|
var bitmapSource = BitmapSource.Create(
|
|
width, height, 96, 96,
|
|
PixelFormats.Gray8, null, pixels, stride);
|
|
|
|
bitmapSource.Freeze();
|
|
return bitmapSource;
|
|
}
|
|
|
|
private void ExecuteOpenLanguageSwitcher()
|
|
{
|
|
try
|
|
{
|
|
var viewModel = _containerProvider.Resolve<XP.Common.Localization.ViewModels.LanguageSwitcherViewModel>();
|
|
var window = new XP.Common.Localization.Views.LanguageSwitcherWindow(viewModel)
|
|
{
|
|
Owner = Application.Current.MainWindow,
|
|
ShowInTaskbar = true
|
|
};
|
|
window.ShowDialog();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Failed to open language settings.");
|
|
}
|
|
}
|
|
|
|
private void ExecuteOpenRealTimeLogViewer()
|
|
{
|
|
ShowOrActivate(_realTimeLogViewerWindow, w => _realTimeLogViewerWindow = w,
|
|
() => new XP.Common.GeneralForm.Views.RealTimeLogViewer(), "实时日志");
|
|
}
|
|
|
|
private void OnNavigateHome()
|
|
{
|
|
_logger.Info("Navigated to home.");
|
|
LicenseInfo = "首页";
|
|
}
|
|
|
|
private void OnNavigateInspect()
|
|
{
|
|
_logger.Info("Navigated to inspection page.");
|
|
LicenseInfo = "Inspection";
|
|
}
|
|
|
|
private void OnOpenFile()
|
|
{
|
|
_logger.Info("Open file.");
|
|
LicenseInfo = "打开文件";
|
|
}
|
|
|
|
private void OnExport()
|
|
{
|
|
_logger.Info("Export data.");
|
|
LicenseInfo = "导出数据";
|
|
}
|
|
|
|
private void OnClear()
|
|
{
|
|
_logger.Info("Clear data.");
|
|
LicenseInfo = "清除数据";
|
|
}
|
|
|
|
private void OnEditProperties()
|
|
{
|
|
_logger.Info("Edit properties.");
|
|
LicenseInfo = "Edit properties";
|
|
}
|
|
|
|
private void OnMainViewportStateChanged(object sender, EventArgs e)
|
|
{
|
|
Application.Current?.Dispatcher?.BeginInvoke(new Action(() =>
|
|
{
|
|
RaisePropertyChanged(nameof(IsMainViewportRealtimeEnabled));
|
|
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
|
}));
|
|
}
|
|
|
|
private void OnPipelinePreviewUpdated(PipelinePreviewUpdatedPayload payload)
|
|
{
|
|
if (payload?.Image == null)
|
|
{
|
|
_logger.Warn("[Image] OnPipelinePreviewUpdated skipped because payload or image is null.");
|
|
return;
|
|
}
|
|
_logger.Info("[Image] OnPipelinePreviewUpdated received a pipeline preview image and pushed it to MainViewportService.");
|
|
_mainViewportService.SetManualImage(payload.Image, string.Empty);
|
|
}
|
|
|
|
public sealed record BuiltInInspectionModuleItem(string DisplayName, string FilePath);
|
|
|
|
#endregion 测量命令实现
|
|
}
|
|
} |