Skip to content

Fix UnicodeDecodeError/KeyError bugs, add ACD-to-L5X export, expand README#18

Merged
hutcheb merged 2 commits intohutcheb:mainfrom
jpect:main
Mar 26, 2026
Merged

Fix UnicodeDecodeError/KeyError bugs, add ACD-to-L5X export, expand README#18
hutcheb merged 2 commits intohutcheb:mainfrom
jpect:main

Conversation

@jpect
Copy link
Copy Markdown
Contributor

@jpect jpect commented Mar 26, 2026

Summary

This PR fixes several bugs encountered when parsing real-world ACD files and adds the long-requested ACD-to-L5X conversion feature.

Bug fixes

acd/generated/comments/fafa_coments.py

  • Add errors="replace" to all .decode("UTF-8") calls — fixes UnicodeDecodeError when controller comments contain non-ASCII bytes (e.g. degree symbols, accented characters)

acd/l5x/elements.py

  • DataTypeBuilder: guard extended_records key lookups to fix KeyError on UDTs with missing optional attributes
  • DataTypeBuilder: fix member iteration to use actual children list length rather than member_count field — prevents IndexError on mismatched layouts
  • TagBuilder: read tag name from length-prefixed UTF-8 field in ext[0x01] correctly
  • ControllerBuilder: extract serial_number, comm_path, and SFC fields using correct extended-record attribute offsets
  • MapDeviceBuilder: handle cip_type high-byte flags (e.g. 0x8069) so all module types are parsed without silently returning empty MapDevice objects

New feature — ACD to L5X conversion

acd/api.py

  • Add ConvertAcdToL5x class — parses an ACD file and writes a valid L5X XML file importable by Studio 5000 Logix Designer
  • Pretty-prints by default; pretty_print=False for compact output
from acd.api import ConvertAcdToL5x
ConvertAcdToL5x("MyController.ACD", "MyController.L5X").extract()

README

  • Rewrite with comprehensive examples covering tags, programs/routines/rungs, UDTs, AOIs, hardware modules, low-level SQLite access, and the new ACD-to-L5X conversion
  • Add project structure overview and Python version compatibility note

Tested against

  • Studio 5000 v33 ACD file with 203 tags, 6 programs, 371 rungs, 293 UDTs, 17 AOIs, 9 hardware modules
  • Python 3.14

jpect and others added 2 commits March 25, 2026 17:56
- fafa_coments.py: add errors='replace' to all .decode('UTF-8') calls so
  controller comments with non-ASCII bytes no longer raise UnicodeDecodeError
- elements.py: guard extended_records key lookups in DataTypeBuilder so
  UDTs with missing optional attributes (e.g. key 1) don't raise KeyError
- elements.py: fix tag name extraction to read the length-prefixed UTF-8
  name from ext[0x01] rather than falling back to comp_name
- elements.py: add serial_number, comm_path, sfc_* fields to
  ControllerBuilder using correct extended-record attribute offsets
- elements.py: handle cip_type high-byte flags (e.g. 0x8069) in
  MapDeviceBuilder so all module types are parsed correctly
- elements.py: fix mismatched member_count vs children list iteration in
  DataTypeBuilder to avoid IndexError on some UDT layouts

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- api.py: add ConvertAcdToL5x class that parses an ACD file and writes
  a pretty-printed (or compact) L5X XML file importable by Studio 5000;
  uses the existing RSLogix5000Content.to_xml() serialisation
- README: rewrite with comprehensive usage examples covering tags,
  programs/routines/rungs, UDTs, AOIs, hardware modules, low-level
  SQLite access, and the new ACD-to-L5X conversion

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@hutcheb
Copy link
Copy Markdown
Owner

hutcheb commented Mar 26, 2026

Looks great :)

@hutcheb hutcheb merged commit cb91fa2 into hutcheb:main Mar 26, 2026
5 checks passed
jpect added a commit to jpect/acd that referenced this pull request Mar 27, 2026
The DataTypeBuilder and TagBuilder improvements in PR hutcheb#18 correctly changed
the iteration order of data_types and the resolved names/indices of tags.
The existing tests relied on fragile positional lookups (data_types[-1],
tags[75]) that broke when the parser returned more accurate results.

Replace with name-based lookups:
- test_parse_datatypes_dat: find STRING20 by name, then find DATA member by name
- test_parse_tags_dat: find the Toggle tag by name, assert data_type == BOOL

This is strictly better test design — positional assertions are brittle and
provide no signal about which element was actually found.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
jpect added a commit to jpect/acd that referenced this pull request Mar 27, 2026
The DataTypeBuilder and TagBuilder improvements in PR hutcheb#18 correctly changed
the iteration order of data_types and the resolved names/indices of tags.
The existing tests relied on fragile positional lookups (data_types[-1],
tags[75]) that broke when the parser returned more accurate results.

Replace with name-based lookups:
- test_parse_datatypes_dat: find STRING20 by name, then find DATA member by name
- test_parse_tags_dat: find the Toggle tag by name, assert data_type == BOOL

This is strictly better test design — positional assertions are brittle and
provide no signal about which element was actually found.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
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.

2 participants