umsh_ulcp_runtime/
temperature.rs1use heapless::Vec;
4use umsh_ulcp::{Status, temperature};
5use umsh_ulcp_device::{MAX_TEMPERATURE_SENSORS, TEMPERATURE_NAMES_MAX};
6
7pub struct TemperatureInventory<S: Copy> {
11 sources: Vec<S, MAX_TEMPERATURE_SENSORS>,
12 names: Vec<u8, TEMPERATURE_NAMES_MAX>,
13}
14
15impl<S: Copy> Default for TemperatureInventory<S> {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl<S: Copy> TemperatureInventory<S> {
22 pub const fn new() -> Self {
23 Self {
24 sources: Vec::new(),
25 names: Vec::new(),
26 }
27 }
28
29 pub fn register(&mut self, source: S, name: &str) -> Result<usize, Status> {
31 let mut encoded = [0; temperature::NAME_MAX_BYTES + 1];
32 let len = temperature::encode_names(&[name], &mut encoded)
33 .map_err(|_| Status::INVALID_ARGUMENT)?;
34 if self.sources.is_full() || self.names.len() + len > TEMPERATURE_NAMES_MAX {
35 return Err(Status::NOMEM);
36 }
37 let index = self.sources.len();
38 let _ = self.sources.push(source);
40 self.names
41 .extend_from_slice(&encoded[..len])
42 .expect("checked name capacity");
43 Ok(index)
44 }
45
46 pub fn snapshot(&self) -> Vec<S, MAX_TEMPERATURE_SENSORS> {
49 self.sources.clone()
50 }
51
52 pub fn read_names(&self, out: &mut [u8]) -> Result<usize, Status> {
54 let len = self.names.len();
55 let dest = out.get_mut(..len).ok_or(Status::NOMEM)?;
56 dest.copy_from_slice(&self.names);
57 Ok(len)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 fn names<S: Copy>(inventory: &TemperatureInventory<S>) -> std::vec::Vec<u8> {
66 let mut out = [0; TEMPERATURE_NAMES_MAX];
67 let len = inventory.read_names(&mut out).unwrap();
68 out[..len].to_vec()
69 }
70
71 #[test]
72 fn temperature_inventory_owns_names_and_keeps_snapshot_indices() {
73 let mut inventory = TemperatureInventory::new();
74 assert!(inventory.snapshot().is_empty());
75 assert_eq!(inventory.read_names(&mut []), Ok(0));
76 let mut label = std::string::String::from("電池");
77 assert_eq!(inventory.register(10, &label), Ok(0));
78 label.clear();
79 let captured = inventory.snapshot();
80 assert_eq!(inventory.register(20, "電池"), Ok(1));
81 assert_eq!(&captured[..], &[10]);
82 assert_eq!(&inventory.snapshot()[..], &[10, 20]);
83 assert_eq!(
84 temperature::names(&names(&inventory))
85 .unwrap()
86 .collect::<std::vec::Vec<_>>(),
87 ["電池", "電池"]
88 );
89 let mut too_short = [0xaa; 1];
90 assert_eq!(inventory.read_names(&mut too_short), Err(Status::NOMEM));
91 assert_eq!(too_short, [0xaa]);
92 }
93
94 #[test]
95 fn temperature_inventory_registration_failures_are_atomic() {
96 let mut inventory = TemperatureInventory::new();
97 inventory.register(0, "MCU die").unwrap();
98 let initial = names(&inventory);
99 for label in ["", "a\0b", &"a".repeat(65)] {
100 assert_eq!(inventory.register(1, label), Err(Status::INVALID_ARGUMENT));
101 assert_eq!(names(&inventory), initial);
102 assert_eq!(&inventory.snapshot()[..], &[0]);
103 }
104 for index in 1..MAX_TEMPERATURE_SENSORS {
105 inventory.register(index, "a").unwrap();
106 }
107 let full = names(&inventory);
108 assert_eq!(inventory.register(99, "b"), Err(Status::NOMEM));
109 assert_eq!(names(&inventory), full);
110 assert_eq!(inventory.snapshot().len(), MAX_TEMPERATURE_SENSORS);
111
112 let mut inventory = TemperatureInventory::new();
113 for index in 0..4 {
114 inventory.register(index, &"a".repeat(64)).unwrap();
115 }
116 let before = names(&inventory);
117 assert_eq!(inventory.register(4, "abcdefghijkl"), Err(Status::NOMEM));
118 assert_eq!(inventory.snapshot().len(), 4);
119 assert_eq!(names(&inventory), before);
120 assert_eq!(inventory.register(4, "abcdefghijk"), Ok(4));
121 assert_eq!(names(&inventory).len(), TEMPERATURE_NAMES_MAX);
122 }
123}