@@ -109,4 +109,103 @@ public static class AbLegacyDataTypeExtensions
|
||||
AbLegacyDataType.MicroLogixFunctionFile => DriverDataType.Int32,
|
||||
_ => DriverDataType.Int32,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Sub-element-aware driver type. Timer/Counter/Control elements expose Boolean status
|
||||
/// bits (<c>.DN</c>, <c>.EN</c>, <c>.TT</c>, <c>.CU</c>, <c>.CD</c>, <c>.OV</c>,
|
||||
/// <c>.UN</c>, <c>.ER</c>, etc.) and Int32 word members (<c>.PRE</c>, <c>.ACC</c>,
|
||||
/// <c>.LEN</c>, <c>.POS</c>). Unknown sub-elements fall back to
|
||||
/// <see cref="ToDriverDataType"/> so the driver remains permissive.
|
||||
/// </summary>
|
||||
public static DriverDataType EffectiveDriverDataType(AbLegacyDataType t, string? subElement)
|
||||
{
|
||||
if (subElement is null) return t.ToDriverDataType();
|
||||
var key = subElement.ToUpperInvariant();
|
||||
return t switch
|
||||
{
|
||||
AbLegacyDataType.TimerElement => key switch
|
||||
{
|
||||
"EN" or "TT" or "DN" => DriverDataType.Boolean,
|
||||
"PRE" or "ACC" => DriverDataType.Int32,
|
||||
_ => t.ToDriverDataType(),
|
||||
},
|
||||
AbLegacyDataType.CounterElement => key switch
|
||||
{
|
||||
"CU" or "CD" or "DN" or "OV" or "UN" => DriverDataType.Boolean,
|
||||
"PRE" or "ACC" => DriverDataType.Int32,
|
||||
_ => t.ToDriverDataType(),
|
||||
},
|
||||
AbLegacyDataType.ControlElement => key switch
|
||||
{
|
||||
"EN" or "EU" or "DN" or "EM" or "ER" or "UL" or "IN" or "FD" => DriverDataType.Boolean,
|
||||
"LEN" or "POS" => DriverDataType.Int32,
|
||||
_ => t.ToDriverDataType(),
|
||||
},
|
||||
_ => t.ToDriverDataType(),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bit position within the parent control word for Timer/Counter/Control status bits.
|
||||
/// Returns <c>null</c> if the sub-element is not a known bit member of the given element
|
||||
/// type. Bit numbering follows Rockwell DTAM / PCCC documentation.
|
||||
/// </summary>
|
||||
public static int? StatusBitIndex(AbLegacyDataType t, string? subElement)
|
||||
{
|
||||
if (subElement is null) return null;
|
||||
var key = subElement.ToUpperInvariant();
|
||||
return t switch
|
||||
{
|
||||
// T4 element word 0: bit 13=DN, 14=TT, 15=EN.
|
||||
AbLegacyDataType.TimerElement => key switch
|
||||
{
|
||||
"DN" => 13,
|
||||
"TT" => 14,
|
||||
"EN" => 15,
|
||||
_ => null,
|
||||
},
|
||||
// C5 element word 0: bit 10=UN, 11=OV, 12=DN, 13=CD, 14=CU.
|
||||
AbLegacyDataType.CounterElement => key switch
|
||||
{
|
||||
"UN" => 10,
|
||||
"OV" => 11,
|
||||
"DN" => 12,
|
||||
"CD" => 13,
|
||||
"CU" => 14,
|
||||
_ => null,
|
||||
},
|
||||
// R6 element word 0: bit 8=FD, 9=IN, 10=UL, 11=ER, 12=EM, 13=DN, 14=EU, 15=EN.
|
||||
AbLegacyDataType.ControlElement => key switch
|
||||
{
|
||||
"FD" => 8,
|
||||
"IN" => 9,
|
||||
"UL" => 10,
|
||||
"ER" => 11,
|
||||
"EM" => 12,
|
||||
"DN" => 13,
|
||||
"EU" => 14,
|
||||
"EN" => 15,
|
||||
_ => null,
|
||||
},
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PLC-set status bits — read-only from the OPC UA side. Operator-controllable bits
|
||||
/// (e.g. <c>.EN</c> on a timer/counter, <c>.CU</c>/<c>.CD</c> rung-driven inputs) are
|
||||
/// omitted so they keep default writable behaviour.
|
||||
/// </summary>
|
||||
public static bool IsPlcSetStatusBit(AbLegacyDataType t, string? subElement)
|
||||
{
|
||||
if (subElement is null) return false;
|
||||
var key = subElement.ToUpperInvariant();
|
||||
return t switch
|
||||
{
|
||||
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",
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user