Auto: ablegacy-5 — PD/MG/PLS/BT structure files

Adds PD (PID), MG (Message), PLS (Programmable Limit Switch) and BT
(Block Transfer) file types to the PCCC parser. New AbLegacyDataType
enum members (PidElement / MessageElement / PlsElement /
BlockTransferElement) plus per-type sub-element catalogue:

  - PD: SP/PV/CV/KP/KI/KD/MAXS/MINS/DB/OUT as Float32; EN/DN/MO/PE/
        AUTO/MAN/SP_VAL/SP_LL/SP_HL as Boolean (word-0 status bits).
  - MG: RBE/MS/SIZE/LEN as Int32; EN/EW/ER/DN/ST/CO/NR/TO as Boolean.
  - PLS: LEN as Int32 (bit table varies per PLC).
  - BT: RLEN/DLEN as Int32; EN/ST/DN/ER/CO/EW/TO/NR as Boolean.

Per-family flags on AbLegacyPlcFamilyProfile gate availability:

  - PD/MG: SLC500 + PLC-5 (operator + status bits both present).
  - PLS/BT: PLC-5 only (chassis-IO block transfer is PLC-5-specific).
  - MicroLogix + LogixPccc: rejected — no legacy file-letter form.

Status-bit indices match Rockwell DTAM / 1747-RM001 / 1785-6.5.12:
PD word 0 bits 0-8, MG/BT word 0 bits 8-15. PLC-set status bits
(PE/DN/SP_*; ST/DN/ER/CO/EW/NR/TO) are surfaced as ViewOnly via
IsPlcSetStatusBit, matching the Timer/Counter/Control pattern from
ablegacy-3.

LibplctagLegacyTagRuntime decodes PD non-bit members as Float32 and
MG/BT/PLS non-bit members as Int32; status bits route through GetBit
with the bit-position encoded by the driver via StatusBitIndex.

Tests: parser positive cases per family + negative cases per family,
catalogue + bit-index + read-only-bit assertions.

Closes #248
This commit is contained in:
Joseph Doherty
2026-04-25 19:08:51 -04:00
parent 5ca2ad83cd
commit 286ab3ba41
5 changed files with 365 additions and 6 deletions
@@ -31,6 +31,30 @@ public enum AbLegacyDataType
/// Sub-element catalogue lives in <see cref="AbLegacyFunctionFile.SubElementType"/>.
/// </summary>
MicroLogixFunctionFile,
/// <summary>
/// PD-file (PID) sub-element — caller addresses <c>.SP</c>, <c>.PV</c>, <c>.CV</c>,
/// <c>.KP</c>, <c>.KI</c>, <c>.KD</c>, <c>.MAXS</c>, <c>.MINS</c>, <c>.DB</c>, <c>.OUT</c>
/// (Float) and <c>.EN</c>, <c>.DN</c>, <c>.MO</c>, <c>.PE</c>, <c>.AUTO</c>, <c>.MAN</c>
/// (Boolean status bits in word 0).
/// </summary>
PidElement,
/// <summary>
/// MG-file (Message) sub-element — caller addresses <c>.RBE</c>, <c>.MS</c>, <c>.SIZE</c>,
/// <c>.LEN</c> (Int32) and <c>.EN</c>, <c>.EW</c>, <c>.ER</c>, <c>.DN</c>, <c>.ST</c>,
/// <c>.CO</c>, <c>.NR</c>, <c>.TO</c> (Boolean status bits).
/// </summary>
MessageElement,
/// <summary>
/// PLS-file (Programmable Limit Switch) sub-element — caller addresses <c>.LEN</c>
/// (Int32). Bit semantics vary by PLC; unknown sub-elements fall back to Int32.
/// </summary>
PlsElement,
/// <summary>
/// BT-file (Block Transfer) sub-element — caller addresses <c>.RLEN</c>, <c>.DLEN</c>
/// (Int32) and <c>.EN</c>, <c>.ST</c>, <c>.DN</c>, <c>.ER</c>, <c>.CO</c>, <c>.EW</c>,
/// <c>.TO</c>, <c>.NR</c> (Boolean status bits in word 0).
/// </summary>
BlockTransferElement,
}
/// <summary>
@@ -107,6 +131,12 @@ public static class AbLegacyDataTypeExtensions
AbLegacyDataType.TimerElement or AbLegacyDataType.CounterElement
or AbLegacyDataType.ControlElement => DriverDataType.Int32,
AbLegacyDataType.MicroLogixFunctionFile => DriverDataType.Int32,
// PD/MG/PLS/BT default to Int32 at the parent-element level. The sub-element-aware
// EffectiveDriverDataType refines specific members (Float for PID gains, Boolean for
// status bits).
AbLegacyDataType.PidElement or AbLegacyDataType.MessageElement
or AbLegacyDataType.PlsElement or AbLegacyDataType.BlockTransferElement
=> DriverDataType.Int32,
_ => DriverDataType.Int32,
};
@@ -141,6 +171,39 @@ public static class AbLegacyDataTypeExtensions
"LEN" or "POS" => DriverDataType.Int32,
_ => t.ToDriverDataType(),
},
// PD-file (PID): SP/PV/CV/KP/KI/KD/MAXS/MINS/DB/OUT are 32-bit floats; EN/DN/MO/PE/
// AUTO/MAN/SP_VAL/SP_LL/SP_HL are status bits in word 0.
AbLegacyDataType.PidElement => key switch
{
"SP" or "PV" or "CV" or "KP" or "KI" or "KD"
or "MAXS" or "MINS" or "DB" or "OUT" => DriverDataType.Float32,
"EN" or "DN" or "MO" or "PE"
or "AUTO" or "MAN" or "SP_VAL" or "SP_LL" or "SP_HL" => DriverDataType.Boolean,
_ => t.ToDriverDataType(),
},
// MG-file (Message): RBE/MS/SIZE/LEN are control words; EN/EW/ER/DN/ST/CO/NR/TO are
// status bits.
AbLegacyDataType.MessageElement => key switch
{
"RBE" or "MS" or "SIZE" or "LEN" => DriverDataType.Int32,
"EN" or "EW" or "ER" or "DN" or "ST" or "CO" or "NR" or "TO" => DriverDataType.Boolean,
_ => t.ToDriverDataType(),
},
// PLS-file (Programmable Limit Switch): LEN is a length word; bit semantics vary by
// PLC so unknown sub-elements stay Int32.
AbLegacyDataType.PlsElement => key switch
{
"LEN" => DriverDataType.Int32,
_ => t.ToDriverDataType(),
},
// BT-file (Block Transfer, PLC-5): RLEN/DLEN are length words; EN/ST/DN/ER/CO/EW/
// TO/NR are status bits in word 0.
AbLegacyDataType.BlockTransferElement => key switch
{
"RLEN" or "DLEN" => DriverDataType.Int32,
"EN" or "ST" or "DN" or "ER" or "CO" or "EW" or "TO" or "NR" => DriverDataType.Boolean,
_ => t.ToDriverDataType(),
},
_ => t.ToDriverDataType(),
};
}
@@ -187,6 +250,50 @@ public static class AbLegacyDataTypeExtensions
"EN" => 15,
_ => null,
},
// PD element word 0 (SLC 5/02+ PID, 1747-RM001 / PLC-5 PID-RM): bit 0=EN, 1=PE,
// 2=DN, 3=MO (manual mode), 4=AUTO, 5=MAN, 6=SP_VAL, 7=SP_LL, 8=SP_HL. Bits 48 are
// the SP-validity / SP-limit flags exposed in RSLogix 5 / 500.
AbLegacyDataType.PidElement => key switch
{
"EN" => 0,
"PE" => 1,
"DN" => 2,
"MO" => 3,
"AUTO" => 4,
"MAN" => 5,
"SP_VAL" => 6,
"SP_LL" => 7,
"SP_HL" => 8,
_ => null,
},
// MG element word 0 (PLC-5 MSG / SLC 5/05 MSG, 1785-6.5.12 / 1747-RM001):
// bit 15=EN, 14=ST, 13=DN, 12=ER, 11=CO, 10=EW, 9=NR, 8=TO.
AbLegacyDataType.MessageElement => key switch
{
"TO" => 8,
"NR" => 9,
"EW" => 10,
"CO" => 11,
"ER" => 12,
"DN" => 13,
"ST" => 14,
"EN" => 15,
_ => null,
},
// BT element word 0 (PLC-5 chassis BTR/BTW, 1785-6.5.12):
// bit 15=EN, 14=ST, 13=DN, 12=ER, 11=CO, 10=EW, 9=NR, 8=TO. Same layout as MG.
AbLegacyDataType.BlockTransferElement => key switch
{
"TO" => 8,
"NR" => 9,
"EW" => 10,
"CO" => 11,
"ER" => 12,
"DN" => 13,
"ST" => 14,
"EN" => 15,
_ => null,
},
_ => null,
};
}
@@ -205,6 +312,13 @@ public static class AbLegacyDataTypeExtensions
AbLegacyDataType.TimerElement => key is "DN" or "TT",
AbLegacyDataType.CounterElement => key is "DN" or "OV" or "UN",
AbLegacyDataType.ControlElement => key is "DN" or "EM" or "ER" or "FD" or "UL" or "IN",
// PID: PE (PID-error), DN (process-done), SP_VAL/SP_LL/SP_HL are PLC-set status.
// EN/MO/AUTO/MAN are operator-controllable via the .EN bit / mode select.
AbLegacyDataType.PidElement => key is "PE" or "DN" or "SP_VAL" or "SP_LL" or "SP_HL",
// MG/BT: ST (started), DN (done), ER (error), CO (continuous), EW (enabled-waiting),
// NR (no-response), TO (timeout) are PLC-set. EN is operator-driven via the rung.
AbLegacyDataType.MessageElement => key is "ST" or "DN" or "ER" or "CO" or "EW" or "NR" or "TO",
AbLegacyDataType.BlockTransferElement => key is "ST" or "DN" or "ER" or "CO" or "EW" or "NR" or "TO",
_ => false,
};
}