Auto: abcip-3.1 — configurable CIP connection size per device

Closes #235
This commit is contained in:
Joseph Doherty
2026-04-25 22:39:05 -04:00
parent 7cbddd4b4a
commit f6c26db609
11 changed files with 683 additions and 10 deletions

View File

@@ -1,3 +1,4 @@
using System.Reflection;
using libplctag;
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
@@ -12,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
internal sealed class LibplctagTagRuntime : IAbCipTagRuntime
{
private readonly Tag _tag;
private readonly int _connectionSize;
public LibplctagTagRuntime(AbCipTagCreateParams p)
{
@@ -35,12 +37,55 @@ internal sealed class LibplctagTagRuntime : IAbCipTagRuntime
// to issue one Rockwell array read for a [N..M] slice.
if (p.ElementCount is int n && n > 0)
_tag.ElementCount = n;
_connectionSize = p.ConnectionSize;
}
public async Task InitializeAsync(CancellationToken cancellationToken)
{
await _tag.InitializeAsync(cancellationToken).ConfigureAwait(false);
// PR abcip-3.1 — propagate the configured CIP connection size to the native libplctag
// handle. The 1.5.x C# wrapper does not expose <c>connection_size</c> as a public Tag
// property, so we reach into the internal <c>NativeTagWrapper</c>'s
// <c>SetIntAttribute</c> (mirroring libplctag's <c>plc_tag_set_int_attribute</c>).
// libplctag native parses <c>connection_size</c> at create time, so this best-effort
// call lights up automatically when a future wrapper release exposes the attribute or
// when libplctag native gains post-create hot-update support — until then it falls back
// to the wrapper default. Failures (older / patched wrappers without the internal API)
// are intentionally swallowed so the driver keeps initialising.
TrySetConnectionSize(_tag, _connectionSize);
}
public Task InitializeAsync(CancellationToken cancellationToken) => _tag.InitializeAsync(cancellationToken);
public Task ReadAsync(CancellationToken cancellationToken) => _tag.ReadAsync(cancellationToken);
public Task WriteAsync(CancellationToken cancellationToken) => _tag.WriteAsync(cancellationToken);
/// <summary>
/// Best-effort propagation of <c>connection_size</c> to libplctag native. Reflects into
/// the wrapper's internal <c>NativeTagWrapper.SetIntAttribute(string, int)</c>; isolated
/// in a static helper so the lookup costs run once + the failure path is one line.
/// </summary>
private static void TrySetConnectionSize(Tag tag, int connectionSize)
{
try
{
var wrapperField = typeof(Tag).GetField("_tag", BindingFlags.NonPublic | BindingFlags.Instance);
var wrapper = wrapperField?.GetValue(tag);
if (wrapper is null) return;
var setInt = wrapper.GetType().GetMethod(
"SetIntAttribute",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
binder: null,
types: [typeof(string), typeof(int)],
modifiers: null);
setInt?.Invoke(wrapper, ["connection_size", connectionSize]);
}
catch
{
// Wrapper internals shifted (newer libplctag.NET) — drop quietly. Either the new
// wrapper exposes ConnectionSize directly (our reflection no-ops) or operators must
// upgrade to a known-good version per docs/drivers/AbCip-Performance.md.
}
}
public int GetStatus() => (int)_tag.GetStatus();
public object? DecodeValue(AbCipDataType type, int? bitIndex) => DecodeValueAt(type, 0, bitIndex);