[ablegacy] AbLegacy — PD/MG/PLS/BT structure files #352
@@ -156,9 +156,19 @@ public sealed record AbLegacyAddress(
|
||||
|
||||
// Reject unknown file letters — these cover SLC/ML/PLC-5 canonical families.
|
||||
// Function-file letters (RTC/HSC/DLS/MMI/PTO/PWM/STI/EII/IOS/BHI) are MicroLogix-only.
|
||||
// Structure-file letters (PD/MG/PLS/BT) are gated per family — PD/MG are common on
|
||||
// SLC500 + PLC-5; PLS/BT are PLC-5 only. MicroLogix and LogixPccc reject them.
|
||||
if (!IsKnownFileLetter(letter))
|
||||
{
|
||||
if (!IsFunctionFileLetter(letter) || profile?.SupportsFunctionFiles != true) return null;
|
||||
if (IsFunctionFileLetter(letter))
|
||||
{
|
||||
if (profile?.SupportsFunctionFiles != true) return null;
|
||||
}
|
||||
else if (IsStructureFileLetter(letter))
|
||||
{
|
||||
if (!StructureFileSupported(letter, profile)) return null;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
var octalForIo = profile?.OctalIoAddressing == true && (letter == "I" || letter == "O");
|
||||
@@ -273,4 +283,28 @@ public sealed record AbLegacyAddress(
|
||||
"RTC" or "HSC" or "DLS" or "MMI" or "PTO" or "PWM" or "STI" or "EII" or "IOS" or "BHI" => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Structure-file prefixes added in #248: PD (PID), MG (Message), PLS (Programmable Limit
|
||||
/// Switch), BT (Block Transfer). Per-family availability is gated by the matching
|
||||
/// <c>Supports*File</c> flag on <see cref="AbLegacyPlcFamilyProfile"/>.
|
||||
/// </summary>
|
||||
internal static bool IsStructureFileLetter(string letter) => letter switch
|
||||
{
|
||||
"PD" or "MG" or "PLS" or "BT" => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
private static bool StructureFileSupported(string letter, AbLegacyPlcFamilyProfile? profile)
|
||||
{
|
||||
if (profile is null) return false;
|
||||
return letter switch
|
||||
{
|
||||
"PD" => profile.SupportsPidFile,
|
||||
"MG" => profile.SupportsMessageFile,
|
||||
"PLS" => profile.SupportsPlsFile,
|
||||
"BT" => profile.SupportsBlockTransferFile,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 4–8 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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,6 +48,17 @@ internal sealed class LibplctagLegacyTagRuntime : IAbLegacyTagRuntime
|
||||
or AbLegacyDataType.ControlElement => bitIndex is int statusBit
|
||||
? _tag.GetBit(statusBit)
|
||||
: _tag.GetInt32(0),
|
||||
// PD-file (PID): non-bit members (SP/PV/CV/KP/KI/KD/MAXS/MINS/DB/OUT) are 32-bit floats.
|
||||
// Status bits (EN/DN/MO/PE/AUTO/MAN/SP_VAL/SP_LL/SP_HL) live in the parent control word
|
||||
// and read through GetBit — the driver encodes the position via StatusBitIndex.
|
||||
AbLegacyDataType.PidElement => bitIndex is int pidBit
|
||||
? _tag.GetBit(pidBit)
|
||||
: _tag.GetFloat32(0),
|
||||
// MG/BT/PLS: non-bit members (RBE/MS/SIZE/LEN, RLEN/DLEN) are word-sized integers.
|
||||
AbLegacyDataType.MessageElement or AbLegacyDataType.BlockTransferElement
|
||||
or AbLegacyDataType.PlsElement => bitIndex is int statusBit2
|
||||
? _tag.GetBit(statusBit2)
|
||||
: _tag.GetInt32(0),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
@@ -83,6 +94,18 @@ internal sealed class LibplctagLegacyTagRuntime : IAbLegacyTagRuntime
|
||||
case AbLegacyDataType.ControlElement:
|
||||
_tag.SetInt32(0, Convert.ToInt32(value));
|
||||
break;
|
||||
// PD-file non-bit writes route to the Float backing store. Status-bit writes within
|
||||
// the parent word are blocked at the driver layer (PLC-set bits are read-only and
|
||||
// operator-controllable bits go through the bit-RMW path with the parent word typed
|
||||
// as Int).
|
||||
case AbLegacyDataType.PidElement:
|
||||
_tag.SetFloat32(0, Convert.ToSingle(value));
|
||||
break;
|
||||
case AbLegacyDataType.MessageElement:
|
||||
case AbLegacyDataType.BlockTransferElement:
|
||||
case AbLegacyDataType.PlsElement:
|
||||
_tag.SetInt32(0, Convert.ToInt32(value));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"AbLegacyDataType {type} not writable.");
|
||||
}
|
||||
|
||||
@@ -11,7 +11,11 @@ public sealed record AbLegacyPlcFamilyProfile(
|
||||
bool SupportsStringFile,
|
||||
bool SupportsLongFile,
|
||||
bool OctalIoAddressing,
|
||||
bool SupportsFunctionFiles)
|
||||
bool SupportsFunctionFiles,
|
||||
bool SupportsPidFile,
|
||||
bool SupportsMessageFile,
|
||||
bool SupportsPlsFile,
|
||||
bool SupportsBlockTransferFile)
|
||||
{
|
||||
public static AbLegacyPlcFamilyProfile ForFamily(AbLegacyPlcFamily family) => family switch
|
||||
{
|
||||
@@ -29,7 +33,11 @@ public sealed record AbLegacyPlcFamilyProfile(
|
||||
SupportsStringFile: true, // ST file available SLC 5/04+
|
||||
SupportsLongFile: true, // L file available SLC 5/05+
|
||||
OctalIoAddressing: false, // SLC500 I:/O: indices are decimal in RSLogix 500
|
||||
SupportsFunctionFiles: false); // SLC500 has no function files
|
||||
SupportsFunctionFiles: false, // SLC500 has no function files
|
||||
SupportsPidFile: true, // SLC 5/02+ supports PD via PID instruction
|
||||
SupportsMessageFile: true, // SLC 5/02+ supports MG via MSG instruction
|
||||
SupportsPlsFile: false, // SLC500 has no native PLS file (uses SQO/SQC instead)
|
||||
SupportsBlockTransferFile: false); // SLC500 has no BT file (BT is PLC-5 ChassisIO only)
|
||||
|
||||
public static readonly AbLegacyPlcFamilyProfile MicroLogix = new(
|
||||
LibplctagPlcAttribute: "micrologix",
|
||||
@@ -38,7 +46,11 @@ public sealed record AbLegacyPlcFamilyProfile(
|
||||
SupportsStringFile: true,
|
||||
SupportsLongFile: false, // ML 1100/1200/1400 don't ship L files
|
||||
OctalIoAddressing: false, // MicroLogix follows SLC-style decimal I/O addressing
|
||||
SupportsFunctionFiles: true); // ML 1100/1400 expose RTC/HSC/DLS/MMI/PTO/PWM/STI/EII/IOS/BHI
|
||||
SupportsFunctionFiles: true, // ML 1100/1400 expose RTC/HSC/DLS/MMI/PTO/PWM/STI/EII/IOS/BHI
|
||||
SupportsPidFile: false, // MicroLogix 1100/1400 use PID-instruction-only addressing — no PD file type
|
||||
SupportsMessageFile: false, // No MG file — MSG instruction control words live in standard files
|
||||
SupportsPlsFile: false,
|
||||
SupportsBlockTransferFile: false);
|
||||
|
||||
public static readonly AbLegacyPlcFamilyProfile Plc5 = new(
|
||||
LibplctagPlcAttribute: "plc5",
|
||||
@@ -47,7 +59,11 @@ public sealed record AbLegacyPlcFamilyProfile(
|
||||
SupportsStringFile: true,
|
||||
SupportsLongFile: false, // PLC-5 predates L files
|
||||
OctalIoAddressing: true, // RSLogix 5 displays I:/O: word + bit indices as octal
|
||||
SupportsFunctionFiles: false);
|
||||
SupportsFunctionFiles: false,
|
||||
SupportsPidFile: true, // PLC-5 PID instruction needs PD file
|
||||
SupportsMessageFile: true, // PLC-5 MSG instruction needs MG file
|
||||
SupportsPlsFile: true, // PLC-5 has PLS (programmable limit switch) file
|
||||
SupportsBlockTransferFile: true); // PLC-5 chassis I/O block transfer (BTR/BTW) needs BT file
|
||||
|
||||
/// <summary>
|
||||
/// Logix ControlLogix / CompactLogix accessed through the legacy PCCC compatibility layer.
|
||||
@@ -61,7 +77,13 @@ public sealed record AbLegacyPlcFamilyProfile(
|
||||
SupportsStringFile: true,
|
||||
SupportsLongFile: true,
|
||||
OctalIoAddressing: false, // Logix natively uses decimal arrays even via the PCCC bridge
|
||||
SupportsFunctionFiles: false);
|
||||
SupportsFunctionFiles: false,
|
||||
// Logix native UDTs (PID_ENHANCED / MESSAGE) replace the legacy PD/MG file types — the
|
||||
// PCCC bridge does not expose them as letter-prefixed files.
|
||||
SupportsPidFile: false,
|
||||
SupportsMessageFile: false,
|
||||
SupportsPlsFile: false,
|
||||
SupportsBlockTransferFile: false);
|
||||
}
|
||||
|
||||
/// <summary>Which PCCC PLC family the device is.</summary>
|
||||
|
||||
@@ -333,4 +333,170 @@ public sealed class AbLegacyAddressTests
|
||||
{
|
||||
AbLegacyFunctionFile.SubElementType(letter, sub).ShouldBe(expected);
|
||||
}
|
||||
|
||||
// ---- Structure files PD/MG/PLS/BT (Issue #248) ----
|
||||
//
|
||||
// PD (PID), MG (Message), PLS (Programmable Limit Switch), BT (Block Transfer) — accepted on
|
||||
// SLC500 + PLC-5 for PD/MG, PLC-5 only for PLS/BT. MicroLogix and LogixPccc reject all four.
|
||||
|
||||
[Theory]
|
||||
[InlineData("PD10:0.SP")]
|
||||
[InlineData("PD10:0.PV")]
|
||||
[InlineData("PD10:0.KP")]
|
||||
[InlineData("PD10:0.EN")]
|
||||
[InlineData("MG11:0.LEN")]
|
||||
[InlineData("MG11:0.DN")]
|
||||
public void TryParse_Slc500_accepts_pd_and_mg(string input)
|
||||
{
|
||||
AbLegacyAddress.TryParse(input, AbLegacyPlcFamily.Slc500).ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("PLS12:0.LEN")]
|
||||
[InlineData("BT13:0.RLEN")]
|
||||
[InlineData("BT13:0.EN")]
|
||||
public void TryParse_Slc500_rejects_pls_and_bt(string input)
|
||||
{
|
||||
// PLS/BT are PLC-5 only; SLC500 must reject.
|
||||
AbLegacyAddress.TryParse(input, AbLegacyPlcFamily.Slc500).ShouldBeNull();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("PD10:0.KP", "PD", "KP")]
|
||||
[InlineData("MG11:0.EN", "MG", "EN")]
|
||||
[InlineData("PLS12:0.LEN", "PLS", "LEN")]
|
||||
[InlineData("BT13:0.RLEN", "BT", "RLEN")]
|
||||
[InlineData("BT13:0.DN", "BT", "DN")]
|
||||
public void TryParse_Plc5_accepts_all_structure_files(string input, string letter, string sub)
|
||||
{
|
||||
var a = AbLegacyAddress.TryParse(input, AbLegacyPlcFamily.Plc5);
|
||||
a.ShouldNotBeNull();
|
||||
a.FileLetter.ShouldBe(letter);
|
||||
a.SubElement.ShouldBe(sub);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("PD10:0.SP")]
|
||||
[InlineData("MG11:0.LEN")]
|
||||
[InlineData("PLS12:0.LEN")]
|
||||
[InlineData("BT13:0.RLEN")]
|
||||
public void TryParse_MicroLogix_rejects_all_structure_files(string input)
|
||||
{
|
||||
AbLegacyAddress.TryParse(input, AbLegacyPlcFamily.MicroLogix).ShouldBeNull();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("PD10:0.SP")]
|
||||
[InlineData("MG11:0.LEN")]
|
||||
[InlineData("PLS12:0.LEN")]
|
||||
[InlineData("BT13:0.RLEN")]
|
||||
public void TryParse_LogixPccc_rejects_all_structure_files(string input)
|
||||
{
|
||||
AbLegacyAddress.TryParse(input, AbLegacyPlcFamily.LogixPccc).ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryParse_Default_overload_rejects_structure_files()
|
||||
{
|
||||
// Without a family the parser cannot allow structure-file letters.
|
||||
AbLegacyAddress.TryParse("PD10:0.SP").ShouldBeNull();
|
||||
AbLegacyAddress.TryParse("MG11:0.LEN").ShouldBeNull();
|
||||
AbLegacyAddress.TryParse("PLS12:0.LEN").ShouldBeNull();
|
||||
AbLegacyAddress.TryParse("BT13:0.RLEN").ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Profiles_advertise_structure_file_support_per_family()
|
||||
{
|
||||
AbLegacyPlcFamilyProfile.Slc500.SupportsPidFile.ShouldBeTrue();
|
||||
AbLegacyPlcFamilyProfile.Slc500.SupportsMessageFile.ShouldBeTrue();
|
||||
AbLegacyPlcFamilyProfile.Slc500.SupportsPlsFile.ShouldBeFalse();
|
||||
AbLegacyPlcFamilyProfile.Slc500.SupportsBlockTransferFile.ShouldBeFalse();
|
||||
|
||||
AbLegacyPlcFamilyProfile.Plc5.SupportsPidFile.ShouldBeTrue();
|
||||
AbLegacyPlcFamilyProfile.Plc5.SupportsMessageFile.ShouldBeTrue();
|
||||
AbLegacyPlcFamilyProfile.Plc5.SupportsPlsFile.ShouldBeTrue();
|
||||
AbLegacyPlcFamilyProfile.Plc5.SupportsBlockTransferFile.ShouldBeTrue();
|
||||
|
||||
AbLegacyPlcFamilyProfile.MicroLogix.SupportsPidFile.ShouldBeFalse();
|
||||
AbLegacyPlcFamilyProfile.MicroLogix.SupportsMessageFile.ShouldBeFalse();
|
||||
AbLegacyPlcFamilyProfile.MicroLogix.SupportsPlsFile.ShouldBeFalse();
|
||||
AbLegacyPlcFamilyProfile.MicroLogix.SupportsBlockTransferFile.ShouldBeFalse();
|
||||
|
||||
AbLegacyPlcFamilyProfile.LogixPccc.SupportsPidFile.ShouldBeFalse();
|
||||
AbLegacyPlcFamilyProfile.LogixPccc.SupportsMessageFile.ShouldBeFalse();
|
||||
AbLegacyPlcFamilyProfile.LogixPccc.SupportsPlsFile.ShouldBeFalse();
|
||||
AbLegacyPlcFamilyProfile.LogixPccc.SupportsBlockTransferFile.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// PID Float members.
|
||||
[InlineData(AbLegacyDataType.PidElement, "SP", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float32)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "PV", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float32)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "KP", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float32)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "KI", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float32)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "KD", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float32)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "OUT", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float32)]
|
||||
// PID status bits.
|
||||
[InlineData(AbLegacyDataType.PidElement, "EN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "DN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "MO", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "PE", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
// MG Int32 control words.
|
||||
[InlineData(AbLegacyDataType.MessageElement, "RBE", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Int32)]
|
||||
[InlineData(AbLegacyDataType.MessageElement, "LEN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Int32)]
|
||||
// MG status bits.
|
||||
[InlineData(AbLegacyDataType.MessageElement, "EN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
[InlineData(AbLegacyDataType.MessageElement, "DN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
[InlineData(AbLegacyDataType.MessageElement, "TO", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
// PLS LEN.
|
||||
[InlineData(AbLegacyDataType.PlsElement, "LEN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Int32)]
|
||||
// BT control words + status bits.
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "RLEN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Int32)]
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "DLEN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Int32)]
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "EN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "DN", ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Boolean)]
|
||||
public void Structure_subelements_resolve_to_expected_driver_type(
|
||||
AbLegacyDataType type, string sub, ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType expected)
|
||||
{
|
||||
AbLegacyDataTypeExtensions.EffectiveDriverDataType(type, sub).ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// PD bits in word 0.
|
||||
[InlineData(AbLegacyDataType.PidElement, "EN", 0)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "PE", 1)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "DN", 2)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "MO", 3)]
|
||||
// MG/BT share the same 8..15 layout.
|
||||
[InlineData(AbLegacyDataType.MessageElement, "TO", 8)]
|
||||
[InlineData(AbLegacyDataType.MessageElement, "EN", 15)]
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "TO", 8)]
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "EN", 15)]
|
||||
public void Structure_status_bit_indices_match_rockwell(
|
||||
AbLegacyDataType type, string sub, int expectedBit)
|
||||
{
|
||||
AbLegacyDataTypeExtensions.StatusBitIndex(type, sub).ShouldBe(expectedBit);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// PD: PE + DN + SP_VAL/SP_LL/SP_HL are PLC-set (read-only); EN + MO + AUTO + MAN are
|
||||
// operator-controllable.
|
||||
[InlineData(AbLegacyDataType.PidElement, "PE", true)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "DN", true)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "SP_VAL", true)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "EN", false)]
|
||||
[InlineData(AbLegacyDataType.PidElement, "MO", false)]
|
||||
// MG/BT: ST/DN/ER/CO/EW/NR/TO are PLC-set; EN is operator-driven.
|
||||
[InlineData(AbLegacyDataType.MessageElement, "DN", true)]
|
||||
[InlineData(AbLegacyDataType.MessageElement, "ER", true)]
|
||||
[InlineData(AbLegacyDataType.MessageElement, "TO", true)]
|
||||
[InlineData(AbLegacyDataType.MessageElement, "EN", false)]
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "DN", true)]
|
||||
[InlineData(AbLegacyDataType.BlockTransferElement, "EN", false)]
|
||||
public void Structure_plc_set_status_bits_are_marked_read_only(
|
||||
AbLegacyDataType type, string sub, bool expected)
|
||||
{
|
||||
AbLegacyDataTypeExtensions.IsPlcSetStatusBit(type, sub).ShouldBe(expected);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user