gondola_core/tof/
master_trigger.rs

1//! MasterTriggerBoard communications
2//!
3//! The MTB (MasterTriggerBoard) is currently
4//! (Jan 2023) connected to the ethernet 
5//! via UDP sockets and sends out its 
6//! own datapackets per each triggered 
7//! event.
8//!
9//! The packet format contains the event id
10//! as well as number of hits and a mask 
11//! which encodes the hit channels.
12//!
13//! The data is encoded in IPBus packets.
14//! [see docs here](https://ipbus.web.cern.ch/doc/user/html/)
15//! 
16// This file is part of gaps-online-software and published 
17// under the GPLv3 license
18
19pub mod control;
20pub mod registers;
21
22//use crate::prelude::*;
23use std::sync::{
24  Arc,
25  Mutex,
26};
27
28use std::time::{
29  Duration,
30  Instant
31};
32
33use std::thread;
34use crossbeam_channel::Sender;
35use serde_json::json;
36
37// FIXME - whenever there are too many things, we 
38//         just do this. idk if this is bad practice.
39//         It might be ok since this is a private import?
40use crate::prelude::*;
41use crate::io::ipbus::IPBus;
42
43use control::*;
44use registers::*;
45
46#[cfg(feature="pybindings")]
47use comfy_table::modifiers::UTF8_ROUND_CORNERS;
48#[cfg(feature="pybindings")]
49use comfy_table::presets::UTF8_FULL;
50#[cfg(feature="pybindings")]
51use comfy_table::*;
52
53/// helper function to parse output for TofBot
54fn remove_from_word(s: String, word: &str) -> String {
55  if let Some(index) = s.find(word) {
56    // Keep everything up to the found index (not including the word itself)
57    s[..index].to_string()
58  } else {
59    // If the word isn't found, return the original string
60    s
61  }
62}
63
64/// In case we get a broken DAQ package, 
65/// make sure we at least read it until the next 
66/// footer
67fn read_until_footer(bus : &mut IPBus) 
68  -> Result<Vec<u32>, MasterTriggerError> {
69  let mut data = Vec::<u32>::new();
70  loop {
71    let val = bus.read(0x11)?;
72    if val != 0xAAAAAAAA {
73        data.push(val);
74    }
75    if (val == 0x55555555) || (val == 0xAAAAAAAA) {
76      break;
77    }
78  }
79  Ok(data)
80}
81
82
83/// Read the complete event of the MTB
84///
85/// FIXME - this can get extended to read 
86/// multiple events at once. 
87/// For that, we just have to query the
88/// event size register multiple times.
89///
90/// <div class="warning"> Blocki until a UDP timeout error occurs or a non-zero result for MT.EVENT_QUEUE.SIZE register has been obtained.</div>
91///
92/// # Arguments
93///
94/// * bus       : connected IPBus for UDP comms
95pub fn get_event(bus                     : &mut IPBus)
96  -> Option<Result<TofEvent, MasterTriggerError>> {
97  //let mut debug_timer   = Instant::now();
98  let mut mte = TofEvent::new();
99  let n_daq_words_fixed = 9u32;
100  let mut data          : Vec<u32>;
101  loop {
102    // this register tells us how many times we can read out 
103    // the DAQ data register. An event has at least 12 fields.
104    // This is for an event with 1 LTB.
105    // (see gitlab docs https://gitlab.com/ucla-gaps-tof/firmware)
106    // so we definitly wait until we have at least 12. If so 
107    // we read out the rest later.
108    // If this reutrns an error, we quit right away.
109    // FIXME: There might be a tiny bug in this register, 
110    // where it is sometimes incorrect 
111    // (https://gitlab.com/ucla-gaps-tof/firmware/-/issues/69) - nice!
112    if EVQ_SIZE.get(bus).ok()? < 12 {
113      return None;
114    } else {
115      break;
116    }
117  }
118  // we read until we get ltb information, after that 
119  match bus.read_multiple(0x11, n_daq_words_fixed as usize, false) {
120    Ok(_data) => {
121      data = _data;
122    }
123    Err(err) => {
124      return Some(Err(err.into()));
125    }
126  }
127  if data.len() < 9 {
128    // something inconsistent happened here. We were requesting more 
129    // words than we got, that is bad
130    error!("Got MTB data, but the package ends before we get LTB information!");
131    warn!("Resetting master trigger DAQ");
132    match reset_daq(bus) {//, &mt_address) {
133      Err(err) => error!("Can not reset DAQ, error {err}"),
134      Ok(_)    => ()
135    }
136    return Some(Err(MasterTriggerError::DataTooShort));
137  }
138  let n_ltb = data[8].count_ones(); 
139  // in case of an odd number of ltbs, 
140  // there are some padding bytes
141  let odd   = n_ltb % 2 != 0;
142  let n_daq_words_flex : usize;
143  let n_hit_words      : usize;
144  // get hit fields
145  if odd {
146    n_hit_words = (n_ltb as usize + 1)/2;
147  } else {
148    n_hit_words = n_ltb as usize/2;
149  }
150  // the variable size part of the DAQ event
151  n_daq_words_flex = n_hit_words + 2; // crc + footer
152  let mut data_flex : Vec<u32>;
153  match bus.read_multiple(0x11, n_daq_words_flex, false) {
154    Ok(_data) => {
155      data_flex = _data;
156    }
157    Err(err) => { 
158      return Some(Err(err.into()));
159    }
160  }
161  data.append(&mut data_flex);
162  if data[0] != 0xAAAAAAAA {
163    error!("Got MTB data, but the header is incorrect {:x}", data[0]);
164    return Some(Err(MasterTriggerError::PackageHeaderIncorrect));
165  }
166 
167  let n_daq_words = n_daq_words_fixed as usize + n_daq_words_flex;
168  let foot_pos    = n_daq_words_fixed as usize + n_daq_words_flex - 1;
169  if data.len() != foot_pos + 1{
170    error!("Somehow the MTB DATA are misaligned! {}, {}", data.len(), foot_pos);
171    return Some(Err(MasterTriggerError::DataTooShort));
172  }
173  if data[foot_pos] != 0x55555555 {
174    error!("Got MTB data, but the footer is incorrect {:x}", data[foot_pos]);
175    if data[foot_pos] == 0xAAAAAAAA {
176      error!("Found next header, the package is TOO LONG! Attempt to fix for this event, but the next is LOST!");
177      info!("If we want to fix this, this whole mechanism needs a refactor and needs to fetch more thatn a single event at a time!");
178      // kill the lost event
179      read_until_footer(bus).ok()?;
180      // salvage from this event what is possible
181      data.pop();
182    } else {
183      // we try to recover!
184      let mut rest = read_until_footer(bus).ok()?;
185      data.append(&mut rest);
186      if data.len() != n_daq_words as usize + 1 {
187        error!("We tried to recover the event, however, that failed! Expected size of the packet {}, actual size {}", n_daq_words, data.len());
188        // get some debugging information to understand why this 
189        // happened
190        println!("-------------- DEBUG -------------------");
191        println!("N LTBs {} ({})", data[8].count_ones(), data[8]);
192        for k in data {
193          println!("-- {:x} ({})", k,k);
194        }
195        println!("--------------------");
196        return Some(Err(MasterTriggerError::PackageFooterIncorrect));
197      } else {
198        info!("Event recoveered!");
199      }
200    }
201  }
202  //println!("MTB packet {:?}", data);
203  // ---------- FIll the MTBEvent now
204  mte.event_id           = data[1];
205  mte.mt_timestamp       = data[2];
206  mte.mt_tiu_timestamp   = data[3];
207  mte.mt_tiu_gps32       = data[4];
208  mte.mt_tiu_gps16       =  (data[5] & 0x0000ffff) as u16;
209  mte.mt_trigger_sources = ((data[5] & 0xffff0000) >> 16) as u16;
210  //mte.get_trigger_sources();
211  let rbmask = (data[7] as u64) << 32 | data[6] as u64; 
212  mte.mtb_link_mask      = rbmask;
213  mte.dsi_j_mask         = data[8];
214  for k in 9..9 + n_hit_words {
215    let ltb_hits = data[k as usize];
216    // split them up
217    let first  =  (ltb_hits & 0x0000ffff) as u16;
218    let second = ((ltb_hits & 0xffff0000) >> 16) as u16;
219    mte.channel_mask.push(first);
220    // if this is the last hit word, only push 
221    // it in case n_ltb is odd
222    if k == ( 9 + n_hit_words) {
223      if !odd {
224         mte.channel_mask.push(second);  
225      }
226    } else {
227      mte.channel_mask.push(second);
228    }
229  }
230  // debug 
231  //println!("DEBUG GET_EVENT TOOK : {}", debug_timer.elapsed().as_nanos());
232  Some(Ok(mte))
233}
234
235/// Gather monitoring data from the Mtb
236///
237/// ISSUES - some values are always 0
238pub fn get_mtbmonidata(bus : &mut IPBus) 
239  -> Result<MtbMoniData, MasterTriggerError> {
240  let mut moni = MtbMoniData::new();
241  let data = bus.read_multiple(0x120, 4, true)?;
242  if data.len() < 4 {
243    return Err(MasterTriggerError::BrokenPackage);
244  }
245  let tiu_busy_len    = TIU_BUSY_LENGTH.get(bus)?;
246  let tiu_aux_link    = (TIU_USE_AUX_LINK.get(bus)? != 0) as u8;
247  let tiu_emu_mode    = (TIU_EMULATION_MODE.get(bus)? != 0) as u8;
248  let aggr_tiu        = TIU_LT_AND_RB_MULT.get(bus)?;
249  let tiu_link_bad    = (aggr_tiu & 0x1) as u8;
250  let tiu_busy_stuck  = ((aggr_tiu & 0x2) >> 1) as u8;
251  let tiu_busy_ign    = ((aggr_tiu & 0x4) >> 2) as u8;
252  let mut tiu_status  = 0u8;
253  tiu_status          = tiu_status | (tiu_emu_mode);
254  tiu_status          = tiu_status | (tiu_aux_link << 1);
255  tiu_status          = tiu_status | ((tiu_link_bad as u8) << 2);
256  tiu_status          = tiu_status | (tiu_busy_stuck << 3);
257  tiu_status          = tiu_status | (tiu_busy_ign << 4);
258  let daq_queue_len   = EVQ_NUM_EVENTS.get(bus)? as u16;
259  moni.tiu_status     = tiu_status;
260  moni.tiu_busy_len   = tiu_busy_len;
261  moni.daq_queue_len  = daq_queue_len;
262  // sensors are 12 bit
263  let first_word     = 0x00000fff;
264  let second_word    = 0x0fff0000;
265  moni.temp          = ( data[2] & first_word  ) as u16;  
266  moni.vccint        = ((data[2] & second_word ) >> 16) as u16;  
267  moni.vccaux        = ( data[3] & first_word  ) as u16;  
268  moni.vccbram       = ((data[3] & second_word ) >> 16) as u16;  
269 
270  let rate           = bus.read_multiple(0x17, 2, true)?;
271  // FIXME - technically, the rate is 24bit, however, we just
272  // read out 16 here (if the rate is beyond ~65kHz, we don't need 
273  // to know with precision
274  let mask           = 0x0000ffff;
275  moni.rate          = (rate[0] & mask) as u16;
276  moni.lost_rate     = (rate[1] & mask) as u16;
277  let rb_lost_rate  = RB_LOST_TRIGGER_RATE.get(bus)?;
278  if rb_lost_rate > 255 {
279    moni.rb_lost_rate = 255;
280  } else {
281    moni.rb_lost_rate = rb_lost_rate as u8;
282  }
283  Ok(moni)
284}
285
286/// Configure the MTB according to lifot settings.
287/// If the settings have a non-zero prescale for 
288/// any of the triggers, this will cause the 
289/// MTB to start triggering 
290/// (if it hasn't been triggering before)
291///
292/// CHANGELOG - in previous versions, this reset the 
293///             MTB DAQ multiple times, this is not 
294///             ncessary and caused more issues than
295///             actually fixed something, so these got 
296///             removed.
297///
298/// # Arguments:
299///   * bus        : IPBus connected to the MTB (UDP)
300///   * settings   : configure the MTB according
301///                  to these settings 
302pub fn configure_mtb(bus : &mut IPBus,
303                     settings   : &MTBSettings) -> Result<(), MasterTriggerError> {
304  let trace_suppression = settings.trace_suppression;
305  match set_trace_suppression(bus, trace_suppression) {
306    Err(err) => error!("Unable to set trace suppression mode! {err}"),
307    Ok(_)    => {
308      if trace_suppression {
309        println!("==> Setting MTB to trace suppression mode!");
310      } else {
311        println!("==> Setting MTB to ALL_RB_READOUT mode!");
312        warn!("Reading out all events from all RBs! Data might be very large!");
313      }
314    }
315  }
316
317  let tiu_ignore_busy    = settings.tiu_ignore_busy;
318  match TIU_BUSY_IGNORE.set(bus, tiu_ignore_busy as u32) {
319    Err(err) => error!("Unable to change tiu busy ignore settint! {err}"),
320    Ok(_)    => {
321      if tiu_ignore_busy {
322        warn!("Ignoring TIU since tiu_busy_ignore is set in the config file!");
323        println!("==> Ignoring TIU since tiu_busy_ignore is set in the config file!");
324      }
325    }
326  }
327
328  // Oct 2025 - new "fixed deadtime mode" - will ignore the deadtime 
329  // coming from the TIU  
330  let use_fixed_deadtime = settings.use_fixed_deadtime.unwrap_or(false);  
331  if use_fixed_deadtime {
332    match MIN_DEADTIME_MODE.set(bus, true as u32) {
333      Err(err) => { 
334        error!("Unable to set MTB in fixed deadtime mode {err}!");
335      }
336      Ok(_)    => {
337        warn!("Ignoring the busy part of the TIU signal from the TIU due to min deadtime setting!");
338        println!("==> MTB in 'Min/fixed deadtime mode'. This will ignore the BUSY part of the TIU signal");
339      }
340    }
341  } else {
342    match MIN_DEADTIME_MODE.set(bus, false as u32) {
343      Err(err) => { 
344        error!("Unable to set MTB in fixed deadtime mode {err}!");
345      }
346      Ok(_)    => {
347        warn!("Ignoring the busy part of the TIU signal from the TIU due to min deadtime setting!");
348        println!("==> MTB in 'Min/fixed deadtime mode'. This will ignore the BUSY part of the TIU signal");
349      }
350    }
351  }
352  info!("Settting rb integration window!");
353  let int_wind = settings.rb_int_window;
354  match set_rb_int_window(bus, int_wind) {
355    Err(err) => error!("Unable to set rb integration window! {err}"),
356    Ok(_)    => {
357      info!("rb integration window set to {}", int_wind); 
358    } 
359  }
360
361  match unset_all_triggers(bus) {
362    Err(err) => error!("Unable to undo previous trigger settings! {err}"),
363    Ok(_)    => ()
364  }
365  match settings.trigger_type {
366    TriggerType::Poisson => {
367      match set_poisson_trigger(bus,settings.poisson_trigger_rate) {
368        Err(err) => error!("Unable to set the POISSON trigger! {err}"),
369        Ok(_)    => ()
370      }
371    }
372    TriggerType::Any     => {
373      match set_any_trigger(bus,settings.trigger_prescale) {
374        Err(err) => error!("Unable to set the ANY trigger! {err}"),
375        Ok(_)    => ()
376      }
377    }
378    TriggerType::Track   => {
379      match set_track_trigger(bus, settings.trigger_prescale) {
380        Err(err) => error!("Unable to set the TRACK trigger! {err}"),
381        Ok(_)    => ()
382      }
383    }
384    TriggerType::TrackCentral   => {
385      match set_central_track_trigger(bus, settings.trigger_prescale) {
386        Err(err) => error!("Unable to set the CENTRAL TRACK trigger! {err}"),
387        Ok(_)    => ()
388      }
389    }
390    TriggerType::TrackUmbCentral  => {
391      match set_track_umb_central_trigger(bus, settings.trigger_prescale) {
392        Err(err) => error!("Unable to set the TRACK UMB CENTRAL trigger! {err}"),
393        Ok(_)   => ()
394      }
395    }
396    TriggerType::Gaps    => {
397      match set_gaps_trigger(bus, settings.gaps_trigger_use_beta) {
398        Err(err) => error!("Unable to set the GAPS trigger! {err}"),
399        Ok(_)    => ()
400      }
401    }
402    TriggerType::Gaps633    => {
403      match set_gaps633_trigger(bus, settings.gaps_trigger_use_beta) {
404        Err(err) => error!("Unable to set the GAPS trigger! {err}"),
405        Ok(_)    => ()
406      }
407    }
408    TriggerType::Gaps422    => {
409      match set_gaps422_trigger(bus, settings.gaps_trigger_use_beta) {
410        Err(err) => error!("Unable to set the GAPS trigger! {err}"),
411        Ok(_)    => ()
412      }
413    }
414    TriggerType::Gaps211    => {
415      match set_gaps211_trigger(bus, settings.gaps_trigger_use_beta) {
416        Err(err) => error!("Unable to set the GAPS trigger! {err}"),
417        Ok(_)    => ()
418      }
419    }
420    TriggerType::Gaps1044   => {
421      match set_gaps1044_trigger(bus, settings.gaps_trigger_use_beta) {
422        Err(err) => error!("Unable to set the GAPS trigger! {err}"),
423        Ok(_)    => ()
424      }
425    }
426    TriggerType::UmbCube => {
427      match set_umbcube_trigger(bus) {
428        Err(err) => error!("Unable to set UmbCube trigger! {err}"),
429        Ok(_)    => ()
430      }
431    }
432    TriggerType::UmbCubeZ => {
433      match set_umbcubez_trigger(bus) {
434        Err(err) => error!("Unable to set UmbCubeZ trigger! {err}"),
435        Ok(_)    => ()
436      }
437    }
438    TriggerType::UmbCorCube => {
439      match set_umbcorcube_trigger(bus) {
440        Err(err) => error!("Unable to set UmbCorCube trigger! {err}"),
441        Ok(_)    => ()
442      }
443    }
444    TriggerType::CorCubeSide => {
445      match set_corcubeside_trigger(bus) {
446        Err(err) => error!("Unable to set CorCubeSide trigger! {err}"),
447        Ok(_)    => ()
448      }
449    }
450    TriggerType::Umb3Cube => {
451      match set_umb3cube_trigger(bus) {
452        Err(err) => error!("Unable to set Umb3Cube trigger! {err}"), 
453        Ok(_)    => ()
454      }
455    }
456    TriggerType::Unknown => {
457      println!("== ==> Not setting any trigger condition. You can set it through pico_hal.py");
458      warn!("Trigger condition undefined! Not setting anything!");
459      error!("Trigger conditions unknown!");
460    }
461    _ => {
462      error!("Trigger type {} not covered!", settings.trigger_type);
463      println!("= => Not setting any trigger condition. You can set it through pico_hal.py");
464      warn!("Trigger condition undefined! Not setting anything!");
465      error!("Trigger conditions unknown!");
466    }
467  }
468    
469  // combo trigger - still named "global_trigger" in settings 
470  // mistakenly.
471  // FIXME
472  if settings.use_combo_trigger {
473    let global_prescale = settings.global_trigger_prescale;
474    let prescale_val    = (u32::MAX as f32 * global_prescale as f32).floor() as u32;
475    println!("=> Setting an additonal trigger - using combo mode. Using prescale of {}", prescale_val as f32 / u32::MAX as f32);
476    // FIXME - the "global" is wrong. We need to rename this at some point
477    match settings.global_trigger_type {
478      TriggerType::Any             => {
479        match ANY_TRIG_PRESCALE.set(bus, prescale_val) {
480          Ok(_)    => (),
481          Err(err) => error!("Settting the prescale {} for the any trigger failed! {err}", prescale_val) 
482        }
483      }
484      TriggerType::Track           => {
485        match TRACK_TRIG_PRESCALE.set(bus, prescale_val) {
486          Ok(_)    => (),
487          Err(err) => error!("Settting the prescale {} for the any trigger failed! {err}", prescale_val) 
488        }
489      }
490      TriggerType::TrackCentral    => {
491        match TRACK_CENTRAL_PRESCALE.set(bus, prescale_val) {
492          Ok(_)    => (),
493          Err(err) => error!("Settting the prescale {} for the track central trigger failed! {err}", prescale_val) 
494        }
495      }
496      TriggerType::TrackUmbCentral => {
497        match TRACK_UMB_CENTRAL_PRESCALE.set(bus, prescale_val) {
498          Ok(_)    => (),
499          Err(err) => error!("Settting the prescale {} for the track umb central trigger failed! {err}", prescale_val) 
500        }
501      }
502      _ => {
503        error!("Unable to set {} as a global trigger type!", settings.global_trigger_type);
504      }
505    }
506  }
507  
508  //match SWAP_RB_LINK_IDS.set(bus,1) {
509  //  Ok(
510  //}
511
512  Ok(())
513}
514
515/// Communications with the master trigger over Udp
516///
517/// The master trigger can send packets over the network.
518/// These packets contain timestamps as well as the 
519/// eventid and a hitmaks to identify which LTBs have
520/// participated in the trigger.
521/// The packet format is described
522/// [here](https://gitlab.com/ucla-gaps-tof/firmware/-/tree/develop/)
523///
524/// # Arguments
525///
526/// * mt_address        : Udp address of the MasterTriggerBoard
527///
528/// * mt_sender         : push retrieved TofEvents to 
529///                       this channel
530/// * mtb_timeout_sec   : reconnect to mtb when we don't
531///                       see events in mtb_timeout seconds.
532///
533/// * verbose           : Print "heartbeat" output 
534///
535pub fn master_trigger(mt_address     : &str,
536                      mt_sender      : &Sender<TofEvent>,
537                      moni_sender    : &Sender<TofPacket>, 
538                      thread_control : Arc<Mutex<ThreadControl>>,
539                      verbose        : bool) {
540  let mut bus            : IPBus;
541  let mut heartbeat      = MasterTriggerHB::new();
542  let mut mtb_timeout    = Instant::now();
543  let mut moni_interval  = Instant::now();
544  let mut tc_timer       = Instant::now();
545  
546  let mut settings       : MTBSettings;
547  let mut cali_active    : bool;
548  let mut holdoff        : bool;
549  let mut veri_active    : bool;
550  loop {
551    match thread_control.lock() {
552      Ok(tc) => {
553        settings    = tc.liftof_settings.mtb_settings.clone();  
554        cali_active = tc.calibration_active; 
555        holdoff     = tc.holdoff_mtb_thread;
556        veri_active = tc.verification_active;
557      }
558      Err(err) => {
559        error!("Can't acquire lock for ThreadControl! Unable to set calibration mode! {err}");
560        return;
561      }
562    }
563
564    if holdoff || cali_active {
565      thread::sleep(Duration::from_secs(5));
566    } else {
567      if !holdoff {
568        println!("=> Docking clamp released!");
569      }
570      break;
571    }
572    if moni_interval.elapsed().as_secs() > settings.mtb_moni_interval {
573      match IPBus::new(mt_address) {
574        Err(err) => {
575          debug!("Can't connect to MTB, will try again in 10 ms! {err}");
576          continue;
577        }
578        Ok(mut moni_bus) => {
579          match get_mtbmonidata(&mut moni_bus) { 
580            Err(err) => {
581              error!("Can not get MtbMoniData! {err}");
582            },
583            Ok(moni) => {
584              let tp = moni.pack();
585              match moni_sender.send(tp) {
586                Err(err) => {
587                  error!("Can not send MtbMoniData over channel! {err}");
588                },
589                Ok(_) => ()
590              }
591            }
592          }
593        }
594      }
595      moni_interval = Instant::now();
596    }
597  } 
598  let mtb_timeout_sec    = settings.mtb_timeout_sec;
599  let mtb_moni_interval  = settings.mtb_moni_interval;
600  
601  // verbose, debugging
602  let mut last_event_id       = 0u32;
603  let mut first               = true;
604  let mut slack_cadence       = 5; // send only one slack message 
605                              // every 5 times we send moni data
606  let mut evq_num_events      = 0u64;
607  let mut n_iter_loop         = 0u64;
608  let mut hb_timer            = Instant::now();
609  let hb_interval             = Duration::from_secs(settings.hb_send_interval as u64);
610  let mut n_non_recoverable   = 0usize; // events which could not be recovered. We 
611                                        // can use this to reset the DAQ at a certain 
612                                        // point
613  let connection_timeout = Instant::now(); 
614  loop { 
615    match IPBus::new(mt_address) {
616      Err(err) => {
617        debug!("Can't connect to MTB, will try again in 10 ms! {err}");
618        thread::sleep(Duration::from_millis(10));
619      }
620      Ok(_bus) => {
621        bus = _bus;
622        break
623      }
624    }
625    if connection_timeout.elapsed().as_secs() > 10 {
626      error!("Unable to connect to MTB after 10 seconds!");
627      match thread_control.lock() {
628        Ok(mut tc) => {
629          tc.thread_master_trg_active = false;
630        }
631        Err(err) => {
632          error!("Can't acquire lock for ThreadControl! Unable to set calibration mode! {err}");
633        },
634      }
635      return;
636    }
637  }
638  
639  debug!("Resetting master trigger DAQ");
640  // We'll reset the pid as well
641  bus.pid = 0;
642  match bus.realign_packet_id() {
643    Err(err) => error!("Can not realign packet ID! {err}"),
644    Ok(_)    => ()
645  }
646  
647  match reset_daq(&mut bus) {//, &mt_address) {
648    Err(err) => error!("Can not reset DAQ! {err}"),
649    Ok(_)    => ()
650  }
651 
652  match RESYNC.pulse_it(&mut bus) {
653    Err(err) => error!("Unable to resycn MTB and RB clocks! {err}"),
654    Ok(_)    => println!("=> RB and MTB clocks synchronized!")
655  }
656
657  match EVENT_CNT_RESET.set(&mut bus, 1) {
658    Err(err) => error!("Unable to reset event counter! {err}"),
659    Ok(_)    => println!("=> Event counter reset!")
660  }
661
662  match configure_mtb(&mut bus, &settings) {
663    Err(err) => error!("Configuring the MTB failed! {err}"),
664    Ok(())   => ()
665  }
666 
667  let mut preload_cache = 1000; // preload the cache when we are starting 
668  loop {
669    // Check thread control and what to do
670    // Deactivate this for now
671    if tc_timer.elapsed().as_secs_f32() > 2.5 {
672      match thread_control.try_lock() {
673        Ok(mut tc) => {
674          if tc.stop_flag || tc.sigint_recvd {
675            tc.end_all_rb_threads = true;
676            break;
677          }
678        
679        },
680        Err(err) => {
681          error!("Can't acquire lock for ThreadControl! Unable to set calibration mode! {err}");
682        },
683      }
684      tc_timer = Instant::now();
685    }
686    // This is a recovery mechanism. In case we don't see an event
687    // for mtb_timeout_sec, we attempt to reconnect to the MTB
688    if mtb_timeout.elapsed().as_secs() > mtb_timeout_sec {
689      if mtb_timeout.elapsed().as_secs() > mtb_timeout_sec {
690        info!("reconnection timer elapsed");
691      } else {
692        info!("reconnection requested");
693      }
694      match IPBus::new(mt_address) {
695        Err(err) => {
696          error!("Can't connect to MTB! {err}");
697          continue; // try again
698        }
699        Ok(_bus) => {
700          bus = _bus;
701          //thread::sleep(Duration::from_micros(1000));
702          debug!("Resetting master trigger DAQ");
703          // We'll reset the pid as well
704          bus.pid = 0;
705          match bus.realign_packet_id() {
706            Err(err) => error!("Can not realign packet ID! {err}"),
707            Ok(_)    => ()
708          }
709          match reset_daq(&mut bus) {//, &mt_address) {
710            Err(err) => error!("Can not reset DAQ! {err}"),
711            Ok(_)    => ()
712          }
713        }
714      }
715      mtb_timeout    = Instant::now();
716    }
717    if moni_interval.elapsed().as_secs() > mtb_moni_interval || first {
718      if first {
719        first = false;
720      }
721      match get_mtbmonidata(&mut bus) { 
722        Err(err) => {
723          error!("Can not get MtbMoniData! {err}");
724        },
725        Ok(_moni) => {
726          if settings.tofbot_webhook != String::from("")  {
727            let url  = &settings.tofbot_webhook;
728            let message = format!("\u{1F916}\u{1F680}\u{1F388} [LIFTOF (Bot)]\n rate - {}[Hz]\n {}", _moni.rate, settings);
729            let clean_message = remove_from_word(message, "tofbot_webhook");
730            let data = json!({
731              "text" : clean_message
732            });
733            match serde_json::to_string(&data) {
734              Ok(data_string) => {
735                if slack_cadence == 0 {
736                  match ureq::post(url)
737                      .set("Content-Type", "application/json")
738                      .send_string(&data_string) {
739                    Err(err) => { 
740                      error!("Unable to send {} to TofBot! {err}", data_string);
741                    }
742                    Ok(response) => {
743                      match response.into_string() {
744                        Err(err) => {
745                          error!("Not able to read response! {err}");
746                        }
747                        Ok(body) => {
748                          if verbose {
749                            println!("[master_trigger] - TofBot responded with {}", body);
750                          }
751                        }
752                      }
753                    }
754                  }
755                } else {
756                  slack_cadence -= 1;
757                }
758                if slack_cadence == 0 {
759                  slack_cadence = 5;
760                }
761              }
762              Err(err) => {
763                error!("Can not convert .json to string! {err}");
764              }
765            }
766          }
767          //let tp = TofPacket::from(&_moni);
768          let tp = _moni.pack();
769          match moni_sender.send(tp) {
770            Err(err) => {
771              error!("Can not send MtbMoniData over channel! {err}");
772            },
773            Ok(_) => ()
774          }
775        }
776      }
777      moni_interval = Instant::now();
778    }
779    
780    match get_event(&mut bus){ //,
781      None     => {
782      }
783      Some(Err(err)) => {
784        match err {
785          MasterTriggerError::PackageFooterIncorrect
786          | MasterTriggerError::PackageHeaderIncorrect 
787          | MasterTriggerError::DataTooShort
788          | MasterTriggerError::BrokenPackage => {
789            // in case we can't recover an event for x times, let's reset the DAQ
790            // not sure if 10 is a good number
791            if n_non_recoverable == 100 {
792              error!("We have seen {} non-recoverable events, let's reset the DAQ!", n_non_recoverable);
793              match reset_daq(&mut bus) {//, &mt_address) {
794                Err(err) => error!("Can not reset DAQ, error {err}"),
795                Ok(_)    => ()
796              }
797              n_non_recoverable = 0;
798            } 
799            n_non_recoverable += 1;
800          }
801          _ => ()
802        }
803      },
804      Some(Ok(mut _ev)) => {
805        if _ev.event_id == last_event_id {
806          error!("We got a duplicate event from the MTB!");
807          continue;
808        }
809        if _ev.event_id > last_event_id + 1 {
810          if last_event_id != 0 {
811            error!("We skipped {} events!", _ev.event_id - last_event_id); 
812            heartbeat.n_ev_missed += (_ev.event_id - last_event_id) as u64;
813          }
814        }
815        last_event_id = _ev.event_id;
816        heartbeat.n_events += 1;
817        // we have to make sure some of the fields get properly filled and 
818        // "transfer" some of the mt_* fields to the fields which get actually serialzied 
819        let mt_timestamp       = _ev.get_mt_timestamp_abs();
820        _ev.timestamp32        = (mt_timestamp  & 0x00000000ffffffff ) as u32;
821        _ev.timestamp16        = ((mt_timestamp & 0x0000ffff00000000 ) >> 32) as u16;
822        _ev.trigger_sources    = _ev.mt_trigger_sources; // FIXME
823        _ev.n_trigger_paddles  = _ev.get_trigger_hits().len() as u8;
824        let triggers           = TriggerType::transcode_trigger_sources(_ev.mt_trigger_sources);
825        if triggers.len() == 2 {
826          heartbeat.trigger_type = triggers[0];
827          heartbeat.combo_trig_type = triggers[1];
828        }
829        if triggers.len() == 1 {
830          heartbeat.trigger_type = triggers[0];
831        }
832        if !veri_active {
833          match mt_sender.send(_ev) {
834            Err(err) => {
835              error!("Can not send TofEvent over channel! {err}");
836              heartbeat.n_ev_unsent += 1;
837            },
838            Ok(_) => ()
839          }
840        }
841      }
842    }
843    
844    if preload_cache > 0 {
845      preload_cache -= 1;
846      continue;
847    }
848    if hb_timer.elapsed() >= hb_interval {
849      match EVQ_NUM_EVENTS.get(&mut bus) {
850        Err(err) => {
851          error!("Unable to query {}! {err}", EVQ_NUM_EVENTS);
852        }
853        Ok(num_ev) => {
854          evq_num_events += num_ev as u64;
855          heartbeat.evq_num_events_last = num_ev as u64;
856          n_iter_loop    += 1;
857          heartbeat.evq_num_events_avg = (evq_num_events as u64)/(n_iter_loop as u64);
858        }
859      }
860      heartbeat.total_elapsed += hb_timer.elapsed().as_secs() as u64;
861      match TRIGGER_RATE.get(&mut bus) {
862        Ok(trate) => {
863          heartbeat.trate = trate as u64;
864        }
865        Err(err) => {
866          error!("Unable to query {}! {err}", TRIGGER_RATE);
867        }
868      }
869      match LOST_TRIGGER_RATE.get(&mut bus) {
870        Ok(lost_trate) => {
871          heartbeat.lost_trate = lost_trate as u64;
872        }
873        Err(err) => {
874          error!("Unable to query {}! {err}", LOST_TRIGGER_RATE);
875        }
876      }
877
878       match RB_LOST_TRIGGER_RATE.get(&mut bus) {
879           Err(err) => {
880               error!("Unable to query {}! {err}", RB_LOST_TRIGGER_RATE);
881           }
882           Ok(rb_lost_rate) => {
883               heartbeat.rb_lost_rate = rb_lost_rate as u64;
884           }
885       }
886
887       match CLOCK_RATE.get(&mut bus) {
888           Err(err) => { 
889               error!("Unable to query {}! {err}", CLOCK_RATE);
890           }
891           Ok(clock_rate) => {
892               heartbeat.clock_rate = clock_rate as u64;
893           }
894       }
895      match MIN_DEADTIME_MODE.get(&mut bus) {
896          Err(err) => {
897              error!("Unable to query {}! {err}", MIN_DEADTIME_MODE);
898          }
899          Ok(tiu_ignore_deadtime) => {
900              heartbeat.tiu_ignore_deadtime = tiu_ignore_deadtime != 0;
901          }
902      }
903
904      match TIU_TIMEOUT_CONST.get(&mut bus) {
905          Err(err) => {
906              error!("Unable to query {}! {err}", TIU_TIMEOUT_CONST);
907          }
908          Ok(tiu_timeout_cnt) => {
909              heartbeat.tiu_timeout_cnt = tiu_timeout_cnt as u64;
910          }
911      }
912
913      match TIU_BUSY_RATE.get(&mut bus) {
914          Err(err) => {
915              error!("Unable to query {} {err}!", TIU_BUSY_RATE);
916          }
917          Ok(tiu_busy_rate) => {
918              heartbeat.tiu_busy_rate = tiu_busy_rate as u16;
919          }
920      }
921
922      match TRG_LOST_TRIGGER_RATE.get(&mut bus) {
923          Err(err) => {
924              error!("Unable to query {} {err}!", TRG_LOST_TRIGGER_RATE);
925          }
926          Ok(trg_lost_trg_rate) => {
927              heartbeat.trg_lost_trg_rate = trg_lost_trg_rate as u16;
928          }
929      }
930      
931      match GAPS_TRIGGER_BLOCKED_RATE.get(&mut bus) {
932          Err(err) => {
933              error!("Unable to query {} {err}!", GAPS_TRIGGER_BLOCKED_RATE);
934          }
935          Ok(gaps_blocked_rate) => {
936              heartbeat.gaps_blocked_rate = gaps_blocked_rate as u16;
937          }
938      }
939      match TRACK_TRIGGER_BLOCKED_RATE.get(&mut bus) {
940          Err(err) => {
941              error!("Unable to query {} {err}!", TRACK_TRIGGER_BLOCKED_RATE);
942          }
943          Ok(track_blocked_rate) => {
944              heartbeat.track_blocked_rate = track_blocked_rate as u16;
945          }
946      }
947      match ANY_TRIGGER_BLOCKED_RATE.get(&mut bus) {
948          Err(err) => {
949              error!("Unable to query {} {err}!", ANY_TRIGGER_BLOCKED_RATE);
950          }
951          Ok(any_blocked_rate) => {
952              heartbeat.any_blocked_rate = any_blocked_rate as u16;
953          }
954      }
955
956      match TRACK_CENTRAL_BLOCKED_RATE.get(&mut bus) {
957        Err(err) => {
958            error!("Unable to query {} {err}!", TRACK_CENTRAL_BLOCKED_RATE);
959        }
960        Ok(trkctrl_blocked_rate) => {
961            heartbeat.trkctrl_blocked_rate = trkctrl_blocked_rate as u16;
962        }
963      }
964
965      match TRACK_UMB_CENTRAL_BLOCKED_RATE.get(&mut bus) {
966          Err(err) => {
967              error!("Unable to query {} {err}!", TRACK_UMB_CENTRAL_BLOCKED_RATE);
968          }
969          Ok(trkumbctrl_blocked) => {
970              heartbeat.trkumbctrl_blocked = trkumbctrl_blocked as u16;
971          }
972      }
973
974      match PRESCALE_BYPASS.get(&mut bus) {
975          Err(err) => {
976              error!("Unable to query {} {err}!", PRESCALE_BYPASS);
977          }
978          Ok(prescale_bypass) => {
979              heartbeat.prescale_bypass = prescale_bypass != 0;
980          }
981      }
982
983      match TRACK_TRIG_PRESCALE.get(&mut bus) {
984        Ok(ps) => {
985          heartbeat.prescale_track = (ps as f32) / (u32::MAX as f32) ;
986        }
987        Err(err) => {
988          error!("Unable to query {}! {err}", TRACK_TRIG_PRESCALE);
989        }
990      }
991      match GAPS_TRIG_PRESCALE.get(&mut bus) {
992        Ok(ps) => {
993          heartbeat.prescale_gaps = (ps as f32) / (u32::MAX as f32) ;
994        }
995        Err(err) => {
996          error!("Unable to query {}! {err}", GAPS_TRIG_PRESCALE);
997        }
998      }
999      heartbeat.version = ProtocolVersion::V1; 
1000      if verbose {
1001        println!("{}", heartbeat);
1002      }
1003
1004      let pack = heartbeat.pack();
1005      match moni_sender.send(pack) {
1006        Err(err) => {
1007          error!("Can not send MTB Heartbeat over channel! {err}");
1008        },
1009        Ok(_) => ()
1010      }
1011      hb_timer = Instant::now();
1012    } 
1013  }
1014}
1015
1016#[cfg(feature = "pybindings")]
1017#[pyfunction]
1018#[pyo3(name="prescale_to_u32")]
1019/// Convert a prescale value in range from 0-1.0 to 
1020/// an u32 value so that it can be written to the 
1021/// MTB registers
1022pub fn wrap_prescale_to_u32(prescale : f32) -> u32 {
1023  let mut _prescale = prescale;
1024  prescale_to_u32(prescale)
1025}
1026
1027//---------------------------------------
1028// PORT from pybidings/master_trigger.rs 
1029
1030#[cfg(feature="pybindings")]
1031#[pyclass]
1032#[pyo3(name = "MasterTrigger")]
1033pub struct PyMasterTrigger {
1034  ipbus : IPBus,
1035}
1036
1037#[cfg(feature="pybindings")]
1038#[pymethods]
1039impl PyMasterTrigger {
1040  #[new]
1041  fn new(target_address : &str) -> Self {
1042    let ipbus = IPBus::new(target_address).expect("Unable to connect to {target_address}");
1043    Self {
1044      ipbus : ipbus,
1045    }
1046  }
1047
1048  fn reset_daq(&mut self) -> PyResult<()>{
1049    match self.ipbus.write(0x10,1) {
1050      Ok(result) => {
1051        return Ok(result); 
1052      }
1053      Err(err) => {
1054        return Err(PyValueError::new_err(err.to_string()));
1055      }
1056    }
1057  }
1058
1059  fn get_expected_pid(&mut self) -> PyResult<u16> {
1060    match self.ipbus.get_target_next_expected_packet_id(){
1061      Ok(result) => {
1062        return Ok(result); 
1063      }
1064      Err(err) => {
1065        return Err(PyValueError::new_err(err.to_string()));
1066      }
1067    }
1068  }
1069
1070  fn realign_packet_id(&mut self) -> PyResult<()> {
1071    match self.ipbus.realign_packet_id() {
1072      Ok(_) => {
1073        return Ok(()); 
1074      }
1075      Err(err) => {
1076        return Err(PyValueError::new_err(err.to_string()));
1077      }
1078    }
1079  }
1080
1081  fn set_packet_id(&mut self, pid : u16) {
1082    self.ipbus.pid = pid;
1083  }
1084
1085  fn get_packet_id(&mut self) -> u16 {
1086    self.ipbus.pid
1087  }
1088 
1089  #[getter]
1090  /// Get the global trigger rate in Hz
1091  fn rate(&mut self) -> PyResult<u32> {
1092    match TRIGGER_RATE.get(&mut self.ipbus) {
1093      Ok(rate) => {
1094        return Ok(rate);
1095      }
1096      Err(err) => {
1097        return Err(PyValueError::new_err(err.to_string()));
1098      }
1099    }
1100  }
1101 
1102  #[getter] 
1103  fn get_swap_rb_link_ids(&mut self) -> PyResult<bool> {
1104    match SWAP_RB_LINK_IDS.get(&mut self.ipbus) {
1105      Ok(swap) => {
1106        return Ok(swap > 0);
1107      }
1108      Err(err) => {
1109        return Err(PyValueError::new_err(err.to_string()));
1110      }
1111    }
1112  }
1113  
1114  #[setter] 
1115  fn set_swap_rb_link_ids(&mut self, swap : u32) -> PyResult<()> {
1116    match SWAP_RB_LINK_IDS.set(&mut self.ipbus, swap) {
1117      Ok(_) => {
1118        return Ok(());
1119      }
1120      Err(err) => {
1121        return Err(PyValueError::new_err(err.to_string()));
1122      }
1123    }
1124  }
1125
1126  #[getter]
1127  /// Get the lost global trigger rate in Hz
1128  ///
1129  /// This is the rate of triggers which got 
1130  /// dropped due to TIU BUSY signal + those which 
1131  /// get dropped due to the RBs being busy
1132  fn lost_rate(&mut self) -> PyResult<u32> {
1133    match LOST_TRIGGER_RATE.get(&mut self.ipbus) {
1134      Ok(rate) => {
1135        return Ok(rate);
1136      }
1137      Err(err) => {
1138        return Err(PyValueError::new_err(err.to_string()));
1139      }
1140    }
1141  }
1142  #[getter]
1143  fn get_prescale_bypass(&mut self) -> PyResult<u32> {
1144      match PRESCALE_BYPASS.get(&mut self.ipbus) {
1145          Ok(rate) => {
1146              return Ok(rate);
1147          }
1148          Err(err) => {
1149              return Err(PyValueError::new_err(err.to_string()));
1150          }
1151      }
1152  }
1153  #[getter]
1154  fn clock_rate(&mut self) -> PyResult<u32> {
1155      match CLOCK_RATE.get(&mut self.ipbus) {
1156          Ok(rate) => {
1157              return Ok(rate);
1158          }
1159          Err(err) => {
1160              return Err(PyValueError::new_err(err.to_string()));
1161          }
1162      }
1163  }
1164
1165  #[getter]
1166  /// The lost rate which occured due to RB busy timeouts
1167  fn rb_lost_rate(&mut self) -> PyResult<u32> {
1168    match RB_LOST_TRIGGER_RATE.get(&mut self.ipbus) {
1169      Ok(rate) => {
1170        return Ok(rate);
1171      }
1172      Err(err) => {
1173        return Err(PyValueError::new_err(err.to_string()));
1174      }
1175    }
1176  }
1177
1178  /// The lost rate which occured due to the tracker BUSY signal
1179  #[getter]
1180  fn tiu_lost_rate(&mut self) -> PyResult<u32> {
1181    match TIU_LOST_TRIGGER_RATE.get(&mut self.ipbus) {
1182      Ok(rate) => {
1183        return Ok(rate);
1184      }
1185      Err(err) => {
1186        return Err(PyValueError::new_err(err.to_string()));
1187      }
1188    }
1189  }
1190  
1191  /// the lost rate due to the trigger internal busy
1192  #[getter]
1193  fn trg_lost_trg_rate(&mut self) -> PyResult<u32> {
1194      match TRG_LOST_TRIGGER_RATE.get(&mut self.ipbus) {
1195          Ok(rate) => {
1196              return Ok(rate);
1197          }
1198          Err(err) => {
1199              return Err(PyValueError::new_err(err.to_string()));
1200          }
1201      }
1202  }
1203
1204  /// the amount of fixed deadtime used by the tiu in units of 10ns
1205  #[getter]
1206  fn get_tiu_timeout_cnt(&mut self) -> PyResult<u32> {
1207      match TIU_TIMEOUT_CONST.get(&mut self.ipbus) {
1208          Ok(rate) => {
1209              return Ok(rate);
1210          }
1211          Err(err) => {
1212              return Err(PyValueError::new_err(err.to_string()));
1213          }
1214      }
1215  }
1216  /// get tiu busy rate in Hz
1217  #[getter]
1218  fn tiu_busy_rate(&mut self) -> PyResult<u32> {
1219      match TIU_BUSY_RATE.get(&mut self.ipbus) {
1220          Ok(rate) => {
1221              return Ok(rate);
1222          }
1223          Err(err) => {
1224              return Err(PyValueError::new_err(err.to_string()));
1225          }
1226      }
1227  }
1228  /// Check if the TIU emulation mode is on
1229  ///
1230  fn get_tiu_emulation_mode(&mut self) -> PyResult<u32> {
1231    match TIU_EMULATION_MODE.get(&mut self.ipbus) {
1232      Ok(mode) => {
1233        return Ok(mode);
1234      }
1235      Err(err) => {
1236        return Err(PyValueError::new_err(err.to_string()));
1237      }
1238    }
1239  }
1240  /// check if the MTB is ignoring the TIU and using fixed internal busy
1241  #[getter]
1242  fn get_ignore_tiu_busy(&mut self) -> PyResult<u32> {
1243      match MIN_DEADTIME_MODE.get(&mut self.ipbus) {
1244          Ok(mode) => {
1245              return Ok(mode);
1246          }
1247          Err(err) => {
1248              return Err(PyValueError::new_err(err.to_string()));
1249          }
1250      }
1251  }
1252
1253 #[setter]
1254  fn set_tiu_emulation_mode(&mut self, value : u32) -> PyResult<()> {
1255    match TIU_EMULATION_MODE.set(&mut self.ipbus, value) {
1256      Ok(_) => {
1257        return Ok(());
1258      }
1259      Err(err) => {
1260        return Err(PyValueError::new_err(err.to_string()));
1261      }
1262    }
1263  }
1264
1265 #[setter]
1266  fn set_tiu_timeout_cnt(&mut self, value : u32) -> PyResult<()> {
1267      match TIU_TIMEOUT_CONST.set(&mut self.ipbus, value) {
1268          Ok(_) => {
1269              return Ok(());
1270          }
1271          Err(err) => {
1272              return Err(PyValueError::new_err(err.to_string()));
1273          }
1274      }
1275  }
1276 #[setter]
1277  fn set_ignore_tiu_busy(&mut self, value : u32) -> PyResult<()> {
1278      match MIN_DEADTIME_MODE.set(&mut self.ipbus, value) {
1279          Ok(_) => {
1280              return Ok(());
1281          }
1282          Err(err) => {
1283              return Err(PyValueError::new_err(err.to_string()));
1284          }
1285      }
1286  }
1287
1288  #[setter]
1289  fn set_tiu_emulation_mode_bsy_cnt(&mut self,  cycles : u32) -> PyResult<()> {
1290    match TIU_EMU_BUSY_CNT.set(&mut self.ipbus, cycles) {
1291      Ok(_) => {
1292        return Ok(());
1293      }
1294      Err(err) => {
1295        return Err(PyValueError::new_err(err.to_string()));
1296      }
1297    }
1298  }
1299  
1300  #[getter]
1301  fn get_tiu_emulation_mode_bsy_cnt(&mut self) -> PyResult<u32> {
1302    match TIU_EMU_BUSY_CNT.get(&mut self.ipbus) {
1303      Ok(value) => {
1304        return Ok(value);
1305      }
1306      Err(err) => {
1307        return Err(PyValueError::new_err(err.to_string()));
1308      }
1309    }
1310  }
1311  
1312  fn get_enable_cyclic_trig(&mut self) -> PyResult<bool> {
1313    match TRIG_CYCLIC_EN.get(&mut self.ipbus) {
1314      Ok(value) => {
1315        return Ok(value > 0);
1316      }
1317      Err(err) => {
1318        return Err(PyValueError::new_err(err.to_string()));
1319      }
1320    }
1321  }
1322
1323  fn disable_cyclic_trig(&mut self) -> PyResult<()> {
1324    match TRIG_CYCLIC_EN.set(&mut self.ipbus, 0x0) {
1325      Ok(_) => {
1326        return Ok(());
1327      }
1328      Err(err) => {
1329        return Err(PyValueError::new_err(err.to_string()));
1330      }
1331    }
1332  }
1333  
1334  fn enable_cyclic_trig(&mut self) -> PyResult<()> {
1335    match TRIG_CYCLIC_EN.set(&mut self.ipbus, 0x1) {
1336      Ok(_) => {
1337        return Ok(());
1338      }
1339      Err(err) => {
1340        return Err(PyValueError::new_err(err.to_string()));
1341      }
1342    }
1343  }
1344  
1345  fn get_cyclic_trigger_interval(&mut self) -> PyResult<u32> {
1346    match TRIG_CYCLIC_INTERVAL.get(&mut self.ipbus) {
1347      Ok(interval) =>  {
1348        return Ok(interval);
1349      }
1350      Err(err) => {
1351        return Err(PyValueError::new_err(err.to_string()));
1352      }
1353    }
1354  } 
1355
1356  fn set_cyclic_trigger_interval(&mut self, interval : u32) -> PyResult<()> {
1357    match TRIG_CYCLIC_INTERVAL.set(&mut self.ipbus, interval) {
1358      Ok(_) =>  {
1359        return Ok(());
1360      }
1361      Err(err) => {
1362        return Err(PyValueError::new_err(err.to_string()));
1363      }
1364    }
1365  }
1366  
1367  /// Issue a one-time forced trigger
1368  fn trigger(&mut self) -> PyResult<()> {
1369    match FORCE_TRIGGER.set(&mut self.ipbus, 1) {
1370      Ok(_)  => {
1371        return Ok(());
1372      }
1373      Err(err) => {
1374        return Err(PyValueError::new_err(err.to_string()));
1375      }
1376    }
1377  }
1378
1379  fn set_poisson_trigger(&mut self, rate : u32) -> PyResult<()> {
1380    let clk_period = 100000000;
1381    let rate_val   = (u32::MAX*rate)/clk_period;//(1.0/ clk_period)).floor() as u32;
1382    match TRIG_GEN_RATE.set(&mut self.ipbus, rate_val) {
1383      Ok(_)  => {
1384        return Ok(());
1385      }
1386      Err(err) => {
1387        return Err(PyValueError::new_err(err.to_string()));
1388      }
1389    }
1390  }
1391
1392  fn set_gaps_track_trigger(&mut self, prescale : f32, use_beta : bool) -> PyResult<()>  {
1393    match control::set_gaps_track_trigger(&mut self.ipbus, prescale, use_beta) {
1394      Ok(_) => {
1395        return Ok(());
1396      }
1397      Err(err) => {
1398        return Err(PyValueError::new_err(err.to_string()));
1399      }
1400    }
1401  }
1402  
1403  fn set_gaps_any_trigger(&mut self, prescale : f32, use_beta : bool) -> PyResult<()>  {
1404    match control::set_gaps_any_trigger(&mut self.ipbus, prescale, use_beta) {
1405      Ok(_) => {
1406        return Ok(());
1407      }
1408      Err(err) => {
1409        return Err(PyValueError::new_err(err.to_string()));
1410      }
1411    }
1412  }
1413
1414  fn set_gaps_central_track_trigger(&mut self, prescale : f32, use_beta : bool) -> PyResult<()>  {
1415    match control::set_gaps_central_track_trigger(&mut self.ipbus, prescale, use_beta) {
1416      Ok(_) => {
1417        return Ok(());
1418      }
1419      Err(err) => {
1420        return Err(PyValueError::new_err(err.to_string()));
1421      }
1422    }
1423  }
1424
1425  fn set_gaps422_central_track_trigger(&mut self, prescale : f32, use_beta : bool) -> PyResult<()>  {
1426    match control::set_gaps422_central_track_trigger(&mut self.ipbus, prescale, use_beta) {
1427      Ok(_) => {
1428        return Ok(());
1429      }
1430      Err(err) => {
1431        return Err(PyValueError::new_err(err.to_string()));
1432      }
1433    }
1434  }
1435
1436  /// Get the status of enabling for LTBs 0-9
1437  fn get_lt_link_en0(&mut self) -> PyResult<u32> {
1438    match LT_LINK_EN0.get(&mut self.ipbus) {
1439      Ok(value)  => {
1440        return Ok(value);
1441      }
1442      Err(err) => {
1443        return Err(PyValueError::new_err(err.to_string()));
1444      }
1445    }
1446  }
1447  /// Get the status of enabling for LTBs 10-19
1448  fn get_lt_link_en1(&mut self) -> PyResult<u32> {
1449    match LT_LINK_EN1.get(&mut self.ipbus) {
1450      Ok(value)  => {
1451        return Ok(value);
1452      }
1453      Err(err) => {
1454        return Err(PyValueError::new_err(err.to_string()));
1455      }
1456    }
1457  }
1458  /// Get the status of enabling for LTBs 20-29
1459  fn get_lt_link_en02(&mut self) -> PyResult<u32> {
1460    match LT_LINK_EN2.get(&mut self.ipbus) {
1461      Ok(value)  => {
1462        return Ok(value);
1463      }
1464      Err(err) => {
1465        return Err(PyValueError::new_err(err.to_string()));
1466      }
1467    }
1468  }
1469  /// Get the status of enabling for LTBs 30-39
1470  fn get_lt_link_en3(&mut self) -> PyResult<u32> {
1471    match LT_LINK_EN3.get(&mut self.ipbus) {
1472      Ok(value)  => {
1473        return Ok(value);
1474      }
1475      Err(err) => {
1476        return Err(PyValueError::new_err(err.to_string()));
1477      }
1478    }
1479  }
1480  /// Get the status of enabling for LTBs 40-49
1481  fn get_lt_link_en4(&mut self) -> PyResult<u32> {
1482    match LT_LINK_EN4.get(&mut self.ipbus) {
1483      Ok(value)  => {
1484        return Ok(value);
1485      }
1486      Err(err) => {
1487        return Err(PyValueError::new_err(err.to_string()));
1488      }
1489    }
1490  }
1491  ///Set on/off link enabling for LTBs 0-9
1492  fn set_lt_link_en0(&mut self, value : u32) -> PyResult<u32> {
1493    match LT_LINK_EN0.set(&mut self.ipbus, value) {
1494      Ok(_) => {
1495        return Ok(value);
1496      }
1497      Err(err) => {
1498        return Err(PyValueError::new_err(err.to_string()));
1499      }
1500    }
1501  }
1502  ///Set on/off link enabling for LTBs 10-19
1503  fn set_lt_link_en1(&mut self, value : u32) -> PyResult<u32> {
1504    match LT_LINK_EN1.set(&mut self.ipbus, value) {
1505      Ok(_) => {
1506        return Ok(value);
1507      }
1508      Err(err) => {
1509        return Err(PyValueError::new_err(err.to_string()));
1510      }
1511    }
1512  }
1513  ///Set on/off link enabling for LTBs 20-29
1514  fn set_lt_link_en2(&mut self, value : u32) -> PyResult<u32> {
1515    match LT_LINK_EN2.set(&mut self.ipbus, value) {
1516      Ok(_) => {
1517        return Ok(value);
1518      }
1519      Err(err) => {
1520        return Err(PyValueError::new_err(err.to_string()));
1521      }
1522    }
1523  }
1524  ///Set on/off link enabling for LTBs 30-39
1525  fn set_lt_link_en3(&mut self, value : u32) -> PyResult<u32> {
1526    match LT_LINK_EN3.set(&mut self.ipbus, value) {
1527      Ok(_) => {
1528        return Ok(value);
1529      }
1530      Err(err) => {
1531        return Err(PyValueError::new_err(err.to_string()));
1532      }
1533    }
1534  }
1535  ///Set on/off link enabling for LTBs 40-49
1536  fn set_lt_link_en4(&mut self, value : u32) -> PyResult<u32> {
1537    match LT_LINK_EN4.set(&mut self.ipbus, value) {
1538      Ok(_) => {
1539        return Ok(value);
1540      }
1541      Err(err) => {
1542        return Err(PyValueError::new_err(err.to_string()));
1543      }
1544    }
1545  }
1546  ///get LT LINK AUTOMASK toggle status
1547  fn get_lt_link_automask(&mut self) -> PyResult<bool> {
1548    match LT_LINK_AUTOMASK.get(&mut self.ipbus) {
1549      Ok(value) => {
1550        return Ok(value != 0);
1551      }
1552      Err(err) => {
1553        return Err(PyValueError::new_err(err.to_string()));
1554      }
1555    }
1556  }
1557  ///set LT LINK AUTOMASK toggle status
1558  fn set_lt_link_automask(&mut self, toggle : bool) -> PyResult<bool> {
1559    match LT_LINK_AUTOMASK.set(&mut self.ipbus, toggle as u32) {
1560      Ok(_) => {
1561        return Ok(true);
1562      }
1563      Err(err) => {
1564        return Err(PyValueError::new_err(err.to_string()));
1565      }
1566    }
1567  }
1568fn get_gaps_trigger_prescale(&mut self) -> PyResult<f32> {
1569  match GAPS_TRIG_PRESCALE.get(&mut self.ipbus) {
1570    Ok (prescale_bus) => {
1571      let prescale_val = (u32::MAX as f32 * prescale_bus as f32).floor() as f32;
1572    return Ok(prescale_val)
1573    }
1574    Err(err) => {
1575      return Err(PyValueError::new_err(err.to_string()));
1576    }
1577  }
1578}
1579
1580fn set_gaps_trigger_prescale(&mut self, prescale : f32) -> PyResult<f32> {
1581  let prescale_val = (f32::MAX * prescale as f32).floor() as u32;
1582  match GAPS_TRIG_PRESCALE.set(&mut self.ipbus, prescale_val) {
1583    Ok(_) => {
1584      return Ok(prescale)
1585    }
1586    Err(err) => {
1587      return Err(PyValueError::new_err(err.to_string()));
1588    }
1589  }
1590}
1591
1592fn set_track_trigger_is_global(&mut self) -> PyResult<()> {
1593    match TRACK_CENTRAL_IS_GLOBAL.set(&mut self.ipbus, 1) {
1594      Ok(_) => {
1595        return Ok(());
1596      }
1597      Err(err) => {
1598        return Err(PyValueError::new_err(err.to_string()));
1599      }
1600    }
1601  }
1602
1603
1604fn get_ltb_links_ready(&mut self) -> PyResult<HashMap<u8, u32>> {
1605    let registers = [LT_LINK_READY0, LT_LINK_READY1,
1606                     LT_LINK_READY2, LT_LINK_READY3,
1607                     LT_LINK_READY4];
1608    let mut ready = HashMap::<u8, u32>::new();
1609    for (k,reg) in registers.iter().enumerate() {
1610      match reg.get(&mut self.ipbus) {
1611        Err(err) => {
1612          return Err(PyValueError::new_err(err.to_string()));
1613        }
1614        Ok(cnt) => {
1615          ready.insert(k as u8, cnt);
1616        }
1617      }
1618    }
1619    Ok(ready)
1620  }
1621
1622  fn get_ltb_event_cnts(&mut self) -> PyResult<HashMap<u8, u32>> {
1623    let registers = [LT0, LT1, LT2, LT3, LT4, LT5, LT6, LT7, LT8, LT9,
1624                     LT10, LT11, LT12, LT13, LT14, LT15, LT16, LT17, LT18, LT19,
1625                     LT20, LT21, LT22, LT23, LT24];
1626    let mut counters = HashMap::<u8, u32>::new();
1627    for (k,reg) in registers.iter().enumerate() {
1628      match reg.get(&mut self.ipbus) {
1629        Err(err) => {
1630          return Err(PyValueError::new_err(err.to_string()));
1631        }
1632        Ok(cnt) => {
1633          counters.insert(k as u8, cnt);
1634        }
1635      }
1636    }
1637    // print a table
1638    let mut table = Table::new();
1639    table
1640      .load_preset(UTF8_FULL)
1641      .apply_modifier(UTF8_ROUND_CORNERS)
1642      .set_content_arrangement(ContentArrangement::Dynamic)
1643      .set_width(80)
1644      .set_header(vec!["LT 0", "LT 1", "LT 2", "LT 3", "LT 4"])
1645      .add_row(vec![
1646          Cell::new(&(format!("{}", counters[&0]))),
1647          Cell::new(&(format!("{}", counters[&1]))),
1648          Cell::new(&(format!("{}", counters[&2]))),
1649          Cell::new(&(format!("{}", counters[&3]))),
1650          Cell::new(&(format!("{}", counters[&4]))),
1651          //Cell::new("Center aligned").set_alignment(CellAlignment::Center),
1652      ])
1653      .add_row(vec![
1654          Cell::new(String::from("LT 5")),
1655          Cell::new(String::from("LT 6")),
1656          Cell::new(String::from("LT 7")),
1657          Cell::new(String::from("LT 8")),
1658          Cell::new(String::from("LT 9")),
1659      ])
1660      .add_row(vec![
1661          Cell::new(&(format!("{}", counters[&5]))),
1662          Cell::new(&(format!("{}", counters[&6]))),
1663          Cell::new(&(format!("{}", counters[&7]))),
1664          Cell::new(&(format!("{}", counters[&8]))),
1665          Cell::new(&(format!("{}", counters[&9]))),
1666      ])
1667      .add_row(vec![
1668          Cell::new(String::from("LT 10")),
1669          Cell::new(String::from("LT 11")),
1670          Cell::new(String::from("LT 12")),
1671          Cell::new(String::from("LT 13")),
1672          Cell::new(String::from("LT 14")),
1673      ])
1674      .add_row(vec![
1675          Cell::new(&(format!("{}", counters[&10]))),
1676          Cell::new(&(format!("{}", counters[&11]))),
1677          Cell::new(&(format!("{}", counters[&12]))),
1678          Cell::new(&(format!("{}", counters[&13]))),
1679          Cell::new(&(format!("{}", counters[&14]))),
1680      ])
1681      .add_row(vec![
1682          Cell::new(String::from("LT 15")),
1683          Cell::new(String::from("LT 16")),
1684          Cell::new(String::from("LT 17")),
1685          Cell::new(String::from("LT 18")),
1686          Cell::new(String::from("LT 19")),
1687      ])
1688      .add_row(vec![
1689          Cell::new(&(format!("{}", counters[&15]))),
1690          Cell::new(&(format!("{}", counters[&16]))),
1691          Cell::new(&(format!("{}", counters[&17]))),
1692          Cell::new(&(format!("{}", counters[&18]))),
1693          Cell::new(&(format!("{}", counters[&19]))),
1694      ])
1695      .add_row(vec![
1696          Cell::new(String::from("LT 20")),
1697          Cell::new(String::from("LT 21")),
1698          Cell::new(String::from("LT 22")),
1699          Cell::new(String::from("LT 23")),
1700          Cell::new(String::from("LT 24")),
1701      ])
1702      .add_row(vec![
1703          Cell::new(&(format!("{}", counters[&20]))),
1704          Cell::new(&(format!("{}", counters[&21]))),
1705          Cell::new(&(format!("{}", counters[&22]))),
1706          Cell::new(&(format!("{}", counters[&23]))),
1707          Cell::new(&(format!("{}", counters[&24]))),
1708      ]);
1709
1710    // Set the default alignment for the third column to right
1711    let column = table.column_mut(2).expect("Our table has three columns");
1712    column.set_cell_alignment(CellAlignment::Right);
1713    println!("{table}");
1714    Ok(counters)
1715  }
1716  
1717  /// Readout the RB event counter registers
1718  fn get_rb_event_cnts(&mut self) -> PyResult<HashMap<u8, u8>> {
1719    let registers = [RB0_CNTS, RB1_CNTS, RB2_CNTS, RB3_CNTS, RB4_CNTS,
1720                     RB5_CNTS, RB6_CNTS, RB7_CNTS, RB8_CNTS, RB9_CNTS,
1721                     RB10_CNTS, RB11_CNTS, RB12_CNTS, RB13_CNTS, RB14_CNTS,
1722                     RB15_CNTS, RB16_CNTS, RB17_CNTS, RB18_CNTS, RB19_CNTS,
1723                     RB20_CNTS, RB21_CNTS, RB22_CNTS, RB23_CNTS, RB24_CNTS,
1724                     RB25_CNTS, RB26_CNTS, RB27_CNTS, RB28_CNTS, RB29_CNTS,
1725                     RB30_CNTS, RB31_CNTS, RB32_CNTS, RB33_CNTS, RB34_CNTS,
1726                     RB35_CNTS, RB36_CNTS, RB37_CNTS, RB38_CNTS, RB39_CNTS,
1727                     RB40_CNTS, RB41_CNTS, RB42_CNTS, RB43_CNTS, RB44_CNTS,
1728                     RB45_CNTS, RB46_CNTS, RB47_CNTS, RB48_CNTS, RB49_CNTS];
1729    let mut counters = HashMap::<u8, u8>::new();
1730    for (k,reg) in registers.iter().enumerate() {
1731      match reg.get(&mut self.ipbus) {
1732        Err(err) => {
1733          return Err(PyValueError::new_err(err.to_string()));
1734        }
1735        Ok(cnt) => {
1736          counters.insert(k as u8, cnt as u8);
1737        }
1738      }
1739    }
1740    let mut table = Table::new();
1741    table
1742      .load_preset(UTF8_FULL)
1743      .apply_modifier(UTF8_ROUND_CORNERS)
1744      .set_content_arrangement(ContentArrangement::Dynamic)
1745      .set_width(60)
1746      .set_header(vec!["RB 0", "RB 1", "RB 2", "RB 3", "RB 4"])
1747      .add_row(vec![
1748          Cell::new(&(format!("{}", counters[&0]))),
1749          Cell::new(&(format!("{}", counters[&1]))),
1750          Cell::new(&(format!("{}", counters[&2]))),
1751          Cell::new(&(format!("{}", counters[&3]))),
1752          Cell::new(&(format!("{}", counters[&4]))),
1753          //Cell::new("Center aligned").set_alignment(CellAlignment::Center),
1754      ])
1755      .add_row(vec![
1756          Cell::new(String::from("RB 5")),
1757          Cell::new(String::from("RB 6")),
1758          Cell::new(String::from("RB 7")),
1759          Cell::new(String::from("RB 8")),
1760          Cell::new(String::from("RB 9")),
1761      ])
1762      .add_row(vec![
1763          Cell::new(&(format!("{}", counters[&5]))),
1764          Cell::new(&(format!("{}", counters[&6]))),
1765          Cell::new(&(format!("{}", counters[&7]))),
1766          Cell::new(&(format!("{}", counters[&8]))),
1767          Cell::new(&(format!("{}", counters[&9]))),
1768      ])
1769      .add_row(vec![
1770          Cell::new(String::from("RB 10")),
1771          Cell::new(String::from("RB 11")),
1772          Cell::new(String::from("RB 12")),
1773          Cell::new(String::from("RB 13")),
1774          Cell::new(String::from("RB 14")),
1775      ])
1776      .add_row(vec![
1777          Cell::new(&(format!("{}", counters[&10]))),
1778          Cell::new(&(format!("{}", counters[&11]))),
1779          Cell::new(&(format!("{}", counters[&12]))),
1780          Cell::new(&(format!("{}", counters[&13]))),
1781          Cell::new(&(format!("{}", counters[&14]))),
1782      ])
1783      .add_row(vec![
1784          Cell::new(String::from("RB 15")),
1785          Cell::new(String::from("RB 16")),
1786          Cell::new(String::from("RB 17")),
1787          Cell::new(String::from("RB 18")),
1788          Cell::new(String::from("RB 19")),
1789      ])
1790      .add_row(vec![
1791          Cell::new(&(format!("{}", counters[&15]))),
1792          Cell::new(&(format!("{}", counters[&16]))),
1793          Cell::new(&(format!("{}", counters[&17]))),
1794          Cell::new(&(format!("{}", counters[&18]))),
1795          Cell::new(&(format!("{}", counters[&19]))),
1796      ])
1797      .add_row(vec![
1798          Cell::new(String::from("RB 20")),
1799          Cell::new(String::from("RB 21")),
1800          Cell::new(String::from("RB 22")),
1801          Cell::new(String::from("RB 23")),
1802          Cell::new(String::from("RB 24")),
1803      ])
1804      .add_row(vec![
1805          Cell::new(&(format!("{}", counters[&20]))),
1806          Cell::new(&(format!("{}", counters[&21]))),
1807          Cell::new(&(format!("{}", counters[&22]))),
1808          Cell::new(&(format!("{}", counters[&23]))),
1809          Cell::new(&(format!("{}", counters[&24]))),
1810      ])
1811      .add_row(vec![
1812          Cell::new(String::from("RB 25")),
1813          Cell::new(String::from("RB 26")),
1814          Cell::new(String::from("RB 27")),
1815          Cell::new(String::from("RB 28")),
1816          Cell::new(String::from("RB 29")),
1817      ])
1818      .add_row(vec![
1819          Cell::new(&(format!("{}", counters[&25]))),
1820          Cell::new(&(format!("{}", counters[&26]))),
1821          Cell::new(&(format!("{}", counters[&27]))),
1822          Cell::new(&(format!("{}", counters[&28]))),
1823          Cell::new(&(format!("{}", counters[&29]))),
1824      ])
1825      .add_row(vec![
1826          Cell::new(String::from("RB 30")),
1827          Cell::new(String::from("RB 31")),
1828          Cell::new(String::from("RB 32")),
1829          Cell::new(String::from("RB 33")),
1830          Cell::new(String::from("RB 34")),
1831      ])
1832      .add_row(vec![
1833          Cell::new(&(format!("{}", counters[&30]))),
1834          Cell::new(&(format!("{}", counters[&31]))),
1835          Cell::new(&(format!("{}", counters[&32]))),
1836          Cell::new(&(format!("{}", counters[&33]))),
1837          Cell::new(&(format!("{}", counters[&34]))),
1838      ])
1839      .add_row(vec![
1840          Cell::new(String::from("RB 35")),
1841          Cell::new(String::from("RB 36")),
1842          Cell::new(String::from("RB 37")),
1843          Cell::new(String::from("RB 38")),
1844          Cell::new(String::from("RB 39")),
1845      ])
1846      .add_row(vec![
1847          Cell::new(&(format!("{}", counters[&35]))),
1848          Cell::new(&(format!("{}", counters[&36]))),
1849          Cell::new(&(format!("{}", counters[&37]))),
1850          Cell::new(&(format!("{}", counters[&38]))),
1851          Cell::new(&(format!("{}", counters[&39]))),
1852      ])
1853      .add_row(vec![
1854          Cell::new(String::from("RB 40")),
1855          Cell::new(String::from("RB 41")),
1856          Cell::new(String::from("RB 42")),
1857          Cell::new(String::from("RB 43")),
1858          Cell::new(String::from("RB 44")),
1859      ])
1860      .add_row(vec![
1861          Cell::new(&(format!("{}", counters[&40]))),
1862          Cell::new(&(format!("{}", counters[&41]))),
1863          Cell::new(&(format!("{}", counters[&42]))),
1864          Cell::new(&(format!("{}", counters[&43]))),
1865          Cell::new(&(format!("{}", counters[&44]))),
1866      ])
1867      .add_row(vec![
1868          Cell::new(String::from("RB 45")),
1869          Cell::new(String::from("RB 46")),
1870          Cell::new(String::from("RB 47")),
1871          Cell::new(String::from("RB 48")),
1872          Cell::new(String::from("RB 49")),
1873      ])
1874      .add_row(vec![
1875          Cell::new(&(format!("{}", counters[&45]))),
1876          Cell::new(&(format!("{}", counters[&46]))),
1877          Cell::new(&(format!("{}", counters[&47]))),
1878          Cell::new(&(format!("{}", counters[&48]))),
1879          Cell::new(&(format!("{}", counters[&49]))),
1880      ]);
1881
1882    // Set the default alignment for the third column to right
1883    let column = table.column_mut(2).expect("Our table has three columns");
1884    column.set_cell_alignment(CellAlignment::Right);
1885    println!("{table}");
1886    Ok(counters)
1887  }
1888  
1889  /// Reset all the RB counters
1890  fn reset_rb_counters(&mut self) -> PyResult<()> {
1891    println!("{}", RB_CNTS_RESET);
1892    match RB_CNTS_RESET.set(&mut self.ipbus, 1) {
1893      Ok(_) => {
1894        return Ok(());
1895      }
1896      Err(err) => {
1897        return Err(PyValueError::new_err(err.to_string()));
1898      }
1899    }
1900  }
1901
1902  /// Reset all the LTB counters
1903  fn reset_ltb_counters(&mut self) -> PyResult<()> {
1904    match LT_HIT_CNT_RESET.set(&mut self.ipbus, 1) {
1905      Ok(_) => {
1906        return Ok(());
1907      }
1908      Err(err) => {
1909        return Err(PyValueError::new_err(err.to_string()));
1910      }
1911    }
1912  }
1913  
1914  /// Set a channel mask for a LTB. 
1915  ///
1916  /// # Arguments
1917  /// * lt_link : 0-24, dsi/j connection of the LTB on the MTB
1918  /// * mask    : bitmask 1 = ch0 2 = ch1, etc. setting a channel
1919  ///             to 1 will DISABLE the channel!
1920  fn set_ltb_ch_mask(&mut self, lt_link : u8, mask : u8) -> PyResult<()> {
1921    let registers = [LT0_CHMASK, LT1_CHMASK, LT2_CHMASK, LT3_CHMASK, LT4_CHMASK,
1922                     LT5_CHMASK, LT6_CHMASK, LT7_CHMASK, LT8_CHMASK, LT9_CHMASK,
1923                     LT10_CHMASK, LT11_CHMASK, LT12_CHMASK, LT13_CHMASK, LT14_CHMASK,
1924                     LT15_CHMASK, LT16_CHMASK, LT17_CHMASK, LT18_CHMASK, LT19_CHMASK,
1925                     LT20_CHMASK, LT21_CHMASK, LT22_CHMASK, LT23_CHMASK, LT24_CHMASK];
1926    if lt_link as usize > registers.len() {
1927      return Err(PyValueError::new_err(String::from("Mask has to be in range 0-24!")));
1928    }
1929
1930    match registers[lt_link as usize].set(&mut self.ipbus, mask as u32) {
1931      Ok(_) => {
1932        return Ok(());
1933      }
1934      Err(err) => {
1935        return Err(PyValueError::new_err(err.to_string()));
1936      }
1937    }
1938  }
1939
1940  
1941  fn set_trace_suppression(&mut self, trace_sup : bool) -> PyResult<()> {
1942    let read_all_rb : u32;
1943    if trace_sup {
1944      read_all_rb = 0;
1945    } else {
1946      read_all_rb = 1;
1947    }
1948    match RB_READ_ALL_CHANNELS.set(&mut self.ipbus, read_all_rb) {
1949      Ok(_)  => {
1950        Ok(())
1951      }
1952      Err(err) => {
1953        return Err(PyValueError::new_err(err.to_string()));
1954      }
1955    }
1956  }
1957
1958  fn get_trace_suppression(&mut self) -> PyResult<u32> {
1959    match RB_READ_ALL_CHANNELS.get(&mut self.ipbus) {
1960      Ok(cnt) => {
1961        return Ok(cnt);
1962      }
1963      Err(err) => {
1964        return Err(PyValueError::new_err(err.to_string()));
1965      }
1966    }
1967  }
1968  
1969  fn set_total_tof_thresh(&mut self, value : u32) -> PyResult<()> {
1970    match TOTAL_TOF_THRESH.set(&mut self.ipbus, value) {
1971      Ok(_)  => {
1972        Ok(())
1973      }
1974      Err(err) => {
1975        return Err(PyValueError::new_err(err.to_string()));
1976      }
1977    }
1978  }
1979
1980  fn get_total_tof_thresh(&mut self) -> PyResult<u32> {
1981    match TOTAL_TOF_THRESH.get(&mut self.ipbus) {
1982      Ok(cnt) => {
1983        return Ok(cnt);
1984      }
1985      Err(err) => {
1986        return Err(PyValueError::new_err(err.to_string()));
1987      }
1988    }
1989  }
1990  
1991  fn set_inner_tof_thresh(&mut self, value : u32) -> PyResult<()> {
1992    match INNER_TOF_THRESH.set(&mut self.ipbus, value) {
1993      Ok(_) =>  {
1994        return Ok(());
1995      }
1996      Err(err) => {
1997        return Err(PyValueError::new_err(err.to_string()));
1998      }
1999    }
2000  }
2001
2002  fn get_inner_tof_thresh(&mut self) -> PyResult<u32> {
2003    match INNER_TOF_THRESH.get(&mut self.ipbus) {
2004      Ok(cnt) => {
2005        return Ok(cnt);
2006      }
2007      Err(err) => {
2008        return Err(PyValueError::new_err(err.to_string()));
2009      }
2010    }
2011  }
2012
2013  fn set_outer_tof_thresh(&mut self, value : u32) -> PyResult<()> {
2014    match OUTER_TOF_THRESH.set(&mut self.ipbus, value) {
2015      Ok(_) =>  {
2016        return Ok(());
2017      }
2018      Err(err) => {
2019        return Err(PyValueError::new_err(err.to_string()));
2020      }
2021    }
2022  }
2023
2024  fn get_outer_tof_thresh(&mut self) -> PyResult<u32> {
2025    match OUTER_TOF_THRESH.get(&mut self.ipbus) {
2026      Ok(cnt) => {
2027        return Ok(cnt);
2028      }
2029      Err(err) => {
2030        return Err(PyValueError::new_err(err.to_string()));
2031      }
2032    }
2033  }
2034
2035  fn set_cube_side_thresh(&mut self, value : u32) -> PyResult<()> {
2036    match CUBE_SIDE_THRESH.set(&mut self.ipbus, value) {
2037      Ok(_) =>  {
2038        return Ok(());
2039      }
2040      Err(err) => {
2041        return Err(PyValueError::new_err(err.to_string()));
2042      }
2043    }
2044  }
2045
2046  fn get_cube_side_thresh(&mut self) -> PyResult<u32> {
2047    match CUBE_SIDE_THRESH.get(&mut self.ipbus) {
2048      Ok(cnt) => {
2049        return Ok(cnt);
2050      }
2051      Err(err) => {
2052        return Err(PyValueError::new_err(err.to_string()));
2053      }
2054    }
2055  }
2056
2057  fn set_cube_top_thresh(&mut self, value : u32) -> PyResult<()> {
2058    match CUBE_TOP_THRESH.set(&mut self.ipbus, value) {
2059      Ok(_) =>  {
2060        return Ok(());
2061      }
2062      Err(err) => {
2063        return Err(PyValueError::new_err(err.to_string()));
2064      }
2065    }
2066  }
2067
2068  fn get_cube_top_thresh(&mut self) -> PyResult<u32> {
2069    match CUBE_TOP_THRESH.get(&mut self.ipbus) {
2070      Ok(cnt) => {
2071        return Ok(cnt);
2072      }
2073      Err(err) => {
2074        return Err(PyValueError::new_err(err.to_string()));
2075      }
2076    }
2077  }
2078
2079  fn set_cube_bot_thresh(&mut self, value : u32) -> PyResult<()> {
2080    match CUBE_BOT_THRESH.set(&mut self.ipbus, value) {
2081      Ok(_) =>  {
2082        return Ok(());
2083      }
2084      Err(err) => {
2085        return Err(PyValueError::new_err(err.to_string()));
2086      }
2087    }
2088  }
2089
2090  fn get_cube_bot_thresh(&mut self) -> PyResult<u32> {
2091    match CUBE_BOT_THRESH.get(&mut self.ipbus) {
2092      Ok(cnt) => {
2093        return Ok(cnt);
2094      }
2095      Err(err) => {
2096        return Err(PyValueError::new_err(err.to_string()));
2097      }
2098    }
2099  }
2100
2101  fn set_cube_corner_thresh(&mut self, value : u32) -> PyResult<()> {
2102    match CUBE_CORNER_THRESH.set(&mut self.ipbus, value) {
2103      Ok(_) =>  {
2104        return Ok(());
2105      }
2106      Err(err) => {
2107        return Err(PyValueError::new_err(err.to_string()));
2108      }
2109    }
2110  }
2111
2112  fn get_cube_corner_thresh(&mut self) -> PyResult<u32> {
2113    match CUBE_CORNER_THRESH.get(&mut self.ipbus) {
2114      Ok(cnt) => {
2115        return Ok(cnt);
2116      }
2117      Err(err) => {
2118        return Err(PyValueError::new_err(err.to_string()));
2119      }
2120    }
2121  }
2122 
2123  fn set_umbrella_thresh(&mut self, value : u32) -> PyResult<()> {
2124    match UMBRELLA_THRESH.set(&mut self.ipbus, value) {
2125      Ok(_) =>  {
2126        return Ok(());
2127      }
2128      Err(err) => {
2129        return Err(PyValueError::new_err(err.to_string()));
2130      }
2131    }
2132  }
2133
2134  fn get_umbrella_thresh(&mut self) -> PyResult<u32> {
2135    match UMBRELLA_THRESH.get(&mut self.ipbus) {
2136      Ok(cnt) => {
2137        return Ok(cnt);
2138      }
2139      Err(err) => {
2140        return Err(PyValueError::new_err(err.to_string()));
2141      }
2142    }
2143  }
2144
2145  fn set_umbrella_center_thresh(&mut self, value : u32) -> PyResult<()> {
2146    match UMBRELLA_CENTER_THRESH.set(&mut self.ipbus, value) {
2147      Ok(_) =>  {
2148        return Ok(());
2149      }
2150      Err(err) => {
2151        return Err(PyValueError::new_err(err.to_string()));
2152      }
2153    }
2154  }
2155
2156  fn get_umbrella_center_thresh(&mut self) -> PyResult<u32> {
2157    match UMBRELLA_CENTER_THRESH.get(&mut self.ipbus) {
2158      Ok(cnt) => {
2159        return Ok(cnt);
2160      }
2161      Err(err) => {
2162        return Err(PyValueError::new_err(err.to_string()));
2163      }
2164    }
2165  }
2166
2167  fn set_cortina_thresh(&mut self, value : u32) -> PyResult<()> {
2168    match CORTINA_THRESH.set(&mut self.ipbus, value) {
2169      Ok(_) =>  {
2170        return Ok(());
2171      }
2172      Err(err) => {
2173        return Err(PyValueError::new_err(err.to_string()));
2174      }
2175    }
2176  }
2177  
2178  fn get_cortina_thresh(&mut self) -> PyResult<u32> {
2179    match CORTINA_THRESH.get(&mut self.ipbus) {
2180      Ok(cnt) => {
2181        return Ok(cnt);
2182      }
2183      Err(err) => {
2184        return Err(PyValueError::new_err(err.to_string()));
2185      }
2186    }
2187  }
2188
2189  fn set_configurable_trigger(&mut self, value : u32) -> PyResult<()> {
2190    match CONFIGURABLE_TRIGGER_EN.set(&mut self.ipbus, value) {
2191      Ok(_) => {
2192        return Ok(());
2193      }
2194      Err(err) => {
2195        return Err(PyValueError::new_err(err.to_string()));
2196      }
2197    }
2198  }
2199  
2200  fn get_configurable_trigger(&mut self) -> PyResult<u32> {
2201    match CONFIGURABLE_TRIGGER_EN.get(&mut self.ipbus) {
2202      Ok(cnt) => {
2203        return Ok(cnt);
2204      }
2205      Err(err) => {
2206        return Err(PyValueError::new_err(err.to_string()));
2207      }
2208    }
2209  }
2210
2211  fn set_any_trigger(&mut self, prescale : u32) -> PyResult<()> {
2212    match ANY_TRIG_PRESCALE.set(&mut self.ipbus, prescale) {
2213      Ok(_) =>  {
2214        return Ok(());
2215      }
2216      Err(err) => {
2217        return Err(PyValueError::new_err(err.to_string()));
2218      }
2219    }
2220  }
2221
2222  fn set_track_trigger(&mut self, prescale : u32) -> PyResult<()> {
2223    match TRACK_TRIG_PRESCALE.set(&mut self.ipbus, prescale) {
2224      Ok(_) =>  {
2225        return Ok(());
2226      }
2227      Err(err) => {
2228        return Err(PyValueError::new_err(err.to_string()));
2229      }
2230    }
2231  }
2232  
2233  fn set_central_track_trigger(&mut self, prescale : u32) -> PyResult<()> {
2234    match TRACK_CENTRAL_PRESCALE.set(&mut self.ipbus, prescale) {
2235      Ok(_) =>  {
2236        return Ok(());
2237      }
2238      Err(err) => {
2239        return Err(PyValueError::new_err(err.to_string()));
2240      }
2241    }
2242  }
2243
2244  fn use_tiu_aux_link(&mut self, use_it : bool) -> PyResult<()> {
2245    match control::use_tiu_aux_link(&mut self.ipbus, use_it) {
2246      Ok(_) => {
2247        return Ok(());
2248      }
2249      Err(err) => {
2250        return Err(PyValueError::new_err(err.to_string()));
2251      }
2252    }
2253  }
2254
2255  fn stop_all_triggers(&mut self) -> PyResult<()> {
2256    match control::unset_all_triggers(&mut self.ipbus) {
2257      Ok(_) => {
2258        return Ok(());
2259      }
2260      Err(err) => {
2261        return Err(PyValueError::new_err(err.to_string()));
2262      }
2263    }
2264  }
2265
2266  fn set_umbcube_trigger(&mut self) -> PyResult<()> {
2267    match control::set_umbcube_trigger(&mut self.ipbus) {
2268      Ok(_) => {
2269        return Ok(());
2270      }
2271      Err(err) => {
2272        return Err(PyValueError::new_err(err.to_string()));
2273      }
2274    }
2275  }
2276  
2277  fn set_umbcubez_trigger(&mut self) -> PyResult<()> {
2278    match control::set_umbcubez_trigger(&mut self.ipbus) {
2279      Ok(_) => {
2280        return Ok(());
2281      }
2282      Err(err) => {
2283        return Err(PyValueError::new_err(err.to_string()));
2284      }
2285    }
2286  }
2287
2288  fn set_umbcorcube_trigger(&mut self) -> PyResult<()> {
2289    match control::set_umbcorcube_trigger(&mut self.ipbus) {
2290      Ok(_) => {
2291        return Ok(());
2292      }
2293      Err(err) => {
2294        return Err(PyValueError::new_err(err.to_string()));
2295      }
2296    }
2297  }
2298
2299  fn set_corcubeside_trigger(&mut self) -> PyResult<()> {
2300    match control::set_corcubeside_trigger(&mut self.ipbus) {
2301      Ok(_) => {
2302        return Ok(());
2303      }
2304      Err(err) => {
2305        return Err(PyValueError::new_err(err.to_string()));
2306      }
2307    }
2308  }
2309  
2310  fn set_umb3cube_trigger(&mut self) -> PyResult<()> {
2311    match control::set_umb3cube_trigger(&mut self.ipbus) {
2312      Ok(_) => {
2313        return Ok(());
2314      }
2315      Err(err) => {
2316        return Err(PyValueError::new_err(err.to_string()));
2317      }
2318    }
2319  }
2320
2321  #[getter]
2322  fn get_tiu_busy_ignore(&mut self) -> PyResult<bool> {
2323    match TIU_BUSY_IGNORE.get(&mut self.ipbus) {
2324      Ok(bsy) => {
2325        let res = bsy != 0;
2326        return Ok(res);
2327      }
2328      Err(err) => {
2329        return Err(PyValueError::new_err(err.to_string()));
2330      }
2331    }
2332  }
2333  
2334  #[setter]
2335  fn set_tiu_busy_ignore(&mut self, bsy : bool) -> PyResult<()> {
2336    match TIU_BUSY_IGNORE.set(&mut self.ipbus, bsy as u32) {
2337      Ok(_) => {
2338        return Ok(());
2339      }
2340      Err(err) => {
2341        return Err(PyValueError::new_err(err.to_string()));
2342      }
2343    }
2344  }
2345
2346  #[getter]
2347  fn get_tiu_busy_stuck(&mut self) -> PyResult<bool> {
2348    match TIU_BUSY_STUCK.get(&mut self.ipbus) {
2349      Ok(value) => {
2350        return Ok(value > 0);
2351      }
2352      Err(err) => {
2353        return Err(PyValueError::new_err(err.to_string()));
2354      }
2355    }
2356  }
2357
2358  #[getter]
2359  fn get_tiu_bad(&mut self) -> PyResult<bool> {
2360    match TIU_BAD.get(&mut self.ipbus) {
2361      Ok(value) => {
2362        return Ok(value > 0);
2363      }
2364      Err(err) => {
2365        return Err(PyValueError::new_err(err.to_string()));
2366      }
2367    }
2368  }
2369
2370  fn get_event_cnt(&mut self) -> PyResult<u32> {
2371    match EVENT_CNT.get(&mut self.ipbus) {
2372      Ok(cnt) => {
2373        return Ok(cnt);
2374      }
2375      Err(err) => {
2376        return Err(PyValueError::new_err(err.to_string()));
2377      }
2378    }
2379  }
2380  
2381  fn get_event_queue_size(&mut self)
2382    -> PyResult<u32> {
2383    match EVQ_SIZE.get(&mut self.ipbus) {
2384      Ok(cnt) => {
2385        return Ok(cnt);
2386      }
2387      Err(err) => {
2388        return Err(PyValueError::new_err(err.to_string()));
2389      }
2390    }
2391  }
2392  
2393  fn get_event_queue_full(&mut self)
2394    -> PyResult<u32> {
2395    match EVQ_FULL.get(&mut self.ipbus) {
2396      Ok(cnt) => {
2397        return Ok(cnt);
2398      }
2399      Err(err) => {
2400        return Err(PyValueError::new_err(err.to_string()));
2401      }
2402    }
2403  }
2404
2405  fn get_nevents_in_queue(&mut self) 
2406    -> PyResult<u32> {
2407    match EVQ_NUM_EVENTS.get(&mut self.ipbus) {
2408      Ok(cnt) => {
2409        return Ok(cnt);
2410      }
2411      Err(err) => {
2412        return Err(PyValueError::new_err(err.to_string()));
2413      }
2414    }
2415  }
2416
2417  
2418  #[pyo3(name="get_event")]
2419  fn get_event_py(&mut self, read_until_footer : bool, verbose : bool, debug : bool)
2420    -> PyResult<TofEvent> {
2421    let use_dbg_version = debug;
2422    if !use_dbg_version {
2423      //let mut event = TofEvent::new();
2424      match get_event(&mut self.ipbus) {
2425        None => {
2426          // we just return an empty event!
2427          warn!("Did not get an event, returning empty event!");
2428          let event = TofEvent::new();
2429          Ok(event)
2430        }
2431        Some(Err(err)) => {
2432          //error!("Unable to obtain event from the MTB!");
2433          return Err(PyValueError::new_err(err.to_string()));
2434        }
2435        Some(Ok(mte)) => {
2436          Ok(mte)
2437        }
2438      }
2439    } else {
2440      // This can be great for debugging. However, at some point 
2441      // I'd like to introduce debugging features and have all 
2442      // the debugging at the same place
2443      let mut n_daq_words : u16;
2444      let mut n_daq_words_actual : u16;
2445      loop {
2446        match EVQ_NUM_EVENTS.get(&mut self.ipbus) {
2447          Err(_err) => {
2448            continue;
2449          }
2450          Ok(nevents_in_q) => {
2451            if nevents_in_q == 0 {
2452              if verbose {
2453                println!("[MasterTrigger::get_event] => EventQueue empty!!");
2454              }
2455              return Err(PyValueError::new_err(String::from("<MasterTriggerError: EventQueueEmpty>")));
2456            }
2457          }
2458        }
2459        match self.ipbus.read(0x13) { 
2460          Err(_err) => {
2461            // A timeout does not ncecessarily mean that there 
2462            // is no event, it can also just mean that 
2463            // the rate is low.
2464            //trace!("Timeout in read_register for MTB! {err}");
2465            continue;
2466          },
2467          Ok(_n_words) => {
2468            n_daq_words = (_n_words >> 16) as u16;
2469            if _n_words == 0 {
2470              continue;
2471            }
2472            //trace!("Got n_daq_words {n_daq_words}");
2473            let rest = n_daq_words % 2;
2474            n_daq_words /= 2 + rest; //mtb internally operates in 16bit words, but 
2475            //                  //registers return 32bit words.
2476            
2477            break;
2478          }
2479        }
2480      }
2481      let mut data : Vec<u32>;
2482      if verbose {
2483        println!("[MasterTrigger::get_event] => Will query DAQ for {n_daq_words} words!");
2484      }
2485      n_daq_words_actual = n_daq_words;
2486      match self.ipbus.read_multiple(
2487                                     0x11,
2488                                     n_daq_words as usize,
2489                                     false) {
2490        Err(err) => {
2491          if verbose {
2492            println!("[MasterTrigger::get_event] => failed! {err}");
2493          }
2494          return Err(PyValueError::new_err(err.to_string()));
2495        }
2496        Ok(_data) => {
2497          data = _data;
2498          for (i,word) in data.iter().enumerate() {
2499            let desc : &str;
2500            let desc_str : String;
2501            //let mut nhit_words = 0;
2502            match i {
2503              0 => desc = "HEADER",
2504              1 => desc = "EVENTID",
2505              2 => desc = "TIMESTAMP",
2506              3 => desc = "TIU_TIMESTAMP",
2507              4 => desc = "TIU_GPS32",
2508              5 => desc = "TIU_GPS16 + TRIG_SOURCE",
2509              6 => desc = "RB MASK 0",
2510              7 => desc = "RB MASK 1",
2511              8 => {
2512                //nhit_words = nhit_words / 2 + nhit_words % 2;
2513                desc_str  = format!("BOARD MASK ({} ltbs)", word.count_ones());
2514                desc  = &desc_str;
2515              },
2516              _ => desc = "?"
2517            }
2518            if verbose {
2519              println!("[MasterTrigger::get_event] => DAQ word {}    \t({:x})    \t[{}]", word, word, desc);
2520            }
2521          }
2522        }
2523      }
2524      if data[0] != 0xAAAAAAAA {
2525        if verbose {
2526          println!("[MasterTrigger::get_event] => Got MTB data, but the header is incorrect {}", data[0]);
2527        }
2528        return Err(PyValueError::new_err(String::from("Incorrect header value!")));
2529      }
2530      let foot_pos = (n_daq_words - 1) as usize;
2531      if data.len() <= foot_pos {
2532        if verbose {
2533          println!("[MasterTrigger::get_event] => Got MTB data, but the format is not correct");
2534        }
2535        return Err(PyValueError::new_err(String::from("Empty data!")));
2536      }
2537      if data[foot_pos] != 0x55555555 {
2538        if verbose {
2539          println!("[MasterTrigger::get_event] => Did not read unti footer!");
2540        }
2541        if read_until_footer {
2542          if verbose {
2543            println!("[MasterTrigger::get_event] => .. will read additional words!");
2544          }
2545          loop {
2546            match self.ipbus.read(0x11) {
2547              Err(err) => {
2548                if verbose {
2549                  println!("[MasterTrigger::get_event] => Issues reading from 0x11");
2550                }
2551                return Err(PyValueError::new_err(err.to_string()));
2552              },
2553              Ok(next_word) => {
2554                n_daq_words_actual += 1;
2555                data.push(next_word);
2556                if next_word == 0x55555555 {
2557                  break;
2558                }
2559              }
2560            }
2561          }
2562          if verbose {
2563            println!("[MasterTrigger::get_event] => We read {} additional words!", n_daq_words_actual - n_daq_words);
2564          }
2565        } else {
2566          if verbose {
2567            println!("[MasterTrigger::get_event] => Got MTB data, but the footer is incorrect {}", data[foot_pos]);
2568          }
2569          return Err(PyValueError::new_err(String::from("Footer incorrect!")));
2570        }
2571      }
2572
2573      // Number of words which will be always there. 
2574      // Min event size is +1 word for hits
2575      //const MTB_DAQ_PACKET_FIXED_N_WORDS : u32 = 9; 
2576      //let n_hit_packets = n_daq_words as u32 - MTB_DAQ_PACKET_FIXED_N_WORDS;
2577      //println!("We are expecting {}", n_hit_packets);
2578      let mut mte          = TofEvent::new();
2579      mte.event_id         = data[1];
2580      mte.mt_timestamp     = data[2];
2581      mte.mt_tiu_timestamp = data[3];
2582      mte.mt_tiu_gps32     = data[4];
2583      mte.mt_tiu_gps16     = (data[5] & 0x0000ffff) as u16;
2584      mte.trigger_sources  = ((data[5] & 0xffff0000) >> 16) as u16;
2585      //mte.get_trigger_sources();
2586      let rbmask = (data[7] as u64) << 31 | data[6] as u64; 
2587      mte.mtb_link_mask  = rbmask;
2588      mte.dsi_j_mask     = data[8];
2589      let mut n_hit_words    = n_daq_words_actual - 9 - 2; // fixed part is 11 words
2590      if n_hit_words > n_daq_words_actual {
2591        n_hit_words = 0;
2592        println!("[MasterTrigger::get_event] N hit word calculation failed! fixing... {}", n_hit_words);
2593      }
2594      if verbose {
2595        println!("[MasterTrigger::get_event] => Will read {} hit word", n_hit_words);
2596      }
2597      for k in 1..n_hit_words+1 {
2598        if verbose {
2599          println!("[MasterTrigger::get_event] => Getting word {}", k);
2600        }
2601        let first  = (data[8 + k as usize] & 0x0000ffff) as u16;
2602        let second = ((data[8 + k as usize] & 0xffff0000) >> 16) as u16; 
2603        mte.channel_mask.push(first);
2604        if second != 0 {
2605          mte.channel_mask.push(second);
2606        }
2607      }
2608      if verbose {
2609        println!("[MasterTrigger::get_event] => Got MTE \n{}", mte);
2610      }
2611      let event = TofEvent::new();
2612      Ok(event)
2613    }
2614  }
2615}
2616
2617