Files
XplorePlane/XP.Hardware.Detector/Implementations/VarexDetector.cs
T

1438 lines
70 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 System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Prism.Events;
using XP.Hardware.Detector.Abstractions;
using XP.Hardware.Detector.Abstractions.Enums;
using XP.Hardware.Detector.Config;
using XP.Common.Logging.Interfaces;
namespace XP.Hardware.Detector.Implementations
{
/// <summary>
/// Varex 探测器实现类 | Varex detector implementation class
/// 通过 XISL API 控制 Varex 探测器硬件
/// </summary>
public class VarexDetector : AreaDetectorBase, IVarexDetector
{
#region | Private Fields
// 配置 | Configuration
private readonly VarexDetectorConfig _config;
// 日志服务 | Logger service
private readonly ILoggerService _logger;
// 内存管理 | Memory Management
private readonly ArrayPool<ushort> _arrayPool;
private readonly ConcurrentQueue<short[]> _imageQueue;
// XISL 句柄 | XISL Handles
private IntPtr _hAcqDesc = IntPtr.Zero;
private IntPtr _pAcqBuffer = IntPtr.Zero;
private IntPtr _pOffsetBuffer = IntPtr.Zero;
private IntPtr _pGainBuffer = IntPtr.Zero;
private IntPtr _pGainAvgBuffer = IntPtr.Zero; // 增益映射输出缓冲区(DWORD*| Gain map output buffer (DWORD*)
private IntPtr _pCorrList = IntPtr.Zero;
// 回调委托 | Callback Delegates
private XISLApi.EndFrameCallback _endFrameCallback;
private XISLApi.EndAcqCallback _endAcqCallback;
// 事件句柄 | Event Handles
private IntPtr _hevEndFrame = IntPtr.Zero;
private IntPtr _hevEndAcq = IntPtr.Zero;
// 配置参数 | Configuration Parameters
private BinningMode _binningMode;
private GainMode _gainMode;
private uint _exposureTime;
private (uint x, uint y, uint width, uint height) _roi;
// 采集控制 | Acquisition Control
private bool _isAcquiring = false;
private CancellationTokenSource _acquisitionCts;
private readonly object _acquisitionLock = new object();
// 缓存的图像分辨率(初始化/参数变更时更新)| Cached image resolution (updated on init/param change)
private uint _cachedRows;
private uint _cachedColumns;
#endregion
#region | Properties
/// <summary>
/// 探测器类型 | Detector type
/// </summary>
public override DetectorType Type => DetectorType.Varex;
#endregion
#region | Constructor
/// <summary>
/// 构造函数 | Constructor
/// </summary>
/// <param name="config">Varex 探测器配置 | Varex detector configuration</param>
/// <param name="eventAggregator">事件聚合器 | Event aggregator</param>
/// <param name="logger">日志服务 | Logger service</param>
public VarexDetector(VarexDetectorConfig config, IEventAggregator eventAggregator, ILoggerService logger = null)
: base(eventAggregator)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
_logger = logger?.ForModule("VarexDetector");
// 初始化内存管理 | Initialize memory management
_arrayPool = ArrayPool<ushort>.Shared;
_imageQueue = new ConcurrentQueue<short[]>();
// 初始化配置参数 | Initialize configuration parameters
_binningMode = config.BinningMode;
_gainMode = config.GainMode;
_exposureTime = config.ExposureTime;
_roi = (config.RoiX, config.RoiY, config.RoiWidth, config.RoiHeight);
// 创建回调委托实例(防止被 GC 回收)| Create callback delegate instances (prevent GC collection)
_endFrameCallback = OnEndFrameCallback;
_endAcqCallback = OnEndAcqCallback;
_logger?.Info("VarexDetector 实例已创建 | VarexDetector instance created");
}
#endregion
#region | Abstract Method Implementations (Placeholders)
/// <summary>
/// 初始化探测器内部实现 | Initialize detector internal implementation
/// </summary>
protected override Task<DetectorResult> InitializeInternalAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
try
{
_logger?.Info("开始初始化 Varex 探测器 | Starting Varex detector initialization");
// 1. 调用 VarexDetDll 底层初始化 | Call VarexDetDll low-level initialization
_logger?.Debug("调用 DoDetInit 进行底层初始化 | Calling DoDetInit for low-level initialization");
int initResult = XISLApi.DoDetInit(ref _hAcqDesc);
if (initResult != 0)
{
var errorMsg = $"探测器底层初始化失败 | Detector low-level initialization failed, DoDetInit 返回码:{initResult}";
_logger?.Error(null, errorMsg);
return DetectorResult.Failure(errorMsg, null, initResult);
}
_logger?.Info("DoDetInit 初始化成功 | DoDetInit initialization successful");
// 2. 获取探测器句柄 | Get detector handle
IntPtr hWnd = IntPtr.Zero;
var result = XISLApi.Acquisition_GetNextSensor(ref hWnd, ref _hAcqDesc);
if (_hAcqDesc == IntPtr.Zero)
{
var errorMsg = "获取探测器句柄失败,句柄为空 | Failed to get detector handle, handle is null";
_logger?.Error(null, errorMsg);
return DetectorResult.Failure(errorMsg, null, (int)result);
}
_logger?.Debug("探测器句柄获取成功 | Detector handle acquired successfully");
// 3. 注册回调函数 | Register callbacks
Thread.Sleep(200);
result = XISLApi.Acquisition_SetCallbacksAndMessages(
_hAcqDesc, IntPtr.Zero, 0, 0, _endFrameCallback, _endAcqCallback);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"注册回调函数失败 | Failed to register callbacks: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
_logger?.Debug("回调函数注册成功 | Callbacks registered successfully");
// 4. 设置帧同步模式为内部定时器 | Set frame sync mode to internal timer
result = XISLApi.Acquisition_SetFrameSyncMode(_hAcqDesc, (uint)XISLApi.HIS_SYNCMODE_INTERNAL_TIMER);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
_logger?.Warn($"设置帧同步模式失败 | Failed to set frame sync mode: {result}");
}
// 5. 获取探测器配置(分辨率等)| Get detector configuration (resolution etc.)
uint dwFrames, dwRows, dwColumns, dwDataType, dwSortFlags;
uint dwAcqType, dwSystemID, dwSyncMode, dwHwAccess;
int iIRQFlags;
result = XISLApi.Acquisition_GetConfiguration(
_hAcqDesc, out dwFrames, out dwRows, out dwColumns,
out dwDataType, out dwSortFlags, out iIRQFlags,
out dwAcqType, out dwSystemID, out dwSyncMode, out dwHwAccess);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"获取探测器配置失败 | Failed to get detector configuration: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
_logger?.Info($"探测器配置:分辨率 {dwColumns}x{dwRows},数据类型 {dwDataType} | Detector config: resolution {dwColumns}x{dwRows}, data type {dwDataType}");
// 6. 分配采集缓冲区(校正缓冲区保持 IntPtr.Zero,未校正前不可传给 API)
// Allocate acquisition buffer (correction buffers stay IntPtr.Zero until calibration is done)
int bufferSize = (int)(dwRows * dwColumns * sizeof(ushort));
_pAcqBuffer = Marshal.AllocHGlobal(bufferSize);
_logger?.Debug($"采集缓冲区已分配,大小:{bufferSize} 字节 | Acquisition buffer allocated, size: {bufferSize} bytes");
// 7. 定义目标缓冲区 | Define destination buffers
result = XISLApi.Acquisition_DefineDestBuffers(
_hAcqDesc, _pAcqBuffer, 1, dwRows, dwColumns);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"定义目标缓冲区失败 | Failed to define destination buffers: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
_logger?.Debug("目标缓冲区定义成功 | Destination buffers defined successfully");
// 9. 创建事件句柄 | Create event handles
_hevEndFrame = XISLApi.CreateEvent(IntPtr.Zero, false, false, null);
_hevEndAcq = XISLApi.CreateEvent(IntPtr.Zero, false, false, null);
_logger?.Debug("事件句柄已创建 | Event handles created");
// 10. 应用初始配置(Binning/增益/曝光时间)| Apply initial configuration (Binning/Gain/Exposure)
var configResult = ApplyConfiguration();
if (!configResult.IsSuccess)
{
_logger?.Error(null, $"应用初始配置失败 | Failed to apply initial configuration: {configResult.ErrorMessage}");
return configResult;
}
_logger?.Info("Varex 探测器初始化成功 | Varex detector initialized successfully");
return DetectorResult.Success("Varex 探测器初始化成功 | Varex detector initialized successfully");
}
catch (Exception ex)
{
var errorMsg = $"初始化异常 | Initialization exception: {ex.Message}";
_logger?.Error(ex, errorMsg);
return DetectorResult.Failure(errorMsg, ex, -1);
}
}, cancellationToken);
}
/// <summary>
/// 应用配置参数 | Apply configuration parameters
/// </summary>
private DetectorResult ApplyConfiguration()
{
try
{
// 设置 Binning 模式(API 值从 1 开始)| Set binning mode (API value starts from 1)
var result = XISLApi.Acquisition_SetCameraBinningMode(_hAcqDesc, (uint)_binningMode + 1);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"设置 Binning 模式失败 | Failed to set binning mode: {result}");
}
// 设置增益模式 | Set gain mode
result = XISLApi.Acquisition_SetCameraGain(_hAcqDesc, (uint)_gainMode);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"设置增益模式失败 | Failed to set gain mode: {result}");
}
// 设置曝光时间 | Set exposure time
uint exposureTime = _exposureTime;
result = XISLApi.Acquisition_SetTimerSync(_hAcqDesc, ref exposureTime);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"设置曝光时间失败 | Failed to set exposure time: {result}");
}
return DetectorResult.Success("配置应用成功 | Configuration applied successfully");
}
catch (Exception ex)
{
return DetectorResult.Failure($"应用配置异常 | Apply configuration exception: {ex.Message}", ex);
}
}
/// <summary>
/// 获取当前帧号 | Get current frame number
/// </summary>
private int GetCurrentFrameNumber()
{
uint dwActFrame, dwSecFrame;
XISLApi.Acquisition_GetActFrame(_hAcqDesc, out dwActFrame, out dwSecFrame);
return (int)dwActFrame;
}
/// <summary>
/// 启动连续采集内部实现 | Start continuous acquisition internal implementation
/// </summary>
/// <summary>
/// 采集前准备:获取配置、分配缓冲区、定义目标缓冲区、设置采集标志
/// Prepare for acquisition: get config, allocate buffers, define dest buffers, set acq data
/// </summary>
private DetectorResult PrepareForAcquisition(uint acqDataFlag, int targetFrames, out uint dwRows, out uint dwColumns)
{
dwRows = 0;
dwColumns = 0;
// 1. 获取当前探测器配置(Binning 改变后分辨率会变)| Get current config (resolution changes with binning)
uint dwFrames, dwDataType, dwSortFlags, dwAcqType, dwSystemID, dwSyncMode, dwHwAccess;
int iIRQFlags;
var result = XISLApi.Acquisition_GetConfiguration(
_hAcqDesc, out dwFrames, out dwRows, out dwColumns,
out dwDataType, out dwSortFlags, out iIRQFlags,
out dwAcqType, out dwSystemID, out dwSyncMode, out dwHwAccess);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"获取探测器配置失败 | Failed to get detector configuration: {result}", null, (int)result);
}
_logger?.Debug($"采集前配置:分辨率 {dwColumns}x{dwRows} | Pre-acquisition config: resolution {dwColumns}x{dwRows}");
// 缓存分辨率供回调使用 | Cache resolution for callback use
_cachedRows = dwRows;
_cachedColumns = dwColumns;
// 2. 设置采集数据标志 | Set acquisition data flag
uint acqData = acqDataFlag;
result = XISLApi.Acquisition_SetAcqData(_hAcqDesc, ref acqData);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"设置采集数据标志失败 | Failed to set acquisition data flag: {result}", null, (int)result);
}
// 3. 重新分配采集缓冲区 | Reallocate acquisition buffer
if (_pAcqBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pAcqBuffer);
}
_pAcqBuffer = Marshal.AllocHGlobal(targetFrames * (int)dwRows * (int)dwColumns * sizeof(ushort));
// 4. 定义目标缓冲区 | Define destination buffers
result = XISLApi.Acquisition_DefineDestBuffers(_hAcqDesc, _pAcqBuffer, targetFrames, dwRows, dwColumns);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"定义目标缓冲区失败 | Failed to define destination buffers: {result}", null, (int)result);
}
return DetectorResult.Success();
}
protected override Task<DetectorResult> StartAcquisitionInternalAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
try
{
_logger?.Info("开始启动连续采集 | Starting continuous acquisition");
// 检查取消令牌 | Check cancellation token
if (cancellationToken.IsCancellationRequested)
{
_logger?.Warn("启动采集操作已取消 | Start acquisition operation cancelled");
return DetectorResult.Failure("操作已取消 | Operation cancelled");
}
lock (_acquisitionLock)
{
// 防止重复启动 | Prevent duplicate start
if (_isAcquiring)
{
var errorMsg = "采集已在进行中,无法重复启动 | Acquisition already in progress, cannot start again";
_logger?.Warn(errorMsg);
return DetectorResult.Failure(errorMsg);
}
// 确保之前的 CancellationTokenSource 已清理 | Ensure previous CancellationTokenSource is cleaned up
if (_acquisitionCts != null)
{
_logger?.Debug("清理之前的 CancellationTokenSource | Cleaning up previous CancellationTokenSource");
try
{
_acquisitionCts.Cancel();
_acquisitionCts.Dispose();
}
catch (Exception ex)
{
_logger?.Warn($"清理 CancellationTokenSource 时发生异常 | Exception while cleaning CancellationTokenSource: {ex.Message}");
}
_acquisitionCts = null;
}
_isAcquiring = true;
_acquisitionCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
}
// 再次检查取消令牌 | Check cancellation token again
if (cancellationToken.IsCancellationRequested)
{
lock (_acquisitionLock)
{
_isAcquiring = false;
_acquisitionCts?.Dispose();
_acquisitionCts = null;
}
_logger?.Warn("启动采集操作已取消 | Start acquisition operation cancelled");
return DetectorResult.Failure("操作已取消 | Operation cancelled");
}
// 启动连续采集前准备 | Prepare before starting continuous acquisition
var prepResult = PrepareForAcquisition(XISLApi.ACQ_SNAP, 1, out uint dwRows, out uint dwColumns);
if (!prepResult.IsSuccess)
{
lock (_acquisitionLock)
{
_isAcquiring = false;
_acquisitionCts?.Dispose();
_acquisitionCts = null;
}
_logger?.Error(null, $"采集准备失败 | Acquisition preparation failed: {prepResult.ErrorMessage}");
return prepResult;
}
// 启动连续采集 | Start continuous acquisition
var result = XISLApi.Acquisition_Acquire_Image(
_hAcqDesc,
1, // 帧数 | Frame count
0, // 跳过帧数 | Skip frames
(uint)XISLApi.HIS_SEQ_CONTINUOUS,
_pOffsetBuffer, // 未校正时为 IntPtr.Zero | IntPtr.Zero before calibration
_pGainAvgBuffer, // 未校正时为 IntPtr.Zero,校正后为增益映射 | IntPtr.Zero before calibration, gain map after
_pCorrList); // 未校正时为 IntPtr.Zero | IntPtr.Zero before calibration
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
lock (_acquisitionLock)
{
_isAcquiring = false;
_acquisitionCts?.Dispose();
_acquisitionCts = null;
}
var errorMsg = $"启动采集失败 | Failed to start acquisition: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
_logger?.Info("连续采集已启动 | Continuous acquisition started");
return DetectorResult.Success("采集已启动 | Acquisition started");
}
catch (OperationCanceledException)
{
lock (_acquisitionLock)
{
_isAcquiring = false;
_acquisitionCts?.Dispose();
_acquisitionCts = null;
}
_logger?.Warn("启动采集操作已取消 | Start acquisition operation cancelled");
return DetectorResult.Failure("操作已取消 | Operation cancelled");
}
catch (Exception ex)
{
lock (_acquisitionLock)
{
_isAcquiring = false;
_acquisitionCts?.Dispose();
_acquisitionCts = null;
}
var errorMsg = $"启动采集异常 | Start acquisition exception: {ex.Message}";
_logger?.Error(ex, errorMsg);
return DetectorResult.Failure(errorMsg, ex, -1);
}
}, cancellationToken);
}
/// <summary>
/// 停止采集内部实现 | Stop acquisition internal implementation
/// </summary>
protected override Task<DetectorResult> StopAcquisitionInternalAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
try
{
_logger?.Info("开始停止采集 | Starting to stop acquisition");
// 检查取消令牌 | Check cancellation token
if (cancellationToken.IsCancellationRequested)
{
_logger?.Warn("停止采集操作已取消 | Stop acquisition operation cancelled");
return DetectorResult.Failure("操作已取消 | Operation cancelled");
}
lock (_acquisitionLock)
{
// 幂等性检查:如果未在采集,直接返回成功 | Idempotency check: if not acquiring, return success
if (!_isAcquiring)
{
_logger?.Debug("采集未运行,无需停止 | Acquisition not running, no need to stop");
return DetectorResult.Success("采集未运行 | Acquisition not running");
}
}
// 停止采集 | Stop acquisition
var result = XISLApi.Acquisition_Abort(_hAcqDesc);
_logger?.Debug($"调用 Acquisition_Abort,返回码:{result} | Called Acquisition_Abort, return code: {result}");
// 等待采集结束事件,支持取消令牌 | Wait for acquisition end event, support cancellation token
const int timeoutMs = 5000; // 5 秒超时 | 5 seconds timeout
const int pollIntervalMs = 100; // 轮询间隔 | Poll interval
int elapsedMs = 0;
while (elapsedMs < timeoutMs)
{
// 检查取消令牌 | Check cancellation token
if (cancellationToken.IsCancellationRequested)
{
_logger?.Warn("停止采集操作已取消,但仍会清理资源 | Stop acquisition operation cancelled, but will still clean up resources");
break;
}
uint waitResult = XISLApi.WaitForSingleObject(_hevEndAcq, (uint)pollIntervalMs);
if (waitResult == XISLApi.WAIT_OBJECT_0)
{
// 成功等到事件 | Successfully waited for event
break;
}
elapsedMs += pollIntervalMs;
}
// 清理资源 | Clean up resources
lock (_acquisitionLock)
{
_isAcquiring = false;
if (_acquisitionCts != null)
{
try
{
_acquisitionCts.Cancel();
_acquisitionCts.Dispose();
}
catch (Exception ex)
{
_logger?.Warn($"清理 CancellationTokenSource 时发生异常 | Exception while cleaning CancellationTokenSource: {ex.Message}");
}
_acquisitionCts = null;
}
}
// 检查是否超时 | Check if timeout occurred
if (elapsedMs >= timeoutMs && !cancellationToken.IsCancellationRequested)
{
var errorMsg = $"停止采集超时({timeoutMs}ms),但资源已清理 | Stop acquisition timeout ({timeoutMs}ms), but resources cleaned up";
_logger?.Warn(errorMsg);
return DetectorResult.Failure(errorMsg, null, -2);
}
if (cancellationToken.IsCancellationRequested)
{
_logger?.Info("停止采集操作已取消,资源已清理 | Stop acquisition operation cancelled, resources cleaned up");
return DetectorResult.Failure("操作已取消 | Operation cancelled");
}
// 等待硬件完全释放采集资源,避免后续操作出现 HIS_ERROR_TIMEOUT
// Wait for hardware to fully release acquisition resources to avoid HIS_ERROR_TIMEOUT on subsequent operations
Thread.Sleep(200);
_logger?.Info("采集已停止 | Acquisition stopped");
return DetectorResult.Success("采集已停止 | Acquisition stopped");
}
catch (OperationCanceledException)
{
// 确保资源清理 | Ensure resource cleanup
lock (_acquisitionLock)
{
_isAcquiring = false;
_acquisitionCts?.Dispose();
_acquisitionCts = null;
}
_logger?.Warn("停止采集操作已取消 | Stop acquisition operation cancelled");
return DetectorResult.Failure("操作已取消 | Operation cancelled");
}
catch (Exception ex)
{
// 确保资源清理 | Ensure resource cleanup
lock (_acquisitionLock)
{
_isAcquiring = false;
_acquisitionCts?.Dispose();
_acquisitionCts = null;
}
var errorMsg = $"停止采集异常 | Stop acquisition exception: {ex.Message}";
_logger?.Error(ex, errorMsg);
return DetectorResult.Failure(errorMsg, ex, -1);
}
}, cancellationToken);
}
/// <summary>
/// 单帧采集内部实现 | Single frame acquisition internal implementation
/// </summary>
protected override Task<DetectorResult> AcquireSingleFrameInternalAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
try
{
// 单帧采集前准备(含重试机制,防止连续采集停止后硬件未完全释放)
// Prepare before single frame acquisition (with retry to handle hardware not fully released after continuous acquisition)
DetectorResult prepResult = null;
const int maxRetries = 3;
const int retryDelayMs = 300;
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
prepResult = PrepareForAcquisition(XISLApi.ACQ_SNAP, 1, out uint dwRows, out uint dwColumns);
if (prepResult.IsSuccess)
{
// 采集单帧 | Acquire single frame
var result = XISLApi.Acquisition_Acquire_Image(
_hAcqDesc,
1, // 采集 1 帧 | Acquire 1 frame
0, // 跳过帧数 | Skip frames
(uint)XISLApi.HIS_SEQ_ONE_BUFFER,
_pOffsetBuffer,
_pGainAvgBuffer,
_pCorrList);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"单帧采集失败 | Failed to acquire single frame: {result}");
}
// 等待帧结束事件 | Wait for frame end event
uint waitResult = XISLApi.WaitForSingleObject(_hevEndFrame, 5000);
if (waitResult != XISLApi.WAIT_OBJECT_0)
{
return DetectorResult.Failure("单帧采集超时 | Single frame acquisition timeout");
}
return DetectorResult.Success("单帧采集成功 | Single frame acquired successfully");
}
// 准备失败,等待后重试 | Preparation failed, wait and retry
if (attempt < maxRetries)
{
_logger?.Warn($"单帧采集准备失败(第 {attempt} 次),{retryDelayMs}ms 后重试 | " +
$"Single frame preparation failed (attempt {attempt}), retrying in {retryDelayMs}ms: {prepResult.ErrorMessage}");
Thread.Sleep(retryDelayMs);
}
}
// 所有重试均失败 | All retries failed
_logger?.Error(null, $"单帧采集准备失败(已重试 {maxRetries} 次)| Single frame preparation failed after {maxRetries} retries: {prepResult?.ErrorMessage}");
return prepResult ?? DetectorResult.Failure("单帧采集准备失败 | Single frame preparation failed");
}
catch (Exception ex)
{
return DetectorResult.Failure($"单帧采集异常 | Single frame acquisition exception: {ex.Message}", ex);
}
}, cancellationToken);
}
/// <summary>
/// 暗场校正内部实现 | Dark correction internal implementation
/// </summary>
protected override Task<DetectorResult> DarkCorrectionInternalAsync(int frameCount, CancellationToken cancellationToken)
{
return Task.Run(() =>
{
try
{
_logger?.Info($"开始暗场校正,帧数:{frameCount} | Starting dark correction, frame count: {frameCount}");
// 1. 获取探测器配置信息 | Get detector configuration
uint dwFrames, dwRows, dwColumns, dwDataType, dwSortFlags;
uint dwAcqType, dwSystemID, dwSyncMode, dwHwAccess;
int iIRQFlags;
var result = XISLApi.Acquisition_GetConfiguration(
_hAcqDesc, out dwFrames, out dwRows, out dwColumns,
out dwDataType, out dwSortFlags, out iIRQFlags,
out dwAcqType, out dwSystemID, out dwSyncMode, out dwHwAccess);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"获取探测器配置失败 | Failed to get detector configuration: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
// 2. 分配暗场缓冲区(如果尚未分配或大小不匹配)| Allocate offset buffer if not allocated or size mismatch
int requiredSize = (int)(dwRows * dwColumns) * sizeof(ushort);
if (_pOffsetBuffer == IntPtr.Zero)
{
_pOffsetBuffer = Marshal.AllocHGlobal(requiredSize);
_logger?.Debug($"已分配暗场缓冲区,大小:{requiredSize} 字节 | Allocated offset buffer, size: {requiredSize} bytes");
}
// 3. 调用 XISL API 采集暗场图像 | Call XISL API to acquire offset image
_logger?.Debug($"调用 Acquisition_Acquire_OffsetImage,分辨率:{dwColumns}x{dwRows} | Calling Acquisition_Acquire_OffsetImage, resolution: {dwColumns}x{dwRows}");
result = XISLApi.Acquisition_Acquire_OffsetImage(
_hAcqDesc, _pOffsetBuffer, dwRows, dwColumns, (uint)frameCount);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"采集暗场图像失败 | Failed to acquire offset image: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
// 3. 等待采集完成 | Wait for acquisition to complete
uint waitResult = XISLApi.WaitForSingleObject(_hevEndAcq, 60000); // 60 秒超时
if (waitResult != XISLApi.WAIT_OBJECT_0)
{
var errorMsg = $"等待暗场采集完成超时 | Timeout waiting for offset acquisition: {waitResult}";
_logger?.Warn(errorMsg);
return DetectorResult.Failure(errorMsg, null, (int)waitResult);
}
// 4. 保存暗场数据到配置的存储路径 | Save offset data to configured storage path
if (_config.AutoSave && !string.IsNullOrEmpty(_config.SavePath))
{
try
{
string offsetFilePath = System.IO.Path.Combine(_config.SavePath, $"offset_{DateTime.Now:yyyyMMdd_HHmmss}.raw");
System.IO.Directory.CreateDirectory(_config.SavePath);
int bufferSize = (int)(dwRows * dwColumns * sizeof(ushort));
byte[] offsetData = new byte[bufferSize];
Marshal.Copy(_pOffsetBuffer, offsetData, 0, bufferSize);
System.IO.File.WriteAllBytes(offsetFilePath, offsetData);
_logger?.Info($"暗场数据已保存到:{offsetFilePath} | Offset data saved to: {offsetFilePath}");
}
catch (Exception ex)
{
// 保存失败不影响校正结果,仅记录错误 | Save failure doesn't affect correction result, just log error
var errorMsg = $"保存暗场数据失败 | Failed to save offset data: {ex.Message}";
_logger?.Warn(errorMsg);
PublishError(DetectorResult.Failure(errorMsg, ex));
}
}
// 5. 发布校正完成事件 | Publish correction completed event
var correctionResult = DetectorResult.Success("暗场校正完成 | Dark correction completed");
PublishCorrectionCompleted(CorrectionType.Dark, correctionResult);
_logger?.Info("暗场校正完成 | Dark correction completed");
return correctionResult;
}
catch (Exception ex)
{
var errorMsg = $"暗场校正异常 | Dark correction exception: {ex.Message}";
_logger?.Error(ex, errorMsg);
var errorResult = DetectorResult.Failure(errorMsg, ex, -1);
PublishError(errorResult);
return errorResult;
}
}, cancellationToken);
}
/// <summary>
/// 增益校正内部实现 | Gain correction internal implementation
/// </summary>
protected override Task<DetectorResult> GainCorrectionInternalAsync(int frameCount, CancellationToken cancellationToken)
{
return Task.Run(() =>
{
try
{
_logger?.Info($"开始增益校正,帧数:{frameCount} | Starting gain correction, frame count: {frameCount}");
// 1. 获取探测器配置信息 | Get detector configuration
uint dwFrames, dwRows, dwColumns, dwDataType, dwSortFlags;
uint dwAcqType, dwSystemID, dwSyncMode, dwHwAccess;
int iIRQFlags;
var result = XISLApi.Acquisition_GetConfiguration(
_hAcqDesc, out dwFrames, out dwRows, out dwColumns,
out dwDataType, out dwSortFlags, out iIRQFlags,
out dwAcqType, out dwSystemID, out dwSyncMode, out dwHwAccess);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"获取探测器配置失败 | Failed to get detector configuration: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
// 2. 分配增益采集缓冲区(ushort,用于 Acquire_GainImage 输入)| Allocate gain acquisition buffer (ushort, for Acquire_GainImage)
int gainAcqSize = (int)(dwRows * dwColumns) * sizeof(ushort);
if (_pGainBuffer == IntPtr.Zero)
{
_pGainBuffer = Marshal.AllocHGlobal(gainAcqSize);
_logger?.Debug($"已分配增益采集缓冲区,大小:{gainAcqSize} 字节 | Allocated gain acquisition buffer, size: {gainAcqSize} bytes");
}
// 分配增益映射输出缓冲区(uint/DWORD,用于 CreateGainMap 输出)| Allocate gain map output buffer (uint/DWORD, for CreateGainMap)
int gainAvgSize = (int)(dwRows * dwColumns) * sizeof(uint);
if (_pGainAvgBuffer == IntPtr.Zero)
{
_pGainAvgBuffer = Marshal.AllocHGlobal(gainAvgSize);
_logger?.Debug($"已分配增益映射缓冲区,大小:{gainAvgSize} 字节 | Allocated gain map buffer, size: {gainAvgSize} bytes");
}
// 3. 调用 XISL API 采集增益图像 | Call XISL API to acquire gain image
_logger?.Debug($"调用 Acquisition_Acquire_GainImage,分辨率:{dwColumns}x{dwRows} | Calling Acquisition_Acquire_GainImage, resolution: {dwColumns}x{dwRows}");
result = XISLApi.Acquisition_Acquire_GainImage(
_hAcqDesc, _pOffsetBuffer, _pGainBuffer, dwRows, dwColumns, (uint)frameCount);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"采集增益图像失败 | Failed to acquire gain image: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
// 3. 等待采集完成 | Wait for acquisition to complete
uint waitResult = XISLApi.WaitForSingleObject(_hevEndAcq, 60000); // 60 秒超时
if (waitResult != XISLApi.WAIT_OBJECT_0)
{
var errorMsg = $"等待增益采集完成超时 | Timeout waiting for gain acquisition: {waitResult}";
_logger?.Warn(errorMsg);
return DetectorResult.Failure(errorMsg, null, (int)waitResult);
}
// 4. 创建增益映射(输入为采集的 ushort 数据,输出为 DWORD 映射)| Create gain map (input: ushort data, output: DWORD map)
_logger?.Debug("创建增益映射 | Creating gain map");
result = XISLApi.Acquisition_CreateGainMap(_pGainBuffer, _pGainAvgBuffer, (int)(dwRows * dwColumns), frameCount);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
var errorMsg = $"创建增益映射失败 | Failed to create gain map: {result}";
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
return DetectorResult.Failure(errorMsg, null, (int)result);
}
// 5. 保存增益数据到配置的存储路径 | Save gain data to configured storage path
if (_config.AutoSave && !string.IsNullOrEmpty(_config.SavePath))
{
try
{
string gainFilePath = System.IO.Path.Combine(_config.SavePath, $"gain_{DateTime.Now:yyyyMMdd_HHmmss}.raw");
System.IO.Directory.CreateDirectory(_config.SavePath);
int bufferSize = (int)(dwRows * dwColumns * sizeof(uint));
byte[] gainData = new byte[bufferSize];
Marshal.Copy(_pGainAvgBuffer, gainData, 0, bufferSize);
System.IO.File.WriteAllBytes(gainFilePath, gainData);
_logger?.Info($"增益数据已保存到:{gainFilePath} | Gain data saved to: {gainFilePath}");
}
catch (Exception ex)
{
// 保存失败不影响校正结果,仅记录错误 | Save failure doesn't affect correction result, just log error
var errorMsg = $"保存增益数据失败 | Failed to save gain data: {ex.Message}";
_logger?.Warn(errorMsg);
PublishError(DetectorResult.Failure(errorMsg, ex));
}
}
// 6. 发布校正完成事件 | Publish correction completed event
var correctionResult = DetectorResult.Success("增益校正完成 | Gain correction completed");
PublishCorrectionCompleted(CorrectionType.Gain, correctionResult);
_logger?.Info("增益校正完成 | Gain correction completed");
return correctionResult;
}
catch (Exception ex)
{
var errorMsg = $"增益校正异常 | Gain correction exception: {ex.Message}";
_logger?.Error(ex, errorMsg);
var errorResult = DetectorResult.Failure(errorMsg, ex, -1);
PublishError(errorResult);
return errorResult;
}
}, cancellationToken);
}
/// <summary>
/// 自动校正内部实现 | Auto correction internal implementation
/// </summary>
protected override async Task<DetectorResult> AutoCorrectionInternalAsync(int frameCount, CancellationToken cancellationToken)
{
try
{
_logger?.Info($"开始自动校正,帧数:{frameCount} | Starting auto correction, frame count: {frameCount}");
// 1. 执行暗场校正 | Execute dark correction
_logger?.Debug("执行暗场校正 | Executing dark correction");
var darkResult = await DarkCorrectionInternalAsync(frameCount, cancellationToken);
if (!darkResult.IsSuccess)
{
var errorMsg = $"自动校正失败:暗场校正失败 | Auto correction failed: dark correction failed - {darkResult.ErrorMessage}";
_logger?.Error(null, errorMsg);
return DetectorResult.Failure(errorMsg, darkResult.Exception, darkResult.ErrorCode);
}
// 2. 执行增益校正 | Execute gain correction
_logger?.Debug("执行增益校正 | Executing gain correction");
var gainResult = await GainCorrectionInternalAsync(frameCount, cancellationToken);
if (!gainResult.IsSuccess)
{
var errorMsg = $"自动校正失败:增益校正失败 | Auto correction failed: gain correction failed - {gainResult.ErrorMessage}";
_logger?.Error(null, errorMsg);
return DetectorResult.Failure(errorMsg, gainResult.Exception, gainResult.ErrorCode);
}
// 3. 执行坏像素校正 | Execute bad pixel correction
_logger?.Debug("执行坏像素校正 | Executing bad pixel correction");
var badPixelResult = await BadPixelCorrectionInternalAsync(cancellationToken);
if (!badPixelResult.IsSuccess)
{
var errorMsg = $"自动校正失败:坏像素校正失败 | Auto correction failed: bad pixel correction failed - {badPixelResult.ErrorMessage}";
_logger?.Error(null, errorMsg);
return DetectorResult.Failure(errorMsg, badPixelResult.Exception, badPixelResult.ErrorCode);
}
// 4. 发布自动校正完成事件 | Publish auto correction completed event
var correctionResult = DetectorResult.Success("自动校正完成 | Auto correction completed");
PublishCorrectionCompleted(CorrectionType.Auto, correctionResult);
_logger?.Info("自动校正完成 | Auto correction completed");
return correctionResult;
}
catch (Exception ex)
{
var errorMsg = $"自动校正异常 | Auto correction exception: {ex.Message}";
_logger?.Error(ex, errorMsg);
var errorResult = DetectorResult.Failure(errorMsg, ex, -1);
PublishError(errorResult);
return errorResult;
}
}
/// <summary>
/// 坏像素校正内部实现 | Bad pixel correction internal implementation
/// </summary>
protected override Task<DetectorResult> BadPixelCorrectionInternalAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
try
{
// 1. 获取探测器配置信息 | Get detector configuration
uint dwFrames, dwRows, dwColumns, dwDataType, dwSortFlags;
uint dwAcqType, dwSystemID, dwSyncMode, dwHwAccess;
int iIRQFlags;
var result = XISLApi.Acquisition_GetConfiguration(
_hAcqDesc, out dwFrames, out dwRows, out dwColumns,
out dwDataType, out dwSortFlags, out iIRQFlags,
out dwAcqType, out dwSystemID, out dwSyncMode, out dwHwAccess);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
return DetectorResult.Failure($"获取探测器配置失败 | Failed to get detector configuration: {result}");
}
// 2. 分配坏像素映射数据缓冲区 | Allocate bad pixel map data buffer
int pixelMapSize = (int)(dwRows * dwColumns);
IntPtr pPixelMapData = Marshal.AllocHGlobal(pixelMapSize * sizeof(ushort));
try
{
// 3. 第一次调用获取列表大小 | First call to get list size
int corrListSize = 0;
result = XISLApi.Acquisition_CreatePixelMap(
pPixelMapData, (int)dwRows, (int)dwColumns, IntPtr.Zero, ref corrListSize);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK && corrListSize == 0)
{
Marshal.FreeHGlobal(pPixelMapData);
return DetectorResult.Failure($"获取坏像素列表大小失败 | Failed to get bad pixel list size: {result}");
}
// 4. 分配坏像素校正列表缓冲区 | Allocate bad pixel correction list buffer
if (_pCorrList != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pCorrList);
}
_pCorrList = Marshal.AllocHGlobal(corrListSize);
_logger?.Debug($"已分配坏像素校正列表缓冲区,大小:{corrListSize} 字节 | Allocated bad pixel correction list buffer, size: {corrListSize} bytes");
// 5. 第二次调用创建坏像素列表 | Second call to create bad pixel list
result = XISLApi.Acquisition_CreatePixelMap(
pPixelMapData, (int)dwRows, (int)dwColumns, _pCorrList, ref corrListSize);
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
{
Marshal.FreeHGlobal(pPixelMapData);
return DetectorResult.Failure($"创建坏像素列表失败 | Failed to create bad pixel list: {result}");
}
// 5. 保存坏像素数据到配置的存储路径 | Save bad pixel data to configured storage path
if (_config.AutoSave && !string.IsNullOrEmpty(_config.SavePath))
{
try
{
string badPixelFilePath = System.IO.Path.Combine(_config.SavePath, $"badpixel_{DateTime.Now:yyyyMMdd_HHmmss}.raw");
System.IO.Directory.CreateDirectory(_config.SavePath);
byte[] badPixelData = new byte[corrListSize];
Marshal.Copy(_pCorrList, badPixelData, 0, corrListSize);
System.IO.File.WriteAllBytes(badPixelFilePath, badPixelData);
}
catch (Exception ex)
{
// 保存失败不影响校正结果,仅记录错误 | Save failure doesn't affect correction result, just log error
PublishError(DetectorResult.Failure($"保存坏像素数据失败 | Failed to save bad pixel data: {ex.Message}", ex));
}
}
// 6. 发布校正完成事件 | Publish correction completed event
var correctionResult = DetectorResult.Success($"坏像素校正完成,检测到 {corrListSize} 个坏像素 | Bad pixel correction completed, detected {corrListSize} bad pixels");
PublishCorrectionCompleted(CorrectionType.BadPixel, correctionResult);
return correctionResult;
}
finally
{
// 释放临时缓冲区 | Release temporary buffer
Marshal.FreeHGlobal(pPixelMapData);
}
}
catch (Exception ex)
{
var errorResult = DetectorResult.Failure($"坏像素校正异常 | Bad pixel correction exception: {ex.Message}", ex);
PublishError(errorResult);
return errorResult;
}
}, cancellationToken);
}
/// <summary>
/// 获取探测器信息 | Get detector information
/// </summary>
public override DetectorInfo GetInfo()
{
try
{
var info = new DetectorInfo
{
Type = DetectorType.Varex,
Model = "Varex 4343N", // 默认型号 | Default model
SerialNumber = "Unknown", // 序列号需要从硬件查询 | Serial number needs to be queried from hardware
FirmwareVersion = "Unknown", // 固件版本需要从硬件查询 | Firmware version needs to be queried from hardware
BitDepth = 16 // Varex 探测器使用 16 位数据 | Varex detector uses 16-bit data
};
// 如果探测器已初始化,从 XISL API 获取配置信息 | If detector is initialized, get configuration from XISL API
if (_hAcqDesc != IntPtr.Zero)
{
uint dwFrames, dwRows, dwColumns, dwDataType, dwSortFlags;
uint dwAcqType, dwSystemID, dwSyncMode, dwHwAccess;
int iIRQFlags;
var result = XISLApi.Acquisition_GetConfiguration(
_hAcqDesc, out dwFrames, out dwRows, out dwColumns,
out dwDataType, out dwSortFlags, out iIRQFlags,
out dwAcqType, out dwSystemID, out dwSyncMode, out dwHwAccess);
if (result == XISLApi.HIS_RETURN.HIS_ALL_OK)
{
info.MaxWidth = dwColumns;
info.MaxHeight = dwRows;
// 根据型号设置像素尺寸 | Set pixel size based on model
// Varex 4343N 的像素尺寸为 139 微米 | Varex 4343N pixel size is 139 micrometers
info.PixelSize = 139.0;
// 根据 SystemID 确定具体型号 | Determine specific model based on SystemID
info.Model = dwSystemID switch
{
_ => "Varex 4343N" // 默认型号 | Default model
};
}
else
{
// 如果无法获取配置,使用默认值 | If configuration cannot be obtained, use default values
info.MaxWidth = 2880;
info.MaxHeight = 2880;
info.PixelSize = 139.0;
}
}
else
{
// 探测器未初始化,使用默认值 | Detector not initialized, use default values
info.MaxWidth = 2880;
info.MaxHeight = 2880;
info.PixelSize = 139.0;
}
return info;
}
catch (Exception ex)
{
// 发生异常时返回基本信息 | Return basic information when exception occurs
return new DetectorInfo
{
Type = DetectorType.Varex,
Model = "Varex (Error)",
SerialNumber = $"Error: {ex.Message}",
FirmwareVersion = "Unknown",
MaxWidth = 2880,
MaxHeight = 2880,
PixelSize = 139.0,
BitDepth = 16
};
}
}
#endregion
#region IVarexDetector | IVarexDetector Interface Implementations (Placeholders)
/// <summary>
/// 设置 Binning 模式 | Set binning mode
/// </summary>
public Task<DetectorResult> SetBinningModeAsync(BinningMode mode)
{
return Task.Run(() =>
{
try
{
var result = XISLApi.Acquisition_SetCameraBinningMode(_hAcqDesc, (uint)mode + 1);
if (result == XISLApi.HIS_RETURN.HIS_ALL_OK)
{
_binningMode = mode;
return DetectorResult.Success($"Binning 模式已设置为 {mode} | Binning mode set to {mode}");
}
return DetectorResult.Failure($"设置 Binning 模式失败 | Failed to set binning mode: {result}");
}
catch (Exception ex)
{
return DetectorResult.Failure($"设置 Binning 模式异常 | Set binning mode exception: {ex.Message}", ex);
}
});
}
/// <summary>
/// 获取 Binning 模式 | Get binning mode
/// </summary>
public BinningMode GetBinningMode()
{
return _binningMode;
}
/// <summary>
/// 设置增益模式 | Set gain mode
/// </summary>
public Task<DetectorResult> SetGainModeAsync(GainMode mode)
{
return Task.Run(() =>
{
try
{
var result = XISLApi.Acquisition_SetCameraGain(_hAcqDesc, (uint)mode);
if (result == XISLApi.HIS_RETURN.HIS_ALL_OK)
{
_gainMode = mode;
return DetectorResult.Success($"增益模式已设置为 {mode} | Gain mode set to {mode}");
}
return DetectorResult.Failure($"设置增益模式失败 | Failed to set gain mode: {result}");
}
catch (Exception ex)
{
return DetectorResult.Failure($"设置增益模式异常 | Set gain mode exception: {ex.Message}", ex);
}
});
}
/// <summary>
/// 获取增益模式 | Get gain mode
/// </summary>
public GainMode GetGainMode()
{
return _gainMode;
}
/// <summary>
/// 设置曝光时间 | Set exposure time
/// </summary>
public Task<DetectorResult> SetExposureTimeAsync(uint milliseconds)
{
return Task.Run(() =>
{
try
{
uint exposureTime = milliseconds;
var result = XISLApi.Acquisition_SetTimerSync(_hAcqDesc, ref exposureTime);
if (result == XISLApi.HIS_RETURN.HIS_ALL_OK)
{
_exposureTime = milliseconds;
return DetectorResult.Success($"曝光时间已设置为 {milliseconds} ms | Exposure time set to {milliseconds} ms");
}
return DetectorResult.Failure($"设置曝光时间失败 | Failed to set exposure time: {result}");
}
catch (Exception ex)
{
return DetectorResult.Failure($"设置曝光时间异常 | Set exposure time exception: {ex.Message}", ex);
}
});
}
/// <summary>
/// 获取曝光时间 | Get exposure time
/// </summary>
public uint GetExposureTime()
{
return _exposureTime;
}
/// <summary>
/// 设置 ROI 区域 | Set ROI region
/// </summary>
public Task<DetectorResult> SetROIAsync(uint x, uint y, uint width, uint height)
{
return Task.Run(() =>
{
try
{
// 注意:XISL API 可能需要特定的 ROI 设置函数
// 这里暂时只更新内部状态,实际 API 调用需要根据 XISL 文档补充
// Note: XISL API may require specific ROI setting functions
// Currently only updating internal state, actual API call needs to be added based on XISL documentation
_roi = (x, y, width, height);
return DetectorResult.Success($"ROI 区域已设置 | ROI region set: ({x}, {y}, {width}x{height})");
}
catch (Exception ex)
{
return DetectorResult.Failure($"设置 ROI 区域异常 | Set ROI region exception: {ex.Message}", ex);
}
});
}
/// <summary>
/// 获取 ROI 区域 | Get ROI region
/// </summary>
public (uint x, uint y, uint width, uint height) GetROI()
{
return _roi;
}
#endregion
#region | Callback Functions (Placeholders)
/// <summary>
/// 帧结束回调函数 | End frame callback
/// </summary>
/// <param name="hAcqDesc">采集描述符句柄 | Acquisition descriptor handle</param>
private void OnEndFrameCallback(IntPtr hAcqDesc)
{
try
{
uint dwRows = _cachedRows;
uint dwColumns = _cachedColumns;
int imageSize = (int)(dwRows * dwColumns);
if (imageSize <= 0) return;
// 分配独立数组(由消费者负责生命周期,避免 ArrayPool 竞态)
// Allocate independent array (consumer owns lifetime, avoids ArrayPool race condition)
ushort[] buffer = new ushort[imageSize];
// 拷贝图像数据 | Copy image data
unsafe
{
fixed (ushort* pDest = buffer)
{
Buffer.MemoryCopy(
(void*)_pAcqBuffer,
pDest,
imageSize * sizeof(ushort),
imageSize * sizeof(ushort));
}
}
// 获取当前帧号 | Get current frame number
int frameNumber = GetCurrentFrameNumber();
// 创建事件参数 | Create event arguments
var eventArgs = new ImageCapturedEventArgs
{
ImageData = buffer,
Width = dwColumns,
Height = dwRows,
FrameNumber = frameNumber,
CaptureTime = DateTime.Now,
ExposureTime = _exposureTime,
SavePath = _config.SavePath
};
// 发布图像采集事件 | Publish image captured event
PublishImageCaptured(eventArgs);
_logger?.Debug($"图像采集完成,帧号:{frameNumber},分辨率:{dwColumns}x{dwRows} | Image captured, frame: {frameNumber}, resolution: {dwColumns}x{dwRows}");
// 设置帧结束事件 | Set frame end event
if (_hevEndFrame != IntPtr.Zero)
{
XISLApi.SetEvent(_hevEndFrame);
}
}
catch (Exception ex)
{
var errorMsg = $"回调函数异常 | Callback exception: {ex.Message}";
_logger?.Error(ex, errorMsg);
var errorResult = DetectorResult.Failure(errorMsg, ex, -1);
PublishError(errorResult);
}
}
/// <summary>
/// 采集结束回调函数 | End acquisition callback
/// </summary>
/// <param name="hAcqDesc">采集描述符句柄 | Acquisition descriptor handle</param>
private void OnEndAcqCallback(IntPtr hAcqDesc)
{
try
{
// 重置板载选项 | Reset onboard options
XISLApi.Acquisition_Reset_OnboardOptions(hAcqDesc);
// 设置采集结束事件 | Set acquisition end event
if (_hevEndAcq != IntPtr.Zero)
{
XISLApi.SetEvent(_hevEndAcq);
}
}
catch (Exception ex)
{
var errorResult = DetectorResult.Failure($"采集结束回调异常 | End acquisition callback exception: {ex.Message}", ex);
PublishError(errorResult);
}
}
#endregion
#region | Resource Disposal
/// <summary>
/// 释放资源 | Dispose resources
/// </summary>
/// <param name="disposing">是否释放托管资源 | Whether to dispose managed resources</param>
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_logger?.Info("开始释放 VarexDetector 资源 | Starting to dispose VarexDetector resources");
if (disposing)
{
// 释放托管资源 | Release managed resources
try
{
// 取消并释放采集取消令牌源 | Cancel and dispose acquisition cancellation token source
_acquisitionCts?.Cancel();
_acquisitionCts?.Dispose();
_acquisitionCts = null;
_logger?.Debug("采集取消令牌源已释放 | Acquisition cancellation token source disposed");
}
catch (Exception ex)
{
// 记录异常但继续释放其他资源 | Log exception but continue disposing other resources
_logger?.Warn($"释放 CancellationTokenSource 时发生异常 | Exception disposing CancellationTokenSource: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"释放 CancellationTokenSource 时发生异常 | Exception disposing CancellationTokenSource: {ex.Message}");
}
}
// 释放非托管资源 | Release unmanaged resources
try
{
// 停止采集 | Stop acquisition
if (_hAcqDesc != IntPtr.Zero && _isAcquiring)
{
XISLApi.Acquisition_Abort(_hAcqDesc);
_isAcquiring = false;
_logger?.Debug("采集已停止 | Acquisition stopped");
}
// 关闭所有连接 | Close all connections
if (_hAcqDesc != IntPtr.Zero)
{
XISLApi.Acquisition_CloseAll();
_hAcqDesc = IntPtr.Zero;
_logger?.Debug("XISL 连接已关闭 | XISL connections closed");
}
}
catch (Exception ex)
{
_logger?.Warn($"关闭 XISL 连接时发生异常 | Exception closing XISL connections: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"关闭 XISL 连接时发生异常 | Exception closing XISL connections: {ex.Message}");
}
// 释放图像缓冲区 | Free image buffers
try
{
if (_pAcqBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pAcqBuffer);
_pAcqBuffer = IntPtr.Zero;
}
if (_pOffsetBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pOffsetBuffer);
_pOffsetBuffer = IntPtr.Zero;
}
if (_pGainBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pGainBuffer);
_pGainBuffer = IntPtr.Zero;
}
if (_pGainAvgBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pGainAvgBuffer);
_pGainAvgBuffer = IntPtr.Zero;
}
if (_pCorrList != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pCorrList);
_pCorrList = IntPtr.Zero;
}
_logger?.Debug("图像缓冲区已释放 | Image buffers freed");
}
catch (Exception ex)
{
_logger?.Warn($"释放缓冲区时发生异常 | Exception freeing buffers: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"释放缓冲区时发生异常 | Exception freeing buffers: {ex.Message}");
}
// 释放事件句柄 | Free event handles
try
{
if (_hevEndFrame != IntPtr.Zero)
{
XISLApi.CloseHandle(_hevEndFrame);
_hevEndFrame = IntPtr.Zero;
}
if (_hevEndAcq != IntPtr.Zero)
{
XISLApi.CloseHandle(_hevEndAcq);
_hevEndAcq = IntPtr.Zero;
}
_logger?.Debug("事件句柄已释放 | Event handles freed");
}
catch (Exception ex)
{
_logger?.Warn($"释放事件句柄时发生异常 | Exception freeing event handles: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"释放事件句柄时发生异常 | Exception freeing event handles: {ex.Message}");
}
_logger?.Info("VarexDetector 资源释放完成 | VarexDetector resources disposed");
// 调用基类的 Dispose 方法 | Call base class Dispose method
base.Dispose(disposing);
}
}
#endregion
}
}