@@ -9,43 +9,32 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests;
|
||||
public sealed class FocasPmcBitRmwTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Fake client simulating PMC byte storage + exposing it as a sbyte so RMW callers can
|
||||
/// observe the read-modify-write round-trip. ReadAsync for a Bit with bitIndex surfaces
|
||||
/// the current bit; WriteAsync stores the full byte the driver issues.
|
||||
/// Fake client simulating PMC byte storage as a single 1024-byte buffer. Post-F4-c
|
||||
/// (issue #270) the FOCAS driver routes PMC writes through the typed
|
||||
/// <see cref="IFocasClient.WritePmcRangeAsync"/> + <see cref="IFocasClient.WritePmcBitAsync"/>
|
||||
/// entry points — the bit path performs RMW via <c>ReadPmcRangeAsync</c> +
|
||||
/// <c>WritePmcRangeAsync</c>, so this fake overrides those to drive a shared
|
||||
/// <see cref="PmcBytes"/> buffer the tests can assert against. <see cref="PmcBytes"/>
|
||||
/// is the unit-test surface; we mirror writes to <see cref="FakeFocasClient.PmcByteRanges"/>
|
||||
/// too so any helper that reads from there sees the same source of truth.
|
||||
/// </summary>
|
||||
private sealed class PmcRmwFake : FakeFocasClient
|
||||
{
|
||||
public byte[] PmcBytes { get; } = new byte[1024];
|
||||
|
||||
public override Task<(object? value, uint status)> ReadAsync(
|
||||
FocasAddress address, FocasDataType type, CancellationToken ct)
|
||||
public override Task<(byte[]? buffer, uint status)> ReadPmcRangeAsync(
|
||||
string letter, int pathId, int startByte, int byteCount, CancellationToken ct)
|
||||
{
|
||||
if (address.Kind == FocasAreaKind.Pmc && type == FocasDataType.Byte)
|
||||
return Task.FromResult(((object?)(sbyte)PmcBytes[address.Number], FocasStatusMapper.Good));
|
||||
if (address.Kind == FocasAreaKind.Pmc && type == FocasDataType.Bit && address.BitIndex is int bit)
|
||||
return Task.FromResult(((object?)((PmcBytes[address.Number] & (1 << bit)) != 0), FocasStatusMapper.Good));
|
||||
return base.ReadAsync(address, type, ct);
|
||||
var buf = new byte[byteCount];
|
||||
Array.Copy(PmcBytes, startByte, buf, 0, byteCount);
|
||||
return Task.FromResult<(byte[]?, uint)>((buf, FocasStatusMapper.Good));
|
||||
}
|
||||
|
||||
public override Task<uint> WriteAsync(
|
||||
FocasAddress address, FocasDataType type, object? value, CancellationToken ct)
|
||||
public override Task<uint> WritePmcRangeAsync(
|
||||
string letter, int pathId, int startByte, byte[] bytes, CancellationToken ct)
|
||||
{
|
||||
// Driver writes the full byte after RMW (type==Byte with full byte value), OR a raw
|
||||
// bit write (type==Bit, bitIndex non-null) — depending on how the driver routes it.
|
||||
if (address.Kind == FocasAreaKind.Pmc && type == FocasDataType.Byte)
|
||||
{
|
||||
PmcBytes[address.Number] = (byte)Convert.ToSByte(value);
|
||||
return Task.FromResult(FocasStatusMapper.Good);
|
||||
}
|
||||
if (address.Kind == FocasAreaKind.Pmc && type == FocasDataType.Bit && address.BitIndex is int bit)
|
||||
{
|
||||
var current = PmcBytes[address.Number];
|
||||
PmcBytes[address.Number] = Convert.ToBoolean(value)
|
||||
? (byte)(current | (1 << bit))
|
||||
: (byte)(current & ~(1 << bit));
|
||||
return Task.FromResult(FocasStatusMapper.Good);
|
||||
}
|
||||
return base.WriteAsync(address, type, value, ct);
|
||||
Array.Copy(bytes, 0, PmcBytes, startByte, bytes.Length);
|
||||
return Task.FromResult(FocasStatusMapper.Good);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +53,7 @@ public sealed class FocasPmcBitRmwTests
|
||||
Devices = [new FocasDeviceOptions("focas://10.0.0.5:8193")],
|
||||
Tags = writableTags,
|
||||
Probe = new FocasProbeOptions { Enabled = false },
|
||||
Writes = new FocasWritesOptions { Enabled = true },
|
||||
Writes = new FocasWritesOptions { Enabled = true, AllowPmc = true },
|
||||
}, "drv-1", factory);
|
||||
return (drv, fake);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user