using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SSPCTester.Devices.Config;
using SSPCTester.Devices.Interfaces;
namespace SSPCTester.Devices.Drivers.Mock;
///
/// 所有 Mock 驱动的公共基类:实现 IDeviceConnection 的连接状态机。
///
public abstract class MockDeviceBase : IDeviceConnection
{
private readonly ILogger _logger;
protected MockDeviceBase(ILogger logger, string displayName)
{
_logger = logger;
DisplayName = displayName;
}
public bool IsConnected { get; private set; }
public string DisplayName { get; }
public event EventHandler? ConnectionChanged;
public virtual async Task ConnectAsync(CancellationToken ct = default)
{
await Task.Delay(200, ct).ConfigureAwait(false);
IsConnected = true;
_logger.LogInformation("[Mock] {Device} 已连接。", DisplayName);
ConnectionChanged?.Invoke(this, true);
return true;
}
public virtual Task DisconnectAsync()
{
if (IsConnected)
{
IsConnected = false;
_logger.LogInformation("[Mock] {Device} 已断开。", DisplayName);
ConnectionChanged?.Invoke(this, false);
}
return Task.CompletedTask;
}
}
///
/// 模拟固态功率控制器:内部维护 24 路开关状态。
///
public sealed class MockSspc : MockDeviceBase, ISspc
{
private readonly bool[] _states;
public MockSspc(IOptions options, ILogger logger)
: base(logger, "SSPC (Mock)")
{
ChannelCount = 24;
_states = new bool[ChannelCount];
}
public int ChannelCount { get; }
public async Task TurnOnAsync(int physicalSlot, CancellationToken ct = default)
{
await Task.Delay(20, ct).ConfigureAwait(false);
ValidateChannel(physicalSlot);
_states[physicalSlot - 1] = true;
}
public async Task TurnOffAsync(int physicalSlot, CancellationToken ct = default)
{
await Task.Delay(20, ct).ConfigureAwait(false);
ValidateChannel(physicalSlot);
_states[physicalSlot - 1] = false;
}
public async Task FlashAsync(int physicalSlot, int milliseconds, CancellationToken ct = default)
{
await TurnOnAsync(physicalSlot, ct).ConfigureAwait(false);
await Task.Delay(milliseconds, ct).ConfigureAwait(false);
await TurnOffAsync(physicalSlot, ct).ConfigureAwait(false);
}
public Task GetChannelStateAsync(int physicalSlot, CancellationToken ct = default)
{
ValidateChannel(physicalSlot);
return Task.FromResult(_states[physicalSlot - 1]);
}
public Task ReadMeasurementAsync(int physicalSlot, CancellationToken ct = default)
{
ValidateChannel(physicalSlot);
bool on = _states[physicalSlot - 1];
return Task.FromResult(new ChannelMeasurement
{
PhysicalSlot = physicalSlot,
Voltage = 28.0,
Current = on ? 1.0 : 0.0,
VoltageRaw = 2800,
CurrentRaw = on ? (short)167 : (short)0,
Timestamp = DateTimeOffset.Now
});
}
public async Task> ReadMeasurementsAsync(CancellationToken ct = default)
{
var values = new ChannelMeasurement[ChannelCount];
for (int slot = 1; slot <= ChannelCount; slot++)
values[slot - 1] = await ReadMeasurementAsync(slot, ct).ConfigureAwait(false);
return values;
}
private void ValidateChannel(int physicalSlot)
{
if (physicalSlot < 1 || physicalSlot > ChannelCount)
throw new ArgumentOutOfRangeException(nameof(physicalSlot), physicalSlot, $"通道范围 1..{ChannelCount}");
}
}