From cc8d1375f640fcdc8de22519b0733c3efb455912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ant=C3=B4nio=20Cardoso?= Date: Thu, 30 Jul 2026 19:58:23 -0300 Subject: [PATCH 1/2] frontend: store: mavlink: Allow lowering message refresh rates Track per-message rate claims and apply max(claims) so consumers can lower streams on destroy without overriding other live subscribers. --- core/frontend/src/store/mavlink.ts | 65 +++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/core/frontend/src/store/mavlink.ts b/core/frontend/src/store/mavlink.ts index 007d920594..647e9bdb08 100644 --- a/core/frontend/src/store/mavlink.ts +++ b/core/frontend/src/store/mavlink.ts @@ -16,6 +16,18 @@ interface messsageRefreshRate { refreshRate: number } +// Per-message requested rates from live consumers; wire rate is max(claims), or 1 Hz when empty. +const message_rate_claims: Dictionary = {} +const IDLE_MESSAGE_RATE_HZ = 1 + +function claimedRefreshRate(messageName: string): number { + const claims = message_rate_claims[messageName] + if (!claims?.length) { + return IDLE_MESSAGE_RATE_HZ + } + return Math.max(...claims) +} + @Module({ dynamic: true, store, @@ -24,31 +36,64 @@ interface messsageRefreshRate { class MavlinkStore extends VuexModule { available_messages: Dictionary = {} + available_identified_messages: Dictionary> = {} message_listeners: Dictionary = {} - @Action({ commit: 'updateMessage' }) - setMessageRefreshRate(rate: messsageRefreshRate): void { + @Action + subscribeMessageRefreshRate(rate: messsageRefreshRate): void { const { messageName, refreshRate } = rate if (refreshRate < 0) { console.warn(`Invalid request rate requested for message ${messageName}@${refreshRate}Hz`) + return } - mavlink2rest.requestMessageRate(messageName, refreshRate, autopilot_data.system_id) - // Remove any listener that has a lower frequency than requested + if (!message_rate_claims[messageName]) { + message_rate_claims[messageName] = [] + } + message_rate_claims[messageName].push(refreshRate) + this.applyMessageRefreshRate(messageName) + } + + @Action + unsubscribeMessageRefreshRate(rate: messsageRefreshRate): void { + const { messageName, refreshRate } = rate + const claims = message_rate_claims[messageName] + if (!claims?.length) { + return + } + + const index = claims.indexOf(refreshRate) + if (index < 0) { + console.warn(`No ${refreshRate}Hz claim to release for message ${messageName}`) + return + } + claims.splice(index, 1) + this.applyMessageRefreshRate(messageName) + } + + /** Lifetime / one-shot claim. Prefer subscribe/unsubscribe when the consumer leaves. */ + @Action + setMessageRefreshRate(rate: messsageRefreshRate): void { + this.subscribeMessageRefreshRate(rate) + } + + @Action + applyMessageRefreshRate(messageName: string): void { + const refreshRate = claimedRefreshRate(messageName) + + // Equal rate: keep existing listener and skip the wire request. + // Any other rate change discards the listener and creates a replacement. if (messageName in this.message_listeners) { - const currentRate = this.message_listeners[messageName].frequency - if (currentRate > refreshRate) { - console.warn( - `Request with higher rate already registered for message ${messageName}@${currentRate}Hz vs ${refreshRate}Hz`, - ) + if (this.message_listeners[messageName].frequency === refreshRate) { return } this.message_listeners[messageName].discard() } - // Create a new listener + mavlink2rest.requestMessageRate(messageName, refreshRate, autopilot_data.system_id) + this.message_listeners[messageName] = mavlink2rest.startListening(messageName).setCallback((receivedMessage) => { this.updateMessage({ messageName, From c73a1ec8507ec966cd4c00d604f5f087b58dd434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ant=C3=B4nio=20Cardoso?= Date: Thu, 30 Jul 2026 19:58:23 -0300 Subject: [PATCH 2/2] frontend: vehiclesetup: Clean up MAVLink rates and compass timers on leave Release message rate claims on destroy, discard position listeners, and clear the CompassDisplay render interval plus GSAP tweens. --- .../src/components/vehiclesetup/PwmSetup.vue | 4 ++-- .../compass/AutoCoordinateDetector.vue | 18 +++++++++++----- .../configuration/compass/CompassDisplay.vue | 16 +++++++++++--- .../compass/LevelHorizonCalibration.vue | 21 ++++++++++++++++++- .../vehiclesetup/overview/GyroCalib.vue | 11 ++++++++-- core/frontend/src/views/MainView.vue | 3 ++- 6 files changed, 59 insertions(+), 14 deletions(-) diff --git a/core/frontend/src/components/vehiclesetup/PwmSetup.vue b/core/frontend/src/components/vehiclesetup/PwmSetup.vue index 12eec0013e..7cc0f32394 100644 --- a/core/frontend/src/components/vehiclesetup/PwmSetup.vue +++ b/core/frontend/src/components/vehiclesetup/PwmSetup.vue @@ -426,7 +426,7 @@ export default Vue.extend({ this.motor_writer_interval = setInterval(this.write_motors, 100) fetchCurrentBoard() - mavlink.setMessageRefreshRate({ messageName: 'SERVO_OUTPUT_RAW', refreshRate: 10 }) + mavlink.subscribeMessageRefreshRate({ messageName: 'SERVO_OUTPUT_RAW', refreshRate: 10 }) this.desired_armed_state = this.is_armed this.installListeners() this.updateReversionValues() @@ -434,7 +434,7 @@ export default Vue.extend({ beforeDestroy() { clearInterval(this.motor_zeroer_interval) clearInterval(this.motor_writer_interval) - mavlink.setMessageRefreshRate({ messageName: 'SERVO_OUTPUT_RAW', refreshRate: 1 }) + mavlink.unsubscribeMessageRefreshRate({ messageName: 'SERVO_OUTPUT_RAW', refreshRate: 10 }) this.uninstallListeners() }, methods: { diff --git a/core/frontend/src/components/vehiclesetup/configuration/compass/AutoCoordinateDetector.vue b/core/frontend/src/components/vehiclesetup/configuration/compass/AutoCoordinateDetector.vue index 2085362e7b..deb52f4a3e 100644 --- a/core/frontend/src/components/vehiclesetup/configuration/compass/AutoCoordinateDetector.vue +++ b/core/frontend/src/components/vehiclesetup/configuration/compass/AutoCoordinateDetector.vue @@ -61,9 +61,10 @@