Skip to main content

gondola_core/
database.rs

1//! Database access & entities for gaps-online-software
2//!
3//! A local .sqlite database is shipped with gaps-online-software,
4//! pre-populated with relevant meta data for the GAPS experiment.
5// This file is part of gaps-online-software and published 
6// under the GPLv3 license
7
8use crate::prelude::*;
9
10use std::fs::File;
11use std::io::{self, BufRead, BufReader};
12use std::path::Path;
13
14use diesel::prelude::*;
15use diesel::FromSqlRow;
16use diesel::AsExpression;
17use diesel::serialize;
18use diesel::deserialize;
19use diesel::sqlite::Sqlite;
20use diesel::deserialize::FromSql;
21use diesel::serialize::Output;
22use diesel::sql_types::Integer;
23use diesel::serialize::ToSql;
24use diesel::backend::Backend;
25
26mod schema;    
27use schema::tof_db_rat::dsl::*;
28
29mod tracker_mask;
30pub use tracker_mask::{
31  TrackerStripMask,
32  create_trk_mask_table
33};
34
35mod tracker_pedestal;
36pub use tracker_pedestal::create_trk_pedestal_table;
37
38mod tracker_transfer_fn;
39pub use tracker_transfer_fn::{
40  TrackerStripTransferFunction,
41  create_trk_transfer_fn_table
42};
43
44mod tracker_gain;
45pub use tracker_gain::{
46  TrackerStripGain,
47  create_trk_gain_table
48};
49
50mod tracker_pulse;
51pub use tracker_pulse::{
52  TrackerStripPulse,
53  create_trk_pulse_table
54};
55
56/// Low gain/LTB connections to paddle ID 
57pub type DsiJChPidMapping = HashMap<u8, HashMap<u8, HashMap<u8, (u8, u8)>>>;
58/// Low gain/LTB connections to rb ID 
59pub type DsiJChRbMapping = HashMap<u8, HashMap<u8, HashMap<u8, u8>>>;
60/// RB ID and RB Ch to paddle ID mapping 
61pub type RbChPidMapping = HashMap<u8, HashMap<u8, u8>>;
62
63//--------------------------------------------------------------
64
65/// Describe the contents of a byte sequence ("packet") typicially 
66/// crafted for telemetry. The numbers are defined by the 'bfsw'
67/// software package.
68#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, FromRepr, AsRefStr, EnumIter,AsExpression, FromSqlRow)]
69#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
70#[diesel(sql_type = Integer)]
71pub enum TrackerCalibrationFileType {
72  Unknown        = 0,
73  Pedestal       = 200,
74  TransferFn     = 201,
75  ChannelMask    = 202,
76  PulsedChannels = 203,
77  Gains          = 204,
78  Any            = 255,
79}
80
81impl ToSql<Integer, Sqlite> for TrackerCalibrationFileType {
82  fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
83    out.set_value(*self as i32);
84    Ok(serialize::IsNull::No)
85  }
86}
87
88impl FromSql<Integer, Sqlite> for TrackerCalibrationFileType {
89  fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
90    let v = i32::from_sql(bytes)?;
91    match v {
92      0   => Ok(TrackerCalibrationFileType::Unknown),
93      200 => Ok(TrackerCalibrationFileType::Pedestal),
94      201 => Ok(TrackerCalibrationFileType::TransferFn),
95      202 => Ok(TrackerCalibrationFileType::ChannelMask),
96      203 => Ok(TrackerCalibrationFileType::PulsedChannels),
97      204 => Ok(TrackerCalibrationFileType::Gains),
98      255 => Ok(TrackerCalibrationFileType::Any),
99      _ => {
100        error!("Unable to understand type {v}");
101        return Err("Unrecognized calibration file type".into());
102      }
103    }
104  }
105}
106
107impl fmt::Display for TrackerCalibrationFileType {
108  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109    let mut repr = String::from("<TrackerCalFileType::");
110    match self {
111      Self::Unknown        => { repr += "Unknown>";}
112      Self::Pedestal       => { repr += "Pedestal>";}
113      Self::TransferFn     => { repr += "TransferFn>";}
114      Self::ChannelMask    => { repr += "ChannelMask>";}
115      Self::PulsedChannels => { repr += "PulsedChannels>";}
116      Self::Gains          => { repr += "Gains>";}
117      Self::Any            => { repr += "Any>";}
118    }
119    write!(f, "{}", repr)
120  }
121}
122
123//expand_and_test_enum!(TrackerCalibrationFileType, test_telemetrypackettype_repr);
124
125//--------------------------------------------------------------
126
127/// This loads the calbration DB for SimpleDet as provided by Elena 
128/// and INFN. 
129///
130/// This calibration db is basically a list of files and timestamps
131/// Exitence of these paths is not guaranteed.
132#[cfg_attr(feature="pybindings", pyfunction)]
133#[cfg_attr(feature="pybindings", pyo3(signature = (db_path, cali_ftype = TrackerCalibrationFileType::Any)))]
134pub fn load_calibration_db_elena(db_path : &str, cali_ftype : TrackerCalibrationFileType) -> Option<Vec<TrackerCalibrationFile>> {
135  use schema::calibration_files::dsl::*;
136  info!("Will load SD calibration DB from {}", db_path);
137  let mut conn = SqliteConnection::establish(db_path).ok()?;
138  match calibration_files.load::<TrackerCalibrationFile>(&mut conn) {
139    Err(err) => {
140      error!("Unable to load tracker calibration files from db! {err}");
141      return None;
142    }
143    Ok(mut files) => {
144      match cali_ftype {
145        TrackerCalibrationFileType::Any => {
146          return Some(files);
147        }
148        _ => {
149          files.retain(|x| x.file_type == cali_ftype);            
150          return Some(files);
151        }
152      }
153    }
154  }
155}
156
157//--------------------------------------------------------------
158
159
160/// Connect to a database at a given location
161pub fn connect_to_db_path(db_path : &str) -> Result<diesel::SqliteConnection, ConnectionError> {
162  info!("Will set GONDOLA_DB_URL to {}", db_path);
163  warn!("Setting environment variables is not thread safe!");
164  unsafe {
165    //env::set_var("GONDOLA_DB_URL", db_path);
166    env::set_var("GONDOLA_DB_URL", db_path);
167  }
168  SqliteConnection::establish(db_path)
169}
170
171//---------------------------------------------------------------------
172
173/// Connect to the default database at the standard location
174pub fn connect_to_db() -> Result<diesel::SqliteConnection, ConnectionError>  {
175  let db_path  = env::var("GONDOLA_DB_URL").unwrap_or_else(|_| "".to_string());
176  if db_path == "" {
177    error!("Empty GONDOLA_DB_URL. Did you forget to load the gaps-online-software setup-env.sh shell?");
178  }
179  SqliteConnection::establish(&db_path)
180}
181
182//---------------------------------------------------------------------
183
184/// Create a mapping of DSI/J(LTB) -> PaddleID
185///
186/// This will basically tell you for a given LTB hit which paddle has 
187/// triggered.
188pub fn get_dsi_j_ch_pid_map(paddles : &Vec<TofPaddle>) -> DsiJChPidMapping {
189  let mut mapping = DsiJChPidMapping::new();
190  for dsi in 1..6 {
191    let mut jmap = HashMap::<u8, HashMap<u8, (u8, u8)>>::new();
192    for j in 1..6 {
193      let mut rbidch_map : HashMap<u8, (u8,u8)> = HashMap::new();
194      for ch in 1..17 {
195        let rbidch = (0,0);
196        rbidch_map.insert(ch,rbidch);
197        //map[dsi] = 
198      }
199      jmap.insert(j,rbidch_map);
200    }
201    mapping.insert(dsi,jmap);
202  }
203  for pdl in paddles {
204    let dsi  = pdl.dsi as u8;
205    let   j  = pdl.j_ltb   as u8;
206    let ch_a = pdl.ltb_chA as u8;
207    let ch_b = pdl.ltb_chB as u8;
208    let pid  = pdl.paddle_id as u8;
209    let panel_id = pdl.panel_id as u8;
210    mapping.get_mut(&dsi).unwrap().get_mut(&j).unwrap().insert(ch_a,(pid, panel_id));
211    mapping.get_mut(&dsi).unwrap().get_mut(&j).unwrap().insert(ch_b,(pid, panel_id));
212  }
213  return mapping;
214}
215
216//---------------------------------------------------------------------
217
218/// Create a mapping of DSI/J(LTB) -> RBID
219///
220/// This will basically tell you for a given LTB hit which rb has 
221/// triggered.
222pub fn get_dsi_j_ch_rb_map(paddles : &Vec<TofPaddle>) -> DsiJChRbMapping {
223  let mut mapping = DsiJChRbMapping::new();
224  for dsi in 1..6 {
225    let mut jmap = HashMap::<u8, HashMap<u8, u8>>::new();
226    for j in 1..6 {
227      let mut rbidch_map : HashMap<u8, u8> = HashMap::new();
228      for ch in 1..17 {
229        let rbidch = 0u8;
230        rbidch_map.insert(ch,rbidch);
231        //map[dsi] = 
232      }
233      jmap.insert(j,rbidch_map);
234    }
235    mapping.insert(dsi,jmap);
236  }
237  for pdl in paddles {
238    let dsi    = pdl.dsi as u8;
239    let   j    = pdl.j_ltb   as u8;
240    let ch_a   = pdl.ltb_chA as u8;
241    let ch_b   = pdl.ltb_chB as u8;
242    let rb_id  = pdl.paddle_id as u8;
243    mapping.get_mut(&dsi).unwrap().get_mut(&j).unwrap().insert(ch_a,rb_id);
244    mapping.get_mut(&dsi).unwrap().get_mut(&j).unwrap().insert(ch_b,rb_id);
245  }
246  return mapping;
247}
248
249//---------------------------------------------------------------------
250
251/// Get a mapping of which pb controls paddles connected to which RB
252#[cfg_attr(feature="pybindings", pyfunction)]
253pub fn get_rbids_for_pbid(pbid : u8) -> Option<(u8, u8)> {
254  let mut conn = connect_to_db().ok()?;
255  let rats = RAT::all(&mut conn)?;
256  for rat in rats {
257    if rat.pb_id as u8 == pbid {
258      return Some((rat.rb1_id as u8, rat.rb2_id as u8));
259    }
260  }
261  None
262}
263
264pub fn get_rbid_pbchannel_pid_map(paddles : &Vec<TofPaddle>) -> Option<(RbChPidMapping, RbChPidMapping)> {
265  let mut map_a    = RbChPidMapping::new();
266  let mut map_b    = RbChPidMapping::new();
267  let mut map_a_rb = RbChPidMapping::new();
268  let mut map_b_rb = RbChPidMapping::new();
269
270  //let all_rbs       = get_all_rbids_in_db()?; 
271  //let pbch_to_pid_a = HashMap::<u8,u8>::new(); 
272  //let pbch_to_pid_b = HashMap::<u8,u8>::new(); 
273  //let all_pbs       = get_all_pbids_in_db()?;
274  // first we fill the maps with the pb id instead of the rb id, then we 
275  // translate that later
276  for pdl in paddles {
277    if pdl.pb_id == 19 {
278      println!("{}", pdl);
279    }
280    if map_a.contains_key(&(pdl.pb_id as u8)) {
281      map_a.get_mut(&(pdl.pb_id as u8)).unwrap().insert(pdl.pb_chA as u8, pdl.paddle_id as u8);
282    } else {
283      let mut ch_map = HashMap::<u8,u8>::new();
284      ch_map.insert(pdl.pb_chA as u8, pdl.paddle_id as u8);
285      map_a.insert(pdl.pb_id as u8, ch_map);
286    }
287    if map_b.contains_key(&(pdl.pb_id as u8)) {
288      map_b.get_mut(&(pdl.pb_id as u8)).unwrap().insert(pdl.pb_chA as u8, pdl.paddle_id as u8);
289    } else {
290      let mut ch_map = HashMap::<u8,u8>::new();
291      ch_map.insert(pdl.pb_chB as u8, pdl.paddle_id as u8);
292      map_b.insert(pdl.pb_id as u8, ch_map);
293    }
294  }
295  for k in map_a.keys() {
296    match get_rbids_for_pbid(*k) {
297      Some(rbid) => {
298        map_a_rb.insert(rbid.0, map_a[k].clone()); 
299        map_a_rb.insert(rbid.1, map_a[k].clone()); 
300      }
301      None => {
302        println!("Can not get rb ids for pb id {}!", k);
303      }
304    }
305  }
306  for k in map_b.keys() {
307    let rbid = get_rbids_for_pbid(*k).unwrap();
308    map_b_rb.insert(rbid.0, map_b[k].clone()); 
309    map_b_rb.insert(rbid.1, map_b[k].clone()); 
310  }
311  return Some((map_a_rb, map_b_rb))
312}
313
314//---------------------------------------------------------------------
315
316/// Create a mapping of DSI/J(LTB) -> RBID
317///
318/// This will basically tell you for a given LTB hit which rb has 
319/// triggered.
320#[cfg(feature="pybindings")]
321#[pyfunction]
322#[pyo3(name="get_dsi_j_ch_rb_map")]
323pub fn get_dsi_j_ch_rb_map_py() -> Option<DsiJChRbMapping> {
324  if let Some(paddles) = TofPaddle::all() {
325    return Some(get_dsi_j_ch_rb_map(&paddles));
326  } else {
327    return None;
328  }
329}
330
331
332
333
334//---------------------------------------------------------------------
335
336/// Create a mapping of RB/PB Channel -> PaddleID
337///
338/// Find out to which paddle ends the sipms are connected 
339/// on the powerboard. 
340/// Per each RAT, there is one RB which controls the 
341/// PB (powerboard). For a given RB id, find out which 
342/// PB is in the same RAT and return the connected 
343/// paddle id
344///
345/// # Returns:
346///   * tuple(dict (rb_id, dict(pb_ch, pid)),..) 
347///     The returned tuple contains two dictionaries. 
348///     The first is for paddle end A, the second for 
349///     paddle end B
350#[cfg(feature="pybindings")]
351#[pyfunction]
352#[pyo3(name="get_rbid_pbchannel_pid_map")]
353pub fn get_rbid_pbchannel_pid_map_py() -> Option<(RbChPidMapping, RbChPidMapping)> {
354  if let Some(paddles) = TofPaddle::all() {
355    return Some(get_rbid_pbchannel_pid_map(&paddles).unwrap());
356  } else {
357    return None;
358  }
359}
360
361//---------------------------------------------------------------------
362
363/// Create a mapping of DSI/J(LTB) -> PaddleID
364///
365/// This will basically tell you for a given LTB hit which paddle has 
366/// triggered.
367#[cfg(feature="pybindings")]
368#[pyfunction]
369#[pyo3(name="get_dsi_j_ch_pid_map")]
370pub fn get_dsi_j_ch_pid_map_py() -> Option<DsiJChPidMapping> {
371  if let Some(paddles) = TofPaddle::all() {
372    return Some(get_dsi_j_ch_pid_map(&paddles));
373  } else {
374    return None;
375  }
376}
377
378//---------------------------------------------------------------------
379
380/// Map the detector ids ("hardware" ids) to volume ids as used in the 
381/// simulation 
382///
383/// Get the map first and then query it 
384/// 
385/// ```
386/// m = gondola.db.get_hid_vid_maps() 
387/// print (m[0][160]) # volume id for paddle 160
388/// ```  
389/// 
390/// # Returns: 
391///   * tuple (dict,dict) : Two maps, the first for the tof, the second 
392///                         for the tracker 
393#[cfg_attr(feature="pybindings", pyfunction)]
394pub fn get_hid_vid_maps() -> Option<(HashMap<u32, u64>, HashMap<u32, u64>)> {
395  // FIXME - error catching
396  let pdls   = TofPaddle::all_as_dict().unwrap(); 
397  let strips = TrackerStrip::all_as_dict().unwrap();
398  let mut hid_vid_map_tof = HashMap::<u32, u64>::new();
399  let mut hid_vid_map_trk = HashMap::<u32, u64>::new();  
400  for k in pdls.keys() {
401    hid_vid_map_tof.insert(*k as u32, pdls[k].volume_id as u64);
402  }
403  for k in strips.keys() {
404    hid_vid_map_trk.insert(*k as u32,strips[k].volume_id as u64);
405  }
406  Some((hid_vid_map_tof, hid_vid_map_trk))
407}
408
409//---------------------------------------------------------------------
410
411/// Map the volume id as it is used in the simulation to the actual 
412/// ("hardware") detector ids
413///
414/// Get the map first and then query it 
415/// ```
416/// m = gondola.db.get_vid_hid_maps() 
417/// print (m[0][100000000]) # tof paddle 1
418/// ```  
419/// 
420/// # Returns: 
421///   * tuple (dict,dict) : Two maps, the first for the tof, the second 
422///                         for the tracker 
423#[cfg_attr(feature="pybindings", pyfunction)]
424pub fn get_vid_hid_maps() -> Option<(HashMap<u64, u32>, HashMap<u64,u32>)> {
425  // FIXME - error catching
426  let pdls   = TofPaddle::all_as_dict().unwrap(); 
427  let strips = TrackerStrip::all_as_dict().unwrap();
428  let mut vid_hid_map_tof = HashMap::<u64, u32>::new();
429  let mut vid_hid_map_trk = HashMap::<u64, u32>::new();
430  for k in pdls.keys() {
431    vid_hid_map_tof.insert(pdls[k].volume_id as u64, *k as u32);
432  }
433  for k in strips.keys() {
434    vid_hid_map_trk.insert(strips[k].volume_id as u64, *k);
435  }
436  Some((vid_hid_map_tof, vid_hid_map_trk))
437}
438
439//---------------------------------------------------------------------
440
441/// Create a mapping of mtb link ids to rb ids
442//#[cfg_attr(feature="pybindings", pyfunction)]
443pub fn get_linkid_rbid_map(rbs : &Vec<ReadoutBoard>) -> HashMap<u8, u8>{
444  let mut mapping = HashMap::<u8, u8>::new();
445  for rb in rbs {
446    mapping.insert(rb.mtb_link_id, rb.rb_id);
447  }
448  mapping
449}
450
451//---------------------------------------------------------------------
452
453/// Create a map for rbid, ch -> paddle id. This is only for the A 
454/// side and will not have an entry in case the given RB channel
455/// is connected to the B side of the paddle
456pub fn get_rb_ch_pid_a_map() -> Option<RbChPidMapping> {
457  let paddles = TofPaddle::all()?;
458  let mut mapping = RbChPidMapping::new();
459  for rbid  in 1..51 {
460    let mut chmap = HashMap::<u8, u8>::new();
461    for ch in 1..9 {
462      chmap.insert(ch,0);
463    }
464    mapping.insert(rbid,chmap);
465  }
466  for pdl in paddles {
467    let rb_id = pdl.rb_id  as u8;
468    let ch_a  = pdl.rb_chA as u8;
469    let pid   = pdl.paddle_id as u8;
470    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_a).unwrap() = pid;
471  }
472  Some(mapping)
473}
474
475//---------------------------------------------------------------------
476
477/// Create a map for rbid, ch -> paddle id. This is only for the B 
478/// side and will not have an entry in case the given RB channel
479/// is connected to the A side of the paddle
480pub fn get_rb_ch_pid_b_map() -> Option<RbChPidMapping> {
481  let mut mapping = RbChPidMapping::new();
482  let paddles = TofPaddle::all()?;
483  for rbid  in 1..51 {
484    let mut chmap = HashMap::<u8, u8>::new();
485    for ch in 1..9 {
486      chmap.insert(ch,0);
487    }
488    mapping.insert(rbid,chmap);
489  }
490
491  for pdl in paddles {
492    let rb_id = pdl.rb_id  as u8;
493    let ch_b  = pdl.rb_chB as u8;
494    let pid   = pdl.paddle_id as u8;
495    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_b).unwrap() = pid;
496  }
497  Some(mapping)
498}
499
500//---------------------------------------------------------------------
501
502/// Create a map for rbid, ch -> paddle id. 
503///
504/// This version is oblivious to the paddle ends 
505pub fn get_rb_ch_pid() -> Option<RbChPidMapping> {
506  let paddles = TofPaddle::all()?;
507  let mut mapping = RbChPidMapping::new();
508  for rbid  in 1..51 {
509    let mut chmap = HashMap::<u8, u8>::new();
510    for ch in 1..9 {
511      chmap.insert(ch,0);
512    }
513    mapping.insert(rbid,chmap);
514  }
515  for pdl in paddles {
516    let rb_id = pdl.rb_id  as u8;
517    let ch_a  = pdl.rb_chA as u8;
518    let ch_b  = pdl.rb_chB as u8;
519    let pid   = pdl.paddle_id as u8;
520    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_a).unwrap() = pid;
521    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_b).unwrap() = pid;
522  }
523  Some(mapping)
524}
525
526//---------------------------------------------------------------------
527
528/// Get all rb ids from paddles which are stored in the 
529/// database
530#[cfg_attr(feature="pybindings", pyfunction)]
531pub fn get_all_rbids_in_db() -> Option<Vec<u8>> {
532  match TofPaddle::all() {
533    None => {
534      error!("Can not load paddles from DB! Did you load the setup-env.sh shell?");
535      return None;
536    }
537    Some(paddles) => {
538      let mut rbids : Vec<u8> = paddles.iter().map(|p| p.rb_id as u8).collect();
539      rbids.sort();
540      rbids.dedup();
541      return Some(rbids);
542    }
543  }
544}
545
546//---------------------------------------------------------------------
547
548#[cfg_attr(feature="pybindings", pyfunction)]
549pub fn get_all_pbids_in_db() -> Option<Vec<u8>> {
550  match TofPaddle::all() {
551    None => {
552      error!("Can not load paddles from DB! Did you load the setup-env.sh shell?");
553      return None;
554    }
555    Some(paddles) => {
556      let mut rbids : Vec<u8> = paddles.iter().map(|p| p.pb_id as u8).collect();
557      rbids.sort();
558      rbids.dedup();
559      return Some(rbids);
560    }
561  }
562}
563
564//---------------------------------------------------------------------
565
566/// A single TOF paddle with 2 ends comnected
567#[derive(Debug,PartialEq, Clone, Queryable, Selectable, Insertable, serde::Serialize, serde::Deserialize)]
568#[diesel(table_name = schema::tof_db_paddle)]
569#[diesel(primary_key(paddle_id))]
570#[allow(non_snake_case)]
571#[cfg_attr(feature="pybindings", pyclass)]
572pub struct TofPaddle {
573  pub paddle_id         : i16, 
574  pub volume_id         : i64, 
575  pub panel_id          : i16, 
576  pub mtb_link_id       : i16, 
577  pub rb_id             : i16, 
578  pub rb_chA            : i16, 
579  pub rb_chB            : i16, 
580  pub ltb_id            : i16, 
581  pub ltb_chA           : i16, 
582  pub ltb_chB           : i16, 
583  pub pb_id             : i16, 
584  pub pb_chA            : i16, 
585  pub pb_chB            : i16, 
586  pub cable_len         : f32, 
587  pub dsi               : i16, 
588  pub j_rb              : i16, 
589  pub j_ltb             : i16, 
590  pub height            : f32, 
591  pub width             : f32, 
592  pub length            : f32, 
593  pub normal_x          : f32,
594  pub normal_y          : f32,
595  pub normal_z          : f32,
596  pub global_pos_x_l0   : f32, 
597  pub global_pos_y_l0   : f32, 
598  pub global_pos_z_l0   : f32, 
599  pub global_pos_x_l0_A : f32, 
600  pub global_pos_y_l0_A : f32, 
601  pub global_pos_z_l0_A : f32, 
602  pub global_pos_x_l0_B : f32, 
603  pub global_pos_y_l0_B : f32, 
604  pub global_pos_z_l0_B : f32, 
605  pub coax_cable_time   : f32,
606  pub harting_cable_time: f32,
607}
608
609impl TofPaddle {
610  pub fn new() -> Self {
611    Self {
612      paddle_id         : 0, 
613      volume_id         : 0, 
614      panel_id          : 0, 
615      mtb_link_id       : 0, 
616      rb_id             : 0, 
617      rb_chA            : 0, 
618      rb_chB            : 0, 
619      ltb_id            : 0, 
620      ltb_chA           : 0, 
621      ltb_chB           : 0, 
622      pb_id             : 0, 
623      pb_chA            : 0, 
624      pb_chB            : 0, 
625      cable_len         : 0.0, 
626      dsi               : 0, 
627      j_rb              : 0, 
628      j_ltb             : 0, 
629      height            : 0.0, 
630      width             : 0.0, 
631      length            : 0.0, 
632      normal_x          : 0.0,
633      normal_y          : 0.0,
634      normal_z          : 0.0,
635      global_pos_x_l0   : 0.0, 
636      global_pos_y_l0   : 0.0, 
637      global_pos_z_l0   : 0.0, 
638      global_pos_x_l0_A : 0.0, 
639      global_pos_y_l0_A : 0.0, 
640      global_pos_z_l0_A : 0.0, 
641      global_pos_x_l0_B : 0.0, 
642      global_pos_y_l0_B : 0.0, 
643      global_pos_z_l0_B : 0.0,
644      coax_cable_time   : 0.0, 
645      harting_cable_time: 0.0
646    }
647  }
648
649  /// Save myself to the database
650  pub fn save(&self) {
651    use schema::tof_db_paddle::dsl::*;
652    //let conn = connect_to_db().unwrap();
653    let _ = diesel::insert_into(tof_db_paddle)
654      .values(self);
655  }
656
657  /// Return the lowest channel number (either A or B)
658  /// to be able to sort the paddles into RBs
659  pub fn get_lowest_rb_ch(&self) -> u8 {
660    if self.rb_chA < self.rb_chB {
661      return self.rb_chA as u8;
662    }
663    else {
664      return self.rb_chB as u8;
665    }
666  }
667
668  /// Get all TofPaddles which are connected to a certain
669  /// Readoutboard
670  ///
671  /// # Arguments:
672  ///   * rbid : The RB id identifier (1-50)
673  pub fn by_rbid(rbid : u8) -> Option<Vec<TofPaddle>> {
674    if rbid > 50 {
675      error!("We don't have any RBs with an ID > 50!");
676      return None
677    }
678    use schema::tof_db_paddle::dsl::*;
679    let mut conn = connect_to_db().ok()?;
680    let rbid_tmp = rbid as i16;
681
682    let paddles = tof_db_paddle.filter(rb_id.eq(rbid_tmp))
683                  .load::<TofPaddle>(&mut conn);
684
685    match paddles {
686      Err(err) => {
687        error!("Unable to load paddles from db! {err}");
688        return None;
689      }
690      Ok(pdls) => {
691        return Some(pdls);
692      }
693    } 
694  }
695
696  /// Retrieve all 160 paddles from the database 
697  pub fn all() -> Option<Vec<TofPaddle>> {
698    use schema::tof_db_paddle::dsl::*;
699    let mut conn = connect_to_db().ok()?;
700    match tof_db_paddle.load::<TofPaddle>(&mut conn) {
701      Err(err) => {
702        error!("Unable to load paddles from db! {err}");
703        return None;
704      }
705      Ok(pdls) => {
706        return Some(pdls);
707      }
708    }
709  }
710  
711  /// Retrive all paddles from the database, but return a 
712  /// HashMap <paddle_id, TofPaddle>
713  pub fn all_as_dict() -> Result<HashMap<u8,Self>, ConnectionError> {
714    let mut paddles = HashMap::<u8, Self>::new();
715    match Self::all() {
716      None => {
717        error!("We can't find any paddles in the database!");
718        return Ok(paddles);
719      }
720      Some(pdls) => {
721        for p in pdls {
722          paddles.insert(p.paddle_id as u8, p.clone());
723        }
724      }
725    }
726    return Ok(paddles);
727  }
728  
729  /// The principal is the direction along the longest
730  /// dimension from A -> B
731  pub fn principal(&self) -> (f32,f32,f32) {
732    let mut pr = (self.global_pos_x_l0_B - self.global_pos_x_l0_A,
733                  self.global_pos_y_l0_B - self.global_pos_y_l0_A,
734                  self.global_pos_z_l0_B - self.global_pos_z_l0_A);
735    let length = f32::sqrt(pr.0.powf(2.0) + pr.1.powf(2.0) + pr.2.powf(2.0)); 
736    pr = (pr.0/length, pr.1/length, pr.2/length);
737    return pr;
738  }
739
740  /// Normal vector of the paddle 
741  pub fn normal(&self) -> (f32, f32, f32) {
742    (self.normal_x, self.normal_y, self.normal_z)
743  }
744
745  pub fn center_pos(&self) -> (f32,f32,f32) {
746    (self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0)
747  }
748
749  #[allow(non_snake_case)]
750  pub fn sideA_pos(&self) -> (f32,f32,f32) {
751    (self.global_pos_x_l0_A, self.global_pos_y_l0_A, self.global_pos_z_l0_A)
752  }
753  
754  #[allow(non_snake_case)]
755  pub fn sideB_pos(&self) -> (f32,f32,f32) {
756    (self.global_pos_x_l0_B, self.global_pos_y_l0_B, self.global_pos_z_l0_B)
757  }
758
759  ///Convert DSI and J connection to the actual 
760  ///slot they are plugged in on the MTB (0-24)
761  pub fn rb_slot(&self) -> i16 {
762    (self.dsi-1)*5 + self.j_rb - 1
763  }
764  
765  /// Convert DSI and J connection to the actual 
766  /// slot they are plugged in on the MTB (0-24)
767  pub fn lt_slot(&self) -> i16 {
768    (self.dsi-1)*5 + self.j_ltb - 1
769  }
770} 
771
772#[cfg(feature="pybindings")]
773#[pymethods]
774impl TofPaddle {
775 
776  /// Get the (numerically) lower of the two 
777  /// RB channels the TOF paddle is connected with 
778  /// to its associate ReadoutBoard 
779  #[getter]
780  fn get_lowest_rb_ch_py(&self) -> u8 {
781    self.get_lowest_rb_ch() 
782  }
783
784  /// The paddle id of the paddle in range [1,160]
785  #[getter]
786  fn get_paddle_id   (&self) -> i16 {  
787    self.paddle_id
788  }
789
790  /// Retrieve the volume id. The volume id is 
791  /// assigned by the simulation 
792  #[getter]
793  fn get_volume_id   (&self) -> i64 {  
794    self.volume_id
795  }
796
797  /// Retrieve the panel id.
798  ///
799  /// Panel 1-6  : Cube 
800  /// Panel 7-14 : Umbrella 
801  /// Panel > 14 : Cortina 
802  #[getter]
803  fn get_panel_id    (&self) -> i16 {  
804    self.panel_id
805  }
806
807  /// The MTB link id is an internal number
808  /// used by the MTB to identify ReadoutBoards 
809  #[getter]
810  fn get_mtb_link_id (&self) -> i16 {  
811    self.mtb_link_id
812  }
813
814  /// Retrieve the Readoutboard id for the 
815  /// Readoutboard the paddle is connected to 
816  #[getter]
817  fn get_rb_id       (&self) -> i16 {  
818    self.rb_id
819  } 
820
821  /// Get the ReadoutBoard channel to which 
822  /// the A-side of the Paddle is connected 
823  #[getter]
824  #[allow(non_snake_case)]
825  fn get_rb_chA      (&self) -> i16 {  
826    self.rb_chA
827  }
828  
829  /// Get the ReadoutBoard channel to which 
830  /// the b-side of the Paddle is connected 
831  #[getter]
832  #[allow(non_snake_case)]
833  fn get_rb_chB      (&self) -> i16 {  
834    self.rb_chB
835  }
836
837  /// Get the LTB id the low-gain channels 
838  /// of this paddle are connected to
839  #[getter]
840  fn get_ltb_id      (&self) -> i16 {  
841    self.ltb_id
842  }
843
844  /// Get the LTB channel the A-side of this 
845  /// paddle is connected to 
846  #[getter]
847  #[allow(non_snake_case)]
848  fn get_ltb_chA     (&self) -> i16 {  
849    self.ltb_chA
850  }
851  
852  /// Get the LTB channel the B-side of this 
853  /// paddle is connected to 
854  #[getter]
855  #[allow(non_snake_case)]
856  fn get_ltb_chB     (&self) -> i16 {  
857    self.ltb_chB
858  }
859
860  /// Get the powerboard id for the respective
861  /// powerboard which provides the driving 
862  /// voltage for this paddle
863  #[getter]
864  fn get_pb_id       (&self) -> i16 {  
865    self.pb_id
866  }
867
868  /// Get the channel on the powerboard 
869  /// the paddle A-side is connected to 
870  #[getter]
871  #[allow(non_snake_case)]
872  fn get_pb_chA      (&self) -> i16 {  
873    self.pb_chA
874  }
875  
876  /// Get the channel on the powerboard 
877  /// the paddle b-side is connected to 
878  #[getter]
879  #[allow(non_snake_case)]
880  fn get_pb_chB      (&self) -> i16 {  
881    self.pb_chB
882  }
883
884  /// DEPRECATED - the length of the Harting
885  /// cable connected to this paddle 
886  #[getter]
887  fn get_cable_len   (&self) -> f32 {  
888    self.cable_len
889  }
890
891  /// Retrive the DSI connector this paddle
892  /// is connected through the RB/LTB to 
893  /// the MTB 
894  #[getter]
895  fn get_dsi         (&self) -> i16 {  
896    self.dsi
897  }
898
899  /// Retrieve the j address of the DSI connector 
900  /// this paddle is connected through the RB
901  /// to the MTB 
902  #[getter]
903  fn get_j_rb        (&self) -> i16 {  
904    self.j_rb
905  }
906  
907  /// Retrieve the j address of the DSI connector 
908  /// this paddle is connected through the LTB
909  /// to the MTB 
910  #[getter]
911  fn get_j_ltb       (&self) -> i16 {  
912    self.j_ltb
913  }
914
915  /// Get the (local) height of the paddle 
916  #[getter]
917  fn get_height      (&self) -> f32 {  
918    self.height
919  }
920
921  /// Get the (local) width of the paddle 
922  #[getter]
923  fn get_width       (&self) -> f32 {  
924    self.width
925  }
926
927  /// Get the (local) length of the paddle 
928  #[getter]
929  fn get_length      (&self) -> f32 {  
930    self.length
931  }
932  
933  /// Retrieve all 160 paddles from the database 
934  #[staticmethod]
935  #[pyo3(name="all")]
936  pub fn all_py() -> Option<Vec<Self>> {
937    TofPaddle::all()
938  } 
939
940  /// Retrieve all paddles connected to a certain 
941  /// ReadoutBoard from the database 
942  ///
943  /// # Arguments 
944  ///   * rbid   : RB id of the desired ReadoutBoard (typically < 50) 
945  #[staticmethod]
946  #[pyo3(name="by_rbid")]
947  pub fn by_rbid_py(rbid : u8) -> Option<Vec<Self>> {
948    TofPaddle::by_rbid(rbid)
949  }
950
951  /// Retrieve all paddles from the database and return 
952  /// a dictionary as dict([rbid, TofPaddle]) 
953  // FIXME use PyResult
954  #[staticmethod]
955  #[pyo3(name="all_as_dict")]
956  pub fn all_as_dict_py() -> Option<HashMap<u8,Self>> {
957    match TofPaddle::all_as_dict() {
958      Err(err) => {
959        error!("Unable to retrieve paddle dictionary. {err}. Did you laod the setup-env.sh shell?");
960        return None;
961      }
962      Ok(_paddles) => {
963        return Some(_paddles);
964      }
965    }
966  } 
967  
968  /// The principal is the direction along the longest
969  /// dimension from A -> B
970  #[getter]
971  #[pyo3(name="principal")]
972  pub fn principal_py(&self) -> (f32,f32,f32) {
973    let mut pr = (self.global_pos_x_l0_B - self.global_pos_x_l0_A,
974                  self.global_pos_y_l0_B - self.global_pos_y_l0_A,
975                  self.global_pos_z_l0_B - self.global_pos_z_l0_A);
976    let length = f32::sqrt(pr.0.powf(2.0) + pr.1.powf(2.0) + pr.2.powf(2.0)); 
977    pr = (pr.0/length, pr.1/length, pr.2/length);
978    return pr;
979  }
980
981  /// Return normal axis - that is orthogonal to the principal ans 
982  /// paralel to the axis of "width" 
983  #[getter]
984  #[pyo3(name="normal")]
985  pub fn normal_py(&self) -> (f32, f32, f32) {
986    self.normal()
987  }
988
989  /// The center position of the paddle (middle of all 3 local dimensions)
990  #[getter]
991  #[pyo3(name="center_pos")]
992  pub fn center_pos_py(&self) -> (f32,f32,f32) {
993    (self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0)
994  }
995
996  /// The position of the SiPm at the A-side 
997  #[getter]
998  #[pyo3(name="sideA_pos")]
999  #[allow(non_snake_case)]
1000  pub fn sideA_pos_py(&self) -> (f32,f32,f32) {
1001    (self.global_pos_x_l0_A, self.global_pos_y_l0_A, self.global_pos_z_l0_A)
1002  }
1003  
1004  /// The position of the SiPm at the B-side 
1005  #[getter]
1006  #[pyo3(name="sideB_pos")]
1007  #[allow(non_snake_case)]
1008  pub fn sideB_pos_py(&self) -> (f32,f32,f32) {
1009    (self.global_pos_x_l0_B, self.global_pos_y_l0_B, self.global_pos_z_l0_B)
1010  }
1011
1012  ///Convert DSI and J connection to the actual 
1013  ///slot they are plugged in on the MTB (0-24)
1014  #[getter]
1015  #[pyo3(name="rb_slot")]
1016  pub fn rb_slot_py(&self) -> i16 {
1017    self.rb_slot()
1018  }
1019  
1020  /// Convert DSI and J connection to the actual 
1021  /// slot they are plugged in on the MTB (0-24)
1022  #[getter]
1023  #[pyo3(name="lt_slot")]
1024  pub fn lt_slot_py(&self) -> i16 {
1025    self.lt_slot()
1026  }
1027  
1028  /// The x-coordinate of the center position of the paddle  
1029  #[getter]
1030  #[pyo3(name="global_pos_x_l0")]
1031  fn get_global_pos_x_l0(&self) -> f32 {
1032    self.global_pos_x_l0 
1033  }
1034  
1035  /// The y-coordinate of the center position of the paddle  
1036  #[getter]
1037  #[pyo3(name="global_pos_y_l0")]
1038  fn get_global_pos_y_l0(&self) -> f32 {
1039    self.global_pos_y_l0
1040  }
1041  
1042  /// The z-coordinate of the center position of the paddle  
1043  #[getter]
1044  #[pyo3(name="global_pos_z_l0")]
1045  fn get_global_pos_z_l0(&self) -> f32 {
1046    self.global_pos_z_l0
1047  }
1048}
1049
1050
1051#[cfg_attr(feature="pybindings", pymethods)]
1052impl TofPaddle {
1053}
1054
1055//---------------------------------------------------------------------
1056
1057impl fmt::Display for TofPaddle {
1058  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1059    let mut repr = String::from("<TofPaddle:");
1060    repr += "\n** identifiers **";
1061    repr += &(format!("\n   pid                : {}", self.paddle_id));     
1062    repr += &(format!("\n   vid                : {}", self.volume_id));
1063    repr += &(format!("\n   panel id           : {}", self.panel_id));
1064    repr += "\n  ** connedtions **";
1065    repr += &(format!("\n   DSI/J/CH (LG) [A]  : {}  | {} | {:02}", self.dsi, self.j_ltb, self.ltb_chA));
1066    repr += &(format!("\n   DSI/J/CH (HG) [A]  : {}  | {} | {:02}", self.dsi, self.j_rb, self.rb_chA));
1067    repr += &(format!("\n   DSI/J/CH (LG) [B]  : {}  | {} | {:02}", self.dsi, self.j_ltb, self.ltb_chB));
1068    repr += &(format!("\n   DSI/J/CH (HG) [B]  : {}  | {} | {:02}", self.dsi, self.j_rb, self.rb_chB));
1069    repr += &(format!("\n   RB/CH         [A]  : {:02} | {}", self.rb_id, self.rb_chA));
1070    repr += &(format!("\n   RB/CH         [B]  : {:02} | {}", self.rb_id, self.rb_chB));
1071    repr += &(format!("\n   LTB/CH        [A]  : {:02} | {}", self.ltb_id, self.ltb_chA));
1072    repr += &(format!("\n   LTB/CH        [B]  : {:02} | {}", self.ltb_id, self.ltb_chB));
1073    repr += &(format!("\n   PB/CH         [A]  : {:02} | {}", self.pb_id, self.pb_chA));
1074    repr += &(format!("\n   PB/CH         [B]  : {:02} | {}", self.pb_id, self.pb_chB));
1075    repr += &(format!("\n   MTB Link ID        : {:02}", self.mtb_link_id));
1076    repr += "\n   cable len [cm] :";
1077    repr += &(format!("\n    \u{21B3} {:.2}", self.cable_len));
1078    repr += "\n    (Harting -> RB)";
1079    repr += "\n   cable times [ns] (JAZ) :";
1080    repr += &(format!("\n    \u{21B3} {:.2} {:.2}", self.coax_cable_time, self.harting_cable_time));
1081    repr += "\n  ** Coordinates (L0) & dimensions **";
1082    repr += "\n   length, width, height [mm]";
1083    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.length, self.width, self.height));
1084    repr += "\n   center [mm]:";
1085    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0));
1086    repr += "\n   normal vector:";
1087    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.normal_x, self.normal_y, self.normal_z));
1088    repr += "\n   A-side [mm]:";
1089    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]>", self.global_pos_x_l0_A, self.global_pos_y_l0_A, self.global_pos_z_l0_A));
1090    repr += "\n   B-side [mm]:";
1091    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]>", self.global_pos_x_l0_B, self.global_pos_y_l0_B, self.global_pos_z_l0_B));
1092    write!(f, "{}", repr)
1093  }
1094}
1095
1096//---------------------------------------------------------------------
1097
1098#[cfg(feature="pybindings")]
1099pythonize!(TofPaddle);
1100
1101//---------------------------------------------------------------------
1102
1103/// A Readoutboard with paddles connected
1104#[derive(Debug, Clone)]
1105#[allow(non_snake_case)]
1106#[cfg_attr(feature="pybindings", pyclass)]
1107pub struct ReadoutBoard {
1108  pub rb_id           : u8, 
1109  pub dsi             : u8, 
1110  pub j               : u8, 
1111  pub mtb_link_id     : u8, 
1112  pub paddle12        : TofPaddle,
1113  //pub paddle12_chA    : u8,
1114  pub paddle34        : TofPaddle,
1115  //pub paddle34_chA    : u8,
1116  pub paddle56        : TofPaddle,
1117  //pub paddle56_chA    : u8,
1118  pub paddle78        : TofPaddle,
1119  //pub paddle78_chA    : u8,
1120  // extra stuff, not from the db
1121  // or maybe in the future?
1122  pub calib_file_path : String,
1123  pub calibration     : RBCalibrations,       
1124}
1125  
1126impl ReadoutBoard {
1127  
1128  pub fn new() -> Self {
1129    Self {
1130      rb_id           : 0, 
1131      dsi             : 0, 
1132      j               : 0, 
1133      mtb_link_id     : 0, 
1134      paddle12        : TofPaddle::new(),
1135      //paddle12_chA    : 0,
1136      paddle34        : TofPaddle::new(),
1137      //paddle34_chA    : 0,
1138      paddle56        : TofPaddle::new(),
1139      //paddle56_chA    : 0,
1140      paddle78        : TofPaddle::new(),
1141      //paddle78_chA    : 0,
1142      calib_file_path : String::from(""),
1143      calibration     : RBCalibrations::new(0),
1144    }
1145  }
1146
1147  #[allow(non_snake_case)]
1148  pub fn get_paddle12_chA(&self) -> u8 {
1149    self.paddle12.rb_chA as u8 
1150  }
1151  
1152  #[allow(non_snake_case)]
1153  pub fn get_paddle34_chA(&self) -> u8 {
1154    self.paddle34.rb_chA as u8 
1155  }
1156  
1157  #[allow(non_snake_case)]
1158  pub fn get_paddle56_chA(&self) -> u8 {
1159    self.paddle56.rb_chA as u8 
1160  }
1161  
1162  #[allow(non_snake_case)]
1163  pub fn get_paddle78_chA(&self) -> u8 {
1164    self.paddle78.rb_chA as u8 
1165  }
1166
1167  pub fn by_rbid(rbid : u8) -> Option<Self> {
1168    let paddles = TofPaddle::by_rbid(rbid);
1169    if paddles.is_none() {
1170      return None;
1171    }
1172    let mut rb_paddles = paddles.unwrap();   
1173    if rb_paddles.len() != 4 {
1174      panic!("Found more than 4 paddles for this RB! DB inconsistency! Abort!");
1175    }
1176    rb_paddles.sort_by_key(|paddle| paddle.get_lowest_rb_ch());
1177    // we ensured earlier that the vector is of len 4
1178    let paddle78 = rb_paddles.pop().unwrap();
1179    let paddle56 = rb_paddles.pop().unwrap();
1180    let paddle34 = rb_paddles.pop().unwrap();
1181    let paddle12 = rb_paddles.pop().unwrap();
1182
1183    Some(ReadoutBoard {
1184      rb_id           : rbid, 
1185      dsi             : paddle12.dsi as u8, 
1186      j               : paddle12.j_rb as u8, 
1187      mtb_link_id     : paddle12.mtb_link_id as u8, 
1188      paddle12        : paddle12,
1189      //paddle12_chA    : paddle12,
1190      paddle34        : paddle34,
1191      //paddle34_chA    : 0,
1192      paddle56        : paddle56,
1193      //paddle56_chA    : 0,
1194      paddle78        : paddle78,
1195      //paddle78_chA    : 0,
1196      calib_file_path : String::from(""),
1197      calibration     : RBCalibrations::new(0),
1198    })
1199  }
1200
1201  /// Returns the ip address following a convention
1202  ///
1203  /// This does NOT GUARANTEE that the address is correct!
1204  pub fn guess_address(&self) -> String {
1205    format!("tcp://10.0.1.1{:02}:42000", self.rb_id)
1206  }
1207 
1208  pub fn get_paddle_ids(&self) -> [u8;4] {
1209    let pid0 = self.paddle12.paddle_id as u8;
1210    let pid1 = self.paddle34.paddle_id as u8;
1211    let pid2 = self.paddle56.paddle_id as u8;
1212    let pid3 = self.paddle78.paddle_id as u8;
1213    [pid0, pid1, pid2, pid3]
1214  }
1215
1216  #[allow(non_snake_case)]
1217  pub fn get_A_sides(&self) -> [u8;4] {
1218    let pa_0 = self.get_paddle12_chA();
1219    let pa_1 = self.get_paddle34_chA();
1220    let pa_2 = self.get_paddle56_chA();
1221    let pa_3 = self.get_paddle78_chA();
1222    [pa_0, pa_1, pa_2, pa_3]
1223  }
1224
1225  #[allow(non_snake_case)]
1226  pub fn get_pid_rbchA(&self, pid : u8) -> Option<u8> {
1227    if self.paddle12.paddle_id as u8 == pid {
1228      let rv = self.paddle12.rb_chA as u8;
1229      return Some(rv);
1230    } else if self.paddle34.paddle_id as u8 == pid {
1231      let rv = self.paddle34.rb_chA as u8;
1232      return Some(rv);
1233    } else if self.paddle56.paddle_id as u8 == pid {
1234      let rv = self.paddle56.rb_chA as u8;
1235      return Some(rv);
1236    } else if self.paddle78.paddle_id as u8== pid {
1237      let rv = self.paddle78.rb_chA as u8;
1238      return Some(rv);
1239    } else {
1240      return None;
1241    }
1242  }
1243  
1244  #[allow(non_snake_case)]
1245  pub fn get_pid_rbchB(&self, pid : u8) -> Option<u8> {
1246    if self.paddle12.paddle_id as u8 == pid {
1247      let rv = self.paddle12.rb_chB as u8;
1248      return Some(rv);
1249    } else if self.paddle34.paddle_id as u8== pid {
1250      let rv = self.paddle34.rb_chB as u8;
1251      return Some(rv);
1252    } else if self.paddle56.paddle_id as u8== pid {
1253      let rv = self.paddle56.rb_chB as u8;
1254      return Some(rv);
1255    } else if self.paddle78.paddle_id as u8 == pid {
1256      let rv = self.paddle78.rb_chB as u8;
1257      return Some(rv);
1258    } else {
1259      return None;
1260    }
1261  }
1262
1263  pub fn get_paddle_length(&self, pid : u8) -> Option<f32> {
1264    if self.paddle12.paddle_id as u8 == pid {
1265      let rv = self.paddle12.length;
1266      return Some(rv);
1267    } else if self.paddle34.paddle_id as u8== pid {
1268      let rv = self.paddle34.length;
1269      return Some(rv);
1270    } else if self.paddle56.paddle_id as u8== pid {
1271      let rv = self.paddle56.length;
1272      return Some(rv);
1273    } else if self.paddle78.paddle_id as u8 == pid {
1274      let rv = self.paddle78.length;
1275      return Some(rv);
1276    } else {
1277      return None;
1278    }
1279  }
1280  
1281  pub fn all() -> Option<Vec<Self>> {
1282    let all_rbs = get_all_rbids_in_db();
1283    match all_rbs {
1284      None => {
1285        error!("Can not get TofPaddle information from DB, and thus can't construct ReadoutBoard instances. Did you load the setup-env.sh shell?");
1286        return None;
1287      }
1288      Some(all_rbids) => {
1289        let mut rbs = Vec::<Self>::new();
1290        for k in all_rbids {
1291          rbs.push(ReadoutBoard::by_rbid(k)?);
1292        }
1293        return Some(rbs);
1294      }
1295    }
1296  }
1297  
1298  /// Get all Readoutboards from the database
1299  ///
1300  /// # Returns:
1301  ///   * dict [rbid->Readoutboard]
1302  pub fn all_as_dict() -> Result<HashMap<u8,Self>, ConnectionError> {
1303    let mut rbs = HashMap::<u8, Self>::new();
1304    match Self::all() {
1305      None => {
1306        error!("We can't find any readoutboards in the database!");
1307        return Ok(rbs);
1308      }
1309      Some(rbs_) => {
1310        for rb in rbs_ {
1311          rbs.insert(rb.rb_id, rb );
1312        }
1313      }
1314    }
1315    return Ok(rbs);
1316  }
1317  
1318  pub fn to_summary_str(&self) -> String {
1319    let mut repr  = String::from("<ReadoutBoard:");
1320    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1321    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1322    repr += &(format!("\n  RAT         : {}",self.paddle12.ltb_id));
1323    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1324    repr += "\n **Connected paddles**";
1325    repr += &(format!("\n  Channel 1/2 : {:02} (panel {:01})", self.paddle12.paddle_id, self.paddle12.panel_id));
1326    repr += &(format!("\n  Channel 3/4 : {:02} (panel {:01})", self.paddle34.paddle_id, self.paddle34.panel_id));
1327    repr += &(format!("\n  Channel 5/6 : {:02} (panel {:01})", self.paddle56.paddle_id, self.paddle56.panel_id));
1328    repr += &(format!("\n  Channel 7/8 : {:02} (panel {:01})", self.paddle78.paddle_id, self.paddle78.panel_id));
1329    repr
1330  }
1331
1332  /// Load the newest calibration from the calibration file path
1333  pub fn load_latest_calibration(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1334    //  files look like RB20_2024_01_26-08_15_54.cali.tof.gaps
1335    //let re = Regex::new(r"(\d{4}_\d{2}_\d{2}-\d{2}_\d{2}_\d{2})")?;
1336    let re = Regex::new(r"(\d{6}_\d{6})")?;
1337    // Define your file pattern (e.g., "logs/*.log" for all .log files in the logs directory)
1338    let pattern = format!("{}/RB{:02}_*", self.calib_file_path, self.rb_id); // Adjust this pattern to your files' naming convention
1339    let timestamp = DateTime::<Utc>::from_timestamp(0,0).unwrap(); // I am not sure what to do here
1340                                                                   // otherwise than unwrap. How is
1341                                                                   // this allowed to fail?
1342    //let mut newest_file = (String::from(""), NaiveDateTime::from_timestamp(0, 0));
1343    let mut newest_file = (String::from(""), timestamp);
1344
1345    // Iterate over files that match the pattern
1346    let mut filename : String;
1347    for entry in glob(&pattern)? {
1348      if let Ok(path) = entry {
1349        // Get the filename as a string
1350        //let cpath = path.clone();
1351        match path.file_name() {
1352          None => continue,
1353          Some(fname) => {
1354              // the expect might be ok, since this is something done during initialization
1355              filename = fname.to_os_string().into_string().expect("Unwrapping filename failed!");
1356          }
1357        }
1358        if let Some(caps) = re.captures(&filename) {
1359          if let Some(timestamp_str) = caps.get(0).map(|m| m.as_str()) {
1360            //println!("timestamp_str {}, {}",timestamp_str, HUMAN_TIMESTAMP_FORMAT);
1361            //let timestamp = NaiveDateTime::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
1362            //let timestamp = DateTime::<Utc>::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
1363            let footzstring = format!("{}+0000", timestamp_str);
1364            let timestamp = DateTime::parse_from_str(&footzstring, "%y%m%d_%H%M%S%z")?;
1365            //let timestamp = DateTime::parse_from_str(&footzstring, HUMAN_TIMESTAMP_FORMAT)?;
1366            //println!("parse successful");
1367            //let _timestamp = DateTime
1368            if timestamp > newest_file.1 {
1369              // FIXME - into might panic?
1370              newest_file.1 = timestamp.into();
1371              newest_file.0 = filename.clone();
1372            }
1373          }
1374        }
1375      }
1376    }
1377    
1378    if newest_file.0.is_empty() {
1379      error!("No matching calibration available for board {}!", self.rb_id);
1380    } else {
1381      let file_to_load = format!("{}/{}", self.calib_file_path, newest_file.0);
1382      info!("Loading calibration from file: {}", file_to_load);
1383      self.calibration = RBCalibrations::from_file(file_to_load, true)?;
1384    }
1385    Ok(())
1386  }
1387}
1388
1389impl fmt::Display for ReadoutBoard {
1390  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1391    let mut repr  = String::from("<ReadoutBoard:");
1392    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1393    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1394    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1395    repr += "\n **Connected paddles**";
1396    repr += &(format!("\n  Ch0/1(1/2)  : {}",self.paddle12)); 
1397    repr += &(format!("\n    A-side    : {}", self.get_paddle12_chA()));
1398    repr += &(format!("\n  Ch1/2(2/3)  : {}",self.paddle34));         
1399    repr += &(format!("\n    A-side    : {}", self.get_paddle34_chA()));
1400    repr += &(format!("\n  Ch2/3(3/4)  : {}",self.paddle56));         
1401    repr += &(format!("\n    A-side    : {}", self.get_paddle56_chA()));
1402    repr += &(format!("\n  Ch3/4(4/5)  : {}>",self.paddle78));         
1403    repr += &(format!("\n    A-side    : {}", self.get_paddle78_chA()));
1404    repr += "** calibration will be loaded from this path:";
1405    repr += &(format!("\n      \u{021B3} {}", self.calib_file_path));
1406    repr += &(format!("\n  calibration : {}>", self.calibration));
1407    write!(f, "{}", repr)
1408  }
1409}
1410
1411//---------------------------------------------------------------------
1412
1413#[cfg(feature="pybindings")]
1414#[pymethods]
1415impl ReadoutBoard {
1416
1417  #[getter]
1418  fn get_rb_id(&self) -> u8 {
1419    self.rb_id
1420  }
1421
1422  #[getter]
1423  fn get_dsi(&self) -> u8 {
1424    self.dsi
1425  }
1426
1427  #[getter]
1428  fn get_j(&self) -> u8 {
1429    self.j
1430  }
1431
1432  #[getter]
1433  fn get_mtb_link_id(&self) -> u8 {
1434    self.mtb_link_id
1435  }
1436
1437  #[getter]
1438  fn get_paddle12(&self) -> TofPaddle { 
1439    self.paddle12.clone() 
1440  }
1441  
1442  #[getter]
1443  fn get_paddle34(&self) -> TofPaddle { 
1444    self.paddle34.clone() 
1445  }
1446  
1447  #[getter]
1448  fn get_paddle56(&self) -> TofPaddle { 
1449    self.paddle56.clone() 
1450  }
1451  
1452  #[getter]
1453  fn get_paddle78(&self) -> TofPaddle { 
1454    self.paddle78.clone() 
1455  }
1456
1457  #[staticmethod]
1458  #[pyo3(name="all")]
1459  pub fn all_py() -> Option<Vec<Self>> {
1460    Self::all()
1461  } 
1462
1463  /// Load a calibration file for this specific Readoutboard
1464  #[pyo3(name="load_calibration")]
1465  pub fn load_calibration_py(&mut self, path : &Bound<'_,PyAny>) -> PyResult<()> {
1466    let mut string_value = String::from("foo");
1467    if let Ok(s) = path.extract::<String>() {
1468       string_value = s;
1469    } //else if let Ok(p) = filename_or_directory.extract::<&Path>() {
1470    if let Ok(fspath_method) = path.getattr("__fspath__") {
1471      if let Ok(fspath_result) = fspath_method.call0() {
1472        if let Ok(py_string) = fspath_result.extract::<String>() {
1473          string_value = py_string;
1474        }
1475      }
1476    }
1477    self.calib_file_path = string_value;
1478    match self.load_latest_calibration() {
1479      Ok(_) => {
1480        return Ok(())
1481      }
1482      Err(err) => { 
1483        return Err(PyValueError::new_err(err.to_string()));
1484      }
1485    }
1486  }
1487
1488  #[staticmethod]
1489  #[pyo3(name="all_as_dict")]
1490  pub fn all_as_dict_py() -> Option<HashMap<u8,Self>> {
1491    match Self::all_as_dict() {
1492      Err(err) => {
1493        error!("Unable to retrieve RB dictionary. {err}. Did you laod the setup-env.sh shell?");
1494        return None;
1495      }
1496      Ok(_data) => {
1497        return Some(_data);
1498      }
1499    }
1500  } 
1501}
1502//paddle12        : TofPaddle::new(),
1503////paddle12_chA    : 0,
1504//paddle34        : TofPaddle::new(),
1505////paddle34_chA    : 0,
1506//paddle56        : TofPaddle::new(),
1507////paddle56_chA    : 0,
1508//paddle78        : TofPaddle::new(),
1509////paddle78_chA    : 0,
1510//calib_file_path : String::from(""),
1511//calibration     : RBCalibrations::new(0),
1512//}
1513
1514#[cfg(feature="pybindings")]
1515pythonize!(ReadoutBoard);
1516
1517//---------------------------------------------------------------------
1518
1519#[derive(Debug,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
1520#[diesel(table_name = schema::tof_db_rat)]
1521#[diesel(primary_key(rat_id))]
1522#[allow(non_snake_case)]
1523#[cfg_attr(feature="pybindings", pyclass)]
1524pub struct RAT {
1525  pub rat_id                    : i16, 
1526  pub pb_id                     : i16, 
1527  pub rb1_id                    : i16, 
1528  pub rb2_id                    : i16, 
1529  pub ltb_id                    : i16, 
1530  pub ltb_harting_cable_length  : i16, 
1531}
1532
1533impl RAT {
1534  pub fn new() -> Self {
1535    Self {
1536      rat_id                    : 0, 
1537      pb_id                     : 0, 
1538      rb1_id                    : 0, 
1539      rb2_id                    : 0, 
1540      ltb_id                    : 0, 
1541      ltb_harting_cable_length  : 0, 
1542    }
1543  }
1544  
1545  /// Get the RAT where rb2id matched the argument
1546  pub fn where_rb2id(conn: &mut SqliteConnection, rb2id : u8) -> Option<Vec<RAT>> {
1547
1548    let mut result = Vec::<RAT>::new();
1549    match RAT::all(conn) {
1550      Some(rats) => {
1551        for rat in rats {
1552          if rat.rb2_id == rb2id as i16 {
1553            result.push(rat);
1554          }
1555        }
1556        return Some(result);
1557      }
1558      None => ()
1559    }
1560    Some(result)
1561  }
1562  
1563  /// Get the RAT where rb1id (the rb id of rb"1" in the RAT) matched the argument
1564  pub fn where_rb1id(conn: &mut SqliteConnection, rb2id : u8) -> Option<Vec<RAT>> {
1565    let mut result = Vec::<RAT>::new();
1566    match RAT::all(conn) {
1567      Some(rats) => {
1568        for rat in rats {
1569          if rat.rb1_id == rb2id as i16 {
1570            result.push(rat);
1571          }
1572        }
1573        return Some(result);
1574      }
1575      None => ()
1576    }
1577    Some(result)
1578  }
1579
1580  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<RAT>> {
1581    match tof_db_rat.load::<RAT>(conn) {
1582      Err(err) => {
1583        error!("Unable to load RATs from db! {err}");
1584        return None;
1585      }
1586      Ok(rats) => {
1587        return Some(rats);
1588      }
1589    }
1590  }
1591
1592}
1593
1594impl fmt::Display for RAT {
1595  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1596    let mut repr = String::from("<RAT");
1597    repr += &(format!("\n  ID                : {}", self.rat_id));                   
1598    repr += &(format!("\n  PB                : {} ", self.pb_id));                    
1599    repr += &(format!("\n  RB1               : {}", self.rb1_id));                   
1600    repr += &(format!("\n  RB2               : {}", self.rb2_id));                   
1601    repr += &(format!("\n  LTB               : {}", self.ltb_id));                   
1602    repr += &(format!("\n  H. cable len [cm] : {}>", self.ltb_harting_cable_length)); 
1603    write!(f, "{}", repr)
1604  }
1605}
1606
1607///// Get all trackerstrips from the database
1608//pub fn get_trackerstrips() -> Result<HashMap<u32,TrackerStrip>, ConnectionError> {
1609//  let db_path  = env::var("GONDOLA_DB_URL").unwrap_or_else(|_| "".to_string());
1610//  let mut conn = connect_to_db(db_path)?;
1611//  let mut strips = HashMap::<u32, TrackerStrip>::new();
1612//  match TrackerStrip::all(&mut conn) {
1613//    None => {
1614//      error!("We can't find any tracker strips in the database!");
1615//      return Ok(strips);
1616//    }
1617//    Some(ts) => {
1618//      for s in ts {
1619//        strips.insert(s.get_stripid() as u32, s.clone());
1620//      }
1621//    }
1622//  }
1623//  return Ok(strips);
1624//}
1625//
1626///// Create a mapping of mtb link ids to rb ids
1627//pub fn get_linkid_rbid_map(rbs : &Vec<ReadoutBoard>) -> HashMap<u8, u8>{
1628//  let mut mapping = HashMap::<u8, u8>::new();
1629//  for rb in rbs {
1630//    mapping.insert(rb.mtb_link_id, rb.rb_id);
1631//  }
1632//  mapping
1633//}
1634//
1635///// Create a mapping of rb id to mtb link ids
1636//pub fn get_rbid_linkid_map(rbs : &Vec<ReadoutBoard>) -> HashMap<u8, u8> {
1637//  let mut mapping = HashMap::<u8, u8>::new();
1638//  for rb in rbs {
1639//    mapping.insert(rb.rb_id, rb.mtb_link_id);
1640//  }
1641//  mapping
1642//}
1643//
1644
1645
1646
1647
1648///// Create a map for rbid, ch -> paddle id. This is for both sides
1649///// and will always return a paddle id independent of A or B
1650//pub fn get_rb_ch_pid_map(paddles : &Vec<Paddle>) -> RbChPidMapping {
1651//  let mut mapping = RbChPidMapping::new();
1652//  for rbid  in 1..51 {
1653//    let mut chmap = HashMap::<u8, u8>::new();
1654//    for ch in 1..9 {
1655//      chmap.insert(ch,0);
1656//    }
1657//    mapping.insert(rbid,chmap);
1658//  }
1659//  for pdl in paddles {
1660//    let rb_id = pdl.rb_id  as u8;
1661//    let ch_a  = pdl.rb_chA as u8;
1662//    let ch_b  = pdl.rb_chB as u8;
1663//    let pid   = pdl.paddle_id as u8;
1664//    //println!("rb_id {rb_id}, chA {ch_a}, chB {ch_b}");
1665//    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_a).unwrap() = pid;
1666//    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_b).unwrap() = pid;
1667//  }
1668//  mapping
1669//}
1670//
1671///// Create a map for rbid, ch -> paddle id. This is only for the A 
1672///// side and will not have an entry in case the given RB channel
1673///// is connected to the B side of the paddle
1674//pub fn get_rb_ch_pid_a_map(paddles : &Vec<Paddle>) -> RbChPidMapping {
1675//  let mut mapping = RbChPidMapping::new();
1676//  for rbid  in 1..51 {
1677//    let mut chmap = HashMap::<u8, u8>::new();
1678//    for ch in 1..9 {
1679//      chmap.insert(ch,0);
1680//    }
1681//    mapping.insert(rbid,chmap);
1682//  }
1683//  for pdl in paddles {
1684//    let rb_id = pdl.rb_id  as u8;
1685//    let ch_a  = pdl.rb_chA as u8;
1686//    let pid   = pdl.paddle_id as u8;
1687//    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_a).unwrap() = pid;
1688//  }
1689//  mapping
1690//}
1691//
1692//
1693///// Create a map for rbid, ch -> paddle id. This is only for the B 
1694///// side and will not have an entry in case the given RB channel
1695///// is connected to the A side of the paddle
1696//pub fn get_rb_ch_pid_b_map(paddles : &Vec<Paddle>) -> RbChPidMapping {
1697//  let mut mapping = RbChPidMapping::new();
1698//  for rbid  in 1..51 {
1699//    let mut chmap = HashMap::<u8, u8>::new();
1700//    for ch in 1..9 {
1701//      chmap.insert(ch,0);
1702//    }
1703//    mapping.insert(rbid,chmap);
1704//  }
1705//  for pdl in paddles {
1706//    let rb_id = pdl.rb_id  as u8;
1707//    let ch_b  = pdl.rb_chB as u8;
1708//    let pid   = pdl.paddle_id as u8;
1709//    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_b).unwrap() = pid;
1710//  }
1711//  mapping
1712//}
1713//
1714///// A representation of a run 
1715//#[derive(Debug, Clone, Queryable,Insertable, Selectable, serde::Serialize, serde::Deserialize)]
1716//#[diesel(table_name = schema::tof_db_run)]
1717//#[diesel(primary_key(run_id))]
1718//pub struct Run {
1719//  pub run_id                    : i64,
1720//  pub runtime_secs              : Option<i64>,
1721//  pub calib_before              : Option<bool>,
1722//  pub shifter                   : Option<i16>,
1723//  pub run_type                  : Option<i16>,
1724//  pub run_path                  : Option<String>,
1725//}
1726//
1727//impl Run {
1728//  pub fn new() -> Self {
1729//    Self {
1730//      run_id        : 0, 
1731//      runtime_secs  : Some(0), 
1732//      calib_before  : Some(true), 
1733//      shifter       : Some(0), 
1734//      run_type      : Some(0), 
1735//      run_path      : Some(String::from("")), 
1736//    }
1737//  }
1738//
1739//  pub fn get_last_run(conn: &mut SqliteConnection) -> Option<u32> {
1740//    use schema::tof_db_run::dsl::*;
1741//    match tof_db_run.load::<Run>(conn) {
1742//      Err(err) => {
1743//        error!("Unable to load DSICards from db! {err}");
1744//        return None;
1745//      }
1746//      Ok(_runs) => {
1747//        //return Some(runs);
1748//      }
1749//    }
1750//    let _results = tof_db_run
1751//      //.filter(published.eq(true))
1752//      .limit(1)
1753//      //.select(Run::as_select())
1754//      .load::<Run>(conn)
1755//      .expect("Error loading posts");
1756//    None
1757//  }
1758//}
1759//
1760//impl fmt::Display for Run {
1761//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1762//    let mut repr = String::from("<Run");
1763//    repr += &(format!("\n  RunID         : {}", self.run_id));                   
1764//    repr += &(format!("\n  - auto cali   : {}", self.calib_before.unwrap_or(false)));
1765//    repr += &(format!("\n  runtime [sec] : {}", self.runtime_secs.unwrap_or(-1)));
1766//    repr += &(format!("\n  shifter       : {}", self.shifter.unwrap_or(-1)));
1767//    repr += &(format!("\n  run_type      : {}", self.run_type.unwrap_or(-1)));
1768//    repr += &(format!("\n  run_path      : {}", self.run_path.clone().unwrap_or(String::from(""))));
1769//    write!(f, "{}", repr)
1770//  }
1771//}
1772//
1773///// Representation of a local trigger board.
1774///// 
1775///// The individual LTB channels do not map directly to PaddleEnds. Rather two of them
1776///// map to a paddle and then the whole paddle should get read out.
1777///// To be more specific about this. The LTB has 16 channels, but we treat them as 8.
1778///// Each 2 LTB channels get "married" internally in the board and will then continue
1779///// on as 1 LTB channel, visible to the outside. The information about which end of 
1780///// the Paddle crossed which threshhold is lost.
1781///// How it works is that the two channels will be combined by the trigger logic:
1782///// - There are 4 states (2 bits)
1783/////   - 0 - no hit
1784/////   - 1 - Hit
1785/////   - 2 - Beta
1786/////   - 3 - Veto
1787///// 
1788///// Each defining an individual threshold. If that is crossed, the whole paddle
1789///// (ends A+B) will be read out by the ReadoutBoard
1790///// 
1791///// The LTB channels here are labeled 1-8. This is as it is in the TOF spreadsheet.
1792///// Also dsi is labeled as in the spreadsheet and will start from one.
1793///// 
1794///// It is NOT clear from this which ch on the rb is connected to which side, for that
1795///// the paddle/RB tables need to be consulted.
1796///// Again: rb_ch0 does NOT necessarily correspond to the A side!
1797///// 
1798//#[derive(Debug,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
1799//#[diesel(table_name = schema::tof_db_rat)]
1800//#[diesel(primary_key(rat_id))]
1801//pub struct RAT {
1802//  pub rat_id                    : i16, 
1803//  pub pb_id                     : i16, 
1804//  pub rb1_id                    : i16, 
1805//  pub rb2_id                    : i16, 
1806//  pub ltb_id                    : i16, 
1807//  pub ltb_harting_cable_length  : i16, 
1808//}
1809//
1810//impl RAT {
1811//  pub fn new() -> Self {
1812//    Self {
1813//      rat_id                    : 0, 
1814//      pb_id                     : 0, 
1815//      rb1_id                    : 0, 
1816//      rb2_id                    : 0, 
1817//      ltb_id                    : 0, 
1818//      ltb_harting_cable_length  : 0, 
1819//    }
1820//  }
1821//  
1822//  /// Get the RAT where rb2id matched the argument
1823//  pub fn where_rb2id(conn: &mut SqliteConnection, rb2id : u8) -> Option<Vec<RAT>> {
1824//    let mut result = Vec::<RAT>::new();
1825//    match RAT::all(conn) {
1826//      Some(rats) => {
1827//        for rat in rats {
1828//          if rat.rb2_id == rb2id as i16 {
1829//            result.push(rat);
1830//          }
1831//        }
1832//        return Some(result);
1833//      }
1834//      None => ()
1835//    }
1836//    Some(result)
1837//  }
1838//  
1839//  /// Get the RAT where rb1id (the rb id of rb"1" in the RAT) matched the argument
1840//  pub fn where_rb1id(conn: &mut SqliteConnection, rb2id : u8) -> Option<Vec<RAT>> {
1841//    let mut result = Vec::<RAT>::new();
1842//    match RAT::all(conn) {
1843//      Some(rats) => {
1844//        for rat in rats {
1845//          if rat.rb1_id == rb2id as i16 {
1846//            result.push(rat);
1847//          }
1848//        }
1849//        return Some(result);
1850//      }
1851//      None => ()
1852//    }
1853//    Some(result)
1854//  }
1855//
1856//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<RAT>> {
1857//    match tof_db_rat.load::<RAT>(conn) {
1858//      Err(err) => {
1859//        error!("Unable to load RATs from db! {err}");
1860//        return None;
1861//      }
1862//      Ok(rats) => {
1863//        return Some(rats);
1864//      }
1865//    }
1866//  }
1867//
1868//}
1869//
1870//impl fmt::Display for RAT {
1871//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1872//    let mut repr = String::from("<RAT");
1873//    repr += &(format!("\n  ID                : {}", self.rat_id));                   
1874//    repr += &(format!("\n  PB                : {} ", self.pb_id));                    
1875//    repr += &(format!("\n  RB1               : {}", self.rb1_id));                   
1876//    repr += &(format!("\n  RB2               : {}", self.rb2_id));                   
1877//    repr += &(format!("\n  LTB               : {}", self.ltb_id));                   
1878//    repr += &(format!("\n  H. cable len [cm] : {}>", self.ltb_harting_cable_length)); 
1879//    write!(f, "{}", repr)
1880//  }
1881//}
1882//
1883//
1884///// A DSI card which is plugged into one of five slots on the MTB
1885///// The DSI card provides the connection to RBs and LTBs and has 
1886///// a subdivision, which is called 'j'
1887//#[derive(Queryable, Selectable)]
1888//#[diesel(primary_key(dsi_id))]
1889//#[diesel(table_name = schema::tof_db_dsicard)]
1890//pub struct DSICard { 
1891//  pub dsi_id    : i16,
1892//  pub j1_rat_id : Option<i16>,
1893//  pub j2_rat_id : Option<i16>,
1894//  pub j3_rat_id : Option<i16>,
1895//  pub j4_rat_id : Option<i16>,
1896//  pub j5_rat_id : Option<i16>,
1897//}
1898// 
1899//
1900//impl DSICard {
1901//  pub fn new() -> Self {
1902//    Self {
1903//      dsi_id    : 0,
1904//      j1_rat_id : None,
1905//      j2_rat_id : None,
1906//      j3_rat_id : None,
1907//      j4_rat_id : None,
1908//      j5_rat_id : None,
1909//    }
1910//  }
1911//  
1912//  /// True if this RAT box is plugged in to any of the j 
1913//  /// connectors on this specific DSI card
1914//  pub fn has_rat(&self, r_id : u8) -> bool {
1915//    if let Some(rid) = self.j1_rat_id {
1916//      if rid as u8 == r_id {
1917//        return true;
1918//      }
1919//    }
1920//    if let Some(rid) = self.j2_rat_id {
1921//      if rid as u8 == r_id {
1922//        return true;
1923//      }
1924//    }
1925//    if let Some(rid) = self.j3_rat_id {
1926//      if rid as u8 == r_id {
1927//        return true;
1928//      }
1929//    }
1930//    if let Some(rid) = self.j4_rat_id {
1931//      if rid as u8 == r_id {
1932//        return true;
1933//      }
1934//    }
1935//    if let Some(rid) = self.j5_rat_id {
1936//      if rid as u8 == r_id {
1937//        return true;
1938//      }
1939//    }
1940//    return false;
1941//  }
1942//
1943//  /// Get the j connetor for this specific RAT
1944//  /// Raises ValueError if the RAT is not connected
1945//  pub fn get_j(&self, r_id : u8) -> Option<u8> {
1946//    if !self.has_rat(r_id) {
1947//      return None;
1948//    }
1949//    if let Some(rid) = self.j1_rat_id {
1950//      if rid as u8 == r_id {
1951//        let _j = self.j1_rat_id.unwrap() as u8;
1952//        return Some(_j);
1953//      }
1954//    }
1955//    if let Some(rid) = self.j2_rat_id {
1956//      if rid as u8 == r_id {
1957//        let _j = self.j2_rat_id.unwrap() as u8;
1958//        return Some(_j);
1959//      }
1960//    }
1961//    if let Some(rid) = self.j3_rat_id {
1962//      if rid as u8 == r_id {
1963//        let _j = self.j3_rat_id.unwrap() as u8;
1964//        return Some(_j);
1965//      }
1966//    }
1967//    if let Some(rid) = self.j4_rat_id {
1968//      if rid as u8 == r_id {
1969//        let _j = self.j4_rat_id.unwrap() as u8;
1970//        return Some(_j);
1971//      }
1972//    }
1973//    if let Some(rid) = self.j5_rat_id {
1974//      if rid as u8 == r_id {
1975//        let _j = self.j5_rat_id.unwrap() as u8;
1976//        return Some(_j);
1977//      }
1978//    }
1979//  None
1980//  }
1981//  
1982//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DSICard>> {
1983//    match tof_db_dsicard.load::<DSICard>(conn) {
1984//      Err(err) => {
1985//        error!("Unable to load DSICards from db! {err}");
1986//        return None;
1987//      }
1988//      Ok(dsis) => {
1989//        return Some(dsis);
1990//      }
1991//    }
1992//  }
1993//}
1994//
1995//impl fmt::Display for DSICard {
1996//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1997//    let mut repr  = String::from("<DSI Card:");
1998//    repr += &(format!("\n  ID     : {}", self.dsi_id));     
1999//    repr += "\n  -- -- -- --";
2000//    if let Some(_j) = self.j1_rat_id {
2001//        repr += &(format!("\n  J1 RAT : {}",_j));
2002//    } else {
2003//        repr += "\n  J1 RAT : Not connected";
2004//    }
2005//    if let Some(_j) = self.j2_rat_id {
2006//        repr += &(format!("\n  J2 RAT : {}",_j));
2007//    } else {
2008//        repr += "\n  J2 RAT : Not connected";
2009//    }
2010//    if let Some(_j) = self.j3_rat_id {
2011//        repr += &(format!("\n  J3 RAT : {}",_j));
2012//    } else {
2013//        repr += "\n  J3 RAT : Not connected";
2014//    }
2015//    if let Some(_j) = self.j4_rat_id {
2016//        repr += &(format!("\n  J4 RAT : {}",_j));
2017//    } else {
2018//        repr += "\n  J4 RAT : Not connected";
2019//    }
2020//    if let Some(_j) = self.j5_rat_id {
2021//        repr += &(format!("\n  J5 RAT : {}>",_j));
2022//    } else {
2023//        repr += "\n  J5 RAT : Not connected>";
2024//    }
2025//    write!(f, "{}", repr)
2026//  }
2027//}
2028//
2029
2030/// A single Tracker strip
2031#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
2032#[diesel(table_name = schema::tof_db_trackerstrip)]
2033#[diesel(primary_key(strip_id))]
2034#[allow(non_snake_case)]
2035#[cfg_attr(feature="pybindings", pyclass)]
2036pub struct TrackerStrip {
2037  pub strip_id            : i32,
2038  pub layer               : i32, 
2039  pub row                 : i32, 
2040  pub module              : i32, 
2041  pub channel             : i32,  
2042  pub global_pos_x_l0     : f32,
2043  pub global_pos_y_l0     : f32,
2044  pub global_pos_z_l0     : f32,
2045  pub global_pos_x_det_l0 : f32,
2046  pub global_pos_y_det_l0 : f32,
2047  pub global_pos_z_det_l0 : f32,
2048  pub principal_x         : f32,
2049  pub principal_y         : f32,
2050  pub principal_z         : f32,
2051  pub volume_id           : i64,
2052}
2053
2054impl TrackerStrip {
2055  pub fn new() -> Self {
2056    Self {
2057      strip_id            : 0,
2058      layer               : 0, 
2059      row                 : 0, 
2060      module              : 0, 
2061      channel             : 0,  
2062      global_pos_x_l0     : 0.0,
2063      global_pos_y_l0     : 0.0,
2064      global_pos_z_l0     : 0.0,
2065      global_pos_x_det_l0 : 0.0,
2066      global_pos_y_det_l0 : 0.0,
2067      global_pos_z_det_l0 : 0.0,
2068      principal_x         : 0.0,
2069      principal_y         : 0.0,
2070      principal_z         : 0.0,
2071      volume_id           : 0,
2072    }
2073  }
2074  
2075  /// Factory method for the strip id following tracker convention
2076  pub fn create_stripid(layer : u8, row :u8, module : u8, channel : u8) -> u32 {
2077    channel as u32 + (module as u32)*100 + (row as u32)*10000 + (layer as u32)*100000
2078  }
2079
2080  /// FIXME - why use this at all and not just get the one from the db??
2081  pub fn get_stripid(&self) -> u32 {
2082    self.channel as u32 + (self.module as u32)*100 + (self.row as u32)*10000 + (self.layer as u32)*100000
2083  }
2084  
2085  /// Get all TRK strips from the database
2086  pub fn all_as_dict() -> Result<HashMap<u32,Self>, ConnectionError> {
2087    let mut strips = HashMap::<u32, Self>::new();
2088    match Self::all() {
2089      None => {
2090        error!("We can't find any tracker strips in the database!");
2091        return Ok(strips);
2092      }
2093      Some(strips_) => {
2094        for s in strips_ {
2095          strips.insert(s.strip_id as u32, s );
2096        }
2097      }
2098    }
2099    return Ok(strips);
2100  }
2101
2102  pub fn all() -> Option<Vec<Self>> {
2103    use schema::tof_db_trackerstrip::dsl::*;
2104    let mut conn = connect_to_db().ok()?;
2105    match tof_db_trackerstrip.load::<Self>(&mut conn) {
2106      Err(err) => {
2107        error!("Unable to load tracker strips from db! {err}");
2108        return None;
2109      }
2110      Ok(strips) => {
2111        return Some(strips);
2112      }
2113    }
2114  }
2115
2116  pub fn get_coordinates(&self) -> (f32, f32, f32) {
2117    (self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0)
2118  }
2119
2120  pub fn get_detcoordinates(&self) -> (f32, f32, f32) {
2121    (self.global_pos_x_det_l0, self.global_pos_y_det_l0, self.global_pos_z_det_l0)
2122  }
2123}
2124
2125impl fmt::Display for TrackerStrip {
2126  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2127    let mut repr = format!("<TrackerStrip [{}]:", self.strip_id);
2128    repr += &(format!("\n   vid                : {}", self.volume_id));
2129    repr += &(format!("\n   layer              : {}", self.layer));
2130    repr += &(format!("\n   row                : {}", self.row));
2131    repr += &(format!("\n   module             : {}", self.module));
2132    repr += &(format!("\n   channel            : {}", self.channel));
2133    repr += "\n   strip center [mm]:";
2134    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0));
2135    repr += "\n   detector (disk) center [mm]:";
2136    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.global_pos_x_det_l0, self.global_pos_y_det_l0, self.global_pos_z_det_l0));
2137    repr += "\n   strip principal direction:";
2138    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.principal_x, self.principal_y, self.principal_z));
2139    write!(f, "{}", repr)
2140  }
2141}
2142
2143#[cfg(feature="pybindings")]
2144#[pymethods]
2145impl TrackerStrip {
2146  #[getter]
2147  fn get_strip_id           (&self) -> i32 {
2148    self.strip_id
2149  }
2150  
2151  #[getter]
2152  fn get_layer              (&self) -> i32 { 
2153    self.layer
2154  }
2155  
2156  #[getter]
2157  fn get_row                (&self) -> i32 { 
2158    self.row
2159  }
2160  
2161  #[getter]
2162  fn get_module             (&self) -> i32 { 
2163    self.module
2164  }
2165  
2166  #[getter]
2167  fn get_channel            (&self) -> i32 {  
2168    self.channel
2169  }
2170
2171  #[getter]
2172  fn get_global_pos_x_l0    (&self) -> f32 {
2173    self.global_pos_x_l0
2174  }
2175
2176  #[getter]
2177  fn get_global_pos_y_l0    (&self) -> f32 {
2178    self.global_pos_y_l0
2179  }
2180
2181  #[getter]
2182  fn get_global_pos_z_l0    (&self) -> f32 {
2183    self.global_pos_z_l0
2184  }
2185
2186  #[getter]
2187  fn get_global_pos_x_det_l0(&self) -> f32 {
2188    self.global_pos_x_det_l0
2189  }
2190
2191  #[getter]
2192  fn get_global_pos_y_det_l0(&self) -> f32 {
2193    self.global_pos_y_det_l0
2194  }
2195
2196  #[getter]
2197  fn get_global_pos_z_det_l0(&self) -> f32 {
2198    self.global_pos_z_det_l0
2199  }
2200
2201  #[getter]
2202  fn coordinates(&self) -> (f32, f32, f32) {
2203    self.get_coordinates()
2204  }
2205
2206  #[getter]
2207  fn detector_coordinates(&self) -> (f32, f32, f32) {
2208    self.get_detcoordinates() 
2209  }
2210
2211  #[getter]
2212  fn get_principal_x        (&self) -> f32 {
2213    self.principal_x
2214  }
2215  #[getter]
2216  fn get_principal_y        (&self) -> f32 {
2217    self.principal_y
2218  }
2219  #[getter]
2220  fn get_principal_z        (&self) -> f32 {
2221    self.principal_z
2222  }
2223  #[getter]
2224  fn get_volume_id          (&self) -> i64 {
2225    self.volume_id
2226  }
2227
2228  #[staticmethod]
2229  #[pyo3(name="all")]
2230  pub fn all_py() -> Option<Vec<Self>> {
2231    Self::all()
2232  } 
2233  
2234  #[staticmethod]
2235  #[pyo3(name="all_as_dict")]
2236  pub fn all_as_dict_py() -> Option<HashMap<u32,Self>> {
2237    match Self::all_as_dict() {
2238      Err(err) => {
2239        error!("Unable to retrieve tracker strip dictionary. {err}. Did you laod the setup-env.sh shell?");
2240        return None;
2241      }
2242      Ok(_data) => {
2243        return Some(_data);
2244      }
2245    }
2246  } 
2247}
2248
2249#[cfg(feature="pybindings")]
2250pythonize!(TrackerStrip);
2251
2252//------------------------------------------------
2253
2254/// Masking of unusable strips as curated by the tracker team 
2255#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
2256#[diesel(table_name = schema::tof_db_tofpaddletimingconstant)]
2257#[diesel(primary_key(data_id))]
2258#[allow(non_snake_case)]
2259#[cfg_attr(feature="pybindings", pyclass)]
2260pub struct TofPaddleTimingConstant {
2261  pub data_id             : i32,
2262  pub paddle_id           : i32,    
2263  pub volume_id           : i64,    
2264  pub utc_timestamp_start : i64,
2265  pub utc_timestamp_stop  : i64,
2266  pub name                : Option<String>, 
2267  pub version             : Option<i32>,   
2268  pub timing_constant     : f32,  
2269}
2270
2271impl TofPaddleTimingConstant {
2272
2273  pub fn new() -> Self {
2274    Self {
2275      data_id             : 0,
2276      paddle_id           : 0,    
2277      volume_id           : 0,    
2278      utc_timestamp_start : 0,  
2279      utc_timestamp_stop  : 0,
2280      name                : None, 
2281      version             : None,
2282      timing_constant     : 0.0,
2283    }
2284  }
2285
2286  /// Retrieve the names under which the timing constants are 
2287  /// saved 
2288  pub fn all_names() -> Result<Vec<String>, ConnectionError> {
2289    let mut conn = connect_to_db()?;
2290    let mut names = Vec::<String>::new();
2291    let unique_names =
2292      schema::tof_db_tofpaddletimingconstant::table.select(
2293      schema::tof_db_tofpaddletimingconstant::name)
2294      .distinct()
2295      .load::<Option<String>>(&mut conn).expect("Error getting names from db!");
2296    for k in unique_names {
2297      if let Some(n) = k {
2298        names.push(n);
2299      }
2300    }
2301    Ok(names)
2302  }
2303
2304  /// Get Tof timing constants as associated with a distinct name 
2305  ///
2306  /// # Returns:
2307  ///   * HashMap<u32 \[paddle id\], Self> 
2308  pub fn as_dict_by_name(fname : &str) -> Result<HashMap<u8,Self>, ConnectionError> {
2309    use schema::tof_db_tofpaddletimingconstant::dsl::*;
2310    let mut paddles = HashMap::<u8, Self>::new();
2311    if fname == "" {
2312      match Self::all() {
2313        None => {
2314          error!("Unable to retrive ANY TofPaddleTimingConstant");
2315          return Ok(paddles);
2316        }
2317        Some(_paddles) => {
2318          for k in _paddles {
2319            paddles.insert(k.paddle_id as u8, k);
2320          }
2321          return Ok(paddles);
2322        }
2323      }
2324    }
2325    let mut conn = connect_to_db()?;
2326    match tof_db_tofpaddletimingconstant.filter(
2327      schema::tof_db_tofpaddletimingconstant::name.eq(fname)).load::<Self>(&mut conn) {
2328      Err(err) => {
2329        error!("We can't find any TOF paddle timing constants in the database! {err}");
2330        return Ok(paddles);
2331      }
2332      Ok(paddles_) => {
2333        for p in paddles_ {
2334          paddles.insert(p.paddle_id as u8, p );
2335        }
2336      }
2337    }
2338    return Ok(paddles);
2339  }
2340
2341  /// Get all tracker strip mask from the database
2342  ///
2343  /// # Returns:
2344  ///   * HashMap<u32 [strip id], TrackeStripMask> 
2345  pub fn all() -> Option<Vec<Self>> {
2346    use schema::tof_db_tofpaddletimingconstant::dsl::*;
2347    let mut conn = connect_to_db().ok()?;
2348    match tof_db_tofpaddletimingconstant.load::<Self>(&mut conn) {
2349      Err(err) => {
2350        error!("Unable to load TOF paddle timing constants from db! {err}");
2351        return None;
2352      }
2353      Ok(tc) => {
2354        return Some(tc);
2355      }
2356    }
2357  }
2358}
2359
2360impl Default for TofPaddleTimingConstant {
2361  fn default() -> Self {
2362    Self::new()
2363  }
2364}
2365
2366impl fmt::Display for TofPaddleTimingConstant {
2367  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2368    let mut repr = format!("<TofPaddleTimingConstant [{}]:", self.paddle_id);
2369    repr += &(format!("\n   vid           : {}", self.volume_id));
2370    repr += "\n   UTC Timestamps (Begin/End):";
2371    repr += &(format!("\n   {}/{}", self.utc_timestamp_start, self.utc_timestamp_stop));    
2372    if self.name.is_some() {
2373      repr += &(format!("\n   name        : {}", self.name.clone().unwrap())); 
2374    }
2375    if self.version.is_some() {
2376      repr += &(format!("\n   version        : {}", self.version.unwrap())); 
2377    }
2378    repr += &(format!("\n   timing const.    : {}", self.timing_constant));   
2379    write!(f, "{}", repr)
2380  }
2381}
2382
2383#[cfg(feature="pybindings")]
2384#[pymethods]
2385impl TofPaddleTimingConstant {
2386  
2387  #[staticmethod]
2388  #[pyo3(name="all")]
2389  pub fn all_py() -> Option<Vec<Self>> {
2390    Self::all()
2391  } 
2392  
2393  #[staticmethod]
2394  #[pyo3(name="all_names")]
2395  /// Get all names for registered datasets. These
2396  /// can be used in .as_dict_by_name() to query 
2397  /// the db for a set of values
2398  pub fn all_names_py() -> Option<Vec<String>> {
2399    match Self::all_names() {
2400      Err(_) => {
2401        return None;
2402      }
2403      Ok(names) => {
2404        return Some(names);
2405      }
2406    }
2407  }
2408 
2409  /// Get the TOF paddle timing constants as associated by a specific 
2410  /// name
2411  ///
2412  /// # Arguments 
2413  ///   * name : The name the constants are associated with 
2414  #[staticmethod]
2415  #[pyo3(name="as_dict_by_name")]
2416  pub fn all_as_dict_py(name : &str) -> Option<HashMap<u8,Self>> {
2417    match Self::as_dict_by_name(name) {
2418      Err(err) => {
2419        error!("Unable to retrieve TOF paddle timing constants dictionary. {err}. Did you laod the setup-env.sh shell?");
2420        return None;
2421      }
2422      Ok(_data) => {
2423        return Some(_data);
2424      }
2425    }
2426  } 
2427  
2428  #[getter]
2429  fn get_paddle_id     (&self) -> i32 {    
2430    self.paddle_id
2431  }
2432  
2433  #[getter]
2434  fn get_volume_id    (&self) -> i64 {    
2435    self.volume_id
2436  }
2437  
2438  #[getter]
2439  fn get_utc_timestamp_start(&self) -> i64 {
2440    self.utc_timestamp_start
2441  }
2442  
2443  #[getter]
2444  fn get_utc_timestamp_stop(&self) -> i64 {
2445    self.utc_timestamp_stop
2446  }
2447  
2448  #[getter]
2449  fn get_name    (&self) -> Option<String> {
2450    self.name.clone()
2451  }
2452 
2453  #[getter]
2454  fn get_version    (&self) -> Option<i32> {
2455    self.version.clone()
2456  }
2457  
2458  #[getter]
2459  fn get_timing_constant       (&self) -> f32 { 
2460    self.timing_constant
2461  }
2462}
2463
2464#[cfg(feature="pybindings")]
2465pythonize!(TofPaddleTimingConstant);
2466
2467//----------------------------------
2468
2469/// Measurement of tracker pedestal values for each strip
2470#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
2471#[diesel(table_name = schema::tof_db_trackerstrippedestal)]
2472#[diesel(primary_key(data_id))]
2473#[allow(non_snake_case)]
2474#[cfg_attr(feature="pybindings", pyclass)]
2475pub struct TrackerStripPedestal {
2476  pub data_id             : i32,  
2477  pub strip_id            : i32,    
2478  pub volume_id           : i64,    
2479  pub utc_timestamp_start : i64,
2480  pub utc_timestamp_stop  : i64,
2481  pub name                : Option<String>,
2482  pub pedestal_mean       : f32, 
2483  pub pedestal_sigma      : f32, 
2484  pub is_mean_value       : bool,
2485}
2486
2487impl TrackerStripPedestal {
2488
2489  pub fn new() -> Self {
2490    Self {
2491      data_id             : 0,
2492      strip_id            : 0,    
2493      volume_id           : 0,    
2494      utc_timestamp_start : 0,    
2495      utc_timestamp_stop  : 0,
2496      name                : None,
2497      pedestal_mean       : 0.0,
2498      pedestal_sigma      : 0.0,
2499      is_mean_value       : false
2500    }
2501  }
2502  
2503  /// Get Tracker strip pedestals for a certain dataset 
2504  ///
2505  /// # Returns:
2506  ///   * HashMap<u32 [strip id], TrackerStripMask> 
2507  pub fn as_dict_by_name(fname : &str) -> Result<HashMap<u32,Self>, ConnectionError> {
2508    use schema::tof_db_trackerstrippedestal::dsl::*;
2509    let mut strips = HashMap::<u32, Self>::new();
2510    if fname == "" {
2511      match Self::all() {
2512        None => {
2513          error!("Unable to retrive ANY TrackerStripPedestal");
2514          return Ok(strips);
2515        }
2516        Some(_strips) => {
2517          for k in _strips {
2518            strips.insert(k.strip_id as u32, k);
2519          }
2520          return Ok(strips);
2521        }
2522      }
2523    }
2524    let mut conn = connect_to_db()?;
2525    match tof_db_trackerstrippedestal.filter(
2526      schema::tof_db_trackerstrippedestal::name.eq(fname)).load::<Self>(&mut conn) {
2527      Err(err) => {
2528        error!("We can't find any tracker strip masks in the database! {err}");
2529        return Ok(strips);
2530      }
2531      Ok(peds_) => {
2532        for s in peds_ {
2533          strips.insert(s.strip_id as u32, s );
2534        }
2535      }
2536    }
2537    return Ok(strips);
2538  }
2539  
2540  pub fn all_names() -> Result<Vec<String>, ConnectionError> {
2541    let mut conn = connect_to_db()?;
2542    let mut names = Vec::<String>::new();
2543    let unique_names =
2544      schema::tof_db_trackerstrippedestal::table.select(
2545      schema::tof_db_trackerstrippedestal::name)
2546      .distinct()
2547      .load::<Option<String>>(&mut conn).expect("Error getting names from db!");
2548    for k in unique_names {
2549      if let Some(n) = k {
2550        names.push(n);
2551      }
2552    }
2553    Ok(names)
2554  }
2555  
2556  /// Get all tracker strip mask from the database
2557  ///
2558  /// # Returns:
2559  ///   * HashMap<u32 [strip id], TrackeStripMask> 
2560  pub fn all() -> Option<Vec<Self>> {
2561    use schema::tof_db_trackerstrippedestal::dsl::*;
2562    let mut conn = connect_to_db().ok()?;
2563    match tof_db_trackerstrippedestal.load::<Self>(&mut conn) {
2564      Err(err) => {
2565        error!("Unable to load tracker strips pedestals from db! {err}");
2566        return None;
2567      }
2568      Ok(strips) => {
2569        return Some(strips);
2570      }
2571    }
2572  }
2573  
2574  ///
2575  pub fn parse_from_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<Self>> {
2576    let file = File::open(path)?;
2577    let reader = BufReader::new(file);
2578    let mut pedestals = Vec::<Self>::new();
2579    let hid_vid_map = get_hid_vid_maps().unwrap().1;
2580    for line in reader.lines() {
2581      let line = line?;
2582      if line.starts_with("#") || line.starts_with("Layer") || line.starts_with("layer") {
2583        continue;
2584      }
2585      let mut ped = TrackerStripPedestal::new();
2586      let parts: Vec<&str> = line.split_whitespace().collect();
2587      if parts.len() == 6 {
2588        // Parse the first number as a standard decimal
2589        let layer   = parts[0].parse::<u8>()
2590          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
2591        let row     = parts[1].parse::<u8>()
2592          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
2593        let module   = parts[2].parse::<u8>()
2594          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
2595        let channel  = parts[3].parse::<u8>()
2596          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
2597        ped.strip_id  = TrackerStrip::create_stripid(layer, row, module, channel) as i32; 
2598        ped.volume_id = *hid_vid_map.get(&(ped.strip_id as u32)).unwrap() as i64; // critical error is good here,
2599                                                               // should alert the user
2600        ped.pedestal_mean  = parts[4].parse::<f32>()
2601          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
2602        ped.pedestal_sigma = parts[5].parse::<f32>()
2603          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
2604        pedestals.push(ped); 
2605      }
2606    }
2607    Ok(pedestals)
2608  }
2609}
2610
2611impl Default for TrackerStripPedestal {
2612  fn default() -> Self {
2613    Self::new()
2614  }
2615}
2616
2617impl fmt::Display for TrackerStripPedestal {
2618  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2619    let mut repr = format!("<TrackerStripPedestal [{}]:", self.strip_id);
2620    repr += &(format!("\n   vid            : {}", self.volume_id));
2621    repr += "\n   UTC Timestamps (Begin/End):";
2622    repr += &(format!("\n   {}/{}", self.utc_timestamp_start, self.utc_timestamp_stop));    
2623    if self.name.is_some() {
2624      repr += &(format!("\n   name : {}", self.name.clone().unwrap()));
2625    }
2626    repr += &(format!("\n   pedestal_mean  : {}", self.pedestal_mean));    
2627    repr += &(format!("\n   pedestal_sigma : {}", self.pedestal_sigma));    
2628    repr += &(format!("\n   is_mean_value  : {}", self.is_mean_value));    
2629    write!(f, "{}", repr)
2630  }
2631}
2632
2633#[cfg(feature="pybindings")]
2634#[pymethods]
2635impl TrackerStripPedestal {
2636  
2637  #[staticmethod]
2638  #[pyo3(name="parse_from_file")]
2639  fn parse_from_file_py(fname : &str) -> Option<Vec<Self>> {
2640    let result = Self::parse_from_file(fname);
2641    if result.is_ok() {
2642      return Some(result.unwrap());
2643    } else {
2644      error!("An error occured when parsing {} : '{}'", fname, result.unwrap_err());
2645      return None;
2646    }
2647  }
2648  
2649  #[staticmethod]
2650  #[pyo3(name="all")]
2651  pub fn all_py() -> Option<Vec<Self>> {
2652    Self::all()
2653  } 
2654  
2655  #[staticmethod]
2656  #[pyo3(name="all_names")]
2657  /// Get all names for registered datasets. These
2658  /// can be used in .as_dict_by_name() to query 
2659  /// the db for a set of values
2660  pub fn all_names_py() -> Option<Vec<String>> {
2661    match Self::all_names() {
2662      Err(_) => {
2663        return None;
2664      }
2665      Ok(names) => {
2666        return Some(names);
2667      }
2668    }
2669  }
2670  
2671  #[staticmethod]
2672  #[pyo3(name="as_dict_by_name")]
2673  pub fn all_as_dict_py(name : &str) -> Option<HashMap<u32,Self>> {
2674    match Self::as_dict_by_name(name) {
2675      Err(err) => {
2676        error!("Unable to retrieve tracker strip pedestal dictionary. {err}. Did you laod the setup-env.sh shell?");
2677        return None;
2678      }
2679      Ok(_data) => {
2680        return Some(_data);
2681      }
2682    }
2683  } 
2684 
2685  #[getter]
2686  #[pyo3(name="name")]
2687  fn get_name_py (&self) -> Option<String> {
2688    self.name.clone()
2689  }
2690  
2691  #[setter]
2692  #[pyo3(name="name")] 
2693  fn set_name_py(&mut self, value : String) {
2694    self.name = Some(value);
2695  }
2696
2697  #[getter]
2698  fn get_strip_id     (&self) -> i32 {    
2699    self.strip_id
2700  }
2701  
2702  #[getter]
2703  fn get_volume_id    (&self) -> i64 {    
2704    self.volume_id
2705  }
2706  
2707  #[getter]
2708  fn get_utc_timestamp_start(&self) -> i64 {
2709    self.utc_timestamp_start
2710  }
2711  
2712  #[getter]
2713  fn get_utc_timestamp_stop(&self) -> i64 {
2714    self.utc_timestamp_stop
2715  }
2716  
2717  #[setter]
2718  fn set_utc_timestamp_start(&mut self, value : i64) {
2719    self.utc_timestamp_start = value;
2720  }
2721  
2722  #[setter]
2723  fn set_utc_timestamp_stop(&mut self, value : i64) {
2724    self.utc_timestamp_stop = value;
2725  }
2726  
2727  #[getter]
2728  fn get_pedestal_mean    (&self) -> f32 {
2729    self.pedestal_mean
2730  }
2731  
2732  #[getter]
2733  fn get_pedestal_sigam   (&self) -> f32 { 
2734    self.pedestal_sigma
2735  }
2736  
2737  #[getter]
2738  fn get_is_mean_value   (&self) -> bool { 
2739    self.is_mean_value
2740  }
2741}
2742
2743#[cfg(feature="pybindings")]
2744pythonize!(TrackerStripPedestal);
2745
2746//-------------------------------------------------
2747
2748/// Common noise subtraction - pulse channels on the wafers and get the average adc. 
2749/// The gain is available as well. Data from Mengjiao's group 
2750#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
2751#[diesel(table_name = schema::tof_db_trackerstripcmnnoise)]
2752#[diesel(primary_key(data_id))]
2753#[allow(non_snake_case)]
2754#[cfg_attr(feature="pybindings", pyclass)]
2755pub struct TrackerStripCmnNoise {   
2756  pub data_id              : i32,
2757  pub strip_id             : i32,    
2758  pub volume_id            : i64,    
2759  pub utc_timestamp_start  : i64,    
2760  pub utc_timestamp_stop   : i64,    
2761  pub name                 : Option<String>, 
2762  pub gain                 : f32,
2763  pub pulse_chn            : i32,
2764  pub pulse_avg            : f32,
2765  pub gain_is_mean         : bool,
2766  pub pulse_is_mean        : bool,
2767} 
2768
2769impl TrackerStripCmnNoise {
2770
2771  pub fn new() -> Self {
2772    Self {
2773      data_id             : 0,
2774      strip_id            : 0,    
2775      volume_id           : 0,    
2776      utc_timestamp_start : 0,   
2777      utc_timestamp_stop  : 0,
2778      name                : None, 
2779      gain                : 0.0, 
2780      pulse_chn           : 0,
2781      pulse_avg           : 0.0,
2782      gain_is_mean        : false,
2783      pulse_is_mean       : false
2784    }
2785  }
2786  
2787  pub fn all_names() -> Result<Vec<String>, ConnectionError> {
2788    let mut conn = connect_to_db()?;
2789    let mut names = Vec::<String>::new();
2790    let unique_names =
2791      schema::tof_db_trackerstripcmnnoise::table.select(
2792      schema::tof_db_trackerstripcmnnoise::name)
2793      .distinct()
2794      .load::<Option<String>>(&mut conn).expect("Error getting names from db!");
2795    for k in unique_names {
2796      if let Some(n) = k {
2797        names.push(n);
2798      }
2799    }
2800    Ok(names)
2801  }
2802  
2803  /// Get Tracker strip cmn noise data for a certain dataset 
2804  ///
2805  /// # Returns:
2806  ///   * HashMap<u32 [strip id], TrackerStripTransferFn> 
2807  pub fn as_dict_by_name(fname : &str) -> Result<HashMap<u32,Self>, ConnectionError> {
2808    use schema::tof_db_trackerstripcmnnoise::dsl::*;
2809    let mut strips = HashMap::<u32, Self>::new();
2810    if fname == "" {
2811      match Self::all() {
2812        None => {
2813          error!("Unable to retrive ANY TrackerStripCMNNoise Data (pulser)");
2814          return Ok(strips);
2815        }
2816        Some(_strips) => {
2817          for k in _strips {
2818            strips.insert(k.strip_id as u32, k);
2819          }
2820          return Ok(strips);
2821        }
2822      }
2823    }
2824    let mut conn = connect_to_db()?;
2825    match tof_db_trackerstripcmnnoise.filter(
2826      schema::tof_db_trackerstripcmnnoise::name.eq(fname)).load::<Self>(&mut conn) {
2827      Err(err) => {
2828        error!("We can't find any tracker strip common noise information with that name in the database! {err}");
2829        return Ok(strips);
2830      }
2831      Ok(peds_) => {
2832        for s in peds_ {
2833          strips.insert(s.strip_id as u32, s );
2834        }
2835      }
2836    }
2837    return Ok(strips);
2838  }
2839
2840  /// Get all tracker strip transfer functions from the database
2841  ///
2842  /// # Returns:
2843  ///   * HashMap<u32 [strip id], TrackeStripTransferFunction> 
2844  pub fn all() -> Option<Vec<Self>> {
2845    use schema::tof_db_trackerstripcmnnoise::dsl::*;
2846    let mut conn = connect_to_db().ok()?;
2847    match tof_db_trackerstripcmnnoise.load::<Self>(&mut conn) {
2848      Err(err) => {
2849        error!("Unable to load tracker transfer functions from db! {err}");
2850        return None;
2851      }
2852      Ok(strips) => {
2853        return Some(strips);
2854      }
2855    }
2856  }
2857}
2858
2859impl fmt::Display for TrackerStripCmnNoise {
2860  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2861    let mut repr = format!("<TrackerStripCmnNoise [{}]:", self.strip_id);
2862    repr += &(format!("\n   vid              : {}", self.volume_id));
2863    repr += "\n   UTC Timestamps (Begin/End):";
2864    repr += &(format!("\n   {}/{}", self.utc_timestamp_start, self.utc_timestamp_stop));    
2865    if self.gain_is_mean {
2866      repr += &(String::from("\n -- Gain is mean value!"));
2867    }
2868    if self.pulse_is_mean {
2869      repr += &(String::from("\n -- Pulse is mean value!"));
2870    }
2871    if self.name.is_some() {
2872      repr += &(format!("\n   name     : {}", self.name.clone().unwrap())); 
2873    }
2874    repr += &(format!("\n   gain : {} pulse ch : {} pulse avg : {}>", self.gain, self.pulse_chn, self.pulse_avg));
2875    write!(f, "{}", repr)
2876  }
2877}
2878
2879
2880
2881#[cfg(feature="pybindings")]
2882#[pymethods]
2883impl TrackerStripCmnNoise {
2884  
2885  #[staticmethod]
2886  #[pyo3(name="all")]
2887  pub fn all_py() -> Option<Vec<Self>> {
2888    Self::all()
2889  } 
2890 
2891  #[staticmethod]
2892  #[pyo3(name="all_names")]
2893  /// Get all names for registered datasets. These
2894  /// can be used in .as_dict_by_name() to query 
2895  /// the db for a set of values
2896  pub fn all_names_py() -> Option<Vec<String>> {
2897    match Self::all_names() {
2898      Err(_) => {
2899        return None;
2900      }
2901      Ok(names) => {
2902        return Some(names);
2903      }
2904    }
2905  }
2906
2907  #[staticmethod]
2908  #[pyo3(name="as_dict_by_name")]
2909  pub fn all_as_dict_py(name : &str) -> Option<HashMap<u32,Self>> {
2910    match Self::as_dict_by_name(name) {
2911      Err(err) => {
2912        error!("Unable to retrieve tracker strip cmn noise dictionary. {err}. Did you laod the setup-env.sh shell?");
2913        return None;
2914      }
2915      Ok(_data) => {
2916        return Some(_data);
2917      }
2918    }
2919  } 
2920  
2921  #[getter]
2922  fn get_strip_id     (&self) -> i32 {    
2923    self.strip_id
2924  }
2925  
2926  #[getter]
2927  fn get_volume_id    (&self) -> i64 {    
2928    self.volume_id
2929  }
2930  
2931  #[getter]
2932  fn get_utc_timestamp_start(&self) -> i64 {
2933    self.utc_timestamp_start
2934  }
2935  
2936  #[getter]
2937  fn get_utc_timestamp_stop(&self) -> i64 {
2938    self.utc_timestamp_stop
2939  }
2940
2941  #[getter]
2942  fn get_name(&self) -> Option<String> {
2943    self.name.clone()
2944  }
2945      
2946  #[getter]
2947  fn get_gain(&self) -> f32 {
2948    self.gain
2949  }
2950  
2951  #[getter]
2952  fn get_pulse_cn(&self) -> u32 {
2953    self.pulse_chn as u32
2954  }
2955
2956  #[getter]
2957  fn get_gain_is_mean(&self) -> bool {
2958    self.gain_is_mean
2959  }
2960  
2961  #[getter]
2962  fn get_pulse_is_mean(&self) -> bool {
2963    self.pulse_is_mean
2964  }
2965
2966  #[getter]
2967  fn get_pulse_avg(&self) -> f32 {
2968    self.pulse_avg
2969  }
2970
2971  //fn get_common_level(&self, adc : f32) -> f32 {
2972  //  self.common_level(adc)
2973  //}
2974
2975}
2976
2977#[cfg(feature="pybindings")]
2978pythonize!(TrackerStripCmnNoise);
2979
2980//-------------------------------------------------
2981
2982#[derive(Debug, Queryable, Selectable)]
2983#[diesel(table_name = schema::calibration_files)]
2984#[diesel(primary_key(id))]
2985#[allow(non_snake_case)]
2986#[cfg_attr(feature="pybindings", pyclass)]
2987pub struct TrackerCalibrationFile {
2988  pub id                        : i32, 
2989  pub file_type                 : TrackerCalibrationFileType,
2990  pub path                      : String, 
2991  pub from_timestamp            : i64, 
2992  pub to_timestamp              : i64, 
2993}
2994
2995impl TrackerCalibrationFile {
2996  pub fn new() -> Self {
2997    Self {
2998      id             : 0,
2999      file_type      : TrackerCalibrationFileType::Unknown,
3000      path           : String::from(""),
3001      from_timestamp : 0,
3002      to_timestamp   : 0, 
3003    }
3004  }
3005}
3006
3007impl Default for TrackerCalibrationFile {
3008  fn default() -> Self {
3009    Self::new()
3010  }
3011}
3012
3013impl fmt::Display for TrackerCalibrationFile  {
3014  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3015    let mut repr = String::from("<TrackerCalibrationFile:");
3016    repr += &(format!("\n id        : {}", self.id));
3017    repr += &(format!("\n file_type : {}", self.file_type));
3018    repr += &(format!("\n path      : {}", self.path));
3019    repr += &(format!("\n from ts   : {} [{}]", self.from_timestamp, get_utc_timestamp_from_unix(self.from_timestamp as f64).unwrap()));
3020    repr += &(format!("\n to   ts   : {} [{}]", self.to_timestamp,   get_utc_timestamp_from_unix(self.to_timestamp as f64).unwrap()));
3021    write!(f, "{}", repr)
3022  }
3023}
3024
3025#[cfg(feature="pybindings")]
3026pythonize!(TrackerCalibrationFile);
3027
3028#[cfg(feature="pybindings")]
3029#[pymethods]
3030impl TrackerCalibrationFile {
3031      
3032  #[getter]
3033  fn get_id(&self) -> i32 {
3034    self.id
3035  }
3036
3037  #[getter]
3038  fn get_file_type(&self) -> TrackerCalibrationFileType {
3039    self.file_type
3040  }
3041
3042#[getter]
3043  fn get_path(&self) -> String {
3044    self.path.clone()
3045  }
3046
3047#[getter]
3048  fn get_from_timestamp(&self) -> i64 {
3049    self.from_timestamp
3050  }
3051
3052#[getter]
3053  fn get_to_timestamp(&self) -> i64 {
3054    self.to_timestamp
3055  }
3056}
3057
3058//-------------------------------------------------
3059
3060//
3061//
3062//
3063//    
3064//// Summary of DSI/J/LTBCH (0-319)
3065//// This is not "official" but provides a way of indexing all
3066//// the individual channels
3067//#[derive(Debug,PartialEq,Queryable, Selectable)]
3068//#[diesel(table_name = schema::tof_db_mtbchannel)]
3069//#[diesel(primary_key(mtb_ch))]
3070//#[allow(non_snake_case)]
3071//pub struct MTBChannel {
3072//  pub mtb_ch      : i64,         
3073//  pub dsi         : Option<i16>, 
3074//  pub j           : Option<i16>, 
3075//  pub ltb_id      : Option<i16>, 
3076//  pub ltb_ch      : Option<i16>, 
3077//  pub rb_id       : Option<i16>, 
3078//  pub rb_ch       : Option<i16>, 
3079//  pub mtb_link_id : Option<i16>, 
3080//  pub paddle_id   : Option<i16>, 
3081//  pub paddle_isA  : Option<bool>,
3082//  pub hg_ch       : Option<i16>, 
3083//  pub lg_ch       : Option<i16>, 
3084//}
3085//
3086//impl MTBChannel {
3087//
3088//  pub fn new() -> Self {
3089//    Self {
3090//      mtb_ch      : -1,         
3091//      dsi         : None, 
3092//      j           : None, 
3093//      ltb_id      : None, 
3094//      ltb_ch      : None, 
3095//      rb_id       : None, 
3096//      rb_ch       : None, 
3097//      mtb_link_id : None, 
3098//      paddle_id   : None, 
3099//      paddle_isA  : None,
3100//      hg_ch       : None, 
3101//      lg_ch       : None, 
3102//    }
3103//  }
3104//  
3105//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<MTBChannel>> {
3106//    use schema::tof_db_mtbchannel::dsl::*;
3107//    match tof_db_mtbchannel.load::<MTBChannel>(conn) {
3108//      Err(err) => {
3109//        error!("Unable to load RATs from db! {err}");
3110//        return None;
3111//      }
3112//      Ok(mtbch) => {
3113//        return Some(mtbch);
3114//      }
3115//    }
3116//  }
3117//}
3118//
3119//
3120//impl fmt::Display for MTBChannel {
3121//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3122//    let mut repr = String::from("<MTBChannel");
3123//    repr += &(format!("\n  Channel ID : {}", self.mtb_ch));
3124//    repr += &(format!("\n  DSI/J/     : {}/{}", self.dsi.unwrap_or(-1), self.j.unwrap_or(-1)));
3125//    repr += "\n  LTB ID/CH => RB ID/CH";
3126//    repr += &(format!("\n   |-> {}/{} => {}/{}", self.ltb_id.unwrap_or(-1), self.ltb_ch.unwrap_or(-1), self.rb_id.unwrap_or(-1), self.rb_ch.unwrap_or(-1)));
3127//    repr += &(format!("\n  MTB Link ID [RB] : {}", self.mtb_link_id.unwrap_or(-1)));
3128//    repr += "\n  LG CH => HG CH";
3129//    repr += &(format!("\n   |-> {} => {}", self.lg_ch.unwrap_or(-1), self.hg_ch.unwrap_or(-1)));
3130//    repr += &(format!("\n  Paddle Id: {}", self.paddle_id.unwrap_or(-1)));
3131//    let mut pend = "None";
3132//    if !self.paddle_isA.is_none() {
3133//      if self.paddle_isA.unwrap() {
3134//          pend = "A";
3135//      } else {
3136//          pend = "B";
3137//      }
3138//    }
3139//    repr += &(format!("\n  Paddle End: {}>", pend));
3140//    write!(f, "{}", repr)
3141//  }
3142//}
3143//
3144//
3145/////////////////////////////////////////////////////
3146////
3147//// The following models exceed a bit the capabilities
3148//// of Diesel, or my Diesel skill.
3149//// These models contain multiple ForeignKeys, in all
3150//// cases these link to the paddle table. 
3151////
3152//// For each of LocalTriggerBoard, ReadoutBoard, Panel
3153//// we have 2 structs:
3154//// One called DB<entity> and the other <entity>. The
3155//// first does have the ForeignKeys as SmallInt, and 
3156//// the latter looks them up and fills in the blanks
3157////
3158////
3159////
3160//
3161///// The DB wrapper for the LocalTriggerBoard, for 
3162///// easy implementation there are no joins, we do 
3163///// them manually in the public implementation 
3164///// of the LocaltriggerBoard
3165//#[derive(Queryable, Selectable, Identifiable, Associations)]
3166//#[diesel(table_name = schema::tof_db_localtriggerboard)]
3167//#[diesel(primary_key(board_id))]
3168//#[diesel(belongs_to(Paddle, foreign_key=paddle1_id))]
3169//pub struct DBLocalTriggerBoard {
3170//    pub board_id      : i16,    
3171//    pub dsi           : Option<i16>,
3172//    pub j             : Option<i16>,
3173//    pub rat           : Option<i16>,
3174//    pub ltb_id        : Option<i16>, 
3175//    pub cable_len     : f32,
3176//    pub paddle1_id    : Option<i16>,
3177//    pub paddle2_id    : Option<i16>,
3178//    pub paddle3_id    : Option<i16>,
3179//    pub paddle4_id    : Option<i16>,
3180//    pub paddle5_id    : Option<i16>,
3181//    pub paddle6_id    : Option<i16>,
3182//    pub paddle7_id    : Option<i16>,
3183//    pub paddle8_id    : Option<i16>,
3184//}
3185//
3186//impl DBLocalTriggerBoard {
3187//  
3188//  //pub fn new() -> Self {
3189//  //  Self {
3190//  //    board_id      : 0,    
3191//  //    dsi           : None,
3192//  //    j             : None,
3193//  //    rat           : None,
3194//  //    ltb_id        : None, 
3195//  //    cable_len     : 0.0,
3196//  //    paddle1_id    : None,
3197//  //    paddle2_id    : None,
3198//  //    paddle3_id    : None,
3199//  //    paddle4_id    : None,
3200//  //    paddle5_id    : None,
3201//  //    paddle6_id    : None,
3202//  //    paddle7_id    : None,
3203//  //    paddle8_id    : None,
3204//  //  }
3205//  //}
3206//
3207//  /// True if sane dsi and j values are 
3208//  /// assigned to this board
3209//  pub fn connected(&self) -> bool {
3210//    self.dsi != None && self.j != None
3211//  }
3212//
3213//  /// True if all fields are filled with 
3214//  /// reasonable values and not the default
3215//  pub fn valid(&self) -> bool {
3216//    self.board_id      > 0 &&    
3217//    self.dsi       .is_some() && 
3218//    self.j         .is_some() && 
3219//    self.rat       .is_some() && 
3220//    // right now, we explicitly don't care
3221//    // about the ltb_id
3222//    //self.ltb_id    .is_some() &&  
3223//    self.cable_len     > 0.0  &&
3224//    self.paddle1_id.is_some() &&
3225//    self.paddle2_id.is_some() &&
3226//    self.paddle3_id.is_some() &&
3227//    self.paddle4_id.is_some() &&
3228//    self.paddle5_id.is_some() &&
3229//    self.paddle6_id.is_some() &&
3230//    self.paddle7_id.is_some() &&
3231//    self.paddle8_id.is_some()
3232//  }
3233//  
3234//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBLocalTriggerBoard>> {
3235//    use schema::tof_db_localtriggerboard::dsl::*;
3236//    match tof_db_localtriggerboard
3237//        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
3238//        .load::<DBLocalTriggerBoard>(conn) {
3239//      Err(err) => {
3240//        error!("Unable to load LocalTriggerBoards from db! {err}");
3241//        return None;
3242//      }
3243//      Ok(ltbs) => {
3244//        return Some(ltbs);
3245//      }
3246//    }
3247//  }
3248//}
3249//
3250//impl fmt::Display for DBLocalTriggerBoard {
3251//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3252//    let mut repr : String;
3253//    if !self.connected() {
3254//      repr = format!("<DBLocalTriggerBoard: ID {}  - UNCONNECTED>", self.board_id);
3255//    } else {
3256//      repr = String::from("<DBLocalTriggerBoard:");
3257//      repr += &(format!("\n  LTB ID  : {}", self.board_id));             
3258//    }
3259//    repr += &(format!("\n  DSI/J   : {}/{}", self.dsi.unwrap(), self.j.unwrap()));     
3260//    repr += &(format!("\n  RAT ID  : {}", self.rat.unwrap()));
3261//    repr += "\n  H. cable len (MTB connection):";
3262//    repr += &(format!("\n    ->      {}", self.cable_len));
3263//    repr += "\n  -- -- -- -- -- -- -- -- -- -- -- -- -- --";
3264//    repr += "\n  Paddle IDs:";
3265//    repr += &(format!("\n    {:02}", self.paddle1_id.unwrap_or(-1))); 
3266//    repr += &(format!("\n    {:02}", self.paddle2_id.unwrap_or(-1)));  
3267//    repr += &(format!("\n    {:02}", self.paddle3_id.unwrap_or(-1)));  
3268//    repr += &(format!("\n    {:02}", self.paddle4_id.unwrap_or(-1)));  
3269//    repr += &(format!("\n    {:02}", self.paddle5_id.unwrap_or(-1))); 
3270//    repr += &(format!("\n    {:02}", self.paddle6_id.unwrap_or(-1))); 
3271//    repr += &(format!("\n    {:02}", self.paddle7_id.unwrap_or(-1))); 
3272//    repr += &(format!("\n    {:02}", self.paddle8_id.unwrap_or(-1))); 
3273//    write!(f, "{}", repr)
3274//  }
3275//}
3276//
3277//#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
3278//pub struct LocalTriggerBoard {
3279//    pub board_id      : u8,    
3280//    pub dsi           : u8,
3281//    pub j             : u8,
3282//    pub rat           : u8,
3283//    pub ltb_id        : u8, 
3284//    pub cable_len     : f32,
3285//    pub paddle1       : Paddle,
3286//    pub paddle2       : Paddle,
3287//    pub paddle3       : Paddle,
3288//    pub paddle4       : Paddle,
3289//    pub paddle5       : Paddle,
3290//    pub paddle6       : Paddle,
3291//    pub paddle7       : Paddle,
3292//    pub paddle8       : Paddle,
3293//}
3294//
3295//impl LocalTriggerBoard {
3296//  
3297//  pub fn new() -> Self {
3298//    Self {
3299//      board_id      : 0,    
3300//      dsi           : 0,
3301//      j             : 0,
3302//      rat           : 0,
3303//      ltb_id        : 0, 
3304//      cable_len     : 0.0,
3305//      paddle1       : Paddle::new(),
3306//      paddle2       : Paddle::new(),
3307//      paddle3       : Paddle::new(),
3308//      paddle4       : Paddle::new(),
3309//      paddle5       : Paddle::new(),
3310//      paddle6       : Paddle::new(),
3311//      paddle7       : Paddle::new(),
3312//      paddle8       : Paddle::new(),
3313//    }
3314//  }
3315//  
3316//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<LocalTriggerBoard>> {
3317//    use schema::tof_db_localtriggerboard::dsl::*;
3318//    let db_ltbs : Vec<DBLocalTriggerBoard>;
3319//    match tof_db_localtriggerboard
3320//        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
3321//        .load::<DBLocalTriggerBoard>(conn) {
3322//      Err(err) => {
3323//        error!("Unable to load LocalTriggerBoards from db! {err}");
3324//        return None;
3325//      }
3326//      Ok(ltbs) => {
3327//        db_ltbs = ltbs;
3328//      }
3329//    }
3330//    let paddles_op = Paddle::all(conn);
3331//    match paddles_op {
3332//      None => {
3333//        return None;
3334//      }
3335//      Some(_) => ()
3336//    }
3337//    let paddles = paddles_op.unwrap();
3338//    // This is not the best and fastest, but since our diesel skills 
3339//    // are a merely 3, we can't do it right now.
3340//    let mut ltbs = Vec::<LocalTriggerBoard>::new();
3341//    //println!("Iterating over {} ltbs in the DB!", db_ltbs.len());
3342//    for dbltb in db_ltbs {
3343//      let mut ltb  = LocalTriggerBoard::new();
3344//      for pdl in paddles.iter() {
3345//        // this call ensures that the following unwraps
3346//        // go through
3347//        if !dbltb.valid() {
3348//          error!("Got unpopulated LTB from DB for LTB {}", dbltb);
3349//          continue;
3350//        }
3351//        if pdl.paddle_id == dbltb.paddle1_id.unwrap() {
3352//          ltb.board_id  = dbltb.board_id as u8;        
3353//          ltb.dsi       = dbltb.dsi.unwrap_or(0) as u8;
3354//          ltb.j         = dbltb.j.unwrap_or(0) as u8;     
3355//          ltb.rat       = dbltb.rat.unwrap_or(0) as u8;     
3356//          ltb.ltb_id    = dbltb.ltb_id.unwrap_or(0) as u8;    
3357//          ltb.cable_len = dbltb.cable_len;    
3358//          ltb.paddle1   = pdl.clone();
3359//        }
3360//        if pdl.paddle_id == dbltb.paddle2_id.unwrap() {
3361//          ltb.paddle2   = pdl.clone();
3362//        }
3363//        if pdl.paddle_id == dbltb.paddle3_id.unwrap() {
3364//          ltb.paddle3   = pdl.clone();
3365//        }
3366//        if pdl.paddle_id == dbltb.paddle4_id.unwrap() {
3367//          ltb.paddle4   = pdl.clone();
3368//        }
3369//        if pdl.paddle_id == dbltb.paddle5_id.unwrap() {
3370//          ltb.paddle5   = pdl.clone();
3371//        }
3372//        if pdl.paddle_id == dbltb.paddle6_id.unwrap() {
3373//          ltb.paddle6   = pdl.clone();
3374//        }
3375//        if pdl.paddle_id == dbltb.paddle7_id.unwrap() {
3376//          ltb.paddle7   = pdl.clone();
3377//        }
3378//        if pdl.paddle_id == dbltb.paddle8_id.unwrap() {
3379//          ltb.paddle8   = pdl.clone();
3380//        }
3381//      }
3382//      ltbs.push(ltb);
3383//    }
3384//    Some(ltbs)
3385//  }
3386//}
3387//
3388//impl fmt::Display for LocalTriggerBoard {
3389//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3390//    let mut repr : String;
3391//    repr = String::from("<LocalTriggerBoard:");
3392//    repr += &(format!("\n  LTB ID  : {}", self.board_id));             
3393//    repr += &(format!("\n  DSI/J   : {}/{}", self.dsi, self.j));     
3394//    repr += &(format!("\n  RAT ID  : {}", self.rat));
3395//    repr += "\n  H. cable len (MTB connection):";
3396//    repr += &(format!("\n    ->      {}", self.cable_len));
3397//    repr += "\n  -- -- -- -- -- -- -- -- -- -- -- -- -- --";
3398//    repr += "\n  LTB Ch -> RB Id, RB chn, Pdl ID, Pan ID:";
3399//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}",  self.paddle1.rb_id, self.paddle1.rb_chA, self.paddle1.rb_chB, self.paddle1.paddle_id, self.paddle1.panel_id)); 
3400//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}",  self.paddle2.rb_id, self.paddle2.rb_chA, self.paddle2.rb_chB, self.paddle2.paddle_id, self.paddle2.panel_id));  
3401//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}",  self.paddle3.rb_id, self.paddle3.rb_chA, self.paddle3.rb_chB, self.paddle3.paddle_id, self.paddle3.panel_id));  
3402//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}",  self.paddle4.rb_id, self.paddle4.rb_chA, self.paddle4.rb_chB, self.paddle4.paddle_id, self.paddle4.panel_id));  
3403//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}",  self.paddle5.rb_id, self.paddle5.rb_chA, self.paddle5.rb_chB, self.paddle5.paddle_id, self.paddle5.panel_id)); 
3404//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}",  self.paddle6.rb_id, self.paddle6.rb_chA, self.paddle6.rb_chB, self.paddle6.paddle_id, self.paddle6.panel_id)); 
3405//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}",  self.paddle7.rb_id, self.paddle7.rb_chA, self.paddle7.rb_chB, self.paddle7.paddle_id, self.paddle7.panel_id)); 
3406//    repr += &(format!("\n            {:02}   |   {},{} |  {:03} | {:02}>", self.paddle8.rb_id, self.paddle8.rb_chA, self.paddle8.rb_chB, self.paddle8.paddle_id, self.paddle8.panel_id)); 
3407//    write!(f, "{}", repr)
3408//  }
3409//}
3410//
3411///// A Readoutboard with paddles connected
3412///// 
3413//#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
3414//#[diesel(table_name = schema::tof_db_readoutboard)]
3415//#[diesel(primary_key(rb_id_id))]
3416//#[allow(non_snake_case)]
3417//pub struct DBReadoutBoard {
3418//  // FIXME - this HAS TO BE (MUST!) the same order
3419//  // as in schema.rs !!
3420//  pub rb_id        : i16, 
3421//  pub dsi          : i16, 
3422//  pub j            : i16, 
3423//  pub mtb_link_id  : i16, 
3424//  pub paddle12_chA : Option<i16>,
3425//  pub paddle34_chA : Option<i16>,
3426//  pub paddle56_chA : Option<i16>,
3427//  pub paddle78_chA : Option<i16>,
3428//  pub paddle12_id  : Option<i16>,
3429//  pub paddle34_id  : Option<i16>,
3430//  pub paddle56_id  : Option<i16>,
3431//  pub paddle78_id  : Option<i16>,
3432//}
3433//
3434//impl DBReadoutBoard {
3435//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBReadoutBoard>> {
3436//    use schema::tof_db_readoutboard::dsl::*;
3437//    match tof_db_readoutboard
3438//        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
3439//        .load::<DBReadoutBoard>(conn) {
3440//      Err(err) => {
3441//        error!("Unable to load ReadoutBoards from db! {err}");
3442//        return None;
3443//      }
3444//      Ok(rbs) => {
3445//        return Some(rbs);
3446//      }
3447//    }
3448//  }
3449//}
3450//
3451//impl fmt::Display for DBReadoutBoard {
3452//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3453//    let mut repr  = String::from("<ReadoutBoard:");
3454//    repr += &(format!("\n  Board id    : {}",self.rb_id));            
3455//    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
3456//    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
3457//    repr += "\n **Connected paddles**";
3458//    repr += &(format!("\n  Ch0/1(1/2)  : {}", self.paddle12_id.unwrap_or(-1)));         
3459//    repr += &(format!("\n  Ch1/2(2/3)  : {}", self.paddle34_id.unwrap_or(-1)));         
3460//    repr += &(format!("\n  Ch2/3(3/4)  : {}", self.paddle56_id.unwrap_or(-1)));         
3461//    repr += &(format!("\n  Ch3/4(4/5)  : {}>",self.paddle78_id.unwrap_or(-1)));         
3462//    write!(f, "{}", repr)
3463//  }
3464//}
3465//
3466///// A Readoutboard with paddles connected
3467//#[derive(Debug, Clone)]
3468//#[allow(non_snake_case)]
3469//pub struct ReadoutBoard {
3470//  pub rb_id           : u8, 
3471//  pub dsi             : u8, 
3472//  pub j               : u8, 
3473//  pub mtb_link_id     : u8, 
3474//  pub paddle12        : Paddle,
3475//  pub paddle12_chA    : u8,
3476//  pub paddle34        : Paddle,
3477//  pub paddle34_chA    : u8,
3478//  pub paddle56        : Paddle,
3479//  pub paddle56_chA    : u8,
3480//  pub paddle78        : Paddle,
3481//  pub paddle78_chA    : u8,
3482//  // extra stuff, not from the db
3483//  // or maybe in the future?
3484//  pub calib_file_path : String,
3485//  pub calibration     : RBCalibrations,       
3486//}
3487//
3488//impl ReadoutBoard {
3489//
3490//  pub fn new() -> Self {
3491//    Self {
3492//      rb_id           : 0, 
3493//      dsi             : 0, 
3494//      j               : 0, 
3495//      mtb_link_id     : 0, 
3496//      paddle12        : Paddle::new(),
3497//      paddle12_chA    : 0,
3498//      paddle34        : Paddle::new(),
3499//      paddle34_chA    : 0,
3500//      paddle56        : Paddle::new(),
3501//      paddle56_chA    : 0,
3502//      paddle78        : Paddle::new(),
3503//      paddle78_chA    : 0,
3504//      calib_file_path : String::from(""),
3505//      calibration     : RBCalibrations::new(0),
3506//    }
3507//  }
3508//
3509//  /// Returns the ip address following a convention
3510//  ///
3511//  /// This does NOT GUARANTEE that the address is correct!
3512//  pub fn guess_address(&self) -> String {
3513//    format!("tcp://10.0.1.1{:02}:42000", self.rb_id)
3514//  }
3515// 
3516//  pub fn get_paddle_ids(&self) -> [u8;4] {
3517//    let pid0 = self.paddle12.paddle_id as u8;
3518//    let pid1 = self.paddle34.paddle_id as u8;
3519//    let pid2 = self.paddle56.paddle_id as u8;
3520//    let pid3 = self.paddle78.paddle_id as u8;
3521//    [pid0, pid1, pid2, pid3]
3522//  }
3523//
3524//  #[allow(non_snake_case)]
3525//  pub fn get_A_sides(&self) -> [u8;4] {
3526//    let pa_0 = self.paddle12_chA;
3527//    let pa_1 = self.paddle34_chA;
3528//    let pa_2 = self.paddle56_chA;
3529//    let pa_3 = self.paddle78_chA;
3530//    [pa_0, pa_1, pa_2, pa_3]
3531//  }
3532//
3533//  #[allow(non_snake_case)]
3534//  pub fn get_pid_rbchA(&self, pid : u8) -> Option<u8> {
3535//    if self.paddle12.paddle_id as u8 == pid {
3536//      let rv = self.paddle12.rb_chA as u8;
3537//      return Some(rv);
3538//    } else if self.paddle34.paddle_id as u8 == pid {
3539//      let rv = self.paddle34.rb_chA as u8;
3540//      return Some(rv);
3541//    } else if self.paddle56.paddle_id as u8 == pid {
3542//      let rv = self.paddle56.rb_chA as u8;
3543//      return Some(rv);
3544//    } else if self.paddle78.paddle_id as u8== pid {
3545//      let rv = self.paddle78.rb_chA as u8;
3546//      return Some(rv);
3547//    } else {
3548//      return None;
3549//    }
3550//  }
3551//  
3552//  #[allow(non_snake_case)]
3553//  pub fn get_pid_rbchB(&self, pid : u8) -> Option<u8> {
3554//    if self.paddle12.paddle_id as u8 == pid {
3555//      let rv = self.paddle12.rb_chB as u8;
3556//      return Some(rv);
3557//    } else if self.paddle34.paddle_id as u8== pid {
3558//      let rv = self.paddle34.rb_chB as u8;
3559//      return Some(rv);
3560//    } else if self.paddle56.paddle_id as u8== pid {
3561//      let rv = self.paddle56.rb_chB as u8;
3562//      return Some(rv);
3563//    } else if self.paddle78.paddle_id as u8 == pid {
3564//      let rv = self.paddle78.rb_chB as u8;
3565//      return Some(rv);
3566//    } else {
3567//      return None;
3568//    }
3569//  }
3570//
3571//  pub fn get_paddle_length(&self, pid : u8) -> Option<f32> {
3572//    if self.paddle12.paddle_id as u8 == pid {
3573//      let rv = self.paddle12.length;
3574//      return Some(rv);
3575//    } else if self.paddle34.paddle_id as u8== pid {
3576//      let rv = self.paddle34.length;
3577//      return Some(rv);
3578//    } else if self.paddle56.paddle_id as u8== pid {
3579//      let rv = self.paddle56.length;
3580//      return Some(rv);
3581//    } else if self.paddle78.paddle_id as u8 == pid {
3582//      let rv = self.paddle78.length;
3583//      return Some(rv);
3584//    } else {
3585//      return None;
3586//    }
3587//  }
3588//
3589//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<ReadoutBoard>> {
3590//    use schema::tof_db_readoutboard::dsl::*;
3591//    let db_rbs : Vec<DBReadoutBoard>;
3592//    match tof_db_readoutboard
3593//        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
3594//        .load::<DBReadoutBoard>(conn) {
3595//      Err(err) => {
3596//        error!("Unable to load ReadoutBoards from db! {err}");
3597//        return None;
3598//      }
3599//      Ok(rbs) => {
3600//        db_rbs = rbs;
3601//      }
3602//    }
3603//    let paddles_op = Paddle::all(conn);
3604//    match paddles_op {
3605//      None => {
3606//        return None;
3607//      }
3608//      Some(_) => ()
3609//    }
3610//    let paddles = paddles_op.unwrap();
3611//    // This is not the best and fastest, but since our diesel skills 
3612//    // are a merely 3, we can't do it right now.
3613//    let mut rbs = Vec::<ReadoutBoard>::new();
3614//    //println!("Iterating over {} rbs in the DB!", db_rbs.len());
3615//    for dbrb in db_rbs {
3616//      let mut rb  = ReadoutBoard::new();
3617//      rb.rb_id        = dbrb.rb_id as u8;        
3618//      rb.dsi          = dbrb.dsi as u8;
3619//      rb.j            = dbrb.j  as u8;     
3620//      rb.mtb_link_id  = dbrb.mtb_link_id  as u8;    
3621//      rb.paddle12_chA = dbrb.paddle12_chA.unwrap() as u8;
3622//      rb.paddle34_chA = dbrb.paddle34_chA.unwrap() as u8;
3623//      rb.paddle56_chA = dbrb.paddle56_chA.unwrap() as u8;
3624//      rb.paddle78_chA = dbrb.paddle78_chA.unwrap() as u8;
3625//      for pdl in paddles.iter() {
3626//        // this call ensures that the following unwraps
3627//        // go through
3628//        //if !dbltb.valid() {
3629//        //  error!("Got unpopulated LTB from DB for LTB {}", dbltb);
3630//        //  continue;
3631//        //}
3632//        if pdl.paddle_id == dbrb.paddle12_id.unwrap_or(0) {
3633//          rb.paddle12     = pdl.clone();
3634//        }
3635//        if pdl.paddle_id == dbrb.paddle34_id.unwrap_or(0) {
3636//          rb.paddle34   = pdl.clone();
3637//        }
3638//        if pdl.paddle_id == dbrb.paddle56_id.unwrap_or(0) {
3639//          rb.paddle56   = pdl.clone();
3640//        }
3641//        if pdl.paddle_id == dbrb.paddle78_id.unwrap_or(0) {
3642//          rb.paddle78   = pdl.clone();
3643//        }
3644//      }
3645//      rbs.push(rb);
3646//    }
3647//    Some(rbs)
3648//  }
3649//  
3650//  // FIXME - better query
3651//  pub fn where_rbid(conn: &mut SqliteConnection, rb_id : u8) -> Option<ReadoutBoard> {
3652//    let all = ReadoutBoard::all(conn)?;
3653//    for rb in all {
3654//      if rb.rb_id == rb_id {
3655//        return Some(rb);
3656//      }
3657//    }
3658//    None
3659//  }
3660//
3661//  pub fn to_summary_str(&self) -> String {
3662//    let mut repr  = String::from("<ReadoutBoard:");
3663//    repr += &(format!("\n  Board id    : {}",self.rb_id));            
3664//    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
3665//    repr += &(format!("\n  RAT         : {}",self.paddle12.ltb_id));
3666//    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
3667//    repr += "\n **Connected paddles**";
3668//    repr += &(format!("\n  Channel 1/2 : {:02} (panel {:01})", self.paddle12.paddle_id, self.paddle12.panel_id));
3669//    repr += &(format!("\n  Channel 3/4 : {:02} (panel {:01})", self.paddle34.paddle_id, self.paddle34.panel_id));
3670//    repr += &(format!("\n  Channel 5/6 : {:02} (panel {:01})", self.paddle56.paddle_id, self.paddle56.panel_id));
3671//    repr += &(format!("\n  Channel 7/8 : {:02} (panel {:01})", self.paddle78.paddle_id, self.paddle78.panel_id));
3672//    repr
3673//  }
3674//
3675//  /// Load the newest calibration from the calibration file path
3676//  pub fn load_latest_calibration(&mut self) -> Result<(), Box<dyn std::error::Error>> {
3677//    //  files look like RB20_2024_01_26-08_15_54.cali.tof.gaps
3678//    //let re = Regex::new(r"(\d{4}_\d{2}_\d{2}-\d{2}_\d{2}_\d{2})")?;
3679//    let re = Regex::new(r"(\d{6}_\d{6})")?;
3680//    // Define your file pattern (e.g., "logs/*.log" for all .log files in the logs directory)
3681//    let pattern = format!("{}/RB{:02}_*", self.calib_file_path, self.rb_id); // Adjust this pattern to your files' naming convention
3682//    let timestamp = DateTime::<Utc>::from_timestamp(0,0).unwrap(); // I am not sure what to do here
3683//                                                                   // otherwise than unwrap. How is
3684//                                                                   // this allowed to fail?
3685//    //let mut newest_file = (String::from(""), NaiveDateTime::from_timestamp(0, 0));
3686//    let mut newest_file = (String::from(""), timestamp);
3687//
3688//    // Iterate over files that match the pattern
3689//    let mut filename : String;
3690//    for entry in glob(&pattern)? {
3691//      if let Ok(path) = entry {
3692//        // Get the filename as a string
3693//        //let cpath = path.clone();
3694//        match path.file_name() {
3695//          None => continue,
3696//          Some(fname) => {
3697//              // the expect might be ok, since this is something done during initialization
3698//              filename = fname.to_os_string().into_string().expect("Unwrapping filename failed!");
3699//          }
3700//        }
3701//        if let Some(caps) = re.captures(&filename) {
3702//          if let Some(timestamp_str) = caps.get(0).map(|m| m.as_str()) {
3703//            //println!("timestamp_str {}, {}",timestamp_str, HUMAN_TIMESTAMP_FORMAT);
3704//            //let timestamp = NaiveDateTime::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
3705//            //let timestamp = DateTime::<Utc>::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
3706//            let footzstring = format!("{}+0000", timestamp_str);
3707//            let timestamp = DateTime::parse_from_str(&footzstring, "%y%m%d_%H%M%S%z")?;
3708//            //let timestamp = DateTime::parse_from_str(&footzstring, HUMAN_TIMESTAMP_FORMAT)?;
3709//            //println!("parse successful");
3710//            //let _timestamp = DateTime
3711//            if timestamp > newest_file.1 {
3712//              // FIXME - into might panic?
3713//              newest_file.1 = timestamp.into();
3714//              newest_file.0 = filename.clone();
3715//            }
3716//          }
3717//        }
3718//      }
3719//    }
3720//    
3721//    if newest_file.0.is_empty() {
3722//      error!("No matching calibration available for board {}!", self.rb_id);
3723//    } else {
3724//      let file_to_load = format!("{}/{}", self.calib_file_path, newest_file.0);
3725//      info!("Loading calibration from file: {}", file_to_load);
3726//      self.calibration = RBCalibrations::from_file(file_to_load, true)?;
3727//    }
3728//    Ok(())
3729//  }
3730//}
3731//
3732//impl fmt::Display for ReadoutBoard {
3733//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3734//    let mut repr  = String::from("<ReadoutBoard:");
3735//    repr += &(format!("\n  Board id    : {}",self.rb_id));            
3736//    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
3737//    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
3738//    repr += "\n **Connected paddles**";
3739//    repr += &(format!("\n  Ch0/1(1/2)  : {}",self.paddle12)); 
3740//    repr += &(format!("\n    A-side    : {}", self.paddle12_chA));
3741//    repr += &(format!("\n  Ch1/2(2/3)  : {}",self.paddle34));         
3742//    repr += &(format!("\n    A-side    : {}", self.paddle34_chA));
3743//    repr += &(format!("\n  Ch2/3(3/4)  : {}",self.paddle56));         
3744//    repr += &(format!("\n    A-side    : {}", self.paddle56_chA));
3745//    repr += &(format!("\n  Ch3/4(4/5)  : {}>",self.paddle78));         
3746//    repr += &(format!("\n    A-side    : {}", self.paddle78_chA));
3747//    repr += "** calibration will be loaded from this path:";
3748//    repr += &(format!("\n      \u{021B3} {}", self.calib_file_path));
3749//    repr += &(format!("\n  calibration : {}>", self.calibration));
3750//    write!(f, "{}", repr)
3751//  }
3752//}
3753//
3754//
3755///// A TOF Panel is a larger unit of paddles next to each other
3756/////
3757///// TOF faces (e.g. Umbrella) can have multiple Panels
3758//#[derive(Debug, Clone,Queryable, Selectable)]
3759//#[diesel(table_name = schema::tof_db_panel)]
3760//#[diesel(primary_key(panel_id))]
3761//pub struct DBPanel {
3762//  // ORDER OF THESE FIELDS HAS TO BE THE SAME AS IN schema.rs!!
3763//  pub  panel_id    : i16        ,   
3764//  pub  description : String     ,   
3765//  pub  normal_x    : i16        ,   
3766//  pub  normal_y    : i16        ,   
3767//  pub  normal_z    : i16        ,   
3768//  pub  dw_paddle   : Option<i16>,   
3769//  pub  dh_paddle   : Option<i16>,   
3770//  pub  paddle0_id  : Option<i16>,   
3771//  pub  paddle1_id  : Option<i16>,   
3772//  pub  paddle10_id : Option<i16>,   
3773//  pub  paddle11_id : Option<i16>,   
3774//  pub  paddle2_id  : Option<i16>,   
3775//  pub  paddle3_id  : Option<i16>,   
3776//  pub  paddle4_id  : Option<i16>,   
3777//  pub  paddle5_id  : Option<i16>,   
3778//  pub  paddle6_id  : Option<i16>,   
3779//  pub  paddle7_id  : Option<i16>,   
3780//  pub  paddle8_id  : Option<i16>,   
3781//  pub  paddle9_id  : Option<i16>,   
3782//}
3783//
3784//impl DBPanel {
3785//
3786//  pub fn valid(&self) -> bool {
3787//    self.panel_id    > 0 &&    
3788//    self.description != String::from("") &&   
3789//    self.paddle0_id.is_some()   
3790//  }
3791//
3792//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBPanel>> {
3793//    use schema::tof_db_panel::dsl::*;
3794//    match tof_db_panel
3795//        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
3796//        .load::<DBPanel>(conn) {
3797//      Err(err) => {
3798//        error!("Unable to load Panels from db! {err}");
3799//        return None;
3800//      }
3801//      // dirty mind check
3802//      Ok(pnls) => {
3803//        return Some(pnls);
3804//      }
3805//    }
3806//  }
3807//  
3808//  pub fn get_npaddles(&self) -> u8 {
3809//    let mut npaddles = 0u8;
3810//    if self.paddle0_id.is_some() {
3811//      npaddles += 1;
3812//    }
3813//    if self.paddle1_id.is_some() {
3814//      npaddles += 1;
3815//    }
3816//    if self.paddle2_id.is_some() {
3817//      npaddles += 1;
3818//    }
3819//    if self.paddle3_id.is_some() {
3820//      npaddles += 1;
3821//    }
3822//    if self.paddle4_id.is_some() {
3823//      npaddles += 1;
3824//    }
3825//    if self.paddle5_id.is_some() {
3826//      npaddles += 1;
3827//    }
3828//    if self.paddle6_id.is_some() {
3829//      npaddles += 1;
3830//    }
3831//    if self.paddle7_id.is_some() {
3832//      npaddles += 1;
3833//    }
3834//    if self.paddle8_id.is_some() {
3835//      npaddles += 1;
3836//    }
3837//    if self.paddle9_id.is_some() {
3838//      npaddles += 1;
3839//    }
3840//    if self.paddle10_id.is_some() {
3841//      npaddles += 1;
3842//    }
3843//    if self.paddle11_id.is_some() {
3844//      npaddles += 1;
3845//    }
3846//    npaddles
3847//  }
3848//}
3849//
3850//impl fmt::Display for DBPanel {
3851//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3852//    let mut repr = String::from("<DBPanel");
3853//    repr += &(format!("\n  id    : {}",self.panel_id));
3854//    repr += &(format!("\n  descr : {}",self.description));
3855//    repr += "\n  orientation:";
3856//    repr += &(format!("\n   [{},{},{}]", self.normal_x, self.normal_y, self.normal_z));
3857//    repr += &(format!("\n  paddle list ({}) paddles)", self.get_npaddles()));
3858//    if self.paddle0_id.is_some() {
3859//      repr += &(format!("\n   {}",self.paddle0_id.unwrap()));
3860//    }
3861//    if self.paddle1_id.is_some() {
3862//      repr += &(format!("\n   {}",self.paddle1_id.unwrap()));
3863//    }
3864//    if self.paddle2_id.is_some() { 
3865//      repr += &(format!("\n   {}",self.paddle2_id.unwrap()));
3866//    }
3867//    if self.paddle3_id.is_some() { 
3868//      repr += &(format!("\n   {}",self.paddle3_id.unwrap()));
3869//    }
3870//    if self.paddle4_id.is_some() {
3871//      repr += &(format!("\n   {}",self.paddle4_id.unwrap()));
3872//    }
3873//    if self.paddle5_id.is_some() {
3874//      repr += &(format!("\n   {}",self.paddle5_id.unwrap()));
3875//    }
3876//    if self.paddle6_id.is_some()  {
3877//      repr += &(format!("\n   {}",self.paddle6_id.unwrap()));
3878//    }
3879//    if self.paddle7_id.is_some() {
3880//      repr += &(format!("\n   {}",self.paddle7_id.unwrap()));
3881//    }
3882//    if self.paddle8_id.is_some() {
3883//      repr += &(format!("\n   {}",self.paddle8_id.unwrap()));
3884//    }
3885//    if self.paddle9_id.is_some() {
3886//      repr += &(format!("\n   {}",self.paddle9_id.unwrap()));
3887//    }
3888//    if self.paddle10_id.is_some() {
3889//      repr += &(format!("\n   {}",self.paddle10_id.unwrap()));
3890//    }
3891//    if self.paddle11_id.is_some() {
3892//      repr += &(format!("\n   {}",self.paddle11_id.unwrap()));
3893//    }
3894//    repr += ">";
3895//    write!(f, "{}", repr)
3896//  }
3897//}
3898//
3899//pub struct Panel {
3900//  pub  panel_id    : u8        ,   
3901//  pub  description : String    ,   
3902//  pub  normal_x    : u8        ,   
3903//  pub  normal_y    : u8        ,   
3904//  pub  normal_z    : u8        ,   
3905//  pub  paddle0  : Paddle,   
3906//  pub  paddle1  : Option<Paddle>,   
3907//  pub  paddle2  : Option<Paddle>,   
3908//  pub  paddle3  : Option<Paddle>,   
3909//  pub  paddle4  : Option<Paddle>,   
3910//  pub  paddle5  : Option<Paddle>,   
3911//  pub  paddle6  : Option<Paddle>,   
3912//  pub  paddle7  : Option<Paddle>,   
3913//  pub  paddle8  : Option<Paddle>,   
3914//  pub  paddle9  : Option<Paddle>,   
3915//  pub  paddle10 : Option<Paddle>,   
3916//  pub  paddle11 : Option<Paddle>,   
3917//  // FIXME - these are for the future 
3918//  // when we are buiding the geometry 
3919//  // from the database
3920//  //pub  dh_paddle   : Option<>,   
3921//  //pub  dw_paddle   : Option<>,   
3922//}
3923//
3924//impl Panel {
3925// 
3926//  pub fn new() -> Self {
3927//    Self {
3928//      panel_id    : 0        ,   
3929//      description : String::from(""),   
3930//      normal_x    : 0        ,   
3931//      normal_y    : 0        ,   
3932//      normal_z    : 0        ,   
3933//      paddle0     : Paddle::new(),   
3934//      paddle1     : None,   
3935//      paddle2     : None,   
3936//      paddle3     : None,   
3937//      paddle4     : None,   
3938//      paddle5     : None,   
3939//      paddle6     : None,   
3940//      paddle7     : None,   
3941//      paddle8     : None,   
3942//      paddle9     : None,   
3943//      paddle10    : None,   
3944//      paddle11    : None,   
3945//    }
3946//  }
3947//
3948//
3949//  pub fn get_npaddles(&self) -> u8 {
3950//    let mut npaddles = 1u8;
3951//    if self.paddle1.is_some() {
3952//      npaddles += 1;
3953//    }
3954//    if self.paddle2.is_some() {
3955//      npaddles += 1;
3956//    }
3957//    if self.paddle3.is_some() {
3958//      npaddles += 1;
3959//    }
3960//    if self.paddle4.is_some() {
3961//      npaddles += 1;
3962//    }
3963//    if self.paddle5.is_some() {
3964//      npaddles += 1;
3965//    }
3966//    if self.paddle6.is_some() {
3967//      npaddles += 1;
3968//    }
3969//    if self.paddle7.is_some() {
3970//      npaddles += 1;
3971//    }
3972//    if self.paddle8.is_some() {
3973//      npaddles += 1;
3974//    }
3975//    if self.paddle9.is_some() {
3976//      npaddles += 1;
3977//    }
3978//    if self.paddle10.is_some() {
3979//      npaddles += 1;
3980//    }
3981//    if self.paddle11.is_some() {
3982//      npaddles += 1;
3983//    }
3984//    npaddles
3985//  }
3986//  
3987//  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<Panel>> {
3988//    use schema::tof_db_panel::dsl::*;
3989//    let db_panels : Vec<DBPanel>;
3990//    match tof_db_panel
3991//        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
3992//        .load::<DBPanel>(conn) {
3993//      Err(err) => {
3994//        error!("Unable to load Panels from db! {err}");
3995//        return None;
3996//      }
3997//      Ok(pnls) => {
3998//        db_panels = pnls;
3999//      }
4000//    }
4001//    let paddles_op = Paddle::all(conn);
4002//    match paddles_op {
4003//      None => {
4004//        return None;
4005//      }
4006//      Some(_) => ()
4007//    }
4008//    let paddles = paddles_op.unwrap();
4009//    // This is not the best and fastest, but since our diesel skills 
4010//    // are a merely 3, we can't do it right now.
4011//    let mut panels = Vec::<Panel>::new();
4012//    println!("Iterating over {} panels in the DB!", db_panels.len());
4013//    for dbpanel in db_panels {
4014//      let mut pnl  = Panel::new();
4015//      for pdl in paddles.iter() {
4016//        // this call ensures that the following unwraps
4017//        // go through
4018//        if !dbpanel.valid() {
4019//          error!("Got unpopulated Panel from DB for Panel {}", dbpanel);
4020//          continue;
4021//        }
4022//        if pdl.paddle_id == dbpanel.paddle0_id.unwrap() {
4023//          pnl.panel_id     = dbpanel.panel_id as u8;        
4024//          pnl.description  = dbpanel.description.clone();
4025//          pnl.normal_x     = dbpanel.normal_x as u8;     
4026//          pnl.normal_y     = dbpanel.normal_y as u8;     
4027//          pnl.normal_z     = dbpanel.normal_z as u8;    
4028//          pnl.paddle0      = pdl.clone();
4029//        }
4030//        if pdl.paddle_id == dbpanel.paddle1_id.unwrap() {
4031//          pnl.paddle1   = Some(pdl.clone());
4032//        }
4033//        if pdl.paddle_id == dbpanel.paddle2_id.unwrap() {
4034//          pnl.paddle2   = Some(pdl.clone());
4035//        }
4036//        if pdl.paddle_id == dbpanel.paddle3_id.unwrap() {
4037//          pnl.paddle3   = Some(pdl.clone());
4038//        }
4039//        if pdl.paddle_id == dbpanel.paddle4_id.unwrap() {
4040//          pnl.paddle4   = Some(pdl.clone());
4041//        }
4042//        if pdl.paddle_id == dbpanel.paddle5_id.unwrap() {
4043//          pnl.paddle5   = Some(pdl.clone());
4044//        }
4045//        if pdl.paddle_id == dbpanel.paddle6_id.unwrap() {
4046//          pnl.paddle6   = Some(pdl.clone());
4047//        }
4048//        if pdl.paddle_id == dbpanel.paddle7_id.unwrap() {
4049//          pnl.paddle7   = Some(pdl.clone());
4050//        }
4051//        if pdl.paddle_id == dbpanel.paddle8_id.unwrap() {
4052//          pnl.paddle8   = Some(pdl.clone());
4053//        }
4054//        if pdl.paddle_id == dbpanel.paddle9_id.unwrap() {
4055//          pnl.paddle9   = Some(pdl.clone());
4056//        }
4057//        if pdl.paddle_id == dbpanel.paddle10_id.unwrap() {
4058//          pnl.paddle10   = Some(pdl.clone());
4059//        }
4060//        if pdl.paddle_id == dbpanel.paddle11_id.unwrap() {
4061//          pnl.paddle11   = Some(pdl.clone());
4062//        }
4063//      }
4064//      panels.push(pnl);
4065//    }
4066//    Some(panels)
4067//  }
4068//}
4069//
4070//impl fmt::Display for Panel {
4071//  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4072//    let mut repr = String::from("<Panel");
4073//    repr += &(format!("\n  id    : {}",self.panel_id));
4074//    repr += &(format!("\n  descr : {}",self.description));
4075//    repr += "\n  orientation:";
4076//    repr += &(format!("\n   [{},{},{}]", self.normal_x, self.normal_y, self.normal_z));
4077//    repr += &(format!("\n  paddle list ({}) paddles)", self.get_npaddles()));
4078//    repr += &(format!("\n   {}",self.paddle0));
4079//    if self.paddle1.is_some() {
4080//      repr += &(format!("\n   {}",self.paddle1.as_ref().unwrap()));
4081//    }
4082//    if self.paddle2.is_some() { 
4083//      repr += &(format!("\n   {}",self.paddle2.as_ref().unwrap()));
4084//    }
4085//    if self.paddle3.is_some() { 
4086//      repr += &(format!("\n   {}",self.paddle3.as_ref().unwrap()));
4087//    }
4088//    if self.paddle4.is_some() {
4089//      repr += &(format!("\n   {}",self.paddle4.as_ref().unwrap()));
4090//    }
4091//    if self.paddle5.is_some() {
4092//      repr += &(format!("\n   {}",self.paddle5.as_ref().unwrap()));
4093//    }
4094//    if self.paddle6.is_some()  {
4095//      repr += &(format!("\n   {}",self.paddle6.as_ref().unwrap()));
4096//    }
4097//    if self.paddle7.is_some() {
4098//      repr += &(format!("\n   {}",self.paddle7.as_ref().unwrap()));
4099//    }
4100//    if self.paddle8.is_some() {
4101//      repr += &(format!("\n   {}",self.paddle8.as_ref().unwrap()));
4102//    }
4103//    if self.paddle9.is_some() {
4104//      repr += &(format!("\n   {}",self.paddle9.as_ref().unwrap()));
4105//    }
4106//    if self.paddle10.is_some() {
4107//      repr += &(format!("\n   {}",self.paddle10.as_ref().unwrap()));
4108//    }
4109//    if self.paddle11.is_some() {
4110//      repr += &(format!("\n   {}",self.paddle11.as_ref().unwrap()));
4111//    }
4112//    repr += ">";
4113//    write!(f, "{}", repr)
4114//  }
4115//}
4116//
4117//
4118//
4119