using ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests;
internal class FakeFocasClient : IFocasClient
{
/// Gets a value indicating whether the client is connected.
public bool IsConnected { get; private set; }
/// Gets the count of connection attempts.
public int ConnectCount { get; private set; }
/// Gets the count of dispose operations.
public int DisposeCount { get; private set; }
/// Gets or sets a value indicating whether to throw on connect.
public bool ThrowOnConnect { get; set; }
/// Gets or sets a value indicating whether to throw on read.
public bool ThrowOnRead { get; set; }
/// Gets or sets a value indicating whether to throw on write.
public bool ThrowOnWrite { get; set; }
/// Gets or sets the result of probe operations.
public bool ProbeResult { get; set; } = true;
/// Gets or sets the exception to throw.
public Exception? Exception { get; set; }
/// Gets the dictionary of read values keyed by address.
public Dictionary Values { get; } = new(StringComparer.OrdinalIgnoreCase);
/// Gets the dictionary of read statuses keyed by address.
public Dictionary ReadStatuses { get; } = new(StringComparer.OrdinalIgnoreCase);
/// Gets the dictionary of write statuses keyed by address.
public Dictionary WriteStatuses { get; } = new(StringComparer.OrdinalIgnoreCase);
/// Gets the log of write operations.
public List<(FocasAddress addr, FocasDataType type, object? value)> WriteLog { get; } = new();
/// Connects to a FOCAS host asynchronously.
/// The FOCAS host address.
/// The connection timeout duration.
/// The cancellation token.
public virtual Task ConnectAsync(FocasHostAddress address, TimeSpan timeout, CancellationToken ct)
{
ConnectCount++;
if (ThrowOnConnect) throw Exception ?? new InvalidOperationException();
IsConnected = true;
return Task.CompletedTask;
}
/// Reads a value from a FOCAS address asynchronously.
/// The FOCAS address to read from.
/// The data type of the value.
/// The cancellation token.
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));
}
/// Writes a value to a FOCAS address asynchronously.
/// The FOCAS address to write to.
/// The data type of the value.
/// The value to write.
/// The cancellation token.
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);
}
/// Probes the FOCAS connection asynchronously.
/// The cancellation token.
public virtual Task ProbeAsync(CancellationToken ct) => Task.FromResult(ProbeResult);
/// Gets the list of active alarms.
public List Alarms { get; } = [];
/// Reads active alarms asynchronously.
/// The cancellation token.
public virtual Task> ReadAlarmsAsync(CancellationToken ct) =>
Task.FromResult>([.. Alarms]);
// ---- Fixed-tree T1 ----
/// Gets or sets the system information.
public FocasSysInfo SysInfo { get; set; } = new(0, 3, "M", "M", "30i", "A1.0", 3);
/// Gets the list of axis names.
public List AxisNames { get; } = [new("X", ""), new("Y", ""), new("Z", "")];
/// Gets the list of spindle names.
public List SpindleNames { get; } = [new("S", "1", "", "")];
/// Gets the dictionary of dynamic snapshots keyed by axis index.
public Dictionary DynamicByAxis { get; } = [];
/// Gets system information asynchronously.
/// The cancellation token.
public virtual Task GetSysInfoAsync(CancellationToken ct) => Task.FromResult(SysInfo);
/// Gets axis names asynchronously.
/// The cancellation token.
public virtual Task> GetAxisNamesAsync(CancellationToken ct) =>
Task.FromResult>([.. AxisNames]);
/// Gets spindle names asynchronously.
/// The cancellation token.
public virtual Task> GetSpindleNamesAsync(CancellationToken ct) =>
Task.FromResult>([.. SpindleNames]);
/// Reads dynamic data for an axis asynchronously.
/// The zero-based axis index.
/// The cancellation token.
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);
}
/// Gets or sets the program information.
public FocasProgramInfo ProgramInfo { get; set; } = new("O0001", 1, 0, 1);
/// Gets program information asynchronously.
/// The cancellation token.
public virtual Task GetProgramInfoAsync(CancellationToken ct) =>
Task.FromResult(ProgramInfo);
/// Gets the dictionary of timers keyed by timer kind.
public Dictionary Timers { get; } = [];
/// Gets timer data asynchronously.
/// The timer kind to retrieve.
/// The cancellation token.
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);
}
/// Gets the list of servo loads.
public List ServoLoads { get; } = [];
/// Gets servo loads asynchronously.
/// The cancellation token.
public virtual Task> GetServoLoadsAsync(CancellationToken ct) =>
Task.FromResult>([.. ServoLoads]);
/// Gets the list of spindle loads.
public List SpindleLoads { get; } = [];
/// Gets the list of spindle maximum RPMs.
public List SpindleMaxRpms { get; } = [];
/// Gets spindle loads asynchronously.
/// The cancellation token.
public virtual Task> GetSpindleLoadsAsync(CancellationToken ct) =>
Task.FromResult>([.. SpindleLoads]);
/// Gets spindle maximum RPMs asynchronously.
/// The cancellation token.
public virtual Task> GetSpindleMaxRpmsAsync(CancellationToken ct) =>
Task.FromResult>([.. SpindleMaxRpms]);
/// Disposes the client.
public virtual void Dispose()
{
DisposeCount++;
IsConnected = false;
}
}
/// A factory for creating fake FOCAS clients.
internal sealed class FakeFocasClientFactory : IFocasClientFactory
{
/// Gets the list of created clients.
public List Clients { get; } = new();
/// Gets or sets a customization function for creating clients.
public Func? Customise { get; set; }
/// Creates a fake FOCAS client.
public IFocasClient Create()
{
var c = Customise?.Invoke() ?? new FakeFocasClient();
Clients.Add(c);
return c;
}
}