fix(cpn):board hardware definition not loaded for firmware objects with empty hwdefnId - #7744
Draft
pfeerick wants to merge 2 commits into
Draft
fix(cpn):board hardware definition not loaded for firmware objects with empty hwdefnId#7744pfeerick wants to merge 2 commits into
pfeerick wants to merge 2 commits into
Conversation
…registration Firmware::Firmware() passed hwdefnId to gBoardFactories->registerBoard() as-is, without the getFlavour() fallback that Firmware::getHwDefnId() already applies. Since registerOpenTxFirmwares() constructs every base firmware object without an explicit hwdefnId, BoardJson::loadFile() bailed out immediately (hwdefn.isEmpty()) and that board's hardware definition (sticks, switches, trims, etc.) never loaded. Any profile whose stored fwType() resolves to one of these "bare" firmware objects - e.g. a profile whose fwType was never explicitly set via Preferences and so defaulted to Firmware::getDefaultVariant() in companion.cpp - hits this broken object. With no stick data loaded, Boards::getInputYamlIndex() fails to resolve names like "Thr"/"Ail", and YAML source decoding collapses them to SOURCE_TYPE_NONE, silently dropping expoData/mixData srcRaw sources on model load. This is a regression from EdgeTX#4406, which introduced JSON-based hardware definitions and made an empty hwdefnId significant for the first time; before that, board capability came from hardcoded tables and hwdefnId was unused. It affects both the 2.12 branch and main identically. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pfeerick
marked this pull request as draft
September 2, 2026 02:52
registerOpenTxFirmwares() now actually needs the embedded hwdefs.qrc resource (previously every board's hwdefnId was empty, so BoardJson::loadFile() bailed out before ever touching it). That resource is bundled into the "common" static library, so per Qt's resource-in-static-lib rules it must be force-initialized with Q_INIT_RESOURCE(hwdefs) - companion.cpp and simulator.cpp already do this at startup, but the gtest entry point never did, since it never needed to. Without it, gtests-companion crashes: the first board loaded via registerOpenTxFirmwares() can't find its resource, BoardJson::loadFile() calls QMessageBox::critical(), and that aborts under the QCoreApplication (no QApplication) the test harness constructs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Might be fixed in #7623
Summary
Companion silently drops model input sources (
expoData/mixDatasrcRaw, e.g. Thr/Ail/Ele/Rud) toSOURCE_TYPE_NONEwhen loading a model YAML, for any profile whose firmware type resolves to a "bare" (non-variant)Firmwareobject - most commonly a profile whosefwTypewas never explicitly saved via Preferences and so defaulted toFirmware::getDefaultVariant().Root cause
Firmware::Firmware()registers the board's hardware definition like this:registerOpenTxFirmwares()constructs every base firmware object with an emptyhwdefnId- it relies onFirmware::getFlavour()as a fallback, the same wayFirmware::getHwDefnId()already does:But the constructor's direct call to
registerBoard()bypassed that fallback and passed the raw, empty field.BoardFactory::loadFile()bails out immediately when the hwdefn name is empty:...with no warning. That board's
BoardJsondata (sticks, switches, trims, etc.) never loads, so every capability query for it returns 0/empty for the lifetime of the app.A "variant"
Firmwareobject (created via the(id, parent)constructor, e.g. for language variants such asedgetx-tx16s-en) resolveshwdefnIdthroughparent->getHwDefnId(), which does apply the fallback, so it registers correctly. SinceAppPreferencesDialog::getFirmwareVariant()always appends a language suffix, any profile saved fresh through Preferences ends up with a decorated id and never hits the bug.However,
companion.cppdefaults a profile with an emptyfwType()straight to the bare id:Any profile that ends up with that bare id resolves to the broken
Firmwareobject. With no stick data loaded,Boards::getInputYamlIndex("Thr", ...)etc. fail to resolve, andYamlRawSourceDecode()collapses the source toSOURCE_TYPE_NONE- silently dropping the input source on every model load for that profile.Regression origin
This is a regression from #4406 ("Support JSON radio hardware definitions"), which introduced
registerBoard()/JSON-based hardware definitions and made an emptyhwdefnIdsignificant for the first time. Before that PR, board capability came from hardcoded C++ tables and the firmware object'shwdefnIdfield was never used to look anything up, so this code path was inert. The defect has been present, unchanged, in both the 2.12 branch andmainsince #4406 merged.Steps to reproduce
mainqualifies).fwTypevalue in Companion's settings back to a bare id (e.g.edgetx-tx16s, with no trailing-<lang>suffix), simulating a profile whosefwTypewas never explicitly saved via Preferences.companion.cppseesfwType()is empty/unset in some cases and defaults it toFirmware::getDefaultVariant()->getId()- a bare id - if it wasn't already bare from step 2.expoData[i].srcRawdecodes toSOURCE_TYPE_NONEinstead of the expected stick source - visible in the Inputs/Mixer UI as the source field being blank, and confirmed by inspecting the decodedRawSource(type == SOURCE_TYPE_NONE).fwTypecarries a language-decorated id (e.g.edgetx-tx16s-en) saved via Preferences → General → Firmware - the same model loads correctly, because that path already applies thegetFlavour()fallback viagetHwDefnId().Fix
Apply the same
getFlavour()fallback in theFirmwareconstructor before callingregisterBoard(), so a "bare" firmware object registers its hardware definition correctly regardless of which code path constructed it:This mirrors existing logic already used by
getHwDefnId(). Verified that every current call site passes an emptyhwdefnIdfor base board registration (onlydownloadIdis ever overridden, for the 4 boards needing alternate download filenames), so this fallback is exercised universally and consistently for all boards, and that the later "variant" registration for the same board always resolves to the identical hwdefn string (idempotent no-op viaBoardFactories::registerBoard's existing equality check) - so there's no risk of the duplicate-registration conflict path being triggered by this change.Test plan
fwType(e.g.edgetx-tx16s) now correctly decodes stick-basedRawSourcevalues (Thr/Ail/Ele/Rud) on model load.fwType(e.g.edgetx-tx16s-en) is unaffected (unchanged behavior).🤖 Generated with Claude Code