tof_dataclasses/
database.rs

1//! Database access & entities of the TOF
2//!
3//! A local .sqlite database is shipped with 
4//! this packet and allows to access all
5//! mapping relevant TOF information, e.g. 
6//! paddle connection to LTBs anr RBs,
7//! paddle information, paddle cordinates,
8//! panel ids and so on.
9//!
10
11use std::fmt;
12use std::env;
13use std::collections::HashMap;
14
15use glob::glob;
16use regex::Regex;
17use chrono::{
18    DateTime,
19    Utc,
20};
21
22//use rusqlite::Connection;
23use diesel::prelude::*;
24mod schema;
25    
26use schema::tof_db_rat::dsl::*;
27use schema::tof_db_dsicard::dsl::*;
28
29use crate::calibrations::RBCalibrations;
30//use crate::constants::HUMAN_TIMESTAMP_FORMAT;
31use crate::DsiLtbRBMapping;
32pub use crate::RbChPidMapping;
33
34// FIXME - probably we should make this nicer
35pub type DsiJChPidMapping = DsiLtbRBMapping; 
36
37/// Universal function to connect to the database
38pub fn connect_to_db(database_url : String) -> Result<diesel::SqliteConnection, ConnectionError>  {
39  //let database_url = "database.sqlite3";
40  SqliteConnection::establish(&database_url)
41}
42
43/// Get all tof paddles in the database
44pub fn get_tofpaddles() -> Result<HashMap<u8,Paddle>, ConnectionError> {
45  let db_path  = env::var("DATABASE_URL").unwrap_or_else(|_| "".to_string());
46  let mut conn = connect_to_db(db_path)?;
47  let mut paddles = HashMap::<u8, Paddle>::new();
48  match Paddle::all(&mut conn) {
49    None => {
50      error!("We can't find any paddles in the database!");
51      return Ok(paddles);
52    }
53    Some(pdls) => {
54      for p in pdls {
55        paddles.insert(p.paddle_id as u8, p.clone());
56      }
57    }
58  }
59  return Ok(paddles);
60}
61
62/// Get all trackerstrips from the database
63pub fn get_trackerstrips() -> Result<HashMap<u32,TrackerStrip>, ConnectionError> {
64  let db_path  = env::var("DATABASE_URL").unwrap_or_else(|_| "".to_string());
65  let mut conn = connect_to_db(db_path)?;
66  let mut strips = HashMap::<u32, TrackerStrip>::new();
67  match TrackerStrip::all(&mut conn) {
68    None => {
69      error!("We can't find any tracker strips in the database!");
70      return Ok(strips);
71    }
72    Some(ts) => {
73      for s in ts {
74        strips.insert(s.get_stripid() as u32, s.clone());
75      }
76    }
77  }
78  return Ok(strips);
79}
80
81/// Create a mapping of mtb link ids to rb ids
82pub fn get_linkid_rbid_map(rbs : &Vec<ReadoutBoard>) -> HashMap<u8, u8>{
83  let mut mapping = HashMap::<u8, u8>::new();
84  for rb in rbs {
85    mapping.insert(rb.mtb_link_id, rb.rb_id);
86  }
87  mapping
88}
89
90/// Create a mapping of rb id to mtb link ids
91pub fn get_rbid_linkid_map(rbs : &Vec<ReadoutBoard>) -> HashMap<u8, u8> {
92  let mut mapping = HashMap::<u8, u8>::new();
93  for rb in rbs {
94    mapping.insert(rb.rb_id, rb.mtb_link_id);
95  }
96  mapping
97}
98
99pub fn get_dsi_j_ch_pid_map(paddles : &Vec<Paddle>) -> DsiJChPidMapping {
100  let mut mapping = DsiJChPidMapping::new();
101  for dsi in 1..6 {
102    let mut jmap = HashMap::<u8, HashMap<u8, (u8, u8)>>::new();
103    for j in 1..6 {
104      let mut rbidch_map : HashMap<u8, (u8,u8)> = HashMap::new();
105      for ch in 1..17 {
106        let rbidch = (0,0);
107        rbidch_map.insert(ch,rbidch);
108        //map[dsi] = 
109      }
110      jmap.insert(j,rbidch_map);
111    }
112    mapping.insert(dsi,jmap);
113  }
114  for pdl in paddles {
115    let dsi  = pdl.dsi as u8;
116    let   j  = pdl.j_ltb   as u8;
117    let ch_a = pdl.ltb_chA as u8;
118    let ch_b = pdl.ltb_chB as u8;
119    let pid  = pdl.paddle_id as u8;
120    let panel_id = pdl.panel_id as u8;
121    mapping.get_mut(&dsi).unwrap().get_mut(&j).unwrap().insert(ch_a,(pid, panel_id));
122    mapping.get_mut(&dsi).unwrap().get_mut(&j).unwrap().insert(ch_b,(pid, panel_id));
123  }
124  return mapping;
125}
126
127/// Create a map for rbid, ch -> paddle id. This is for both sides
128/// and will always return a paddle id independent of A or B
129pub fn get_rb_ch_pid_map(paddles : &Vec<Paddle>) -> RbChPidMapping {
130  let mut mapping = RbChPidMapping::new();
131  for rbid  in 1..51 {
132    let mut chmap = HashMap::<u8, u8>::new();
133    for ch in 1..9 {
134      chmap.insert(ch,0);
135    }
136    mapping.insert(rbid,chmap);
137  }
138  for pdl in paddles {
139    let rb_id = pdl.rb_id  as u8;
140    let ch_a  = pdl.rb_chA as u8;
141    let ch_b  = pdl.rb_chB as u8;
142    let pid   = pdl.paddle_id as u8;
143    //println!("rb_id {rb_id}, chA {ch_a}, chB {ch_b}");
144    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_a).unwrap() = pid;
145    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_b).unwrap() = pid;
146  }
147  mapping
148}
149
150/// Create a map for rbid, ch -> paddle id. This is only for the A 
151/// side and will not have an entry in case the given RB channel
152/// is connected to the B side of the paddle
153pub fn get_rb_ch_pid_a_map(paddles : &Vec<Paddle>) -> RbChPidMapping {
154  let mut mapping = RbChPidMapping::new();
155  for rbid  in 1..51 {
156    let mut chmap = HashMap::<u8, u8>::new();
157    for ch in 1..9 {
158      chmap.insert(ch,0);
159    }
160    mapping.insert(rbid,chmap);
161  }
162  for pdl in paddles {
163    let rb_id = pdl.rb_id  as u8;
164    let ch_a  = pdl.rb_chA as u8;
165    let pid   = pdl.paddle_id as u8;
166    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_a).unwrap() = pid;
167  }
168  mapping
169}
170
171
172/// Create a map for rbid, ch -> paddle id. This is only for the B 
173/// side and will not have an entry in case the given RB channel
174/// is connected to the A side of the paddle
175pub fn get_rb_ch_pid_b_map(paddles : &Vec<Paddle>) -> RbChPidMapping {
176  let mut mapping = RbChPidMapping::new();
177  for rbid  in 1..51 {
178    let mut chmap = HashMap::<u8, u8>::new();
179    for ch in 1..9 {
180      chmap.insert(ch,0);
181    }
182    mapping.insert(rbid,chmap);
183  }
184  for pdl in paddles {
185    let rb_id = pdl.rb_id  as u8;
186    let ch_b  = pdl.rb_chB as u8;
187    let pid   = pdl.paddle_id as u8;
188    *mapping.get_mut(&rb_id).unwrap().get_mut(&ch_b).unwrap() = pid;
189  }
190  mapping
191}
192
193/// A representation of a run 
194#[derive(Debug, Clone, Queryable,Insertable, Selectable, serde::Serialize, serde::Deserialize)]
195#[diesel(table_name = schema::tof_db_run)]
196#[diesel(primary_key(run_id))]
197pub struct Run {
198  pub run_id                    : i64,
199  pub runtime_secs              : Option<i64>,
200  pub calib_before              : Option<bool>,
201  pub shifter                   : Option<i16>,
202  pub run_type                  : Option<i16>,
203  pub run_path                  : Option<String>,
204}
205
206impl Run {
207  pub fn new() -> Self {
208    Self {
209      run_id        : 0, 
210      runtime_secs  : Some(0), 
211      calib_before  : Some(true), 
212      shifter       : Some(0), 
213      run_type      : Some(0), 
214      run_path      : Some(String::from("")), 
215    }
216  }
217
218  pub fn get_last_run(conn: &mut SqliteConnection) -> Option<u32> {
219    use schema::tof_db_run::dsl::*;
220    match tof_db_run.load::<Run>(conn) {
221      Err(err) => {
222        error!("Unable to load DSICards from db! {err}");
223        return None;
224      }
225      Ok(_runs) => {
226        //return Some(runs);
227      }
228    }
229    let _results = tof_db_run
230      //.filter(published.eq(true))
231      .limit(1)
232      //.select(Run::as_select())
233      .load::<Run>(conn)
234      .expect("Error loading posts");
235    None
236  }
237}
238
239impl fmt::Display for Run {
240  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241    let mut repr = String::from("<Run");
242    repr += &(format!("\n  RunID         : {}", self.run_id));                   
243    repr += &(format!("\n  - auto cali   : {}", self.calib_before.unwrap_or(false)));
244    repr += &(format!("\n  runtime [sec] : {}", self.runtime_secs.unwrap_or(-1)));
245    repr += &(format!("\n  shifter       : {}", self.shifter.unwrap_or(-1)));
246    repr += &(format!("\n  run_type      : {}", self.run_type.unwrap_or(-1)));
247    repr += &(format!("\n  run_path      : {}", self.run_path.clone().unwrap_or(String::from(""))));
248    write!(f, "{}", repr)
249  }
250}
251
252/// Representation of a local trigger board.
253/// 
254/// The individual LTB channels do not map directly to PaddleEnds. Rather two of them
255/// map to a paddle and then the whole paddle should get read out.
256/// To be more specific about this. The LTB has 16 channels, but we treat them as 8.
257/// Each 2 LTB channels get "married" internally in the board and will then continue
258/// on as 1 LTB channel, visible to the outside. The information about which end of 
259/// the Paddle crossed which threshhold is lost.
260/// How it works is that the two channels will be combined by the trigger logic:
261/// - There are 4 states (2 bits)
262///   - 0 - no hit
263///   - 1 - Hit
264///   - 2 - Beta
265///   - 3 - Veto
266/// 
267/// Each defining an individual threshold. If that is crossed, the whole paddle
268/// (ends A+B) will be read out by the ReadoutBoard
269/// 
270/// The LTB channels here are labeled 1-8. This is as it is in the TOF spreadsheet.
271/// Also dsi is labeled as in the spreadsheet and will start from one.
272/// 
273/// It is NOT clear from this which ch on the rb is connected to which side, for that
274/// the paddle/RB tables need to be consulted.
275/// Again: rb_ch0 does NOT necessarily correspond to the A side!
276/// 
277#[derive(Debug,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
278#[diesel(table_name = schema::tof_db_rat)]
279#[diesel(primary_key(rat_id))]
280pub struct RAT {
281  pub rat_id                    : i16, 
282  pub pb_id                     : i16, 
283  pub rb1_id                    : i16, 
284  pub rb2_id                    : i16, 
285  pub ltb_id                    : i16, 
286  pub ltb_harting_cable_length  : i16, 
287}
288
289impl RAT {
290  pub fn new() -> Self {
291    Self {
292      rat_id                    : 0, 
293      pb_id                     : 0, 
294      rb1_id                    : 0, 
295      rb2_id                    : 0, 
296      ltb_id                    : 0, 
297      ltb_harting_cable_length  : 0, 
298    }
299  }
300  
301  /// Get the RAT where rb2id matched the argument
302  pub fn where_rb2id(conn: &mut SqliteConnection, rb2id : u8) -> Option<Vec<RAT>> {
303    let mut result = Vec::<RAT>::new();
304    match RAT::all(conn) {
305      Some(rats) => {
306        for rat in rats {
307          if rat.rb2_id == rb2id as i16 {
308            result.push(rat);
309          }
310        }
311        return Some(result);
312      }
313      None => ()
314    }
315    Some(result)
316  }
317  
318  /// Get the RAT where rb1id (the rb id of rb"1" in the RAT) matched the argument
319  pub fn where_rb1id(conn: &mut SqliteConnection, rb2id : u8) -> Option<Vec<RAT>> {
320    let mut result = Vec::<RAT>::new();
321    match RAT::all(conn) {
322      Some(rats) => {
323        for rat in rats {
324          if rat.rb1_id == rb2id as i16 {
325            result.push(rat);
326          }
327        }
328        return Some(result);
329      }
330      None => ()
331    }
332    Some(result)
333  }
334
335  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<RAT>> {
336    match tof_db_rat.load::<RAT>(conn) {
337      Err(err) => {
338        error!("Unable to load RATs from db! {err}");
339        return None;
340      }
341      Ok(rats) => {
342        return Some(rats);
343      }
344    }
345  }
346
347}
348
349impl fmt::Display for RAT {
350  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351    let mut repr = String::from("<RAT");
352    repr += &(format!("\n  ID                : {}", self.rat_id));                   
353    repr += &(format!("\n  PB                : {} ", self.pb_id));                    
354    repr += &(format!("\n  RB1               : {}", self.rb1_id));                   
355    repr += &(format!("\n  RB2               : {}", self.rb2_id));                   
356    repr += &(format!("\n  LTB               : {}", self.ltb_id));                   
357    repr += &(format!("\n  H. cable len [cm] : {}>", self.ltb_harting_cable_length)); 
358    write!(f, "{}", repr)
359  }
360}
361
362
363/// A DSI card which is plugged into one of five slots on the MTB
364/// The DSI card provides the connection to RBs and LTBs and has 
365/// a subdivision, which is called 'j'
366#[derive(Queryable, Selectable)]
367#[diesel(primary_key(dsi_id))]
368#[diesel(table_name = schema::tof_db_dsicard)]
369pub struct DSICard { 
370  pub dsi_id    : i16,
371  pub j1_rat_id : Option<i16>,
372  pub j2_rat_id : Option<i16>,
373  pub j3_rat_id : Option<i16>,
374  pub j4_rat_id : Option<i16>,
375  pub j5_rat_id : Option<i16>,
376}
377 
378
379impl DSICard {
380  pub fn new() -> Self {
381    Self {
382      dsi_id    : 0,
383      j1_rat_id : None,
384      j2_rat_id : None,
385      j3_rat_id : None,
386      j4_rat_id : None,
387      j5_rat_id : None,
388    }
389  }
390  
391  /// True if this RAT box is plugged in to any of the j 
392  /// connectors on this specific DSI card
393  pub fn has_rat(&self, r_id : u8) -> bool {
394    if let Some(rid) = self.j1_rat_id {
395      if rid as u8 == r_id {
396        return true;
397      }
398    }
399    if let Some(rid) = self.j2_rat_id {
400      if rid as u8 == r_id {
401        return true;
402      }
403    }
404    if let Some(rid) = self.j3_rat_id {
405      if rid as u8 == r_id {
406        return true;
407      }
408    }
409    if let Some(rid) = self.j4_rat_id {
410      if rid as u8 == r_id {
411        return true;
412      }
413    }
414    if let Some(rid) = self.j5_rat_id {
415      if rid as u8 == r_id {
416        return true;
417      }
418    }
419    return false;
420  }
421
422  /// Get the j connetor for this specific RAT
423  /// Raises ValueError if the RAT is not connected
424  pub fn get_j(&self, r_id : u8) -> Option<u8> {
425    if !self.has_rat(r_id) {
426      return None;
427    }
428    if let Some(rid) = self.j1_rat_id {
429      if rid as u8 == r_id {
430        let _j = self.j1_rat_id.unwrap() as u8;
431        return Some(_j);
432      }
433    }
434    if let Some(rid) = self.j2_rat_id {
435      if rid as u8 == r_id {
436        let _j = self.j2_rat_id.unwrap() as u8;
437        return Some(_j);
438      }
439    }
440    if let Some(rid) = self.j3_rat_id {
441      if rid as u8 == r_id {
442        let _j = self.j3_rat_id.unwrap() as u8;
443        return Some(_j);
444      }
445    }
446    if let Some(rid) = self.j4_rat_id {
447      if rid as u8 == r_id {
448        let _j = self.j4_rat_id.unwrap() as u8;
449        return Some(_j);
450      }
451    }
452    if let Some(rid) = self.j5_rat_id {
453      if rid as u8 == r_id {
454        let _j = self.j5_rat_id.unwrap() as u8;
455        return Some(_j);
456      }
457    }
458  None
459  }
460  
461  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DSICard>> {
462    match tof_db_dsicard.load::<DSICard>(conn) {
463      Err(err) => {
464        error!("Unable to load DSICards from db! {err}");
465        return None;
466      }
467      Ok(dsis) => {
468        return Some(dsis);
469      }
470    }
471  }
472}
473
474impl fmt::Display for DSICard {
475  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
476    let mut repr  = String::from("<DSI Card:");
477    repr += &(format!("\n  ID     : {}", self.dsi_id));     
478    repr += "\n  -- -- -- --";
479    if let Some(_j) = self.j1_rat_id {
480        repr += &(format!("\n  J1 RAT : {}",_j));
481    } else {
482        repr += "\n  J1 RAT : Not connected";
483    }
484    if let Some(_j) = self.j2_rat_id {
485        repr += &(format!("\n  J2 RAT : {}",_j));
486    } else {
487        repr += "\n  J2 RAT : Not connected";
488    }
489    if let Some(_j) = self.j3_rat_id {
490        repr += &(format!("\n  J3 RAT : {}",_j));
491    } else {
492        repr += "\n  J3 RAT : Not connected";
493    }
494    if let Some(_j) = self.j4_rat_id {
495        repr += &(format!("\n  J4 RAT : {}",_j));
496    } else {
497        repr += "\n  J4 RAT : Not connected";
498    }
499    if let Some(_j) = self.j5_rat_id {
500        repr += &(format!("\n  J5 RAT : {}>",_j));
501    } else {
502        repr += "\n  J5 RAT : Not connected>";
503    }
504    write!(f, "{}", repr)
505  }
506}
507
508/// A single Tracker strip
509#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
510#[diesel(table_name = schema::tof_db_trackerstrip)]
511#[diesel(primary_key(strip_id))]
512#[allow(non_snake_case)]
513pub struct TrackerStrip {
514    pub strip_id            : i32,
515    pub layer               : i32, 
516    pub row                 : i32, 
517    pub module              : i32, 
518    pub channel             : i32,  
519    pub global_pos_x_l0     : f32,
520    pub global_pos_y_l0     : f32,
521    pub global_pos_z_l0     : f32,
522    pub global_pos_x_det_l0 : f32,
523    pub global_pos_y_det_l0 : f32,
524    pub global_pos_z_det_l0 : f32,
525    pub principal_x         : f32,
526    pub principal_y         : f32,
527    pub principal_z         : f32,
528    pub volume_id           : i64,
529}
530
531impl TrackerStrip {
532  pub fn new() -> Self {
533    Self {
534      strip_id            : 0,
535      layer               : 0, 
536      row                 : 0, 
537      module              : 0, 
538      channel             : 0,  
539      global_pos_x_l0     : 0.0,
540      global_pos_y_l0     : 0.0,
541      global_pos_z_l0     : 0.0,
542      global_pos_x_det_l0 : 0.0,
543      global_pos_y_det_l0 : 0.0,
544      global_pos_z_det_l0 : 0.0,
545      principal_x         : 0.0,
546      principal_y         : 0.0,
547      principal_z         : 0.0,
548      volume_id           : 0,
549    }
550  }
551 
552  pub fn get_stripid(&self) -> u32 {
553    self.channel as u32 + (self.module as u32)*100 + (self.row as u32)*10000 + (self.layer as u32)*10000
554  }
555
556  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<Self>> {
557    use schema::tof_db_trackerstrip::dsl::*;
558    match tof_db_trackerstrip.load::<Self>(conn) {
559      Err(err) => {
560        error!("Unable to load tracker strips from db! {err}");
561        return None;
562      }
563      Ok(pdls) => {
564        return Some(pdls);
565      }
566    }
567  }
568}
569
570impl fmt::Display for TrackerStrip {
571  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
572    let mut repr = String::from("<TrackerStrip:");
573    repr += &(format!("\n   strip id           : {}", self.strip_id));     
574    repr += &(format!("\n   vid                : {}", self.volume_id));
575    repr += &(format!("\n   layer              : {}", self.layer));
576    repr += &(format!("\n   row                : {}", self.row));
577    repr += &(format!("\n   module             : {}", self.module));
578    repr += &(format!("\n   channel            : {}", self.channel));
579    repr += "\n   strip center [mm]:";
580    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0));
581    repr += "\n   detector (disk) center [mm]:";
582    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));
583    repr += "\n   strip principal direction:";
584    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.principal_x, self.principal_y, self.principal_z));
585    write!(f, "{}", repr)
586  }
587}
588
589
590
591/// A single TOF paddle with 2 ends 
592/// comnected
593#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
594#[diesel(table_name = schema::tof_db_paddle)]
595#[diesel(primary_key(paddle_id))]
596#[allow(non_snake_case)]
597pub struct Paddle {
598  pub paddle_id         : i16, 
599  pub volume_id         : i64, 
600  pub panel_id          : i16, 
601  pub mtb_link_id       : i16, 
602  pub rb_id             : i16, 
603  pub rb_chA            : i16, 
604  pub rb_chB            : i16, 
605  pub ltb_id            : i16, 
606  pub ltb_chA           : i16, 
607  pub ltb_chB           : i16, 
608  pub pb_id             : i16, 
609  pub pb_chA            : i16, 
610  pub pb_chB            : i16, 
611  pub cable_len         : f32, 
612  pub dsi               : i16, 
613  pub j_rb              : i16, 
614  pub j_ltb             : i16, 
615  pub height            : f32, 
616  pub width             : f32, 
617  pub length            : f32, 
618  pub normal_x          : f32,
619  pub normal_y          : f32,
620  pub normal_z          : f32,
621  pub global_pos_x_l0   : f32, 
622  pub global_pos_y_l0   : f32, 
623  pub global_pos_z_l0   : f32, 
624  pub global_pos_x_l0_A : f32, 
625  pub global_pos_y_l0_A : f32, 
626  pub global_pos_z_l0_A : f32, 
627  pub global_pos_x_l0_B : f32, 
628  pub global_pos_y_l0_B : f32, 
629  pub global_pos_z_l0_B : f32, 
630  pub coax_cable_time   : f32,
631  pub harting_cable_time: f32,
632}
633
634impl Paddle {
635  pub fn new() -> Self {
636    Self {
637      paddle_id         : 0, 
638      volume_id         : 0, 
639      panel_id          : 0, 
640      mtb_link_id       : 0, 
641      rb_id             : 0, 
642      rb_chA            : 0, 
643      rb_chB            : 0, 
644      ltb_id            : 0, 
645      ltb_chA           : 0, 
646      ltb_chB           : 0, 
647      pb_id             : 0, 
648      pb_chA            : 0, 
649      pb_chB            : 0, 
650      cable_len         : 0.0, 
651      dsi               : 0, 
652      j_rb              : 0, 
653      j_ltb             : 0, 
654      height            : 0.0, 
655      width             : 0.0, 
656      length            : 0.0, 
657      normal_x          : 0.0,
658      normal_y          : 0.0,
659      normal_z          : 0.0,
660      global_pos_x_l0   : 0.0, 
661      global_pos_y_l0   : 0.0, 
662      global_pos_z_l0   : 0.0, 
663      global_pos_x_l0_A : 0.0, 
664      global_pos_y_l0_A : 0.0, 
665      global_pos_z_l0_A : 0.0, 
666      global_pos_x_l0_B : 0.0, 
667      global_pos_y_l0_B : 0.0, 
668      global_pos_z_l0_B : 0.0,
669      coax_cable_time   : 0.0, 
670      harting_cable_time: 0.0
671    }
672  }
673
674  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<Paddle>> {
675    use schema::tof_db_paddle::dsl::*;
676    match tof_db_paddle.load::<Paddle>(conn) {
677      Err(err) => {
678        error!("Unable to load paddles from db! {err}");
679        return None;
680      }
681      Ok(pdls) => {
682        return Some(pdls);
683      }
684    }
685  }
686
687  /// The principal is the direction along the longest
688  /// dimension from A -> B
689  pub fn principal(&self) -> (f32,f32,f32) {
690    let mut pr = (self.global_pos_x_l0_B - self.global_pos_x_l0_A,
691                  self.global_pos_y_l0_B - self.global_pos_y_l0_A,
692                  self.global_pos_z_l0_B - self.global_pos_z_l0_A);
693    let length = f32::sqrt(pr.0.powf(2.0) + pr.1.powf(2.0) + pr.2.powf(2.0)); 
694    pr = (pr.0/length, pr.1/length, pr.2/length);
695    return pr;
696  }
697
698}
699
700impl fmt::Display for Paddle {
701  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702    let mut repr = String::from("<Paddle:");
703    repr += "\n** identifiers **";
704    repr += &(format!("\n   pid                : {}", self.paddle_id));     
705    repr += &(format!("\n   vid                : {}", self.volume_id));
706    repr += &(format!("\n   panel id           : {}", self.panel_id));
707    repr += "\n  ** connedtions **";
708    repr += &(format!("\n   DSI/J/CH (LG) [A]  : {}  | {} | {:02}", self.dsi, self.j_ltb, self.ltb_chA));
709    repr += &(format!("\n   DSI/J/CH (HG) [A]  : {}  | {} | {:02}", self.dsi, self.j_rb, self.rb_chA));
710    repr += &(format!("\n   DSI/J/CH (LG) [B]  : {}  | {} | {:02}", self.dsi, self.j_ltb, self.ltb_chB));
711    repr += &(format!("\n   DSI/J/CH (HG) [B]  : {}  | {} | {:02}", self.dsi, self.j_rb, self.rb_chB));
712    repr += &(format!("\n   RB/CH         [A]  : {:02} | {}", self.rb_id, self.rb_chA));
713    repr += &(format!("\n   RB/CH         [B]  : {:02} | {}", self.rb_id, self.rb_chB));
714    repr += &(format!("\n   LTB/CH        [A]  : {:02} | {}", self.ltb_id, self.ltb_chA));
715    repr += &(format!("\n   LTB/CH        [B]  : {:02} | {}", self.ltb_id, self.ltb_chB));
716    repr += &(format!("\n   PB/CH         [A]  : {:02} | {}", self.pb_id, self.pb_chA));
717    repr += &(format!("\n   PB/CH         [B]  : {:02} | {}", self.pb_id, self.pb_chB));
718    repr += &(format!("\n   MTB Link ID        : {:02}", self.mtb_link_id));
719    repr += "\n   cable len [cm] :";
720    repr += &(format!("\n    \u{21B3} {:.2}", self.cable_len));
721    repr += "\n    (Harting -> RB)";
722    repr += "\n   cable times [ns] (JAZ) :";
723    repr += &(format!("\n    \u{21B3} {:.2} {:.2}", self.coax_cable_time, self.harting_cable_time));
724    repr += "\n  ** Coordinates (L0) & dimensions **";
725    repr += "\n   length, width, height [mm]";
726    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.length, self.width, self.height));
727    repr += "\n   center [mm]:";
728    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0));
729    repr += "\n   normal vector:";
730    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.normal_x, self.normal_y, self.normal_z));
731    repr += "\n   A-side [mm]:";
732    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));
733    repr += "\n   B-side [mm]:";
734    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));
735    write!(f, "{}", repr)
736  }
737}
738    
739// Summary of DSI/J/LTBCH (0-319)
740// This is not "official" but provides a way of indexing all
741// the individual channels
742#[derive(Debug,PartialEq,Queryable, Selectable)]
743#[diesel(table_name = schema::tof_db_mtbchannel)]
744#[diesel(primary_key(mtb_ch))]
745#[allow(non_snake_case)]
746pub struct MTBChannel {
747  pub mtb_ch      : i64,         
748  pub dsi         : Option<i16>, 
749  pub j           : Option<i16>, 
750  pub ltb_id      : Option<i16>, 
751  pub ltb_ch      : Option<i16>, 
752  pub rb_id       : Option<i16>, 
753  pub rb_ch       : Option<i16>, 
754  pub mtb_link_id : Option<i16>, 
755  pub paddle_id   : Option<i16>, 
756  pub paddle_isA  : Option<bool>,
757  pub hg_ch       : Option<i16>, 
758  pub lg_ch       : Option<i16>, 
759}
760
761impl MTBChannel {
762
763  pub fn new() -> Self {
764    Self {
765      mtb_ch      : -1,         
766      dsi         : None, 
767      j           : None, 
768      ltb_id      : None, 
769      ltb_ch      : None, 
770      rb_id       : None, 
771      rb_ch       : None, 
772      mtb_link_id : None, 
773      paddle_id   : None, 
774      paddle_isA  : None,
775      hg_ch       : None, 
776      lg_ch       : None, 
777    }
778  }
779  
780  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<MTBChannel>> {
781    use schema::tof_db_mtbchannel::dsl::*;
782    match tof_db_mtbchannel.load::<MTBChannel>(conn) {
783      Err(err) => {
784        error!("Unable to load RATs from db! {err}");
785        return None;
786      }
787      Ok(mtbch) => {
788        return Some(mtbch);
789      }
790    }
791  }
792}
793
794
795impl fmt::Display for MTBChannel {
796  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
797    let mut repr = String::from("<MTBChannel");
798    repr += &(format!("\n  Channel ID : {}", self.mtb_ch));
799    repr += &(format!("\n  DSI/J/     : {}/{}", self.dsi.unwrap_or(-1), self.j.unwrap_or(-1)));
800    repr += "\n  LTB ID/CH => RB ID/CH";
801    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)));
802    repr += &(format!("\n  MTB Link ID [RB] : {}", self.mtb_link_id.unwrap_or(-1)));
803    repr += "\n  LG CH => HG CH";
804    repr += &(format!("\n   |-> {} => {}", self.lg_ch.unwrap_or(-1), self.hg_ch.unwrap_or(-1)));
805    repr += &(format!("\n  Paddle Id: {}", self.paddle_id.unwrap_or(-1)));
806    let mut pend = "None";
807    if !self.paddle_isA.is_none() {
808      if self.paddle_isA.unwrap() {
809          pend = "A";
810      } else {
811          pend = "B";
812      }
813    }
814    repr += &(format!("\n  Paddle End: {}>", pend));
815    write!(f, "{}", repr)
816  }
817}
818
819
820///////////////////////////////////////////////////
821//
822// The following models exceed a bit the capabilities
823// of Diesel, or my Diesel skill.
824// These models contain multiple ForeignKeys, in all
825// cases these link to the paddle table. 
826//
827// For each of LocalTriggerBoard, ReadoutBoard, Panel
828// we have 2 structs:
829// One called DB<entity> and the other <entity>. The
830// first does have the ForeignKeys as SmallInt, and 
831// the latter looks them up and fills in the blanks
832//
833//
834//
835
836/// The DB wrapper for the LocalTriggerBoard, for 
837/// easy implementation there are no joins, we do 
838/// them manually in the public implementation 
839/// of the LocaltriggerBoard
840#[derive(Queryable, Selectable, Identifiable, Associations)]
841#[diesel(table_name = schema::tof_db_localtriggerboard)]
842#[diesel(primary_key(board_id))]
843#[diesel(belongs_to(Paddle, foreign_key=paddle1_id))]
844pub struct DBLocalTriggerBoard {
845    pub board_id      : i16,    
846    pub dsi           : Option<i16>,
847    pub j             : Option<i16>,
848    pub rat           : Option<i16>,
849    pub ltb_id        : Option<i16>, 
850    pub cable_len     : f32,
851    pub paddle1_id    : Option<i16>,
852    pub paddle2_id    : Option<i16>,
853    pub paddle3_id    : Option<i16>,
854    pub paddle4_id    : Option<i16>,
855    pub paddle5_id    : Option<i16>,
856    pub paddle6_id    : Option<i16>,
857    pub paddle7_id    : Option<i16>,
858    pub paddle8_id    : Option<i16>,
859}
860
861impl DBLocalTriggerBoard {
862  
863  //pub fn new() -> Self {
864  //  Self {
865  //    board_id      : 0,    
866  //    dsi           : None,
867  //    j             : None,
868  //    rat           : None,
869  //    ltb_id        : None, 
870  //    cable_len     : 0.0,
871  //    paddle1_id    : None,
872  //    paddle2_id    : None,
873  //    paddle3_id    : None,
874  //    paddle4_id    : None,
875  //    paddle5_id    : None,
876  //    paddle6_id    : None,
877  //    paddle7_id    : None,
878  //    paddle8_id    : None,
879  //  }
880  //}
881
882  /// True if sane dsi and j values are 
883  /// assigned to this board
884  pub fn connected(&self) -> bool {
885    self.dsi != None && self.j != None
886  }
887
888  /// True if all fields are filled with 
889  /// reasonable values and not the default
890  pub fn valid(&self) -> bool {
891    self.board_id      > 0 &&    
892    self.dsi       .is_some() && 
893    self.j         .is_some() && 
894    self.rat       .is_some() && 
895    // right now, we explicitly don't care
896    // about the ltb_id
897    //self.ltb_id    .is_some() &&  
898    self.cable_len     > 0.0  &&
899    self.paddle1_id.is_some() &&
900    self.paddle2_id.is_some() &&
901    self.paddle3_id.is_some() &&
902    self.paddle4_id.is_some() &&
903    self.paddle5_id.is_some() &&
904    self.paddle6_id.is_some() &&
905    self.paddle7_id.is_some() &&
906    self.paddle8_id.is_some()
907  }
908  
909  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBLocalTriggerBoard>> {
910    use schema::tof_db_localtriggerboard::dsl::*;
911    match tof_db_localtriggerboard
912        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
913        .load::<DBLocalTriggerBoard>(conn) {
914      Err(err) => {
915        error!("Unable to load LocalTriggerBoards from db! {err}");
916        return None;
917      }
918      Ok(ltbs) => {
919        return Some(ltbs);
920      }
921    }
922  }
923}
924
925impl fmt::Display for DBLocalTriggerBoard {
926  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
927    let mut repr : String;
928    if !self.connected() {
929      repr = format!("<DBLocalTriggerBoard: ID {}  - UNCONNECTED>", self.board_id);
930    } else {
931      repr = String::from("<DBLocalTriggerBoard:");
932      repr += &(format!("\n  LTB ID  : {}", self.board_id));             
933    }
934    repr += &(format!("\n  DSI/J   : {}/{}", self.dsi.unwrap(), self.j.unwrap()));     
935    repr += &(format!("\n  RAT ID  : {}", self.rat.unwrap()));
936    repr += "\n  H. cable len (MTB connection):";
937    repr += &(format!("\n    ->      {}", self.cable_len));
938    repr += "\n  -- -- -- -- -- -- -- -- -- -- -- -- -- --";
939    repr += "\n  Paddle IDs:";
940    repr += &(format!("\n    {:02}", self.paddle1_id.unwrap_or(-1))); 
941    repr += &(format!("\n    {:02}", self.paddle2_id.unwrap_or(-1)));  
942    repr += &(format!("\n    {:02}", self.paddle3_id.unwrap_or(-1)));  
943    repr += &(format!("\n    {:02}", self.paddle4_id.unwrap_or(-1)));  
944    repr += &(format!("\n    {:02}", self.paddle5_id.unwrap_or(-1))); 
945    repr += &(format!("\n    {:02}", self.paddle6_id.unwrap_or(-1))); 
946    repr += &(format!("\n    {:02}", self.paddle7_id.unwrap_or(-1))); 
947    repr += &(format!("\n    {:02}", self.paddle8_id.unwrap_or(-1))); 
948    write!(f, "{}", repr)
949  }
950}
951
952#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
953pub struct LocalTriggerBoard {
954    pub board_id      : u8,    
955    pub dsi           : u8,
956    pub j             : u8,
957    pub rat           : u8,
958    pub ltb_id        : u8, 
959    pub cable_len     : f32,
960    pub paddle1       : Paddle,
961    pub paddle2       : Paddle,
962    pub paddle3       : Paddle,
963    pub paddle4       : Paddle,
964    pub paddle5       : Paddle,
965    pub paddle6       : Paddle,
966    pub paddle7       : Paddle,
967    pub paddle8       : Paddle,
968}
969
970impl LocalTriggerBoard {
971  
972  pub fn new() -> Self {
973    Self {
974      board_id      : 0,    
975      dsi           : 0,
976      j             : 0,
977      rat           : 0,
978      ltb_id        : 0, 
979      cable_len     : 0.0,
980      paddle1       : Paddle::new(),
981      paddle2       : Paddle::new(),
982      paddle3       : Paddle::new(),
983      paddle4       : Paddle::new(),
984      paddle5       : Paddle::new(),
985      paddle6       : Paddle::new(),
986      paddle7       : Paddle::new(),
987      paddle8       : Paddle::new(),
988    }
989  }
990  
991  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<LocalTriggerBoard>> {
992    use schema::tof_db_localtriggerboard::dsl::*;
993    let db_ltbs : Vec<DBLocalTriggerBoard>;
994    match tof_db_localtriggerboard
995        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
996        .load::<DBLocalTriggerBoard>(conn) {
997      Err(err) => {
998        error!("Unable to load LocalTriggerBoards from db! {err}");
999        return None;
1000      }
1001      Ok(ltbs) => {
1002        db_ltbs = ltbs;
1003      }
1004    }
1005    let paddles_op = Paddle::all(conn);
1006    match paddles_op {
1007      None => {
1008        return None;
1009      }
1010      Some(_) => ()
1011    }
1012    let paddles = paddles_op.unwrap();
1013    // This is not the best and fastest, but since our diesel skills 
1014    // are a merely 3, we can't do it right now.
1015    let mut ltbs = Vec::<LocalTriggerBoard>::new();
1016    //println!("Iterating over {} ltbs in the DB!", db_ltbs.len());
1017    for dbltb in db_ltbs {
1018      let mut ltb  = LocalTriggerBoard::new();
1019      for pdl in paddles.iter() {
1020        // this call ensures that the following unwraps
1021        // go through
1022        if !dbltb.valid() {
1023          error!("Got unpopulated LTB from DB for LTB {}", dbltb);
1024          continue;
1025        }
1026        if pdl.paddle_id == dbltb.paddle1_id.unwrap() {
1027          ltb.board_id  = dbltb.board_id as u8;        
1028          ltb.dsi       = dbltb.dsi.unwrap_or(0) as u8;
1029          ltb.j         = dbltb.j.unwrap_or(0) as u8;     
1030          ltb.rat       = dbltb.rat.unwrap_or(0) as u8;     
1031          ltb.ltb_id    = dbltb.ltb_id.unwrap_or(0) as u8;    
1032          ltb.cable_len = dbltb.cable_len;    
1033          ltb.paddle1   = pdl.clone();
1034        }
1035        if pdl.paddle_id == dbltb.paddle2_id.unwrap() {
1036          ltb.paddle2   = pdl.clone();
1037        }
1038        if pdl.paddle_id == dbltb.paddle3_id.unwrap() {
1039          ltb.paddle3   = pdl.clone();
1040        }
1041        if pdl.paddle_id == dbltb.paddle4_id.unwrap() {
1042          ltb.paddle4   = pdl.clone();
1043        }
1044        if pdl.paddle_id == dbltb.paddle5_id.unwrap() {
1045          ltb.paddle5   = pdl.clone();
1046        }
1047        if pdl.paddle_id == dbltb.paddle6_id.unwrap() {
1048          ltb.paddle6   = pdl.clone();
1049        }
1050        if pdl.paddle_id == dbltb.paddle7_id.unwrap() {
1051          ltb.paddle7   = pdl.clone();
1052        }
1053        if pdl.paddle_id == dbltb.paddle8_id.unwrap() {
1054          ltb.paddle8   = pdl.clone();
1055        }
1056      }
1057      ltbs.push(ltb);
1058    }
1059    Some(ltbs)
1060  }
1061}
1062
1063impl fmt::Display for LocalTriggerBoard {
1064  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1065    let mut repr : String;
1066    repr = String::from("<LocalTriggerBoard:");
1067    repr += &(format!("\n  LTB ID  : {}", self.board_id));             
1068    repr += &(format!("\n  DSI/J   : {}/{}", self.dsi, self.j));     
1069    repr += &(format!("\n  RAT ID  : {}", self.rat));
1070    repr += "\n  H. cable len (MTB connection):";
1071    repr += &(format!("\n    ->      {}", self.cable_len));
1072    repr += "\n  -- -- -- -- -- -- -- -- -- -- -- -- -- --";
1073    repr += "\n  LTB Ch -> RB Id, RB chn, Pdl ID, Pan ID:";
1074    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)); 
1075    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));  
1076    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));  
1077    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));  
1078    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)); 
1079    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)); 
1080    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)); 
1081    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)); 
1082    write!(f, "{}", repr)
1083  }
1084}
1085
1086/// A Readoutboard with paddles connected
1087/// 
1088#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
1089#[diesel(table_name = schema::tof_db_readoutboard)]
1090#[diesel(primary_key(rb_id_id))]
1091#[allow(non_snake_case)]
1092pub struct DBReadoutBoard {
1093  // FIXME - this HAS TO BE (MUST!) the same order
1094  // as in schema.rs !!
1095  pub rb_id        : i16, 
1096  pub dsi          : i16, 
1097  pub j            : i16, 
1098  pub mtb_link_id  : i16, 
1099  pub paddle12_chA : Option<i16>,
1100  pub paddle34_chA : Option<i16>,
1101  pub paddle56_chA : Option<i16>,
1102  pub paddle78_chA : Option<i16>,
1103  pub paddle12_id  : Option<i16>,
1104  pub paddle34_id  : Option<i16>,
1105  pub paddle56_id  : Option<i16>,
1106  pub paddle78_id  : Option<i16>,
1107}
1108
1109impl DBReadoutBoard {
1110  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBReadoutBoard>> {
1111    use schema::tof_db_readoutboard::dsl::*;
1112    match tof_db_readoutboard
1113        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1114        .load::<DBReadoutBoard>(conn) {
1115      Err(err) => {
1116        error!("Unable to load ReadoutBoards from db! {err}");
1117        return None;
1118      }
1119      Ok(rbs) => {
1120        return Some(rbs);
1121      }
1122    }
1123  }
1124}
1125
1126impl fmt::Display for DBReadoutBoard {
1127  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1128    let mut repr  = String::from("<ReadoutBoard:");
1129    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1130    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1131    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1132    repr += "\n **Connected paddles**";
1133    repr += &(format!("\n  Ch0/1(1/2)  : {}", self.paddle12_id.unwrap_or(-1)));         
1134    repr += &(format!("\n  Ch1/2(2/3)  : {}", self.paddle34_id.unwrap_or(-1)));         
1135    repr += &(format!("\n  Ch2/3(3/4)  : {}", self.paddle56_id.unwrap_or(-1)));         
1136    repr += &(format!("\n  Ch3/4(4/5)  : {}>",self.paddle78_id.unwrap_or(-1)));         
1137    write!(f, "{}", repr)
1138  }
1139}
1140
1141/// A Readoutboard with paddles connected
1142#[derive(Debug, Clone)]
1143#[allow(non_snake_case)]
1144pub struct ReadoutBoard {
1145  pub rb_id           : u8, 
1146  pub dsi             : u8, 
1147  pub j               : u8, 
1148  pub mtb_link_id     : u8, 
1149  pub paddle12        : Paddle,
1150  pub paddle12_chA    : u8,
1151  pub paddle34        : Paddle,
1152  pub paddle34_chA    : u8,
1153  pub paddle56        : Paddle,
1154  pub paddle56_chA    : u8,
1155  pub paddle78        : Paddle,
1156  pub paddle78_chA    : u8,
1157  // extra stuff, not from the db
1158  // or maybe in the future?
1159  pub calib_file_path : String,
1160  pub calibration     : RBCalibrations,       
1161}
1162
1163impl ReadoutBoard {
1164
1165  pub fn new() -> Self {
1166    Self {
1167      rb_id           : 0, 
1168      dsi             : 0, 
1169      j               : 0, 
1170      mtb_link_id     : 0, 
1171      paddle12        : Paddle::new(),
1172      paddle12_chA    : 0,
1173      paddle34        : Paddle::new(),
1174      paddle34_chA    : 0,
1175      paddle56        : Paddle::new(),
1176      paddle56_chA    : 0,
1177      paddle78        : Paddle::new(),
1178      paddle78_chA    : 0,
1179      calib_file_path : String::from(""),
1180      calibration     : RBCalibrations::new(0),
1181    }
1182  }
1183
1184  /// Returns the ip address following a convention
1185  ///
1186  /// This does NOT GUARANTEE that the address is correct!
1187  pub fn guess_address(&self) -> String {
1188    format!("tcp://10.0.1.1{:02}:42000", self.rb_id)
1189  }
1190 
1191  pub fn get_paddle_ids(&self) -> [u8;4] {
1192    let pid0 = self.paddle12.paddle_id as u8;
1193    let pid1 = self.paddle34.paddle_id as u8;
1194    let pid2 = self.paddle56.paddle_id as u8;
1195    let pid3 = self.paddle78.paddle_id as u8;
1196    [pid0, pid1, pid2, pid3]
1197  }
1198
1199  #[allow(non_snake_case)]
1200  pub fn get_A_sides(&self) -> [u8;4] {
1201    let pa_0 = self.paddle12_chA;
1202    let pa_1 = self.paddle34_chA;
1203    let pa_2 = self.paddle56_chA;
1204    let pa_3 = self.paddle78_chA;
1205    [pa_0, pa_1, pa_2, pa_3]
1206  }
1207
1208  #[allow(non_snake_case)]
1209  pub fn get_pid_rbchA(&self, pid : u8) -> Option<u8> {
1210    if self.paddle12.paddle_id as u8 == pid {
1211      let rv = self.paddle12.rb_chA as u8;
1212      return Some(rv);
1213    } else if self.paddle34.paddle_id as u8 == pid {
1214      let rv = self.paddle34.rb_chA as u8;
1215      return Some(rv);
1216    } else if self.paddle56.paddle_id as u8 == pid {
1217      let rv = self.paddle56.rb_chA as u8;
1218      return Some(rv);
1219    } else if self.paddle78.paddle_id as u8== pid {
1220      let rv = self.paddle78.rb_chA as u8;
1221      return Some(rv);
1222    } else {
1223      return None;
1224    }
1225  }
1226  
1227  #[allow(non_snake_case)]
1228  pub fn get_pid_rbchB(&self, pid : u8) -> Option<u8> {
1229    if self.paddle12.paddle_id as u8 == pid {
1230      let rv = self.paddle12.rb_chB as u8;
1231      return Some(rv);
1232    } else if self.paddle34.paddle_id as u8== pid {
1233      let rv = self.paddle34.rb_chB as u8;
1234      return Some(rv);
1235    } else if self.paddle56.paddle_id as u8== pid {
1236      let rv = self.paddle56.rb_chB as u8;
1237      return Some(rv);
1238    } else if self.paddle78.paddle_id as u8 == pid {
1239      let rv = self.paddle78.rb_chB as u8;
1240      return Some(rv);
1241    } else {
1242      return None;
1243    }
1244  }
1245
1246  pub fn get_paddle_length(&self, pid : u8) -> Option<f32> {
1247    if self.paddle12.paddle_id as u8 == pid {
1248      let rv = self.paddle12.length;
1249      return Some(rv);
1250    } else if self.paddle34.paddle_id as u8== pid {
1251      let rv = self.paddle34.length;
1252      return Some(rv);
1253    } else if self.paddle56.paddle_id as u8== pid {
1254      let rv = self.paddle56.length;
1255      return Some(rv);
1256    } else if self.paddle78.paddle_id as u8 == pid {
1257      let rv = self.paddle78.length;
1258      return Some(rv);
1259    } else {
1260      return None;
1261    }
1262  }
1263
1264  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<ReadoutBoard>> {
1265    use schema::tof_db_readoutboard::dsl::*;
1266    let db_rbs : Vec<DBReadoutBoard>;
1267    match tof_db_readoutboard
1268        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1269        .load::<DBReadoutBoard>(conn) {
1270      Err(err) => {
1271        error!("Unable to load ReadoutBoards from db! {err}");
1272        return None;
1273      }
1274      Ok(rbs) => {
1275        db_rbs = rbs;
1276      }
1277    }
1278    let paddles_op = Paddle::all(conn);
1279    match paddles_op {
1280      None => {
1281        return None;
1282      }
1283      Some(_) => ()
1284    }
1285    let paddles = paddles_op.unwrap();
1286    // This is not the best and fastest, but since our diesel skills 
1287    // are a merely 3, we can't do it right now.
1288    let mut rbs = Vec::<ReadoutBoard>::new();
1289    //println!("Iterating over {} rbs in the DB!", db_rbs.len());
1290    for dbrb in db_rbs {
1291      let mut rb  = ReadoutBoard::new();
1292      rb.rb_id        = dbrb.rb_id as u8;        
1293      rb.dsi          = dbrb.dsi as u8;
1294      rb.j            = dbrb.j  as u8;     
1295      rb.mtb_link_id  = dbrb.mtb_link_id  as u8;    
1296      rb.paddle12_chA = dbrb.paddle12_chA.unwrap() as u8;
1297      rb.paddle34_chA = dbrb.paddle34_chA.unwrap() as u8;
1298      rb.paddle56_chA = dbrb.paddle56_chA.unwrap() as u8;
1299      rb.paddle78_chA = dbrb.paddle78_chA.unwrap() as u8;
1300      for pdl in paddles.iter() {
1301        // this call ensures that the following unwraps
1302        // go through
1303        //if !dbltb.valid() {
1304        //  error!("Got unpopulated LTB from DB for LTB {}", dbltb);
1305        //  continue;
1306        //}
1307        if pdl.paddle_id == dbrb.paddle12_id.unwrap_or(0) {
1308          rb.paddle12     = pdl.clone();
1309        }
1310        if pdl.paddle_id == dbrb.paddle34_id.unwrap_or(0) {
1311          rb.paddle34   = pdl.clone();
1312        }
1313        if pdl.paddle_id == dbrb.paddle56_id.unwrap_or(0) {
1314          rb.paddle56   = pdl.clone();
1315        }
1316        if pdl.paddle_id == dbrb.paddle78_id.unwrap_or(0) {
1317          rb.paddle78   = pdl.clone();
1318        }
1319      }
1320      rbs.push(rb);
1321    }
1322    Some(rbs)
1323  }
1324  
1325  // FIXME - better query
1326  pub fn where_rbid(conn: &mut SqliteConnection, rb_id : u8) -> Option<ReadoutBoard> {
1327    let all = ReadoutBoard::all(conn)?;
1328    for rb in all {
1329      if rb.rb_id == rb_id {
1330        return Some(rb);
1331      }
1332    }
1333    None
1334  }
1335
1336  pub fn to_summary_str(&self) -> String {
1337    let mut repr  = String::from("<ReadoutBoard:");
1338    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1339    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1340    repr += &(format!("\n  RAT         : {}",self.paddle12.ltb_id));
1341    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1342    repr += "\n **Connected paddles**";
1343    repr += &(format!("\n  Channel 1/2 : {:02} (panel {:01})", self.paddle12.paddle_id, self.paddle12.panel_id));
1344    repr += &(format!("\n  Channel 3/4 : {:02} (panel {:01})", self.paddle34.paddle_id, self.paddle34.panel_id));
1345    repr += &(format!("\n  Channel 5/6 : {:02} (panel {:01})", self.paddle56.paddle_id, self.paddle56.panel_id));
1346    repr += &(format!("\n  Channel 7/8 : {:02} (panel {:01})", self.paddle78.paddle_id, self.paddle78.panel_id));
1347    repr
1348  }
1349
1350  /// Load the newest calibration from the calibration file path
1351  pub fn load_latest_calibration(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1352    //  files look like RB20_2024_01_26-08_15_54.cali.tof.gaps
1353    //let re = Regex::new(r"(\d{4}_\d{2}_\d{2}-\d{2}_\d{2}_\d{2})")?;
1354    let re = Regex::new(r"(\d{6}_\d{6})")?;
1355    // Define your file pattern (e.g., "logs/*.log" for all .log files in the logs directory)
1356    let pattern = format!("{}/RB{:02}_*", self.calib_file_path, self.rb_id); // Adjust this pattern to your files' naming convention
1357    let timestamp = DateTime::<Utc>::from_timestamp(0,0).unwrap(); // I am not sure what to do here
1358                                                                   // otherwise than unwrap. How is
1359                                                                   // this allowed to fail?
1360    //let mut newest_file = (String::from(""), NaiveDateTime::from_timestamp(0, 0));
1361    let mut newest_file = (String::from(""), timestamp);
1362
1363    // Iterate over files that match the pattern
1364    let mut filename : String;
1365    for entry in glob(&pattern)? {
1366      if let Ok(path) = entry {
1367        // Get the filename as a string
1368        //let cpath = path.clone();
1369        match path.file_name() {
1370          None => continue,
1371          Some(fname) => {
1372              // the expect might be ok, since this is something done during initialization
1373              filename = fname.to_os_string().into_string().expect("Unwrapping filename failed!");
1374          }
1375        }
1376        if let Some(caps) = re.captures(&filename) {
1377          if let Some(timestamp_str) = caps.get(0).map(|m| m.as_str()) {
1378            //println!("timestamp_str {}, {}",timestamp_str, HUMAN_TIMESTAMP_FORMAT);
1379            //let timestamp = NaiveDateTime::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
1380            //let timestamp = DateTime::<Utc>::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
1381            let footzstring = format!("{}+0000", timestamp_str);
1382            let timestamp = DateTime::parse_from_str(&footzstring, "%y%m%d_%H%M%S%z")?;
1383            //let timestamp = DateTime::parse_from_str(&footzstring, HUMAN_TIMESTAMP_FORMAT)?;
1384            //println!("parse successful");
1385            //let _timestamp = DateTime
1386            if timestamp > newest_file.1 {
1387              // FIXME - into might panic?
1388              newest_file.1 = timestamp.into();
1389              newest_file.0 = filename.clone();
1390            }
1391          }
1392        }
1393      }
1394    }
1395    
1396    if newest_file.0.is_empty() {
1397      error!("No matching calibration available for board {}!", self.rb_id);
1398    } else {
1399      let file_to_load = format!("{}/{}", self.calib_file_path, newest_file.0);
1400      info!("Loading calibration from file: {}", file_to_load);
1401      self.calibration = RBCalibrations::from_file(file_to_load, true)?;
1402    }
1403    Ok(())
1404  }
1405}
1406
1407impl fmt::Display for ReadoutBoard {
1408  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1409    let mut repr  = String::from("<ReadoutBoard:");
1410    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1411    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1412    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1413    repr += "\n **Connected paddles**";
1414    repr += &(format!("\n  Ch0/1(1/2)  : {}",self.paddle12)); 
1415    repr += &(format!("\n    A-side    : {}", self.paddle12_chA));
1416    repr += &(format!("\n  Ch1/2(2/3)  : {}",self.paddle34));         
1417    repr += &(format!("\n    A-side    : {}", self.paddle34_chA));
1418    repr += &(format!("\n  Ch2/3(3/4)  : {}",self.paddle56));         
1419    repr += &(format!("\n    A-side    : {}", self.paddle56_chA));
1420    repr += &(format!("\n  Ch3/4(4/5)  : {}>",self.paddle78));         
1421    repr += &(format!("\n    A-side    : {}", self.paddle78_chA));
1422    repr += "** calibration will be loaded from this path:";
1423    repr += &(format!("\n      \u{021B3} {}", self.calib_file_path));
1424    repr += &(format!("\n  calibration : {}>", self.calibration));
1425    write!(f, "{}", repr)
1426  }
1427}
1428
1429
1430/// A TOF Panel is a larger unit of paddles next to each other
1431///
1432/// TOF faces (e.g. Umbrella) can have multiple Panels
1433#[derive(Debug, Clone,Queryable, Selectable)]
1434#[diesel(table_name = schema::tof_db_panel)]
1435#[diesel(primary_key(panel_id))]
1436pub struct DBPanel {
1437  // ORDER OF THESE FIELDS HAS TO BE THE SAME AS IN schema.rs!!
1438  pub  panel_id    : i16        ,   
1439  pub  description : String     ,   
1440  pub  normal_x    : i16        ,   
1441  pub  normal_y    : i16        ,   
1442  pub  normal_z    : i16        ,   
1443  pub  dw_paddle   : Option<i16>,   
1444  pub  dh_paddle   : Option<i16>,   
1445  pub  paddle0_id  : Option<i16>,   
1446  pub  paddle1_id  : Option<i16>,   
1447  pub  paddle10_id : Option<i16>,   
1448  pub  paddle11_id : Option<i16>,   
1449  pub  paddle2_id  : Option<i16>,   
1450  pub  paddle3_id  : Option<i16>,   
1451  pub  paddle4_id  : Option<i16>,   
1452  pub  paddle5_id  : Option<i16>,   
1453  pub  paddle6_id  : Option<i16>,   
1454  pub  paddle7_id  : Option<i16>,   
1455  pub  paddle8_id  : Option<i16>,   
1456  pub  paddle9_id  : Option<i16>,   
1457}
1458
1459impl DBPanel {
1460
1461  pub fn valid(&self) -> bool {
1462    self.panel_id    > 0 &&    
1463    self.description != String::from("") &&   
1464    self.paddle0_id.is_some()   
1465  }
1466
1467  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBPanel>> {
1468    use schema::tof_db_panel::dsl::*;
1469    match tof_db_panel
1470        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1471        .load::<DBPanel>(conn) {
1472      Err(err) => {
1473        error!("Unable to load Panels from db! {err}");
1474        return None;
1475      }
1476      // dirty mind check
1477      Ok(pnls) => {
1478        return Some(pnls);
1479      }
1480    }
1481  }
1482  
1483  pub fn get_npaddles(&self) -> u8 {
1484    let mut npaddles = 0u8;
1485    if self.paddle0_id.is_some() {
1486      npaddles += 1;
1487    }
1488    if self.paddle1_id.is_some() {
1489      npaddles += 1;
1490    }
1491    if self.paddle2_id.is_some() {
1492      npaddles += 1;
1493    }
1494    if self.paddle3_id.is_some() {
1495      npaddles += 1;
1496    }
1497    if self.paddle4_id.is_some() {
1498      npaddles += 1;
1499    }
1500    if self.paddle5_id.is_some() {
1501      npaddles += 1;
1502    }
1503    if self.paddle6_id.is_some() {
1504      npaddles += 1;
1505    }
1506    if self.paddle7_id.is_some() {
1507      npaddles += 1;
1508    }
1509    if self.paddle8_id.is_some() {
1510      npaddles += 1;
1511    }
1512    if self.paddle9_id.is_some() {
1513      npaddles += 1;
1514    }
1515    if self.paddle10_id.is_some() {
1516      npaddles += 1;
1517    }
1518    if self.paddle11_id.is_some() {
1519      npaddles += 1;
1520    }
1521    npaddles
1522  }
1523}
1524
1525impl fmt::Display for DBPanel {
1526  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1527    let mut repr = String::from("<DBPanel");
1528    repr += &(format!("\n  id    : {}",self.panel_id));
1529    repr += &(format!("\n  descr : {}",self.description));
1530    repr += "\n  orientation:";
1531    repr += &(format!("\n   [{},{},{}]", self.normal_x, self.normal_y, self.normal_z));
1532    repr += &(format!("\n  paddle list ({}) paddles)", self.get_npaddles()));
1533    if self.paddle0_id.is_some() {
1534      repr += &(format!("\n   {}",self.paddle0_id.unwrap()));
1535    }
1536    if self.paddle1_id.is_some() {
1537      repr += &(format!("\n   {}",self.paddle1_id.unwrap()));
1538    }
1539    if self.paddle2_id.is_some() { 
1540      repr += &(format!("\n   {}",self.paddle2_id.unwrap()));
1541    }
1542    if self.paddle3_id.is_some() { 
1543      repr += &(format!("\n   {}",self.paddle3_id.unwrap()));
1544    }
1545    if self.paddle4_id.is_some() {
1546      repr += &(format!("\n   {}",self.paddle4_id.unwrap()));
1547    }
1548    if self.paddle5_id.is_some() {
1549      repr += &(format!("\n   {}",self.paddle5_id.unwrap()));
1550    }
1551    if self.paddle6_id.is_some()  {
1552      repr += &(format!("\n   {}",self.paddle6_id.unwrap()));
1553    }
1554    if self.paddle7_id.is_some() {
1555      repr += &(format!("\n   {}",self.paddle7_id.unwrap()));
1556    }
1557    if self.paddle8_id.is_some() {
1558      repr += &(format!("\n   {}",self.paddle8_id.unwrap()));
1559    }
1560    if self.paddle9_id.is_some() {
1561      repr += &(format!("\n   {}",self.paddle9_id.unwrap()));
1562    }
1563    if self.paddle10_id.is_some() {
1564      repr += &(format!("\n   {}",self.paddle10_id.unwrap()));
1565    }
1566    if self.paddle11_id.is_some() {
1567      repr += &(format!("\n   {}",self.paddle11_id.unwrap()));
1568    }
1569    repr += ">";
1570    write!(f, "{}", repr)
1571  }
1572}
1573
1574pub struct Panel {
1575  pub  panel_id    : u8        ,   
1576  pub  description : String    ,   
1577  pub  normal_x    : u8        ,   
1578  pub  normal_y    : u8        ,   
1579  pub  normal_z    : u8        ,   
1580  pub  paddle0  : Paddle,   
1581  pub  paddle1  : Option<Paddle>,   
1582  pub  paddle2  : Option<Paddle>,   
1583  pub  paddle3  : Option<Paddle>,   
1584  pub  paddle4  : Option<Paddle>,   
1585  pub  paddle5  : Option<Paddle>,   
1586  pub  paddle6  : Option<Paddle>,   
1587  pub  paddle7  : Option<Paddle>,   
1588  pub  paddle8  : Option<Paddle>,   
1589  pub  paddle9  : Option<Paddle>,   
1590  pub  paddle10 : Option<Paddle>,   
1591  pub  paddle11 : Option<Paddle>,   
1592  // FIXME - these are for the future 
1593  // when we are buiding the geometry 
1594  // from the database
1595  //pub  dh_paddle   : Option<>,   
1596  //pub  dw_paddle   : Option<>,   
1597}
1598
1599impl Panel {
1600 
1601  pub fn new() -> Self {
1602    Self {
1603      panel_id    : 0        ,   
1604      description : String::from(""),   
1605      normal_x    : 0        ,   
1606      normal_y    : 0        ,   
1607      normal_z    : 0        ,   
1608      paddle0     : Paddle::new(),   
1609      paddle1     : None,   
1610      paddle2     : None,   
1611      paddle3     : None,   
1612      paddle4     : None,   
1613      paddle5     : None,   
1614      paddle6     : None,   
1615      paddle7     : None,   
1616      paddle8     : None,   
1617      paddle9     : None,   
1618      paddle10    : None,   
1619      paddle11    : None,   
1620    }
1621  }
1622
1623
1624  pub fn get_npaddles(&self) -> u8 {
1625    let mut npaddles = 1u8;
1626    if self.paddle1.is_some() {
1627      npaddles += 1;
1628    }
1629    if self.paddle2.is_some() {
1630      npaddles += 1;
1631    }
1632    if self.paddle3.is_some() {
1633      npaddles += 1;
1634    }
1635    if self.paddle4.is_some() {
1636      npaddles += 1;
1637    }
1638    if self.paddle5.is_some() {
1639      npaddles += 1;
1640    }
1641    if self.paddle6.is_some() {
1642      npaddles += 1;
1643    }
1644    if self.paddle7.is_some() {
1645      npaddles += 1;
1646    }
1647    if self.paddle8.is_some() {
1648      npaddles += 1;
1649    }
1650    if self.paddle9.is_some() {
1651      npaddles += 1;
1652    }
1653    if self.paddle10.is_some() {
1654      npaddles += 1;
1655    }
1656    if self.paddle11.is_some() {
1657      npaddles += 1;
1658    }
1659    npaddles
1660  }
1661  
1662  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<Panel>> {
1663    use schema::tof_db_panel::dsl::*;
1664    let db_panels : Vec<DBPanel>;
1665    match tof_db_panel
1666        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1667        .load::<DBPanel>(conn) {
1668      Err(err) => {
1669        error!("Unable to load Panels from db! {err}");
1670        return None;
1671      }
1672      Ok(pnls) => {
1673        db_panels = pnls;
1674      }
1675    }
1676    let paddles_op = Paddle::all(conn);
1677    match paddles_op {
1678      None => {
1679        return None;
1680      }
1681      Some(_) => ()
1682    }
1683    let paddles = paddles_op.unwrap();
1684    // This is not the best and fastest, but since our diesel skills 
1685    // are a merely 3, we can't do it right now.
1686    let mut panels = Vec::<Panel>::new();
1687    println!("Iterating over {} panels in the DB!", db_panels.len());
1688    for dbpanel in db_panels {
1689      let mut pnl  = Panel::new();
1690      for pdl in paddles.iter() {
1691        // this call ensures that the following unwraps
1692        // go through
1693        if !dbpanel.valid() {
1694          error!("Got unpopulated Panel from DB for Panel {}", dbpanel);
1695          continue;
1696        }
1697        if pdl.paddle_id == dbpanel.paddle0_id.unwrap() {
1698          pnl.panel_id     = dbpanel.panel_id as u8;        
1699          pnl.description  = dbpanel.description.clone();
1700          pnl.normal_x     = dbpanel.normal_x as u8;     
1701          pnl.normal_y     = dbpanel.normal_y as u8;     
1702          pnl.normal_z     = dbpanel.normal_z as u8;    
1703          pnl.paddle0      = pdl.clone();
1704        }
1705        if pdl.paddle_id == dbpanel.paddle1_id.unwrap() {
1706          pnl.paddle1   = Some(pdl.clone());
1707        }
1708        if pdl.paddle_id == dbpanel.paddle2_id.unwrap() {
1709          pnl.paddle2   = Some(pdl.clone());
1710        }
1711        if pdl.paddle_id == dbpanel.paddle3_id.unwrap() {
1712          pnl.paddle3   = Some(pdl.clone());
1713        }
1714        if pdl.paddle_id == dbpanel.paddle4_id.unwrap() {
1715          pnl.paddle4   = Some(pdl.clone());
1716        }
1717        if pdl.paddle_id == dbpanel.paddle5_id.unwrap() {
1718          pnl.paddle5   = Some(pdl.clone());
1719        }
1720        if pdl.paddle_id == dbpanel.paddle6_id.unwrap() {
1721          pnl.paddle6   = Some(pdl.clone());
1722        }
1723        if pdl.paddle_id == dbpanel.paddle7_id.unwrap() {
1724          pnl.paddle7   = Some(pdl.clone());
1725        }
1726        if pdl.paddle_id == dbpanel.paddle8_id.unwrap() {
1727          pnl.paddle8   = Some(pdl.clone());
1728        }
1729        if pdl.paddle_id == dbpanel.paddle9_id.unwrap() {
1730          pnl.paddle9   = Some(pdl.clone());
1731        }
1732        if pdl.paddle_id == dbpanel.paddle10_id.unwrap() {
1733          pnl.paddle10   = Some(pdl.clone());
1734        }
1735        if pdl.paddle_id == dbpanel.paddle11_id.unwrap() {
1736          pnl.paddle11   = Some(pdl.clone());
1737        }
1738      }
1739      panels.push(pnl);
1740    }
1741    Some(panels)
1742  }
1743}
1744
1745impl fmt::Display for Panel {
1746  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1747    let mut repr = String::from("<Panel");
1748    repr += &(format!("\n  id    : {}",self.panel_id));
1749    repr += &(format!("\n  descr : {}",self.description));
1750    repr += "\n  orientation:";
1751    repr += &(format!("\n   [{},{},{}]", self.normal_x, self.normal_y, self.normal_z));
1752    repr += &(format!("\n  paddle list ({}) paddles)", self.get_npaddles()));
1753    repr += &(format!("\n   {}",self.paddle0));
1754    if self.paddle1.is_some() {
1755      repr += &(format!("\n   {}",self.paddle1.as_ref().unwrap()));
1756    }
1757    if self.paddle2.is_some() { 
1758      repr += &(format!("\n   {}",self.paddle2.as_ref().unwrap()));
1759    }
1760    if self.paddle3.is_some() { 
1761      repr += &(format!("\n   {}",self.paddle3.as_ref().unwrap()));
1762    }
1763    if self.paddle4.is_some() {
1764      repr += &(format!("\n   {}",self.paddle4.as_ref().unwrap()));
1765    }
1766    if self.paddle5.is_some() {
1767      repr += &(format!("\n   {}",self.paddle5.as_ref().unwrap()));
1768    }
1769    if self.paddle6.is_some()  {
1770      repr += &(format!("\n   {}",self.paddle6.as_ref().unwrap()));
1771    }
1772    if self.paddle7.is_some() {
1773      repr += &(format!("\n   {}",self.paddle7.as_ref().unwrap()));
1774    }
1775    if self.paddle8.is_some() {
1776      repr += &(format!("\n   {}",self.paddle8.as_ref().unwrap()));
1777    }
1778    if self.paddle9.is_some() {
1779      repr += &(format!("\n   {}",self.paddle9.as_ref().unwrap()));
1780    }
1781    if self.paddle10.is_some() {
1782      repr += &(format!("\n   {}",self.paddle10.as_ref().unwrap()));
1783    }
1784    if self.paddle11.is_some() {
1785      repr += &(format!("\n   {}",self.paddle11.as_ref().unwrap()));
1786    }
1787    repr += ">";
1788    write!(f, "{}", repr)
1789  }
1790}
1791
1792
1793
1794