Skip to main content

gondola_core/
database.rs

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