Skip to content

Conversation

haslinghuis
Copy link
Member

@haslinghuis haslinghuis commented Oct 14, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Prevents duplicate connection attempts when already connected.
    • Handles unavailable ports and failed connections gracefully, avoiding stuck states.
    • Clears stale listeners and ensures reliable disconnection after failures.
    • Improves detection of when a target is fully loaded before proceeding.
  • Refactor

    • Verification flow is now asynchronous with guarded precondition checks.
    • Centralized async cleanup consolidates connection and error-handling paths.

@haslinghuis haslinghuis added this to the 2025.12 milestone Oct 14, 2025
@haslinghuis haslinghuis self-assigned this Oct 14, 2025
@haslinghuis haslinghuis moved this to App in 2025.12.0 Oct 14, 2025
Copy link
Contributor

coderabbitai bot commented Oct 14, 2025

Walkthrough

Converted AutoDetect.verifyBoard to async, added precondition checks for port availability and targets loaded, reworked connect flow into a guarded try/catch/finally with one-time listeners and local result tracking, and introduced a centralized async cleanup() that removes listeners, clears MSP state, and disconnects serial on failure or finish.

Changes

Cohort / File(s) Summary of changes
AutoDetect connection flow refactor
src/js/utils/AutoDetect.js
verifyBoard() signature changed to async; added guarded pre-checks (port availability, target.isLoaded), moved connect logic into try/catch/finally with one-time event listeners and local result tracking, added async cleanup() to await serial.disconnect(), remove serial/MSP listeners and clear MSP state, and updated onFinishClose to call cleanup().

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant AD as AutoDetect.verifyBoard()
    participant SP as SerialPort
    participant MSP as MSP Layer

    Note over AD: async verifyBoard()

    User->>AD: invoke verifyBoard()
    alt already connected
        AD-->>User: return (connected)
    else not connected
        AD->>AD: register one-time listeners & init local result
        AD->>AD: check port available & target.isLoaded?
        alt preconditions fail
            AD->>AD: cleanup()
            AD-->>User: fail
        else preconditions ok
            AD->>SP: connect() (await in try/catch)
            alt connect success
                AD->>MSP: confirm target/isLoaded (optional chaining)
                alt target loaded
                    AD-->>User: success
                else target not loaded
                    AD->>AD: cleanup()
                    AD-->>User: fail
                end
            else connect failure
                AD->>AD: cleanup()
                AD-->>User: fail
            end
        end
    end

    rect rgba(230,245,255,0.6)
    Note right of AD: cleanup(): await serial.disconnect(), remove serial/MSP listeners, clear MSP state
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • McGiverGim
  • nerdCopter
  • blckmn
  • VitroidFPV

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description consists only of a single-line reference to an isolated fix and does not follow the repository’s description template or include essential information such as a summary of changes, rationale, testing instructions, or linked issues. Without these details, reviewers lack context to understand and verify the proposed changes. Please expand the description to use the provided template by removing any leftover template text and adding sections that summarize the changes made, explain why the fix is necessary, reference related issue numbers, and outline any testing steps or impacts to ensure reviewers have full context.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely summarizes the core change by stating that it fixes the cleanup logic for autodetect event handlers, which directly reflects the addition of a centralized cleanup() method and the consolidation of handler removal in AutoDetect.js. It is specific to the main change and avoids unnecessary detail or generic phrasing. Teammates reviewing history will immediately understand the primary purpose of this pull request.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59a980a and e0039c9.

📒 Files selected for processing (1)
  • src/js/utils/AutoDetect.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T07:45:48.606Z
Learnt from: blckmn
PR: betaflight/betaflight-configurator#4583
File: src/js/tabs/firmware_flasher.js:949-961
Timestamp: 2025-09-02T07:45:48.606Z
Learning: In src/js/tabs/firmware_flasher.js, the config file loading code path after firmware loading (in the load_file click handler) cannot be reached when UF2 firmware is loaded, according to the maintainer blckmn. This code is maintained for backward compatibility with unified target settings and will be refactored in a future PR.

Applied to files:

  • src/js/utils/AutoDetect.js
🧬 Code graph analysis (1)
src/js/utils/AutoDetect.js (4)
src/js/port_handler.js (1)
  • PortHandler (16-50)
src/js/gui.js (1)
  • TABS (8-8)
src/js/gui_log.js (1)
  • gui_log (7-21)
src/js/serial.js (2)
  • serial (319-319)
  • serial (319-319)
🔇 Additional comments (3)
src/js/utils/AutoDetect.js (3)

36-74: LGTM! Well-structured connection flow with proper safeguards.

The async conversion, precondition checks, just-in-time listener registration, and conditional cleanup pattern are all well-implemented. The attempted flag ensures cleanup only runs when listeners were actually registered, preventing unnecessary teardown of unrelated connections.


120-120: LGTM! Centralized cleanup improves consistency.

Consolidating cleanup logic into a dedicated method is a good refactor.


190-207: LGTM! Cleanup implementation is correct.

The centralized cleanup with proper error handling, selective listener removal, and MSP cleanup is well-structured. The decision to not remove the disconnect listener (line 201) is intentional—it allows the disconnect event to fire and trigger onClosed() when a successful connection is being torn down, while the { once: true } option ensures automatic removal after firing.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@haslinghuis haslinghuis changed the title Fix cleanup of autodetect handlers Fix cleanup of autodetect event handlers Oct 14, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ecb566 and d604c80.

📒 Files selected for processing (1)
  • src/js/utils/AutoDetect.js (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/js/utils/AutoDetect.js (4)
src/js/port_handler.js (1)
  • PortHandler (16-50)
src/js/gui.js (1)
  • TABS (8-8)
src/js/gui_log.js (1)
  • gui_log (7-21)
src/js/msp.js (1)
  • MSP (5-455)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/js/utils/AutoDetect.js (1)

139-153: this.logHead is undefined; logs will print “undefined …”.

Define this.logHead in the constructor (or remove it from the message).

Add in constructor:

constructor() {
    this.logHead = "[AUTODETECT]";
    this.board = FC.CONFIG.boardName;
    this.targetAvailable = false;
    // ...
}
🧹 Nitpick comments (1)
src/js/utils/AutoDetect.js (1)

49-51: Re-check port availability after permission flow to avoid false negatives.

PortHandler.portAvailable can be stale immediately after a permission grant. Refresh the device list before gating on availability (e.g., ensure WebSerial.getDevices() is re-queried in the flow that sets portAvailable).

Based on learnings

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d604c80 and f9c2fde.

📒 Files selected for processing (1)
  • src/js/utils/AutoDetect.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-19T22:13:09.136Z
Learnt from: blckmn
PR: betaflight/betaflight-configurator#4521
File: src/js/protocols/WebSerial.js:148-151
Timestamp: 2025-06-19T22:13:09.136Z
Learning: In WebSerial.js, there's a timing issue where the cached `this.ports` array doesn't immediately reflect newly permitted devices after `requestPermissionDevice()` completes. The `getDevices()` method needs to refresh the device list from the browser API to return accurate data immediately following a permission request and user acceptance.

Applied to files:

  • src/js/utils/AutoDetect.js
🧬 Code graph analysis (1)
src/js/utils/AutoDetect.js (5)
src/js/port_handler.js (1)
  • PortHandler (16-50)
src/js/serial.js (2)
  • serial (319-319)
  • serial (319-319)
src/js/gui.js (1)
  • TABS (8-8)
src/js/gui_log.js (1)
  • gui_log (7-21)
src/js/msp.js (1)
  • MSP (5-455)

…up if a connect was attempted. Move listener registration to just-in-time.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/js/utils/AutoDetect.js (1)

40-68: Register listeners just-in-time to avoid disconnecting unrelated sessions and leaving stale handlers.

Listeners are registered at lines 40-41 before the guard conditions at lines 50-61. When guards reject the attempt (no port, targets not loaded, or already connected), result stays false and cleanup() runs at line 66—potentially disconnecting an existing session or leaving the once-registered disconnect listener attached for the next unrelated connection.

Apply this diff to register listeners only when you will actually call serial.connect():

     async verifyBoard() {
         const port = PortHandler.portPicker.selectedPort;
 
         if (!port.startsWith("virtual")) {
-            serial.addEventListener("connect", this.boundHandleConnect, { once: true });
-            serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true });
-
             // Safely check firmware_flasher.targets (use optional chaining so this doesn't throw when undefined)
             const isLoaded = TABS.firmware_flasher?.targets
                 ? Object.keys(TABS.firmware_flasher.targets).length > 0
                 : false;
             let result = false;
+            let attempted = false;
 
             try {
                 if (!PortHandler.portAvailable) {
                     gui_log(i18n.getMessage("firmwareFlasherNoValidPort"));
                 } else if (!isLoaded) {
                     gui_log(i18n.getMessage("firmwareFlasherNoTargetsLoaded"));
                 } else if (serial.connected || serial.connectionId) {
                     console.warn("Attempting to connect while there still is a connection", serial.connected);
                     gui_log(i18n.getMessage("serialPortOpenFail"));
                 } else {
+                    // Register one-time listeners only when we are actually going to connect
+                    serial.addEventListener("connect", this.boundHandleConnect, { once: true });
+                    serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true });
                     console.log("Connecting to serial port", port);
                     gui_log(i18n.getMessage("firmwareFlasherDetectBoardQuery"));
+                    attempted = true;
                     result = await serial.connect(port, { baudRate: PortHandler.portPicker.selectedBauds || 115200 });
                 }
             } catch (error) {
                 console.error("Failed to connect:", error);
             } finally {
-                if (!result) {
+                if (!result && attempted) {
                     this.cleanup();
                 }
             }
         }
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9c2fde and b607a1d.

📒 Files selected for processing (1)
  • src/js/utils/AutoDetect.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T07:45:48.606Z
Learnt from: blckmn
PR: betaflight/betaflight-configurator#4583
File: src/js/tabs/firmware_flasher.js:949-961
Timestamp: 2025-09-02T07:45:48.606Z
Learning: In src/js/tabs/firmware_flasher.js, the config file loading code path after firmware loading (in the load_file click handler) cannot be reached when UF2 firmware is loaded, according to the maintainer blckmn. This code is maintained for backward compatibility with unified target settings and will be refactored in a future PR.

Applied to files:

  • src/js/utils/AutoDetect.js
🧬 Code graph analysis (1)
src/js/utils/AutoDetect.js (5)
src/js/port_handler.js (1)
  • PortHandler (16-50)
src/js/serial.js (2)
  • serial (319-319)
  • serial (319-319)
src/js/gui.js (1)
  • TABS (8-8)
src/js/gui_log.js (1)
  • gui_log (7-21)
src/js/msp.js (1)
  • MSP (5-455)
🔇 Additional comments (2)
src/js/utils/AutoDetect.js (2)

43-46: LGTM—optional chaining guards against undefined TABS.firmware_flasher.

The optional chaining at line 44 properly prevents a TypeError if TABS.firmware_flasher is undefined, addressing the past critical issue.


116-116: LGTM—centralized cleanup after board verification.

Calling this.cleanup() here consolidates resource release after the board verification flow completes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/js/utils/AutoDetect.js (1)

196-196: Consider using a string literal directly for clarity.

While the fallback this.logHead || "AutoDetect" is functional, this.logHead is undefined in this class. Using "AutoDetect" directly (as suggested in previous reviews) would be simpler and avoid referencing an undefined property.

Apply this diff:

-            console.error(`${this.logHead || "AutoDetect"}: serial.disconnect() failed:`, error);
+            console.error("AutoDetect: serial.disconnect() failed:", error);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b607a1d and 59a980a.

📒 Files selected for processing (1)
  • src/js/utils/AutoDetect.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T07:45:48.606Z
Learnt from: blckmn
PR: betaflight/betaflight-configurator#4583
File: src/js/tabs/firmware_flasher.js:949-961
Timestamp: 2025-09-02T07:45:48.606Z
Learning: In src/js/tabs/firmware_flasher.js, the config file loading code path after firmware loading (in the load_file click handler) cannot be reached when UF2 firmware is loaded, according to the maintainer blckmn. This code is maintained for backward compatibility with unified target settings and will be refactored in a future PR.

Applied to files:

  • src/js/utils/AutoDetect.js
🧬 Code graph analysis (1)
src/js/utils/AutoDetect.js (5)
src/js/port_handler.js (1)
  • PortHandler (16-50)
src/js/gui.js (1)
  • TABS (8-8)
src/js/gui_log.js (1)
  • gui_log (7-21)
src/js/serial.js (2)
  • serial (319-319)
  • serial (319-319)
src/js/msp.js (1)
  • MSP (5-455)
🔇 Additional comments (3)
src/js/utils/AutoDetect.js (3)

36-74: LGTM! Well-structured async conversion with proper safeguards.

The changes correctly address previous review concerns:

  • Optional chaining prevents TypeError when TABS.firmware_flasher is undefined
  • Listeners registered just-in-time only when actually connecting, preventing stale registrations
  • The attempted flag ensures cleanup runs only when needed
  • Precondition checks prevent connection attempts in invalid states

The try/catch/finally structure with conditional cleanup is sound.


120-120: LGTM! Proper centralization of cleanup logic.

Calling the centralized cleanup() method here consolidates resource release, making the flow easier to maintain.


190-207: LGTM! Robust cleanup with proper error handling.

The try/catch/finally structure correctly ensures:

  • Serial disconnection triggers the once-registered disconnect handler (line 193)
  • Errors don't block the cleanup flow (lines 194-196)
  • Listeners and MSP state are always cleaned up (lines 199-205)
  • The once-registered disconnect listener auto-removes, so explicit removal is unnecessary (line 201)

Copy link

Copy link
Contributor

Copy link
Member

@nerdCopter nerdCopter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • approving based on #4661 testing

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

Projects

Status: App

Development

Successfully merging this pull request may close these issues.

3 participants