1use crate::types::{
7 MAX_PARAM_NAME_LEN, Parameter, ParameterDescriptor, ParameterType, ParameterValue,
8 SetParameterResult,
9};
10use heapless::String;
11
12use crate::types::MAX_PARAMETERS;
14
15#[derive(Debug, Clone)]
17struct ParameterEntry {
18 param: Parameter,
20 descriptor: Option<ParameterDescriptor>,
22}
23
24pub struct ParameterServer {
48 entries: [Option<ParameterEntry>; MAX_PARAMETERS],
50 count: usize,
52 dirty: bool,
56}
57
58impl Default for ParameterServer {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl ParameterServer {
65 #[allow(clippy::large_stack_arrays)] pub const fn new() -> Self {
68 Self {
70 entries: [const { None }; MAX_PARAMETERS],
71 count: 0,
72 dirty: false,
73 }
74 }
75
76 pub fn take_dirty(&mut self) -> bool {
80 core::mem::take(&mut self.dirty)
81 }
82
83 pub fn len(&self) -> usize {
85 self.count
86 }
87
88 pub fn is_empty(&self) -> bool {
90 self.count == 0
91 }
92
93 pub fn is_full(&self) -> bool {
95 self.count >= MAX_PARAMETERS
96 }
97
98 fn find_index(&self, name: &str) -> Option<usize> {
100 self.entries.iter().position(|entry| {
101 entry
102 .as_ref()
103 .map(|e| e.param.name.as_str() == name)
104 .unwrap_or(false)
105 })
106 }
107
108 fn find_empty_slot(&self) -> Option<usize> {
110 self.entries.iter().position(|entry| entry.is_none())
111 }
112
113 pub fn declare(&mut self, name: &str, value: ParameterValue) -> bool {
118 self.declare_with_descriptor(name, value, None)
119 }
120
121 pub fn declare_with_descriptor(
125 &mut self,
126 name: &str,
127 value: ParameterValue,
128 descriptor: Option<ParameterDescriptor>,
129 ) -> bool {
130 if self.find_index(name).is_some() {
132 return false;
133 }
134
135 let slot = match self.find_empty_slot() {
137 Some(idx) => idx,
138 None => return false,
139 };
140
141 let param = match Parameter::new(name, value) {
143 Some(p) => p,
144 None => return false,
145 };
146
147 self.entries[slot] = Some(ParameterEntry { param, descriptor });
148 self.count += 1;
149 true
150 }
151
152 pub fn get(&self, name: &str) -> Option<&ParameterValue> {
154 self.find_index(name)
155 .and_then(|idx| self.entries[idx].as_ref())
156 .map(|entry| &entry.param.value)
157 }
158
159 pub fn get_parameter(&self, name: &str) -> Option<&Parameter> {
161 self.find_index(name)
162 .and_then(|idx| self.entries[idx].as_ref())
163 .map(|entry| &entry.param)
164 }
165
166 pub fn get_descriptor(&self, name: &str) -> Option<&ParameterDescriptor> {
168 self.find_index(name)
169 .and_then(|idx| self.entries[idx].as_ref())
170 .and_then(|entry| entry.descriptor.as_ref())
171 }
172
173 pub fn set(&mut self, name: &str, value: ParameterValue) -> SetParameterResult {
177 let idx = match self.find_index(name) {
178 Some(idx) => idx,
179 None => return SetParameterResult::NotFound,
180 };
181
182 let entry = match self.entries[idx].as_mut() {
183 Some(e) => e,
184 None => return SetParameterResult::NotFound,
185 };
186
187 if let Some(ref desc) = entry.descriptor {
189 if desc.read_only {
190 return SetParameterResult::ReadOnly;
191 }
192
193 if !desc.dynamic_typing && desc.param_type != value.param_type() {
195 return SetParameterResult::TypeMismatch;
196 }
197
198 if !desc.validate_range(&value) {
200 return SetParameterResult::OutOfRange;
201 }
202 }
203
204 entry.param.value = value;
205 self.dirty = true;
206 SetParameterResult::Success
207 }
208
209 pub fn unset(&mut self, name: &str) -> SetParameterResult {
214 let idx = match self.find_index(name) {
215 Some(idx) => idx,
216 None => return SetParameterResult::NotFound,
217 };
218
219 let entry = match self.entries[idx].as_mut() {
220 Some(e) => e,
221 None => return SetParameterResult::NotFound,
222 };
223
224 if let Some(ref desc) = entry.descriptor
226 && desc.read_only
227 {
228 return SetParameterResult::ReadOnly;
229 }
230
231 entry.param.value = ParameterValue::NotSet;
232 self.dirty = true;
233 SetParameterResult::Success
234 }
235
236 pub fn set_or_declare(&mut self, name: &str, value: ParameterValue) -> SetParameterResult {
240 if self.find_index(name).is_some() {
241 self.set(name, value)
242 } else if self.declare(name, value) {
243 SetParameterResult::Success
244 } else {
245 SetParameterResult::StorageFull
246 }
247 }
248
249 pub fn has(&self, name: &str) -> bool {
251 self.find_index(name).is_some()
252 }
253
254 pub fn remove(&mut self, name: &str) -> bool {
258 if let Some(idx) = self.find_index(name) {
259 self.entries[idx] = None;
260 self.count -= 1;
261 true
262 } else {
263 false
264 }
265 }
266
267 pub fn get_type(&self, name: &str) -> Option<ParameterType> {
269 self.get(name).map(|v| v.param_type())
270 }
271
272 pub fn iter(&self) -> impl Iterator<Item = &Parameter> {
274 self.entries
275 .iter()
276 .filter_map(|entry| entry.as_ref().map(|e| &e.param))
277 }
278
279 pub fn list_names(&self) -> impl Iterator<Item = &str> {
281 self.iter().map(|p| p.name.as_str())
282 }
283
284 pub fn list_with_prefix<'a>(&'a self, prefix: &'a str) -> impl Iterator<Item = &'a str> {
286 self.list_names()
287 .filter(move |name| name.starts_with(prefix))
288 }
289
290 pub fn get_bool(&self, name: &str) -> Option<bool> {
292 self.get(name).and_then(|v| v.as_bool())
293 }
294
295 pub fn get_integer(&self, name: &str) -> Option<i64> {
297 self.get(name).and_then(|v| v.as_integer())
298 }
299
300 pub fn get_double(&self, name: &str) -> Option<f64> {
302 self.get(name).and_then(|v| v.as_double())
303 }
304
305 pub fn get_string(&self, name: &str) -> Option<&str> {
307 self.get(name).and_then(|v| v.as_string())
308 }
309
310 pub fn set_bool(&mut self, name: &str, value: bool) -> SetParameterResult {
312 self.set(name, ParameterValue::Bool(value))
313 }
314
315 pub fn set_integer(&mut self, name: &str, value: i64) -> SetParameterResult {
317 self.set(name, ParameterValue::Integer(value))
318 }
319
320 pub fn set_double(&mut self, name: &str, value: f64) -> SetParameterResult {
322 self.set(name, ParameterValue::Double(value))
323 }
324
325 pub fn set_string(&mut self, name: &str, value: &str) -> SetParameterResult {
327 match ParameterValue::from_string(value) {
328 Some(v) => self.set(name, v),
329 None => SetParameterResult::StorageFull, }
331 }
332
333 pub fn declare_parameter(
342 &mut self,
343 descriptor: ParameterDescriptor,
344 initial_value: Option<&ParameterValue>,
345 ) -> Result<(), SetParameterResult> {
346 let name = descriptor.name.as_str();
347
348 if self.find_index(name).is_some() {
350 return Err(SetParameterResult::TypeMismatch); }
352
353 let slot = match self.find_empty_slot() {
355 Some(idx) => idx,
356 None => return Err(SetParameterResult::StorageFull),
357 };
358
359 let param_value = initial_value.cloned().unwrap_or_default();
360
361 let param = Parameter::new(name, param_value).ok_or(SetParameterResult::StorageFull)?; self.entries[slot] = Some(ParameterEntry {
364 param,
365 descriptor: Some(descriptor),
366 });
367 self.count += 1;
368 Ok(())
369 }
370
371 pub fn get_parameter_value(&self, name: &str) -> Option<ParameterValue> {
376 self.get(name).cloned()
377 }
378
379 pub fn set_parameter_value(&mut self, name: &str, value: ParameterValue) -> SetParameterResult {
384 self.set(name, value)
385 }
386}
387
388pub struct LegacyParameterBuilder<'a> {
390 server: &'a mut ParameterServer,
391 name: String<MAX_PARAM_NAME_LEN>,
392 value: ParameterValue,
393 descriptor: Option<ParameterDescriptor>,
394}
395
396impl<'a> LegacyParameterBuilder<'a> {
397 pub fn new(server: &'a mut ParameterServer, name: &str, value: ParameterValue) -> Option<Self> {
399 let mut n = String::new();
400 n.push_str(name).ok()?;
401 Some(Self {
402 server,
403 name: n,
404 value,
405 descriptor: None,
406 })
407 }
408
409 pub fn description(mut self, desc: &str) -> Self {
411 let param_type = self.value.param_type();
412 if self.descriptor.is_none() {
413 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
414 }
415 if let Some(ref mut d) = self.descriptor {
416 d.description.clear();
417 let _ = d.description.push_str(desc);
418 }
419 self
420 }
421
422 pub fn read_only(mut self) -> Self {
424 let param_type = self.value.param_type();
425 if self.descriptor.is_none() {
426 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
427 }
428 if let Some(ref mut d) = self.descriptor {
429 d.read_only = true;
430 }
431 self
432 }
433
434 pub fn integer_range(mut self, min: i64, max: i64, step: i64) -> Self {
436 let param_type = self.value.param_type();
437 if self.descriptor.is_none() {
438 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
439 }
440 if let Some(ref mut d) = self.descriptor {
441 d.range = crate::types::ParameterRange::Integer(crate::types::IntegerRange::new(
442 min, max, step,
443 ));
444 }
445 self
446 }
447
448 pub fn float_range(mut self, min: f64, max: f64, step: f64) -> Self {
450 let param_type = self.value.param_type();
451 if self.descriptor.is_none() {
452 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
453 }
454 if let Some(ref mut d) = self.descriptor {
455 d.range = crate::types::ParameterRange::FloatingPoint(
456 crate::types::FloatingPointRange::new(min, max, step),
457 );
458 }
459 self
460 }
461
462 pub fn declare(self) -> bool {
464 self.server
465 .declare_with_descriptor(self.name.as_str(), self.value, self.descriptor)
466 }
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472
473 #[test]
474 fn test_new_server() {
475 let server = ParameterServer::new();
476 assert_eq!(server.len(), 0);
477 assert!(server.is_empty());
478 }
479
480 #[test]
481 #[allow(clippy::approx_constant)]
482 fn test_declare_and_get() {
483 let mut server = ParameterServer::new();
484
485 assert!(server.declare("my_bool", ParameterValue::Bool(true)));
486 assert!(server.declare("my_int", ParameterValue::Integer(42)));
487 assert!(server.declare("my_double", ParameterValue::Double(3.14)));
488
489 assert_eq!(server.len(), 3);
490 assert!(!server.is_empty());
491
492 assert_eq!(server.get_bool("my_bool"), Some(true));
493 assert_eq!(server.get_integer("my_int"), Some(42));
494 assert_eq!(server.get_double("my_double"), Some(3.14));
495 }
496
497 #[test]
498 fn test_dirty_tracks_value_changes_not_declarations() {
499 let mut server = ParameterServer::new();
502 assert!(server.declare("count", ParameterValue::Integer(0)));
503 assert!(!server.take_dirty(), "declare must not dirty");
504
505 assert_eq!(server.set_integer("count", 10), SetParameterResult::Success);
506 assert!(server.take_dirty(), "set must dirty");
507 assert!(!server.take_dirty(), "take_dirty clears");
508
509 assert_eq!(
511 server.set("missing", ParameterValue::Integer(1)),
512 SetParameterResult::NotFound
513 );
514 assert!(!server.take_dirty(), "failed set must not dirty");
515
516 assert_eq!(server.unset("count"), SetParameterResult::Success);
517 assert!(server.take_dirty(), "unset must dirty");
518 }
519
520 #[test]
521 fn test_set_parameter() {
522 let mut server = ParameterServer::new();
523 server.declare("count", ParameterValue::Integer(0));
524
525 assert_eq!(server.set_integer("count", 10), SetParameterResult::Success);
526 assert_eq!(server.get_integer("count"), Some(10));
527 }
528
529 #[test]
530 fn test_set_nonexistent() {
531 let mut server = ParameterServer::new();
532 assert_eq!(
533 server.set("nonexistent", ParameterValue::Integer(1)),
534 SetParameterResult::NotFound
535 );
536 }
537
538 #[test]
539 fn test_read_only_parameter() {
540 let mut server = ParameterServer::new();
541
542 let desc = ParameterDescriptor::new("version", ParameterType::String)
543 .unwrap()
544 .with_read_only(true);
545
546 server.declare_with_descriptor(
547 "version",
548 ParameterValue::from_string("1.0.0").unwrap(),
549 Some(desc),
550 );
551
552 assert_eq!(
553 server.set_string("version", "2.0.0"),
554 SetParameterResult::ReadOnly
555 );
556 assert_eq!(server.get_string("version"), Some("1.0.0"));
557 }
558
559 #[test]
560 fn test_range_constraints() {
561 let mut server = ParameterServer::new();
562
563 let desc = ParameterDescriptor::new("speed", ParameterType::Double)
564 .unwrap()
565 .with_float_range(0.0, 10.0, 0.0);
566
567 server.declare_with_descriptor("speed", ParameterValue::Double(5.0), Some(desc));
568
569 assert_eq!(server.set_double("speed", 8.0), SetParameterResult::Success);
571
572 assert_eq!(
574 server.set_double("speed", 15.0),
575 SetParameterResult::OutOfRange
576 );
577 assert_eq!(server.get_double("speed"), Some(8.0)); }
579
580 #[test]
581 fn test_remove_parameter() {
582 let mut server = ParameterServer::new();
583 server.declare("temp", ParameterValue::Double(25.0));
584
585 assert!(server.has("temp"));
586 assert!(server.remove("temp"));
587 assert!(!server.has("temp"));
588 assert!(!server.remove("temp")); }
590
591 #[test]
592 fn test_list_parameters() {
593 let mut server = ParameterServer::new();
594 server.declare("robot.speed", ParameterValue::Double(1.0));
595 server.declare("robot.name", ParameterValue::from_string("bot1").unwrap());
596 server.declare("sensor.range", ParameterValue::Double(10.0));
597
598 let robot_params: heapless::Vec<&str, 8> = server.list_with_prefix("robot.").collect();
599 assert_eq!(robot_params.len(), 2);
600 }
601
602 #[test]
603 fn test_set_or_declare() {
604 let mut server = ParameterServer::new();
605
606 assert_eq!(
608 server.set_or_declare("new_param", ParameterValue::Integer(1)),
609 SetParameterResult::Success
610 );
611 assert_eq!(server.get_integer("new_param"), Some(1));
612
613 assert_eq!(
615 server.set_or_declare("new_param", ParameterValue::Integer(2)),
616 SetParameterResult::Success
617 );
618 assert_eq!(server.get_integer("new_param"), Some(2));
619 }
620
621 #[test]
622 fn test_duplicate_declare() {
623 let mut server = ParameterServer::new();
624 assert!(server.declare("param", ParameterValue::Integer(1)));
625 assert!(!server.declare("param", ParameterValue::Integer(2))); assert_eq!(server.get_integer("param"), Some(1)); }
628}
629
630#[cfg(test)]
635mod ghost_checks {
636 use super::*;
637 use nros_ghost_types::ParamServerGhost;
638
639 fn ghost_from_server(s: &ParameterServer) -> ParamServerGhost {
642 ParamServerGhost {
643 count: s.count,
644 max: MAX_PARAMETERS,
645 }
646 }
647
648 #[test]
649 fn ghost_new_count() {
650 let server = ParameterServer::new();
651 let ghost = ghost_from_server(&server);
652 assert_eq!(ghost.count, 0);
653 assert_eq!(ghost.max, 32);
654 }
655
656 #[test]
657 fn ghost_declare_increments() {
658 let mut server = ParameterServer::new();
659 let before = ghost_from_server(&server).count;
660 assert!(server.declare("test", ParameterValue::Integer(1)));
661 let after = ghost_from_server(&server).count;
662 assert_eq!(after, before + 1);
663 }
664
665 #[test]
666 fn ghost_remove_decrements() {
667 let mut server = ParameterServer::new();
668 server.declare("test", ParameterValue::Integer(1));
669 let before = ghost_from_server(&server).count;
670 assert!(server.remove("test"));
671 let after = ghost_from_server(&server).count;
672 assert_eq!(after, before - 1);
673 }
674
675 #[test]
676 fn ghost_count_bounded() {
677 let mut server = ParameterServer::new();
678 for i in 0..MAX_PARAMETERS {
679 let mut name = heapless::String::<64>::new();
680 let _ = core::fmt::write(&mut name, format_args!("p{}", i));
681 server.declare(name.as_str(), ParameterValue::Integer(i as i64));
682 }
683 let ghost = ghost_from_server(&server);
684 assert!(ghost.count <= ghost.max);
685 assert_eq!(ghost.count, MAX_PARAMETERS);
686 assert!(!server.declare("overflow", ParameterValue::Integer(0)));
688 let ghost2 = ghost_from_server(&server);
689 assert!(ghost2.count <= ghost2.max);
690 }
691}
692
693#[cfg(kani)]
698mod verification {
699 use super::*;
700
701 #[kani::proof]
702 fn server_new_is_empty() {
703 let server = ParameterServer::new();
704 assert!(server.is_empty());
705 assert_eq!(server.len(), 0);
706 assert!(!server.is_full());
707 }
708
709 #[kani::proof]
710 fn server_declare_get_roundtrip_integer() {
711 let mut server = ParameterServer::new();
712 let val: i64 = kani::any();
713 assert!(server.declare("test", ParameterValue::Integer(val)));
714 assert_eq!(server.get_integer("test"), Some(val));
715 assert!(server.has("test"));
716 assert_eq!(server.len(), 1);
717 }
718
719 #[kani::proof]
720 fn server_declare_get_roundtrip_bool() {
721 let mut server = ParameterServer::new();
722 let val: bool = kani::any();
723 assert!(server.declare("test", ParameterValue::Bool(val)));
724 assert_eq!(server.get_bool("test"), Some(val));
725 }
726
727 #[kani::proof]
728 fn server_set_requires_declare() {
729 let mut server = ParameterServer::new();
730 let val: i64 = kani::any();
731 let result = server.set("test", ParameterValue::Integer(val));
733 assert_eq!(result, SetParameterResult::NotFound);
734 }
735
736 #[kani::proof]
737 fn server_duplicate_declare_fails() {
738 let mut server = ParameterServer::new();
739 assert!(server.declare("test", ParameterValue::Integer(1)));
740 assert!(!server.declare("test", ParameterValue::Integer(2)));
741 assert_eq!(server.get_integer("test"), Some(1));
743 }
744
745 #[kani::proof]
746 fn server_remove_clears() {
747 let mut server = ParameterServer::new();
748 assert!(server.declare("test", ParameterValue::Integer(42)));
749 assert!(server.has("test"));
750 assert!(server.remove("test"));
751 assert!(!server.has("test"));
752 assert_eq!(server.len(), 0);
753 }
754
755 #[kani::proof]
756 fn server_get_nonexistent_returns_none() {
757 let server = ParameterServer::new();
758 assert!(server.get("nonexistent").is_none());
759 assert!(server.get_bool("nonexistent").is_none());
760 assert!(server.get_integer("nonexistent").is_none());
761 assert!(server.get_double("nonexistent").is_none());
762 assert!(server.get_string("nonexistent").is_none());
763 }
764}