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 trigger_type        : TriggerType,
13  pub combo_trig_type     : TriggerType,
14  pub n_events            : u64,
15  pub evq_num_events_last : u64,
16  pub evq_num_events_avg  : u64,
17  pub n_ev_unsent         : u64,
18  pub n_ev_missed         : u64,
19  pub trate               : u64,
20  pub lost_trate          : u64,
21  pub clock_rate          : u64,
22  pub rb_lost_rate        : u64,
23  // these will be available for ProtocolVersion::V1
24  pub prescale_track      : f32,
25  pub prescale_gaps       : f32,
26  pub tiu_ignore_deadtime : bool,
27  pub tiu_timeout_cnt     : u64,
28  pub tiu_busy_rate       : u16,
29  pub trg_lost_trg_rate   : u16,
30  pub gaps_blocked_rate   : u16,
31  pub track_blocked_rate  : u16,
32  pub any_blocked_rate    : u16,
33  pub trkctrl_blocked_rate: u16,
34  pub trkumbctrl_blocked  : u16,
35  pub prescale_bypass     : bool 
36}
37
38impl MasterTriggerHB {
39  pub fn new() -> Self {
40    Self {
41      version             : ProtocolVersion::Unknown,
42      total_elapsed       : 0,
43      trigger_type        : TriggerType::Unknown,
44      combo_trig_type     : TriggerType::Unknown,
45      n_events            : 0,
46      evq_num_events_last : 0,
47      evq_num_events_avg  : 0,
48      n_ev_unsent         : 0,
49      n_ev_missed         : 0,
50      trate               : 0,
51      lost_trate          : 0,
52      clock_rate          : 0,
53      rb_lost_rate        : 0,
54      // available for protocol version V1 and larger
55      prescale_track      : 0.0,
56      prescale_gaps       : 0.0,
57      tiu_ignore_deadtime : false,
58      tiu_timeout_cnt     : 0,
59      tiu_busy_rate       : 0,
60      trg_lost_trg_rate   : 0,
61      gaps_blocked_rate   : 0,
62      track_blocked_rate  : 0,
63      any_blocked_rate    : 0,
64      trkctrl_blocked_rate: 0,
65      trkumbctrl_blocked  : 0,
66      prescale_bypass     : false
67    }
68  }
69
70  pub fn get_sent_packet_rate(&self) -> f64 {
71    if self.total_elapsed > 0 {
72      return self.n_events as f64 / self.total_elapsed as f64;
73    }
74    0.0
75  }
76
77  // get the prescale for the secondary trigger
78  pub fn get_prescale_track(&self) -> f64 {
79    if self.version == ProtocolVersion::Unknown {
80      error!("Prescale not available for protocol version < V1!");
81      return 0.0;
82    }
83    return self.prescale_track as f64
84  }
85  
86  // get the prescale for the secondary trigger
87  pub fn get_prescale_gaps(&self) -> f64 {
88    if self.version == ProtocolVersion::Unknown {
89      error!("Prescale not available for protocol version < V1!");
90      return 0.0;
91    }
92    return self.prescale_gaps as f64
93  }
94  
95
96  pub fn pretty_print(&self) -> String {
97    let mut repr = format!("<MasterTriggerHBs (version : {})", self.version);
98    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} "));
99    repr += &(format!("\n MET (Mission Elapsed Time)        : {:.1} sec", self.total_elapsed));
100    repr += &(format!("\n Primary trigger type              : {}", self.trigger_type));
101    repr += &(format!("\n Secondary trigger type            : {}", self.combo_trig_type));  
102    if self.version != ProtocolVersion::Unknown {
103        repr += &(format!("\n Primary trigger prescale          : {:.4}", self.prescale_gaps));
104        repr += &(format!("\n Secondary trigger prescale        : {:.4}", self.prescale_track));
105        repr += &(format!("\n Bypass presecale?                 : {}", self.prescale_bypass));
106    }
107    repr += &(format!("\n \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504}")); 
108    repr += &(format!("\n total trigger rate, recorded:     : {:.2} Hz", self.n_events as f64 / self.total_elapsed as f64));
109    repr += &(format!("\n total trigger rate, from register : {:.2} Hz", self.trate));
110    repr += &(format!("\n \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504}"));
111    repr += &(format!("\n lost total trg rate, from register: {:.2} Hz", self.lost_trate));
112    repr += &(format!("\n trigger lost rate, from register  : {:.2} Hz", self.trg_lost_trg_rate));
113    repr += &(format!("\n RB lost rate, from register       : {:.2} Hz", self.rb_lost_rate));
114    repr += &(format!("\n TIU lost rate, from register      : {:.2} HZ", self.tiu_busy_rate));
115    repr += &(format!("\n"));
116    match self.trigger_type {
117        TriggerType::Gaps => {
118            repr += &(format!("\n GAPS trigger blocked rate     : {}", self.gaps_blocked_rate));
119        }
120        TriggerType::Track => {
121            repr += &(format!("\n Track trigger blocked rate    : {}", self.track_blocked_rate));
122        }
123        TriggerType::Any => {
124            repr += &(format!("\n Any trigger blocked rate      : {}", self.any_blocked_rate));
125        }
126        TriggerType::TrackCentral => {
127            repr += &(format!("\n Track central blocked rate    : {}", self.trkctrl_blocked_rate));
128        }
129        TriggerType::TrackUmbCentral => {
130            repr += &(format!("\n TrackUmbCentral blocked rate  : {}", self.trkumbctrl_blocked));
131        }
132        _ => {
133            repr += &(format!("\n register not implemented for {} trigger blocked rate", self.trigger_type));
134        }
135    }
136
137    repr += &(format!("\n \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504}"));
138    repr += &(format!("\n Num. recorded Events        : {}", self.n_events));
139    repr += &(format!("\n MTB clock rate              : {}", self.clock_rate));
140
141    repr += &(format!("\n"));
142    repr += &(format!("\n Using TOF fixed deadtime?         : {}", self.tiu_ignore_deadtime));
143    repr += &(format!("\n Fixed deadtime (*10ns)      : {}", self.tiu_timeout_cnt));
144    
145    repr += &(format!("\n \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504} \u{2504}"));
146    repr += &(format!("\n Last MTB EVQ size           : {}", self.evq_num_events_last));
147    repr += &(format!("\n Avg. MTB EVQ size (per 30s ): {:.2}", self.evq_num_events_avg));
148    if self.n_ev_unsent > 0 {
149        repr += &(format!("\n Num. sent errors        : {}", self.n_ev_unsent).bold());
150    }
151    if self.n_ev_missed > 0 {
152        repr += &(format!("\n Num. missed events      : {}", self.n_ev_missed).bold());
153    }
154    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} "));
155    repr
156  }
157}
158  
159impl Default for MasterTriggerHB {
160  fn default () -> Self {
161    Self::new()
162  }
163}
164
165impl TofPackable for MasterTriggerHB {
166  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::MasterTriggerHB;
167}
168
169impl Serialization for MasterTriggerHB {
170  const HEAD : u16 = 0xAAAA;
171  const TAIL : u16 = 0x5555;
172  const SIZE : usize = 111;
173
174  fn from_bytestream(stream    :&Vec<u8>,
175                     pos       :&mut usize)
176  -> Result<Self, SerializationError>{
177    Self::verify_fixed(stream, pos)?;
178    let mut hb = MasterTriggerHB::new(); 
179    hb.version                = ProtocolVersion::from(parse_u8(stream, pos) as u8);
180    hb.total_elapsed          = parse_u64(stream, pos);
181    hb.trigger_type           = TriggerType::from(parse_u8(stream, pos) as u8);
182    hb.combo_trig_type        = TriggerType::from(parse_u8(stream, pos) as u8);
183    hb.n_events               = parse_u64(stream, pos);
184    hb.evq_num_events_last    = parse_u64(stream, pos);
185    hb.evq_num_events_avg     = parse_u64(stream, pos);
186    hb.n_ev_unsent            = parse_u64(stream, pos);
187    hb.n_ev_missed            = parse_u64(stream, pos);
188    hb.trate                  = parse_u64(stream, pos);
189    hb.lost_trate             = parse_u64(stream, pos);
190    hb.clock_rate             = parse_u64(stream, pos);
191    hb.rb_lost_rate           = parse_u64(stream, pos);
192    hb.prescale_track         = parse_f32(stream, pos);
193    hb.prescale_gaps          = parse_f32(stream, pos);
194    /*if hb.version == ProtocolVersion::Unknown {
195      hb.prescale_gaps  = 0.0;
196      hb.prescale_track = 0.0
197    }
198    */
199    hb.tiu_ignore_deadtime    = parse_bool(stream, pos);
200    hb.tiu_timeout_cnt        = parse_u64(stream, pos);
201    hb.tiu_busy_rate          = parse_u16(stream, pos);
202    hb.trg_lost_trg_rate      = parse_u16(stream, pos);
203    match hb.trigger_type {
204        TriggerType::Gaps => {
205            hb.gaps_blocked_rate    = parse_u16(stream, pos);
206        }
207        TriggerType::Track => {
208            hb.track_blocked_rate   = parse_u16(stream, pos);
209        }
210        TriggerType::Any => {
211            hb.any_blocked_rate     = parse_u16(stream, pos);
212        }
213        TriggerType::TrackCentral => {
214            hb.trkctrl_blocked_rate = parse_u16(stream, pos);
215        }
216        TriggerType::TrackUmbCentral => {
217            hb.trkumbctrl_blocked   = parse_u16(stream, pos);
218        }
219        _ => {
220            *pos += 2;
221        }
222    }
223    hb.prescale_bypass        = parse_bool(stream, pos);
224    *pos += 2;
225    Ok(hb)
226  }
227
228  fn to_bytestream(&self) -> Vec<u8> {
229    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
230    bs.extend_from_slice(&Self::HEAD.to_le_bytes());
231    bs.push(self.version as u8);
232    bs.extend_from_slice(&self.total_elapsed.to_le_bytes());
233    bs.extend_from_slice(&(self.trigger_type as u8).to_le_bytes());
234    bs.extend_from_slice(&(self.combo_trig_type as u8).to_le_bytes());
235    bs.extend_from_slice(&self.n_events.to_le_bytes());
236    bs.extend_from_slice(&self.evq_num_events_last.to_le_bytes());
237    bs.extend_from_slice(&self.evq_num_events_avg.to_le_bytes());
238    bs.extend_from_slice(&self.n_ev_unsent.to_le_bytes());
239    bs.extend_from_slice(&self.n_ev_missed.to_le_bytes());
240    bs.extend_from_slice(&self.trate.to_le_bytes());
241    bs.extend_from_slice(&self.lost_trate.to_le_bytes());
242    bs.extend_from_slice(&self.clock_rate.to_le_bytes());
243    bs.extend_from_slice(&self.rb_lost_rate.to_le_bytes());
244    bs.extend_from_slice(&self.prescale_track.to_le_bytes());
245    bs.extend_from_slice(&self.prescale_gaps.to_le_bytes());
246    bs.extend_from_slice(&(self.tiu_ignore_deadtime as u8).to_le_bytes());
247    bs.extend_from_slice(&self.tiu_timeout_cnt.to_le_bytes());
248    bs.extend_from_slice(&self.tiu_busy_rate.to_le_bytes());
249    bs.extend_from_slice(&self.trg_lost_trg_rate.to_le_bytes());
250    match self.trigger_type {
251        TriggerType::Gaps => {
252            bs.extend_from_slice(&self.gaps_blocked_rate.to_le_bytes());
253        }
254        TriggerType::Track => {
255            bs.extend_from_slice(&self.track_blocked_rate.to_le_bytes());
256        }
257        TriggerType::Any => {
258            bs.extend_from_slice(&self.any_blocked_rate.to_le_bytes());
259        }
260        TriggerType::TrackCentral => {
261            bs.extend_from_slice(&self.trkctrl_blocked_rate.to_le_bytes());
262        }
263        TriggerType::TrackUmbCentral => {
264            bs.extend_from_slice(&self.trkumbctrl_blocked.to_le_bytes());
265        }
266        _ => {
267            bs.extend_from_slice(&[0u8; 2]);
268        }
269    }
270    bs.extend_from_slice(&(self.prescale_bypass as u8).to_le_bytes());
271    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
272    bs
273  }
274}
275
276#[cfg(feature = "random")]
277impl FromRandom for MasterTriggerHB {
278  fn from_random() -> Self {
279    let mut hb = Self::new();
280    let mut rng            = rand::rng(); 
281    hb.version             = ProtocolVersion::from_random();  
282    hb.total_elapsed       = rng.random::<u64>();
283    hb.trigger_type        = TriggerType::from_random();
284    hb.combo_trig_type     = TriggerType::from_random();
285    hb.n_events            = rng.random::<u64>();
286    hb.evq_num_events_last = rng.random::<u64>();
287    hb.evq_num_events_avg  = rng.random::<u64>();
288    hb.n_ev_unsent         = rng.random::<u64>();
289    hb.n_ev_missed         = rng.random::<u64>();
290    hb.trate               = rng.random::<u16>() as u64;
291    hb.lost_trate          = rng.random::<u16>() as u64;
292    hb.clock_rate          = rng.random::<u64>();
293    hb.rb_lost_rate        = rng.random::<u16>() as u64;
294    hb.prescale_track       = rng.random::<f32>();
295    hb.prescale_gaps      = rng.random::<f32>();
296    hb.tiu_ignore_deadtime = rng.random::<bool>();
297    hb.tiu_timeout_cnt     = rng.random::<u64>();
298    hb.tiu_busy_rate       = rng.random::<u16>();
299    hb.trg_lost_trg_rate   = rng.random::<u16>();
300    match hb.trigger_type {
301        TriggerType::Gaps => {
302            hb.gaps_blocked_rate = rng.random::<u16>();
303        }
304        TriggerType::Track => {
305            hb.track_blocked_rate = rng.random::<u16>();
306        }
307        TriggerType::Any => {
308            hb.any_blocked_rate = rng.random::<u16>();
309        }
310        TriggerType::TrackCentral => {
311            hb.trkctrl_blocked_rate = rng.random::<u16>();
312        }
313        TriggerType::TrackUmbCentral => {
314            hb.trkumbctrl_blocked = rng.random::<u16>();
315        }
316        _ => {}
317    }
318    hb.prescale_bypass   = rng.random::<bool>();
319    hb
320  }
321}
322
323impl fmt::Display for MasterTriggerHB {
324  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325    let repr = self.pretty_print();
326    write!(f, "{}", repr)
327  }
328} 
329
330impl MoniData for MasterTriggerHB {
331  fn get_board_id(&self) -> u8 {
332    0
333  }
334 
335  fn get_timestamp(&self) -> u64 {
336    self.total_elapsed
337  }
338  /*
339  fn get_timestamp(&self) -> u64 {
340    self.timestamp 
341  }
342
343  fn set_timestamp(&mut self, ts : u64) { 
344    self.timestamp = ts;
345  }
346  */
347
348  /// Access the (data) members by name 
349  fn get(&self, varname : &str) -> Option<f32> {
350    match varname {
351      "total_elapsed"       => Some(self.total_elapsed as f32),
352      "n_events"            => Some(self.n_events as f32),
353      "evq_num_events_last" => Some(self.evq_num_events_last as f32),
354      "evq_num_events_avg"  => Some(self.evq_num_events_avg as f32),
355      "n_ev_unsent"         => Some(self.n_ev_unsent as f32),
356      "n_ev_missed"         => Some(self.n_ev_missed as f32),
357      "trate"               => Some(self.trate as f32), 
358      "lost_trate"          => Some(self.lost_trate as f32),
359      "prescale_track"      => Some(self.prescale_track as f32),
360      "prescale_gaps"       => Some(self.prescale_gaps as f32),
361      "clock_rate"          => Some(self.clock_rate as f32),
362      "rb_lost_rate"        => Some(self.rb_lost_rate as f32),
363      "tiu_timeout_cnt"     => Some(self.tiu_timeout_cnt as f32),
364      "tiu_busy_rate"       => Some(self.tiu_busy_rate as f32),
365      "trg_lost_trg_rate"   => Some(self.trg_lost_trg_rate as f32),
366      "prescale_bypass"     => Some(self.prescale_bypass as u8 as f32),
367      "tiu_ignore_deadtime" => Some(self.tiu_ignore_deadtime as u8 as f32),
368      "trigger_type"        => Some(self.trigger_type.to_u8() as f32),
369      "combo_trig_type"     => Some(self.combo_trig_type.to_u8() as f32), 
370      "gaps_blocked_rate"   => Some(self.gaps_blocked_rate as f32), 
371      "track_blocked_rate"  => Some(self.track_blocked_rate as f32),
372      "any_blocked_rate"    => Some(self.any_blocked_rate as f32), 
373      "trkctrl_blocked_rate"=> Some(self.trkctrl_blocked_rate as f32),
374      "trkumbctrl_blocked"  => Some(self.trkumbctrl_blocked as f32),
375      //"timestamp"           => Some(self.timestamp as f32),
376      _                     => None
377    }
378  }
379
380  /// A list of the variables in this MoniData
381  fn keys() -> Vec<&'static str> {
382    vec!["total_elapsed", "trigger_type", "combo_trig_type", "n_events",
383         "evq_num_events_last", "evq_num_events_avg", "n_ev_unsent",
384         "n_ev_missed", "trate", "lost_trate","clock_rate", "rb_lost_rate", "prescale_track",
385         "prescale_gaps", "tiu_ignore_deadtime", "tiu_timeout_cnt", "tiu_busy_rate", "trg_lost_trg_rate", 
386         "gaps_blocked_rate", "track_blocked_rate", "any_blocked_rate", "trkctrl_blocked_rate",
387         "trkumbctrl_blocked", "prescale_bypass"]
388  }
389}
390
391moniseries!(MasterTriggerHBSeries, MasterTriggerHB);
392
393//-----------------------------------------------------
394
395#[cfg(feature="pybindings")]
396#[pymethods]
397impl MasterTriggerHB {
398
399  //    version             
400
401  #[getter]
402  fn get_clock_rate(&self) -> u64 {
403      self.clock_rate
404  }
405  #[getter]
406  fn get_trigger_type(&self) -> TriggerType {
407      self.trigger_type
408  }
409
410  #[getter]
411  fn get_combo_trig_type(&self) -> TriggerType {
412      self.combo_trig_type
413  }
414  #[getter]
415  fn get_rb_lost_rate(&self) -> u64 {
416      self.rb_lost_rate
417  }
418
419  #[getter]
420  fn get_tiu_ignore_deadtime(&self) -> bool {
421      self.tiu_ignore_deadtime
422  }
423  #[getter]
424  fn get_tiu_timeout_cnt(&self) -> u64 {
425      self.tiu_timeout_cnt
426  }
427  #[getter]
428  fn get_tiu_busy_rate(&self) -> u16 {
429      self.tiu_busy_rate
430  }
431  #[getter]
432  fn get_trg_lost_trg_rate(&self) -> u16 {
433      self.trg_lost_trg_rate
434  }
435  #[getter]
436  fn get_gaps_blocked_rate(&self) -> u16 {
437      self.gaps_blocked_rate
438  }
439
440  #[getter]
441  fn get_track_blocked_rate(&self) -> u16 {
442      self.track_blocked_rate
443  }
444
445  #[getter]
446  fn get_any_blocked_rate(&self) -> u16 {
447      self.any_blocked_rate
448  }
449  #[getter]
450  fn get_track_central_blocked_rate(&self) -> u16 {
451      self.trkctrl_blocked_rate
452  }
453  #[getter]
454  fn get_track_umb_central_blocked_rate(&self) -> u16 {
455      self.trkumbctrl_blocked
456  }
457  #[getter]
458  fn get_prescale_bypass(&self) -> bool {
459      self.prescale_bypass
460  }
461  #[getter]
462  fn get_total_elapsed(&self) -> u64 {
463    self.total_elapsed
464  }
465
466  #[getter]
467  fn get_evq_mum_events_last(&self) -> u64 {
468    self.evq_num_events_last
469  }
470
471  #[getter]
472  fn get_evq_num_events_avg(&self) -> u64 {
473    self.evq_num_events_avg
474  }
475  
476  #[getter]
477  fn get_n_ev_unsent(&self) -> u64 {
478    self.n_ev_unsent
479  }
480
481  #[getter]
482  fn get_n_ev_missed(&self) -> u64 {
483    self.n_ev_missed
484  }
485  
486  #[getter]
487  fn get_trate(&self) -> u64 {
488    self.trate
489  }
490
491  #[getter]
492  fn get_lost_trate(&self) -> u64 {
493    self.lost_trate
494  }
495
496  #[getter]
497  #[pyo3(name="get_prescale_track")]
498  fn get_prescale_track_py(&self) -> f32 {
499    self.prescale_track
500  }
501
502  #[getter]
503  #[pyo3(name="get_prescale_gaps")]
504  fn get_prescale_gaps_py(&self) -> f32 {
505    self.prescale_gaps
506  }
507
508  /*
509  #[getter]
510  #[pyo3(name="timestamp")]
511  fn get_timestamp_py(&self) -> u64 {
512    self.timestamp
513  }
514  */
515}
516
517#[cfg(feature="pybindings")]
518pythonize_packable!(MasterTriggerHB);
519#[cfg(feature="pybindings")]
520pythonize_monidata!(MasterTriggerHB);
521
522//-----------------------------------------------------
523
524#[cfg(feature="random")]
525#[test]
526fn pack_master_trigger_hb() {
527  for _ in 0..100 {
528    let hb = MasterTriggerHB::from_random();
529    let test : MasterTriggerHB = hb.pack().unpack().unwrap();
530    assert_eq!(hb, test);
531  }
532} 
533
534