Files
lmxopcua/tests/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs
2026-04-25 23:36:01 -04:00

97 lines
3.7 KiB
C#

using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
internal class FakeAbLegacyTag : IAbLegacyTagRuntime
{
public AbLegacyTagCreateParams CreationParams { get; }
public object? Value { get; set; }
public int Status { get; set; }
public bool ThrowOnInitialize { get; set; }
public bool ThrowOnRead { get; set; }
public bool ThrowOnWrite { get; set; }
public Exception? Exception { get; set; }
public int InitializeCount { get; private set; }
public int ReadCount { get; private set; }
public int WriteCount { get; private set; }
public bool Disposed { get; private set; }
public FakeAbLegacyTag(AbLegacyTagCreateParams p) => CreationParams = p;
public virtual Task InitializeAsync(CancellationToken ct)
{
InitializeCount++;
if (ThrowOnInitialize) throw Exception ?? new InvalidOperationException();
return Task.CompletedTask;
}
public virtual Task ReadAsync(CancellationToken ct)
{
ReadCount++;
if (ThrowOnRead) throw Exception ?? new InvalidOperationException();
return Task.CompletedTask;
}
public virtual Task WriteAsync(CancellationToken ct)
{
WriteCount++;
if (ThrowOnWrite) throw Exception ?? new InvalidOperationException();
return Task.CompletedTask;
}
public virtual int GetStatus() => Status;
public int? LastDecodeBitIndex { get; private set; }
public AbLegacyDataType? LastDecodeType { get; private set; }
public virtual object? DecodeValue(AbLegacyDataType type, int? bitIndex)
{
LastDecodeType = type;
LastDecodeBitIndex = bitIndex;
// If the test seeded a parent-word value (ushort/short/int) and the driver asked for a
// specific status bit, mask it out so we can assert the correct bit reaches the client.
if (bitIndex is int bit && Value is not null and not bool)
{
try
{
var word = Convert.ToInt32(Value);
return ((word >> bit) & 1) != 0;
}
catch (Exception ex) when (ex is FormatException or InvalidCastException) { }
}
return Value;
}
public virtual void EncodeValue(AbLegacyDataType type, int? bitIndex, object? value) => Value = value;
/// <summary>
/// PR 7 — array contiguous-block element decoder. Tests seed <see cref="ArrayValues"/>
/// with the per-index payload; the fake then returns each element in order. Falls back
/// to <see cref="Value"/> when the test only seeded a scalar (Convert.ChangeType handles
/// the cast back to the requested element type in the driver's BuildArray helper).
/// </summary>
public IReadOnlyList<object?>? ArrayValues { get; set; }
public AbLegacyDataType? LastArrayDecodeType { get; private set; }
public int LastArrayDecodeMaxIndex { get; private set; } = -1;
public virtual object? DecodeArrayElement(AbLegacyDataType type, int elementIndex)
{
LastArrayDecodeType = type;
if (elementIndex > LastArrayDecodeMaxIndex) LastArrayDecodeMaxIndex = elementIndex;
if (ArrayValues is not null && elementIndex < ArrayValues.Count)
return ArrayValues[elementIndex];
return Value;
}
public virtual void Dispose() => Disposed = true;
}
internal sealed class FakeAbLegacyTagFactory : IAbLegacyTagFactory
{
public Dictionary<string, FakeAbLegacyTag> Tags { get; } = new(StringComparer.OrdinalIgnoreCase);
public Func<AbLegacyTagCreateParams, FakeAbLegacyTag>? Customise { get; set; }
public IAbLegacyTagRuntime Create(AbLegacyTagCreateParams p)
{
var fake = Customise?.Invoke(p) ?? new FakeAbLegacyTag(p);
Tags[p.TagName] = fake;
return fake;
}
}