-
-
Notifications
You must be signed in to change notification settings - Fork 983
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
base: master
Are you sure you want to change the base?
Improve msp send #4510
Conversation
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 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. 📒 Files selected for processing (8)
WalkthroughThe 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
Sequence Diagram(s)Fixed Timeout Request Flow in MSPsequenceDiagram
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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~7 minutes ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this 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
ignoresdoCallbackOnError
flagError-side invocation of
callback_msp
is unconditional. That breaks the original contract implied by thedoCallbackOnError
parameter and can trigger “double callbacks” or resolve aPromise
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
storescallbackOnError
, 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-shootMIN_TIMEOUT
this.timeout--
happens on every send, regardless of frequency, and can push the value belowMIN_TIMEOUT
whentimeout
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
📒 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 ofcode
; 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 whendataLength > 0
(even if duplicate).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this 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 GCWhen
MAX_RETRIES
is exceeded the function bails out but does notclearTimeout(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
📒 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) { |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
eb77f13
to
4fcad77
Compare
There was a problem hiding this 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 issueDuplicate requests silently drop callbacks
When
isDuplicateRequest
istrue
, the newcallback_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:
- Produce the same hash for different payloads (higher collision risk on large traffic).
- 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 timesend_message
is invoked.
WithMAX_QUEUE_SIZE
already tracked, switching to aMap<requestKey, requestObj>
would give O(1) look-ups and simplify removal logic.
433-438
: Timer handle lost if_setupTimeout
is called twice
_setupTimeout
overwritesrequestObj.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 resetsstart
— metrics become meaningless
requestObj.start
is set once insend_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
📒 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
referencesYou have introduced a fixed
TIMEOUT
constant but removed the old adaptivethis.timeout
.
Please grep the rest of the file / project for stalethis.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 foundA full‐repo search for
this.timeout
only returns occurrences ofthis.timeout_array
insrc/js/gui.js
, not the legacythis.timeout
property. There are no remaining direct references to the removed adaptive timeout field—this concern can be closed.
There was a problem hiding this 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 unresolvedIf you adopt the
Map
optimisation above, ensure this method also deletes therequestKey
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
, andTIMEOUT
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 fromCONFIGURATOR
, a constructor argument, or at least exporting setters.
399-420
: Duplicate check performsO(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:
- Keep a
Map<requestKey, requestObj>
to getO(1)
look-ups and removals.- 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
callsclearTimeout(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 linksRetries 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 bugThe 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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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) => { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this 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 mutationDefining
MAX_RETRIES
,MAX_QUEUE_SIZE
andTIMEOUT
directly on the mutableMSP
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”.
Additionallyhash
may end up negative, so keys like42:-123
and42: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
: Guardcallback_sent
with a functional check
promise()
passesfalse
, socallback_sent()
may be a boolean.
A trivial typeof guard avoids accidentalfalse 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 aMap<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
📒 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 onsendInfo.bytesSent
- A fixed 1 s retry interval can hammer sluggish links. Consider exponential back-off or
TIMEOUT * (1 + attempts)
to avoid congestion.- If the serial driver does not populate
bytesSent
,sendInfo.bytesSent === bufferOut.byteLength
evaluatesfalse
⇒ 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) {
- You re-assign
requestObj.timer
but never clear it inside_handleTimeout
.
Although the fired timer auto-invalidates, explicitlyclearTimeout(requestObj.timer)
before re-arming removes any doubt.
checking the coderrabbit "nitpicks"? i'm finding them useful outside of this project. |
cdbdda0
to
086ac12
Compare
There was a problem hiding this 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 frameworksrc/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
📒 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.
d0f39ae
to
52e1aeb
Compare
52e1aeb
to
bef646a
Compare
|
Preview URL: https://bef646af.betaflight-configurator.pages.dev |
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Tests