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 29 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

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive MSP Debug Dashboard for real-time queue monitoring, live metrics, alerts, and stress testing.
    • Added real-time MSP queue monitoring with alerting, performance grading, and detailed reporting.
    • Implemented a full-featured stress test suite for MSP with multiple test scenarios and recovery checks.
    • Provided a global debug API and test runner with quick monitoring, health checks, and report exporting.
    • Added dynamic loading of debug tools in development environments for immediate feedback.
  • Bug Fixes

    • Improved handling of configuration retrieval during process startup to avoid errors with missing data.
  • Documentation

    • Added in-depth documentation for MSP Debug Tools, including usage, features, and troubleshooting.
  • Refactor

    • Simplified MSP message timeout logic to use a fixed timeout and streamlined duplicate request checks.
    • Updated browser compatibility checks to support test environments.
  • Tests

    • Updated tests to use the latest API version for board info validation.

Copy link
Contributor

coderabbitai bot commented Jun 10, 2025

Walkthrough

The changes introduce a comprehensive suite of debugging and stress-testing tools for the MSP (Multiwii Serial Protocol) JavaScript implementation. This includes real-time queue monitoring, a visual debug dashboard, and a robust stress test framework. The MSP queue logic was simplified to use a fixed timeout. Conditional dynamic loading of debug tools was added for development environments, and browser compatibility checks now recognize test environments. Extensive documentation for the new debug tools was also added.

Changes

File(s) Change Summary
src/js/msp.js Simplified MSP queue timeout logic by removing adaptive timeouts and messageIsJumboFrame; replaced with fixed TIMEOUT; refactored duplicate request check and timeout handling in send_message.
src/js/main.js Added conditional dynamic import of MSP debug tools in development environments; improved handling of getConfig for firstRun.
src/js/msp/MSP_DEBUG_README.md Added comprehensive documentation for MSP Debug Tools, including features, usage, APIs, and integration notes.
src/js/msp/msp_debug_dashboard.js Added visual debug dashboard class for real-time MSP queue monitoring, stress testing, and metric visualization; exposes global MSPDebug object.
src/js/msp/msp_debug_tools.js New entry point loading all MSP debug/testing modules; auto-starts monitoring in development environments with alert logging.
src/js/msp/msp_queue_monitor.js Introduced MSPQueueMonitor class for real-time queue monitoring, alerting, metrics, and reporting; exports singleton instance.
src/js/msp/msp_stress_test.js Added MSPStressTest class for comprehensive stress testing of MSP queue, including various scenarios; exports singleton instance.
src/js/msp/msp_test_runner.js Implemented MSPTestRunner module for monitoring, running tests, generating reports, and dashboard control; exposes global API.
src/js/utils/checkBrowserCompatibility.js Updated compatibility check to consider test environments as compatible.
test/js/msp/MSPHelper.test.js Updated API version reference from API_VERSION_1_46 to API_VERSION_1_47 in tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MainJS
    participant DebugTools
    participant Dashboard
    participant QueueMonitor
    participant StressTest

    User->>MainJS: Load application
    MainJS->>DebugTools: (If dev) Dynamically import debug tools
    DebugTools->>QueueMonitor: Initialize and start monitoring
    DebugTools->>Dashboard: Initialize dashboard UI
    DebugTools->>StressTest: Initialize stress test module
    User->>Dashboard: Toggle dashboard (keyboard shortcut)
    Dashboard->>QueueMonitor: Request queue metrics and alerts
    Dashboard->>StressTest: Run stress test (on user action)
    StressTest->>QueueMonitor: Collect metrics during test
    QueueMonitor-->>Dashboard: Send updated metrics/alerts
    Dashboard-->>User: Display real-time queue status, alerts, and test results
Loading
sequenceDiagram
    participant MSP
    participant QueueMonitor
    participant StressTest

    MSP->>QueueMonitor: send_message (hooked)
    QueueMonitor->>MSP: Track request start
    MSP->>QueueMonitor: _dispatch_message/_removeRequestFromCallbacks (hooked)
    QueueMonitor->>MSP: Track request completion/failure
    StressTest->>MSP: Send test requests (various scenarios)
    MSP->>QueueMonitor: All requests monitored for metrics/alerts
    StressTest->>QueueMonitor: Retrieve metrics for reporting
Loading
sequenceDiagram
    participant User
    participant MSPTestRunner
    participant QueueMonitor
    participant StressTest
    participant Dashboard

    User->>MSPTestRunner: Run test/help/monitor command
    MSPTestRunner->>QueueMonitor: Start/stop monitor, get status
    MSPTestRunner->>StressTest: Run specific/full test suite
    MSPTestRunner->>Dashboard: Show/hide dashboard
    MSPTestRunner-->>User: Log results, show dashboard, print help
Loading

📜 Recent review details

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

📥 Commits

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

📒 Files selected for processing (2)
  • src/js/msp/msp_debug_dashboard.js (1 hunks)
  • src/js/msp/msp_test_runner.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/js/msp/msp_test_runner.js
🔇 Additional comments (9)
src/js/msp/msp_debug_dashboard.js (9)

1-29: Well-structured initialization with good separation of concerns

The constructor properly initializes all required state including chart data, update controls, performance optimization properties, and element caching. Good foundation for the dashboard implementation.


31-134: Excellent XSS protection implementation

The escapeHtml method uses a secure approach with DOM manipulation rather than string replacement, and the dashboard HTML structure is static without dynamic interpolation. This addresses previous security concerns effectively.


441-501: Proper event delegation implementation

Excellent improvement! Event listeners are now properly scoped to this.container instead of the entire document, which addresses previous performance and conflict concerns. The delegation pattern is correctly implemented for all interactive elements.


925-988: Excellent Hi-DPI canvas implementation

The canvas rendering now properly handles Hi-DPI displays by setting internal dimensions based on devicePixelRatio and scaling the drawing context accordingly. This resolves the previous blur issue and ensures sharp chart rendering on all displays.


1012-1027: XSS vulnerabilities properly addressed

All dynamic content in the queue analysis now uses escapeHtml consistently, including code values and potential issues. This effectively prevents XSS attacks from malicious queue data.


1051-1067: Secure test results rendering

Test names and status values are properly escaped before HTML insertion, preventing any potential XSS from test data while maintaining proper display formatting.


1072-1130: Multiple security and usability improvements implemented

This section addresses several previous issues:

  • Close button uses CSS class with event delegation instead of inline onclick
  • All dynamic content properly escaped with escapeHtml
  • Correct metrics path (test.metrics.* not test.metrics.metrics.*)
  • Proper event handling for interactive elements

1174-1175: Proper handling of external dependencies

The optional chaining for window.MSPTestRunner properly addresses the previous issue where the dashboard might load before the test runner, preventing runtime errors while maintaining functionality when available.


683-729: Excellent performance optimization strategy

The change detection logic with primitive checks first followed by more expensive object comparisons is well-designed. This prevents unnecessary DOM updates while maintaining accuracy, which is crucial for a real-time monitoring interface.

✨ Finishing Touches
  • 📝 Generate Docstrings

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 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 419
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: 3

🧹 Nitpick comments (8)
src/js/msp/MSP_DEBUG_README.md (5)

36-44: Fix inconsistent import paths in documentation

The import paths shown in the examples are inconsistent. In line 39, you use './src/js/msp_debug_tools.js', but in line 44, you use './src/js/msp_debug_tools.js' again. These paths should be consistent and relative to where the user would typically run the code.

-import './src/js/msp_debug_tools.js';
+import './js/msp/msp_debug_tools.js';

And for the console import:

-import('./src/js/msp_debug_tools.js');
+import('./js/msp/msp_debug_tools.js');

217-217: Remove duplicate word "dashboard"

There's a duplicated word in the keyboard shortcuts section.

-- `Ctrl+Shift+M` - Toggle debug dashboard
+- `Ctrl+Shift+M` - Toggle debug dashboard
 - Dashboard is draggable and resizable

280-281: Use consistent spelling for "autoloading"

The compound word should be spelled as one word for consistency.

-### Auto-loading
-The debug tools auto-load when `msp_debug_tools.js` is imported.
+### Autoloading
+The debug tools autoload when `msp_debug_tools.js` is imported.

317-325: Add language identifier to code block

The fenced code block should have a language specified for proper syntax highlighting.

-```
+```text
 src/js/
 ├── msp_queue_monitor.js     # Core monitoring functionality
 ├── msp_stress_test.js       # Stress testing framework
 ├── msp_debug_dashboard.js   # Visual dashboard UI
 ├── msp_test_runner.js       # Console command interface
 └── msp_debug_tools.js       # Integration and auto-loading

---

`359-359`: **Add missing article for grammatical correctness**


```diff
-Same as Betaflight Configurator project.
+Same as the Betaflight Configurator project.
src/js/msp/msp_stress_test.js (3)

126-126: Use optional chaining for cleaner code

Replace manual null checks with optional chaining for better readability and safety.

-const successful = results.filter((r) => r.status === "fulfilled" && !(r.value && r.value.error)).length;
+const successful = results.filter((r) => r.status === "fulfilled" && !r.value?.error).length;

And for line 134:

-peakQueueSize: (this.monitor.getStatus().metrics || {}).queuePeakSize ?? 0,
+peakQueueSize: this.monitor.getStatus().metrics?.queuePeakSize ?? 0,

Also applies to: 134-135


231-234: Simplify null checks with optional chaining

Use optional chaining for cleaner error checking.

-const successful = results.filter((r) => r.status === "fulfilled" && !(r.value && r.value.error)).length;
+const successful = results.filter((r) => r.status === "fulfilled" && !r.value?.error).length;
 const duplicateErrors = results.filter(
-    (r) => r.status === "rejected" || (r.value && r.value.error && r.value.error.includes("duplicate")),
+    (r) => r.status === "rejected" || r.value?.error?.includes("duplicate"),
 ).length;

335-335: Apply optional chaining consistently

-const successful = results.filter((r) => r.status === "fulfilled" && !(r.value && r.value.error)).length;
+const successful = results.filter((r) => r.status === "fulfilled" && !r.value?.error).length;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 972ef7a and b793fe4.

📒 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 (6)
  • test/js/msp/MSPHelper.test.js
  • src/js/main.js
  • src/js/utils/checkBrowserCompatibility.js
  • src/js/msp/msp_debug_tools.js
  • src/js/msp.js
  • src/js/msp/msp_queue_monitor.js
🧰 Additional context used
🪛 LanguageTool
src/js/msp/MSP_DEBUG_README.md

[uncategorized] ~36-~36: The preposition “on” seems more likely in this position than the preposition “in”.
Context: ...he Debug Tools Include the debug tools in your page: ```javascript import './src...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION_IN_ON)


[uncategorized] ~60-~60: You might be missing the article “the” here.
Context: ...press Ctrl+Shift+M **Quick test of alert system:**javascript MSPDebug.testAl...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~116-~116: A punctuation mark might be missing here.
Context: ... Description | |---------|-------------| | MSPDebug.setTestThresholds() | Lower...

(AI_EN_LECTOR_MISSING_PUNCTUATION)


[uncategorized] ~124-~124: A punctuation mark might be missing here.
Context: ... Description | |---------|-------------| | MSPDebug.show() | Show visual debug ...

(AI_EN_LECTOR_MISSING_PUNCTUATION)


[duplication] ~217-~217: Possible typo: you repeated a word.
Context: ...rtcuts - Ctrl+Shift+M - Toggle debug dashboard - Dashboard is draggable and resizable ## Example ...

(ENGLISH_WORD_REPEAT_RULE)


[misspelling] ~280-~280: This word is normally spelled as one.
Context: ...Original MSP behavior is preserved ### Auto-loading The debug tools auto-load when `msp_deb...

(EN_COMPOUNDS_AUTO_LOADING)


[misspelling] ~281-~281: This word is normally spelled as one.
Context: ...erved ### Auto-loading The debug tools auto-load when msp_debug_tools.js is imported. ...

(EN_COMPOUNDS_AUTO_LOAD)


[uncategorized] ~313-~313: You might be missing the article “the” here.
Context: ...n as documented - Auto-loading works in development environment ## File Structure ``` src...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~344-~344: You might be missing the article “the” here.
Context: ...ry MSPTestRunner.showDashboard() from console - Check for CSS conflicts - Verify no p...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~353-~353: The preposition ‘to’ seems more likely in this position.
Context: ...ssTestclass 2. Update monitor metrics inMSPQueueMonitor` 3. Extend dashboard U...

(AI_HYDRA_LEO_REPLACE_IN_TO)


[uncategorized] ~359-~359: You might be missing the article “the” here.
Context: ...this documentation ## License Same as Betaflight Configurator project.

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

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

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

(MD040, fenced-code-language)

🪛 Biome (1.9.4)
src/js/msp/msp_stress_test.js

[error] 126-126: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 134-135: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 231-231: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 233-234: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 335-335: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (1)
src/js/msp/msp_debug_dashboard.js (1)

1165-1166: Good use of optional chaining for MSPTestRunner

The optional chaining correctly handles cases where MSPTestRunner might not be loaded yet.

Copy link

Copy link
Contributor

Preview URL: https://cdbdda0f.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