feat(dronecan): on-demand GetNodeInfo, GetSet, ExecuteOpcode, RestartNode via async slot - #11683
feat(dronecan): on-demand GetNodeInfo, GetSet, ExecuteOpcode, RestartNode via async slot#11683daijoubu wants to merge 6 commits into
Conversation
|
Test firmware build ready — commit Download firmware for PR #11683 244 targets built. Find your board's
|
|
Companion configurator PR: iNavFlight/inav-configurator#2671 (draft) — adds the DroneCAN node tab UI for GetNodeInfo, GetSet, ExecuteOpcode, and RestartNode. The two should be reviewed/merged together. |
f29d605 to
74a9d21
Compare
Add on-demand GetNodeInfo support: request a target node's software/ hardware version and name over DroneCAN, decode the response into the node table, and expose it to the configurator/CLI via MSP (MSP2_INAV_DRONECAN_NODE_INFO). This is the first on-demand (as opposed to broadcast-only) DroneCAN service request INAV makes - GetNodeInfo uses canardRequestOrRespond() under ATOMIC_BLOCK(NVIC_PRIO_CAN), matching the ISR masking the H7/F7 driver rework established for TX. MSP2_INAV_DRONECAN_NODE_INFO response grew to 71 bytes to carry the decoded version/name fields (docs/msp regenerated to match; MSP2_DRONECAN_NODE_INFO_SIZE replaces an inline field-count literal wherever the response size was checked, including the msp_protocol_v2_inav.h constant's own location, which moved out of fc_msp.c). Also: per-node transfer_id (rather than one shared counter across all nodes) so concurrent/overlapping GetNodeInfo requests to different nodes don't cross-contaminate; node name storage extended to 80 bytes with overflow logging; dronecanGetNodeByID() added to eliminate node-table lookups duplicated across handlers; bus-off recovery now gives up after 50 attempts and enters STATE_DRONECAN_FAILED instead of retrying forever. Full unit test suite passes: GetNodeInfo/SoftwareVersion/HardwareVersion/ RTCMStream response decode, shouldAcceptTransfer dispatch (GAP-S1/S2), and node-table tests. Squashed from the original feature/dronecan-getnodeinfo commit sequence (24 commits - the initial multi-phase implementation, several rounds of code-review fixups, and one rebase-artifact cleanup, "fixup: remove orphaned TX loop and duplicate process1HzTasks from rebase artifact" - into this single commit for a clean PR diff. No functional changes from the squash itself.
…ed async slot Extend on-demand DroneCAN service requests beyond GetNodeInfo to param GetSet, ExecuteOpcode, and RestartNode. All four services now share a single in-flight async request slot (dronecanAsyncSlot) rather than per-service state, since only one on-demand request is ever outstanding at a time in practice: dronecanAsyncRequest() encodes and sends whichever service's request (masked under ATOMIC_BLOCK against the CAN TX ISR), and one response handler decodes whichever service's response arrives, guarded by service_id/node_id/transfer_id matching so a stale or mismatched response can't be misattributed to the wrong in-flight request. A timeout (DRONECAN_ASYNC_TIMEOUT_MS) expires a request that never gets a response, so the slot can't wedge waiting forever. GetSet: full int/float/bool/string value union plus min/max NumericValue range, exposed through MSP so the configurator can read/write a remote node's parameters and see their valid range. ExecuteOpcode/RestartNode: simple ok/fail response, for triggering a remote node's save/erase opcodes or a restart. Full unit test suite passes: response-decode coverage for GetSet (int/float/bool/string/empty), ExecuteOpcode, and RestartNode, plus the async-slot dispatch tests (GAP-S2) updated for the new shared-slot architecture. Squashed from the original feature/dronecan-param-getset commit sequence (20 commits - the initial async-slot/GetSet/ExecuteOpcode/RestartNode implementation plus several rounds of code-review fixups) into this single commit for a clean PR diff. No functional changes from the squash itself.
…odule Split dronecanAsyncRequest() and the response handler (GetNodeInfo, ParamGetSet, ExecuteOpcode, RestartNode - a single shared slot serialising all on-demand service requests) out of dronecan.c into dronecan_async.c/.h. dronecanAsyncSlot's definition and the response handler move too; dronecan.h keeps declaring dronecanAsyncRequest()/ dronecanAsyncSlot since fc_msp.c is an external caller of both. dronecan.c's onTransferReceived() now calls dronecanAsyncHandleServiceResponse() (renamed from the static handle_AsyncServiceResponse for external linkage), and STATE_DRONECAN_NORMAL calls the new dronecanAsyncCheckTimeout() instead of carrying the timeout-expiry check inline. Also flips the file-scope `canard` CanardInstance from static to plain external linkage, since dronecan_async.c needs `extern CanardInstance canard` to reach it. (On the branch this was originally authored on, that linkage change had already landed earlier, as part of the DNA server work - rebasing this extraction back to sit directly on param-getset instead means picking it up here.) Cherry-picked from feature/dronecan-actuator-control (original commit e577393) onto feature/dronecan-param-getset: this is general dronecan.c restructuring in async-request/GetNodeInfo/ParamGetSet/ ExecuteOpcode/RestartNode territory - this branch's own scope - not actuator-control-specific, so it belongs here rather than riding along with unrelated actuator-output work. Full unit test suite (29 tests in dronecan_application_unittest, full suite otherwise unchanged) passes. SITL builds clean with -Werror.
74a9d21 to
b056d2a
Compare
|
RAM / Flash usage vs. base branch — commit
See RAM/flash optimization guide for techniques to reduce usage. |
Add coverage for the async client added by this branch (GetNodeInfo, ParamGetSet, ExecuteOpcode, RestartNode via a shared request slot) and the MSP2_INAV_DRONECAN_ASYNC_REQUEST/RESULT messages that replace the old 0x2043 NODE_INFO meaning. Also corrects drift found while updating: dronecanNodeInfo_t no longer carries name/name_len, MSP2_INAV_DRONECAN_NODES is 13 bytes/node (not 30) with last_seen_ms as an elapsed delta rather than an absolute timestamp, and the documented settings (dronecan_mode/dronecan_baudrate) don't match the real ones (dronecan_node_id/dronecan_bitrate_kbps). DroneCAN.md's Parameter Get/Set feature entry flips from Planned to Supported, with a new section on the configurator DroneCAN tab workflow, including a note that RestartNode can report failure even when the node restarted successfully (peripheral-side ack-before-reset race, not an INAV bug).
|
ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing |
PR Summary by QodoDroneCAN: add single-slot async service client + MSP async request/result
AI Description
Diagram
High-Level Assessment
Files changed (14)
|
Code Review by Qodo
1.
|
dronecanUpdate() checked dronecanAsyncCheckTimeout() before processing this tick's received CAN frames, so a response arriving exactly at the timeout deadline could be marked ERROR before it was read, silently discarding an on-time reply. Adds a regression test that drives dronecanUpdate() directly, building a real response frame with libcanard's own encoder from a throwaway peer node, correctly handling the multi-frame transfer the payload needs. Verified red (fails against the old call order) before restoring the fix (green).
fc_msp.c's MSP2_INAV_DRONECAN_ASYNC_REQUEST handler skipped each value's byte-read when the payload was too short but dispatched the request anyway, sending a real UAVCAN write with a zeroed value or truncated param name instead of rejecting it. Moves all DroneCAN-specific MSP command handling (PARAM_GETSET request parsing, node-table serialization, async request/result handling) into new files fc_msp_dronecan.c/.h, following the existing fc_msp_box.c precedent for splitting self-contained MSP logic out of this file. Each case in fc_msp.c's command switch is now a single function call. The extracted parser (mspParseDronecanParamGetSetRequest) rejects any truncated read immediately instead of the old behavior of silently proceeding with zeroed defaults. 13 unit tests cover complete and truncated INT/FLOAT/BOOL/STRING values, the EMPTY-on-write case, and truncated/complete name fields.
Summary
Adds an on-demand async service slot (
dronecanAsyncSlot) that handles GetNodeInfo, param GetSet, ExecuteOpcode, and RestartNode requests through a single IDLE → PENDING → READY/ERROR state machine — one request in flight at a time rather than a queue per service. All four are exposed over MSP (MSP2_INAV_DRONECAN_ASYNC_REQUEST/MSP2_INAV_DRONECAN_ASYNC_RESULT), which is what the companion configurator PR uses to read/write node parameters and trigger restarts without any custom DroneCAN tooling on the host side.Also extends the node table MSP response with
last_seen_msas a delta (ms since the node's last NodeStatus) instead of an absolute FC timestamp, and updatesdocs/DroneCAN-Driver.md/docs/DroneCAN.mdto cover the new client and fix a few things that had drifted out of sync with the code (stale field names, a settings table that no longer matchedsettings.yaml).Companion UI:
daijoubu:feature/dronecan-configurator-tab(iNavFlight/inav-configurator#2671) — review/merge together.29 unit tests: node table management, the
shouldAcceptTransferfilter, all four guard-rejection paths, all fiveparam.Valuetypes with min/max range fields, ExecuteOpcode and RestartNode ok/fail, and the re-entry guard.Test plan
-Werror)ExecuteOpcode(SAVE)to persistnameset alongsideindex— index-only writes are silently ignored by this node (and DroneCAN param.GetSet nodes generally); index-only reads work fine. The configurator always sendsnameon writes; raw MSP testing won't.READYresult resets the slot toIDLEIDLE/ERRORafter ~2sdocs/DroneCAN.md.