虚拟探测器与实时切换按钮的绑定
This commit is contained in:
Binary file not shown.
@@ -19,6 +19,11 @@ namespace XP.Hardware.Detector.Abstractions.Enums
|
||||
/// <summary>
|
||||
/// Hamamatsu 探测器 | Hamamatsu detector (预留)
|
||||
/// </summary>
|
||||
Hamamatsu = 2
|
||||
Hamamatsu = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 软件模拟探测器,用于集成验证(无需硬件)| Software simulated detector for integration testing (no hardware required)
|
||||
/// </summary>
|
||||
Simulated = 99
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace XP.Hardware.Detector.Config
|
||||
{
|
||||
DetectorType.Varex => LoadVarexConfiguration(),
|
||||
DetectorType.IRay => LoadIRayConfiguration(),
|
||||
DetectorType.Simulated => LoadSimulatedConfiguration(),
|
||||
_ => throw new NotSupportedException($"不支持的探测器类型:{detectorType} | Unsupported detector type: {detectorType}")
|
||||
};
|
||||
|
||||
@@ -111,6 +112,27 @@ namespace XP.Hardware.Detector.Config
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载软件模拟探测器配置 | Load simulated detector configuration
|
||||
/// </summary>
|
||||
private static SimulatedDetectorConfig LoadSimulatedConfiguration()
|
||||
{
|
||||
var config = new SimulatedDetectorConfig();
|
||||
|
||||
if (int.TryParse(ConfigurationManager.AppSettings["Detector:Simulated:Width"], out var w) && w > 0)
|
||||
config.Width = w;
|
||||
|
||||
if (int.TryParse(ConfigurationManager.AppSettings["Detector:Simulated:Height"], out var h) && h > 0)
|
||||
config.Height = h;
|
||||
|
||||
if (double.TryParse(ConfigurationManager.AppSettings["Detector:Simulated:FrameRateFps"],
|
||||
System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var fps) && fps > 0)
|
||||
config.FrameRateFps = fps;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载 iRay 配置 | Load iRay configuration
|
||||
/// </summary>
|
||||
@@ -208,16 +230,20 @@ namespace XP.Hardware.Detector.Config
|
||||
/// </summary>
|
||||
private static DetectorResult ValidateConfiguration(DetectorConfig config)
|
||||
{
|
||||
// 验证 IP 地址 | Validate IP address
|
||||
if (!IPAddress.TryParse(config.IP, out _))
|
||||
// 模拟探测器不需要网络连接,跳过 IP/端口验证 | Simulated detector skips IP/port validation
|
||||
if (config.Type != DetectorType.Simulated)
|
||||
{
|
||||
return DetectorResult.Failure($"无效的 IP 地址:{config.IP} | Invalid IP address: {config.IP}");
|
||||
}
|
||||
// 验证 IP 地址 | Validate IP address
|
||||
if (!IPAddress.TryParse(config.IP, out _))
|
||||
{
|
||||
return DetectorResult.Failure($"无效的 IP 地址:{config.IP} | Invalid IP address: {config.IP}");
|
||||
}
|
||||
|
||||
// 验证端口范围 | Validate port range
|
||||
if (config.Port < 1 || config.Port > 65535)
|
||||
{
|
||||
return DetectorResult.Failure($"无效的端口号:{config.Port},必须在 1-65535 之间 | Invalid port: {config.Port}, must be between 1-65535");
|
||||
// 验证端口范围 | Validate port range
|
||||
if (config.Port < 1 || config.Port > 65535)
|
||||
{
|
||||
return DetectorResult.Failure($"无效的端口号:{config.Port},必须在 1-65535 之间 | Invalid port: {config.Port}, must be between 1-65535");
|
||||
}
|
||||
}
|
||||
|
||||
// 验证存储路径 | Validate save path
|
||||
|
||||
@@ -7,7 +7,6 @@ using XP.Hardware.Detector.Abstractions.Enums;
|
||||
using XP.Hardware.Detector.Config;
|
||||
using XP.Hardware.Detector.Implementations;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
|
||||
namespace XP.Hardware.Detector.Factories
|
||||
{
|
||||
/// <summary>
|
||||
@@ -54,6 +53,7 @@ namespace XP.Hardware.Detector.Factories
|
||||
DetectorType.Varex => CreateVarexDetector(config),
|
||||
//DetectorType.IRay => CreateIRayDetector(config),
|
||||
DetectorType.Hamamatsu => throw new NotImplementedException("Hamamatsu 探测器尚未实现 | Hamamatsu detector not implemented yet"),
|
||||
DetectorType.Simulated => CreateSimulatedDetector(config),
|
||||
_ => throw new NotSupportedException($"不支持的探测器类型:{config.Type} | Unsupported detector type: {config.Type}")
|
||||
};
|
||||
|
||||
@@ -113,7 +113,21 @@ namespace XP.Hardware.Detector.Factories
|
||||
/// </summary>
|
||||
public IEnumerable<DetectorType> GetSupportedTypes()
|
||||
{
|
||||
return new[] { DetectorType.Varex, DetectorType.IRay };
|
||||
return new[] { DetectorType.Varex, DetectorType.IRay, DetectorType.Simulated };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建软件模拟探测器 | Create simulated detector
|
||||
/// </summary>
|
||||
private IAreaDetector CreateSimulatedDetector(DetectorConfig config)
|
||||
{
|
||||
var simConfig = config as SimulatedDetectorConfig
|
||||
?? new SimulatedDetectorConfig();
|
||||
|
||||
_logger?.Info("[DetectorFactory] 创建 SimulatedDetector,分辨率 {W}x{H},帧率 {FPS} fps",
|
||||
simConfig.Width, simConfig.Height, simConfig.FrameRateFps);
|
||||
|
||||
return new SimulatedDetector(simConfig, _eventAggregator, _logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1705,7 +1705,10 @@
|
||||
"Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408"
|
||||
},
|
||||
"runtime": {
|
||||
"XP.Common.dll": {}
|
||||
"XP.Common.dll": {
|
||||
"assemblyVersion": "1.4.16.1",
|
||||
"fileVersion": "1.4.16.1"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"en-US/XP.Common.resources.dll": {
|
||||
|
||||
@@ -57,13 +57,18 @@
|
||||
|
||||
<!-- 探测器配置 -->
|
||||
<!-- 探测器类型 | Detector Type -->
|
||||
<!-- 可选值: Varex, IRay, Hamamatsu | Available values: Varex, IRay, Hamamatsu -->
|
||||
<!-- 可选值: Varex, IRay, Hamamatsu, Simulated | Available values: Varex, IRay, Hamamatsu, Simulated -->
|
||||
<!-- 切换为 Simulated 可在无硬件环境下验证图像采集链路 | Switch to Simulated to verify image chain without hardware -->
|
||||
<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" />
|
||||
<!-- Simulated 探测器专属配置 | Simulated Detector Specific Configuration -->
|
||||
<add key="Detector:Simulated:Width" value="256" />
|
||||
<add key="Detector:Simulated:Height" value="256" />
|
||||
<add key="Detector:Simulated:FrameRateFps" value="5" />
|
||||
<!-- Varex 探测器专属配置 | Varex Detector Specific Configuration -->
|
||||
<!-- Binning 模式: Bin1x1, Bin2x2, Bin4x4 | Binning mode: Bin1x1, Bin2x2, Bin4x4 -->
|
||||
<add key="Detector:Varex:BinningMode" value="Bin1x1" />
|
||||
|
||||
+43
-1
@@ -27,6 +27,7 @@ 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;
|
||||
using XP.Hardware.RaySource.Module;
|
||||
@@ -298,7 +299,7 @@ namespace XplorePlane
|
||||
var shell = Container.Resolve<MainWindow>();
|
||||
|
||||
// 主窗口加载完成后再连接相机,确保所有模块和原生 DLL 已完成初始化
|
||||
shell.Loaded += (s, e) =>
|
||||
shell.Loaded += async (s, e) =>
|
||||
{
|
||||
// [DEV] 导航相机连接已屏蔽,开发阶段跳过以加快启动速度
|
||||
// TryConnectCamera();
|
||||
@@ -313,6 +314,9 @@ namespace XplorePlane
|
||||
Log.Error(ex, "初始化主界面探测器帧流水线失败");
|
||||
}
|
||||
|
||||
// 若配置为模拟探测器,自动初始化并启动采集(无需用户手动操作)
|
||||
await TryAutoStartSimulatedDetectorAsync();
|
||||
|
||||
// [DEV] 相机状态通知已屏蔽
|
||||
// try
|
||||
// {
|
||||
@@ -333,6 +337,44 @@ namespace XplorePlane
|
||||
return shell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 若 App.config 中 Detector:Type = Simulated,自动初始化并启动连续采集。
|
||||
/// 真实硬件类型不受影响。
|
||||
/// </summary>
|
||||
private async System.Threading.Tasks.Task TryAutoStartSimulatedDetectorAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var typeStr = System.Configuration.ConfigurationManager.AppSettings["Detector:Type"];
|
||||
if (!string.Equals(typeStr, "Simulated", StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
|
||||
Log.Information("[SimulatedDetector] 检测到 Simulated 类型,开始自动初始化探测器...");
|
||||
|
||||
var detectorService = Container.Resolve<XP.Hardware.Detector.Services.IDetectorService>();
|
||||
|
||||
var initResult = await detectorService.InitializeAsync();
|
||||
if (!initResult.IsSuccess)
|
||||
{
|
||||
Log.Error("SimulatedDetector 自动初始化失败:{Msg}", initResult.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
Log.Information("[SimulatedDetector] 初始化成功,启动连续采集...");
|
||||
|
||||
var startResult = await detectorService.StartAcquisitionAsync();
|
||||
if (!startResult.IsSuccess)
|
||||
{
|
||||
Log.Error("SimulatedDetector 启动采集失败:{Msg}", startResult.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
Log.Information("[SimulatedDetector] 连续采集已启动,帧将以 5 fps 持续发布");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "SimulatedDetector 自动启动异常");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在主线程上检索并连接导航相机。
|
||||
/// pylon SDK 要求在主线程(STA)上操作,不能放到后台线程。
|
||||
|
||||
@@ -586,20 +586,49 @@ namespace XplorePlane.Services.Cnc
|
||||
|
||||
private BitmapSource TryGetSourceImage()
|
||||
{
|
||||
var viewportImage = _mainViewportService?.LatestManualImage as BitmapSource
|
||||
?? _mainViewportService?.CurrentDisplayImage as BitmapSource;
|
||||
if (viewportImage != null)
|
||||
return viewportImage;
|
||||
// ── 优先级 1:MainViewportService 中的手动图像或当前显示图像 ──
|
||||
var manualImage = _mainViewportService?.LatestManualImage as BitmapSource;
|
||||
if (manualImage != null)
|
||||
{
|
||||
_logger.ForModule<CncExecutionService>().Info(
|
||||
"[图像链路] TryGetSourceImage:从 MainViewportService.LatestManualImage 获取图像,尺寸 {W}x{H}",
|
||||
manualImage.PixelWidth, manualImage.PixelHeight);
|
||||
return manualImage;
|
||||
}
|
||||
|
||||
var displayImage = _mainViewportService?.CurrentDisplayImage as BitmapSource;
|
||||
if (displayImage != null)
|
||||
{
|
||||
_logger.ForModule<CncExecutionService>().Info(
|
||||
"[图像链路] TryGetSourceImage:从 MainViewportService.CurrentDisplayImage 获取图像,尺寸 {W}x{H}",
|
||||
displayImage.PixelWidth, displayImage.PixelHeight);
|
||||
return displayImage;
|
||||
}
|
||||
|
||||
// ── 优先级 2:AppStateService 中的原始探测器帧 ──
|
||||
var detectorFrame = _appStateService?.LatestDetectorFrame;
|
||||
if (detectorFrame?.ImageData == null || detectorFrame.Width <= 0 || detectorFrame.Height <= 0)
|
||||
{
|
||||
_logger.ForModule<CncExecutionService>().Warn(
|
||||
"[图像链路] TryGetSourceImage:所有图像源均为空(MainViewport 无图像,AppState 无探测器帧),返回 null");
|
||||
return null;
|
||||
}
|
||||
|
||||
_logger.ForModule<CncExecutionService>().Info(
|
||||
"[图像链路] TryGetSourceImage:从 AppStateService.LatestDetectorFrame 获取原始帧,帧号 {N},分辨率 {W}x{H},采集时间 {T}",
|
||||
detectorFrame.FrameNumber, detectorFrame.Width, detectorFrame.Height,
|
||||
detectorFrame.CaptureTime.ToString("HH:mm:ss.fff"));
|
||||
|
||||
var bitmap = XP.Common.Converters.ImageConverter.ConvertGray16ToBitmapSource(
|
||||
detectorFrame.ImageData,
|
||||
(int)detectorFrame.Width,
|
||||
(int)detectorFrame.Height);
|
||||
bitmap.Freeze();
|
||||
|
||||
_logger.ForModule<CncExecutionService>().Info(
|
||||
"[图像链路] TryGetSourceImage:Gray16→BitmapSource 转换完成,输出尺寸 {W}x{H}",
|
||||
bitmap.PixelWidth, bitmap.PixelHeight);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,12 @@ namespace XplorePlane.Services.MainViewport
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Info(
|
||||
"[图像链路] DetectorFramePipeline 收到帧 #{N},分辨率 {W}x{H},采集时间 {T},当前队列深度 AcquireQ={AQ} ProcessQ={PQ}",
|
||||
args.FrameNumber, args.Width, args.Height,
|
||||
args.CaptureTime.ToString("HH:mm:ss.fff"),
|
||||
AcquireQueueCount, ProcessQueueCount);
|
||||
|
||||
var rawPixels = new ushort[args.ImageData.Length];
|
||||
Array.Copy(args.ImageData, rawPixels, rawPixels.Length);
|
||||
|
||||
@@ -99,6 +105,9 @@ namespace XplorePlane.Services.MainViewport
|
||||
{
|
||||
EnqueueBounded(_processQueue, frame, ProcessQueueCapacity, ref _processQueueCount);
|
||||
_processSignal.Release();
|
||||
_logger.Info(
|
||||
"[图像链路] 帧 #{N} 已入处理队列(每 {Every} 帧采样一次),累计接收 {Total} 帧",
|
||||
args.FrameNumber, ProcessEveryNFrames, sequence);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -127,7 +127,8 @@
|
||||
Margin="4,10,4,4"
|
||||
HorizontalAlignment="Center"
|
||||
ToolTip="主界面实时"
|
||||
IsChecked="True"
|
||||
IsChecked="{Binding IsMainViewportRealtimeEnabled, Mode=TwoWay}"
|
||||
IsEnabled="{Binding IsMainViewportSwitchEnabled}"
|
||||
SwitchWidth="44"
|
||||
SwitchHeight="24" />
|
||||
<TextBlock
|
||||
|
||||
Reference in New Issue
Block a user