gondola_core/
database.rs

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