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