Skip to content

Commit 9b1c279

Browse files
committed
Fix bug with datastreams not getting set, fix bug with heartbeat thread not sending at 1hz
1 parent fba7c4a commit 9b1c279

4 files changed

Lines changed: 86 additions & 20 deletions

File tree

gcs/src/components/settingsModal.jsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ import {
2424
} from "@tabler/icons-react"
2525
import { Octokit } from "octokit"
2626
import { memo, useEffect, useState } from "react"
27+
import { useDispatch } from "react-redux"
2728
import semverGt from "semver/functions/gt"
2829
import DefaultSettings from "../../data/default_settings.json"
30+
import { DATA_STREAM_MAP } from "../helpers/mavlinkConstants"
2931
import {
3032
closeLoadingNotification,
3133
redColor,
3234
showLoadingNotification,
3335
} from "../helpers/notification"
34-
import { useDispatch } from "react-redux"
3536
import { emitSetStreamRates } from "../redux/slices/droneConnectionSlice"
36-
import { DATA_STREAM_MAP } from "../helpers/mavlinkConstants"
3737

3838
const octokit = new Octokit({})
3939

@@ -203,10 +203,16 @@ function ReleaseCheckRow() {
203203
function SetRatesRow() {
204204
const { getSetting } = useSettings()
205205
const dispatch = useDispatch()
206+
const developerDefaults = DefaultSettings?.Developer ?? {}
206207

207208
const onClick = () => {
208209
for (const [name, value] of Object.entries(DATA_STREAM_MAP)) {
209-
let rate = getSetting(`Developer.${name}`)
210+
if (!Object.hasOwn(developerDefaults, name)) continue
211+
212+
const rateSetting = getSetting(`Developer.${name}`)
213+
const rate = Number(rateSetting)
214+
if (!Number.isFinite(rate)) continue
215+
if (rate < 0 || rate > 15) continue
210216
dispatch(emitSetStreamRates({ stream: value, rate: rate }))
211217
}
212218
}

gcs/src/helpers/settingsProvider.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@ export const SettingsProvider = ({ children }) => {
5656

5757
const getSetting = (setting) => {
5858
const userSetting = getSettingFromSettings(setting, settings.settings)
59+
const defaultSetting = getSettingFromSettings(setting, DefaultSettings)
5960

60-
return userSetting === null
61-
? getSettingFromSettings(setting, DefaultSettings).default
62-
: userSetting
61+
if (userSetting !== null) return userSetting
62+
63+
if (defaultSetting === null || defaultSetting === undefined) {
64+
return null
65+
}
66+
67+
return defaultSetting.default
6368
}
6469

6570
return (

radio/app/drone.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
mavutil.mavlink.MAV_DATA_STREAM_RAW_SENSORS: 1,
4747
mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS: 1,
4848
mavutil.mavlink.MAV_DATA_STREAM_RC_CHANNELS: 1,
49+
mavutil.mavlink.MAV_DATA_STREAM_RAW_CONTROLLER: 1,
4950
mavutil.mavlink.MAV_DATA_STREAM_POSITION: 1,
5051
mavutil.mavlink.MAV_DATA_STREAM_EXTRA1: 4,
5152
mavutil.mavlink.MAV_DATA_STREAM_EXTRA2: 3,
@@ -520,7 +521,13 @@ def setupSingleDataStream(self, stream: int) -> None:
520521
Args:
521522
stream (int): The data stream to set up
522523
"""
523-
self.sendDataStreamRequestMessage(stream, DATASTREAM_RATES[stream])
524+
rate = DATASTREAM_RATES.get(stream)
525+
if rate is None:
526+
self.logger.warning(
527+
f"No configured rate for stream {stream}; skipping setup request"
528+
)
529+
return
530+
self.sendDataStreamRequestMessage(stream, rate)
524531

525532
@sendingCommandLock
526533
def sendDataStreamRequestMessage(self, stream: int, rate: int) -> None:
@@ -888,7 +895,15 @@ def getLinkDebugData(self) -> None:
888895

889896
def sendHeartbeatMessage(self) -> None:
890897
"""Sends a heartbeat message to the drone every second."""
898+
heartbeat_interval_secs = 1.0
899+
next_heartbeat_time = time.monotonic()
900+
891901
while self.is_active.is_set():
902+
now = time.monotonic()
903+
sleep_time = next_heartbeat_time - now
904+
if sleep_time > 0:
905+
time.sleep(sleep_time)
906+
892907
try:
893908
self.master.mav.heartbeat_send(
894909
mavutil.mavlink.MAV_TYPE_GCS,
@@ -899,7 +914,11 @@ def sendHeartbeatMessage(self) -> None:
899914
)
900915
except Exception as e:
901916
self.logger.error(f"Failed to send heartbeat: {e}", exc_info=True)
902-
time.sleep(1)
917+
918+
# Keep a stable 1Hz cadence and recover if we fall behind.
919+
next_heartbeat_time += heartbeat_interval_secs
920+
if next_heartbeat_time < time.monotonic():
921+
next_heartbeat_time = time.monotonic() + heartbeat_interval_secs
903922

904923
def startThread(self) -> None:
905924
"""Starts the listener and sender threads."""

radio/app/endpoints/states.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import copy
2+
13
from pymavlink import mavutil
24
from typing_extensions import TypedDict
35

46
import app.droneStatus as droneStatus
57
from app import logger, socketio
8+
from app.drone import DATASTREAM_RATES
69
from app.utils import (
710
missingParameterError,
811
sendMessage,
@@ -48,6 +51,17 @@ class SetStreamRateType(TypedDict):
4851
}
4952

5053

54+
DASHBOARD_STREAM_RATES = copy.deepcopy(DATASTREAM_RATES)
55+
56+
57+
def apply_dashboard_stream_rates() -> None:
58+
if not droneStatus.drone:
59+
return
60+
61+
for stream, rate in DASHBOARD_STREAM_RATES.items():
62+
droneStatus.drone.sendDataStreamRequestMessage(stream, rate)
63+
64+
5165
@socketio.on("set_state")
5266
def set_state(data: SetStateType) -> None:
5367
"""
@@ -73,29 +87,37 @@ def set_state(data: SetStateType) -> None:
7387
# Remove all existing message listeners
7488
droneStatus.drone.clearAllMessageListeners()
7589

76-
# Always setup position stream to get GLOBAL_POSITION_INT messages
77-
droneStatus.drone.setupSingleDataStream(mavutil.mavlink.MAV_DATA_STREAM_POSITION)
90+
# Always setup position stream to get GLOBAL_POSITION_INT messages on
91+
# non-dashboard pages
92+
if droneStatus.state != "dashboard":
93+
droneStatus.drone.sendDataStreamRequestMessage(
94+
mavutil.mavlink.MAV_DATA_STREAM_POSITION, 1
95+
)
7896

7997
for message in GLOBAL_MESSAGE_LISTENERS:
8098
droneStatus.drone.addMessageListener(message, sendMessage)
8199

82100
if droneStatus.state == "dashboard":
83-
droneStatus.drone.setupDataStreams()
101+
apply_dashboard_stream_rates()
84102
for message in STATES_MESSAGE_LISTENERS["dashboard"]:
85103
droneStatus.drone.addMessageListener(message, sendMessage)
86104

87105
elif droneStatus.state == "missions":
88-
droneStatus.drone.setupSingleDataStream(
89-
mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS
106+
droneStatus.drone.sendDataStreamRequestMessage(
107+
mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS, 1
90108
)
91109
for message in STATES_MESSAGE_LISTENERS["missions"]:
92110
droneStatus.drone.addMessageListener(message, sendMessage)
93111
elif droneStatus.state == "graphs":
94-
droneStatus.drone.setupSingleDataStream(
95-
mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS
112+
droneStatus.drone.sendDataStreamRequestMessage(
113+
mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS, 1
114+
)
115+
droneStatus.drone.sendDataStreamRequestMessage(
116+
mavutil.mavlink.MAV_DATA_STREAM_EXTRA1, 4
117+
)
118+
droneStatus.drone.sendDataStreamRequestMessage(
119+
mavutil.mavlink.MAV_DATA_STREAM_EXTRA2, 3
96120
)
97-
droneStatus.drone.setupSingleDataStream(mavutil.mavlink.MAV_DATA_STREAM_EXTRA1)
98-
droneStatus.drone.setupSingleDataStream(mavutil.mavlink.MAV_DATA_STREAM_EXTRA2)
99121

100122
for message in STATES_MESSAGE_LISTENERS["graphs"]:
101123
droneStatus.drone.addMessageListener(message, sendMessage)
@@ -128,11 +150,25 @@ def set_stream_rate(data: SetStreamRateType):
128150
return
129151

130152
if (rate := data.get("rate", None)) is None:
131-
return missingParameterError("set_state", "state")
153+
return missingParameterError("set_stream_rate", "rate")
154+
155+
if (stream := data.get("stream", None)) is None:
156+
return missingParameterError("set_stream_rate", "stream")
157+
158+
try:
159+
rate = int(rate)
160+
stream = int(stream)
161+
except (ValueError, TypeError):
162+
logger.error("Invalid set_stream_rate payload types")
163+
return
132164

133165
if rate > 15 or rate < 0:
134166
logger.error("Cannot set data stream rate outside of range [0, 15]")
135167
return
136168

137-
logger.info(f"Setting data stream rate {data['rate']}")
138-
droneStatus.drone.sendDataStreamRequestMessage(data["stream"], rate)
169+
DASHBOARD_STREAM_RATES[stream] = rate
170+
171+
# Dashboard-only behavior: only apply immediately while dashboard is active.
172+
if droneStatus.state == "dashboard":
173+
logger.info(f"Setting dashboard data stream {stream} rate to {rate}")
174+
droneStatus.drone.sendDataStreamRequestMessage(stream, rate)

0 commit comments

Comments
 (0)