fix(twincat): size strings by the resolved type and refuse VAR_IN_OUT references (live TC2 gate)
Found by the read-only live gate against the TwinCAT 2.11 Elma cleaners Z33856 / Z33733: - The Beckhoff library reports a TC2 dotted MEMBER symbol's ByteSize as if the PLC's byte count were a bit count (STRING(80) -> 11, INT -> 1, UDINT -> 1) while DataType.ByteSize is right (81/2/4, raw SYM_INFOBYNAMEEX says 81). The previous commit's typeof(byte[]) read sliced by the symbol size, so .OPC_MACHINEDATA.PROCESSSTATUS read "ready for o" at Good; the notification width check would also have refused every numeric member. The shape now takes the resolved type's size, and string reads go by index group/offset (or a fresh handle for 0xF005) at that width. - P_INITINPUT_ACTION.SLOTID is "STRING(20) (VAR_IN_OUT)" - a 4-byte reference - but resolves to a plain STRING(20); a String tag read 21 bytes at the pointer as text at Good. The symbol's own VAR_IN_OUT marker now classifies it NonScalar -> BadTypeMismatch.
This commit is contained in:
@@ -37,6 +37,47 @@ public sealed class TwinCATTypeGuardTests
|
||||
Plc("INT16", 2).IsFloat.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Live on the TwinCAT 2.11 Elma cleaner Z33856 (2026-10-07): for a dotted MEMBER path the
|
||||
/// Beckhoff library reports the symbol's <c>ByteSize</c> as if the PLC's byte count were a bit
|
||||
/// count — <c>.OPC_MACHINEDATA.PROCESSSTATUS</c> (STRING(80)) came back as 11 bytes, an
|
||||
/// <c>INT</c> member as 1, a <c>UDINT</c> as 1 — while its resolved <c>DataType.ByteSize</c> was
|
||||
/// the true 81 / 2 / 4 (raw <c>SYM_INFOBYNAMEEX</c> answers 81). Sizing a string read by the
|
||||
/// symbol truncated <c>"ready for operation"</c> to <c>"ready for o"</c> at Good, and the
|
||||
/// notification width check refused every numeric member. The resolved type's size wins.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Classify_prefers_the_resolved_data_type_size_over_the_symbol_size()
|
||||
{
|
||||
TwinCATPlcSymbolShape.Classify("STRING(80)", DataTypeCategory.String, byteSize: 11, dataTypeByteSize: 81)
|
||||
.ByteSize.ShouldBe(81);
|
||||
var member = TwinCATPlcSymbolShape.Classify("INT", DataTypeCategory.Primitive, byteSize: 1, dataTypeByteSize: 2);
|
||||
member.ByteSize.ShouldBe(2);
|
||||
TwinCATTypeGuard.CheckNotification(TwinCATDataType.Int, member).ShouldBeNull();
|
||||
TwinCATPlcSymbolShape.Classify("UDINT", DataTypeCategory.Primitive, byteSize: 1, dataTypeByteSize: 4).ByteSize.ShouldBe(4);
|
||||
|
||||
// No resolved type (or a zero size) falls back to the symbol's own size.
|
||||
TwinCATPlcSymbolShape.Classify("STRING(30)", null, byteSize: 31, dataTypeByteSize: null).ByteSize.ShouldBe(31);
|
||||
TwinCATPlcSymbolShape.Classify("STRING(30)", null, byteSize: 31, dataTypeByteSize: 0).ByteSize.ShouldBe(31);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Live on Z33856 (2026-10-07): <c>P_INITINPUT_ACTION.SLOTID</c> is a TwinCAT 2
|
||||
/// <c>STRING(20) (VAR_IN_OUT)</c> — a 4-byte reference — but the Beckhoff library resolves its
|
||||
/// <c>DataType</c> to the plain <c>STRING(20)</c> (category String). Classifying by the resolved
|
||||
/// type alone let a String tag read 21 bytes starting at the pointer and publish them as text at
|
||||
/// Good (<c>"n±\u008dâa»‰â…"</c>). The symbol's own type name marks it a reference, and that wins.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Classify_treats_a_var_in_out_symbol_as_non_scalar_even_when_its_resolved_type_is_a_string()
|
||||
{
|
||||
var shape = TwinCATPlcSymbolShape.Classify("STRING(20)", DataTypeCategory.String, byteSize: 4,
|
||||
dataTypeByteSize: 21, symbolTypeName: "STRING(20) (VAR_IN_OUT)");
|
||||
shape.Kind.ShouldBe(TwinCATPlcKind.NonScalar);
|
||||
TwinCATTypeGuard.CheckRead(TwinCATDataType.String, shape).ShouldNotBeNull();
|
||||
TwinCATTypeGuard.CheckNotification(TwinCATDataType.String, shape).ShouldNotBeNull();
|
||||
}
|
||||
|
||||
// ---- read path (symbol-aware: only the string kind can go wrong) ----
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user