@@ -26,6 +26,41 @@ use pyo3_stub_gen_derive::*;
2626use std:: sync:: Arc ;
2727
2828use crate :: error:: to_pyerr;
29+ use crate :: value:: value_to_python;
30+
31+ /// State of a persistent parameter returned by `Param.persistent_get_state()`
32+ #[ gen_stub_pyclass]
33+ #[ pyclass]
34+ pub struct PersistentParamState {
35+ is_stored : bool ,
36+ default_value : Py < PyAny > ,
37+ stored_value : Py < PyAny > ,
38+ }
39+
40+ #[ gen_stub_pymethods]
41+ #[ pymethods]
42+ impl PersistentParamState {
43+ /// True if a value is currently stored in persistent storage
44+ #[ getter]
45+ #[ gen_stub( override_return_type( type_repr = "bool" ) ) ]
46+ fn is_stored ( & self ) -> bool {
47+ self . is_stored
48+ }
49+
50+ /// The firmware's default value for this parameter
51+ #[ getter]
52+ #[ gen_stub( override_return_type( type_repr = "int | float" ) ) ]
53+ fn default_value ( & self , py : Python < ' _ > ) -> Py < PyAny > {
54+ self . default_value . clone_ref ( py)
55+ }
56+
57+ /// The value stored in persistent storage, or None if not stored
58+ #[ getter]
59+ #[ gen_stub( override_return_type( type_repr = "int | float | None" ) ) ]
60+ fn stored_value ( & self , py : Python < ' _ > ) -> Py < PyAny > {
61+ self . stored_value . clone_ref ( py)
62+ }
63+ }
2964
3065/// Access to the Crazyflie Param Subsystem
3166///
@@ -88,25 +123,8 @@ impl Param {
88123 fn get < ' py > ( & self , py : Python < ' py > , name : String ) -> PyResult < Bound < ' py , PyAny > > {
89124 let cf = self . cf . clone ( ) ;
90125 pyo3_async_runtimes:: tokio:: future_into_py ( py, async move {
91- let value: crazyflie_lib:: Value = cf. param . get ( & name) . await . map_err ( to_pyerr) ?;
92-
93- // Convert Rust Value to Python object
94- // We need to acquire the GIL to create Python objects
95- Python :: attach ( |py| {
96- Ok ( match value {
97- crazyflie_lib:: Value :: U8 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
98- crazyflie_lib:: Value :: U16 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
99- crazyflie_lib:: Value :: U32 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
100- crazyflie_lib:: Value :: U64 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
101- crazyflie_lib:: Value :: I8 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
102- crazyflie_lib:: Value :: I16 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
103- crazyflie_lib:: Value :: I32 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
104- crazyflie_lib:: Value :: I64 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
105- crazyflie_lib:: Value :: F16 ( v) => v. to_f32 ( ) . into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
106- crazyflie_lib:: Value :: F32 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
107- crazyflie_lib:: Value :: F64 ( v) => v. into_pyobject ( py) ?. into_any ( ) . unbind ( ) ,
108- } )
109- } )
126+ let value = cf. param . get ( & name) . await . map_err ( to_pyerr) ?;
127+ Python :: attach ( |py| value_to_python ( py, value) )
110128 } )
111129 }
112130
@@ -187,4 +205,99 @@ impl Param {
187205 Ok ( ( ) )
188206 } )
189207 }
208+
209+ /// Check if a parameter supports persistent storage
210+ ///
211+ /// Returns False for parameters that do not support persistence.
212+ /// Raises an error if the parameter does not exist.
213+ ///
214+ /// # Arguments
215+ /// * `name` - Parameter name in format "group.name"
216+ #[ gen_stub( override_return_type( type_repr = "collections.abc.Coroutine[typing.Any, typing.Any, bool]" ) ) ]
217+ fn is_persistent < ' py > ( & self , py : Python < ' py > , name : String ) -> PyResult < Bound < ' py , PyAny > > {
218+ let cf = self . cf . clone ( ) ;
219+ pyo3_async_runtimes:: tokio:: future_into_py ( py, async move {
220+ cf. param . is_persistent ( & name) . await . map_err ( to_pyerr)
221+ } )
222+ }
223+
224+ /// Get the firmware's default value for a parameter
225+ ///
226+ /// Raises an error if the parameter is read-only or does not exist.
227+ ///
228+ /// # Arguments
229+ /// * `name` - Parameter name in format "group.name"
230+ ///
231+ /// # Returns
232+ /// The default value (int or float depending on parameter type)
233+ #[ gen_stub( override_return_type( type_repr = "collections.abc.Coroutine[typing.Any, typing.Any, int | float]" ) ) ]
234+ fn get_default_value < ' py > ( & self , py : Python < ' py > , name : String ) -> PyResult < Bound < ' py , PyAny > > {
235+ let cf = self . cf . clone ( ) ;
236+ pyo3_async_runtimes:: tokio:: future_into_py ( py, async move {
237+ let value = cf. param . get_default_value ( & name) . await . map_err ( to_pyerr) ?;
238+ Python :: attach ( |py| value_to_python ( py, value) )
239+ } )
240+ }
241+
242+ /// Get the persistent storage state of a parameter
243+ ///
244+ /// Returns a PersistentParamState with:
245+ /// - `is_stored`: True if a value is currently in persistent storage
246+ /// - `default_value`: The firmware's default value
247+ /// - `stored_value`: The stored value, or None if not stored
248+ ///
249+ /// Raises an error if the parameter does not exist or is not persistent.
250+ ///
251+ /// # Arguments
252+ /// * `name` - Parameter name in format "group.name"
253+ #[ gen_stub( override_return_type( type_repr = "collections.abc.Coroutine[typing.Any, typing.Any, PersistentParamState]" ) ) ]
254+ fn persistent_get_state < ' py > ( & self , py : Python < ' py > , name : String ) -> PyResult < Bound < ' py , PyAny > > {
255+ let cf = self . cf . clone ( ) ;
256+ pyo3_async_runtimes:: tokio:: future_into_py ( py, async move {
257+ let state = cf. param . persistent_get_state ( & name) . await . map_err ( to_pyerr) ?;
258+ Python :: attach ( |py| {
259+ let stored = match state. stored_value {
260+ Some ( v) => value_to_python ( py, v) ?,
261+ None => py. None ( ) ,
262+ } ;
263+ Ok ( PersistentParamState {
264+ is_stored : state. is_stored ,
265+ default_value : value_to_python ( py, state. default_value ) ?,
266+ stored_value : stored,
267+ } )
268+ } )
269+ } )
270+ }
271+
272+ /// Store the current parameter value to persistent storage
273+ ///
274+ /// The parameter's current value (set with `set()`) will be saved so that
275+ /// it persists across reboots. Raises an error if the parameter does not
276+ /// exist or is not persistent.
277+ ///
278+ /// # Arguments
279+ /// * `name` - Parameter name in format "group.name"
280+ #[ gen_stub( override_return_type( type_repr = "collections.abc.Coroutine[typing.Any, typing.Any, None]" ) ) ]
281+ fn persistent_store < ' py > ( & self , py : Python < ' py > , name : String ) -> PyResult < Bound < ' py , PyAny > > {
282+ let cf = self . cf . clone ( ) ;
283+ pyo3_async_runtimes:: tokio:: future_into_py ( py, async move {
284+ cf. param . persistent_store ( & name) . await . map_err ( to_pyerr)
285+ } )
286+ }
287+
288+ /// Clear the stored value from persistent storage
289+ ///
290+ /// After clearing, the parameter will revert to the firmware default on
291+ /// the next reboot. Raises an error if the parameter does not exist or
292+ /// is not persistent.
293+ ///
294+ /// # Arguments
295+ /// * `name` - Parameter name in format "group.name"
296+ #[ gen_stub( override_return_type( type_repr = "collections.abc.Coroutine[typing.Any, typing.Any, None]" ) ) ]
297+ fn persistent_clear < ' py > ( & self , py : Python < ' py > , name : String ) -> PyResult < Bound < ' py , PyAny > > {
298+ let cf = self . cf . clone ( ) ;
299+ pyo3_async_runtimes:: tokio:: future_into_py ( py, async move {
300+ cf. param . persistent_clear ( & name) . await . map_err ( to_pyerr)
301+ } )
302+ }
190303}
0 commit comments