将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。

This commit is contained in:
QI Mingxuan
2026-04-16 17:31:13 +08:00
parent 6ec4c3ddaa
commit 2bd6e566c3
581 changed files with 74600 additions and 222 deletions
+151 -28
View File
@@ -1,44 +1,167 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
</sectionGroup>
</configSections>
<appSettings>
<!-- Serilog 日志配置 -->
<add key="Serilog:LogPath" value="logs" />
<add key="Serilog:MinimumLevel" value="Information" />
<!-- 语言配置 可选值: ZhCN, ZhTW, EnUS| Language Configuration -->
<add key="Language" value="ZhCN" />
<add key="UserManual" value="D:\HMQProject\XplorePlane_CT\Code\XplorePlane\XP.App\bin\Debug\net8.0-windows7.0\UserManual.pdf" />
<!-- Serilog日志配置 -->
<add key="Serilog:LogPath" value="D:\XplorePlane\Logs" />
<add key="Serilog:MinimumLevel" value="Debug" />
<add key="Serilog:EnableConsole" value="true" />
<add key="Serilog:RollingInterval" value="Day" />
<add key="Serilog:FileSizeLimitMB" value="100" />
<add key="Serilog:RetainedFileCountLimit" value="30" />
<add key="Serilog:RetainedFileCountLimit" value="365" />
<!-- 射线源配置(key 格式: RaySource:xxx,与 ConfigLoader 一致) -->
<add key="RaySource:PlcIpAddress" value="192.168.1.100" />
<add key="RaySource:PlcPort" value="11160" />
<!-- 数据库SQLite配置 -->
<add key="Sqlite:DbFilePath" value="D:\XplorePlane\DataBase\XP.db" />
<add key="Sqlite:ConnectionTimeout" value="10" />
<add key="Sqlite:CreateIfNotExists" value="true" />
<!-- 是否启用SQLite WAL模式(提升并发读写性能,默认true) -->
<add key="Sqlite:EnableWalMode" value="true" />
<!-- 是否开启SQL操作日志(记录所有执行的SQL语句,默认false -->
<add key="Sqlite:EnableSqlLogging" value="false" />
<!-- 射线源配置 -->
<!-- 射线源类型 | Ray Source Type -->
<!-- 可选值: Comet225 | Available values: Comet225 -->
<add key="RaySource:SourceType" value="Comet225" />
<add key="RaySource:SerialNumber" value="SN08602861" />
<add key="RaySource:TotalLifeThreshold" value="10" />
<!-- PVI通讯参数 | PVI Communication Parameters -->
<add key="RaySource:PlcIpAddress" value="192.168.12.10" />
<add key="RaySource:PlcPort" value="11159" />
<add key="RaySource:PortNumber" value="11" />
<add key="RaySource:StationNumber" value="1" />
<add key="RaySource:CpuName" value="X20CP1584" />
<add key="RaySource:ConnectionTimeout" value="5000" />
<add key="RaySource:CpuName" value="cpu" />
<add key="RaySource:ConnectionTimeout" value="30000" />
<!-- 硬件参数范围 | Hardware Parameter Ranges -->
<add key="RaySource:MinVoltage" value="20" />
<add key="RaySource:MaxVoltage" value="225" />
<add key="RaySource:MinCurrent" value="10" />
<add key="RaySource:MaxCurrent" value="1000" />
<add key="RaySource:WarmUpTimeout" value="300000" />
<add key="RaySource:StartUpTimeout" value="180000" />
<add key="RaySource:StatusPollingInterval" value="500" />
<add key="RaySource:EnableAutoStatusMonitoring" value="true" />
<!-- SQLite 数据库配置 -->
<add key="Sqlite:DbFilePath" value="Data\XP.db" />
<add key="Sqlite:ConnectionTimeout" value="30" />
<add key="Sqlite:CreateIfNotExists" value="true" />
<add key="Sqlite:EnableWalMode" value="true" />
<add key="Sqlite:EnableSqlLogging" value="false" />
<add key="RaySource:MaxCurrent" value="1440" />
<!-- 外部程序配置 | External Program Configuration -->
<add key="RaySource:AdvanceExePath" value="C:\Program Files (x86)\Feinfocus\FXEControl_3.1.1.65\FXEControl.exe" />
<!-- 操作超时配置 | Operation Timeout Configuration -->
<add key="RaySource:RaySource:InitializationTimeout" value="30000" />
<add key="RaySource:WarmUpTimeout" value="600000" />
<add key="RaySource:StartUpTimeout" value="1800000" />
<add key="RaySource:AutoCenterTimeout" value="1200000" />
<add key="RaySource:FilamentAdjustTimeout" value="1200000" />
<add key="RaySource:GeneralOperationTimeout" value="100000" />
<!-- 探测器配置 -->
<add key="Detector.Type" value="Varex4343" />
<add key="Detector.IPAddress" value="192.168.1.101" />
<add key="Detector.Port" value="8080" />
<!-- 探测器类型 | Detector Type -->
<!-- 可选值: Varex, IRay, Hamamatsu | Available values: Varex, IRay, Hamamatsu -->
<add key="Detector:Type" value="Varex" />
<!-- 通用配置 | Common Configuration -->
<add key="Detector:IP" value="192.168.1.200" />
<add key="Detector:Port" value="5000" />
<add key="Detector:SavePath" value="D:\XplorePlane\DetectorImages" />
<add key="Detector:AutoSave" value="true" />
<!-- Varex 探测器专属配置 | Varex Detector Specific Configuration -->
<!-- Binning 模式: Bin1x1, Bin2x2, Bin4x4 | Binning mode: Bin1x1, Bin2x2, Bin4x4 -->
<add key="Detector:Varex:BinningMode" value="Bin1x1" />
<!-- 增益模式: Low, High | Gain mode: Low, High -->
<add key="Detector:Varex:GainMode" value="High" />
<!-- 曝光时间(毫秒)| Exposure time (milliseconds) -->
<add key="Detector:Varex:ExposureTime" value="100" />
<!-- ROI 区域 | ROI Region -->
<add key="Detector:Varex:ROI_X" value="0" />
<add key="Detector:Varex:ROI_Y" value="0" />
<add key="Detector:Varex:ROI_Width" value="2880" />
<add key="Detector:Varex:ROI_Height" value="2880" />
<!-- iRay 探测器专属配置 | iRay Detector Specific Configuration -->
<!-- 采集模式: Continuous, SingleFrame | Acquisition mode: Continuous, SingleFrame -->
<add key="Detector:IRay:AcquisitionMode" value="Continuous" />
<!-- 默认增益值 | Default gain value -->
<add key="Detector:IRay:DefaultGain" value="1.0" />
<!-- 校正配置 | Correction Configuration -->
<add key="Detector:Correction:DarkFrameCount" value="10" />
<add key="Detector:Correction:GainFrameCount" value="10" />
<add key="Detector:Correction:SaveCorrectionData" value="true" />
<!-- 操作超时配置 | Operation Timeout Configuration -->
<add key="Detector:InitializationTimeout" value="30000" />
<add key="Detector:AcquisitionTimeout" value="10000" />
<add key="Detector:CorrectionTimeout" value="60000" />
<!-- PLC 配置 -->
<add key="PLC.Type" value="B&amp;R" />
<add key="PLC.StationName" value="PLC_STATION" />
<add key="PLC.ConnectionTimeout" value="5000" />
<!-- Dump 配置 | Dump Configuration -->
<add key="Dump:StoragePath" value="D:\XplorePlane\Dump" />
<add key="Dump:EnableScheduledDump" value="false" />
<add key="Dump:ScheduledIntervalMinutes" value="60" />
<add key="Dump:MiniDumpSizeLimitMB" value="100" />
<add key="Dump:RetentionDays" value="7" />
<!-- PLC 配置 | PLC Configuration -->
<add key="Plc:IpAddress" value="192.168.0.1" />
<add key="Plc:Port" value="102" />
<add key="Plc:Rack" value="0" />
<add key="Plc:Slot" value="1" />
<!-- PlcType可选值: S200Smart, S300, S400, S1200, S1500 | PlcType Available values: S200Smart, S300, S400, S1200, S1500 -->
<add key="Plc:PlcType" value="S1200" />
<!-- 数据块配置 | Data Block Configuration -->
<add key="Plc:ReadDbBlock" value="DB31" />
<add key="Plc:WriteDbBlock" value="DB31" />
<add key="Plc:ReadStartAddress" value="0" />
<add key="Plc:ReadLength" value="200" />
<!-- 批量读取周期(毫秒)| Bulk read interval (ms) -->
<add key="Plc:BulkReadIntervalMs" value="250" />
<!-- 超时配置 | Timeout Configuration -->
<add key="Plc:ConnectTimeoutMs" value="3000" />
<add key="Plc:ReadTimeoutMs" value="1000" />
<add key="Plc:WriteTimeoutMs" value="1000" />
<!-- 自动重连 | Auto Reconnection -->
<add key="Plc:bReConnect" value="true" />
<!-- ==================== 运动控制配置 | Motion Control Configuration ==================== -->
<!-- 直线轴配置(单位:mm| Linear axis config (unit: mm) -->
<add key="MotionControl:SourceZ:Min" value="-500" />
<add key="MotionControl:SourceZ:Max" value="500" />
<add key="MotionControl:SourceZ:Origin" value="0" />
<add key="MotionControl:DetectorZ:Min" value="-600" />
<add key="MotionControl:DetectorZ:Max" value="600" />
<add key="MotionControl:DetectorZ:Origin" value="600" />
<add key="MotionControl:StageX:Min" value="-150" />
<add key="MotionControl:StageX:Max" value="150" />
<add key="MotionControl:StageX:Origin" value="0" />
<add key="MotionControl:StageY:Min" value="-150" />
<add key="MotionControl:StageY:Max" value="150" />
<add key="MotionControl:StageY:Origin" value="0" />
<!-- 旋转轴配置(单位:度)| Rotary axis config (unit: degrees) -->
<add key="MotionControl:DetectorSwing:Min" value="-45" />
<add key="MotionControl:DetectorSwing:Max" value="45" />
<add key="MotionControl:DetectorSwing:Origin" value="0" />
<add key="MotionControl:DetectorSwing:Enabled" value="true" />
<add key="MotionControl:StageRotation:Min" value="-360" />
<add key="MotionControl:StageRotation:Max" value="360" />
<add key="MotionControl:StageRotation:Origin" value="0" />
<add key="MotionControl:StageRotation:Enabled" value="true" />
<add key="MotionControl:FixtureRotation:Min" value="-90" />
<add key="MotionControl:FixtureRotation:Max" value="90" />
<add key="MotionControl:FixtureRotation:Origin" value="0" />
<add key="MotionControl:FixtureRotation:Enabled" value="true" />
<!-- 几何原点(mm| Geometry origins (mm) -->
<add key="MotionControl:Geometry:SourceZOrigin" value="0" />
<add key="MotionControl:Geometry:DetectorZOrigin" value="600" />
<add key="MotionControl:Geometry:StageRotationCenterZ" value="300" />
<!-- 探测器摆动几何参数(mm| Detector swing geometry parameters (mm) -->
<!-- SwingPivotOffset: Pivot 相对于 DetectorZ 绝对坐标的 Z 方向偏移,正值表示 Pivot 在 DetectorZ_abs 上方 -->
<add key="MotionControl:Geometry:SwingPivotOffset" value="80" />
<!-- SwingRadius: Pivot 到探测器感光面中心的距离,为 0 时退化为无摆动模型 -->
<add key="MotionControl:Geometry:SwingRadius" value="200" />
<!-- 运行参数 | Runtime parameters -->
<add key="MotionControl:PollingInterval" value="500" />
<add key="MotionControl:DefaultVelocity" value="500" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<!-- 允许捕获非托管异常(如 AccessViolationException)以便生成 Dump | Allow catching unmanaged exceptions (e.g. AccessViolationException) for dump generation -->
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
</configuration>
+89 -52
View File
@@ -1,4 +1,5 @@
using Prism.DryIoc;
using DryIoc;
using Prism.DryIoc;
using Prism.Ioc;
using Prism.Modularity;
using Serilog;
@@ -9,15 +10,25 @@ 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.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.MotionControl.Module;
using XP.Hardware.PLC;
using XP.Hardware.RaySource.Module;
using XP.Hardware.RaySource.Services;
using XplorePlane.Services;
using XplorePlane.Services.AppState;
@@ -38,13 +49,10 @@ namespace XplorePlane
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// 设置 Telerik Windows11 主题,缩小 Ribbon 整体尺寸
StyleManager.ApplicationTheme = new Windows11Theme();
//Windows11Palette.Palette.FontSize = 11;
//Windows11Palette.Palette.FontSizeS = 10;
//Windows11Palette.Palette.FontSizeL = 13;
protected override void OnStartup(StartupEventArgs e)
{
// 设置 Telerik Windows11 主题,缩小 Ribbon 整体尺寸
StyleManager.ApplicationTheme = new Windows11Theme();
// 强制使用中文 UI,确保 ImageProcessing 库显示中文
var zhCN = new CultureInfo("zh-CN");
@@ -56,8 +64,8 @@ namespace XplorePlane
// 配置 Serilog 日志系统
ConfigureLogging();
// 捕获未处理的异常
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
// 捕获未处理的异常
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
DispatcherUnhandledException += OnDispatcherUnhandledException;
try
@@ -92,14 +100,12 @@ namespace XplorePlane
private void ConfigureLogging()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/xploreplane-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30)
.CreateLogger();
// 加载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("========================================");
@@ -159,7 +165,23 @@ namespace XplorePlane
Log.Error(ex, "相机服务资源释放失败");
}
Log.CloseAndFlush();
// 释放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");
}
Log.CloseAndFlush();
base.OnExit(e);
}
@@ -168,7 +190,7 @@ namespace XplorePlane
/// </summary>
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
var exception = e.ExceptionObject as Exception;
Log.Fatal(exception, "应用程序发生未处理的异常");
MessageBox.Show(
@@ -207,21 +229,45 @@ namespace XplorePlane
Instance = this;
}
private bool _modulesInitialized = false;
protected override Window CreateShell()
{
// 提前初始化模块,确保硬件服务在 MainWindow XAML 解析前已注册
// 默认 Prism 顺序是 CreateShell → InitializeModules
// 但 MainWindow 中嵌入的硬件控件会在 XAML 解析时触发 ViewModelLocator
// 此时模块尚未加载,导致依赖解析失败
if (!_modulesInitialized)
{
base.InitializeModules();
_modulesInitialized = true;
}
return Container.Resolve<MainWindow>();
}
/// <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<XP.Common.Logging.Interfaces.ILoggerService, Services.LoggerServiceAdapter>();
// 注册 XP.Common.ILoggerService 适配器
containerRegistry.RegisterSingleton<ILoggerService, SerilogLoggerService>();
// 注册视图和视图模型
containerRegistry.RegisterForNavigation<MainWindow>();
// 注册视图和视图模型
containerRegistry.RegisterForNavigation<MainWindow>();
containerRegistry.RegisterForNavigation<MainWindowB>();
containerRegistry.Register<MainViewModel>();
containerRegistry.RegisterSingleton<NavigationPropertyPanelViewModel>();
@@ -253,12 +299,14 @@ namespace XplorePlane
containerRegistry.RegisterInstance(sqliteConfig);
containerRegistry.RegisterSingleton<IDbContext, SqliteContext>();
// 注册硬件库的 ViewModel(供 ViewModelLocator 自动装配)
//containerRegistry.Register<XP.Hardware.RaySource.ViewModels.RaySourceOperateViewModel>();
// 手动注册射线源模块的所有服务(确保 DryIoc 容器中可用,避免模块加载顺序问题)
var raySourceConfig = XP.Hardware.RaySource.Config.ConfigLoader.LoadConfig();
containerRegistry.RegisterInstance(raySourceConfig);
containerRegistry.RegisterSingleton<XP.Hardware.RaySource.Abstractions.IRaySourceFactory, XP.Hardware.RaySource.Factories.RaySourceFactory>();
containerRegistry.RegisterSingleton<IRaySourceService, XP.Hardware.RaySource.Services.RaySourceService>();
containerRegistry.RegisterSingleton<XP.Hardware.RaySource.Services.IFilamentLifetimeService, XP.Hardware.RaySource.Services.FilamentLifetimeService>();
//var raySourceConfig = XP.Hardware.RaySource.Config.ConfigLoader.LoadConfig();
//containerRegistry.RegisterInstance(raySourceConfig);
//containerRegistry.RegisterSingleton<XP.Hardware.RaySource.Abstractions.IRaySourceFactory, XP.Hardware.RaySource.Factories.RaySourceFactory>();
//containerRegistry.RegisterSingleton<IRaySourceService, XP.Hardware.RaySource.Services.RaySourceService>();
//containerRegistry.RegisterSingleton<XP.Hardware.RaySource.Services.IFilamentLifetimeService, XP.Hardware.RaySource.Services.FilamentLifetimeService>();
// 手动注册通用模块的服务(本地化、Dump)
containerRegistry.RegisterSingleton<ILocalizationConfig, LocalizationConfig>();
@@ -291,31 +339,20 @@ namespace XplorePlane
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
// 所有模块服务已在 RegisterTypes 中手动注册
// CommonModule: ILocalizationService, IDumpService
// RaySourceModule: IRaySourceService, IRaySourceFactory, IFilamentLifetimeService
// 所有模块服务已在 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);
}
protected override void InitializeModules()
{
base.InitializeModules();
// 手动初始化本地化扩展(原由 CommonModule.OnInitialized 完成)
// 必须在 CreateShell 之前执行,XAML 中的 {loc:Localization} 标记扩展依赖它
var locService = Container.Resolve<ILocalizationService>();
LocalizationExtension.Initialize(locService);
// 启动 Dump 服务
try
{
var dumpService = Container.Resolve<IDumpService>();
dumpService.Start();
}
catch (System.Exception ex)
{
Log.Warning(ex, "Dump 服务启动失败,跳过");
}
}
}
}
+281 -31
View File
@@ -1,13 +1,21 @@
using Prism.Commands;
using Prism.Ioc;
using Prism.Mvvm;
using System;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Windows;
using XP.Common.Logging.Interfaces;
using XP.Common.PdfViewer.Interfaces;
using XP.Hardware.MotionControl.Abstractions;
namespace XplorePlane.ViewModels
{
public class MainViewModel : BindableBase
{
private readonly ILoggerService _logger;
private readonly IContainerProvider _containerProvider;
private string _licenseInfo = "当前时间";
public string LicenseInfo
@@ -18,60 +26,300 @@ namespace XplorePlane.ViewModels
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; set; }
public DelegateCommand OpenPipelineEditorCommand { get; set; }
public DelegateCommand OpenCncEditorCommand { get; set; }
public DelegateCommand OpenMatrixEditorCommand { get; set; }
public MainViewModel(ILoggerService logger)
// 窗口打开命令
public DelegateCommand OpenImageProcessingCommand { 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 OpenCameraSettingsCommand { get; }
// 硬件命令
public DelegateCommand AxisResetCommand { get; }
public DelegateCommand OpenDetectorConfigCommand { get; }
public DelegateCommand OpenMotionDebugCommand { get; }
public DelegateCommand OpenPlcAddrConfigCommand { get; }
public DelegateCommand OpenRaySourceConfigCommand { get; }
public DelegateCommand WarmUpCommand { get; }
// 设置命令
public DelegateCommand OpenLanguageSwitcherCommand { get; }
public DelegateCommand OpenRealTimeLogViewerCommand { get; }
// 窗口引用(单例窗口防止重复打开)
private Window _motionDebugWindow;
private Window _detectorConfigWindow;
private Window _plcAddrConfigWindow;
private Window _realTimeLogViewerWindow;
private Window _toolboxWindow;
private Window _raySourceConfigWindow;
public MainViewModel(ILoggerService logger, IContainerProvider containerProvider)
{
_logger = logger?.ForModule<MainViewModel>() ?? throw new System.ArgumentNullException(nameof(logger));
_logger = logger?.ForModule<MainViewModel>() ?? throw new ArgumentNullException(nameof(logger));
_containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
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);
OpenImageProcessingCommand = new DelegateCommand(() =>
{
var window = new Views.ImageProcessingWindow();
window.Show();
_logger.Info("图像处理窗口已打开");
});
OpenPipelineEditorCommand = new DelegateCommand(() =>
{
var window = new Views.PipelineEditorWindow();
window.Show();
_logger.Info("流水线编辑器窗口已打开");
});
// 窗口打开命令
OpenImageProcessingCommand = new DelegateCommand(() => ShowWindow(new Views.ImageProcessingWindow(), "图像处理"));
OpenPipelineEditorCommand = new DelegateCommand(() => ShowWindow(new Views.PipelineEditorWindow(), "流水线编辑器"));
OpenCncEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.CncEditorWindow(), "CNC 编辑器"));
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);
OpenCncEditorCommand = new DelegateCommand(() =>
{
var window = new Views.Cnc.CncEditorWindow();
window.Show();
_logger.Info("CNC 编辑器窗口已打开");
});
// 硬件命令
AxisResetCommand = new DelegateCommand(ExecuteAxisReset);
OpenDetectorConfigCommand = new DelegateCommand(ExecuteOpenDetectorConfig);
OpenMotionDebugCommand = new DelegateCommand(ExecuteOpenMotionDebug);
OpenPlcAddrConfigCommand = new DelegateCommand(ExecuteOpenPlcAddrConfig);
OpenRaySourceConfigCommand = new DelegateCommand(ExecuteOpenRaySourceConfig);
WarmUpCommand = new DelegateCommand(ExecuteWarmUp);
OpenMatrixEditorCommand = new DelegateCommand(() =>
{
var window = new Views.Cnc.MatrixEditorWindow();
window.Show();
_logger.Info("矩阵编排窗口已打开");
});
// 设置命令
OpenLanguageSwitcherCommand = new DelegateCommand(ExecuteOpenLanguageSwitcher);
OpenRealTimeLogViewerCommand = new DelegateCommand(ExecuteOpenRealTimeLogViewer);
_logger.Info("MainViewModel 已初始化");
}
#region
/// <summary>
/// 显示一个新窗口(非模态)
/// </summary>
private void ShowWindow(Window window, string name)
{
window.Owner = Application.Current.MainWindow;
window.Show();
_logger.Info("{Name} 窗口已打开", name);
}
/// <summary>
/// 显示或激活单例窗口(非模态,防止重复打开)
/// </summary>
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);
}
#endregion
#region
private void ExecuteOpenToolbox()
{
ShowOrActivate(_toolboxWindow, w => _toolboxWindow = w, () => new Views.OperatorToolboxWindow(), "算子工具箱");
}
private void ExecuteOpenUserManual()
{
try
{
var manualPath = ConfigurationManager.AppSettings["UserManual"];
if (string.IsNullOrEmpty(manualPath))
{
_logger.Warn("未配置用户手册路径");
MessageBox.Show("未配置用户手册路径,请检查 App.config 中的 UserManual 配置项。",
"提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (!File.Exists(manualPath))
{
_logger.Warn("用户手册文件不存在:{Path}", manualPath);
MessageBox.Show($"用户手册文件不存在:\n{manualPath}",
"提示", 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, "打开用户手册失败");
MessageBox.Show($"打开用户手册失败:{ex.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ExecuteOpenCameraSettings()
{
try
{
var vm = _containerProvider.Resolve<NavigationPropertyPanelViewModel>();
if (!vm.IsCameraConnected)
{
MessageBox.Show("请先连接相机", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var window = new Views.CameraSettingsWindow(vm) { Owner = Application.Current.MainWindow };
window.Show();
}
catch (Exception ex)
{
_logger.Error(ex, "打开相机设置失败");
}
}
#endregion
#region
private void ExecuteAxisReset()
{
var result = MessageBox.Show("确认执行轴复位操作?", "轴复位",
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($"轴复位失败:{resetResult.ErrorMessage}", "错误",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
catch (Exception ex)
{
_logger.Error(ex, "轴复位异常");
MessageBox.Show($"轴复位异常:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ExecuteOpenDetectorConfig()
{
try
{
ShowOrActivate(_detectorConfigWindow, w => _detectorConfigWindow = w,
() => new XP.Hardware.Detector.Views.DetectorConfigWindow(), "探测器配置");
}
catch (Exception ex)
{
_logger.Error(ex, "打开探测器配置窗口失败");
MessageBox.Show($"打开探测器配置窗口失败:\n{ex.InnerException?.Message ?? ex.Message}",
"错误", 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(), "射线源配置");
}
private void ExecuteWarmUp()
{
var messageBoxResult = MessageBox.Show("确认执行射线源暖机操作?", "暖机",
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($"暖机失败:{result.ErrorMessage}", "错误",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
else
{
_logger.Info("暖机命令已发送");
}
}
catch (Exception ex)
{
_logger.Error(ex, "暖机异常");
MessageBox.Show($"暖机异常:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region
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, "打开语言设置失败");
}
}
private void ExecuteOpenRealTimeLogViewer()
{
ShowOrActivate(_realTimeLogViewerWindow, w => _realTimeLogViewerWindow = w,
() => new XP.Common.GeneralForm.Views.RealTimeLogViewer(), "实时日志");
}
#endregion
#region
private void OnNavigateHome()
{
_logger.Info("导航到主页");
@@ -107,5 +355,7 @@ namespace XplorePlane.ViewModels
_logger.Info("编辑属性");
LicenseInfo = "编辑属性";
}
#endregion
}
}
}
@@ -0,0 +1,86 @@
using Prism.Events;
using Prism.Mvvm;
using System;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using XP.Common.Logging.Interfaces;
using XP.Hardware.Detector.Abstractions;
using XP.Hardware.Detector.Abstractions.Events;
namespace XplorePlane.ViewModels
{
/// <summary>
/// 实时图像 ViewModel,订阅探测器采集事件并显示图像
/// </summary>
public class ViewportPanelViewModel : BindableBase
{
private readonly ILoggerService _logger;
private int _isProcessingFrame;
private ImageSource _imageSource;
public ImageSource ImageSource
{
get => _imageSource;
set => SetProperty(ref _imageSource, value);
}
private string _imageInfo = "等待图像...";
public string ImageInfo
{
get => _imageInfo;
set => SetProperty(ref _imageInfo, value);
}
public ViewportPanelViewModel(IEventAggregator eventAggregator, ILoggerService logger)
{
_logger = logger?.ForModule<ViewportPanelViewModel>();
eventAggregator.GetEvent<ImageCapturedEvent>()
.Subscribe(OnImageCaptured, ThreadOption.BackgroundThread);
}
private void OnImageCaptured(ImageCapturedEventArgs args)
{
if (args?.ImageData == null || args.Width == 0 || args.Height == 0) return;
// 帧节流:上一帧未消费完则跳过
if (Interlocked.CompareExchange(ref _isProcessingFrame, 1, 0) != 0) return;
try
{
var bitmap = ConvertToBitmapSource(args.ImageData, (int)args.Width, (int)args.Height);
bitmap.Freeze();
var info = $"{args.Width}×{args.Height} 帧#{args.FrameNumber} {args.CaptureTime:HH:mm:ss.fff}";
Application.Current?.Dispatcher?.BeginInvoke(new Action(() =>
{
try
{
ImageSource = bitmap;
ImageInfo = info;
}
finally
{
Interlocked.Exchange(ref _isProcessingFrame, 0);
}
}));
}
catch (Exception ex)
{
Interlocked.Exchange(ref _isProcessingFrame, 0);
_logger?.Error(ex, "图像转换失败:{Message}", ex.Message);
}
}
/// <summary>
/// 16 位灰度数据线性拉伸为 8 位 BitmapSource(委托给 XP.Common 通用转换器)
/// </summary>
private static BitmapSource ConvertToBitmapSource(ushort[] data, int width, int height)
{
return XP.Common.Converters.ImageConverter.ConvertGray16ToBitmapSource(data, width, height);
}
}
}
+1 -1
View File
@@ -19,4 +19,4 @@
</Border>
<views:PipelineEditorView Grid.Row="1" />
</Grid>
</UserControl>
</UserControl>
+41 -32
View File
@@ -9,13 +9,14 @@
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:views="clr-namespace:XplorePlane.Views"
xmlns:views1="clr-namespace:XP.Hardware.RaySource.Views;assembly=XP.Hardware.RaySource"
xmlns:mcViews="clr-namespace:XP.Hardware.MotionControl.Views;assembly=XP.Hardware.MotionControl"
x:Name="ParentWindow"
Title="XplorePlane"
Width="1920"
Height="1040"
d:DesignWidth="1580"
Background="#F5F5F5"
Icon="pack://application:,,,/GapInspect.ico"
Icon="pack://application:,,,/XplorerPlane.ico"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Window.Resources>
@@ -196,10 +197,7 @@
<telerik:RadRibbonToggleButton
telerik:ScreenTip.Description="暖机"
telerik:ScreenTip.Title="暖机"
Command="{Binding Path=SetIsWrapped.Command}"
CommandParameter="{Binding Path=SetIsWrapped.SelectedValue}"
IsChecked="{Binding Path=SetIsWrapped.SelectedValue, Mode=TwoWay}"
IsEnabled="{Binding Path=SetIsWrapped.IsEnabled}"
Command="{Binding WarmUpCommand}"
Size="Medium"
SmallImage="/Assets/Icons/heat-engine.png"
Text="暖机" />
@@ -207,10 +205,7 @@
x:Name="MergeAndCenterButton"
telerik:ScreenTip.Description="轴复位"
telerik:ScreenTip.Title="轴复位"
Command="{Binding Path=MergeAndCenter.Command}"
CommandParameter="{Binding Path=MergeAndCenter.SelectedValue}"
IsChecked="{Binding Path=MergeAndCenter.SelectedValue, Mode=TwoWay}"
IsEnabled="{Binding Path=MergeAndCenter.IsEnabled}"
Command="{Binding AxisResetCommand}"
Size="Medium"
SmallImage="/Assets/Icons/home.png"
Text="轴复位" />
@@ -222,10 +217,7 @@
x:Name="MergeAndCenterButton1"
telerik:ScreenTip.Description="射线源控制"
telerik:ScreenTip.Title="射线源"
Command="{Binding Path=MergeAndCenter.Command}"
CommandParameter="{Binding Path=MergeAndCenter.SelectedValue}"
IsChecked="{Binding Path=MergeAndCenter.SelectedValue, Mode=TwoWay}"
IsEnabled="{Binding Path=MergeAndCenter.IsEnabled}"
Command="{Binding OpenRaySourceConfigCommand}"
Size="Medium"
SmallImage="/Assets/Icons/xray.png"
Text="射线源" />
@@ -233,10 +225,7 @@
x:Name="MergeAndCenterButton2"
telerik:ScreenTip.Description="探测器控制"
telerik:ScreenTip.Title="探测器"
Command="{Binding Path=MergeAndCenter.Command}"
CommandParameter="{Binding Path=MergeAndCenter.SelectedValue}"
IsChecked="{Binding Path=MergeAndCenter.SelectedValue, Mode=TwoWay}"
IsEnabled="{Binding Path=MergeAndCenter.IsEnabled}"
Command="{Binding OpenDetectorConfigCommand}"
Size="Medium"
SmallImage="/Assets/Icons/detector2.png"
Text="探测器" />
@@ -244,24 +233,28 @@
x:Name="MergeAndCenterButton3"
telerik:ScreenTip.Description="运动控制"
telerik:ScreenTip.Title="运动控制"
Command="{Binding Path=MergeAndCenter.Command}"
CommandParameter="{Binding Path=MergeAndCenter.SelectedValue}"
IsChecked="{Binding Path=MergeAndCenter.SelectedValue, Mode=TwoWay}"
IsEnabled="{Binding Path=MergeAndCenter.IsEnabled}"
Command="{Binding OpenMotionDebugCommand}"
Size="Medium"
SmallImage="/Assets/Icons/xyz.png"
Text="运动控制" />
</StackPanel>
<!-- 第三列: 相机设置 -->
<!-- 第三列: 相机设置 / PLC 地址配置 -->
<StackPanel>
<telerik:RadRibbonButton
telerik:ScreenTip.Description="打开相机参数设置对话框"
telerik:ScreenTip.Title="相机设置"
Click="CameraSettings_Click"
Command="{Binding OpenCameraSettingsCommand}"
Size="Medium"
SmallImage="/Assets/Icons/detector2.png"
Text="相机设置" />
<telerik:RadRibbonButton
telerik:ScreenTip.Description="打开 PLC 信号地址定义编辑器"
telerik:ScreenTip.Title="PLC 地址配置"
Command="{Binding OpenPlcAddrConfigCommand}"
Size="Medium"
SmallImage="/Assets/Icons/tools.png"
Text="PLC 地址" />
</StackPanel>
</telerik:RadRibbonGroup>
@@ -269,7 +262,7 @@
<telerik:RadRibbonButton
telerik:ScreenTip.Description="打开算子工具箱,拖拽算子到流水线中"
telerik:ScreenTip.Title="算子工具箱"
Click="OpenToolboxButton_Click"
Command="{Binding OpenToolboxCommand}"
Size="Large"
SmallImage="/Assets/Icons/dynamic-range.png"
Text="算子工具箱" />
@@ -411,7 +404,7 @@
<telerik:RadRibbonButton
Size="Large"
SmallImage="/Assets/Icons/tools.png"
Click="LibraryVersions_Click"
Command="{Binding OpenLibraryVersionsCommand}"
Text="关于 XplorePlane" />
</telerik:RadRibbonGroup>
<telerik:RadRibbonGroup Header="帮助">
@@ -421,8 +414,28 @@
<telerik:RadRibbonButton
Size="Large"
SmallImage="/Assets/Icons/message.png"
Command="{Binding OpenUserManualCommand}"
Text="帮助文档" />
</telerik:RadRibbonGroup>
<telerik:RadRibbonGroup Header="设置">
<telerik:RadRibbonGroup.Variants>
<telerik:GroupVariant Priority="0" Variant="Large" />
</telerik:RadRibbonGroup.Variants>
<telerik:RadRibbonButton
telerik:ScreenTip.Description="切换应用程序显示语言"
telerik:ScreenTip.Title="多语言设置"
Size="Large"
SmallImage="/Assets/Icons/tools.png"
Command="{Binding OpenLanguageSwitcherCommand}"
Text="多语言设置" />
<telerik:RadRibbonButton
telerik:ScreenTip.Description="打开实时日志查看器"
telerik:ScreenTip.Title="查看日志"
Size="Large"
SmallImage="/Assets/Icons/message.png"
Command="{Binding OpenRealTimeLogViewerCommand}"
Text="查看日志" />
</telerik:RadRibbonGroup>
</telerik:RadRibbonTab>
<telerik:RadRibbonView.ContextualGroups>
@@ -473,16 +486,12 @@
<ColumnDefinition Width="250*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="250" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="340" />
</Grid.RowDefinitions>
<views1:RaySourceOperateView Grid.Row="0" Grid.ColumnSpan="2" />
<views:MotionControlPanelView
Grid.Row="1"
Grid.RowSpan="2"
Grid.ColumnSpan="2"
Margin="0,0,0,10" />
<mcViews:MotionControlView Grid.Row="1" Grid.ColumnSpan="2"/>
<views:NavigationPropertyPanelView Grid.Row="2" Grid.ColumnSpan="2" />
</Grid>
</Grid>
+1 -39
View File
@@ -1,4 +1,3 @@
using Prism.Ioc;
using System.Windows;
using XplorePlane.ViewModels;
@@ -21,42 +20,5 @@ namespace XplorePlane.Views
NavigationPanel.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
NavColumn.Width = show ? new GridLength(180) : new GridLength(0);
}
private OperatorToolboxWindow _toolboxWindow;
private void OpenToolboxButton_Click(object sender, RoutedEventArgs e)
{
if (_toolboxWindow == null || !_toolboxWindow.IsLoaded)
{
_toolboxWindow = new OperatorToolboxWindow { Owner = this };
_toolboxWindow.Show();
}
else
{
_toolboxWindow.Activate();
}
}
private void CameraSettings_Click(object sender, RoutedEventArgs e)
{
var bootstrapper = AppBootstrapper.Instance;
if (bootstrapper == null) return;
var vm = bootstrapper.Container.Resolve<NavigationPropertyPanelViewModel>();
if (!vm.IsCameraConnected)
{
MessageBox.Show("请先连接相机", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var window = new CameraSettingsWindow(vm) { Owner = this };
window.Show();
}
private void LibraryVersions_Click(object sender, RoutedEventArgs e)
{
var window = new LibraryVersionsWindow { Owner = this };
window.Show();
}
}
}
}
+24 -20
View File
@@ -4,6 +4,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
@@ -11,26 +13,28 @@
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Background="#F0F0F0"
BorderBrush="#DDDDDD"
BorderThickness="0,0,0,1">
<TextBlock
Margin="8,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontWeight="SemiBold"
Foreground="#333333"
Text="实时图像" />
<!-- 标题栏 -->
<Border Grid.Row="0" Background="#F0F0F0" BorderBrush="#DDDDDD" BorderThickness="0,0,0,1">
<TextBlock Margin="8,4" HorizontalAlignment="Left" VerticalAlignment="Center"
FontWeight="SemiBold" Foreground="#333333" Text="实时图像" />
</Border>
<!-- 图像显示区域,支持滚动 -->
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Image Source="{Binding ImageSource}"
Stretch="None"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="NearestNeighbor" />
</ScrollViewer>
<!-- 图像信息栏 -->
<Border Grid.Row="2" Background="#F0F0F0" BorderBrush="#DDDDDD" BorderThickness="0,1,0,0">
<TextBlock Margin="8,2" FontSize="11" Foreground="#666666"
Text="{Binding ImageInfo}" />
</Border>
<TextBlock
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="18"
Foreground="#999999"
Text="2D Viewport" />
</Grid>
</UserControl>
</UserControl>
+6 -11
View File
@@ -5,7 +5,7 @@
<UseWPF>true</UseWPF>
<RootNamespace>XplorePlane</RootNamespace>
<AssemblyName>XplorePlane</AssemblyName>
<ApplicationIcon>GapInspect.ico</ApplicationIcon>
<ApplicationIcon>XplorerPlane.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
@@ -37,15 +37,6 @@
<!-- DLL 引用 -->
<ItemGroup>
<!-- 硬件库 DLL 引用 -->
<Reference Include="XP.Common">
<HintPath>Libs\Hardware\XP.Common.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="XP.Hardware.RaySource">
<HintPath>Libs\Hardware\XP.Hardware.RaySource.dll</HintPath>
<Private>True</Private>
</Reference>
<!-- 原生依赖库引用 -->
<Reference Include="BR.AN.PviServices">
@@ -154,7 +145,7 @@
<Link>Libs\Hardware\zh-TW\%(Filename)%(Extension)</Link>
</None>
<Resource Include="GapInspect.ico" />
<Resource Include="XplorerPlane.ico" />
<!-- 配置文件 -->
<None Update="App.config">
@@ -162,6 +153,10 @@
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\XP.Common\XP.Common.csproj" />
<ProjectReference Include="..\XP.Hardware.Detector\XP.Hardware.Detector.csproj" />
<ProjectReference Include="..\XP.Hardware.MotionControl\XP.Hardware.MotionControl.csproj" />
<ProjectReference Include="..\XP.Hardware.RaySource\XP.Hardware.RaySource.csproj" />
<ProjectReference Include="..\XP.ImageProcessing.CfgControl\XP.ImageProcessing.CfgControl.csproj" />
<ProjectReference Include="..\XP.ImageProcessing.Core\XP.ImageProcessing.Core.csproj" />
<ProjectReference Include="..\XP.ImageProcessing.Processors\XP.ImageProcessing.Processors.csproj" />
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB