feat(s7): expose ReadMultipleVarsAsync through the IS7Plc seam
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using S7.Net;
|
||||
using S7DataItem = global::S7.Net.Types.DataItem;
|
||||
using S7NetDataType = global::S7.Net.DataType;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.S7;
|
||||
@@ -43,6 +44,22 @@ public interface IS7Plc : IDisposable
|
||||
/// <returns>The raw byte block.</returns>
|
||||
Task<byte[]> ReadBytesAsync(S7NetDataType area, int db, int startByteAdr, int count, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Reads several individually-addressed items in ONE PDU round-trip — the multi-var request
|
||||
/// the span planner's leftovers batch onto instead of issuing an address read apiece.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ <b>The CALLER owns chunking.</b> S7.Net does NOT split a multi-var request: both the item
|
||||
/// count and the total payload are bounded by the negotiated PDU size, and an oversized request
|
||||
/// fails on the wire rather than being paged. Split the item list to PDU capacity before calling.
|
||||
/// The library populates each <see cref="S7DataItem.Value"/> in place and hands the same list
|
||||
/// back; the returned list is the authoritative result and is ordered as supplied.
|
||||
/// </remarks>
|
||||
/// <param name="items">The items to read; each carries its area / DB / offset / var-type and receives its value.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the read.</param>
|
||||
/// <returns>The read items, in the supplied order, with their values populated.</returns>
|
||||
Task<IReadOnlyList<S7DataItem>> ReadMultipleVarsAsync(List<S7DataItem> items, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>Writes a scalar value by S7 address string; the value must be the unsigned wire type for the address size.</summary>
|
||||
/// <param name="address">The S7 address string.</param>
|
||||
/// <param name="value">The wire-typed value to write.</param>
|
||||
@@ -132,6 +149,17 @@ internal sealed class S7PlcAdapter : IS7Plc
|
||||
t => _plc.ReadBytesAsync(area, db, startByteAdr, count, t),
|
||||
_operationTimeout, $"block read of {area} db{db} @{startByteAdr}+{count}", cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<S7DataItem>> ReadMultipleVarsAsync(List<S7DataItem> items, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(items);
|
||||
// S7.Net returns the SAME list with each item's Value populated; await + widen because
|
||||
// Task<List<T>> is not assignable to Task<IReadOnlyList<T>>.
|
||||
return await S7OperationDeadline.RunAsync(
|
||||
t => _plc.ReadMultipleVarsAsync(items, t),
|
||||
_operationTimeout, $"multi-var read of {items.Count} item(s)", cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task WriteAsync(string address, object value, CancellationToken cancellationToken) =>
|
||||
S7OperationDeadline.RunAsync(
|
||||
|
||||
@@ -569,6 +569,10 @@ public sealed class S7DriverReconnectTests
|
||||
public Task<byte[]> ReadBytesAsync(S7NetDataType area, int db, int startByteAdr, int count, CancellationToken cancellationToken) =>
|
||||
throw new NotSupportedException("FakeS7Plc: block reads not needed for the reconnect tests");
|
||||
|
||||
public Task<IReadOnlyList<global::S7.Net.Types.DataItem>> ReadMultipleVarsAsync(
|
||||
List<global::S7.Net.Types.DataItem> items, CancellationToken cancellationToken) =>
|
||||
throw new NotSupportedException("FakeS7Plc: multi-var reads not needed for the reconnect tests");
|
||||
|
||||
public async Task WriteAsync(string address, object value, CancellationToken cancellationToken)
|
||||
{
|
||||
if (WriteHangsUntilCancelled)
|
||||
|
||||
@@ -47,6 +47,10 @@ public sealed class S7PollEngineMigrationTests
|
||||
return Task.FromResult(block);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<global::S7.Net.Types.DataItem>> ReadMultipleVarsAsync(
|
||||
List<global::S7.Net.Types.DataItem> items, CancellationToken ct)
|
||||
=> throw new NotSupportedException("ConstantArrayPlc: multi-var reads not needed for the poll-engine tests");
|
||||
|
||||
public Task WriteAsync(string address, object value, CancellationToken ct) => Task.CompletedTask;
|
||||
public Task WriteBytesAsync(S7NetDataType area, int db, int startByteAdr, byte[] value, CancellationToken ct) => Task.CompletedTask;
|
||||
public Task ReadStatusAsync(CancellationToken ct) => Task.CompletedTask;
|
||||
|
||||
@@ -323,6 +323,11 @@ public sealed class S7SpanReadParityTests
|
||||
return Task.FromResult(image[startByteAdr..(startByteAdr + count)]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<global::S7.Net.Types.DataItem>> ReadMultipleVarsAsync(
|
||||
List<global::S7.Net.Types.DataItem> items, CancellationToken ct)
|
||||
=> throw new NotSupportedException("ImageS7Plc: multi-var reads not served by the parity image fake");
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<object?> ReadAsync(string address, CancellationToken ct)
|
||||
{
|
||||
|
||||
@@ -92,6 +92,10 @@ internal sealed class ConnectingFakeS7PlcFactory : IS7PlcFactory
|
||||
public Task<byte[]> ReadBytesAsync(S7NetDataType area, int db, int startByteAdr, int count, CancellationToken ct)
|
||||
=> throw new NotSupportedException("ConnectingFakeS7Plc serves no data");
|
||||
/// <summary>Not exercised by these tests.</summary>
|
||||
public Task<IReadOnlyList<global::S7.Net.Types.DataItem>> ReadMultipleVarsAsync(
|
||||
List<global::S7.Net.Types.DataItem> items, CancellationToken ct)
|
||||
=> throw new NotSupportedException("ConnectingFakeS7Plc serves no data");
|
||||
/// <summary>Not exercised by these tests.</summary>
|
||||
public Task WriteAsync(string address, object value, CancellationToken ct)
|
||||
=> throw new NotSupportedException("ConnectingFakeS7Plc serves no data");
|
||||
/// <summary>Not exercised by these tests.</summary>
|
||||
|
||||
Reference in New Issue
Block a user