Files
XplorePlane/XP.Hardware.Detector/Implementations/SimulatedDetector.cs
T
zhengxuan.zhang cdd0db95ff 调试CNC执行
2026-05-26 13:18:29 +08:00

181 lines
7.5 KiB
C#

using System;
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>
/// 软件模拟探测器 | Software simulated detector
/// 无需真实硬件,定时发布合成的 16-bit 灰度帧,用于集成链路验证。
/// 帧内容:渐变灰度 + 帧号水印,便于肉眼确认帧序列。
/// </summary>
public sealed class SimulatedDetector : AreaDetectorBase
{
private readonly SimulatedDetectorConfig _config;
private readonly ILoggerService _logger;
private CancellationTokenSource _acquisitionCts;
private Task _acquisitionTask;
private int _frameNumber;
public override DetectorType Type => DetectorType.Simulated;
public SimulatedDetector(SimulatedDetectorConfig config, IEventAggregator eventAggregator, ILoggerService logger = null)
: base(eventAggregator)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
_logger = logger?.ForModule("SimulatedDetector");
_logger?.Info("[SimulatedDetector] 实例已创建,分辨率 {W}x{H},帧率 {FPS} fps",
_config.Width, _config.Height, _config.FrameRateFps);
}
// ── 模板方法实现 ──────────────────────────────────────────────────
protected override Task<DetectorResult> InitializeInternalAsync(CancellationToken cancellationToken)
{
_logger?.Info("[SimulatedDetector] 初始化完成(无硬件操作)");
return Task.FromResult(DetectorResult.Success("模拟探测器初始化成功"));
}
protected override Task<DetectorResult> StartAcquisitionInternalAsync(CancellationToken cancellationToken)
{
_acquisitionCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_acquisitionTask = Task.Run(() => AcquisitionLoopAsync(_acquisitionCts.Token), _acquisitionCts.Token);
_logger?.Info("[SimulatedDetector] 连续采集已启动");
return Task.FromResult(DetectorResult.Success("模拟采集已启动"));
}
protected override async Task<DetectorResult> StopAcquisitionInternalAsync(CancellationToken cancellationToken)
{
_acquisitionCts?.Cancel();
if (_acquisitionTask != null)
{
try { await _acquisitionTask.ConfigureAwait(false); }
catch (OperationCanceledException) { }
}
_acquisitionCts?.Dispose();
_acquisitionCts = null;
_acquisitionTask = null;
_logger?.Info("[SimulatedDetector] 连续采集已停止");
return DetectorResult.Success("模拟采集已停止");
}
protected override Task<DetectorResult> AcquireSingleFrameInternalAsync(CancellationToken cancellationToken)
{
PublishSyntheticFrame();
_logger?.Info("[SimulatedDetector] 单帧采集完成,帧号 {N}", _frameNumber);
return Task.FromResult(DetectorResult.Success("单帧采集成功"));
}
protected override Task<DetectorResult> DarkCorrectionInternalAsync(int frameCount, CancellationToken cancellationToken)
=> Task.FromResult(DetectorResult.Success("模拟暗场校正完成"));
protected override Task<DetectorResult> GainCorrectionInternalAsync(int frameCount, CancellationToken cancellationToken)
=> Task.FromResult(DetectorResult.Success("模拟亮场校正完成"));
protected override Task<DetectorResult> AutoCorrectionInternalAsync(int frameCount, CancellationToken cancellationToken)
=> Task.FromResult(DetectorResult.Success("模拟自动校正完成"));
protected override Task<DetectorResult> BadPixelCorrectionInternalAsync(CancellationToken cancellationToken)
=> Task.FromResult(DetectorResult.Success("模拟坏像素校正完成"));
protected override Task<DetectorResult> ApplyParametersInternalAsync(int binningIndex, int pga, decimal frameRate, CancellationToken cancellationToken)
{
_logger?.Info("[SimulatedDetector] 应用参数: Binning={Binning}, PGA={PGA}, FrameRate={FPS}",
binningIndex, pga, frameRate);
return Task.FromResult(DetectorResult.Success("模拟探测器参数应用成功"));
}
public override DetectorInfo GetInfo() => new DetectorInfo
{
Type = DetectorType.Simulated,
Model = "SimulatedDetector",
SerialNumber = "SIM-0001",
FirmwareVersion = "1.0.0",
MaxWidth = (uint)_config.Width,
MaxHeight = (uint)_config.Height,
PixelSize = 0.139,
BitDepth = 16
};
// ── 内部实现 ──────────────────────────────────────────────────────
private async Task AcquisitionLoopAsync(CancellationToken ct)
{
var intervalMs = (int)(1000.0 / Math.Max(1, _config.FrameRateFps));
_logger?.Info("[SimulatedDetector] 采集循环启动,间隔 {Interval} ms", intervalMs);
try
{
while (!ct.IsCancellationRequested)
{
PublishSyntheticFrame();
await Task.Delay(intervalMs, ct).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// 正常退出
}
catch (Exception ex)
{
_logger?.Error(ex, "[SimulatedDetector] 采集循环异常退出");
}
_logger?.Info("[SimulatedDetector] 采集循环已退出");
}
/// <summary>
/// 生成并发布一帧合成图像。
/// 内容:水平渐变灰度(0–65535),每行叠加帧号偏移,形成滚动条纹效果。
/// </summary>
private void PublishSyntheticFrame()
{
int n = Interlocked.Increment(ref _frameNumber);
int w = _config.Width;
int h = _config.Height;
var pixels = new ushort[w * h];
int offset = (n * 64) % 65536; // 每帧偏移,产生滚动效果
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
// 水平渐变 + 帧偏移
int value = (int)((double)x / w * 65535) + offset;
pixels[y * w + x] = (ushort)(value & 0xFFFF);
}
}
var args = new ImageCapturedEventArgs
{
ImageData = pixels,
Width = (uint)w,
Height = (uint)h,
FrameNumber = n,
CaptureTime = DateTime.Now,
ExposureTime = 0
};
//_logger?.Info("[SimulatedDetector] 发布合成帧 #{N},分辨率 {W}x{H}", n, w, h);
PublishImageCaptured(args);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_acquisitionCts?.Cancel();
_acquisitionCts?.Dispose();
}
base.Dispose(disposing);
}
}
}