gondola_core/monitoring/heartbeats/
master_trigger_hb.rs

1// This file is part of gaps-online-software and published 
2// under the GPLv3 license
3
4use crate::prelude::*;
5use colored::Colorize;
6
7#[derive(Debug, Copy, Clone, PartialEq)]
8#[cfg_attr(feature="pybindings", pyclass)]
9pub struct MasterTriggerHB {
10  pub version             : ProtocolVersion, 
11  pub total_elapsed       : u64, //aka met (mission elapsed time)
12  pub n_events            : u64,
13  pub evq_num_events_last : u64,
14  pub evq_num_events_avg  : u64,
15  pub n_ev_unsent         : u64,
16  pub n_ev_missed         : u64,
17  pub trate               : u64,
18  pub lost_trate          : u64,
19  // these will be available for ProtocolVersion::V1
20  pub prescale_track      : f32,
21  pub prescale_gaps       : f32,
22  // will not be serialized 
23  pub timestamp           : u64,
24}
25
26impl MasterTriggerHB {
27  pub fn new() -> Self {
28    Self {
29      version             : ProtocolVersion::Unknown,
30      total_elapsed       : 0,
31      n_events            : 0,
32      evq_num_events_last : 0,
33      evq_num_events_avg  : 0,
34      n_ev_unsent         : 0,
35      n_ev_missed         : 0,
36      trate               : 0,
37      lost_trate          : 0,
38      // available for protocol version V1 and larger
39      prescale_track      : 0.0,
40      prescale_gaps       : 0.0,
41      timestamp           : 0, 
42    }
43  }
44
45  pub fn get_sent_packet_rate(&self) -> f64 {
46    if self.total_elapsed > 0 {
47      return self.n_events as f64 / self.total_elapsed as f64;
48    }
49    0.0
50  }
51
52  // get the prescale for the secondary trigger
53  pub fn get_prescale_track(&self) -> f64 {
54    if self.version == ProtocolVersion::Unknown {
55      error!("Prescale not available for protocol version < V1!");
56      return 0.0;
57    }
58    return self.prescale_track as f64
59  }
60  
61  // get the prescale for the secondary trigger
62  pub fn get_prescale_gaps(&self) -> f64 {
63    if self.version == ProtocolVersion::Unknown {
64      error!("Prescale not available for protocol version < V1!");
65      return 0.0;
66    }
67    return self.prescale_gaps as f64
68  }
69
70
71  pub fn pretty_print(&self) -> String {
72    let mut repr = format!("<MasterTriggerHBs (version : {})", self.version);
73    repr += &(format!("\n \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} MTB HEARTBEAT \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} "));
74    repr += &(format!("\n MET (Mission Elapsed Time)  : {:.1} sec", self.total_elapsed));
75    repr += &(format!("\n Num. recorded Events        : {}", self.n_events));
76    repr += &(format!("\n Last MTB EVQ size           : {}", self.evq_num_events_last));
77    repr += &(format!("\n Avg. MTB EVQ size (per 30s ): {:.2}", self.evq_num_events_avg));
78    repr += &(format!("\n trigger rate, recorded:     : {:.2} Hz", self.n_events as f64 / self.total_elapsed as f64));
79    repr += &(format!("\n trigger rate, from register : {:.2} Hz", self.trate));
80    repr += &(format!("\n lost trg rate, from register: {:.2} Hz", self.lost_trate));
81    if self.n_ev_unsent > 0 {
82        repr += &(format!("\n Num. sent errors        : {}", self.n_ev_unsent).bold());
83    }
84    if self.n_ev_missed > 0 {
85        repr += &(format!("\n Num. missed events      : {}", self.n_ev_missed).bold());
86    }
87    if self.version != ProtocolVersion::Unknown {
88        repr += &(format!("\n Prescale, prim. ('GAPS') trg : {:.4}", self.prescale_gaps));
89        repr += &(format!("\n Prescale  sec. ('Track') trg : {:.4}", self.prescale_track));
90    }
91    repr += &(format!("\n \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} END HEARTBEAT \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} \u{1FA90} "));
92    repr
93  }
94}
95  
96impl Default for MasterTriggerHB {
97  fn default () -> Self {
98    Self::new()
99  }
100}
101
102impl TofPackable for MasterTriggerHB {
103  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::MasterTriggerHB;
104}
105
106impl Serialization for MasterTriggerHB {
107  const HEAD : u16 = 0xAAAA;
108  const TAIL : u16 = 0x5555;
109  const SIZE : usize = 68;
110
111  fn from_bytestream(stream    :&Vec<u8>,
112                     pos       :&mut usize)
113  -> Result<Self, SerializationError>{
114    Self::verify_fixed(stream, pos)?;
115    let mut hb = MasterTriggerHB::new();
116    hb.total_elapsed          = parse_u64(stream, pos);
117    hb.n_events               = parse_u64(stream, pos);
118    hb.evq_num_events_last    = parse_u64(stream, pos);
119    hb.evq_num_events_avg     = parse_u64(stream, pos);
120    hb.n_ev_unsent            = parse_u64(stream, pos);
121    hb.n_ev_missed            = parse_u64(stream, pos);
122    // we use only 48bit here to carve out space for the 
123    // protocol version and the prescales
124    // this is a hack, but since we are expeting rates
125    // < 65kHz, we should be fine with only 16bit for the 
126    // rate and can use the rest for the prescale
127    //let version_ps_rate       = parse_u64(stream, pos);
128    //let version               = version_ps_rate & 0xff00000000000000;
129    //let prescale_track        = version_ps_rate & 0x00ffffffff000000;
130    //let trate                 = version_ps_rate & 0x0000000000ffffff;
131    //hb.version                = ProtocolVersion::from((version >> 56) as u8); 
132    hb.version                = ProtocolVersion::from(parse_u8(stream, pos) as u8);
133    *pos += 1;
134    hb.trate                  = parse_u16(stream, pos) as u64;
135    hb.prescale_track         = parse_f32(stream, pos);
136    *pos += 2;
137    hb.lost_trate             = parse_u16(stream, pos) as u64;
138    hb.prescale_gaps          = parse_f32(stream, pos);
139    if hb.version == ProtocolVersion::Unknown {
140      hb.prescale_gaps  = 0.0;
141      hb.prescale_track = 0.0
142    }
143    *pos += 2;
144    Ok(hb)
145  }
146
147  fn to_bytestream(&self) -> Vec<u8> {
148    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
149    bs.extend_from_slice(&Self::HEAD.to_le_bytes());
150    bs.extend_from_slice(&self.total_elapsed.to_le_bytes());
151    bs.extend_from_slice(&self.n_events.to_le_bytes());
152    bs.extend_from_slice(&self.evq_num_events_last.to_le_bytes());
153    bs.extend_from_slice(&self.evq_num_events_avg.to_le_bytes());
154    bs.extend_from_slice(&self.n_ev_unsent.to_le_bytes());
155    bs.extend_from_slice(&self.n_ev_missed.to_le_bytes());
156    bs.push(self.version as u8);
157    bs.push(0u8);
158    let short_trate = (self.trate & 0x0000000000ffffff) as u16;
159    bs.extend_from_slice(&short_trate.to_le_bytes());
160    bs.extend_from_slice(&self.prescale_track.to_le_bytes());
161    let short_lrate = (self.lost_trate & 0x0000000000ffffff) as u16;
162    // FIXME - not needed, just filler
163    bs.extend_from_slice(&short_lrate.to_le_bytes());
164    bs.extend_from_slice(&short_lrate.to_le_bytes());
165    bs.extend_from_slice(&self.prescale_gaps.to_le_bytes());
166    //let rate_n_prescale_track =
167    //    (((self.version as u8) as u64) << 56)
168    //  | (self.prescale_track as u64) << 24 
169    //  | (self.trate & 0x0000000000ffffff);
170    //bs.extend_from_slice(&rate_n_prescale_track.to_le_bytes());
171    //let rate_n_prescale_gaps = 
172    // ((self.prescale_gaps as f64)   << 24)
173    // | (self.lost_trate & 0x0000000000ffffff);
174    //bs.extend_from_slice(&rate_n_prescale_gaps.to_le_bytes());
175    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
176    bs
177  }
178}
179
180#[cfg(feature = "random")]
181impl FromRandom for MasterTriggerHB {
182  fn from_random() -> Self {
183    let mut hb = Self::new();
184    let mut rng            = rand::rng();
185    hb.total_elapsed       = rng.random::<u64>();
186    hb.n_events            = rng.random::<u64>();
187    hb.evq_num_events_last = rng.random::<u64>();
188    hb.evq_num_events_avg  = rng.random::<u64>();
189    hb.n_ev_unsent         = rng.random::<u64>();
190    hb.n_ev_missed         = rng.random::<u64>();
191    hb.trate               = rng.random::<u16>() as u64;
192    hb.lost_trate          = rng.random::<u16>() as u64;
193    hb.version             = ProtocolVersion::from_random();
194    if hb.version != ProtocolVersion::Unknown {
195      hb.prescale_gaps       = rng.random::<f32>();
196      hb.prescale_track      = rng.random::<f32>();
197    }
198    hb
199  }
200}
201
202impl fmt::Display for MasterTriggerHB {
203  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204    let repr = self.pretty_print();
205    write!(f, "{}", repr)
206  }
207} 
208
209impl MoniData for MasterTriggerHB {
210  fn get_board_id(&self) -> u8 {
211    0
212  }
213 
214  fn get_timestamp(&self) -> u64 {
215    self.timestamp 
216  }
217
218  fn set_timestamp(&mut self, ts : u64) { 
219    self.timestamp = ts;
220  }
221
222  /// Access the (data) members by name 
223  fn get(&self, varname : &str) -> Option<f32> {
224    match varname {
225      "total_elapsed"       => Some(self.total_elapsed as f32),
226      "n_events"            => Some(self.n_events as f32),
227      "evq_num_events_last" => Some(self.evq_num_events_last as f32),
228      "evq_num_events_avg"  => Some(self.evq_num_events_avg as f32),
229      "n_ev_unsent"         => Some(self.n_ev_unsent as f32),
230      "n_ev_missed"         => Some(self.n_ev_missed as f32),
231      "trate"               => Some(self.trate as f32), 
232      "lost_trate"          => Some(self.lost_trate as f32),
233      "prescale_track"      => Some(self.prescale_track as f32),
234      "prescale_gaps"       => Some(self.prescale_gaps as f32),
235      "timestamp"           => Some(self.timestamp as f32),
236      _                     => None
237    }
238  }
239
240  /// A list of the variables in this MoniData
241  fn keys() -> Vec<&'static str> {
242    vec!["board_id", "total_elapsed", "n_events",
243         "evq_num_events_last", "evq_num_events_avg", "n_ev_unsent",
244         "n_ev_missed", "trate", "lost_trate", "prescale_track",
245         "prescale_gaps","timestamp"]
246  }
247}
248
249moniseries!(MasterTriggerHBSeries, MasterTriggerHB);
250
251//-----------------------------------------------------
252
253#[cfg(feature="pybindings")]
254#[pymethods]
255impl MasterTriggerHB {
256
257  //    version             
258  #[getter]
259  fn get_total_elapsed(&self) -> u64 {
260    self.total_elapsed
261  }
262
263  #[getter]
264  fn get_evq_mum_events_last(&self) -> u64 {
265    self.evq_num_events_last
266  }
267
268  #[getter]
269  fn get_evq_num_events_avg(&self) -> u64 {
270    self.evq_num_events_avg
271  }
272  
273  #[getter]
274  fn get_n_ev_unsent(&self) -> u64 {
275    self.n_ev_unsent
276  }
277
278  #[getter]
279  fn get_n_ev_missed(&self) -> u64 {
280    self.n_ev_missed
281  }
282  
283  #[getter]
284  fn get_trate(&self) -> u64 {
285    self.trate
286  }
287
288  #[getter]
289  fn get_lost_trate(&self) -> u64 {
290    self.lost_trate
291  }
292
293  #[getter]
294  #[pyo3(name="get_prescale_track")]
295  fn get_prescale_track_py(&self) -> f32 {
296    self.prescale_track
297  }
298
299  #[getter]
300  #[pyo3(name="get_prescale_gaps")]
301  fn get_prescale_gaps_py(&self) -> f32 {
302    self.prescale_gaps
303  }
304
305  #[getter]
306  #[pyo3(name="timestamp")]
307  fn get_timestamp_py(&self) -> u64 {
308    self.timestamp
309  }
310}
311
312#[cfg(feature="pybindings")]
313pythonize_packable!(MasterTriggerHB);
314#[cfg(feature="pybindings")]
315pythonize_monidata!(MasterTriggerHB);
316
317//-----------------------------------------------------
318
319#[cfg(feature="random")]
320#[test]
321fn pack_master_trigger_hb() {
322  for _ in 0..100 {
323    let hb = MasterTriggerHB::from_random();
324    let test : MasterTriggerHB = hb.pack().unpack().unwrap();
325    assert_eq!(hb, test);
326  }
327} 
328
329