using ZB.MOM.WW.OtOpcUa.Driver.AbCip; namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests; /// /// Test fake for . Stores the mock PLC value in /// + returns it from . Use /// to simulate libplctag error codes, /// / to simulate exceptions. /// internal class FakeAbCipTag : IAbCipTagRuntime { public AbCipTagCreateParams CreationParams { get; } public object? Value { get; set; } public int Status { get; set; } public bool ThrowOnInitialize { get; set; } public bool ThrowOnRead { 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 FakeAbCipTag(AbCipTagCreateParams createParams) => CreationParams = createParams; public virtual Task InitializeAsync(CancellationToken cancellationToken) { InitializeCount++; if (ThrowOnInitialize) throw Exception ?? new InvalidOperationException("fake initialize failure"); return Task.CompletedTask; } public virtual Task ReadAsync(CancellationToken cancellationToken) { ReadCount++; if (ThrowOnRead) throw Exception ?? new InvalidOperationException("fake read failure"); return Task.CompletedTask; } public virtual Task WriteAsync(CancellationToken cancellationToken) { WriteCount++; return Task.CompletedTask; } public virtual int GetStatus() => Status; public virtual object? DecodeValue(AbCipDataType type, int? bitIndex) => Value; /// /// Task #194 whole-UDT read support. Tests drive multi-member decoding by setting /// — keyed by member byte offset — before invoking /// . Falls back to when the /// offset is zero or unmapped so existing tests that never set the offset map keep /// working unchanged. /// public Dictionary ValuesByOffset { get; } = new(); public virtual object? DecodeValueAt(AbCipDataType type, int offset, int? bitIndex) { if (ValuesByOffset.TryGetValue(offset, out var v)) return v; return offset == 0 ? Value : null; } public virtual void EncodeValue(AbCipDataType type, int? bitIndex, object? value) => Value = value; public virtual void Dispose() => Disposed = true; } /// Test factory that produces s and indexes them for assertion. internal sealed class FakeAbCipTagFactory : IAbCipTagFactory { public Dictionary Tags { get; } = new(StringComparer.OrdinalIgnoreCase); public Func? Customise { get; set; } public IAbCipTagRuntime Create(AbCipTagCreateParams createParams) { var fake = Customise?.Invoke(createParams) ?? new FakeAbCipTag(createParams); Tags[createParams.TagName] = fake; return fake; } }