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 { /// /// Varex 探测器实现类 | Varex detector implementation class /// 通过 XISL API 控制 Varex 探测器硬件 /// public class VarexDetector : AreaDetectorBase, IVarexDetector { #region 私有字段 | Private Fields // 配置 | Configuration private readonly VarexDetectorConfig _config; // 日志服务 | Logger service private readonly ILoggerService _logger; // 内存管理 | Memory Management private readonly ArrayPool _arrayPool; private readonly ConcurrentQueue _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 /// /// 探测器类型 | Detector type /// public override DetectorType Type => DetectorType.Varex; #endregion #region 构造函数 | Constructor /// /// 构造函数 | Constructor /// /// Varex 探测器配置 | Varex detector configuration /// 事件聚合器 | Event aggregator /// 日志服务 | Logger service 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.Shared; _imageQueue = new ConcurrentQueue(); // 初始化配置参数 | 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) /// /// 初始化探测器内部实现 | Initialize detector internal implementation /// protected override Task 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); } /// /// 应用配置参数 | Apply configuration parameters /// 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); } } /// /// 获取当前帧号 | Get current frame number /// private int GetCurrentFrameNumber() { uint dwActFrame, dwSecFrame; XISLApi.Acquisition_GetActFrame(_hAcqDesc, out dwActFrame, out dwSecFrame); return (int)dwActFrame; } /// /// 启动连续采集内部实现 | Start continuous acquisition internal implementation /// /// /// 采集前准备:获取配置、分配缓冲区、定义目标缓冲区、设置采集标志 /// Prepare for acquisition: get config, allocate buffers, define dest buffers, set acq data /// 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 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); } /// /// 停止采集内部实现 | Stop acquisition internal implementation /// protected override Task 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); } /// /// 单帧采集内部实现 | Single frame acquisition internal implementation /// protected override Task 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); } /// /// 暗场校正内部实现 | Dark correction internal implementation /// protected override Task 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); } /// /// 增益校正内部实现 | Gain correction internal implementation /// protected override Task 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); } /// /// 自动校正内部实现 | Auto correction internal implementation /// protected override async Task 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; } } /// /// 坏像素校正内部实现 | Bad pixel correction internal implementation /// protected override Task 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); } /// /// 获取探测器信息 | Get detector information /// 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) /// /// 设置 Binning 模式 | Set binning mode /// public Task 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); } }); } /// /// 获取 Binning 模式 | Get binning mode /// public BinningMode GetBinningMode() { return _binningMode; } /// /// 设置增益模式 | Set gain mode /// public Task 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); } }); } /// /// 获取增益模式 | Get gain mode /// public GainMode GetGainMode() { return _gainMode; } /// /// 设置曝光时间 | Set exposure time /// public Task 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); } }); } /// /// 获取曝光时间 | Get exposure time /// public uint GetExposureTime() { return _exposureTime; } /// /// 设置 ROI 区域 | Set ROI region /// public Task 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); } }); } /// /// 获取 ROI 区域 | Get ROI region /// public (uint x, uint y, uint width, uint height) GetROI() { return _roi; } #endregion #region 回调函数(占位符)| Callback Functions (Placeholders) /// /// 帧结束回调函数 | End frame callback /// /// 采集描述符句柄 | Acquisition descriptor handle 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); } } /// /// 采集结束回调函数 | End acquisition callback /// /// 采集描述符句柄 | Acquisition descriptor handle 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 /// /// 释放资源 | Dispose resources /// /// 是否释放托管资源 | Whether to dispose managed resources 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 } }