using ZB.MOM.WW.OtOpcUa.Driver.FOCAS; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests; internal class FakeFocasClient : IFocasClient { public bool IsConnected { get; private set; } public int ConnectCount { get; private set; } public int DisposeCount { get; private set; } public bool ThrowOnConnect { get; set; } public bool ThrowOnRead { get; set; } public bool ThrowOnWrite { get; set; } public bool ProbeResult { get; set; } = true; public Exception? Exception { get; set; } public Dictionary Values { get; } = new(StringComparer.OrdinalIgnoreCase); public Dictionary ReadStatuses { get; } = new(StringComparer.OrdinalIgnoreCase); public Dictionary WriteStatuses { get; } = new(StringComparer.OrdinalIgnoreCase); public List<(FocasAddress addr, FocasDataType type, object? value)> WriteLog { get; } = new(); public virtual Task ConnectAsync(FocasHostAddress address, TimeSpan timeout, CancellationToken ct) { ConnectCount++; if (ThrowOnConnect) throw Exception ?? new InvalidOperationException(); IsConnected = true; return Task.CompletedTask; } public virtual Task<(object? value, uint status)> ReadAsync( FocasAddress address, FocasDataType type, CancellationToken ct) { if (ThrowOnRead) throw Exception ?? new InvalidOperationException(); var key = address.Canonical; var status = ReadStatuses.TryGetValue(key, out var s) ? s : FocasStatusMapper.Good; var value = Values.TryGetValue(key, out var v) ? v : null; return Task.FromResult((value, status)); } public virtual Task WriteAsync( FocasAddress address, FocasDataType type, object? value, CancellationToken ct) { if (ThrowOnWrite) throw Exception ?? new InvalidOperationException(); WriteLog.Add((address, type, value)); Values[address.Canonical] = value; var status = WriteStatuses.TryGetValue(address.Canonical, out var s) ? s : FocasStatusMapper.Good; return Task.FromResult(status); } public virtual Task ProbeAsync(CancellationToken ct) => Task.FromResult(ProbeResult); public List Alarms { get; } = []; public virtual Task> ReadAlarmsAsync(CancellationToken ct) => Task.FromResult>([.. Alarms]); // ---- Fixed-tree T1 ---- public FocasSysInfo SysInfo { get; set; } = new(0, 3, "M", "M", "30i", "A1.0", 3); public List AxisNames { get; } = [new("X", ""), new("Y", ""), new("Z", "")]; public List SpindleNames { get; } = [new("S", "1", "", "")]; public Dictionary DynamicByAxis { get; } = []; public virtual Task GetSysInfoAsync(CancellationToken ct) => Task.FromResult(SysInfo); public virtual Task> GetAxisNamesAsync(CancellationToken ct) => Task.FromResult>([.. AxisNames]); public virtual Task> GetSpindleNamesAsync(CancellationToken ct) => Task.FromResult>([.. SpindleNames]); public virtual Task ReadDynamicAsync(int axisIndex, CancellationToken ct) { if (!DynamicByAxis.TryGetValue(axisIndex, out var snap)) snap = new FocasDynamicSnapshot(axisIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); return Task.FromResult(snap); } public FocasProgramInfo ProgramInfo { get; set; } = new("O0001", 1, 0, 1); public virtual Task GetProgramInfoAsync(CancellationToken ct) => Task.FromResult(ProgramInfo); public Dictionary Timers { get; } = []; public virtual Task GetTimerAsync(FocasTimerKind kind, CancellationToken ct) { if (!Timers.TryGetValue(kind, out var t)) t = new FocasTimer(kind, 0, 0); return Task.FromResult(t); } public List ServoLoads { get; } = []; public virtual Task> GetServoLoadsAsync(CancellationToken ct) => Task.FromResult>([.. ServoLoads]); public List SpindleLoads { get; } = []; public List SpindleMaxRpms { get; } = []; public virtual Task> GetSpindleLoadsAsync(CancellationToken ct) => Task.FromResult>([.. SpindleLoads]); public virtual Task> GetSpindleMaxRpmsAsync(CancellationToken ct) => Task.FromResult>([.. SpindleMaxRpms]); public virtual void Dispose() { DisposeCount++; IsConnected = false; } } internal sealed class FakeFocasClientFactory : IFocasClientFactory { public List Clients { get; } = new(); public Func? Customise { get; set; } public IFocasClient Create() { var c = Customise?.Invoke() ?? new FakeFocasClient(); Clients.Add(c); return c; } }