Skip to content

Generate devid.json and devid.json5 from code, use in decode_devid.py - #34396

Merged
tridge merged 2 commits into
ArduPilot:masterfrom
peterbarker:pr-claude-wt/device-json
Sep 16, 2026
Merged

tridge merged 2 commits into
ArduPilot:masterfrom
peterbarker:pr-claude-wt/device-json

Conversation

@peterbarker

@peterbarker peterbarker commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Tools/scripts/decode_devid.py now reads bus and device types from the C++ enums instead of keeping its own copies of the tables, which had drifted. It can also write the tables as devid.json / devid.json5 (with format and data versions). Those files are published with the LogMessages documentation, and CI checks that the headers still parse.

Classification & Testing (check all that apply and add your own)

  • Checked by a human programmer
  • Non-functional change
  • No-binary change
  • Infrastructure change (e.g. unit tests, helper scripts)
  • Automated test(s) verify changes (e.g. unit test, autotest)
  • Tested manually, description below (e.g. SITL)
  • Tested on hardware
  • Logs attached
  • Logs available on request

Testing:

  • flake8 and mypy are clean on decode_devid.py.
  • Compared output against the previous script over 174 cases (29 device IDs, each with no flag and with each of -C/-I/-B/-A/-M, including invalid, negative, out-of-range and DroneCAN IDs). The only differences are the intended name fixes and newly decoded types listed below.
  • Output using a file from --dump-json, read back with --json, is identical to live header parsing in all cases.
  • devid.json is valid JSON. devid.json5 parses with the Python json5 package to the same data as devid.json.
  • Edited copies of the headers confirm the script errors clearly on:
    • an enum entry enum_parse can't evaluate, which would otherwise make it silently drop the whole enum
    • a renamed enum
    • a stale rename-map entry
    • reuse of the retired 0x19 compass ID
  • load_devid_json rejects files with a wrong format_version, a missing data_version, a missing category or duplicate values.
  • Ran build_log_message_documentation.sh in full with BUILDLOGS pointing to a scratch directory. It exited 0, and the published devid.json/devid.json5 match direct dumps.
  • The new CI command exits 0, and logger_metadata/parse.py still runs with the extended AP_Compass_Backend.h comment.
  • A copy of the script laid out like MethodicConfigurator's imports and decodes correctly with a devid.json beside it; without one, the import still succeeds and only the calls fail, with an error naming both places it looked. mypy with MethodicConfigurator's own settings at Python 3.10 reports no issues.
  • Run through a symlink from another directory, the script finds the tree and decodes correctly.
  • A malformed --json file (a top-level array, a missing file, truncated JSON) and an unwritable --dump-json/--dump-json5 path each print an error and exit 1 rather than a traceback.

Description

The device-type tables in decode_devid.py were hand-maintained copies of enums in the firmware, and they had drifted:

  • DEVTYPE_ACC_LSM9DS1 (0x18), DEVTYPE_INS_ZEROONE_FPGA_SCH16T (0x41) and DEVTYPE_INS_ICM56686 (0x44) were missing.
  • 0x13 was still MMC5883, although the header was changed to MMC5983 in 4c756fd.
  • AK8963 and BMM150 had trailing spaces.
  • The RM3100 names were the reverse of the header's.

Live parsing. The script now parses the enums with the existing Tools/autotest/logger_metadata/enum_parse.py:

Table Enum
bus types AP_HAL::Device::BusType
compass AP_Compass_Backend::DevTypes
IMU AP_InertialSensor_Backend::DevTypes
baro AP_Baro_Backend::DevTypes
airspeed AP_Airspeed_Backend::DevType
MAVLink AP_SerialManager::DeviceType

Beyond the enums themselves, the script has:

  • Per-table prefix rules: bus names drop BUS_TYPE_, airspeed names get DEVTYPE_AIRSPEED_.
  • A small rename map for established display names: UAVCANDRONECAN, CANBUSCAN, and DEVTYPE_AK09916DEVTYPE_AK0991x (from ab3b68c). A rename that no longer matches an enum entry is an error.
  • One ID that is in no enum but still turns up in parameters and logs: the retired LIS2MDL 0x19. Colliding with an enum value is an error.

Warnings from enum comments. A comment on an enum entry becomes that entry's description and is printed as a warning when the ID is decoded. The DEVTYPE_RM3100_2 comment now records that 0x12 was only used by master firmware from 2020-03-26 (dd4cf6c) to 2020-05-23 (a0cf4e1):

$ Tools/scripts/decode_devid.py -C 0x120000
Warning: devtype 0x12 (DEVTYPE_RM3100_2): unused, past mistake; this RM3100 ID was only used by master firmware from 2020-03-26 to 2020-05-23
bus_type:UNKNOWN(0)  bus:0 address:0(0x0) devtype:18(0x12) DEVTYPE_RM3100_2

JSON output for other tools. For tools without an ArduPilot source tree (GCSs, log analysers):

  • --dump-json FILE writes devid.json.
  • --dump-json5 FILE writes devid.json5: the same data with 0xNN values and unquoted keys.
  • --json FILE makes the script use such a file instead of the headers.
  • A copy of the script outside an ArduPilot tree reads devid.json from its own directory, which is how MethodicConfigurator's synced copy will work. Inside a tree the headers always win.

Both files contain:

  • format_version (currently 1), the version of the file structure
  • data_version, a sha256: hash of the tables, which changes exactly when the data changes
  • bus_types and device_types.{compass,imu,baro,airspeed,mavlink} as lists of {value, name[, description]}

Build server and CI.

  • build_log_message_documentation.sh publishes devid.json and devid.json5 in LogMessages/, next to the per-vehicle LogMessages.*.
  • The logger_metadata CI step dumps both files to /dev/null, so a header change that breaks parsing fails CI.

Compatibility. Code that imports the module still works:

  • BUSTYPES, COMPASS_TYPES, IMU_TYPES, BARO_TYPES, AIRSPEED_TYPES and MAVLINK_TYPES are still provided, now built when first used, and __all__ keeps them available through from decode_devid import *.
  • The module's __getattr__ checks the attribute name before loading anything, so importing works outside an ArduPilot tree even with no devid.json present; only the calls then fail.
  • decode_device_id, get_device_type_name and format_device_info are unchanged.

Output changes from before (all now match the headers):

  • DEVTYPE_RM3100_OLD/DEVTYPE_RM3100DEVTYPE_RM3100/DEVTYPE_RM3100_2
  • DEVTYPE_MMC5883DEVTYPE_MMC5983
  • DEVTYPE_INS_SERIALDEVTYPE_SERIAL
  • DEVTYPE_INS_BMI270DEVTYPE_BMI270
  • DEVTYPE_INS_ADIS1647xDEVTYPE_INS_ADIS1647X
  • trailing spaces removed from DEVTYPE_AK8963 and DEVTYPE_BMM150

Behaviour changes.

  • The script now needs an ArduPilot source tree, a devid.json beside it, or a file passed with --json.
  • Adding a comment to any entry in these enums will make the script print that comment as a warning.
  • MAVLink devtype 0x00 now decodes as DEVTYPE_MAVLINK_UNKNOWN rather than the literal UNKNOWN.

Follow-up (not needed here). enum_parse.py has some long-standing quirks: unanchored patterns mean 0x18 + 1 parses as 0x18, a comment containing }; ends an enumeration early, and one /* */ pattern mis-parses hexadecimal entries. No header in the tree uses those forms, and the tables generated here are identical either way, so hardening it (with unit tests, and converting the one header that uses /* */ comments in enumerations) is a separate branch.

claude helped!

@peterbarker

Copy link
Copy Markdown
Contributor Author

Will stop ArduPilot/MissionPlanner#3772 and mavlink/qgroundcontrol#15047 from being required. Hopefully.

@tridge

tridge commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Deprecated — see below for the updated review.

Previous review (2026-09-14)

Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting.

Reviewed at head acb1b35ef3 (draft). Full report: https://uav.tridgell.net/DevCallReviews/2026_09_14_AIReview/devcall_pr_reviews.html#pr34396

Verdict: REQUEST CHANGES. The generated tables are correct:

  • Every devtype 0-255 in all five categories, and every bus type, matches the old tables apart from the renames you list.
  • An independent Codex pass matched all 111 compiled enumerators.

The problem is a downstream copy of the script.

Bug

  • Tools/scripts/decode_devid.py:294-301 / :40 / :257-265: MethodicConfigurator's copy of this script fails at import.
    • MethodicConfigurator's update_flightcontroller_ids.yml copies this file into ardupilot_methodic_configurator/decode_devid.py.
    • data_model_vehicle_overview_sensor_rules.py then imports DeviceCategory, decode_device_id, get_device_type_name from it.
    • In that copy REPO_ROOT points to a place with no headers.
    • The import machinery probes hasattr(module, "__path__"), and __getattr__ loads the tables before checking the name. The probe gets FileNotFoundError rather than AttributeError, so the import line itself fails. Reproduced with MethodicConfigurator's exact import line; the old file works.
    • A name check alone isn't enough: decode_device_id() and get_device_type_name() still raise, because nothing falls back to a devid.json beside the script.
    • "Code that imports the module still works" holds only inside an ArduPilot tree.
    • Suggested: raise AttributeError for unknown names before loading, fall back to a devid.json next to the script, and coordinate the sync with MethodicConfigurator.

Notes

  • from decode_devid import * no longer exports the legacy tables (no __all__), and outside a tree any unknown attribute raises FileNotFoundError. The same name check fixes this.

  • The enum parser (enum_parse.py:69) accepts partial matches:

    • = 0x18 + 1, parses as 0x18.
    • (1U<<4) | 8 parses as 16.
    • A second hex entry on the same line is lost.
    • Comments without a following comma are dropped.

    No current header uses these forms, so nothing is wrong today. But the new warnings rely on this parser.

Checked:

  • CLI output and exit codes are identical to the old script across all switches and bad input.
  • --dump-json round-trips byte-identically.
  • vermin reports 3.8, and a real Python 3.8.20 writes identical json.
  • flake8 and mypy are clean.
  • MAVProxy, WebTools and Mission Planner have their own decoders, so they are unaffected.

CI was still running when checked.

@peterbarker
peterbarker force-pushed the pr-claude-wt/device-json branch from acb1b35 to 3ddfcff Compare September 14, 2026 08:56
@github-actions github-actions Bot added the CI label Sep 14, 2026
@peterbarker

Copy link
Copy Markdown
Contributor Author

@amilcarlucas - what are your thoughts on all of this?

decode_devid.py is changing: what copies and importers need to do

ArduPilot PR #34396 changes how Tools/scripts/decode_devid.py gets its bus-type and device-type tables. This note is for projects that copy or import that script, mainly ArduPilot Methodic Configurator (AMC).

What is changing

  • Tables come from the firmware. decode_devid.py no longer contains hard-coded tables. Inside an ArduPilot source tree it reads them from the C++ enums (AP_HAL/Device.h, and the compass, IMU, baro and airspeed backend headers).
  • It can write the tables to a file. decode_devid.py --dump-json FILE writes the same tables as JSON. --dump-json5 FILE writes them as JSON5 with hex values.
  • A copy needs devid.json beside it. A copy of the script outside an ArduPilot tree reads devid.json from its own directory.
  • Without that file, imports work but calls fail. Calling decode_device_id(), get_device_type_name() or get_bus_type_name(), or using a *_TYPES table, raises FileNotFoundError. The error message names both places the script looked.
  • The functions you call are unchanged. decode_device_id, get_device_type_name, format_device_info, parse_device_id, DeviceCategory and DeviceInfo keep the same signatures. BUSTYPES, COMPASS_TYPES, IMU_TYPES, BARO_TYPES, AIRSPEED_TYPES and MAVLINK_TYPES are still available, including through from decode_devid import *.

Action for ArduPilot Methodic Configurator

AMC's .github/workflows/update_flightcontroller_ids.yml copies decode_devid.py into ardupilot_methodic_configurator/. Once #34396 is merged, that copy needs a devid.json next to it. Without one, log_analysis/data_model_vehicle_overview_sensor_rules.py still imports but fails when it decodes a device.

1. Generate devid.json in the sync workflow

In the "Install dependencies" step, add this after the existing cp ../ardupilot/Tools/scripts/decode_devid.py ... line:

# decode_devid.py reads its tables from devid.json when it is not in an ArduPilot tree.
# Older ArduPilot versions have the tables built in and no --dump-json option.
if grep -q -- '--dump-json' ../ardupilot/Tools/scripts/decode_devid.py; then
  python ../ardupilot/Tools/scripts/decode_devid.py --dump-json ardupilot_methodic_configurator/devid.json
fi

Run the script from the ArduPilot checkout (../ardupilot/...), not from the copy, because only that one can see the C++ headers. The grep check means the step works on ArduPilot versions from before and after #34396, so this change can go in now.

In the "Stage changes" step, add:

git add ardupilot_methodic_configurator/devid.json 2>/dev/null || true

Commit the generated devid.json together with the copied decode_devid.py, so the two always match.

2. Packaging, linting and hooks: nothing to change

We checked each of these against AMC's current config:

  • Packaging: [tool.setuptools.package-data] already includes **/*.json, so the file ships in the wheel next to decode_devid.py.
  • pre-commit: check-json only validates the file, and the generated file is valid JSON. It ends with a newline and has no trailing whitespace.
  • mypy: AMC's [tool.mypy] settings with --python-version 3.10 report no issues on the new decode_devid.py.
  • ruff: decode_devid.py is already excluded as a synced file.

3. Check it

From an AMC checkout, with an ArduPilot tree that includes #34396 next to it:

cp ../ardupilot/Tools/scripts/decode_devid.py ardupilot_methodic_configurator/
python ../ardupilot/Tools/scripts/decode_devid.py --dump-json ardupilot_methodic_configurator/devid.json
python -c "
from ardupilot_methodic_configurator.decode_devid import DeviceCategory, decode_device_id, get_device_type_name
decoded, _ = decode_device_id(0x2C0011)
print(get_device_type_name(decoded['devtype'], 'imu'), decoded['bus_type_name'])
"
# expected: DEVTYPE_INS_ICM20948 I2C

Then run AMC's test suite.

4. Device names users will see change

The tables now follow the firmware headers, so a few names differ from the old hard-coded ones. After AMC's clean_devtype() removes the prefixes, the visible changes are:

devtype Category Before After
0x04 compass AK8963 (trailing space) AK8963
0x05 compass BMM150 (trailing space) BMM150
0x11 compass RM3100_OLD RM3100
0x12 compass RM3100 RM3100_2
0x13 compass MMC5883 MMC5983
0x31 imu ADIS1647x ADIS1647X
0x18 imu UNKNOWN ACC_LSM9DS1
0x41 imu UNKNOWN ZEROONE_FPGA_SCH16T
0x44 imu UNKNOWN ICM56686

These names change in the tables but look the same after clean_devtype(): DEVTYPE_INS_SERIALDEVTYPE_SERIAL and DEVTYPE_INS_BMI270DEVTYPE_BMI270.

AMC's current test (clean_devtype("DEVTYPE_INS_ICM42688")) is not affected.

5. Optional: show device-type warnings

Some device types have a note in the firmware header. For example, compass 0x12 (DEVTYPE_RM3100_2) is a mistaken ID that only master firmware used, between 2020-03-26 and 2020-05-23. The command-line tool prints these notes as warnings. Library calls never print anything; to show a note in AMC's UI, use the new function:

from ardupilot_methodic_configurator.decode_devid import get_device_type_description

note = get_device_type_description(devtype, "compass")  # None for most device types

Other tools that decode device IDs

MAVProxy, WebTools and Mission Planner have their own decoders and are not affected. If you'd like to stop maintaining a separate table, you can use ArduPilot's generated files instead:

  • Generate them yourself: run Tools/scripts/decode_devid.py --dump-json devid.json (or --dump-json5 devid.json5) in an ArduPilot checkout.
  • Download them: the autotest server's documentation build publishes devid.json and devid.json5 next to the per-vehicle LogMessages files. Once Generate devid.json and devid.json5 from code, use in decode_devid.py #34396 is deployed, they are expected at https://autotest.ardupilot.org/LogMessages/devid.json and .../devid.json5.

The file layout:

{
    format_version: 1,              // file structure version; readers should reject unknown values
    data_version: "sha256:…",       // hash of the tables; changes whenever any entry changes
    bus_types: [ {value: 0x01, name: "I2C"},  ],
    device_types: {
        compass:  [ {value: 0x12, name: "DEVTYPE_RM3100_2", description: "unused, past mistake; …"},  ],
        imu:      [  ],
        baro:     [  ],
        airspeed: [  ],
        mavlink:  [  ],
    },
}

A device ID packs its fields into bits:

  • bits 0–2: bus type
  • bits 3–7: bus number
  • bits 8–15: address
  • bits 16–23: devtype

For DroneCAN (bus type 3), the devtype byte holds the sensor ID plus one for compasses, and is 0 for other sensors.

Questions go on #34396.

@tridge

tridge commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Deprecated — see below for the updated review.

Previous review (2026-09-14)

Automated review note — AI-generated (Claude), independently cross-checked by a second model and re-verified against the live diff. Please sanity-check before acting.

Reviewed again at head 3ddfcff010 (same head: a second, independent pass for the DevCallEU call); this replaces the review that was here. Full report: https://uav.tridgell.net/DevCallReviews/2026_09_16/devcall_pr_reviews.html#pr34396

The generated tables are correct, but the script now fails when run through a symlink. Verdict: APPROVE → COMMENT. Everything from the earlier rounds is still resolved.

Issue

  • Tools/scripts/decode_devid.py:67: SCRIPT_DIR uses abspath(__file__), so when run through a symlink (e.g. ~/bin/decode_devid.py) REPO_ROOT points outside the checkout and it exits 1 with "ArduPilot C++ headers not found". The old script worked that way. Running it by path from another directory is fine. Fix: os.path.realpath(__file__).

Notes

  • decode_devid.py:97: the comment says the MAVLink types have "no C++ enum" (and the PR body says they only existed in this script), but AP_SerialManager::UARTState::DeviceType (AP_SerialManager.h:234) is what get_device_id() uses, and enum_parse reads it. It could be a normal source with a DEVTYPE_MAVLINK_ prefix and CANBUS→CAN; the values match today.
  • enum_parse.py quirks, all pre-existing and none triggered by the current headers, but devid.json now depends on this parser. Each one still exits 0:
    • 0x18, /* c */ parses as 0 (:57, from the previous round);
    • a comment containing }; ends the enum early (:196);
    • #if/#else alternatives both become rows (:190).
      A small parser test would cover all three.
  • --json: a top-level JSON array gives an AttributeError traceback, and an unwritable dump path gives a traceback rather than an error message.
  • From the previous round: AMC's sync step writing devid.json should land before this, and the anchored patterns add ~32 <description> lines per vehicle to LogMessages (enum values unchanged).

Checked independently by both reviewers: all 111 enumerators match the headers; old and new decoding differ only in the renames you list, over 1662 CLI cases and 30,720 IDs; devid.json5 parses to the same data; the CI header check fails on 8 kinds of header breakage; a real Python 3.8 gives identical output. CI: 102 passing.

@peterbarker
peterbarker marked this pull request as ready for review September 14, 2026 22:44
@peterbarker peterbarker moved this to ReadyForDevCall in Peter's ArduPilot 4.8 Queue Sep 14, 2026
DEVTYPE_RM3100_2 (0x12) was introduced by dd4cf6c and reverted by
a0cf4e1; no release firmware used it.  Tools/scripts/decode_devid.py
shows this comment as a warning when decoding such an ID.
@peterbarker
peterbarker force-pushed the pr-claude-wt/device-json branch from 3ddfcff to 21030ab Compare September 15, 2026 03:16
@tridge

tridge commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Deprecated — see below for the updated review.

Previous review (2026-09-15)

Automated review note — AI-generated (Claude), independently cross-checked by a second model and re-verified against the live diff. Please sanity-check before acting.

Re-reviewed at head 21030ab101 (you were last told 3ddfcff010); my earlier comment above is superseded. Full report: https://uav.tridgell.net/DevCallReviews/followups/2026_09_15_1401/devcall_pr_reviews.html#pr34396

This push is a rebase. The symlink issue from last round is still open. Verdict: COMMENT (unchanged).

The only change to your patch is also dropping 0x44: "DEVTYPE_INS_ICM56686" from the old hand-written table, which is the right conflict resolution: it comes from AP_InertialSensor_Backend.h:183 and -I 0x440000 still decodes to it. devid.json and devid.json5 are byte-identical at both heads.

Still open

  • Tools/scripts/decode_devid.py:67: still os.path.abspath(__file__). Run through a symlink outside the checkout it exits 1 with "ArduPilot C++ headers not found"; master's script works that way. With os.path.realpath(__file__) the same run exits 0 (checked at this head).
  • The notes from last round are unchanged: the MAVLink device types do have a C++ enum (AP_SerialManager::UARTState::DeviceType), the enum_parse.py quirks, the --json tracebacks, and the AMC sync ordering.

CI: 101 passing, 1 pending.

Rather than maintaining copies of the bus and device type tables,
decode_devid.py now parses the enums in AP_HAL/Device.h, AP_SerialManager.h
and the compass, IMU, baro and airspeed backend headers using
logger_metadata/enum_parse.py.  A small rename map keeps established
display names (DRONECAN, AK0991x), and the retired LIS2MDL ID, which is
in no enum, is listed explicitly.  Comments on enum entries are shown as
warnings when decoding.

--dump-json and --dump-json5 write the tables, with a format_version
and a content-hash data_version, for tools without a source tree;
--json reads such a file back.  A copy of the script outside an
ArduPilot tree (as synced by MethodicConfigurator) reads devid.json from
its own directory.  The files are published alongside LogMessages.* by
build_log_message_documentation.sh, and CI dumps them in the
logger_metadata step so that header changes which break parsing are
caught.

The tables had drifted from the headers: ACC_LSM9DS1,
INS_ZEROONE_FPGA_SCH16T and INS_ICM56686 were missing, MMC5883 is now
MMC5983, AK8963/BMM150 had trailing spaces, and several names now
follow the headers.
@peterbarker
peterbarker force-pushed the pr-claude-wt/device-json branch from 21030ab to a1f71a5 Compare September 16, 2026 03:45
peterbarker added a commit to peterbarker/ardupilot that referenced this pull request Sep 16, 2026
…matches

The hex and "1U << n" patterns were not anchored, so "0x18 + 1" parsed
as 0x18, "(1U<<4) | 8" as 16, and a second entry on the same line was
lost; these now fail to match rather than giving wrong values.  The
forms used in the tree (parentheses, U/UL/ULL suffixes) are accepted.

A comment containing "};" no longer ends an enumeration early, and
comments after a closing parenthesis or without a trailing comma,
previously dropped, are now captured; this adds descriptions to the
LogMessages output but leaves enumeration names and values unchanged.

enum_parse_unittests.py covers this and runs in the logger_metadata CI
step.  No header in the tree uses the forms which were parsed wrongly,
but this parser is used to generate device ID metadata in ArduPilot#34396, where
a header written in one of them would give wrong device names.
@AP-Review

Copy link
Copy Markdown

Automated review note — AI-generated (Claude), independently cross-checked by a second model and re-verified against the live diff. Please sanity-check before acting.

Re-reviewed at head a1f71a5a59 (you were last told 21030ab101); my earlier comment above is superseded.

All four open items from the previous round are resolved. Verdict stays COMMENT on two cheap, non-blocking notes below. This push is not a rebase — merge-base with master is 50bc1fa059 at both heads.

Full report: https://uav.tridgell.net/DevCallReviews/followups/2026_09_16_1511/devcall_pr_reviews.html#pr34396

Resolved

  • The symlink failure is fixed. Tools/scripts/decode_devid.py:68 is now os.path.realpath(__file__). Run through a symlink outside the checkout with an unrelated cwd it exits 0 and decodes correctly. Proven by mutation, independently by both passes: reverting that one line to abspath restores exit=1 Error: ArduPilot C++ headers not found.
  • The MAVLink category now comes from a real C++ enumAP_SerialManager::DeviceType, with the four hand-written EXTRA_ENTRIES rows and the Optional[EnumSource] special case deleted, and the stale "no C++ enum" comment removed.
  • Both traceback paths are fixed — a top-level JSON array now gives Error: ...: expected a JSON object, and an unwritable --dump-json target gives Error: [Errno ...], each exit 1.
  • The enum_parse.py hardening was reverted out of this PR and deferred, which you called out explicitly. Agreed it is not needed for correctness today — see the second note below for the one aspect of that worth carrying into the follow-up.

Notes (neither blocks merge)

  • Tools/scripts/decode_devid.py:177 — malformed nested JSON entries still escape the clean-error handling you just added. _validate_entries() calls entry.get("value") before checking the entry is a dict, so a non-dict element raises a raw traceback, while the top-level case you did handle gives a clean message. Reproduced by replacing bus_types[0] in an otherwise valid generated file:

    entry=null   exit=1 traceback=YES  AttributeError: 'NoneType' object has no attribute 'get'
    entry=123    exit=1 traceback=YES  AttributeError: 'int' object has no attribute 'get'
    entry="bad"  exit=1 traceback=YES  AttributeError: 'str' object has no attribute 'get'
    entry=[]     exit=1 traceback=YES  AttributeError: 'list' object has no attribute 'get'
      control (top-level array):  exit=1 traceback=no  Error: arr.json: expected a JSON object
    

    Found independently by both Codex passes. One-line fix: add isinstance(entry, dict) to the check that already rejects a bad value/name.

  • Tools/scripts/decode_devid.py:147 — raising this only because the deferral note frames the parser issue as entries it "can't evaluate", i.e. loud failures. Two of the three cases I reproduced are silent, and one removes an entry that is already in the table. Mutating AP_InertialSensor_Backend.h one change at a time:

    baseline                                             exit=0  imu=43  last=DEVTYPE_INS_ICM56686 (0x44)
    'DEVTYPE_INS_ICM56686 = 0x44, // example: };'        exit=0  imu=42  last=DEVTYPE_INS_LSM6DSK320X   <-- entry LOST
    'DEVTYPE_INS_ICM56686 = 0x44, DEVTYPE_TEST = 0x5F,'  exit=0  imu=43  DEVTYPE_TEST absent            <-- entry LOST
    'DEVTYPE_TEST_EXPR = 0x50 + 1,'                      exit=0  imu=44  value=80, should be 81         <-- value WRONG
    

    A }; inside a trailing comment ends the parse early, so the last real enumerator disappears with exit 0 and no stderr — and build_ci.sh:594 only checks the exit status, so CI stays green. Restoring the previous head's hardened enum_parse.py makes the first and third fail loudly with Error: Failed to match (...). The bounding is genuinely reassuring and worth recording: moved headers, renamed or empty enums, symbolic constants, decimal arithmetic, out-of-range and duplicate values, and #elif all do fail cleanly, and ordinary comments and #ifdef entries parse correctly — so this is a narrow set of syntax to reject, not a rewrite. Nothing is wrong in the tables today; it just seems worth a regression test in the follow-up branch.

Smaller still, not worth acting on separately: no test asserts the generated output (the CI hook checks only the exit status), and the legacy tables are now rebuilt on each attribute access, so BUSTYPES is BUSTYPES is False and in-place mutation of a fetched table is discarded — no consumer does that. AMC's update_flightcontroller_ids.yml still copies only the .py, so it needs its own PR first; nothing to change here.

Verified clean

I established table correctness three independent ways rather than taking the generator's word for it: an eval-based re-parse of all six enums (0 missing, 0 unexpected, 0 name mismatches), a compiled-C++ cross-check (116 enumerators, 0 mismatches), and a sweep of every 24-bit ID across all categories. Round-trip against master gives exactly 11 differences, every one a rename documented in your PR body; -I 0x440000 decodes to DEVTYPE_INS_ICM56686 identically on both. devid.json and devid.json5 parse to identical data, data_version recomputes, and neither file is checked in. vermin reports minimum 3.8 for both master's script and this head's — no regression. flake8, mypy and shellcheck --severity=error clean; LogMessages.xml regenerates with a 0-line diff.

CI at this head: 5 passing, 87 pending, 0 failing, 0 cancelled (snapshotted once, not waited on).

Both an independent Codex pass and this review were run on this head and reconciled. The cold pass argued for REQUEST CHANGES on the parser limitation; I did not carry that, because the current tables are provably correct and you deferred the hardening deliberately.

@tridge
tridge merged commit b2b1b3d into ArduPilot:master Sep 16, 2026
101 checks passed
@github-project-automation github-project-automation Bot moved this from ReadyForDevCall to Done in Peter's ArduPilot 4.8 Queue Sep 16, 2026
@amilcarlucas

Copy link
Copy Markdown
Contributor

AMC 4.4.4 is already using this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants