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