Skip to main content

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