Files
XplorePlane/XplorePlane/App.xaml.cs
T

488 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using DryIoc;
using Prism.DryIoc;
using Prism.Ioc;
using Prism.Modularity;
using Serilog;
using System;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows;
using Telerik.Windows.Controls;
using XP.Camera;
using XP.Common.Configs;
using XP.Common.Database.Implementations;
using XP.Common.Database.Interfaces;
using XP.Common.Dump.Configs;
using XP.Common.Dump.Implementations;
using XP.Common.Dump.Interfaces;
using XP.Common.GeneralForm.Views;
using XP.Common.Helpers;
using XP.Common.Localization.Configs;
using XP.Common.Localization.Extensions;
using XP.Common.Localization.Implementations;
using XP.Common.Localization.Interfaces;
using XP.Common.Logging;
using XP.Common.Logging.Implementations;
using XP.Common.Logging.Interfaces;
using XP.Common.Module;
using XP.Hardware.Detector.Module;
using XP.Hardware.Detector.Services;
using XP.Hardware.MotionControl.Module;
using XP.Hardware.Plc.Abstractions;
using XP.Hardware.PLC;
using XP.Hardware.PLC.Abstractions;
using XP.Hardware.PLC.Services;
using XP.Hardware.RaySource.Module;
using XP.Hardware.RaySource.Services;
using XplorePlane.Services;
using XplorePlane.Services.AppState;
using XplorePlane.Services.Camera;
using XplorePlane.Services.Cnc;
using XplorePlane.Services.InspectionResults;
using XplorePlane.Services.MainViewport;
using XplorePlane.Services.Matrix;
using XplorePlane.Services.Measurement;
using XplorePlane.Services.Recipe;
using XplorePlane.Services.Storage;
using XplorePlane.ViewModels;
using XplorePlane.ViewModels.Cnc;
using XplorePlane.Views;
using XplorePlane.Views.Cnc;
namespace XplorePlane
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// 设置 Telerik Windows11 主题,缩小 Ribbon 整体尺寸
StyleManager.ApplicationTheme = new Windows11Theme();
// 强制使用中文 UI,确保 ImageProcessing 库显示中文
var zhCN = new CultureInfo("zh-CN");
Thread.CurrentThread.CurrentCulture = zhCN;
Thread.CurrentThread.CurrentUICulture = zhCN;
CultureInfo.DefaultThreadCurrentCulture = zhCN;
CultureInfo.DefaultThreadCurrentUICulture = zhCN;
// 配置 Serilog 日志系统
ConfigureLogging();
// 捕获未处理的异常
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
DispatcherUnhandledException += OnDispatcherUnhandledException;
try
{
base.OnStartup(e);
// Initialize Prism with DryIoc
var bootstrapper = new AppBootstrapper();
bootstrapper.Run();
}
catch (FileNotFoundException ex)
{
Log.Fatal(ex, "Required DLL not found: {FileName}", ex.FileName);
MessageBox.Show(
$"Required library not found: {ex.FileName}\n\nPlease ensure all required DLLs are present in the Libs/ImageProcessing/ directory.",
"Missing Library",
MessageBoxButton.OK,
MessageBoxImage.Error);
Shutdown(1);
}
catch (TypeLoadException ex)
{
Log.Fatal(ex, "Failed to load type from DLL: {TypeName}", ex.TypeName);
MessageBox.Show(
$"Failed to load required type: {ex.TypeName}\n\nPlease ensure the correct version of DLLs are present in the Libs/ImageProcessing/ directory.",
"Library Load Error",
MessageBoxButton.OK,
MessageBoxImage.Error);
Shutdown(1);
}
}
private void ConfigureLogging()
{
// 加载 Serilog 配置 | Load Serilog configuration
SerilogConfig serilogConfig = ConfigLoader.LoadSerilogConfig();
// 初始化 Serilog(全局唯一)| Initialize Serilog (global singleton)
SerilogInitializer.Initialize(serilogConfig);
// 记录应用启动日志 | Log application startup
Log.Information("========================================");
Log.Information("XplorePlane 应用程序启动");
Log.Information("========================================");
}
protected override void OnExit(ExitEventArgs e)
{
Log.Information("========================================");
Log.Information("XplorePlane 应用程序退出");
Log.Information("========================================");
// 释放全局状态服务资源
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var appStateService = bootstrapper.Container.Resolve<IAppStateService>();
appStateService?.Dispose();
Log.Information("全局状态服务资源已释放");
}
}
catch (Exception ex)
{
Log.Error(ex, "全局状态服务资源释放失败");
}
// 释放射线源资源
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var raySourceService = bootstrapper.Container.Resolve<IRaySourceService>();
raySourceService?.Dispose();
Log.Information("射线源资源已释放");
}
}
catch (Exception ex)
{
Log.Error(ex, "射线源资源释放失败");
}
// 先停止导航相机实时采集,再释放资源,避免回调死锁
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var cameraVm = bootstrapper.Container.Resolve<NavigationPropertyPanelViewModel>();
cameraVm?.Dispose();
Log.Information("导航相机 ViewModel 已释放");
}
}
catch (Exception ex)
{
Log.Error(ex, "导航相机 ViewModel 释放失败");
}
// 释放导航相机服务资源
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var cameraService = bootstrapper.Container.Resolve<ICameraService>();
cameraService?.Dispose();
Log.Information("导航相机服务资源已释放");
}
}
catch (Exception ex)
{
Log.Error(ex, "导航相机服务资源释放失败");
}
// 释放主界面探测器帧流水线资源
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var detectorFramePipelineService = bootstrapper.Container.Resolve<IDetectorFramePipelineService>();
detectorFramePipelineService?.Dispose();
Log.Information("主界面探测器帧流水线资源已释放");
}
}
catch (Exception ex)
{
Log.Error(ex, "主界面探测器帧流水线资源释放失败");
}
// 释放SQLite数据库资源 | Release SQLite database resources
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var dbContext = bootstrapper.Container.Resolve<IDbContext>(); // 从 Prism 容器获取 IDbContext 实例(单例)| Get IDbContext instance from Prism container (singleton)
dbContext?.Dispose();
Log.Information("数据库资源已成功释放 | Database resources released successfully");
}
}
catch (Exception ex)
{
Log.Error(ex, "数据库资源释放失败,忽略该错误继续退出 | Database resource release failed, ignoring error and continuing exit");
}
// 释放 PLC 资源
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var plcService = bootstrapper.Container.Resolve<IPlcService>();
plcService?.Dispose();
Log.Information("PLC 资源已释放");
}
}
catch (Exception ex)
{
Log.Error(ex, "PLC 资源释放失败");
}
// 释放探测器资源
try
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper != null)
{
var detectorService = bootstrapper.Container.Resolve<IDetectorService>();
detectorService?.Dispose();
Log.Information("探测器资源已释放");
}
}
catch (Exception ex)
{
Log.Error(ex, "探测器资源释放失败");
}
Log.CloseAndFlush();
base.OnExit(e);
}
/// <summary>
/// 处理未捕获的异常
/// </summary>
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
Log.Fatal(exception, "应用程序发生未处理的异常");
MessageBox.Show(
$"A fatal error has occurred:\n\n{exception?.Message}\n\nPlease check the log file for details.",
"Fatal Error",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
/// <summary>
/// 处理 UI 线程未捕获的异常
/// </summary>
private void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
Log.Error(e.Exception, "UI 线程发生未处理的异常");
MessageBox.Show(
$"An error has occurred:\n\n{e.Exception.Message}\n\nPlease check the log file for details.",
"Error",
MessageBoxButton.OK,
MessageBoxImage.Error);
// 标记为已处理,防止应用程序崩溃
e.Handled = true;
}
}
public class AppBootstrapper : PrismBootstrapper
{
public static AppBootstrapper Instance { get; private set; }
public new IContainerProvider Container => base.Container;
private bool _modulesInitialized = false;
private string _cameraError;
public AppBootstrapper()
{
Instance = this;
}
protected override Window CreateShell()
{
// 提前初始化模块,确保硬件服务在 MainWindow XAML 解析前已注册
if (!_modulesInitialized)
{
base.InitializeModules();
_modulesInitialized = true;
}
var shell = Container.Resolve<MainWindow>();
// 主窗口加载完成后再连接相机,确保所有模块和原生 DLL 已完成初始化
shell.Loaded += (s, e) =>
{
// [DEV] 导航相机连接已屏蔽,开发阶段跳过以加快启动速度
// TryConnectCamera();
// 初始化主界面探测器帧流水线,开始接收探测器图像事件
try
{
_ = Container.Resolve<IDetectorFramePipelineService>();
}
catch (Exception ex)
{
Log.Error(ex, "初始化主界面探测器帧流水线失败");
}
// [DEV] 相机状态通知已屏蔽
// try
// {
// var cameraVm = Container.Resolve<NavigationPropertyPanelViewModel>();
// cameraVm.OnCameraReady();
// }
// catch (Exception ex)
// {
// Log.Error(ex, "通知相机 ViewModel 失败");
// }
// if (_cameraError != null)
// {
// HexMessageBox.Show(_cameraError, MessageBoxButton.OK, MessageBoxImage.Error);
// }
};
return shell;
}
/// <summary>
/// 在主线程上检索并连接导航相机。
/// pylon SDK 要求在主线程(STA)上操作,不能放到后台线程。
/// </summary>
private void TryConnectCamera()
{
var camera = Container.Resolve<ICameraController>();
try
{
var info = camera.Open();
Log.Information("导航相机已连接: {ModelName} (SN: {SerialNumber})", info.ModelName, info.SerialNumber);
}
catch (DeviceNotFoundException)
{
Log.Warning("未检测到导航相机");
_cameraError = "未检测到导航相机,请检查连接后重启软件。";
}
catch (Exception ex)
{
Log.Warning(ex, "导航相机自动连接失败: {Message}", ex.Message);
_cameraError = $"导航相机连接失败: {ex.Message}";
}
}
/// <summary>
/// 模块已在 CreateShell 中提前初始化,此处跳过避免重复加载
/// </summary>
protected override void InitializeModules()
{
if (!_modulesInitialized)
{
base.InitializeModules();
_modulesInitialized = true;
}
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// 注册 Serilog 的 ILogger 实例
containerRegistry.RegisterInstance<ILogger>(Log.Logger);
// 注册 XP.Common.ILoggerService 适配器
containerRegistry.RegisterSingleton<ILoggerService, SerilogLoggerService>();
// 注册视图和视图模型
containerRegistry.RegisterForNavigation<MainWindow>();
containerRegistry.RegisterSingleton<MainViewModel>();
containerRegistry.RegisterSingleton<ViewportPanelViewModel>();
containerRegistry.RegisterSingleton<NavigationPropertyPanelViewModel>();
// 注册图像处理服务与视图
containerRegistry.RegisterSingleton<IImageProcessingService, ImageProcessingService>();
containerRegistry.Register<ImageProcessingViewModel>();
// 注册流水线服务(单例,共享 IImageProcessingService
containerRegistry.RegisterSingleton<IPipelineExecutionService, PipelineExecutionService>();
containerRegistry.RegisterSingleton<IPipelinePersistenceService, PipelinePersistenceService>();
// 注册全局状态服务(单例)
containerRegistry.RegisterSingleton<IAppStateService, AppStateService>();
containerRegistry.RegisterSingleton<IXpDataPathService, XpDataPathService>();
// 注册检测配方服务(单例)
containerRegistry.RegisterSingleton<IRecipeService, RecipeService>();
// 注册流水线 ViewModel(每次解析创建新实例)
containerRegistry.Register<PipelineEditorViewModel>();
containerRegistry.Register<OperatorToolboxViewModel>();
// 注册硬件库的 ViewModel(供 ViewModelLocator 自动装配)
containerRegistry.Register<XP.Hardware.RaySource.ViewModels.RaySourceOperateViewModel>();
// 注册 SQLite 配置和数据库上下文(FilamentLifetimeService 依赖)
var sqliteConfig = XP.Common.Helpers.ConfigLoader.LoadSqliteConfig();
containerRegistry.RegisterInstance(sqliteConfig);
containerRegistry.RegisterSingleton<IDbContext, SqliteContext>();
// 注册通用模块的服务(本地化、Dump)
containerRegistry.RegisterSingleton<ILocalizationConfig, LocalizationConfig>();
containerRegistry.RegisterSingleton<ILocalizationService, ResxLocalizationService>();
containerRegistry.RegisterSingleton<DumpConfig>(() => XP.Common.Helpers.ConfigLoader.LoadDumpConfig());
containerRegistry.RegisterSingleton<IDumpService, DumpService>();
// ── CNC / 矩阵编排 / 测量数据服务(单例)──
containerRegistry.RegisterSingleton<ICncProgramService, CncProgramService>();
containerRegistry.RegisterSingleton<IMatrixService, MatrixService>();
containerRegistry.RegisterSingleton<IMeasurementDataService, MeasurementDataService>();
containerRegistry.RegisterSingleton<IInspectionResultStore, InspectionResultStore>();
containerRegistry.RegisterSingleton<ICncExecutionService, CncExecutionService>();
// ── 主界面实时图像 / 探测器双队列服务(单例)──
containerRegistry.RegisterSingleton<IMainViewportService, MainViewportService>();
containerRegistry.RegisterSingleton<IDetectorFramePipelineService, DetectorFramePipelineService>();
// ── CNC / 矩阵 ViewModel(瞬态)──
containerRegistry.Register<CncEditorViewModel>();
containerRegistry.Register<MatrixEditorViewModel>();
containerRegistry.Register<MeasurementStatsViewModel>();
// ── CNC / 矩阵导航视图 ──
containerRegistry.RegisterForNavigation<CncPageView>();
containerRegistry.RegisterForNavigation<MatrixPageView>();
// ── 导航相机服务(单例)──
containerRegistry.RegisterSingleton<ICameraFactory, CameraFactory>();
containerRegistry.RegisterSingleton<ICameraController>(() =>
new CameraFactory().CreateController("Basler"));
containerRegistry.RegisterSingleton<ICameraService, CameraService>();
Log.Information("依赖注入容器配置完成");
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
// 所有模块服务已在 RegisterTypes 中手动注册
// CommonModule: ILocalizationService, IDumpService
// RaySourceModule: IRaySourceService, IRaySourceFactory, IFilamentLifetimeService
// 注册通用模块(必须最先加载)| Register common module (must be loaded first)
moduleCatalog.AddModule<CommonModule>();
// 注册其他模块 | Register other modules
moduleCatalog.AddModule<PLCModule>();
moduleCatalog.AddModule<DetectorModule>();
moduleCatalog.AddModule<RaySourceModule>();
moduleCatalog.AddModule<MotionControlModule>();
base.ConfigureModuleCatalog(moduleCatalog);
}
}
}