Skip to content

Cache element parents for the section state lookup - #103

Open
gunicsba wants to merge 1 commit into
developfrom
fix/section-parent-lookup
Open

gunicsba wants to merge 1 commit into
developfrom
fix/section-parent-lookup

Conversation

@gunicsba

Copy link
Copy Markdown
Contributor

Summary

Closes #100.

For condensed-work-state implements, get_section_actual_state() calls is_element_or_parent_off(). That function used to search the whole object pool for an element's parent on every call, three loops deep and once per level. It runs for every section from the AOG heartbeat (PGN 0xF0, every 100 ms) and from the VT section map.

Now each element's parent is read once from its parent object ID when the pool is activated (ClientState::build_element_hierarchy()), and the check just walks that map. The lookup lives in a small header, include/element_hierarchy.hpp, so it can be unit tested without the TC server.

Behaviour change

The old search looked for parents in element child lists. In ISO 11783-10 those lists only reference process data and properties, so it rarely found the real parent. Its fallback, added for the Müller master switch in #41, matched elements through ddiToElementNumber. That map keeps one element per DDI, so it could pick a wrong element.

I compared old and new on every pool in the fixture collection (25 pools):

Old New
Real parent found Rarely Always (from the parent object ID)
Wrong "parent" Sulky 12 sec: sibling sections (8 → 13, 11 → 15). Kuhn Espro: 1 → 2 (real parent is 0). Sulky 6 sec: cycle 2 → 0 → 2 None
Müller pools (#41) Condensed host 2 → 0 Same
64 lookups, desktop PC 1–23 ms about 4 µs

The only pools where the reported section state can change are those where the condensed-state element now also checks an ancestor that reports DDI 141 (Actual Work State). That's the root element in the AgIsoStack example seeder, the AGL sprayer, and the MFG89 E-COM UD and IXSPRAY demos. If that ancestor reports "not working", the sections now report OFF to AOG. That's the master-switch behaviour #41 was aiming for.

Implements using per-element control still return early and are not affected.

Also

Testing

  • New element_hierarchy CTest: parents come from parent object IDs; only ancestors are checked (a sibling or child being off doesn't count); a malformed pool with a parent cycle terminates.
  • All 7 tests pass. Release build passes on Windows (MSVC). clang-format and cmake-format are clean.
  • Not yet run on a Pi or a real implement. @chriskinal, your CM4 setup with the 64-section simulator would be the best check that the thread load drops.

🤖 Generated with Claude Code

is_element_or_parent_off() searched the whole pool for an element's
parent on every call, nested three loops deep and once per level. It
runs for every section from the AOG heartbeat and the VT section map,
which cost about 2.3 ms a call on a Raspberry Pi CM4 with 64 sections
(#100).

The parent of each element is now read once from its parent object ID
when the pool is activated, and the check walks that map. On the fixture
pools, 64 lookups drop from 1-23 ms to about 4 us on a desktop PC.

The old search looked for parents in element child lists, which in
ISO 11783-10 only reference process data and properties, so it rarely
found the real parent. Its fallback matched elements through the
DDI-to-element map, which keeps one element per DDI, and could pick a
sibling or loop (seen on the Sulky and Kuhn fixture pools). Using the
parent object IDs gives the real ancestors; on the Müller pools from #41
the result for the condensed section element is unchanged.

Also adds the missing <algorithm> include for std::find_if.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

get_section_actual_state() is slow with many sections: 2.3 ms a call on a Raspberry Pi

1 participant