@@ -141,6 +141,25 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
|
||||
}
|
||||
|
||||
var parsed = AbLegacyAddress.TryParse(def.Address, device.Options.PlcFamily);
|
||||
// PR 7 — array contiguous block. Decode N consecutive elements via the runtime's
|
||||
// per-index accessor and box the result as a typed .NET array. The parser has
|
||||
// already rejected array+bit and array+sub-element combinations, so the array
|
||||
// path can ignore the bit/sub-element decoders entirely.
|
||||
int arrayCount;
|
||||
if (parsed is not null && (def.ArrayLength is not null || (parsed.ArrayCount ?? 1) > 1))
|
||||
{
|
||||
arrayCount = ResolveElementCount(def, parsed);
|
||||
}
|
||||
else arrayCount = 1;
|
||||
|
||||
if (arrayCount > 1)
|
||||
{
|
||||
var arr = DecodeArrayAs(runtime, def.DataType, arrayCount);
|
||||
results[i] = new DataValueSnapshot(arr, AbLegacyStatusMapper.Good, now, now);
|
||||
_health = new DriverHealth(DriverState.Healthy, now, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Timer/Counter/Control status bits route through GetBit at the parent-word
|
||||
// address — translate the .DN/.EN/etc. sub-element to its standard bit position
|
||||
// and pass it down to the runtime as a synthetic bitIndex.
|
||||
@@ -275,11 +294,16 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
|
||||
tag.DataType, parsed?.SubElement);
|
||||
var plcSetBit = AbLegacyDataTypeExtensions.IsPlcSetStatusBit(
|
||||
tag.DataType, parsed?.SubElement);
|
||||
// PR 7 — array contiguous-block tags advertise IsArray + ArrayDim so the OPC UA
|
||||
// generic node-manager builds a 1-D array variable. ArrayLength on the tag
|
||||
// definition wins over the parsed `,N` / `[N]` suffix; both null = scalar.
|
||||
var arrayLen = tag.ArrayLength
|
||||
?? (parsed?.ArrayCount is int n && n > 1 ? n : (int?)null);
|
||||
deviceFolder.Variable(tag.Name, tag.Name, new DriverAttributeInfo(
|
||||
FullName: tag.Name,
|
||||
DriverDataType: effectiveType,
|
||||
IsArray: false,
|
||||
ArrayDim: null,
|
||||
IsArray: arrayLen is int al && al > 1,
|
||||
ArrayDim: arrayLen is int al2 && al2 > 1 ? (uint)al2 : null,
|
||||
SecurityClass: tag.Writable && !plcSetBit
|
||||
? SecurityClassification.Operate
|
||||
: SecurityClassification.ViewOnly,
|
||||
@@ -454,13 +478,24 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
|
||||
throw new NotSupportedException(
|
||||
$"AbLegacy tag '{def.Name}' uses indirect addressing ('{def.Address}'); runtime resolution is not yet implemented.");
|
||||
|
||||
// PR 7 — resolve the effective array length: explicit ArrayLength override on the tag
|
||||
// definition wins over the parsed `,N` / `[N]` suffix. ElementCount of 1 means
|
||||
// single-element scalar (libplctag's default); >1 triggers the contiguous-block path.
|
||||
var elementCount = ResolveElementCount(def, parsed);
|
||||
// Drop the parsed array suffix from the libplctag tag name when ArrayLength overrides
|
||||
// it — libplctag would otherwise read the parsed length, not the override.
|
||||
var tagName = (def.ArrayLength is int && parsed.ArrayCount is not null)
|
||||
? (parsed with { ArrayCount = null }).ToLibplctagName()
|
||||
: parsed.ToLibplctagName();
|
||||
|
||||
var runtime = _tagFactory.Create(new AbLegacyTagCreateParams(
|
||||
Gateway: device.ParsedAddress.Gateway,
|
||||
Port: device.ParsedAddress.Port,
|
||||
CipPath: device.ParsedAddress.CipPath,
|
||||
LibplctagPlcAttribute: device.Profile.LibplctagPlcAttribute,
|
||||
TagName: parsed.ToLibplctagName(),
|
||||
Timeout: _options.Timeout));
|
||||
TagName: tagName,
|
||||
Timeout: _options.Timeout,
|
||||
ElementCount: elementCount));
|
||||
try
|
||||
{
|
||||
await runtime.InitializeAsync(ct).ConfigureAwait(false);
|
||||
@@ -474,6 +509,54 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
|
||||
return runtime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PR 7 — pull <paramref name="elementCount"/> consecutive elements from a runtime that
|
||||
/// just completed a single contiguous-block read. Element type drives both the .NET
|
||||
/// array shape (Int32[] / Single[] / Boolean[]) and the per-index decoder routing.
|
||||
/// </summary>
|
||||
private static object DecodeArrayAs(IAbLegacyTagRuntime runtime, AbLegacyDataType type, int elementCount)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
AbLegacyDataType.Bit => BuildArray<bool>(runtime, type, elementCount),
|
||||
AbLegacyDataType.Int or AbLegacyDataType.AnalogInt => BuildArray<int>(runtime, type, elementCount),
|
||||
AbLegacyDataType.Long => BuildArray<int>(runtime, type, elementCount),
|
||||
AbLegacyDataType.Float => BuildArray<float>(runtime, type, elementCount),
|
||||
_ => throw new NotSupportedException(
|
||||
$"AbLegacyDataType {type} is not supported in array contiguous-block reads."),
|
||||
};
|
||||
}
|
||||
|
||||
private static T[] BuildArray<T>(IAbLegacyTagRuntime runtime, AbLegacyDataType type, int n)
|
||||
{
|
||||
var arr = new T[n];
|
||||
for (var i = 0; i < n; i++)
|
||||
{
|
||||
var element = runtime.DecodeArrayElement(type, i);
|
||||
arr[i] = (T)Convert.ChangeType(element!, typeof(T))!;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PR 7 — resolve the effective array element count for a tag. Explicit
|
||||
/// <see cref="AbLegacyTagDefinition.ArrayLength"/> on the tag definition wins; otherwise
|
||||
/// the parsed <see cref="AbLegacyAddress.ArrayCount"/> from the address suffix is used;
|
||||
/// otherwise 1 (scalar). Validates the override against the same PCCC frame ceiling
|
||||
/// enforced by the parser so config-overrides can't bypass the limit.
|
||||
/// </summary>
|
||||
internal static int ResolveElementCount(AbLegacyTagDefinition def, AbLegacyAddress parsed)
|
||||
{
|
||||
if (def.ArrayLength is int n)
|
||||
{
|
||||
if (n < 1 || n > AbLegacyAddress.MaxArrayCount)
|
||||
throw new InvalidOperationException(
|
||||
$"AbLegacy tag '{def.Name}' has ArrayLength {n}; expected 1..{AbLegacyAddress.MaxArrayCount}.");
|
||||
return n;
|
||||
}
|
||||
return parsed.ArrayCount ?? 1;
|
||||
}
|
||||
|
||||
public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult();
|
||||
public async ValueTask DisposeAsync() => await ShutdownAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user