Skip to content

Conversation

@haslinghuis
Copy link
Member

@haslinghuis haslinghuis commented Oct 7, 2025

Purpose

This PR comprehensively fixes port event handling across the configurator, addressing several critical issues with connection management, event processing, and error recovery. The changes improve reliability when connecting to flight controllers, handling device removal, and managing concurrent connection attempts.

Key Issues Fixed

1. MSP Callback Handling with CRC Errors

Previously, callbacks were not notified when CRC errors occurred, leaving the application in an inconsistent state. Now callbacks receive the error flag along with preserved data, allowing proper error handling.

2. Port Event Debouncing and Race Conditions

The firmware flasher lacked proper debouncing for rapid port change events, leading to multiple simultaneous connection attempts. Implemented debouncing (100ms) and proper connection state guards.

3. WebSerial Connection Recovery

When a port was already open (e.g., from a previous failed connection), the configurator would fail to connect. Added recovery logic to attach to already-open ports by acquiring reader/writer streams.

4. Serial Connection Concurrency

Concurrent connection attempts to the same port could cause race conditions. Implemented proper queueing so the second caller waits for the first connection to complete.

5. Reconnection Logic

Improved handling of reconnection requests - if already connected to the requested port, the connection is preserved. If connected to a different port, proper disconnect/reconnect sequence is followed.

6. AutoDetect Preservation

Fixed AutoDetect to avoid disconnecting when a valid connection already exists, preventing unnecessary reconnection cycles.

Summary of Changes

Core Files Modified

src/js/msp/MSPHelper.js

  • Modified process_data to always invoke callbacks, even with CRC errors
  • Callbacks now receive data with crcError flag for proper error handling
  • Wrapped callback invocations in try/catch for robustness

src/js/tabs/firmware_flasher.js

  • Added debouncing constants: PORT_CHANGE_DEBOUNCE_MS (100ms), AUTO_DETECT_DELAY_MS
  • Exposed public event handlers: detectedUsbDevice, detectedSerialDevice, onPortChange, onDeviceRemoved
  • Implemented debounced port change handling
  • Added auto-detect on tab activation
  • Enhanced cleanup to clear timers and unregister handlers

src/js/protocols/WebSerial.js

  • Centralized logging via new logger module
  • Added recovery path for already-open ports (InvalidStateError handling)
  • Acquires reader/writer on already-open ports and resumes operation
  • Enhanced error logging throughout connection lifecycle

src/js/serial.js

  • Added _handleReconnection method for smart reconnection logic
  • Implemented connection queueing for concurrent attempts
  • Added waiting mechanism (up to 5s) for in-flight connections
  • Enhanced error handling with proper state cleanup
  • Modified selectProtocol signature (removed forceDisconnect parameter)

src/js/utils/AutoDetect.js

  • Modified verifyBoard to preserve existing connections instead of disconnecting
  • Only logs warning when connection already exists

src/js/logger.js (new)

  • Test-friendly logger wrapper with info, warn, error methods
  • Facilitates better testing by allowing logger mocking

Test Coverage

test/js/MSPHelper.test.js

  • Updated test descriptions and expectations for CRC error handling
  • Verified callbacks receive crcError flag and preserved data

test/js/firmware_flasher.test.js (new)

  • Tests debounce logic for rapid port changes
  • Verifies USB/serial device detection handlers
  • Tests cleanup behavior (timer clearing, handler unregistration)

test/js/serial_connect.test.js (new)

  • Tests concurrent connection queueing
  • Verifies connection failure handling and state resets
  • Tests disconnect/reconnect flows
  • Uses fake protocols to simulate various scenarios

test/js/webserial.test.js (new)

  • Tests recovery path for already-open ports
  • Verifies failure handling when recovery lacks reader/writer
  • Mocks WebSerial API for isolated testing

Testing Instructions

Manual Testing

  1. Basic Connection Flow

    • Connect a flight controller via USB
    • Verify auto-detection works and board connects successfully
    • Navigate to various tabs and confirm stable connection
  2. Rapid Port Changes

    • Connect and disconnect USB cable rapidly (3-4 times in quick succession)
    • Verify configurator handles changes gracefully without multiple connection attempts
    • Check console for no error spam
  3. Device Removal During Operation

    • Connect to a board
    • Navigate to a tab (e.g., PID Tuning)
    • Unplug the USB cable
    • Verify clean disconnection and appropriate user feedback
    • Reconnect and verify connection resumes properly
  4. Firmware Flasher

    • Go to Firmware Flasher tab
    • Connect board in DFU mode
    • Verify auto-selection of port
    • Flash firmware and verify successful completion
    • Test board detection after flash completes
  5. Concurrent Connection Attempts (requires timing)

    • In console, try rapid serial.connect calls
    • Verify no race conditions or duplicate connections
  6. WebSerial Recovery (Chrome/Edge only)

    • Connect to board
    • Force-close browser (don't disconnect properly)
    • Reopen configurator
    • Verify connection can be re-established

Automated Testing

Run the test suite:

yarn run vitest run

All new and existing tests should pass.

Related Issues

Potentially fixes #4595 (ports tab issues in latest Chrome/Edge)

Breaking Changes

  • Serial.selectProtocol() signature changed - removed forceDisconnect parameter (internal API)

Notes

  • Debounce timing (100ms) is configurable via PORT_CHANGE_DEBOUNCE_MS constant
  • Logger module enables better testability across serial/WebSerial code
  • Comprehensive test coverage prevents regression
  • All changes maintain backward compatibility for public APIs

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

Summary by CodeRabbit

  • New Features

    • Exposed firmware flasher handlers and public APIs for start/clear operations; auto-detects devices after tab activation with debounce.
  • Improvements

    • Centralized logger replaces console noise for serial and flashing.
    • More resilient WebSerial recovery when ports are already open.
    • Smarter connection sequencing to avoid unnecessary disconnects.
  • Bug Fixes

    • Callbacks now notified even when CRC/data errors occur (data preserved).
    • Cleans up timers/handlers on teardown to prevent post-cleanup actions.
  • Tests

    • Added/updated test suites covering debounce, reconnection, WebSerial recovery, and callback behavior.
      <!-- end of auto-generated comment: release notes by coderabbit.ai -->

Summary by CodeRabbit

  • New Features

    • Exposed firmware flasher controls and handlers; debounce and delayed auto-detect for port changes.
  • Improvements

    • Centralized logger usage and richer connection/recovery handling for serial and WebSerial paths.
    • MSP callbacks now always notify callers and include crcError metadata while preserving payload shape.
    • Auto-detect lifecycle and cleanup unified for more reliable teardown.
  • Bug Fixes

    • Prevents unintended reopening of the firmware flasher tab on blocked tab switches.
  • Tests

    • New/expanded tests for flasher, serial connect, WebSerial, and MSP behaviors.

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

coderabbitai bot commented Oct 7, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Multiple modules updated: MSP callbacks now run even on CRC errors; firmware_flasher gained debounced port-change handlers and public APIs; WebSerial uses a logger and adds recovery on InvalidStateError; Serial adds reconnection/wait logic; AutoDetect.verifyBoard became async with centralized cleanup; tests added/updated.

Changes

Cohort / File(s) Summary
MSP callback behavior
src/js/msp/MSPHelper.js, test/js/MSPHelper.test.js
Always invoke callbacks even when crcError is true; preserve data and compute length as data ? data.byteLength : 0; wrap callback in try/catch; tests updated to assert crcError propagation and data preservation.
Firmware flasher handlers & debounce
src/js/tabs/firmware_flasher.js, test/js/firmware_flasher.test.js
Add debounce constants and public fields (portChangeTimer, logHead); expose handlers (detectedUsbDevice, detectedSerialDevice, onPortChange, onDeviceRemoved), startFlashing, clearBufferedFirmware; debounce port-change verification, auto-detect after activation, and unregister/clear timer on cleanup.
WebSerial recovery & centralized logging
src/js/protocols/WebSerial.js, src/js/logger.js, test/js/webserial.test.js
Introduce logger wrapper and replace console logs; on open() InvalidStateError attempt to attach existing reader/writer and recover connection when both acquired; add richer logging and guarded cleanup; tests validate recovery and failure paths.
Serial reconnection, waiters, and error cleanup
src/js/serial.js, test/js/serial_connect.test.js
Add _handleReconnection(requestedPath, options, callback); change selectProtocol signature to selectProtocol(portPath); short-circuit when already connected, wait for in-flight connect/disconnect (up to ~5s), realign/disconnect as needed, and strengthen protocol error cleanup and disconnect notifications; tests for concurrency and failure handling added.
AutoDetect control flow & cleanup
src/js/utils/AutoDetect.js
Make verifyBoard() async, restructure to await serial.connect inside guarded flow, remove immediate early serial.disconnect() paths, add cleanup() to centralize teardown and listener removal; use cleanup() from onFinishClose().
Main UI tab behavior
src/js/main.js
Removed lines that programmatically re-open the firmware flasher tab during a restricted tab toggle; stops forcing the flasher tab to open.
Test imports and new tests
test/js/*
Update/import path fixes in tests and add new suites: serial_connect.test.js, firmware_flasher.test.js, webserial.test.js to cover new behaviors.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant UI as Firmware Flasher UI
  participant EB as EventBus
  participant AD as AutoDetect
  note over UI,AD: Debounced port-change verification (PORT_CHANGE_DEBOUNCE_MS)
  EB-->>UI: ports-input:change(port)
  UI->>UI: clear pending portChangeTimer
  UI->>UI: schedule verifyBoard() after debounce
  UI->>AD: verifyBoard() (after debounce)
  AD-->>UI: verification result
  UI-->>User: update selection/logs
Loading
sequenceDiagram
  participant App
  participant WS as WebSerial
  participant Port as navigator.serial.Port
  note over WS,Port: Recovery when open() throws InvalidStateError
  App->>WS: connect(path, options)
  WS->>Port: open()
  Port-->>WS: throws InvalidStateError
  WS->>Port: attempt acquire reader/writer & getInfo()
  alt reader & writer acquired
    WS->>WS: set connected, start read loop
    WS-->>App: dispatch connect(true)
  else acquisition failed
    WS-->>App: dispatch connect(false)
  end
Loading
sequenceDiagram
  participant Caller1
  participant Caller2
  participant Serial
  participant Protocol
  note over Serial: Concurrent connect handling + reconnection short-circuit
  Caller1->>Serial: connect(path)
  Serial->>Protocol: connect()
  par while in-flight
    Caller2->>Serial: connect(path)
    Serial->>Serial: wait for connect/disconnect (timeout ~5s)
  end
  alt connected to requested path
    Serial-->>Caller1: true
    Serial-->>Caller2: true
  else failure/path-mismatch
    Serial->>Protocol: disconnect/reconnect as needed
    Serial-->>Caller1: false
    Serial-->>Caller2: false
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

Tested

Suggested reviewers

  • nerdCopter
  • VitroidFPV
  • KarateBrot

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning This PR introduces several changes that fall outside the scope of fixing the ports tab behavior from issue #4595, including updates to MSPHelper callback handling, the addition of a standalone logger module, and removal of auto-open logic in main.js. Those modifications are unrelated to restoring port detection compatibility in the ports tab and should be split into separate PRs or scoped differently to maintain focus on the reported bug. Isolate unrelated changes such as CRC error callback handling in MSPHelper and the new logger integration into separate pull requests, and refocus this PR solely on port detection and UI behavior fixes tied to issue #4595.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Linked Issues Check ❓ Inconclusive While this PR improves port-change event handling, debouncing, and connection recovery, it does not explicitly address the ports tab UI listing issue described in issue #4595. The changes focus on firmware flasher event handling, serial reconnection logic, and error recovery but omit direct modifications or tests for the main ports-tab interface or port enumeration flow in Chrome/Edge 140+. Without such targeted updates, it is unclear whether the linked issue is fully resolved. Please clarify how these changes restore the ports tab UI behavior in Chromium 140+ and include any necessary modifications or tests for the ports-tab interface to ensure issue #4595 is fully addressed.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title “Fix port event handling” concisely and accurately captures the primary focus of this pull request, which centers on improving port-change debouncing, detection handlers, and connection recovery across the configurator. It is specific to the key change without listing every detail, making it clear for team members scanning history. The phrasing is direct and avoids unnecessary noise or overly broad language. It aligns well with the modifications to firmware_flasher and serial modules.
Description Check ✅ Passed The pull request description is well structured with clear sections for Purpose, Key Issues Fixed, Summary of Changes, Testing Instructions, Related Issues, and Breaking Changes. It omits the template guidelines as required and provides both manual and automated test instructions along with detailed descriptions of all modifications and test coverage. The description gives reviewers sufficient context to understand the scope, rationale, and testing strategy for the changes. It aligns with the repository’s expectations for a comprehensive and informative PR description.

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.

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 8c10a46 and ab70d34.

📒 Files selected for processing (5)
  • src/js/msp/MSPHelper.js (1 hunks)
  • src/js/tabs/firmware_flasher.js (5 hunks)
  • test/js/MSPHelper.test.js (3 hunks)
  • test/js/firmware_flasher.test.js (1 hunks)
  • test/js/msp_helpers.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
test/js/firmware_flasher.test.js (1)
src/js/tabs/firmware_flasher.js (1)
  • firmware_flasher (24-50)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (2)
  • devicePath (110-110)
  • PortHandler (16-50)
test/js/MSPHelper.test.js (1)
src/js/msp/MSPCodes.js (1)
  • MSPCodes (2-211)
src/js/msp/MSPHelper.js (1)
src/js/msp.js (2)
  • data (74-74)
  • data (355-355)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: deploy

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 (2)
src/js/msp/MSPHelper.js (1)

1782-1793: LGTM - Callback behavior change is intentional and well-structured.

The refactoring to always invoke callbacks (regardless of crcError) is a significant but intentional change, as noted in the comment. The try/catch wrapper provides good protection against callback errors.

One minor observation: The warning at line 1792 is redundant with the warning at line 1768 (both log essentially the same CRC failure message). The warning at line 1768 is sufficient. However, if the intent is to emphasize that a callback was invoked despite the CRC error, consider making the message at line 1792 more specific, e.g., console.warn(\code: ${code} - callback invoked despite crc failure`);`

src/js/tabs/firmware_flasher.js (1)

53-135: LGTM - Handler implementations are well-structured.

The module-scoped handlers provide good separation of concerns:

  • Device detection logic is centralized in handleDetectedDevice
  • Port change debouncing prevents rapid re-entry
  • Debounce timer is properly cleared before starting a new one
  • The debounced callback re-checks GUI.connect_lock to handle race conditions

The logic at line 75 (GUI.connect_lock &&= !wasReboot;) is correct but subtle. The comment helps, but consider expanding it slightly for future maintainers:

- // Only clear the global connect lock when we are resuming from a reboot sequence.
+ // Only clear the global connect lock when we are resuming from a reboot sequence.
+ // If wasReboot is true, this clears connect_lock; otherwise, it leaves it unchanged.
📜 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 ab70d34 and c2cedc5.

📒 Files selected for processing (4)
  • src/js/msp/MSPHelper.js (1 hunks)
  • src/js/tabs/firmware_flasher.js (5 hunks)
  • test/js/MSPHelper.test.js (5 hunks)
  • test/js/firmware_flasher.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/js/MSPHelper.test.js
  • test/js/firmware_flasher.test.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-19T20:41:42.451Z
Learnt from: haslinghuis
PR: betaflight/betaflight-configurator#4510
File: src/js/msp.js:377-382
Timestamp: 2025-09-19T20:41:42.451Z
Learning: In the MSP.send_message function in src/js/msp.js, the doCallbackOnError parameter was removed as it was never used in production code throughout the entire codebase.

Applied to files:

  • src/js/msp/MSPHelper.js
🧬 Code graph analysis (1)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (1)
  • PortHandler (16-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: deploy
🔇 Additional comments (5)
src/js/tabs/firmware_flasher.js (5)

43-51: LGTM - Clean module-scoped handler organization.

Adding handler references to the firmware_flasher object is a good design choice that enables proper cleanup and testability.


835-844: LGTM - Handler wiring is properly sequenced.

The wiring of module-scoped handlers to EventBus after DOM readiness is the correct approach. Exposing clearBufferedFirmware at line 837 provides the necessary access for both the module handlers and cleanup routines.


1478-1481: LGTM - startFlashing registration is clean.

Binding startFlashing to the instance and exposing it on the firmware_flasher object allows it to be invoked directly by the module-scoped handlers without indirection.


1566-1575: Verify if the 1-second delay is necessary.

The auto-detect logic for already-connected devices is a good addition. However, the 1000ms delay at line 1574 seems arbitrary.

Consider whether a smaller delay (e.g., 100-300ms) would suffice, or if there's a specific reason for 1000ms. If the delay is necessary to wait for DOM elements or other initialization, the comment should be more specific about what is being waited for.

// If a specific initialization sequence requires this delay, document it:
setTimeout(() => {
    AutoDetect.verifyBoard();
}, 1000); // Wait for port picker and select2 widgets to fully initialize

1595-1611: LGTM - Cleanup properly handles handlers and timers.

The cleanup implementation correctly:

  • Unregisters EventBus listeners without nulling handler references (preventing re-initialization issues as noted in past reviews)
  • Clears the pending debounce timer to avoid callbacks firing after cleanup

This ensures resources are released while preserving the handler functions for future tab activations.

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.

All critical issues have been addressed! 🎉

The PR successfully fixes port event handling with:
✅ Comprehensive test coverage (4 new test suites)
✅ Proper error handling and state management
✅ Debouncing for port changes
✅ WebSerial recovery logic
✅ Serial connection concurrency handling
✅ All blocking issues resolved

Excellent work on this complex refactor!

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 (3)
src/js/protocols/WebSerial.js (1)

241-261: Consider logging recovery attempt start for clarity.

While warnings are logged when writer/reader acquisition fails (lines 251, 259), adding a log at the start of the recovery attempt would make the flow clearer in debug scenarios.

Add at line 241:

         try {
+            logger.info(`${logHead} Attempting to recover already-open port`);
             const connectionInfo = this.port?.getInfo ? this.port.getInfo() : null;
src/js/serial.js (2)

1-443: Consider migrating to centralized logger for consistency.

WebSerial.js has been migrated to use the centralized logger module (imported at line 3 of WebSerial.js), but serial.js still uses console.log/warn/error directly (e.g., lines 48, 177, 214, 294). For consistency, testability, and alignment with the PR's logging improvements, consider importing and using the logger module here as well.

Example:

+import logger from "./logger";
 import WebSerial from "./protocols/WebSerial.js";
 import WebBluetooth from "./protocols/WebBluetooth.js";

Then replace console calls:

-                console.log(`${this.logHead} Already connected to the requested port`);
+                logger.info(`${this.logHead} Already connected to the requested port`);

234-237: Consider making timeout value configurable.

The 5-second timeout is hardcoded. For different environments or use cases (e.g., slower devices, testing), a configurable timeout might be beneficial.

Example approach:

+        const CONNECT_WAIT_TIMEOUT_MS = 5000; // or make it a class property/config
         const timer = setTimeout(() => {
             cleanup();
             resolve(null);
-        }, 5000);
+        }, CONNECT_WAIT_TIMEOUT_MS);
📜 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 35d44cd and b816e65.

📒 Files selected for processing (11)
  • src/js/logger.js (1 hunks)
  • src/js/main.js (0 hunks)
  • src/js/msp/MSPHelper.js (1 hunks)
  • src/js/protocols/WebSerial.js (19 hunks)
  • src/js/serial.js (4 hunks)
  • src/js/tabs/firmware_flasher.js (5 hunks)
  • src/js/utils/AutoDetect.js (3 hunks)
  • test/js/MSPHelper.test.js (5 hunks)
  • test/js/firmware_flasher.test.js (1 hunks)
  • test/js/serial_connect.test.js (1 hunks)
  • test/js/webserial.test.js (1 hunks)
💤 Files with no reviewable changes (1)
  • src/js/main.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/js/utils/AutoDetect.js
🧰 Additional context used
🧠 Learnings (3)
📚 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/serial.js
  • src/js/protocols/WebSerial.js
📚 Learning: 2025-09-19T20:41:42.451Z
Learnt from: haslinghuis
PR: betaflight/betaflight-configurator#4510
File: src/js/msp.js:377-382
Timestamp: 2025-09-19T20:41:42.451Z
Learning: In the MSP.send_message function in src/js/msp.js, the doCallbackOnError parameter was removed as it was never used in production code throughout the entire codebase.

Applied to files:

  • src/js/msp/MSPHelper.js
📚 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/tabs/firmware_flasher.js
🧬 Code graph analysis (6)
src/js/msp/MSPHelper.js (2)
src/js/tabs/cli.js (1)
  • data (411-411)
src/js/msp.js (2)
  • data (74-74)
  • data (355-355)
test/js/serial_connect.test.js (1)
src/js/serial.js (2)
  • serial (443-443)
  • serial (443-443)
test/js/firmware_flasher.test.js (3)
src/js/tabs/firmware_flasher.js (1)
  • firmware_flasher (27-145)
src/js/gui.js (1)
  • GUI (473-473)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
test/js/MSPHelper.test.js (2)
src/js/fc.js (1)
  • FC (132-1006)
src/js/msp/MSPCodes.js (1)
  • MSPCodes (2-211)
src/js/protocols/WebSerial.js (1)
src/js/logger.js (1)
  • logger (3-7)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (2)
  • devicePath (110-110)
  • PortHandler (16-50)
🔇 Additional comments (6)
src/js/protocols/WebSerial.js (2)

3-3: LGTM: Centralized logging improves testability.

The migration from console methods to the centralized logger module is clean and consistent throughout the file. This enhances testability and aligns with the PR's goal of introducing a test-friendly logger.

Also applies to: 61-61, 96-96, 123-123, 139-141, 154-154, 164-164, 177-177, 194-194, 206-206, 218-218, 223-223


225-312: LGTM: Recovery logic properly handles already-open ports.

The recovery path for InvalidStateError is well-implemented with appropriate safeguards:

  • Validates both reader and writer before declaring success (lines 263-264)
  • Releases partial locks if recovery fails (lines 266-281), preventing port lockup
  • Proper state initialization including event listeners, connection state, and read loop
  • Graceful fallback to the normal failure path if recovery cannot proceed

This addresses Chromium 140+ compatibility issues where concurrent open attempts throw InvalidStateError on already-open ports. The defensive programming (optional chaining at line 242, try-catch around lock operations) ensures robust error handling.

Based on past review feedback that has been addressed in commits 2719276 and 45422a5.

src/js/serial.js (4)

36-79: LGTM: Reconnection helper handles port switching correctly.

The _handleReconnection method properly handles three scenarios:

  1. Already connected to requested port → returns true immediately
  2. Connected to different port → disconnects, then reconnects to new port
  3. Neither case → returns undefined to continue normal flow

Error handling is appropriate with try-catch wrapping and proper callback invocation. The use of undefined as a sentinel value to signal "continue normal flow" is clear and allows the main connect method to proceed with its standard logic.


161-161: LGTM: Simplified selectProtocol signature.

Removing the default parameter and forceDisconnect parameter simplifies the API. The breaking change is documented in the PR summary.


213-279: LGTM: Comprehensive in-flight wait logic prevents race conditions.

The wait logic thoroughly handles concurrent connection attempts:

  • Timeout protection (5s) prevents indefinite hangs (lines 234-237)
  • State validation after timeout checks for lingering openRequested (lines 254-258)
  • Port mismatch handling after wait disconnects if needed (lines 271-278)
  • Proper cleanup of event listeners in all paths (lines 226-233)

This addresses race conditions where multiple callers attempt to connect simultaneously, ensuring they wait for in-flight attempts to complete rather than colliding.

Based on past review feedback that has been addressed in commits 97c806d, bcc1200, 0bb260e, c00a971, and 35d44cd.


283-317: LGTM: Robust error handling prevents stale protocol state.

The try-catch around protocol.connect() ensures:

  • State cleanup on errors (openRequested, connected, connectionId reset)
  • Event notification via disconnect event dispatch so waiters are unblocked
  • Callback invocation with failure signal
  • Graceful degradation with nested try-catch for cleanup operations

This prevents the protocol from getting stuck in an openRequested state when connection fails, which would block future connection attempts.

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/protocols/WebSerial.js (1)

17-31: Use the shared logger inside the read loop helper

Everything else in this module now routes through logger, but streamAsyncIterable still writes straight to console.warn. Swapping those two calls to logger.warn keeps logging consistent (and makes it easier to stub in tests).

Apply this diff:

-                console.warn(`${logHead} Read error in streamAsyncIterable:`, error);
+                logger.warn(`${logHead} Read error in streamAsyncIterable:`, error);
@@
-            console.warn(`${logHead} Error releasing reader lock:`, error);
+            logger.warn(`${logHead} Error releasing reader lock:`, 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 b816e65 and 48350d3.

📒 Files selected for processing (11)
  • src/js/logger.js (1 hunks)
  • src/js/main.js (0 hunks)
  • src/js/msp/MSPHelper.js (1 hunks)
  • src/js/protocols/WebSerial.js (19 hunks)
  • src/js/serial.js (4 hunks)
  • src/js/tabs/firmware_flasher.js (5 hunks)
  • src/js/utils/AutoDetect.js (3 hunks)
  • test/js/MSPHelper.test.js (5 hunks)
  • test/js/firmware_flasher.test.js (1 hunks)
  • test/js/serial_connect.test.js (1 hunks)
  • test/js/webserial.test.js (1 hunks)
💤 Files with no reviewable changes (1)
  • src/js/main.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/js/logger.js
  • test/js/webserial.test.js
  • src/js/msp/MSPHelper.js
  • test/js/firmware_flasher.test.js
  • test/js/MSPHelper.test.js
🧰 Additional context used
🧠 Learnings (2)
📚 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/tabs/firmware_flasher.js
📚 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/serial.js
  • src/js/protocols/WebSerial.js
🧬 Code graph analysis (4)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (2)
  • devicePath (110-110)
  • PortHandler (16-50)
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)
test/js/serial_connect.test.js (2)
src/js/serial.js (2)
  • serial (440-440)
  • serial (440-440)
test/js/webserial.test.js (2)
  • result (83-83)
  • result (149-149)
src/js/protocols/WebSerial.js (1)
src/js/logger.js (1)
  • logger (3-7)

@nerdCopter
Copy link
Member

48350d3

  • briefly tested
  • flashed 1 FC
  • tested recent un-selected DFU/re-select DFU
  • tested basic tabs (Ports, Config, Modes, OSD, Blackbox, CLI)

OSD drag/drop is slow, but highly unlikely anything to do with this PR.

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 after basic test. saw only 1 coderabbitai nitpick.

Choose a reason for hiding this comment

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

هك@

Copy link
Member Author

Choose a reason for hiding this comment

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

انهي وانگر

@haslinghuis haslinghuis marked this pull request as draft October 15, 2025 16:19
@haslinghuis haslinghuis force-pushed the ff-events branch 2 times, most recently from c67ccf0 to 80bc37d Compare October 18, 2025 00:37
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
9.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@github-actions
Copy link
Contributor

@haslinghuis
Copy link
Member Author

No longer needed.

@github-project-automation github-project-automation bot moved this from App to Done in 2025.12.0 Oct 20, 2025
@haslinghuis haslinghuis deleted the ff-events branch October 20, 2025 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Latest configurator is messing ports tab in latest chrome/edge browser

3 participants