VarexDetector:增益校正缓冲区改用 sizeof(uint) 分配(与 SDK DWORD 写入对齐),新增 _pOffsetBuffer 有效性校验和分辨率匹配检查;校正期间设置 _isCorrecting 标志跳过帧回调,防止缓冲区冲突;
VarexDetector:SetBinningMode/SetGainMode 变更后自动释放旧校正缓冲区; DetectorService:暗场/亮场/坏像素校正及参数应用前自动停止采集,完成后恢复,避免 SDK 冲突; DetectorConfigViewModel:校正流程中集成停止/恢复采集逻辑。
This commit is contained in:
@@ -54,6 +54,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
|
||||
// 采集控制 | Acquisition Control
|
||||
private bool _isAcquiring = false;
|
||||
private volatile bool _isCorrecting = false; // 校正中标志,回调应忽略 | Correcting flag, callback should skip
|
||||
private CancellationTokenSource _acquisitionCts;
|
||||
private readonly object _acquisitionLock = new object();
|
||||
|
||||
@@ -61,6 +62,10 @@ namespace XP.Hardware.Detector.Implementations
|
||||
private uint _cachedRows;
|
||||
private uint _cachedColumns;
|
||||
|
||||
// 暗场校正时的分辨率快照(用于亮场校正时校验一致性)| Resolution snapshot at dark correction time (for gain correction validation)
|
||||
private uint _offsetBufferRows;
|
||||
private uint _offsetBufferColumns;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 属性 | Properties
|
||||
@@ -399,7 +404,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
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
|
||||
_pGainBuffer, // 未校正时为 IntPtr.Zero,校正后为增益数据 | IntPtr.Zero before calibration, gain data after
|
||||
_pCorrList); // 未校正时为 IntPtr.Zero | IntPtr.Zero before calibration
|
||||
|
||||
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
|
||||
@@ -537,7 +542,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
|
||||
// 等待硬件完全释放采集资源,避免后续操作出现 HIS_ERROR_TIMEOUT
|
||||
// Wait for hardware to fully release acquisition resources to avoid HIS_ERROR_TIMEOUT on subsequent operations
|
||||
Thread.Sleep(200);
|
||||
Thread.Sleep(500);
|
||||
|
||||
_logger?.Info("采集已停止 | Acquisition stopped");
|
||||
return DetectorResult.Success("采集已停止 | Acquisition stopped");
|
||||
@@ -597,7 +602,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
0, // 跳过帧数 | Skip frames
|
||||
(uint)XISLApi.HIS_SEQ_ONE_BUFFER,
|
||||
_pOffsetBuffer,
|
||||
_pGainAvgBuffer,
|
||||
_pGainBuffer,
|
||||
_pCorrList);
|
||||
|
||||
if (result != XISLApi.HIS_RETURN.HIS_ALL_OK)
|
||||
@@ -646,6 +651,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
try
|
||||
{
|
||||
_logger?.Info($"开始暗场校正,帧数:{frameCount} | Starting dark correction, frame count: {frameCount}");
|
||||
_isCorrecting = true;
|
||||
|
||||
// 1. 获取探测器配置信息 | Get detector configuration
|
||||
uint dwFrames, dwRows, dwColumns, dwDataType, dwSortFlags;
|
||||
@@ -660,16 +666,41 @@ namespace XP.Hardware.Detector.Implementations
|
||||
{
|
||||
var errorMsg = $"获取探测器配置失败 | Failed to get detector configuration: {result}";
|
||||
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, (int)result);
|
||||
}
|
||||
|
||||
// 2. 分配暗场缓冲区(如果尚未分配或大小不匹配)| Allocate offset buffer if not allocated or size mismatch
|
||||
// 2. 分配暗场缓冲区(仅在未分配或分辨率变化时重新分配)| Allocate offset buffer (only when not allocated or resolution changed)
|
||||
int requiredSize = (int)(dwRows * dwColumns) * sizeof(ushort);
|
||||
if (_pOffsetBuffer == IntPtr.Zero)
|
||||
bool needReallocOffset = _pOffsetBuffer == IntPtr.Zero
|
||||
|| _offsetBufferRows != dwRows
|
||||
|| _offsetBufferColumns != dwColumns;
|
||||
|
||||
if (needReallocOffset)
|
||||
{
|
||||
if (_pOffsetBuffer != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(_pOffsetBuffer);
|
||||
_pOffsetBuffer = IntPtr.Zero;
|
||||
}
|
||||
_pOffsetBuffer = Marshal.AllocHGlobal(requiredSize);
|
||||
_logger?.Debug($"已分配暗场缓冲区,大小:{requiredSize} 字节 | Allocated offset buffer, size: {requiredSize} bytes");
|
||||
if (_pOffsetBuffer == IntPtr.Zero)
|
||||
{
|
||||
var errorMsg = $"分配暗场缓冲区失败,所需大小:{requiredSize} 字节 | Failed to allocate offset buffer, required size: {requiredSize} bytes";
|
||||
_logger?.Error(null, errorMsg);
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, -1);
|
||||
}
|
||||
_offsetBufferRows = dwRows;
|
||||
_offsetBufferColumns = dwColumns;
|
||||
_logger?.Debug($"已分配暗场缓冲区,大小:{requiredSize} 字节,分辨率:{dwColumns}x{dwRows} | Allocated offset buffer, size: {requiredSize} bytes, resolution: {dwColumns}x{dwRows}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.Debug($"复用已有暗场缓冲区,分辨率:{dwColumns}x{dwRows} | Reusing existing offset buffer, resolution: {dwColumns}x{dwRows}");
|
||||
}
|
||||
// 零初始化缓冲区 | Zero-initialize buffer
|
||||
unsafe { new Span<byte>((void*)_pOffsetBuffer, requiredSize).Clear(); }
|
||||
|
||||
// 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}");
|
||||
@@ -680,6 +711,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
{
|
||||
var errorMsg = $"采集暗场图像失败 | Failed to acquire offset image: {result}";
|
||||
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, (int)result);
|
||||
}
|
||||
|
||||
@@ -689,6 +721,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
{
|
||||
var errorMsg = $"等待暗场采集完成超时 | Timeout waiting for offset acquisition: {waitResult}";
|
||||
_logger?.Warn(errorMsg);
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, (int)waitResult);
|
||||
}
|
||||
|
||||
@@ -720,10 +753,12 @@ namespace XP.Hardware.Detector.Implementations
|
||||
PublishCorrectionCompleted(CorrectionType.Dark, correctionResult);
|
||||
_logger?.Info("暗场校正完成 | Dark correction completed");
|
||||
|
||||
_isCorrecting = false;
|
||||
return correctionResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_isCorrecting = false;
|
||||
var errorMsg = $"暗场校正异常 | Dark correction exception: {ex.Message}";
|
||||
_logger?.Error(ex, errorMsg);
|
||||
var errorResult = DetectorResult.Failure(errorMsg, ex, -1);
|
||||
@@ -743,6 +778,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
try
|
||||
{
|
||||
_logger?.Info($"开始增益校正,帧数:{frameCount} | Starting gain correction, frame count: {frameCount}");
|
||||
_isCorrecting = true;
|
||||
|
||||
// 1. 获取探测器配置信息 | Get detector configuration
|
||||
uint dwFrames, dwRows, dwColumns, dwDataType, dwSortFlags;
|
||||
@@ -757,27 +793,64 @@ namespace XP.Hardware.Detector.Implementations
|
||||
{
|
||||
var errorMsg = $"获取探测器配置失败 | Failed to get detector configuration: {result}";
|
||||
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
|
||||
_isCorrecting = false;
|
||||
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);
|
||||
// 2. 校验 _pOffsetBuffer 有效性(必须由暗场校正填充,且分辨率一致)
|
||||
// Validate _pOffsetBuffer (must be filled by dark correction with matching resolution)
|
||||
if (_pOffsetBuffer == IntPtr.Zero)
|
||||
{
|
||||
var errorMsg = "暗场缓冲区为空,请先执行暗场校正 | Offset buffer is null, please perform dark correction first";
|
||||
_logger?.Error(null, errorMsg);
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, -1);
|
||||
}
|
||||
if (_offsetBufferRows != dwRows || _offsetBufferColumns != dwColumns)
|
||||
{
|
||||
var errorMsg = $"暗场缓冲区分辨率({_offsetBufferColumns}x{_offsetBufferRows})与当前分辨率({dwColumns}x{dwRows})不匹配,请重新执行暗场校正 | " +
|
||||
$"Offset buffer resolution ({_offsetBufferColumns}x{_offsetBufferRows}) does not match current resolution ({dwColumns}x{dwRows}), please redo dark correction";
|
||||
_logger?.Error(null, errorMsg);
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, -1);
|
||||
}
|
||||
|
||||
// 3. 分配增益采集缓冲区(仅在大小不匹配时重新分配,避免频繁释放/分配导致指针失效)
|
||||
// 注意:SDK 内部按 DWORD (uint, 4字节) 写入增益数据,必须用 sizeof(uint) 分配!
|
||||
// Note: SDK writes gain data as DWORD (uint, 4 bytes), must allocate with sizeof(uint)!
|
||||
int gainAcqSize = (int)(dwRows * dwColumns) * sizeof(uint);
|
||||
if (_pGainBuffer == IntPtr.Zero)
|
||||
{
|
||||
_pGainBuffer = Marshal.AllocHGlobal(gainAcqSize);
|
||||
_logger?.Debug($"已分配增益采集缓冲区,大小:{gainAcqSize} 字节 | Allocated gain acquisition buffer, size: {gainAcqSize} bytes");
|
||||
if (_pGainBuffer == IntPtr.Zero)
|
||||
{
|
||||
var errorMsg = $"分配增益采集缓冲区失败 | Failed to allocate gain buffer, size: {gainAcqSize}";
|
||||
_logger?.Error(null, errorMsg);
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, -1);
|
||||
}
|
||||
}
|
||||
// 零初始化 | Zero-initialize
|
||||
unsafe { new Span<byte>((void*)_pGainBuffer, gainAcqSize).Clear(); }
|
||||
_logger?.Debug($"增益采集缓冲区就绪,大小:{gainAcqSize} 字节(uint) | Gain acquisition buffer ready, size: {gainAcqSize} bytes (uint)");
|
||||
|
||||
// 分配增益映射输出缓冲区(uint/DWORD,用于 CreateGainMap 输出)| Allocate gain map output buffer (uint/DWORD, for CreateGainMap)
|
||||
int gainAvgSize = (int)(dwRows * dwColumns) * sizeof(uint);
|
||||
if (_pGainAvgBuffer == IntPtr.Zero)
|
||||
// 4. 设置采集数据标志为 ACQ_GAIN(告知 SDK 和回调当前处于增益校正模式)
|
||||
// Set acquisition data flag to ACQ_GAIN (inform SDK and callback that we're in gain correction mode)
|
||||
uint acqGainFlag = 4; // ACQ_GAIN = 4
|
||||
var setAcqResult = XISLApi.Acquisition_SetAcqData(_hAcqDesc, ref acqGainFlag);
|
||||
if (setAcqResult != XISLApi.HIS_RETURN.HIS_ALL_OK)
|
||||
{
|
||||
_pGainAvgBuffer = Marshal.AllocHGlobal(gainAvgSize);
|
||||
_logger?.Debug($"已分配增益映射缓冲区,大小:{gainAvgSize} 字节 | Allocated gain map buffer, size: {gainAvgSize} bytes");
|
||||
_logger?.Warn($"设置 ACQ_GAIN 标志失败:{setAcqResult}(继续执行)| Failed to set ACQ_GAIN flag: {setAcqResult} (continuing)");
|
||||
}
|
||||
|
||||
// 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}");
|
||||
// 6. 等待 SDK 内部完全释放采集资源(防止 Abort 后 SDK 仍持有旧缓冲区引用)
|
||||
// Wait for SDK to fully release acquisition resources (prevent SDK holding stale buffer references after Abort)
|
||||
Thread.Sleep(500);
|
||||
|
||||
// 6. 调用 XISL API 采集增益图像 | Call XISL API to acquire gain image
|
||||
_logger?.Info($"调用 Acquisition_Acquire_GainImage:分辨率={dwColumns}x{dwRows},帧数={frameCount}," +
|
||||
$"pOffsetBuffer=0x{_pOffsetBuffer:X},pGainBuffer=0x{_pGainBuffer:X} | " +
|
||||
$"Calling Acquisition_Acquire_GainImage: resolution={dwColumns}x{dwRows}, frames={frameCount}");
|
||||
result = XISLApi.Acquisition_Acquire_GainImage(
|
||||
_hAcqDesc, _pOffsetBuffer, _pGainBuffer, dwRows, dwColumns, (uint)frameCount);
|
||||
|
||||
@@ -785,6 +858,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
{
|
||||
var errorMsg = $"采集增益图像失败 | Failed to acquire gain image: {result}";
|
||||
_logger?.Error(null, errorMsg + $",返回码:{(int)result}");
|
||||
_isCorrecting = false;
|
||||
return DetectorResult.Failure(errorMsg, null, (int)result);
|
||||
}
|
||||
|
||||
@@ -794,18 +868,15 @@ namespace XP.Hardware.Detector.Implementations
|
||||
{
|
||||
var errorMsg = $"等待增益采集完成超时 | Timeout waiting for gain acquisition: {waitResult}";
|
||||
_logger?.Warn(errorMsg);
|
||||
_isCorrecting = false;
|
||||
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);
|
||||
}
|
||||
// 4. 增益校正完成(Acquisition_Acquire_GainImage 已内部完成增益计算,pGainBuffer 可直接用于实时补偿)
|
||||
// Gain correction done (Acquisition_Acquire_GainImage internally computes gain, pGainBuffer can be used directly for real-time compensation)
|
||||
// 注意:旧代码不调用 CreateGainMap,SDK 内部已完成增益映射计算
|
||||
// Note: Old code does not call CreateGainMap, SDK internally completes gain map calculation
|
||||
_logger?.Debug("增益校正采集完成,跳过 CreateGainMap(SDK 内部已处理)| Gain correction acquisition done, skipping CreateGainMap (SDK handles internally)");
|
||||
|
||||
// 5. 保存增益数据到配置的存储路径 | Save gain data to configured storage path
|
||||
if (_config.AutoSave && !string.IsNullOrEmpty(_config.SavePath))
|
||||
@@ -817,7 +888,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
|
||||
int bufferSize = (int)(dwRows * dwColumns * sizeof(uint));
|
||||
byte[] gainData = new byte[bufferSize];
|
||||
Marshal.Copy(_pGainAvgBuffer, gainData, 0, bufferSize);
|
||||
Marshal.Copy(_pGainBuffer, gainData, 0, bufferSize);
|
||||
System.IO.File.WriteAllBytes(gainFilePath, gainData);
|
||||
_logger?.Info($"增益数据已保存到:{gainFilePath} | Gain data saved to: {gainFilePath}");
|
||||
}
|
||||
@@ -835,10 +906,12 @@ namespace XP.Hardware.Detector.Implementations
|
||||
PublishCorrectionCompleted(CorrectionType.Gain, correctionResult);
|
||||
_logger?.Info("增益校正完成 | Gain correction completed");
|
||||
|
||||
_isCorrecting = false;
|
||||
return correctionResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_isCorrecting = false;
|
||||
var errorMsg = $"增益校正异常 | Gain correction exception: {ex.Message}";
|
||||
_logger?.Error(ex, errorMsg);
|
||||
var errorResult = DetectorResult.Failure(errorMsg, ex, -1);
|
||||
@@ -1086,6 +1159,7 @@ namespace XP.Hardware.Detector.Implementations
|
||||
|
||||
/// <summary>
|
||||
/// 设置 Binning 模式 | Set binning mode
|
||||
/// Binning 变化后校正数据失效,需要重新校正 | Correction data becomes invalid after binning change, recalibration needed
|
||||
/// </summary>
|
||||
public Task<DetectorResult> SetBinningModeAsync(BinningMode mode)
|
||||
{
|
||||
@@ -1096,6 +1170,31 @@ namespace XP.Hardware.Detector.Implementations
|
||||
var result = XISLApi.Acquisition_SetCameraBinningMode(_hAcqDesc, (uint)mode + 1);
|
||||
if (result == XISLApi.HIS_RETURN.HIS_ALL_OK)
|
||||
{
|
||||
// Binning 变化后,旧的校正缓冲区大小不匹配新分辨率,必须释放
|
||||
// After binning change, old correction buffers don't match new resolution, must free them
|
||||
if (_binningMode != mode)
|
||||
{
|
||||
_logger?.Info($"Binning 模式从 {_binningMode} 变更为 {mode},校正数据已失效 | Binning mode changed from {_binningMode} to {mode}, correction data invalidated");
|
||||
|
||||
if (_pOffsetBuffer != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(_pOffsetBuffer);
|
||||
_pOffsetBuffer = IntPtr.Zero;
|
||||
_offsetBufferRows = 0;
|
||||
_offsetBufferColumns = 0;
|
||||
}
|
||||
if (_pGainBuffer != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(_pGainBuffer);
|
||||
_pGainBuffer = IntPtr.Zero;
|
||||
}
|
||||
if (_pCorrList != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(_pCorrList);
|
||||
_pCorrList = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
_binningMode = mode;
|
||||
return DetectorResult.Success($"Binning 模式已设置为 {mode} | Binning mode set to {mode}");
|
||||
}
|
||||
@@ -1118,6 +1217,8 @@ namespace XP.Hardware.Detector.Implementations
|
||||
|
||||
/// <summary>
|
||||
/// 设置增益模式 | Set gain mode
|
||||
/// PGA 变化后校正数据在物理意义上失效(噪声特性不同),需要重新校正
|
||||
/// After PGA change, correction data is physically invalid (different noise characteristics), recalibration needed
|
||||
/// </summary>
|
||||
public Task<DetectorResult> SetGainModeAsync(GainMode mode)
|
||||
{
|
||||
@@ -1128,6 +1229,28 @@ namespace XP.Hardware.Detector.Implementations
|
||||
var result = XISLApi.Acquisition_SetCameraGain(_hAcqDesc, (uint)mode);
|
||||
if (result == XISLApi.HIS_RETURN.HIS_ALL_OK)
|
||||
{
|
||||
// PGA 变化后校正数据失效(不会崩溃,但补偿结果不正确)
|
||||
// Correction data invalidated after PGA change (won't crash, but compensation results are incorrect)
|
||||
if (_gainMode != mode && _pOffsetBuffer != IntPtr.Zero)
|
||||
{
|
||||
_logger?.Info($"PGA 从 {_gainMode} 变更为 {mode},校正数据已失效 | PGA changed from {_gainMode} to {mode}, correction data invalidated");
|
||||
Marshal.FreeHGlobal(_pOffsetBuffer);
|
||||
_pOffsetBuffer = IntPtr.Zero;
|
||||
_offsetBufferRows = 0;
|
||||
_offsetBufferColumns = 0;
|
||||
|
||||
if (_pGainBuffer != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(_pGainBuffer);
|
||||
_pGainBuffer = IntPtr.Zero;
|
||||
}
|
||||
if (_pCorrList != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(_pCorrList);
|
||||
_pCorrList = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
_gainMode = mode;
|
||||
return DetectorResult.Success($"增益模式已设置为 {mode} | Gain mode set to {mode}");
|
||||
}
|
||||
@@ -1224,6 +1347,9 @@ namespace XP.Hardware.Detector.Implementations
|
||||
{
|
||||
try
|
||||
{
|
||||
// 校正期间跳过帧回调(校正使用独立缓冲区,_pAcqBuffer 可能无效)| Skip during correction (correction uses separate buffers, _pAcqBuffer may be invalid)
|
||||
if (_isCorrecting) return;
|
||||
|
||||
uint dwRows = _cachedRows;
|
||||
uint dwColumns = _cachedColumns;
|
||||
int imageSize = (int)(dwRows * dwColumns);
|
||||
|
||||
@@ -309,12 +309,39 @@ namespace XP.Hardware.Detector.Services
|
||||
{
|
||||
_logger?.Info("服务层:执行暗场校正,帧数:{FrameCount} | Service layer: Executing dark correction, frame count: {FrameCount}", frameCount);
|
||||
var detector = GetDetectorOrThrow();
|
||||
|
||||
// 如果正在采集,先停止(XISL SDK 不允许采集中执行校正)| Stop acquisition first if running
|
||||
bool wasAcquiring = detector.Status == DetectorStatus.Acquiring;
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("探测器正在采集,先停止采集再执行暗场校正 | Detector is acquiring, stopping before dark correction");
|
||||
var stopResult = await detector.StopAcquisitionAsync(cancellationToken);
|
||||
if (!stopResult.IsSuccess)
|
||||
{
|
||||
_lastError = stopResult;
|
||||
_logger?.Error(stopResult.Exception, "停止采集失败,无法执行暗场校正:{Message} | Failed to stop acquisition, cannot perform dark correction: {Message}", stopResult.ErrorMessage);
|
||||
return DetectorResult.Failure($"停止采集失败,无法执行暗场校正 | Failed to stop acquisition: {stopResult.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
var result = await detector.DarkCorrectionAsync(frameCount, cancellationToken);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
_lastError = result;
|
||||
_logger?.Error(result.Exception, "暗场校正失败:{Message} | Dark correction failed: {Message}", result.ErrorMessage);
|
||||
}
|
||||
|
||||
// 如果之前在采集,恢复采集 | Resume acquisition if it was running before
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("暗场校正完成,恢复连续采集 | Dark correction done, resuming continuous acquisition");
|
||||
var startResult = await detector.StartAcquisitionAsync(cancellationToken);
|
||||
if (!startResult.IsSuccess)
|
||||
{
|
||||
_logger?.Warn("恢复采集失败:{Message} | Failed to resume acquisition: {Message}", startResult.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -339,12 +366,39 @@ namespace XP.Hardware.Detector.Services
|
||||
{
|
||||
_logger?.Info("服务层:执行亮场校正,帧数:{FrameCount} | Service layer: Executing gain correction, frame count: {FrameCount}", frameCount);
|
||||
var detector = GetDetectorOrThrow();
|
||||
|
||||
// 如果正在采集,先停止(XISL SDK 不允许采集中执行校正)| Stop acquisition first if running
|
||||
bool wasAcquiring = detector.Status == DetectorStatus.Acquiring;
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("探测器正在采集,先停止采集再执行亮场校正 | Detector is acquiring, stopping before gain correction");
|
||||
var stopResult = await detector.StopAcquisitionAsync(cancellationToken);
|
||||
if (!stopResult.IsSuccess)
|
||||
{
|
||||
_lastError = stopResult;
|
||||
_logger?.Error(stopResult.Exception, "停止采集失败,无法执行亮场校正:{Message} | Failed to stop acquisition, cannot perform gain correction: {Message}", stopResult.ErrorMessage);
|
||||
return DetectorResult.Failure($"停止采集失败,无法执行亮场校正 | Failed to stop acquisition: {stopResult.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
var result = await detector.GainCorrectionAsync(frameCount, cancellationToken);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
_lastError = result;
|
||||
_logger?.Error(result.Exception, "亮场校正失败:{Message} | Gain correction failed: {Message}", result.ErrorMessage);
|
||||
}
|
||||
|
||||
// 如果之前在采集,恢复采集 | Resume acquisition if it was running before
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("亮场校正完成,恢复连续采集 | Gain correction done, resuming continuous acquisition");
|
||||
var startResult = await detector.StartAcquisitionAsync(cancellationToken);
|
||||
if (!startResult.IsSuccess)
|
||||
{
|
||||
_logger?.Warn("恢复采集失败:{Message} | Failed to resume acquisition: {Message}", startResult.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -368,12 +422,39 @@ namespace XP.Hardware.Detector.Services
|
||||
{
|
||||
_logger?.Info("服务层:执行坏像素校正 | Service layer: Executing bad pixel correction");
|
||||
var detector = GetDetectorOrThrow();
|
||||
|
||||
// 如果正在采集,先停止(XISL SDK 不允许采集中执行校正)| Stop acquisition first if running
|
||||
bool wasAcquiring = detector.Status == DetectorStatus.Acquiring;
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("探测器正在采集,先停止采集再执行坏像素校正 | Detector is acquiring, stopping before bad pixel correction");
|
||||
var stopResult = await detector.StopAcquisitionAsync(cancellationToken);
|
||||
if (!stopResult.IsSuccess)
|
||||
{
|
||||
_lastError = stopResult;
|
||||
_logger?.Error(stopResult.Exception, "停止采集失败,无法执行坏像素校正:{Message} | Failed to stop acquisition, cannot perform bad pixel correction: {Message}", stopResult.ErrorMessage);
|
||||
return DetectorResult.Failure($"停止采集失败,无法执行坏像素校正 | Failed to stop acquisition: {stopResult.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
var result = await detector.BadPixelCorrectionAsync(cancellationToken);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
_lastError = result;
|
||||
_logger?.Error(result.Exception, "坏像素校正失败:{Message} | Bad pixel correction failed: {Message}", result.ErrorMessage);
|
||||
}
|
||||
|
||||
// 如果之前在采集,恢复采集 | Resume acquisition if it was running before
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("坏像素校正完成,恢复连续采集 | Bad pixel correction done, resuming continuous acquisition");
|
||||
var startResult = await detector.StartAcquisitionAsync(cancellationToken);
|
||||
if (!startResult.IsSuccess)
|
||||
{
|
||||
_logger?.Warn("恢复采集失败:{Message} | Failed to resume acquisition: {Message}", startResult.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -431,6 +512,20 @@ namespace XP.Hardware.Detector.Services
|
||||
// 通过 IVarexDetector 接口下发参数 | Apply parameters via IVarexDetector interface
|
||||
if (detector is IVarexDetector varexDetector)
|
||||
{
|
||||
// 如果正在采集,先停止(XISL SDK 不允许采集中修改参数)| Stop acquisition first if running (XISL SDK does not allow parameter changes during acquisition)
|
||||
bool wasAcquiring = detector.Status == DetectorStatus.Acquiring;
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("探测器正在采集,先停止采集再应用参数 | Detector is acquiring, stopping before applying parameters");
|
||||
var stopResult = await detector.StopAcquisitionAsync(cancellationToken);
|
||||
if (!stopResult.IsSuccess)
|
||||
{
|
||||
_lastError = stopResult;
|
||||
_logger?.Error(stopResult.Exception, "停止采集失败,无法应用参数:{Message} | Failed to stop acquisition, cannot apply parameters: {Message}", stopResult.ErrorMessage);
|
||||
return DetectorResult.Failure($"停止采集失败,无法应用参数 | Failed to stop acquisition, cannot apply parameters: {stopResult.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
// 设置 Binning | Set binning
|
||||
var binningResult = await varexDetector.SetBinningModeAsync((BinningMode)binningIndex);
|
||||
if (!binningResult.IsSuccess)
|
||||
@@ -456,6 +551,17 @@ namespace XP.Hardware.Detector.Services
|
||||
return exposureResult;
|
||||
}
|
||||
|
||||
// 如果之前在采集,恢复采集 | Resume acquisition if it was running before
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("参数应用完成,恢复连续采集 | Parameters applied, resuming continuous acquisition");
|
||||
var startResult = await detector.StartAcquisitionAsync(cancellationToken);
|
||||
if (!startResult.IsSuccess)
|
||||
{
|
||||
_logger?.Warn("恢复采集失败:{Message}(参数已成功应用)| Failed to resume acquisition: {Message} (parameters were applied successfully)", startResult.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
_logger?.Info("参数应用成功 | Parameters applied successfully");
|
||||
return DetectorResult.Success("参数应用成功 | Parameters applied successfully");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Prism.Commands;
|
||||
using Prism.Events;
|
||||
using Prism.Mvvm;
|
||||
using XP.Common.GeneralForm.Views;
|
||||
using XP.Common.Localization;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XP.Hardware.Detector.Abstractions.Events;
|
||||
using XP.Hardware.Detector.Abstractions.Enums;
|
||||
@@ -253,6 +254,9 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
|
||||
// 从配置加载 UI 选项 | Load UI options from config
|
||||
LoadOptionsFromConfig();
|
||||
|
||||
// 初始化连接状态(ViewModel 可能在探测器已连接后才创建)| Initialize connection status (ViewModel may be created after detector is already connected)
|
||||
IsConnected = _detectorService.IsConnected;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -410,23 +414,14 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 探测器状态变更回调,用于扫描期间自动锁定/解锁参数 | Detector status changed callback
|
||||
/// 探测器状态变更回调,用于同步连接状态 | Detector status changed callback for connection status sync
|
||||
/// 注意:参数锁定由外部扫描流程通过 LockParameters()/UnlockParameters() 显式控制,
|
||||
/// 普通预览采集不应锁定配置页面按钮
|
||||
/// </summary>
|
||||
private void OnDetectorStatusChanged(DetectorStatus status)
|
||||
{
|
||||
// 同步连接状态:非 Uninitialized 即视为已连接 | Sync connection status: connected if not Uninitialized
|
||||
IsConnected = status != DetectorStatus.Uninitialized;
|
||||
|
||||
if (status == DetectorStatus.Acquiring)
|
||||
{
|
||||
IsParametersLocked = true;
|
||||
_logger?.Debug("探测器进入采集状态,参数已自动锁定 | Detector acquiring, parameters auto-locked");
|
||||
}
|
||||
else if (status == DetectorStatus.Ready)
|
||||
{
|
||||
IsParametersLocked = false;
|
||||
_logger?.Debug("探测器就绪,参数已自动解锁 | Detector ready, parameters auto-unlocked");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -492,8 +487,8 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
{
|
||||
// 弹出用户确认对话框 | Show user confirmation dialog
|
||||
var confirmResult = MessageBox.Show(
|
||||
"请确认射线源已关闭,即将开始暗场校正。\n\nPlease confirm the X-ray source is OFF before starting dark correction.",
|
||||
"暗场校正确认 | Dark Correction Confirmation",
|
||||
LocalizationHelper.Get("Detector_DarkCorrection_ConfirmMessage"),
|
||||
LocalizationHelper.Get("Detector_DarkCorrection_ConfirmTitle"),
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
@@ -509,17 +504,31 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
|
||||
// 显示进度条窗口 | Show progress window
|
||||
var progressWindow = new ProgressWindow(
|
||||
title: "暗场校正 | Dark Correction",
|
||||
message: "正在应用参数... | Applying parameters...",
|
||||
title: LocalizationHelper.Get("Detector_DarkCorrection_Title"),
|
||||
message: LocalizationHelper.Get("Detector_Progress_ApplyingParameters"),
|
||||
isCancelable: false,
|
||||
logger: _logger);
|
||||
progressWindow.Show();
|
||||
|
||||
IsBusy = true;
|
||||
bool wasAcquiring = false;
|
||||
try
|
||||
{
|
||||
// 1. 应用参数到硬件 | Apply parameters to hardware
|
||||
progressWindow.UpdateProgress("正在应用参数... | Applying parameters...", 10);
|
||||
// 1. 如果正在采集,先停止(后续应用参数和校正都需要探测器空闲)| Stop acquisition if running
|
||||
wasAcquiring = _detectorService.Status == DetectorStatus.Acquiring;
|
||||
if (wasAcquiring)
|
||||
{
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_StoppingAcquisition"), 5);
|
||||
var stopResult = await _detectorService.StopAcquisitionAsync();
|
||||
if (!stopResult.IsSuccess)
|
||||
{
|
||||
_logger?.Error(stopResult.Exception, "停止采集失败,暗场校正中止:{Message} | Stop acquisition failed, dark correction aborted: {Message}", stopResult.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 应用参数到硬件 | Apply parameters to hardware
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_ApplyingParameters"), 10);
|
||||
var applyResult = await _detectorService.ApplyParametersAsync(_selectedBinningIndex, _selectedPga, _frameRate);
|
||||
if (!applyResult.IsSuccess)
|
||||
{
|
||||
@@ -527,13 +536,13 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 执行暗场校正(固定 64 帧)| Execute dark correction (fixed 64 frames)
|
||||
progressWindow.UpdateProgress("正在采集暗场数据(64帧)... | Acquiring dark field data (64 frames)...", 30);
|
||||
// 3. 执行暗场校正(固定 64 帧)| Execute dark correction (fixed 64 frames)
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_AcquiringDarkData"), 30);
|
||||
var result = await _detectorService.DarkCorrectionAsync(CorrectionFrameCount);
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
progressWindow.UpdateProgress("暗场校正完成 | Dark correction completed", 100);
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_DarkCorrectionDone"), 100);
|
||||
RecordDarkCorrectionParameters();
|
||||
DarkCorrectionDone = true;
|
||||
_logger?.Info("暗场校正完成 | Dark correction completed");
|
||||
@@ -550,6 +559,12 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 如果之前在采集,恢复连续采集 | Resume acquisition if it was running before
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("暗场校正流程结束,恢复连续采集 | Dark correction flow done, resuming continuous acquisition");
|
||||
await _detectorService.StartAcquisitionAsync();
|
||||
}
|
||||
IsBusy = false;
|
||||
progressWindow.Close();
|
||||
}
|
||||
@@ -565,8 +580,8 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
{
|
||||
_logger?.Warn("暗场校正与亮场校正参数不一致,请重新进行暗场校正 | Parameter mismatch, please redo dark correction");
|
||||
MessageBox.Show(
|
||||
"当前参数与暗场校正时不一致,请重新进行暗场校正。\n\nCurrent parameters differ from dark correction. Please redo dark correction.",
|
||||
"参数不一致 | Parameter Mismatch",
|
||||
LocalizationHelper.Get("Detector_ParameterMismatch_Message"),
|
||||
LocalizationHelper.Get("Detector_ParameterMismatch_Title"),
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
DarkCorrectionDone = false;
|
||||
@@ -575,8 +590,8 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
|
||||
// 弹出确认对话框:物体移出视野 | Confirm object removed from field of view
|
||||
var confirmObjectResult = MessageBox.Show(
|
||||
"请确认物体已移出探测器视野。\n\nPlease confirm the object has been removed from the detector field of view.",
|
||||
"亮场校正确认 | Light Correction Confirmation",
|
||||
LocalizationHelper.Get("Detector_LightCorrection_ConfirmObjectMessage"),
|
||||
LocalizationHelper.Get("Detector_LightCorrection_ConfirmObjectTitle"),
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
@@ -588,8 +603,8 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
|
||||
// 弹出确认对话框:射线源已开启 | Confirm X-ray source is ON
|
||||
var confirmRayResult = MessageBox.Show(
|
||||
"请确认射线源已开启且稳定,即将开始亮场校正。\n\nPlease confirm the X-ray source is ON and stable before starting light correction.",
|
||||
"亮场校正确认 | Light Correction Confirmation",
|
||||
LocalizationHelper.Get("Detector_LightCorrection_ConfirmRayMessage"),
|
||||
LocalizationHelper.Get("Detector_LightCorrection_ConfirmObjectTitle"),
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
@@ -605,17 +620,31 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
|
||||
// 显示进度条窗口 | Show progress window
|
||||
var progressWindow = new ProgressWindow(
|
||||
title: "亮场校正 | Light Correction",
|
||||
message: "正在采集亮场数据(64帧)... | Acquiring light field data (64 frames)...",
|
||||
title: LocalizationHelper.Get("Detector_LightCorrection_Title"),
|
||||
message: LocalizationHelper.Get("Detector_Progress_AcquiringLightData"),
|
||||
isCancelable: false,
|
||||
logger: _logger);
|
||||
progressWindow.Show();
|
||||
|
||||
IsBusy = true;
|
||||
bool wasAcquiring = false;
|
||||
try
|
||||
{
|
||||
// 0. 如果正在采集,先停止 | Stop acquisition if running
|
||||
wasAcquiring = _detectorService.Status == DetectorStatus.Acquiring;
|
||||
if (wasAcquiring)
|
||||
{
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_StoppingAcquisition"), 5);
|
||||
var stopResult = await _detectorService.StopAcquisitionAsync();
|
||||
if (!stopResult.IsSuccess)
|
||||
{
|
||||
_logger?.Error(stopResult.Exception, "停止采集失败,亮场校正中止:{Message} | Stop acquisition failed, light correction aborted: {Message}", stopResult.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 执行亮场校正(固定 64 帧)| Execute light correction (fixed 64 frames)
|
||||
progressWindow.UpdateProgress("正在采集亮场数据(64帧)... | Acquiring light field data (64 frames)...", 20);
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_AcquiringLightData"), 20);
|
||||
var result = await _detectorService.GainCorrectionAsync(CorrectionFrameCount);
|
||||
|
||||
if (result.IsSuccess)
|
||||
@@ -623,17 +652,17 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
_logger?.Info("亮场校正完成,开始执行坏像素校正 | Light correction completed, starting bad pixel correction");
|
||||
|
||||
// 2. 亮场校正完成后自动执行坏像素校正 | Auto execute bad pixel correction after light correction
|
||||
progressWindow.UpdateProgress("正在执行坏像素校正... | Executing bad pixel correction...", 70);
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_BadPixelCorrecting"), 70);
|
||||
var badPixelResult = await _detectorService.BadPixelCorrectionAsync();
|
||||
|
||||
if (badPixelResult.IsSuccess)
|
||||
{
|
||||
progressWindow.UpdateProgress("亮场校正及坏像素校正完成 | Light and bad pixel correction completed", 100);
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_LightAndBadPixelDone"), 100);
|
||||
_logger?.Info("亮场校正及坏像素校正全部完成 | Light correction and bad pixel correction all completed");
|
||||
}
|
||||
else
|
||||
{
|
||||
progressWindow.UpdateProgress("亮场校正完成,但坏像素校正失败 | Light correction done, bad pixel correction failed", 90);
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_LightDoneBadPixelFailed"), 90);
|
||||
_logger?.Error(badPixelResult.Exception, "坏像素校正失败:{Message} | Bad pixel correction failed: {Message}", badPixelResult.ErrorMessage);
|
||||
}
|
||||
}
|
||||
@@ -648,6 +677,12 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 如果之前在采集,恢复连续采集 | Resume acquisition if it was running before
|
||||
if (wasAcquiring)
|
||||
{
|
||||
_logger?.Info("亮场校正流程结束,恢复连续采集 | Light correction flow done, resuming continuous acquisition");
|
||||
await _detectorService.StartAcquisitionAsync();
|
||||
}
|
||||
IsBusy = false;
|
||||
progressWindow.Close();
|
||||
}
|
||||
@@ -662,8 +697,8 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
|
||||
// 显示进度条窗口 | Show progress window
|
||||
var progressWindow = new ProgressWindow(
|
||||
title: "坏像素校正 | Bad Pixel Correction",
|
||||
message: "正在检测坏像素... | Detecting bad pixels...",
|
||||
title: LocalizationHelper.Get("Detector_BadPixelCorrection_Title"),
|
||||
message: LocalizationHelper.Get("Detector_Progress_DetectingBadPixels"),
|
||||
isCancelable: false,
|
||||
logger: _logger);
|
||||
progressWindow.Show();
|
||||
@@ -671,12 +706,12 @@ namespace XP.Hardware.Detector.ViewModels
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
progressWindow.UpdateProgress("正在检测坏像素... | Detecting bad pixels...", 30);
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_DetectingBadPixels"), 30);
|
||||
var result = await _detectorService.BadPixelCorrectionAsync();
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
progressWindow.UpdateProgress("坏像素校正完成 | Bad pixel correction completed", 100);
|
||||
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_BadPixelDone"), 100);
|
||||
_logger?.Info("坏像素校正完成 | Bad pixel correction completed");
|
||||
}
|
||||
else
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user