Skip to main content

gondola_core/monitoring/
pa_moni_data.rs

1// This file is part of gaps-online-software and published 
2// under the GPLv3 license
3
4use crate::prelude::*;
5
6#[cfg(feature="tofcontrol")]
7use tof_control::helper::pa_type::{
8  PATemp,
9  PAReadBias
10};
11
12/// Preamp temperature and bias data
13#[derive(Debug, Copy, Clone, PartialEq)]
14#[cfg_attr(feature="pybindings", pyclass)] 
15pub struct PAMoniData {
16  pub board_id   : u8,
17  pub temps      : [f32;16],
18  pub biases     : [f32;16],
19  pub timestamp  : u64,
20}
21
22impl PAMoniData {
23
24  pub fn new() -> Self {
25    Self {
26      board_id  : 0,
27      temps     : [f32::MAX;16],
28      biases    : [f32::MAX;16],
29      timestamp : 0,
30    }
31  }
32
33  #[cfg(feature = "tofcontrol")]
34  pub fn add_temps(&mut self, pt : &PATemp ) {
35    self.temps = pt.pa_temps;
36  }
37
38  #[cfg(feature = "tofcontrol")]
39  pub fn add_biases(&mut self, pb : &PAReadBias) {
40    self.biases = pb.read_biases;
41  }
42
43  /// Get the paddle temps and resolve the powerboard
44  /// channel to the paddle id 
45  pub fn get_pid_temps(&mut self, pid_map_a : &RbChPidMapping, pid_map_b : &RbChPidMapping ) -> HashMap<u8, (f32, f32)> {
46    let mut pid_temps = HashMap::<u8, (f32,f32)>::new();
47    // the pid_map_a/b are maps rbid -> channel -> pid 
48    // each will contain the board_id 
49    let channels_a = pid_map_a.get(&(self.board_id as u8)).expect("There is no RB {self.board_id} in te provided mapping!");
50    let channels_b = pid_map_b.get(&(self.board_id as u8)).expect("There is no board id {self.board_id} in the provided mapping!");
51    for k in 0..16u8 {
52      if channels_a.contains_key(&(k+1) ) {
53        let pid = channels_a.get(&(k+1)).unwrap();
54        if !pid_temps.contains_key(&pid) {
55          pid_temps.insert(*pid, (-273.0, -273.0));
56        }
57        pid_temps.get_mut(&pid).unwrap().0 = self.temps[k as usize];
58      }
59      if channels_b.contains_key(&(k+1)) {
60        let pid = channels_b.get(&(k+1)).unwrap();
61        if !pid_temps.contains_key(&pid) {
62          pid_temps.insert(*pid, (-273.0, -273.0));
63        }
64        pid_temps.get_mut(&pid).unwrap().1 = self.temps[k as usize];
65      }
66    }
67    pid_temps
68  }
69}
70
71#[cfg(feature="pybindings")]
72#[pymethods]
73impl PAMoniData {
74  
75  /// The temperature for the 16 preamp channels 
76  #[getter]
77  fn get_temps(&self) -> [f32;16] {
78    self.temps
79  }
80
81  /// Pramp bias voltages (mV) for the 16 channels
82  #[getter]
83  fn get_biases(&self) -> [f32;16] {
84    self.biases
85  }
86  
87  /// Get the paddle temps and resolve the powerboard
88  /// channel to the paddle id
89  ///
90  /// ISSUES: This needs to be refactored somehow so that
91  ///         it takes references instead of copies...
92  #[pyo3(name="get_pid_temps")]
93  fn get_pid_temps_py(&mut self, pid_map_a : RbChPidMapping, pid_map_b : RbChPidMapping) -> HashMap<u8, (f32, f32)> {
94    self.get_pid_temps(&pid_map_a, &pid_map_b) 
95  }
96}
97
98impl Default for PAMoniData {
99  fn default() -> Self {
100    Self::new()
101  }
102}
103
104impl fmt::Display for PAMoniData {
105  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106    write!(f, "<PAMoniData:
107  Board ID : {}
108  **16 Temp values**
109  T1   : {:.2} [\u{00B0}C]
110  T2   : {:.2} [\u{00B0}C]
111  T3   : {:.2} [\u{00B0}C]
112  T4   : {:.2} [\u{00B0}C]
113  T5   : {:.2} [\u{00B0}C]
114  T6   : {:.2} [\u{00B0}C]
115  T7   : {:.2} [\u{00B0}C]
116  T8   : {:.2} [\u{00B0}C]
117  T9   : {:.2} [\u{00B0}C]
118  T10  : {:.2} [\u{00B0}C]
119  T11  : {:.2} [\u{00B0}C]
120  T12  : {:.2} [\u{00B0}C]
121  T13  : {:.2} [\u{00B0}C]
122  T14  : {:.2} [\u{00B0}C]
123  T15  : {:.2} [\u{00B0}C]
124  T16  : {:.2} [\u{00B0}C]
125  **16 Bias voltages**
126  V1   : {:.3} [V]
127  V2   : {:.3} [V]
128  V3   : {:.3} [V]
129  V4   : {:.3} [V]
130  V5   : {:.3} [V]
131  V6   : {:.3} [V]
132  V7   : {:.3} [V]
133  V8   : {:.3} [V]
134  V9   : {:.3} [V]
135  V10  : {:.3} [V]
136  V11  : {:.3} [V]
137  V12  : {:.3} [V]
138  V13  : {:.3} [V]
139  V14  : {:.3} [V]
140  V15  : {:.3} [V]
141  V16  : {:.3} [V]>",
142  self.board_id,
143  if self.temps[0]  != f32::MAX {self.temps[0 ].to_string()} else {String::from("f32::MAX (ERR)")},
144  if self.temps[1]  != f32::MAX {self.temps[1 ].to_string()} else {String::from("f32::MAX (ERR)")},
145  if self.temps[2]  != f32::MAX {self.temps[2 ].to_string()} else {String::from("f32::MAX (ERR)")},
146  if self.temps[3]  != f32::MAX {self.temps[3 ].to_string()} else {String::from("f32::MAX (ERR)")},
147  if self.temps[4]  != f32::MAX {self.temps[4 ].to_string()} else {String::from("f32::MAX (ERR)")},
148  if self.temps[5]  != f32::MAX {self.temps[5 ].to_string()} else {String::from("f32::MAX (ERR)")},
149  if self.temps[6]  != f32::MAX {self.temps[6 ].to_string()} else {String::from("f32::MAX (ERR)")},
150  if self.temps[7]  != f32::MAX {self.temps[7 ].to_string()} else {String::from("f32::MAX (ERR)")},
151  if self.temps[8]  != f32::MAX {self.temps[8 ].to_string()} else {String::from("f32::MAX (ERR)")},
152  if self.temps[9]  != f32::MAX {self.temps[9 ].to_string()} else {String::from("f32::MAX (ERR)")},
153  if self.temps[10] != f32::MAX {self.temps[10].to_string()} else {String::from("f32::MAX (ERR)")},
154  if self.temps[11] != f32::MAX {self.temps[11].to_string()} else {String::from("f32::MAX (ERR)")},
155  if self.temps[12] != f32::MAX {self.temps[12].to_string()} else {String::from("f32::MAX (ERR)")},
156  if self.temps[13] != f32::MAX {self.temps[13].to_string()} else {String::from("f32::MAX (ERR)")},
157  if self.temps[14] != f32::MAX {self.temps[14].to_string()} else {String::from("f32::MAX (ERR)")},
158  if self.temps[15] != f32::MAX {self.temps[15].to_string()} else {String::from("f32::MAX (ERR)")},
159  self.biases[0],
160  self.biases[1],
161  self.biases[2],
162  self.biases[3],
163  self.biases[4],
164  self.biases[5],
165  self.biases[6],
166  self.biases[7],
167  self.biases[8],
168  self.biases[9],
169  self.biases[10],
170  self.biases[11],
171  self.biases[12],
172  self.biases[13],
173  self.biases[14],
174  self.biases[15])
175  }
176}
177
178impl TofPackable for PAMoniData {
179  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::PAMoniData;
180}
181
182impl Serialization for PAMoniData {
183  
184  const HEAD : u16 = 0xAAAA;
185  const TAIL : u16 = 0x5555;
186  /// The data size when serialized to a bytestream
187  /// This needs to be updated when we change the 
188  /// packet layout, e.g. add new members.
189  /// HEAD + TAIL + sum(sizeof(m) for m in _all_members_))
190  const SIZE : usize  = 4 + 1 + (4*16*2);
191  
192  fn to_bytestream(&self) -> Vec<u8> {
193    let mut stream = Vec::<u8>::with_capacity(Self::SIZE);
194    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
195    stream.extend_from_slice(&self.board_id.to_le_bytes()); 
196    for k in 0..16 {
197      stream.extend_from_slice(&self.temps[k].to_le_bytes());
198    }
199    for k in 0..16 {
200      stream.extend_from_slice(&self.biases[k].to_le_bytes());
201    }
202    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
203    stream
204  }
205  
206  fn from_bytestream(stream : &Vec<u8>, pos : &mut usize)
207    -> Result<Self, SerializationError> {
208    let mut moni_data      = Self::new();
209    Self::verify_fixed(stream, pos)?;
210    moni_data.board_id = parse_u8(stream, pos);
211    for k in 0..16 {
212      moni_data.temps[k] = parse_f32(stream, pos);
213    }
214    for k in 0..16 {
215      moni_data.biases[k] = parse_f32(stream, pos);
216    }
217    *pos += 2;
218    Ok(moni_data)
219  }
220}
221
222impl MoniData for PAMoniData {
223  
224  fn get_board_id(&self) -> u8 {
225    return self.board_id;
226  }
227
228  fn get_timestamp(&self) -> u64 {
229    self.timestamp 
230  }
231
232  fn set_timestamp(&mut self, ts: u64) {
233    self.timestamp = ts;
234  }
235
236  fn keys() -> Vec<&'static str> {
237    vec!["board_id",
238         "temps1"  , "temps2"  , "temps3"  , "temps4"  ,
239         "temps5"  , "temps6"  , "temps7"  , "temps8"  , 
240         "temps9"  , "temps10" , "temps11" , "temps12" ,
241         "temps13" , "temps14" , "temps15" , "temps16" ,
242         "biases1" , "biases2" , "biases3" , "biases4" , 
243         "biases5" , "biases6" , "biases7" , "biases8" ,
244         "biases9" , "biases10", "biases11", "biases12",
245         "biases13", "biases14", "biases15", "biases16",
246         "timestamp"]
247  }
248
249  fn get(&self, varname : &str) -> Option<f32> {
250    match varname {
251      "board_id" =>  Some(self.board_id as f32),
252      "temps1"   =>  Some(self.temps[0]  ),
253      "temps2"   =>  Some(self.temps[1]  ),
254      "temps3"   =>  Some(self.temps[2]  ),
255      "temps4"   =>  Some(self.temps[3]  ),
256      "temps5"   =>  Some(self.temps[4]  ),
257      "temps6"   =>  Some(self.temps[5]  ),
258      "temps7"   =>  Some(self.temps[6]  ),
259      "temps8"   =>  Some(self.temps[7]  ),
260      "temps9"   =>  Some(self.temps[8]  ),
261      "temps10"  =>  Some(self.temps[9]  ),
262      "temps11"  =>  Some(self.temps[10] ),
263      "temps12"  =>  Some(self.temps[11] ),
264      "temps13"  =>  Some(self.temps[12] ),
265      "temps14"  =>  Some(self.temps[13] ),
266      "temps15"  =>  Some(self.temps[14] ),
267      "temps16"  =>  Some(self.temps[15] ),
268      "biases1"  =>  Some(self.biases[0] ),
269      "biases2"  =>  Some(self.biases[1] ),
270      "biases3"  =>  Some(self.biases[2] ),
271      "biases4"  =>  Some(self.biases[3] ),
272      "biases5"  =>  Some(self.biases[4] ),
273      "biases6"  =>  Some(self.biases[5] ),
274      "biases7"  =>  Some(self.biases[6] ),
275      "biases8"  =>  Some(self.biases[7] ),
276      "biases9"  =>  Some(self.biases[8] ),
277      "biases10" =>  Some(self.biases[9] ),
278      "biases11" =>  Some(self.biases[10]),
279      "biases12" =>  Some(self.biases[11]),
280      "biases13" =>  Some(self.biases[12]),
281      "biases14" =>  Some(self.biases[13]),
282      "biases15" =>  Some(self.biases[14]),
283      "biases16" =>  Some(self.biases[15]),
284      "timestamp" => Some(self.timestamp as f32),
285      _          =>  None
286    }
287  }  
288}
289
290
291#[cfg(feature = "random")]
292impl FromRandom for PAMoniData {
293    
294  fn from_random() -> Self {
295    let mut moni = Self::new();
296    let mut rng  = rand::rng();
297    moni.board_id     = rng.random::<u8>(); 
298    for k in 0..16 {
299      moni.temps[k]   = rng.random::<f32>(); 
300    }
301    for k in 0..16 {
302      moni.biases[k]  = rng.random::<f32>(); 
303    }
304    moni
305  }
306}
307
308//----------------------------------------
309
310#[cfg(feature="pybindings")]
311pythonize_packable_no_new!(PAMoniData);
312
313#[cfg(feature="pybindings")]
314pythonize_monidata!(PAMoniData);
315
316moniseries!(PAMoniDataSeries, PAMoniData);
317
318//----------------------------------------
319
320#[test]
321#[cfg(feature = "random")]
322fn pack_pamonidata() {
323  for _ in 0..100 {
324    let data = PAMoniData::from_random();
325    let test : PAMoniData = data.pack().unpack().unwrap();
326    assert_eq!(data, test);
327  }
328}
329