SSPC-Tester/SSPCTester.Tests/DaqIntegrationTests.cs

196 lines
6.7 KiB
C#
Raw Normal View History

using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Pcie8586Probe.Hardware;
using Pcie8586Probe.Models;
using SSPCTester.Devices.Config;
using SSPCTester.Devices.Drivers.Mock;
using SSPCTester.Devices.Interfaces;
using RealPcie8586Daq = SSPCTester.Devices.Drivers.Real.Pcie8586Daq;
namespace SSPCTester.Tests;
public sealed class DaqIntegrationTests
{
[Fact]
public async Task MockCaptureAroundAction_InvokesActionAndPlacesSoftwareMarker()
{
var options = new DeviceOptions();
options.Daq.ClockDivider = 1_000;
var daq = new MockDaq(Options.Create(options), NullLogger<MockDaq>.Instance);
await daq.ConnectAsync();
bool invoked = false;
var curve = await daq.CaptureAroundActionAsync(
new DaqActionCaptureRequest(10, 40, true),
_ =>
{
invoked = true;
return Task.CompletedTask;
});
Assert.True(invoked);
Assert.Equal(1_000, curve.TriggerSampleIndex);
Assert.Equal(0.010, curve.TriggerTimeSec, 6);
Assert.Contains(curve.EngineeringChannels, x => x.Role == DaqChannelRole.OutputVoltage);
Assert.True(curve.Voltage[^1] > curve.Voltage[0]);
}
[Fact]
public async Task MockCaptureAroundAction_ExecutesSspcOnAndOffActions()
{
var options = new DeviceOptions();
var optionWrapper = Options.Create(options);
var daq = new MockDaq(optionWrapper, NullLogger<MockDaq>.Instance);
var sspc = new MockSspc(optionWrapper, NullLogger<MockSspc>.Instance);
await daq.ConnectAsync();
await sspc.ConnectAsync();
await daq.CaptureAroundActionAsync(
new DaqActionCaptureRequest(10, 40, true),
token => sspc.TurnOnAsync(3, token));
Assert.True(await sspc.GetChannelStateAsync(3));
await daq.CaptureAroundActionAsync(
new DaqActionCaptureRequest(10, 40, false),
token => sspc.TurnOffAsync(3, token));
Assert.False(await sspc.GetChannelStateAsync(3));
}
[Theory]
[InlineData(1000, 100_000)]
[InlineData(100, 1_000_000)]
public void DaqOptions_ComputesActualSampleRate(int divider, double expected)
{
var options = new DaqOptions { ClockDivider = divider };
Assert.Equal(expected, options.ActualSampleRateHz);
}
[Fact(Skip = "Requires an x64 testhost because Pcie8586.Driver is built x64-only.")]
public async Task RealCaptureAroundAction_InvokesActionAfterPreSamples()
{
var options = CreateRealDaqOptions();
var digitizer = FakeDigitizer.WithBlocks(
new SampleBlock(new[] { new[] { 0.1 } }, 100, 0, DateTimeOffset.Now),
new SampleBlock(new[] { new[] { 1.0 } }, 100, 1, DateTimeOffset.Now));
var daq = new RealPcie8586Daq(options.Daq, NullLogger<RealPcie8586Daq>.Instance, digitizer);
bool invoked = false;
var curve = await daq.CaptureAroundActionAsync(
new DaqActionCaptureRequest(10, 10, true),
_ =>
{
invoked = true;
return Task.CompletedTask;
});
Assert.True(invoked);
Assert.True(digitizer.Stopped);
Assert.Equal(1, curve.TriggerSampleIndex);
Assert.Equal(new[] { 0.1, 1.0 }, curve.Voltage);
}
[Fact(Skip = "Requires an x64 testhost because Pcie8586.Driver is built x64-only.")]
public async Task RealCaptureAroundAction_TimesOutWhenPreSamplesNeverArrive()
{
var options = CreateRealDaqOptions();
var digitizer = FakeDigitizer.WithoutBlocks();
var daq = new RealPcie8586Daq(options.Daq, NullLogger<RealPcie8586Daq>.Instance, digitizer);
bool invoked = false;
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
daq.CaptureAroundActionAsync(
new DaqActionCaptureRequest(10, 10, true),
_ =>
{
invoked = true;
return Task.CompletedTask;
}));
Assert.False(invoked);
Assert.True(digitizer.Stopped);
Assert.Contains("pre-trigger samples", ex.Message);
Assert.Contains("samples=0/1", ex.Message);
}
private static DeviceOptions CreateRealDaqOptions()
{
var options = new DeviceOptions();
options.Daq.UseMock = false;
options.Daq.ClockDivider = 1_000_000;
options.Daq.ChannelCount = 1;
options.Daq.PreTriggerMs = 10;
options.Daq.PostTriggerMs = 10;
options.Daq.Channels[0].Role = nameof(DaqChannelRole.OutputVoltage);
options.Daq.Channels[0].Name = "Vout";
options.Daq.Channels[0].Scale = 1;
options.Daq.Channels[0].IsCalibrated = true;
return options;
}
private sealed class FakeDigitizer : IDigitizer
{
private readonly IReadOnlyList<SampleBlock>? _blocks;
private bool _open;
private FakeDigitizer(IReadOnlyList<SampleBlock>? blocks)
{
_blocks = blocks;
}
public bool IsOpen => _open;
public bool Stopped { get; private set; }
public static FakeDigitizer WithBlocks(params SampleBlock[] blocks) => new(blocks);
public static FakeDigitizer WithoutBlocks() => new(null);
public ValueTask<IReadOnlyList<DeviceInfo>> EnumerateDevicesAsync(CancellationToken cancellationToken) =>
ValueTask.FromResult<IReadOnlyList<DeviceInfo>>(new[]
{
new DeviceInfo(0, 0, "fake", true)
});
public ValueTask OpenAsync(DeviceInfo device, CancellationToken cancellationToken)
{
_open = true;
return ValueTask.CompletedTask;
}
public ValueTask ConfigureAsync(AcquisitionConfig config, CancellationToken cancellationToken) =>
ValueTask.CompletedTask;
public async IAsyncEnumerable<SampleBlock> StartAcquisitionAsync(
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
{
if (_blocks == null)
{
while (!cancellationToken.IsCancellationRequested)
await Task.Delay(25, cancellationToken);
yield break;
}
foreach (var block in _blocks)
{
cancellationToken.ThrowIfCancellationRequested();
yield return block;
await Task.Yield();
}
}
public ValueTask StopAsync()
{
Stopped = true;
return ValueTask.CompletedTask;
}
public ValueTask CloseAsync()
{
_open = false;
return ValueTask.CompletedTask;
}
public void Dispose()
{
}
}
}