using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SSPCTester.Devices.Config;
using SSPCTester.Devices.Drivers.Real;
using SSPCTester.Devices.Interfaces;
namespace SSPCTester.Devices.Drivers.Mock;
///
/// 模拟数据采集卡。
/// 生成逼真的电压上升 / 下降波形:V(t) = Vmax * (1 - exp(-t/τ)) + 噪声。
///
public sealed class MockDaq : MockDeviceBase, IDaq
{
private readonly Random _rng = new(1024);
private readonly DaqOptions _opts;
private double? _nextTriggerSec;
/// 满量程电压(V)。
public double NominalVoltage { get; set; } = 28.0;
/// 满量程电流(A)。
public double NominalCurrent { get; set; } = 1.0;
/// 开启延时(秒),用于模拟"控制信号 → 开始上升"的延时。
public double OnDelaySec { get; set; } = 0.0003;
/// 关断延时(秒)。
public double OffDelaySec { get; set; } = 0.0003;
/// RC 时间常数(秒)。
public double TauSec { get; set; } = 0.0005;
/// 下一次 CaptureAsync 的模式:true=上升曲线,false=下降曲线。
public bool NextCaptureIsRising { get; set; } = true;
public MockDaq(IOptions options, ILogger logger)
: base(logger, "PCIe8586 高速采集卡 (Mock)")
{
_opts = options.Value.Daq;
}
public event EventHandler? SampleReceived;
public Task> EnumerateDevicesAsync(CancellationToken ct = default) =>
Task.FromResult>(
new[] { new DaqDeviceDescriptor(0, 0, "模拟 PCIe8586", true) });
public async Task CaptureAsync(
int[] channels, int sampleRateHz, double durationSec, CancellationToken ct = default)
{
if (sampleRateHz <= 0) throw new ArgumentOutOfRangeException(nameof(sampleRateHz));
if (durationSec <= 0) throw new ArgumentOutOfRangeException(nameof(durationSec));
// 模拟采集耗时(不全等比例,避免阻塞太久)
int simulatedDelayMs = (int)Math.Min(durationSec * 1000.0 * 0.05, 400);
await Task.Delay(simulatedDelayMs, ct).ConfigureAwait(false);
int n = (int)Math.Round(sampleRateHz * durationSec);
var v = new double[n];
var i = new double[n];
double dt = 1.0 / sampleRateHz;
bool rising = NextCaptureIsRising;
double delay = rising ? OnDelaySec : OffDelaySec;
// 触发时刻设在 1/4 处,便于前后查看
double triggerSec = _nextTriggerSec ?? durationSec * 0.25;
_nextTriggerSec = null;
for (int k = 0; k < n; k++)
{
double t = k * dt;
double tRel = t - triggerSec - delay; // 相对于"开始上升/下降"的时间
double voltage;
if (rising)
{
voltage = tRel <= 0 ? 0 : NominalVoltage * (1 - Math.Exp(-tRel / TauSec));
}
else
{
voltage = tRel <= 0 ? NominalVoltage : NominalVoltage * Math.Exp(-tRel / TauSec);
}
voltage += NextGaussian() * NominalVoltage * 0.005;
v[k] = voltage;
double current;
if (rising)
current = tRel <= 0 ? 0 : NominalCurrent * (1 - Math.Exp(-tRel / TauSec));
else
current = tRel <= 0 ? NominalCurrent : NominalCurrent * Math.Exp(-tRel / TauSec);
current += NextGaussian() * NominalCurrent * 0.01;
i[k] = current;
}
return new CurveData
{
SampleRateHz = sampleRateHz,
TriggerTimeSec = triggerSec,
Voltage = v,
Current = i
};
}
public Task ReadInstantAsync(int[] channels, CancellationToken ct = default)
{
var result = new double[channels.Length];
for (int k = 0; k < channels.Length; k++)
{
int physical = channels[k];
var cfg = _opts.Channels.FirstOrDefault(x => x.PhysicalChannel == physical);
var role = cfg is null
? DaqChannelRole.Unconfigured
: Pcie8586Daq.ParseRole(cfg.Role);
result[k] = role switch
{
DaqChannelRole.OutputVoltage or DaqChannelRole.InputVoltage =>
NominalVoltage * (1 + (_rng.NextDouble() - 0.5) * 0.004),
DaqChannelRole.OutputCurrent or DaqChannelRole.InputCurrent =>
NominalCurrent * (1 + (_rng.NextDouble() - 0.5) * 0.02),
_ => 0
};
}
return Task.FromResult(result);
}
public async Task CaptureAroundActionAsync(
DaqActionCaptureRequest request,
Func action,
CancellationToken ct = default)
{
// Mock 下也遵循“先采前置窗口,再执行动作”的时序,避免与真卡行为不一致。
await Task.Delay((int)Math.Max(1, request.PreTriggerMs * 0.2), ct).ConfigureAwait(false);
double durationSec = (request.PreTriggerMs + request.PostTriggerMs) / 1_000.0;
int sampleRate = _opts.SampleRateHz;
NextCaptureIsRising = request.IsRising;
await action(ct).ConfigureAwait(false);
_nextTriggerSec = request.PreTriggerMs / 1_000.0;
var curve = await CaptureAsync(new[] { 0 }, sampleRate, durationSec, ct).ConfigureAwait(false);
int triggerIndex = (int)Math.Round(sampleRate * request.PreTriggerMs / 1_000.0);
return new CurveData
{
SampleRateHz = curve.SampleRateHz,
TriggerSampleIndex = triggerIndex,
TriggerTimeSec = triggerIndex / (double)curve.SampleRateHz,
Voltage = curve.Voltage,
Current = curve.Current,
EngineeringChannels = new[]
{
new CurveChannelData(0, DaqChannelRole.OutputVoltage, "模拟输出电压", "V", 1, 0, curve.Voltage),
new CurveChannelData(1, DaqChannelRole.OutputCurrent, "模拟输出电流", "A", 1, 0, curve.Current!)
}
};
}
/// 由 ReadInstantAsync 衍生的"电流读数"接口(基础测试页用)。
public (double volts, double amps) ReadVoltageCurrent(int channel, bool channelOn)
{
if (!channelOn) return (0.0, 0.0);
double v = NominalVoltage * (1 + (_rng.NextDouble() - 0.5) * 0.004);
double a = NominalCurrent * (1 + (_rng.NextDouble() - 0.5) * 0.02);
return (v, a);
}
/// Box-Muller 生成高斯噪声。
private double NextGaussian()
{
double u1 = 1.0 - _rng.NextDouble();
double u2 = 1.0 - _rng.NextDouble();
return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Cos(2.0 * Math.PI * u2);
}
/// 仅用于编译期消除"事件未使用"警告。
private void RaiseSample(CurveSample s) => SampleReceived?.Invoke(this, s);
}