@@ -133,97 +133,89 @@ def set_params(params: List[tuple[str, Number, int]]) -> None:
133133 if droneStatus .drone is None :
134134 raise RuntimeError ("No drone connected to set parameters on." )
135135
136- for param in params :
137- param_name , param_value , param_type = param
138- max_retries = 7
139- retry_count = 0
140- param_set_successfully = False
141-
142- while retry_count < max_retries and not param_set_successfully :
143- try :
144- # Clear any pending messages first to avoid stale responses
145- droneStatus .drone .master .recv_match (
146- type = "PARAM_VALUE" , blocking = False , timeout = 0.1
147- )
148-
149- # Send parameter set command
150- logger .debug (
151- f"Attempt { retry_count + 1 } : Setting { param_name } to { param_value } (type: { param_type } )"
152- )
153- droneStatus .drone .master .mav .param_set_send (
154- droneStatus .drone .master .target_system ,
155- droneStatus .drone .master .target_component ,
156- param_name .encode ("utf-8" ),
157- param_value ,
158- param_type ,
159- )
160-
161- # Wait for ACK with exponential backoff timeout
162- base_timeout = 2 + (retry_count * 0.5 ) # Increase timeout with retries
163- start_time = time .time ()
164-
165- while time .time () - start_time < base_timeout :
166- message = droneStatus .drone .master .recv_match (
167- type = "PARAM_VALUE" , blocking = False , timeout = 0.1
136+ MAX_RETRIES = 7
137+ TIMEOUT = 2.0
138+
139+ def drain_all ():
140+ """Drain the entire buffer"""
141+ while True :
142+ msg = droneStatus .drone .master .recv_match (blocking = False )
143+ if msg is None :
144+ break
145+
146+ pending = {
147+ param_name : (param_value , param_type )
148+ for param_name , param_value , param_type in params
149+ }
150+ retries = {name : 0 for name , _ , _ in params }
151+
152+ logger .debug (f"Setting { len (params )} params" )
153+
154+ drain_all ()
155+
156+ while pending :
157+ # Send all the pending params at once
158+ for param_name , (param_value , param_type ) in pending .items ():
159+ droneStatus .drone .master .mav .param_set_send (
160+ droneStatus .drone .master .target_system ,
161+ droneStatus .drone .master .target_component ,
162+ param_name .encode ("utf-8" ),
163+ param_value ,
164+ param_type ,
165+ )
166+
167+ start_time = time .time ()
168+
169+ while time .time () - start_time < TIMEOUT and pending :
170+ msg = droneStatus .drone .master .recv_match (blocking = False )
171+
172+ if msg is None :
173+ time .sleep (0.005 )
174+ continue
175+
176+ if msg .get_type () != "PARAM_VALUE" :
177+ continue
178+
179+ param_name = msg .param_id
180+ if isinstance (param_name , bytes ):
181+ param_name = param_name .decode ("utf-8" ).strip ("\x00 " )
182+
183+ if param_name in pending :
184+ param_value , param_type = pending [param_name ]
185+ received_value = msg .param_value
186+
187+ if (
188+ abs (received_value - param_value ) < 0.001
189+ ): # Allow for floating point precision
190+ logger .info (
191+ f"Successfully set { param_name } to { param_value } (confirmed: { received_value } )"
168192 )
169193
170- if message is not None :
171- # Check if it's the parameter we're looking for
172- if message .param_id == param_name :
173- # Verify the value was actually set correctly
174- received_value = message .param_value
175- if (
176- abs (received_value - param_value ) < 0.001
177- ): # Allow for floating point precision
178- logger .info (
179- f"Successfully set { param_name } to { param_value } (confirmed: { received_value } )"
180- )
181- # Keep cached params coherent with direct MAVLink writes used in tests.
182- droneStatus .drone .paramsController .saveParam (
183- param_name ,
184- received_value ,
185- param_type ,
186- )
187- param_set_successfully = True
188- break
189- else :
190- logger .warning (
191- f"Parameter { param_name } was set but value mismatch: expected { param_value } , got { received_value } "
192- )
193- else :
194- # Got a different parameter response, keep waiting
195- logger .debug (
196- f"Received PARAM_VALUE for { message .param_id } , waiting for { param_name } "
197- )
198-
199- time .sleep (0.02 ) # Reduced polling interval for faster response
200-
201- if not param_set_successfully :
202- # Timeout occurred
203- retry_count += 1
204- if retry_count < max_retries :
205- backoff_delay = 0.2 * (2 ** retry_count ) # Exponential backoff
206- logger .warning (
207- f"Timeout setting { param_name } , retrying in { backoff_delay :.1f} s... ({ retry_count } /{ max_retries } )"
208- )
209- time .sleep (backoff_delay )
210- else :
211- error_msg = (
212- f"Failed to set { param_name } after { max_retries } attempts"
213- )
214- raise RuntimeError (error_msg )
215- except Exception as e :
216- retry_count += 1
217- if retry_count < max_retries :
218- backoff_delay = 0.2 * (2 ** retry_count )
219- logger .warning (
220- f"Error setting { param_name } : { e } , retrying in { backoff_delay :.1f} s... ({ retry_count } /{ max_retries } )"
194+ # Keep cached params coherent with direct MAVLink writes used in tests.
195+ droneStatus .drone .paramsController .saveParam (
196+ param_name ,
197+ received_value ,
198+ param_type ,
221199 )
222- time .sleep (backoff_delay )
200+
201+ del pending [param_name ]
202+
223203 else :
224- error_msg = f"Failed after { max_retries } attempts: { e } "
225- raise RuntimeError (error_msg )
204+ logger .warning (
205+ f"Parameter { param_name } was set but value mismatch: expected { param_value } , got { received_value } "
206+ )
207+
208+ # Retry only missing
209+ if pending :
210+ for param_name in list (pending .keys ()):
211+ retries [param_name ] += 1
212+ if retries [param_name ] > MAX_RETRIES :
213+ raise RuntimeError (
214+ f"Failed to set { param_name } after { MAX_RETRIES } attempts"
215+ )
216+
217+ logger .warning (f"Retrying: { list (pending .keys ())} " )
226218
227- time . sleep ( 0.1 )
219+ drain_all ( )
228220
229221 logger .info (f"Successfully set all { len (params )} parameters" )
0 commit comments