@@ -72,6 +72,64 @@ internal class FakeTwinCATClient : ITwinCATClient
|
||||
return Task.FromResult(ProbeResult);
|
||||
}
|
||||
|
||||
// ---- Bulk surface (PR 2.1: SumSymbolRead / SumSymbolWrite) ----
|
||||
|
||||
public List<IReadOnlyList<TwinCATBulkReadItem>> BulkReadInvocations { get; } = new();
|
||||
public List<IReadOnlyList<TwinCATBulkWriteItem>> BulkWriteInvocations { get; } = new();
|
||||
public bool ThrowOnBulkRead { get; set; }
|
||||
public bool ThrowOnBulkWrite { get; set; }
|
||||
|
||||
/// <summary>Per-symbol read failure injection — overlay onto <see cref="ReadStatuses"/>.</summary>
|
||||
public Dictionary<string, uint> BulkReadStatuses { get; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public virtual Task<IReadOnlyList<(object? value, uint status)>> ReadValuesAsync(
|
||||
IReadOnlyList<TwinCATBulkReadItem> reads, CancellationToken ct)
|
||||
{
|
||||
// ThrowOnRead applies to both per-tag + bulk paths so legacy tests that toggled
|
||||
// ThrowOnRead before bulk existed still surface BadCommunicationError correctly.
|
||||
if (ThrowOnRead || ThrowOnBulkRead) throw Exception ?? new InvalidOperationException();
|
||||
BulkReadInvocations.Add(reads);
|
||||
|
||||
// Preserve request order — the production sum-read returns one entry per request slot
|
||||
// even on partial failure; so does this fake.
|
||||
var output = new (object? value, uint status)[reads.Count];
|
||||
for (var i = 0; i < reads.Count; i++)
|
||||
{
|
||||
var r = reads[i];
|
||||
ReadLog.Add((r.SymbolPath, r.Type, null, null));
|
||||
if (BulkReadStatuses.TryGetValue(r.SymbolPath, out var bulkStatus))
|
||||
{
|
||||
output[i] = (null, bulkStatus);
|
||||
continue;
|
||||
}
|
||||
if (ReadStatuses.TryGetValue(r.SymbolPath, out var status) && status != TwinCATStatusMapper.Good)
|
||||
{
|
||||
output[i] = (null, status);
|
||||
continue;
|
||||
}
|
||||
var value = Values.TryGetValue(r.SymbolPath, out var v) ? v : null;
|
||||
output[i] = (value, TwinCATStatusMapper.Good);
|
||||
}
|
||||
return Task.FromResult<IReadOnlyList<(object? value, uint status)>>(output);
|
||||
}
|
||||
|
||||
public virtual Task<IReadOnlyList<uint>> WriteValuesAsync(
|
||||
IReadOnlyList<TwinCATBulkWriteItem> writes, CancellationToken ct)
|
||||
{
|
||||
if (ThrowOnWrite || ThrowOnBulkWrite) throw Exception ?? new InvalidOperationException();
|
||||
BulkWriteInvocations.Add(writes);
|
||||
|
||||
var output = new uint[writes.Count];
|
||||
for (var i = 0; i < writes.Count; i++)
|
||||
{
|
||||
var w = writes[i];
|
||||
WriteLog.Add((w.SymbolPath, w.Type, null, w.Value));
|
||||
Values[w.SymbolPath] = w.Value;
|
||||
output[i] = WriteStatuses.TryGetValue(w.SymbolPath, out var s) ? s : TwinCATStatusMapper.Good;
|
||||
}
|
||||
return Task.FromResult<IReadOnlyList<uint>>(output);
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
DisposeCount++;
|
||||
|
||||
Reference in New Issue
Block a user