Skip to content

Bug: ExtendedEnumInfos per-member attributes pushed to wrong member (inner shadows outer ) #185

@Mschmid0

Description

@Mschmid0

Summary

When a TwinCAT ENUM type has {attribute '...'} pragmas on individual members and the PLC runtime provides ExtendedEnumInfos (TC ≥ 4026), getDataTypes() incorrectly maps all per-member attributes to the first member (enumInfos[0]) instead of the member they belong to.

Root cause

In ads-client.js (v2.2.0) around line 2338, the attribute-parsing inner loop reuses let i as its loop variable:

// outer loop — member index
for (let i = 0; i < enumInfoCount; i++) {
    // ...
    dataType.enumInfos[i].attributes = [];

    // inner loop — attribute index — shadows outer `i` !
    for (let i = 0; i < attributeCount; i++) {
        // ...
        dataType.enumInfos[i].attributes.push(attr);  // <-- inner i, not outer i
    }
}

Inside the inner loop i refers to the attribute index, not the member index. So dataType.enumInfos[i].attributes.push(attr) pushes to enumInfos[0], enumInfos[1], … based on how many attributes the current member has — never to the correct member.

Reproduction

TwinCAT ENUM definition:

{attribute 'qualified_only'}
TYPE E_Color :
(
    {attribute 'cold'}
    White  := 0,
    Blue   := 2,
    {attribute 'warm'}
    Red    := 3
) INT;
END_TYPE

Expected getDataTypes() result for E_Color:

[
  { "name": "White", "value": 0, "attributes": [{ "name": "cold", "value": "" }] },
  { "name": "Blue",  "value": 2, "attributes": [] },
  { "name": "Red",   "value": 3, "attributes": [{ "name": "warm", "value": "" }] }
]

Actual result (ads-client v2.2.0):

[
  { "name": "White", "value": 0, "attributes": [{ "name": "cold", "value": "" }, { "name": "warm", "value": "" }] },
  { "name": "Blue",  "value": 2, "attributes": [] },
  { "name": "Red",   "value": 3, "attributes": [] }
]

Both attributes land on White (index 0) because the inner i cycles 0…1 for the two total attributes found, always addressing enumInfos[0] and enumInfos[1].

The raw ADS DataType binary is correct — confirmed by manually parsing the 0xF00E upload with independent code: White has attributeCount=1 (cold), Red has attributeCount=1 (warm).

Fix

Rename the inner loop variable so it doesn't shadow the outer member index:

-    for (let i = 0; i < attributeCount; i++) {
+    for (let ai = 0; ai < attributeCount; ai++) {
         const attr = {};
         // ...
-        dataType.enumInfos[i].attributes.push(attr);
+        dataType.enumInfos[i].attributes.push(attr);   // outer i (member index) is now correct
     }

Environment

  • ads-client: 2.2.0
  • TwinCAT: 3.1.4026.x
  • Node.js: 20 LTS

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions