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 = on ? 28.0 : 0.0, Current = on ? 1.0 : 0.0, VoltageRaw = on ? (ushort)2800 : (ushort)0, 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}"); } }