118 lines
3.8 KiB
C#
118 lines
3.8 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using SSPCTester.Devices.Config;
|
|
using SSPCTester.Devices.Interfaces;
|
|
|
|
namespace SSPCTester.Devices.Drivers.Mock;
|
|
|
|
/// <summary>
|
|
/// 所有 Mock 驱动的公共基类:实现 IDeviceConnection 的连接状态机。
|
|
/// </summary>
|
|
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<bool>? ConnectionChanged;
|
|
|
|
public virtual async Task<bool> 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 模拟固态功率控制器:内部维护 24 路开关状态。
|
|
/// </summary>
|
|
public sealed class MockSspc : MockDeviceBase, ISspc
|
|
{
|
|
private readonly bool[] _states;
|
|
|
|
public MockSspc(IOptions<DeviceOptions> options, ILogger<MockSspc> 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<bool> GetChannelStateAsync(int physicalSlot, CancellationToken ct = default)
|
|
{
|
|
ValidateChannel(physicalSlot);
|
|
return Task.FromResult(_states[physicalSlot - 1]);
|
|
}
|
|
|
|
public Task<ChannelMeasurement> 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<IReadOnlyList<ChannelMeasurement>> 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}");
|
|
}
|
|
}
|