using Microsoft.Extensions.Options; using SSPCTester.Devices.Config; using SSPCTester.Devices.Interfaces; using SSPCTester.Logic.Models; using SSPCTester.Logic.Testing; namespace SSPCTester.Tests; #pragma warning disable CS0067 public sealed class InterfacePowerTests { [Fact] public async Task PowerTest_UsesUdpInputAndIt8702pOutputPower() { var project = new Project(); var sspc = new MeasuringSspc(); var forbiddenPower = new ForbiddenPowerSupply(); var load = new MeasuringLoad(); var daq = new MeasuringDaq(); var options = Options.Create(new DeviceOptions { Daq = new DaqOptions { ChannelCount = 1, Channels = new List { new() { PhysicalChannel = 0, Role = nameof(DaqChannelRole.OutputVoltage), IsCalibrated = true }, } } }); var power = new FixedPowerSupply(); var test = new PowerTest(power) { SamplesPerChannel = 1, IntervalMs = 0 }; var context = new TestContext(project, sspc, forbiddenPower, load, daq); await test.RunAsync(context, new Progress(), CancellationToken.None); Assert.Equal(24, project.PowerResults.Count); Assert.All(project.PowerResults, row => { Assert.Equal(TestStatus.Measured, row.Status); Assert.Equal(30.0, row.Vin, 6); // 输入端电压来自高速采集卡 Assert.Equal(1.0, row.Iin, 6); // 通流电流来自 Modbus(Iin = Iout) Assert.Equal(28.0, row.Vout, 6); // 输出端电压来自 Modbus Assert.Equal(1.0, row.Iout, 6); Assert.Equal(27.5, row.MeasuredPowerOut.GetValueOrDefault(), 6); Assert.Equal(2.0, row.PowerLossW, 6); Assert.Equal(93.333333, row.EfficiencyPct, 5); }); Assert.Equal(0, sspc.TurnOffCount); Assert.Equal(0, sspc.ReadMeasurementCount); Assert.Equal(1, power.OutputOnCount); Assert.Equal(1, load.InputOnCount); Assert.Equal(24, load.ReadPowerCount); Assert.Equal(0, daq.ReadInstantCount); } [Fact] public void PowerLossRow_FallsBackToCalculatedOutputPower() { var row = new PowerLossRow { Vin = 30, Iin = 1, Vout = 28, Iout = 1 }; Assert.Equal(28, row.PowerOut, 6); Assert.Equal(2, row.PowerLossW, 6); } private sealed class MeasuringSspc : ISspc { public int TurnOffCount { get; private set; } public int ReadMeasurementCount { get; private set; } public int ChannelCount => 24; public bool IsConnected => true; public string DisplayName => "test"; public event EventHandler? ConnectionChanged; public Task ConnectAsync(CancellationToken ct = default) => Task.FromResult(true); public Task DisconnectAsync() => Task.CompletedTask; public Task TurnOnAsync(int physicalSlot, CancellationToken ct = default) => Task.CompletedTask; public Task TurnOffAsync(int physicalSlot, CancellationToken ct = default) { TurnOffCount++; return Task.CompletedTask; } public Task FlashAsync(int physicalSlot, int milliseconds, CancellationToken ct = default) => Task.CompletedTask; public Task GetChannelStateAsync(int physicalSlot, CancellationToken ct = default) => Task.FromResult(true); public Task ReadMeasurementAsync(int physicalSlot, CancellationToken ct = default) { ReadMeasurementCount++; return Task.FromResult(new ChannelMeasurement { PhysicalSlot = physicalSlot, Voltage = 28.0, Current = 1.0 }); } public Task> ReadMeasurementsAsync(CancellationToken ct = default) => Task.FromResult>(Array.Empty()); } private sealed class MeasuringDaq : IDaq { public int ReadInstantCount { get; private set; } public bool IsConnected { get; private set; } public string DisplayName => "daq"; public event EventHandler? ConnectionChanged; public event EventHandler? SampleReceived; public Task ConnectAsync(CancellationToken ct = default) { IsConnected = true; ConnectionChanged?.Invoke(this, true); return Task.FromResult(true); } public Task DisconnectAsync() { IsConnected = false; ConnectionChanged?.Invoke(this, false); return Task.CompletedTask; } public Task> EnumerateDevicesAsync(CancellationToken ct = default) => Task.FromResult>(Array.Empty()); public Task CaptureAsync(int[] channels, int sampleRateHz, double durationSec, CancellationToken ct = default) => throw new InvalidOperationException(); public Task ReadInstantAsync(int[] channels, CancellationToken ct = default) { ReadInstantCount++; // 采集卡输入端电压固定返回 30V。 var values = channels.Select(ch => ch == 0 ? 30.0 : double.NaN).ToArray(); return Task.FromResult(values); } public Task CaptureAroundActionAsync(DaqActionCaptureRequest request, Func action, CancellationToken ct = default) => throw new InvalidOperationException(); } private sealed class MeasuringLoad : ILoad { public int ReadPowerCount { get; private set; } public int InputOnCount { get; private set; } public bool IsConnected => true; public string DisplayName => "it8702p"; public event EventHandler? ConnectionChanged; public Task ConnectAsync(CancellationToken ct = default) => Task.FromResult(true); public Task DisconnectAsync() => Task.CompletedTask; public Task SetModeAsync(LoadMode mode, CancellationToken ct = default) => throw new InvalidOperationException(); public Task SetValueAsync(double value, CancellationToken ct = default) => throw new InvalidOperationException(); public Task ResetAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task InputAsync(bool on, CancellationToken ct = default) { if (on) InputOnCount++; return Task.CompletedTask; } public Task GetInputStateAsync(CancellationToken ct = default) => Task.FromResult(true); public Task<(double volts, double amps)> ReadAsync(CancellationToken ct = default) => Task.FromResult((28.0, 1.0)); public Task ReadPowerAsync(CancellationToken ct = default) { ReadPowerCount++; return Task.FromResult(new PowerReading(28.0, 1.0, 27.5)); } public Task QueryIdentityAsync(CancellationToken ct = default) => Task.FromResult("it8702p"); public Task QueryErrorAsync(CancellationToken ct = default) => Task.FromResult("0,\"No error\""); } private sealed class FixedPowerSupply : IPowerSupply { public int OutputOnCount { get; private set; } public bool IsConnected => true; public string DisplayName => "fixed power"; public event EventHandler? ConnectionChanged; public Task ConnectAsync(CancellationToken ct = default) => Task.FromResult(true); public Task DisconnectAsync() => Task.CompletedTask; public Task SetVoltageAsync(double volts, CancellationToken ct = default) => throw new InvalidOperationException(); public Task SetCurrentLimitAsync(double amps, CancellationToken ct = default) => throw new InvalidOperationException(); public Task OutputAsync(bool on, CancellationToken ct = default) { if (on) OutputOnCount++; return Task.CompletedTask; } public Task GetOutputStateAsync(CancellationToken ct = default) => Task.FromResult(true); public Task<(double volts, double amps)> ReadAsync(CancellationToken ct = default) => Task.FromResult((30.0, 1.0)); public Task QueryIdentityAsync(CancellationToken ct = default) => Task.FromResult("fixed"); public Task QueryErrorAsync(CancellationToken ct = default) => Task.FromResult("0,\"No error\""); } private sealed class ForbiddenPowerSupply : IPowerSupply { public bool IsConnected => false; public string DisplayName => "forbidden"; public event EventHandler? ConnectionChanged; public Task ConnectAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task DisconnectAsync() => Task.CompletedTask; public Task SetVoltageAsync(double volts, CancellationToken ct = default) => throw new InvalidOperationException(); public Task SetCurrentLimitAsync(double amps, CancellationToken ct = default) => throw new InvalidOperationException(); public Task OutputAsync(bool on, CancellationToken ct = default) => throw new InvalidOperationException(); public Task GetOutputStateAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task<(double volts, double amps)> ReadAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task QueryIdentityAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task QueryErrorAsync(CancellationToken ct = default) => throw new InvalidOperationException(); } private sealed class ForbiddenLoad : ILoad { public bool IsConnected => false; public string DisplayName => "forbidden"; public event EventHandler? ConnectionChanged; public Task ConnectAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task DisconnectAsync() => Task.CompletedTask; public Task SetModeAsync(LoadMode mode, CancellationToken ct = default) => throw new InvalidOperationException(); public Task SetValueAsync(double value, CancellationToken ct = default) => throw new InvalidOperationException(); public Task ResetAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task InputAsync(bool on, CancellationToken ct = default) => throw new InvalidOperationException(); public Task GetInputStateAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task<(double volts, double amps)> ReadAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task ReadPowerAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task QueryIdentityAsync(CancellationToken ct = default) => throw new InvalidOperationException(); public Task QueryErrorAsync(CancellationToken ct = default) => throw new InvalidOperationException(); } } #pragma warning restore CS0067