Skip to content

Improve msp send #4510

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open

Conversation

haslinghuis
Copy link
Member

@haslinghuis haslinghuis commented Jun 10, 2025

  • use fixed timeout to reduce buffer overflow issues
  • fix yarn test
  • update yarn test to use latest firmware
  • adds msp monitoring queue monitoring tool for testing

image

image

image

Summary by CodeRabbit

  • New Features

    • Added automatic compatibility support for test environments, allowing tests to bypass browser compatibility checks.
    • Debug tools are now dynamically loaded, with user instructions and error handling on load.
  • Bug Fixes

    • Improved handling of configuration retrieval to prevent errors when configuration values are missing.
  • Refactor

    • Simplified message sending and timeout handling to use a fixed timeout, removing adaptive adjustments and retry logic.
  • Tests

    • Updated test cases to use the latest API version.

Copy link
Contributor

coderabbitai bot commented Jun 10, 2025

Warning

Rate limit exceeded

@haslinghuis has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 52e1aeb and bef646a.

📒 Files selected for processing (8)
  • src/js/main.js (2 hunks)
  • src/js/msp/debug/MSP_DEBUG_README.md (1 hunks)
  • src/js/msp/debug/index.js (1 hunks)
  • src/js/msp/debug/msp_debug_dashboard.js (1 hunks)
  • src/js/msp/debug/msp_debug_tools.js (1 hunks)
  • src/js/msp/debug/msp_queue_monitor.js (1 hunks)
  • src/js/msp/debug/msp_stress_test.js (1 hunks)
  • src/js/msp/debug/msp_test_runner.js (1 hunks)

Walkthrough

The changes simplify timeout and retry logic in the MSP module by replacing adaptive timeouts with a single fixed value and removing related state. The main module adds a dynamic import for debug tools and improves first-run configuration safety. Browser compatibility checks now explicitly allow test environments. One test updates to use a newer API version constant.

Changes

Cohort / File(s) Change Summary
MSP Timeout Simplification
src/js/msp.js
Replaced adaptive timeout logic with a fixed TIMEOUT constant, removed related state and properties, simplified request existence check, removed retry/adaptive behavior, and cleaned up timeout handler logic.
Main Module Enhancements
src/js/main.js
Added dynamic import for debug tools with logging, improved safety in first-run configuration handling, and made minor formatting adjustments.
Browser Compatibility Update
src/js/utils/checkBrowserCompatibility.js
Modified compatibility check to treat test environments as compatible by detecting test-related environment variables; no changes to function signature or other logic.
Test API Version Update
test/js/msp/MSPHelper.test.js
Updated test to use API_VERSION_1_47 instead of API_VERSION_1_46 for MSP_BOARD_INFO handling; no other changes to test logic.

Sequence Diagram(s)

Fixed Timeout Request Flow in MSP

sequenceDiagram
    participant Caller
    participant MSP
    participant Timer

    Caller->>MSP: send_message(code, data, ...)
    MSP->>MSP: Check for duplicate request via some()
    alt No duplicate or has data
        MSP->>Timer: Set timeout (fixed TIMEOUT)
        MSP->>Caller: Send message
        Timer-->>MSP: Timeout expires
        MSP->>MSP: Log warning if >5000ms, clear timeout
    else Duplicate exists and no data
        MSP->>Caller: Do not send message
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~7 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 2

🔭 Outside diff range comments (1)
src/js/msp.js (1)

402-413: ⚠️ Potential issue

_validateSendMessage ignores doCallbackOnError flag

Error-side invocation of callback_msp is unconditional. That breaks the original contract implied by the doCallbackOnError parameter and can trigger “double callbacks” or resolve a Promise that expected an actual response.

-_validateSendMessage(code, callback_msp) {
+_validateSendMessage(code, callback_msp, doCallbackOnError = false) {-    if (callback_msp) {
+    if (doCallbackOnError && callback_msp) {
         callback_msp();
     }

You’ll have to forward the flag from send_message.

🧹 Nitpick comments (2)
src/js/msp.js (2)

423-431: Unused field – callbackOnError is never referenced

_createRequestObject stores callbackOnError, but no subsequent code reads it after the refactor. Either remove the property or reinstate the error-handling path.

Eliminating dead fields simplifies debugging and reduces cognitive load.


457-467: Aggressive timeout decay may under-shoot MIN_TIMEOUT

this.timeout-- happens on every send, regardless of frequency, and can push the value below MIN_TIMEOUT when timeout has already been clamped lower elsewhere.

-if (this.timeout > this.MIN_TIMEOUT) {
-    this.timeout--;
-}
+this.timeout = Math.max(this.MIN_TIMEOUT, this.timeout - 1);

A simple guard prevents negative or sub-minimum timeouts.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dafacbd and 8c40cfe.

📒 Files selected for processing (1)
  • src/js/msp.js (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/js/msp.js (2)
src/js/serial.js (2)
  • serial (364-364)
  • serial (364-364)
src/js/gui.js (1)
  • GUI (484-484)
🔇 Additional comments (1)
src/js/msp.js (1)

415-418: Duplicate detection is too coarse

_isDuplicateRequest only checks equality of code; it ignores the payload (data).
Two requests with the same MSP code but different parameters will be treated as duplicates and the second payload may never be transmitted.

Consider comparing a (code, dataHash) tuple or always sending when dataLength > 0 (even if duplicate).

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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a05f3a and 3dd8ae1.

📒 Files selected for processing (1)
  • src/js/msp.js (1 hunks)

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

🧹 Nitpick comments (3)
src/js/msp.js (3)

390-398: Unused allocation for duplicates

requestObj is instantiated even when the call is considered a duplicate and then discarded. This happens on every duplicate invocation and can become a noticeable GC hot-spot under heavy traffic.

Move the allocation inside the if (!isDuplicateRequest) block so it is only created when needed.


445-456: Timer remains referenced after giving up – clear it to aid GC

When MAX_RETRIES is exceeded the function bails out but does not clearTimeout(requestObj.timer);.
While the timer has already fired, keeping the handle prevents some JS engines from reclaiming it promptly.

 if (requestObj.attempts >= this.MAX_RETRIES) {
     console.error(`MSP: Request ${requestObj.code} exceeded max retries (${this.MAX_RETRIES}), giving up`);
-    // Remove from callbacks to prevent memory leak
+    clearTimeout(requestObj.timer);          // 💡 explicit cleanup
+    // Remove from callbacks to prevent memory leak
     this._removeRequestFromCallbacks(requestObj);

411-413: Adaptive back-off step might starve high-latency links

this.timeout is decreased by a fixed 5 ms on every successful send. On slow links (e.g. Bluetooth) this can quickly shrink below the real RTT, leading to unnecessary retries. Consider decaying by a percentage (e.g. timeout *= 0.9) or pinning a lower bound based on recent RTT measurements instead of a constant 5 ms.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 031b3ec and 072b49c.

📒 Files selected for processing (1)
  • src/js/msp.js (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/js/msp.js (3)
src/js/serial.js (2)
  • serial (364-364)
  • serial (364-364)
src/js/data_storage.js (1)
  • CONFIGURATOR (7-40)
src/js/gui.js (1)
  • GUI (484-484)

src/js/msp.js Outdated
});
}, this.timeout);
// Track only the first outstanding request for a given code
if (!isDuplicateRequest) {
Copy link

Choose a reason for hiding this comment

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

not sure if this is the reason for missing events? e.g. motor slider getting "stuck" on old values even if the slider got pulled all the way down ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah - see next commit - not sure if enough - or different from master - at least seems more stable - no longer locking up on SPBEF7V3 for the moment as it does on master.

@haslinghuis haslinghuis force-pushed the improve-msp branch 3 times, most recently from eb77f13 to 4fcad77 Compare June 10, 2025 23:40
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: 2

♻️ Duplicate comments (1)
src/js/msp.js (1)

399-420: ⚠️ Potential issue

Duplicate requests silently drop callbacks

When isDuplicateRequest is true, the new callback_msp is not stored anywhere, so callers issuing concurrent identical requests will never be notified.
This regresses previous behaviour and breaks APIs that rely on per-call completion.

Quick remedy: attach additional callbacks to the existing request object instead of discarding them.

-const isDuplicateRequest = this.callbacks.some((instance) => instance.requestKey === requestKey);
+const existingReq = this.callbacks.find((i) => i.requestKey === requestKey);
+const isDuplicateRequest = Boolean(existingReq);-if (!isDuplicateRequest) {
+if (!isDuplicateRequest) {
     this._setupTimeout(requestObj, bufferOut);
     this.callbacks.push(requestObj);
+} else if (callback_msp) {
+    // fan-out: queue extra callback(s) on the first request
+    (existingReq.extraCallbacks ??= []).push(callback_msp);
 }

And, when the response arrives, invoke extraCallbacks as well.

🧹 Nitpick comments (4)
src/js/msp.js (4)

376-389: Hash function may collide & yield negative keys

_createRequestKey uses a simple 32-bit rolling hash that can:

  1. Produce the same hash for different payloads (higher collision risk on large traffic).
  2. Return negative numbers, generating keys like 123:-842150451 which are harder to eyeball/debug.

Consider switching to a safer deterministic scheme, e.g. CRC-32 or base64 of the first 4 bytes plus length, to lower collision odds and keep keys human-readable.


421-428: Inefficient duplicate scan — O(N) each call

this.callbacks.some() does a linear search every time send_message is invoked.
With MAX_QUEUE_SIZE already tracked, switching to a Map<requestKey, requestObj> would give O(1) look-ups and simplify removal logic.


433-438: Timer handle lost if _setupTimeout is called twice

_setupTimeout overwrites requestObj.timer without clearing a possibly existing one.
Although current flow prevents re-entrance, guarding against future changes is cheap:

+if (requestObj.timer) {
+    clearTimeout(requestObj.timer);
+}
 requestObj.timer = setTimeout(() => {
     this._handleTimeout(requestObj, bufferOut);
 }, this.TIMEOUT);

439-472: Retry path never resets start — metrics become meaningless

requestObj.start is set once in send_message but never updated on retries, so any later duration calculations accumulate total wall-time, not per-attempt latency.

Either reset start inside _handleTimeout before re-arming the timer or drop it altogether if no longer used.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 072b49c and 20f3c6a.

📒 Files selected for processing (1)
  • src/js/msp.js (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/js/msp.js (2)
src/js/serial.js (2)
  • serial (364-364)
  • serial (364-364)
src/js/data_storage.js (1)
  • CONFIGURATOR (7-40)
🔇 Additional comments (1)
src/js/msp.js (1)

69-73:

✅ Verification successful

Confirm removal of legacy this.timeout references

You have introduced a fixed TIMEOUT constant but removed the old adaptive this.timeout.
Please grep the rest of the file / project for stale this.timeout usages to avoid undefined-property bugs at runtime.


🏁 Script executed:

#!/bin/bash
# Find any remaining references to the old timeout field
rg --line-number $'this\\.timeout'

Length of output: 665


No stale this.timeout references found

A full‐repo search for this.timeout only returns occurrences of this.timeout_array in src/js/gui.js, not the legacy this.timeout property. There are no remaining direct references to the removed adaptive timeout field—this concern can be closed.

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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/js/msp.js (1)

482-491: _removeRequestFromCallbacks leaves Map/key tracking unresolved

If you adopt the Map optimisation above, ensure this method also deletes the requestKey entry to keep the structures in sync.

🧹 Nitpick comments (4)
src/js/msp.js (4)

69-73: Expose retry parameters instead of hard-coding them

MAX_RETRIES, MAX_QUEUE_SIZE, and TIMEOUT are baked into the module, making it impossible to tune behaviour from the outside (e.g. per-board, per-environment, unit-tests). Consider reading them from CONFIGURATOR, a constructor argument, or at least exporting setters.


399-420: Duplicate check performs O(n) scans on every call

this.callbacks.some(...) linearly scans the queue. Under heavy traffic (e.g. telemetry floods) the cost grows quadratically.

Two easy wins:

  1. Keep a Map<requestKey, requestObj> to get O(1) look-ups and removals.
  2. Build bufferOut after the duplicate check to avoid unnecessary encoding work when the request is dropped.

433-438: Timer double-cleared – harmless but needless

_handleTimeout calls clearTimeout(requestObj.timer) (l.464) and _removeRequestFromCallbacks does the same. The second call is redundant and can be omitted to save a micro-op.


439-480: Constant timeout may hammer dead links

Retries fire every fixed TIMEOUT ms. On an unplugged device this means 10 exact re-transmits per second, wasting bandwidth and log space.
An exponential back-off (e.g. TIMEOUT * 2 ** attempts) curbs network noise and still converges quickly when the link is restored.

🛑 Comments failed to post (1)
src/js/msp.js (1)

376-389: 🛠️ Refactor suggestion

_createRequestKey collision risk & signed-shift bug

The DJB-style hash on a Uint8Array will inevitably collide for different payloads and, because & 0xffffffff yields a signed 32-bit value, the string key can even become negative.
A wrong “duplicate” classification silently drops tracking / retries.

-        let hash = 0;
-        for (const byte of data) {
-            hash = ((hash << 5) - hash + byte) & 0xffffffff;
-        }
-        return `${code}:${hash}`;
+        // Use a stable unsigned hash (FNV-1a) to minimise collisions
+        let hash = 2166136261 >>> 0;
+        for (const byte of data) {
+            hash ^= byte;
+            hash = Math.imul(hash, 16777619) >>> 0;
+        }
+        return `${code}:${hash.toString(16)}`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    // Helper function to create a unique key for request identification
    _createRequestKey(code, data) {
        if (!data || data.length === 0) {
            return `${code}:empty`;
        }

        // Use a stable unsigned hash (FNV-1a) to minimise collisions
        let hash = 2166136261 >>> 0;
        for (const byte of data) {
            hash ^= byte;
            hash = Math.imul(hash, 16777619) >>> 0;
        }

        return `${code}:${hash.toString(16)}`;
    },
🤖 Prompt for AI Agents
In src/js/msp.js around lines 376 to 389, the _createRequestKey function uses a
DJB-style hash that can produce collisions and negative values due to signed
32-bit shifts. To fix this, replace the hash calculation with a more robust
hashing method that avoids collisions and ensures the hash is always unsigned
and positive. For example, use a standard hashing library or implement a
non-signed hash function that produces a unique, non-negative key string for the
given data.

src/js/msp.js Outdated
}
// Create unique key combining code and data
const requestKey = this._createRequestKey(code, data);
const isDuplicateRequest = this.callbacks.some((instance) => instance.requestKey === requestKey);
Copy link

Choose a reason for hiding this comment

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

why not return here if a dupe is detected ? saves your the conditions below. looks like the code is not following "one return per function" convention anyways.

Copy link

@AJ92 AJ92 Jun 11, 2025

Choose a reason for hiding this comment

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

and, why not "rate-limit" the events from sliders at the source ? i bet there are different event types that need different handling anyways.... like those motor sliders, where probably first and last event is very important, but all events in between can be lost without any noticeable effect for the user, but then there are events that cannot be lost, like transmission of cli stuff ?

src/js/msp.js Outdated
Comment on lines 417 to 423
this._setupTimeout(requestObj, bufferOut);
this.callbacks.push(requestObj);
}

this.callbacks.push(obj);

// always send messages with data payload (even when there is a message already in the queue)
if (data || !requestExists) {
if (this.timeout > this.MIN_TIMEOUT) {
this.timeout--;
}

// Send message if it has data or is a new request
if (data || !isDuplicateRequest) {
serial.send(bufferOut, (sendInfo) => {
Copy link

@AJ92 AJ92 Jun 11, 2025

Choose a reason for hiding this comment

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

(note the line range if this comment)

i would probably start the timeout and call send only if the queue is empty, or if the previous "send" succeeded?

otherwise the buffers of the serial protocols are immediately filled and can overflow

src/js/msp.js Outdated
);

// Check if max retries exceeded OR queue is too large
if (requestObj.attempts >= this.MAX_RETRIES || this.callbacks.length > this.MAX_QUEUE_SIZE) {
Copy link

Choose a reason for hiding this comment

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

i would perform the queue size check prior to insertion of the objects, in the send_message function

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

🧹 Nitpick comments (4)
src/js/msp.js (4)

69-73: Freeze or namespace retry-config constants to prevent accidental mutation

Defining MAX_RETRIES, MAX_QUEUE_SIZE and TIMEOUT directly on the mutable MSP object means any consumer can overwrite them at runtime:

MSP.TIMEOUT = 1;   // 💥

If you really want them tweakable, expose an explicit setter.
Otherwise, lock them down:

-    MAX_RETRIES: 10,
-    MAX_QUEUE_SIZE: 50,
-    TIMEOUT: 1000,
+    MAX_RETRIES: 10,
+    MAX_QUEUE_SIZE: 50,
+    TIMEOUT: 1000,
 }
+
+Object.freeze(MSP);            // or freeze only MSP.retryConfig sub-object

376-389: Hashing scheme is fragile & collision-prone

_createRequestKey builds a 32-bit additive hash; for large or similar payloads collisions are easy, producing false “duplicates”.
Additionally hash may end up negative, so keys like 42:-123 and 42:327375 look different but are produced from different sessions of the same data length.

Consider:

  • Use a stable digest (e.g. DJB2, MurmurHash) or crypto.subtle.digest('SHA-1', data) when available.
  • Fall back to btoa(String.fromCharCode(...data)) for short payloads.
  • At minimum, >>> 0 to keep the value unsigned.

421-428: Guard callback_sent with a functional check

promise() passes false, so callback_sent() may be a boolean.
A trivial typeof guard avoids accidental false is not a function mistakes if the signature changes upstream.

-if (sendInfo.bytesSent === bufferOut.byteLength && callback_sent) {
+if (sendInfo.bytesSent === bufferOut.byteLength && typeof callback_sent === "function") {
     callback_sent();
 }

479-489: O(N²) removal can hurt with large queues

indexOf + splice is fine for the 50-item cap, but if the limit is raised later it becomes quadratic.
Storing callbacks in a Map<requestKey, requestObj> would give O(1) insert/remove/lookup, simplify duplicate handling, and eliminate linear scans elsewhere.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b03408 and debfc36.

📒 Files selected for processing (1)
  • src/js/msp.js (2 hunks)
🔇 Additional comments (2)
src/js/msp.js (2)

433-438: Timeout set-up looks good

_setupTimeout is concise and centralises timer arming – nice.


439-477: Retry handler: no back-off & relies on sendInfo.bytesSent

  1. A fixed 1 s retry interval can hammer sluggish links. Consider exponential back-off or TIMEOUT * (1 + attempts) to avoid congestion.
  2. If the serial driver does not populate bytesSent, sendInfo.bytesSent === bufferOut.byteLength evaluates false ⇒ the request aborts immediately. Validate field presence or fall back to truthiness.
- if (sendInfo.bytesSent === bufferOut.byteLength) {
+ if (!sendInfo || sendInfo.bytesSent === undefined || sendInfo.bytesSent === bufferOut.byteLength) {
  1. You re-assign requestObj.timer but never clear it inside _handleTimeout.
    Although the fired timer auto-invalidates, explicitly clearTimeout(requestObj.timer) before re-arming removes any doubt.

@nerdCopter
Copy link
Member

checking the coderrabbit "nitpicks"? i'm finding them useful outside of this project.

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/MSP_DEBUG_README.md (1)

317-317: Add language specifier to the fenced code block.

The fenced code block showing the file structure should have a language specifier for consistency.

-```
+```plaintext
 src/js/
 ├── msp_queue_monitor.js     # Core monitoring functionality
 ├── msp_stress_test.js       # Stress testing framework
src/js/msp/msp_test_runner.js (1)

43-49: Improve the periodic logging mechanism for reliability.

The current approach using Date.now() % 10000 < 500 is unreliable for consistent 10-second logging intervals. It could log multiple times within the 500ms window or miss intervals entirely.

Consider tracking the last log time:

 // Store the listener function so it can be removed later
 _quickMonitorListener: null,
+_lastLogTime: 0,

 // Define the listener function so it can be referenced for removal
 this._quickMonitorListener = (status) => {
     if (status.alerts && Object.values(status.alerts).some((alert) => alert)) {
         console.warn("🚨 MSP Alert:", status.alerts);
     }

-    // Log every 10 seconds if monitoring
-    if (Date.now() % 10000 < 500) {
+    // Log every 10 seconds
+    const now = Date.now();
+    if (now - this._lastLogTime >= 10000) {
+        this._lastLogTime = now;
         console.log(
             `📊 MSP Status: Queue=${status.currentQueueSize}/${status.maxQueueSize}, Requests=${status.metrics.totalRequests}, AvgTime=${Math.round(status.metrics.avgResponseTime)}ms`,
         );
     }
 };
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cdbdda0 and 086ac12.

📒 Files selected for processing (10)
  • src/js/main.js (2 hunks)
  • src/js/msp.js (3 hunks)
  • src/js/msp/MSP_DEBUG_README.md (1 hunks)
  • src/js/msp/msp_debug_dashboard.js (1 hunks)
  • src/js/msp/msp_debug_tools.js (1 hunks)
  • src/js/msp/msp_queue_monitor.js (1 hunks)
  • src/js/msp/msp_stress_test.js (1 hunks)
  • src/js/msp/msp_test_runner.js (1 hunks)
  • src/js/utils/checkBrowserCompatibility.js (1 hunks)
  • test/js/msp/MSPHelper.test.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • test/js/msp/MSPHelper.test.js
  • src/js/msp.js
  • src/js/main.js
  • src/js/utils/checkBrowserCompatibility.js
  • src/js/msp/msp_debug_tools.js
  • src/js/msp/msp_queue_monitor.js
  • src/js/msp/msp_stress_test.js
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: haslinghuis
PR: betaflight/betaflight-configurator#4532
File: src/js/VirtualFC.js:234-234
Timestamp: 2025-06-27T22:06:49.210Z
Learning: In the betaflight-configurator codebase, the VirtualFC.js AUX_CONFIG array must stay synchronized with the betaflight firmware's msp_box.c definitions to ensure proper auxiliary mode functionality. Changes to mode names should follow the firmware source code even if it potentially breaks backward compatibility with saved presets.
Learnt from: haslinghuis
PR: betaflight/betaflight-configurator#4532
File: src/js/VirtualFC.js:234-234
Timestamp: 2025-06-27T22:06:49.210Z
Learning: In the betaflight-configurator codebase, the VirtualFC.js AUX_CONFIG array must stay synchronized with the betaflight firmware's msp_box.c definitions to ensure proper auxiliary mode functionality. Changes to mode names should follow the firmware source code even if it potentially breaks backward compatibility with saved presets.
📚 Learning: static analysis tool errors (like biome, eslint, ruff, etc.) are typically required fixes in pull re...
Learnt from: syahmizul
PR: betaflight/betaflight-configurator#0
File: :0-0
Timestamp: 2025-06-14T17:00:57.882Z
Learning: Static analysis tool errors (like Biome, ESLint, Ruff, etc.) are typically required fixes in pull requests, not optional suggestions. These are usually flagged as "Changes requested" and must be resolved before the PR can be merged.

Applied to files:

  • src/js/msp/msp_test_runner.js
  • src/js/msp/msp_debug_dashboard.js
📚 Learning: in the betaflight configurator osd positioning feature (src/js/tabs/osd.js), the elementwidth and el...
Learnt from: syahmizul
PR: betaflight/betaflight-configurator#4516
File: src/js/tabs/osd.js:3544-3559
Timestamp: 2025-06-14T13:43:10.231Z
Learning: In the Betaflight Configurator OSD positioning feature (src/js/tabs/osd.js), the elementWidth and elementHeight calculations use limits.maxX - limits.minX and limits.maxY - limits.minY without adding 1. While adding 1 would be mathematically correct for inclusive bounds, it causes visual centering issues where elements appear off-center. The current calculation produces better visual centering results, so the +1 correction should be avoided to maintain proper visual alignment.

Applied to files:

  • src/js/msp/msp_debug_dashboard.js
📚 Learning: in the betaflight-configurator codebase, the virtualfc.js aux_config array must stay synchronized wi...
Learnt from: haslinghuis
PR: betaflight/betaflight-configurator#4532
File: src/js/VirtualFC.js:234-234
Timestamp: 2025-06-27T22:06:49.210Z
Learning: In the betaflight-configurator codebase, the VirtualFC.js AUX_CONFIG array must stay synchronized with the betaflight firmware's msp_box.c definitions to ensure proper auxiliary mode functionality. Changes to mode names should follow the firmware source code even if it potentially breaks backward compatibility with saved presets.

Applied to files:

  • src/js/msp/MSP_DEBUG_README.md
🪛 LanguageTool
src/js/msp/MSP_DEBUG_README.md

[grammar] ~191-~191: Ensure spelling is correct
Context: ...out Rate**: Percentage of requests that timeout - Request Distribution: Breakdown b...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.17.2)
src/js/msp/MSP_DEBUG_README.md

317-317: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (6)
src/js/msp/msp_test_runner.js (1)

291-394: Well-implemented stress scenarios with proper error handling.

The stress test scenarios are well-structured with appropriate error handling for each MSP promise. The error messages provide context about which scenario failed, making debugging easier.

src/js/msp/msp_debug_dashboard.js (5)

34-41: Secure HTML escaping implementation.

Good use of the browser's built-in escaping mechanism via textContent. This approach is secure and prevents XSS vulnerabilities.


925-988: Excellent Hi-DPI canvas implementation.

The chart drawing properly handles high-density displays by adjusting canvas resolution based on devicePixelRatio. This ensures crisp rendering on all displays.


23-26: Excellent performance optimizations throughout the dashboard.

The implementation includes several performance best practices:

  • DOM element caching to avoid repeated queries
  • Data change detection to prevent unnecessary DOM updates
  • Chart update throttling with requestAnimationFrame
  • Efficient comparison strategies (primitives first, then expensive operations)

These optimizations will ensure smooth performance even during high-frequency monitoring.

Also applies to: 682-729, 793-806, 894-920


534-608: Well-designed user interaction handling.

The update pausing mechanism provides an excellent user experience by:

  • Preventing UI flicker during interactions
  • Using appropriate pause durations for different interaction types
  • Providing visual feedback with the pause indicator
  • Properly managing timeouts to avoid memory leaks

Also applies to: 765-789


1174-1175: Safe handling of potentially undefined global variables.

Good use of optional chaining to prevent runtime errors if MSPTestRunner hasn't been loaded yet.

Copy link

sonarqubecloud bot commented Aug 3, 2025

Copy link
Contributor

github-actions bot commented Aug 3, 2025

Preview URL: https://bef646af.betaflight-configurator.pages.dev

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

Successfully merging this pull request may close these issues.

3 participants