fix(ablegacy): indirect element index up to 999 on PLC-5; clear IndirectLastName on dispose

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 16:56:58 -04:00
parent b0b0ce9c77
commit 73fdb80157
2 changed files with 8 additions and 2 deletions
+1 -1
View File
@@ -121,7 +121,7 @@ physical inputs and may reject the write.
- **Indirect addressing (#247 parse, #646 runtime).** `N7:[N7:0]` (word from another address) and `N[N7:0]:5` (file
from another address) parse, one level deep (the inner address must be direct, and carries the family's octal rule).
At runtime the driver reads the inner address first (same runtime factory, so libplctag and Native alike), requires
an integer in range (word/element `0..255`, file `0..999`), composes the concrete address and runs the normal
an integer in range (word/element `0..255`, or `0..999` on PLC-5/LogixPccc; file `0..999`), composes the concrete address and runs the normal
read/write path against it. **The index is re-read on every read/write - never cached** - and the previous
resolution's handle is released when the index moves. A bad index (negative, too large, float/string source) is
`BadOutOfRange` for that tag only (no health impact); an inner-read failure propagates its own status (a wire
@@ -862,6 +862,9 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
/// <summary>Largest word/element index an indirect source may yield (PCCC element offset is one byte).</summary>
private const int MaxIndirectElement = AbLegacyArray.MaxElements - 1;
/// <summary>PLC-5 data files hold up to 1000 words (extended PCCC offset), unlike SLC/MicroLogix (256).</summary>
private const int MaxIndirectElementPlc5 = 999;
/// <summary>Largest data-file number an indirect source may yield (PLC-5 allows up to 999).</summary>
private const int MaxIndirectFile = 999;
@@ -877,6 +880,8 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
{
var fileNumber = parsed.FileNumber;
var word = parsed.WordNumber;
var maxElement = device.Options.PlcFamily is AbLegacyPlcFamily.Plc5 or AbLegacyPlcFamily.LogixPccc
? MaxIndirectElementPlc5 : MaxIndirectElement;
if (parsed.IndirectFileSource is { } fileSrc)
{
@@ -890,7 +895,7 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
{
var r = await ReadIndexAsync(device, wordSrc, ct).ConfigureAwait(false);
if (r.Status != 0) return new IndirectResolution(null, r.Status, r.TransportFault);
if (r.Value is < 0 or > MaxIndirectElement)
if (r.Value < 0 || r.Value > maxElement)
return new IndirectResolution(null, AbLegacyStatusMapper.BadOutOfRange, false);
word = (int)r.Value!;
}
@@ -1329,6 +1334,7 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover
Runtimes.Clear();
foreach (var r in ParentRuntimes.Values) r.Dispose();
ParentRuntimes.Clear();
IndirectLastName.Clear();
// Dispose + clear the per-parent RMW gates and the per-runtime/creation locks so
// ReinitializeAsync cycles don't orphan their SemaphoreSlim instances (each leaks a
// wait handle once contended).