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  /// FIXME - why use this at all and not just get the one from the db??
553  pub fn get_stripid(&self) -> u32 {
554    self.channel as u32 + (self.module as u32)*100 + (self.row as u32)*10000 + (self.layer as u32)*100000
555  }
556
557  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<Self>> {
558    use schema::tof_db_trackerstrip::dsl::*;
559    match tof_db_trackerstrip.load::<Self>(conn) {
560      Err(err) => {
561        error!("Unable to load tracker strips from db! {err}");
562        return None;
563      }
564      Ok(pdls) => {
565        return Some(pdls);
566      }
567    }
568  }
569}
570
571impl fmt::Display for TrackerStrip {
572  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
573    let mut repr = String::from("<TrackerStrip:");
574    repr += &(format!("\n   strip id           : {}", self.strip_id));     
575    repr += &(format!("\n   vid                : {}", self.volume_id));
576    repr += &(format!("\n   layer              : {}", self.layer));
577    repr += &(format!("\n   row                : {}", self.row));
578    repr += &(format!("\n   module             : {}", self.module));
579    repr += &(format!("\n   channel            : {}", self.channel));
580    repr += "\n   strip center [mm]:";
581    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0));
582    repr += "\n   detector (disk) center [mm]:";
583    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));
584    repr += "\n   strip principal direction:";
585    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.principal_x, self.principal_y, self.principal_z));
586    write!(f, "{}", repr)
587  }
588}
589
590
591
592/// A single TOF paddle with 2 ends 
593/// comnected
594#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
595#[diesel(table_name = schema::tof_db_paddle)]
596#[diesel(primary_key(paddle_id))]
597#[allow(non_snake_case)]
598pub struct Paddle {
599  pub paddle_id         : i16, 
600  pub volume_id         : i64, 
601  pub panel_id          : i16, 
602  pub mtb_link_id       : i16, 
603  pub rb_id             : i16, 
604  pub rb_chA            : i16, 
605  pub rb_chB            : i16, 
606  pub ltb_id            : i16, 
607  pub ltb_chA           : i16, 
608  pub ltb_chB           : i16, 
609  pub pb_id             : i16, 
610  pub pb_chA            : i16, 
611  pub pb_chB            : i16, 
612  pub cable_len         : f32, 
613  pub dsi               : i16, 
614  pub j_rb              : i16, 
615  pub j_ltb             : i16, 
616  pub height            : f32, 
617  pub width             : f32, 
618  pub length            : f32, 
619  pub normal_x          : f32,
620  pub normal_y          : f32,
621  pub normal_z          : f32,
622  pub global_pos_x_l0   : f32, 
623  pub global_pos_y_l0   : f32, 
624  pub global_pos_z_l0   : f32, 
625  pub global_pos_x_l0_A : f32, 
626  pub global_pos_y_l0_A : f32, 
627  pub global_pos_z_l0_A : f32, 
628  pub global_pos_x_l0_B : f32, 
629  pub global_pos_y_l0_B : f32, 
630  pub global_pos_z_l0_B : f32, 
631  pub coax_cable_time   : f32,
632  pub harting_cable_time: f32,
633}
634
635impl Paddle {
636  pub fn new() -> Self {
637    Self {
638      paddle_id         : 0, 
639      volume_id         : 0, 
640      panel_id          : 0, 
641      mtb_link_id       : 0, 
642      rb_id             : 0, 
643      rb_chA            : 0, 
644      rb_chB            : 0, 
645      ltb_id            : 0, 
646      ltb_chA           : 0, 
647      ltb_chB           : 0, 
648      pb_id             : 0, 
649      pb_chA            : 0, 
650      pb_chB            : 0, 
651      cable_len         : 0.0, 
652      dsi               : 0, 
653      j_rb              : 0, 
654      j_ltb             : 0, 
655      height            : 0.0, 
656      width             : 0.0, 
657      length            : 0.0, 
658      normal_x          : 0.0,
659      normal_y          : 0.0,
660      normal_z          : 0.0,
661      global_pos_x_l0   : 0.0, 
662      global_pos_y_l0   : 0.0, 
663      global_pos_z_l0   : 0.0, 
664      global_pos_x_l0_A : 0.0, 
665      global_pos_y_l0_A : 0.0, 
666      global_pos_z_l0_A : 0.0, 
667      global_pos_x_l0_B : 0.0, 
668      global_pos_y_l0_B : 0.0, 
669      global_pos_z_l0_B : 0.0,
670      coax_cable_time   : 0.0, 
671      harting_cable_time: 0.0
672    }
673  }
674
675  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<Paddle>> {
676    use schema::tof_db_paddle::dsl::*;
677    match tof_db_paddle.load::<Paddle>(conn) {
678      Err(err) => {
679        error!("Unable to load paddles from db! {err}");
680        return None;
681      }
682      Ok(pdls) => {
683        return Some(pdls);
684      }
685    }
686  }
687
688  /// The principal is the direction along the longest
689  /// dimension from A -> B
690  pub fn principal(&self) -> (f32,f32,f32) {
691    let mut pr = (self.global_pos_x_l0_B - self.global_pos_x_l0_A,
692                  self.global_pos_y_l0_B - self.global_pos_y_l0_A,
693                  self.global_pos_z_l0_B - self.global_pos_z_l0_A);
694    let length = f32::sqrt(pr.0.powf(2.0) + pr.1.powf(2.0) + pr.2.powf(2.0)); 
695    pr = (pr.0/length, pr.1/length, pr.2/length);
696    return pr;
697  }
698
699}
700
701impl fmt::Display for Paddle {
702  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
703    let mut repr = String::from("<Paddle:");
704    repr += "\n** identifiers **";
705    repr += &(format!("\n   pid                : {}", self.paddle_id));     
706    repr += &(format!("\n   vid                : {}", self.volume_id));
707    repr += &(format!("\n   panel id           : {}", self.panel_id));
708    repr += "\n  ** connedtions **";
709    repr += &(format!("\n   DSI/J/CH (LG) [A]  : {}  | {} | {:02}", self.dsi, self.j_ltb, self.ltb_chA));
710    repr += &(format!("\n   DSI/J/CH (HG) [A]  : {}  | {} | {:02}", self.dsi, self.j_rb, self.rb_chA));
711    repr += &(format!("\n   DSI/J/CH (LG) [B]  : {}  | {} | {:02}", self.dsi, self.j_ltb, self.ltb_chB));
712    repr += &(format!("\n   DSI/J/CH (HG) [B]  : {}  | {} | {:02}", self.dsi, self.j_rb, self.rb_chB));
713    repr += &(format!("\n   RB/CH         [A]  : {:02} | {}", self.rb_id, self.rb_chA));
714    repr += &(format!("\n   RB/CH         [B]  : {:02} | {}", self.rb_id, self.rb_chB));
715    repr += &(format!("\n   LTB/CH        [A]  : {:02} | {}", self.ltb_id, self.ltb_chA));
716    repr += &(format!("\n   LTB/CH        [B]  : {:02} | {}", self.ltb_id, self.ltb_chB));
717    repr += &(format!("\n   PB/CH         [A]  : {:02} | {}", self.pb_id, self.pb_chA));
718    repr += &(format!("\n   PB/CH         [B]  : {:02} | {}", self.pb_id, self.pb_chB));
719    repr += &(format!("\n   MTB Link ID        : {:02}", self.mtb_link_id));
720    repr += "\n   cable len [cm] :";
721    repr += &(format!("\n    \u{21B3} {:.2}", self.cable_len));
722    repr += "\n    (Harting -> RB)";
723    repr += "\n   cable times [ns] (JAZ) :";
724    repr += &(format!("\n    \u{21B3} {:.2} {:.2}", self.coax_cable_time, self.harting_cable_time));
725    repr += "\n  ** Coordinates (L0) & dimensions **";
726    repr += "\n   length, width, height [mm]";
727    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.length, self.width, self.height));
728    repr += "\n   center [mm]:";
729    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.global_pos_x_l0, self.global_pos_y_l0, self.global_pos_z_l0));
730    repr += "\n   normal vector:";
731    repr += &(format!("\n    \u{21B3} [{:.2}, {:.2}, {:.2}]", self.normal_x, self.normal_y, self.normal_z));
732    repr += "\n   A-side [mm]:";
733    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));
734    repr += "\n   B-side [mm]:";
735    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));
736    write!(f, "{}", repr)
737  }
738}
739    
740// Summary of DSI/J/LTBCH (0-319)
741// This is not "official" but provides a way of indexing all
742// the individual channels
743#[derive(Debug,PartialEq,Queryable, Selectable)]
744#[diesel(table_name = schema::tof_db_mtbchannel)]
745#[diesel(primary_key(mtb_ch))]
746#[allow(non_snake_case)]
747pub struct MTBChannel {
748  pub mtb_ch      : i64,         
749  pub dsi         : Option<i16>, 
750  pub j           : Option<i16>, 
751  pub ltb_id      : Option<i16>, 
752  pub ltb_ch      : Option<i16>, 
753  pub rb_id       : Option<i16>, 
754  pub rb_ch       : Option<i16>, 
755  pub mtb_link_id : Option<i16>, 
756  pub paddle_id   : Option<i16>, 
757  pub paddle_isA  : Option<bool>,
758  pub hg_ch       : Option<i16>, 
759  pub lg_ch       : Option<i16>, 
760}
761
762impl MTBChannel {
763
764  pub fn new() -> Self {
765    Self {
766      mtb_ch      : -1,         
767      dsi         : None, 
768      j           : None, 
769      ltb_id      : None, 
770      ltb_ch      : None, 
771      rb_id       : None, 
772      rb_ch       : None, 
773      mtb_link_id : None, 
774      paddle_id   : None, 
775      paddle_isA  : None,
776      hg_ch       : None, 
777      lg_ch       : None, 
778    }
779  }
780  
781  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<MTBChannel>> {
782    use schema::tof_db_mtbchannel::dsl::*;
783    match tof_db_mtbchannel.load::<MTBChannel>(conn) {
784      Err(err) => {
785        error!("Unable to load RATs from db! {err}");
786        return None;
787      }
788      Ok(mtbch) => {
789        return Some(mtbch);
790      }
791    }
792  }
793}
794
795
796impl fmt::Display for MTBChannel {
797  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
798    let mut repr = String::from("<MTBChannel");
799    repr += &(format!("\n  Channel ID : {}", self.mtb_ch));
800    repr += &(format!("\n  DSI/J/     : {}/{}", self.dsi.unwrap_or(-1), self.j.unwrap_or(-1)));
801    repr += "\n  LTB ID/CH => RB ID/CH";
802    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)));
803    repr += &(format!("\n  MTB Link ID [RB] : {}", self.mtb_link_id.unwrap_or(-1)));
804    repr += "\n  LG CH => HG CH";
805    repr += &(format!("\n   |-> {} => {}", self.lg_ch.unwrap_or(-1), self.hg_ch.unwrap_or(-1)));
806    repr += &(format!("\n  Paddle Id: {}", self.paddle_id.unwrap_or(-1)));
807    let mut pend = "None";
808    if !self.paddle_isA.is_none() {
809      if self.paddle_isA.unwrap() {
810          pend = "A";
811      } else {
812          pend = "B";
813      }
814    }
815    repr += &(format!("\n  Paddle End: {}>", pend));
816    write!(f, "{}", repr)
817  }
818}
819
820
821///////////////////////////////////////////////////
822//
823// The following models exceed a bit the capabilities
824// of Diesel, or my Diesel skill.
825// These models contain multiple ForeignKeys, in all
826// cases these link to the paddle table. 
827//
828// For each of LocalTriggerBoard, ReadoutBoard, Panel
829// we have 2 structs:
830// One called DB<entity> and the other <entity>. The
831// first does have the ForeignKeys as SmallInt, and 
832// the latter looks them up and fills in the blanks
833//
834//
835//
836
837/// The DB wrapper for the LocalTriggerBoard, for 
838/// easy implementation there are no joins, we do 
839/// them manually in the public implementation 
840/// of the LocaltriggerBoard
841#[derive(Queryable, Selectable, Identifiable, Associations)]
842#[diesel(table_name = schema::tof_db_localtriggerboard)]
843#[diesel(primary_key(board_id))]
844#[diesel(belongs_to(Paddle, foreign_key=paddle1_id))]
845pub struct DBLocalTriggerBoard {
846    pub board_id      : i16,    
847    pub dsi           : Option<i16>,
848    pub j             : Option<i16>,
849    pub rat           : Option<i16>,
850    pub ltb_id        : Option<i16>, 
851    pub cable_len     : f32,
852    pub paddle1_id    : Option<i16>,
853    pub paddle2_id    : Option<i16>,
854    pub paddle3_id    : Option<i16>,
855    pub paddle4_id    : Option<i16>,
856    pub paddle5_id    : Option<i16>,
857    pub paddle6_id    : Option<i16>,
858    pub paddle7_id    : Option<i16>,
859    pub paddle8_id    : Option<i16>,
860}
861
862impl DBLocalTriggerBoard {
863  
864  //pub fn new() -> Self {
865  //  Self {
866  //    board_id      : 0,    
867  //    dsi           : None,
868  //    j             : None,
869  //    rat           : None,
870  //    ltb_id        : None, 
871  //    cable_len     : 0.0,
872  //    paddle1_id    : None,
873  //    paddle2_id    : None,
874  //    paddle3_id    : None,
875  //    paddle4_id    : None,
876  //    paddle5_id    : None,
877  //    paddle6_id    : None,
878  //    paddle7_id    : None,
879  //    paddle8_id    : None,
880  //  }
881  //}
882
883  /// True if sane dsi and j values are 
884  /// assigned to this board
885  pub fn connected(&self) -> bool {
886    self.dsi != None && self.j != None
887  }
888
889  /// True if all fields are filled with 
890  /// reasonable values and not the default
891  pub fn valid(&self) -> bool {
892    self.board_id      > 0 &&    
893    self.dsi       .is_some() && 
894    self.j         .is_some() && 
895    self.rat       .is_some() && 
896    // right now, we explicitly don't care
897    // about the ltb_id
898    //self.ltb_id    .is_some() &&  
899    self.cable_len     > 0.0  &&
900    self.paddle1_id.is_some() &&
901    self.paddle2_id.is_some() &&
902    self.paddle3_id.is_some() &&
903    self.paddle4_id.is_some() &&
904    self.paddle5_id.is_some() &&
905    self.paddle6_id.is_some() &&
906    self.paddle7_id.is_some() &&
907    self.paddle8_id.is_some()
908  }
909  
910  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBLocalTriggerBoard>> {
911    use schema::tof_db_localtriggerboard::dsl::*;
912    match tof_db_localtriggerboard
913        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
914        .load::<DBLocalTriggerBoard>(conn) {
915      Err(err) => {
916        error!("Unable to load LocalTriggerBoards from db! {err}");
917        return None;
918      }
919      Ok(ltbs) => {
920        return Some(ltbs);
921      }
922    }
923  }
924}
925
926impl fmt::Display for DBLocalTriggerBoard {
927  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
928    let mut repr : String;
929    if !self.connected() {
930      repr = format!("<DBLocalTriggerBoard: ID {}  - UNCONNECTED>", self.board_id);
931    } else {
932      repr = String::from("<DBLocalTriggerBoard:");
933      repr += &(format!("\n  LTB ID  : {}", self.board_id));             
934    }
935    repr += &(format!("\n  DSI/J   : {}/{}", self.dsi.unwrap(), self.j.unwrap()));     
936    repr += &(format!("\n  RAT ID  : {}", self.rat.unwrap()));
937    repr += "\n  H. cable len (MTB connection):";
938    repr += &(format!("\n    ->      {}", self.cable_len));
939    repr += "\n  -- -- -- -- -- -- -- -- -- -- -- -- -- --";
940    repr += "\n  Paddle IDs:";
941    repr += &(format!("\n    {:02}", self.paddle1_id.unwrap_or(-1))); 
942    repr += &(format!("\n    {:02}", self.paddle2_id.unwrap_or(-1)));  
943    repr += &(format!("\n    {:02}", self.paddle3_id.unwrap_or(-1)));  
944    repr += &(format!("\n    {:02}", self.paddle4_id.unwrap_or(-1)));  
945    repr += &(format!("\n    {:02}", self.paddle5_id.unwrap_or(-1))); 
946    repr += &(format!("\n    {:02}", self.paddle6_id.unwrap_or(-1))); 
947    repr += &(format!("\n    {:02}", self.paddle7_id.unwrap_or(-1))); 
948    repr += &(format!("\n    {:02}", self.paddle8_id.unwrap_or(-1))); 
949    write!(f, "{}", repr)
950  }
951}
952
953#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
954pub struct LocalTriggerBoard {
955    pub board_id      : u8,    
956    pub dsi           : u8,
957    pub j             : u8,
958    pub rat           : u8,
959    pub ltb_id        : u8, 
960    pub cable_len     : f32,
961    pub paddle1       : Paddle,
962    pub paddle2       : Paddle,
963    pub paddle3       : Paddle,
964    pub paddle4       : Paddle,
965    pub paddle5       : Paddle,
966    pub paddle6       : Paddle,
967    pub paddle7       : Paddle,
968    pub paddle8       : Paddle,
969}
970
971impl LocalTriggerBoard {
972  
973  pub fn new() -> Self {
974    Self {
975      board_id      : 0,    
976      dsi           : 0,
977      j             : 0,
978      rat           : 0,
979      ltb_id        : 0, 
980      cable_len     : 0.0,
981      paddle1       : Paddle::new(),
982      paddle2       : Paddle::new(),
983      paddle3       : Paddle::new(),
984      paddle4       : Paddle::new(),
985      paddle5       : Paddle::new(),
986      paddle6       : Paddle::new(),
987      paddle7       : Paddle::new(),
988      paddle8       : Paddle::new(),
989    }
990  }
991  
992  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<LocalTriggerBoard>> {
993    use schema::tof_db_localtriggerboard::dsl::*;
994    let db_ltbs : Vec<DBLocalTriggerBoard>;
995    match tof_db_localtriggerboard
996        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
997        .load::<DBLocalTriggerBoard>(conn) {
998      Err(err) => {
999        error!("Unable to load LocalTriggerBoards from db! {err}");
1000        return None;
1001      }
1002      Ok(ltbs) => {
1003        db_ltbs = ltbs;
1004      }
1005    }
1006    let paddles_op = Paddle::all(conn);
1007    match paddles_op {
1008      None => {
1009        return None;
1010      }
1011      Some(_) => ()
1012    }
1013    let paddles = paddles_op.unwrap();
1014    // This is not the best and fastest, but since our diesel skills 
1015    // are a merely 3, we can't do it right now.
1016    let mut ltbs = Vec::<LocalTriggerBoard>::new();
1017    //println!("Iterating over {} ltbs in the DB!", db_ltbs.len());
1018    for dbltb in db_ltbs {
1019      let mut ltb  = LocalTriggerBoard::new();
1020      for pdl in paddles.iter() {
1021        // this call ensures that the following unwraps
1022        // go through
1023        if !dbltb.valid() {
1024          error!("Got unpopulated LTB from DB for LTB {}", dbltb);
1025          continue;
1026        }
1027        if pdl.paddle_id == dbltb.paddle1_id.unwrap() {
1028          ltb.board_id  = dbltb.board_id as u8;        
1029          ltb.dsi       = dbltb.dsi.unwrap_or(0) as u8;
1030          ltb.j         = dbltb.j.unwrap_or(0) as u8;     
1031          ltb.rat       = dbltb.rat.unwrap_or(0) as u8;     
1032          ltb.ltb_id    = dbltb.ltb_id.unwrap_or(0) as u8;    
1033          ltb.cable_len = dbltb.cable_len;    
1034          ltb.paddle1   = pdl.clone();
1035        }
1036        if pdl.paddle_id == dbltb.paddle2_id.unwrap() {
1037          ltb.paddle2   = pdl.clone();
1038        }
1039        if pdl.paddle_id == dbltb.paddle3_id.unwrap() {
1040          ltb.paddle3   = pdl.clone();
1041        }
1042        if pdl.paddle_id == dbltb.paddle4_id.unwrap() {
1043          ltb.paddle4   = pdl.clone();
1044        }
1045        if pdl.paddle_id == dbltb.paddle5_id.unwrap() {
1046          ltb.paddle5   = pdl.clone();
1047        }
1048        if pdl.paddle_id == dbltb.paddle6_id.unwrap() {
1049          ltb.paddle6   = pdl.clone();
1050        }
1051        if pdl.paddle_id == dbltb.paddle7_id.unwrap() {
1052          ltb.paddle7   = pdl.clone();
1053        }
1054        if pdl.paddle_id == dbltb.paddle8_id.unwrap() {
1055          ltb.paddle8   = pdl.clone();
1056        }
1057      }
1058      ltbs.push(ltb);
1059    }
1060    Some(ltbs)
1061  }
1062}
1063
1064impl fmt::Display for LocalTriggerBoard {
1065  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1066    let mut repr : String;
1067    repr = String::from("<LocalTriggerBoard:");
1068    repr += &(format!("\n  LTB ID  : {}", self.board_id));             
1069    repr += &(format!("\n  DSI/J   : {}/{}", self.dsi, self.j));     
1070    repr += &(format!("\n  RAT ID  : {}", self.rat));
1071    repr += "\n  H. cable len (MTB connection):";
1072    repr += &(format!("\n    ->      {}", self.cable_len));
1073    repr += "\n  -- -- -- -- -- -- -- -- -- -- -- -- -- --";
1074    repr += "\n  LTB Ch -> RB Id, RB chn, Pdl ID, Pan ID:";
1075    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)); 
1076    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));  
1077    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));  
1078    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));  
1079    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)); 
1080    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)); 
1081    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)); 
1082    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)); 
1083    write!(f, "{}", repr)
1084  }
1085}
1086
1087/// A Readoutboard with paddles connected
1088/// 
1089#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
1090#[diesel(table_name = schema::tof_db_readoutboard)]
1091#[diesel(primary_key(rb_id_id))]
1092#[allow(non_snake_case)]
1093pub struct DBReadoutBoard {
1094  // FIXME - this HAS TO BE (MUST!) the same order
1095  // as in schema.rs !!
1096  pub rb_id        : i16, 
1097  pub dsi          : i16, 
1098  pub j            : i16, 
1099  pub mtb_link_id  : i16, 
1100  pub paddle12_chA : Option<i16>,
1101  pub paddle34_chA : Option<i16>,
1102  pub paddle56_chA : Option<i16>,
1103  pub paddle78_chA : Option<i16>,
1104  pub paddle12_id  : Option<i16>,
1105  pub paddle34_id  : Option<i16>,
1106  pub paddle56_id  : Option<i16>,
1107  pub paddle78_id  : Option<i16>,
1108}
1109
1110impl DBReadoutBoard {
1111  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBReadoutBoard>> {
1112    use schema::tof_db_readoutboard::dsl::*;
1113    match tof_db_readoutboard
1114        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1115        .load::<DBReadoutBoard>(conn) {
1116      Err(err) => {
1117        error!("Unable to load ReadoutBoards from db! {err}");
1118        return None;
1119      }
1120      Ok(rbs) => {
1121        return Some(rbs);
1122      }
1123    }
1124  }
1125}
1126
1127impl fmt::Display for DBReadoutBoard {
1128  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1129    let mut repr  = String::from("<ReadoutBoard:");
1130    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1131    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1132    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1133    repr += "\n **Connected paddles**";
1134    repr += &(format!("\n  Ch0/1(1/2)  : {}", self.paddle12_id.unwrap_or(-1)));         
1135    repr += &(format!("\n  Ch1/2(2/3)  : {}", self.paddle34_id.unwrap_or(-1)));         
1136    repr += &(format!("\n  Ch2/3(3/4)  : {}", self.paddle56_id.unwrap_or(-1)));         
1137    repr += &(format!("\n  Ch3/4(4/5)  : {}>",self.paddle78_id.unwrap_or(-1)));         
1138    write!(f, "{}", repr)
1139  }
1140}
1141
1142/// A Readoutboard with paddles connected
1143#[derive(Debug, Clone)]
1144#[allow(non_snake_case)]
1145pub struct ReadoutBoard {
1146  pub rb_id           : u8, 
1147  pub dsi             : u8, 
1148  pub j               : u8, 
1149  pub mtb_link_id     : u8, 
1150  pub paddle12        : Paddle,
1151  pub paddle12_chA    : u8,
1152  pub paddle34        : Paddle,
1153  pub paddle34_chA    : u8,
1154  pub paddle56        : Paddle,
1155  pub paddle56_chA    : u8,
1156  pub paddle78        : Paddle,
1157  pub paddle78_chA    : u8,
1158  // extra stuff, not from the db
1159  // or maybe in the future?
1160  pub calib_file_path : String,
1161  pub calibration     : RBCalibrations,       
1162}
1163
1164impl ReadoutBoard {
1165
1166  pub fn new() -> Self {
1167    Self {
1168      rb_id           : 0, 
1169      dsi             : 0, 
1170      j               : 0, 
1171      mtb_link_id     : 0, 
1172      paddle12        : Paddle::new(),
1173      paddle12_chA    : 0,
1174      paddle34        : Paddle::new(),
1175      paddle34_chA    : 0,
1176      paddle56        : Paddle::new(),
1177      paddle56_chA    : 0,
1178      paddle78        : Paddle::new(),
1179      paddle78_chA    : 0,
1180      calib_file_path : String::from(""),
1181      calibration     : RBCalibrations::new(0),
1182    }
1183  }
1184
1185  /// Returns the ip address following a convention
1186  ///
1187  /// This does NOT GUARANTEE that the address is correct!
1188  pub fn guess_address(&self) -> String {
1189    format!("tcp://10.0.1.1{:02}:42000", self.rb_id)
1190  }
1191 
1192  pub fn get_paddle_ids(&self) -> [u8;4] {
1193    let pid0 = self.paddle12.paddle_id as u8;
1194    let pid1 = self.paddle34.paddle_id as u8;
1195    let pid2 = self.paddle56.paddle_id as u8;
1196    let pid3 = self.paddle78.paddle_id as u8;
1197    [pid0, pid1, pid2, pid3]
1198  }
1199
1200  #[allow(non_snake_case)]
1201  pub fn get_A_sides(&self) -> [u8;4] {
1202    let pa_0 = self.paddle12_chA;
1203    let pa_1 = self.paddle34_chA;
1204    let pa_2 = self.paddle56_chA;
1205    let pa_3 = self.paddle78_chA;
1206    [pa_0, pa_1, pa_2, pa_3]
1207  }
1208
1209  #[allow(non_snake_case)]
1210  pub fn get_pid_rbchA(&self, pid : u8) -> Option<u8> {
1211    if self.paddle12.paddle_id as u8 == pid {
1212      let rv = self.paddle12.rb_chA as u8;
1213      return Some(rv);
1214    } else if self.paddle34.paddle_id as u8 == pid {
1215      let rv = self.paddle34.rb_chA as u8;
1216      return Some(rv);
1217    } else if self.paddle56.paddle_id as u8 == pid {
1218      let rv = self.paddle56.rb_chA as u8;
1219      return Some(rv);
1220    } else if self.paddle78.paddle_id as u8== pid {
1221      let rv = self.paddle78.rb_chA as u8;
1222      return Some(rv);
1223    } else {
1224      return None;
1225    }
1226  }
1227  
1228  #[allow(non_snake_case)]
1229  pub fn get_pid_rbchB(&self, pid : u8) -> Option<u8> {
1230    if self.paddle12.paddle_id as u8 == pid {
1231      let rv = self.paddle12.rb_chB as u8;
1232      return Some(rv);
1233    } else if self.paddle34.paddle_id as u8== pid {
1234      let rv = self.paddle34.rb_chB as u8;
1235      return Some(rv);
1236    } else if self.paddle56.paddle_id as u8== pid {
1237      let rv = self.paddle56.rb_chB as u8;
1238      return Some(rv);
1239    } else if self.paddle78.paddle_id as u8 == pid {
1240      let rv = self.paddle78.rb_chB as u8;
1241      return Some(rv);
1242    } else {
1243      return None;
1244    }
1245  }
1246
1247  pub fn get_paddle_length(&self, pid : u8) -> Option<f32> {
1248    if self.paddle12.paddle_id as u8 == pid {
1249      let rv = self.paddle12.length;
1250      return Some(rv);
1251    } else if self.paddle34.paddle_id as u8== pid {
1252      let rv = self.paddle34.length;
1253      return Some(rv);
1254    } else if self.paddle56.paddle_id as u8== pid {
1255      let rv = self.paddle56.length;
1256      return Some(rv);
1257    } else if self.paddle78.paddle_id as u8 == pid {
1258      let rv = self.paddle78.length;
1259      return Some(rv);
1260    } else {
1261      return None;
1262    }
1263  }
1264
1265  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<ReadoutBoard>> {
1266    use schema::tof_db_readoutboard::dsl::*;
1267    let db_rbs : Vec<DBReadoutBoard>;
1268    match tof_db_readoutboard
1269        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1270        .load::<DBReadoutBoard>(conn) {
1271      Err(err) => {
1272        error!("Unable to load ReadoutBoards from db! {err}");
1273        return None;
1274      }
1275      Ok(rbs) => {
1276        db_rbs = rbs;
1277      }
1278    }
1279    let paddles_op = Paddle::all(conn);
1280    match paddles_op {
1281      None => {
1282        return None;
1283      }
1284      Some(_) => ()
1285    }
1286    let paddles = paddles_op.unwrap();
1287    // This is not the best and fastest, but since our diesel skills 
1288    // are a merely 3, we can't do it right now.
1289    let mut rbs = Vec::<ReadoutBoard>::new();
1290    //println!("Iterating over {} rbs in the DB!", db_rbs.len());
1291    for dbrb in db_rbs {
1292      let mut rb  = ReadoutBoard::new();
1293      rb.rb_id        = dbrb.rb_id as u8;        
1294      rb.dsi          = dbrb.dsi as u8;
1295      rb.j            = dbrb.j  as u8;     
1296      rb.mtb_link_id  = dbrb.mtb_link_id  as u8;    
1297      rb.paddle12_chA = dbrb.paddle12_chA.unwrap() as u8;
1298      rb.paddle34_chA = dbrb.paddle34_chA.unwrap() as u8;
1299      rb.paddle56_chA = dbrb.paddle56_chA.unwrap() as u8;
1300      rb.paddle78_chA = dbrb.paddle78_chA.unwrap() as u8;
1301      for pdl in paddles.iter() {
1302        // this call ensures that the following unwraps
1303        // go through
1304        //if !dbltb.valid() {
1305        //  error!("Got unpopulated LTB from DB for LTB {}", dbltb);
1306        //  continue;
1307        //}
1308        if pdl.paddle_id == dbrb.paddle12_id.unwrap_or(0) {
1309          rb.paddle12     = pdl.clone();
1310        }
1311        if pdl.paddle_id == dbrb.paddle34_id.unwrap_or(0) {
1312          rb.paddle34   = pdl.clone();
1313        }
1314        if pdl.paddle_id == dbrb.paddle56_id.unwrap_or(0) {
1315          rb.paddle56   = pdl.clone();
1316        }
1317        if pdl.paddle_id == dbrb.paddle78_id.unwrap_or(0) {
1318          rb.paddle78   = pdl.clone();
1319        }
1320      }
1321      rbs.push(rb);
1322    }
1323    Some(rbs)
1324  }
1325  
1326  // FIXME - better query
1327  pub fn where_rbid(conn: &mut SqliteConnection, rb_id : u8) -> Option<ReadoutBoard> {
1328    let all = ReadoutBoard::all(conn)?;
1329    for rb in all {
1330      if rb.rb_id == rb_id {
1331        return Some(rb);
1332      }
1333    }
1334    None
1335  }
1336
1337  pub fn to_summary_str(&self) -> String {
1338    let mut repr  = String::from("<ReadoutBoard:");
1339    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1340    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1341    repr += &(format!("\n  RAT         : {}",self.paddle12.ltb_id));
1342    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1343    repr += "\n **Connected paddles**";
1344    repr += &(format!("\n  Channel 1/2 : {:02} (panel {:01})", self.paddle12.paddle_id, self.paddle12.panel_id));
1345    repr += &(format!("\n  Channel 3/4 : {:02} (panel {:01})", self.paddle34.paddle_id, self.paddle34.panel_id));
1346    repr += &(format!("\n  Channel 5/6 : {:02} (panel {:01})", self.paddle56.paddle_id, self.paddle56.panel_id));
1347    repr += &(format!("\n  Channel 7/8 : {:02} (panel {:01})", self.paddle78.paddle_id, self.paddle78.panel_id));
1348    repr
1349  }
1350
1351  /// Load the newest calibration from the calibration file path
1352  pub fn load_latest_calibration(&mut self) -> Result<(), Box<dyn std::error::Error>> {
1353    //  files look like RB20_2024_01_26-08_15_54.cali.tof.gaps
1354    //let re = Regex::new(r"(\d{4}_\d{2}_\d{2}-\d{2}_\d{2}_\d{2})")?;
1355    let re = Regex::new(r"(\d{6}_\d{6})")?;
1356    // Define your file pattern (e.g., "logs/*.log" for all .log files in the logs directory)
1357    let pattern = format!("{}/RB{:02}_*", self.calib_file_path, self.rb_id); // Adjust this pattern to your files' naming convention
1358    let timestamp = DateTime::<Utc>::from_timestamp(0,0).unwrap(); // I am not sure what to do here
1359                                                                   // otherwise than unwrap. How is
1360                                                                   // this allowed to fail?
1361    //let mut newest_file = (String::from(""), NaiveDateTime::from_timestamp(0, 0));
1362    let mut newest_file = (String::from(""), timestamp);
1363
1364    // Iterate over files that match the pattern
1365    let mut filename : String;
1366    for entry in glob(&pattern)? {
1367      if let Ok(path) = entry {
1368        // Get the filename as a string
1369        //let cpath = path.clone();
1370        match path.file_name() {
1371          None => continue,
1372          Some(fname) => {
1373              // the expect might be ok, since this is something done during initialization
1374              filename = fname.to_os_string().into_string().expect("Unwrapping filename failed!");
1375          }
1376        }
1377        if let Some(caps) = re.captures(&filename) {
1378          if let Some(timestamp_str) = caps.get(0).map(|m| m.as_str()) {
1379            //println!("timestamp_str {}, {}",timestamp_str, HUMAN_TIMESTAMP_FORMAT);
1380            //let timestamp = NaiveDateTime::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
1381            //let timestamp = DateTime::<Utc>::parse_from_str(timestamp_str, "%Y_%m_%d-%H_%M_%S")?;
1382            let footzstring = format!("{}+0000", timestamp_str);
1383            let timestamp = DateTime::parse_from_str(&footzstring, "%y%m%d_%H%M%S%z")?;
1384            //let timestamp = DateTime::parse_from_str(&footzstring, HUMAN_TIMESTAMP_FORMAT)?;
1385            //println!("parse successful");
1386            //let _timestamp = DateTime
1387            if timestamp > newest_file.1 {
1388              // FIXME - into might panic?
1389              newest_file.1 = timestamp.into();
1390              newest_file.0 = filename.clone();
1391            }
1392          }
1393        }
1394      }
1395    }
1396    
1397    if newest_file.0.is_empty() {
1398      error!("No matching calibration available for board {}!", self.rb_id);
1399    } else {
1400      let file_to_load = format!("{}/{}", self.calib_file_path, newest_file.0);
1401      info!("Loading calibration from file: {}", file_to_load);
1402      self.calibration = RBCalibrations::from_file(file_to_load, true)?;
1403    }
1404    Ok(())
1405  }
1406}
1407
1408impl fmt::Display for ReadoutBoard {
1409  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1410    let mut repr  = String::from("<ReadoutBoard:");
1411    repr += &(format!("\n  Board id    : {}",self.rb_id));            
1412    repr += &(format!("\n  MTB Link ID : {}",self.mtb_link_id));
1413    repr += &(format!("\n  DSI/J       : {}/{}",self.dsi,self.j));
1414    repr += "\n **Connected paddles**";
1415    repr += &(format!("\n  Ch0/1(1/2)  : {}",self.paddle12)); 
1416    repr += &(format!("\n    A-side    : {}", self.paddle12_chA));
1417    repr += &(format!("\n  Ch1/2(2/3)  : {}",self.paddle34));         
1418    repr += &(format!("\n    A-side    : {}", self.paddle34_chA));
1419    repr += &(format!("\n  Ch2/3(3/4)  : {}",self.paddle56));         
1420    repr += &(format!("\n    A-side    : {}", self.paddle56_chA));
1421    repr += &(format!("\n  Ch3/4(4/5)  : {}>",self.paddle78));         
1422    repr += &(format!("\n    A-side    : {}", self.paddle78_chA));
1423    repr += "** calibration will be loaded from this path:";
1424    repr += &(format!("\n      \u{021B3} {}", self.calib_file_path));
1425    repr += &(format!("\n  calibration : {}>", self.calibration));
1426    write!(f, "{}", repr)
1427  }
1428}
1429
1430
1431/// A TOF Panel is a larger unit of paddles next to each other
1432///
1433/// TOF faces (e.g. Umbrella) can have multiple Panels
1434#[derive(Debug, Clone,Queryable, Selectable)]
1435#[diesel(table_name = schema::tof_db_panel)]
1436#[diesel(primary_key(panel_id))]
1437pub struct DBPanel {
1438  // ORDER OF THESE FIELDS HAS TO BE THE SAME AS IN schema.rs!!
1439  pub  panel_id    : i16        ,   
1440  pub  description : String     ,   
1441  pub  normal_x    : i16        ,   
1442  pub  normal_y    : i16        ,   
1443  pub  normal_z    : i16        ,   
1444  pub  dw_paddle   : Option<i16>,   
1445  pub  dh_paddle   : Option<i16>,   
1446  pub  paddle0_id  : Option<i16>,   
1447  pub  paddle1_id  : Option<i16>,   
1448  pub  paddle10_id : Option<i16>,   
1449  pub  paddle11_id : Option<i16>,   
1450  pub  paddle2_id  : Option<i16>,   
1451  pub  paddle3_id  : Option<i16>,   
1452  pub  paddle4_id  : Option<i16>,   
1453  pub  paddle5_id  : Option<i16>,   
1454  pub  paddle6_id  : Option<i16>,   
1455  pub  paddle7_id  : Option<i16>,   
1456  pub  paddle8_id  : Option<i16>,   
1457  pub  paddle9_id  : Option<i16>,   
1458}
1459
1460impl DBPanel {
1461
1462  pub fn valid(&self) -> bool {
1463    self.panel_id    > 0 &&    
1464    self.description != String::from("") &&   
1465    self.paddle0_id.is_some()   
1466  }
1467
1468  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<DBPanel>> {
1469    use schema::tof_db_panel::dsl::*;
1470    match tof_db_panel
1471        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1472        .load::<DBPanel>(conn) {
1473      Err(err) => {
1474        error!("Unable to load Panels from db! {err}");
1475        return None;
1476      }
1477      // dirty mind check
1478      Ok(pnls) => {
1479        return Some(pnls);
1480      }
1481    }
1482  }
1483  
1484  pub fn get_npaddles(&self) -> u8 {
1485    let mut npaddles = 0u8;
1486    if self.paddle0_id.is_some() {
1487      npaddles += 1;
1488    }
1489    if self.paddle1_id.is_some() {
1490      npaddles += 1;
1491    }
1492    if self.paddle2_id.is_some() {
1493      npaddles += 1;
1494    }
1495    if self.paddle3_id.is_some() {
1496      npaddles += 1;
1497    }
1498    if self.paddle4_id.is_some() {
1499      npaddles += 1;
1500    }
1501    if self.paddle5_id.is_some() {
1502      npaddles += 1;
1503    }
1504    if self.paddle6_id.is_some() {
1505      npaddles += 1;
1506    }
1507    if self.paddle7_id.is_some() {
1508      npaddles += 1;
1509    }
1510    if self.paddle8_id.is_some() {
1511      npaddles += 1;
1512    }
1513    if self.paddle9_id.is_some() {
1514      npaddles += 1;
1515    }
1516    if self.paddle10_id.is_some() {
1517      npaddles += 1;
1518    }
1519    if self.paddle11_id.is_some() {
1520      npaddles += 1;
1521    }
1522    npaddles
1523  }
1524}
1525
1526impl fmt::Display for DBPanel {
1527  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1528    let mut repr = String::from("<DBPanel");
1529    repr += &(format!("\n  id    : {}",self.panel_id));
1530    repr += &(format!("\n  descr : {}",self.description));
1531    repr += "\n  orientation:";
1532    repr += &(format!("\n   [{},{},{}]", self.normal_x, self.normal_y, self.normal_z));
1533    repr += &(format!("\n  paddle list ({}) paddles)", self.get_npaddles()));
1534    if self.paddle0_id.is_some() {
1535      repr += &(format!("\n   {}",self.paddle0_id.unwrap()));
1536    }
1537    if self.paddle1_id.is_some() {
1538      repr += &(format!("\n   {}",self.paddle1_id.unwrap()));
1539    }
1540    if self.paddle2_id.is_some() { 
1541      repr += &(format!("\n   {}",self.paddle2_id.unwrap()));
1542    }
1543    if self.paddle3_id.is_some() { 
1544      repr += &(format!("\n   {}",self.paddle3_id.unwrap()));
1545    }
1546    if self.paddle4_id.is_some() {
1547      repr += &(format!("\n   {}",self.paddle4_id.unwrap()));
1548    }
1549    if self.paddle5_id.is_some() {
1550      repr += &(format!("\n   {}",self.paddle5_id.unwrap()));
1551    }
1552    if self.paddle6_id.is_some()  {
1553      repr += &(format!("\n   {}",self.paddle6_id.unwrap()));
1554    }
1555    if self.paddle7_id.is_some() {
1556      repr += &(format!("\n   {}",self.paddle7_id.unwrap()));
1557    }
1558    if self.paddle8_id.is_some() {
1559      repr += &(format!("\n   {}",self.paddle8_id.unwrap()));
1560    }
1561    if self.paddle9_id.is_some() {
1562      repr += &(format!("\n   {}",self.paddle9_id.unwrap()));
1563    }
1564    if self.paddle10_id.is_some() {
1565      repr += &(format!("\n   {}",self.paddle10_id.unwrap()));
1566    }
1567    if self.paddle11_id.is_some() {
1568      repr += &(format!("\n   {}",self.paddle11_id.unwrap()));
1569    }
1570    repr += ">";
1571    write!(f, "{}", repr)
1572  }
1573}
1574
1575pub struct Panel {
1576  pub  panel_id    : u8        ,   
1577  pub  description : String    ,   
1578  pub  normal_x    : u8        ,   
1579  pub  normal_y    : u8        ,   
1580  pub  normal_z    : u8        ,   
1581  pub  paddle0  : Paddle,   
1582  pub  paddle1  : Option<Paddle>,   
1583  pub  paddle2  : Option<Paddle>,   
1584  pub  paddle3  : Option<Paddle>,   
1585  pub  paddle4  : Option<Paddle>,   
1586  pub  paddle5  : Option<Paddle>,   
1587  pub  paddle6  : Option<Paddle>,   
1588  pub  paddle7  : Option<Paddle>,   
1589  pub  paddle8  : Option<Paddle>,   
1590  pub  paddle9  : Option<Paddle>,   
1591  pub  paddle10 : Option<Paddle>,   
1592  pub  paddle11 : Option<Paddle>,   
1593  // FIXME - these are for the future 
1594  // when we are buiding the geometry 
1595  // from the database
1596  //pub  dh_paddle   : Option<>,   
1597  //pub  dw_paddle   : Option<>,   
1598}
1599
1600impl Panel {
1601 
1602  pub fn new() -> Self {
1603    Self {
1604      panel_id    : 0        ,   
1605      description : String::from(""),   
1606      normal_x    : 0        ,   
1607      normal_y    : 0        ,   
1608      normal_z    : 0        ,   
1609      paddle0     : Paddle::new(),   
1610      paddle1     : None,   
1611      paddle2     : None,   
1612      paddle3     : None,   
1613      paddle4     : None,   
1614      paddle5     : None,   
1615      paddle6     : None,   
1616      paddle7     : None,   
1617      paddle8     : None,   
1618      paddle9     : None,   
1619      paddle10    : None,   
1620      paddle11    : None,   
1621    }
1622  }
1623
1624
1625  pub fn get_npaddles(&self) -> u8 {
1626    let mut npaddles = 1u8;
1627    if self.paddle1.is_some() {
1628      npaddles += 1;
1629    }
1630    if self.paddle2.is_some() {
1631      npaddles += 1;
1632    }
1633    if self.paddle3.is_some() {
1634      npaddles += 1;
1635    }
1636    if self.paddle4.is_some() {
1637      npaddles += 1;
1638    }
1639    if self.paddle5.is_some() {
1640      npaddles += 1;
1641    }
1642    if self.paddle6.is_some() {
1643      npaddles += 1;
1644    }
1645    if self.paddle7.is_some() {
1646      npaddles += 1;
1647    }
1648    if self.paddle8.is_some() {
1649      npaddles += 1;
1650    }
1651    if self.paddle9.is_some() {
1652      npaddles += 1;
1653    }
1654    if self.paddle10.is_some() {
1655      npaddles += 1;
1656    }
1657    if self.paddle11.is_some() {
1658      npaddles += 1;
1659    }
1660    npaddles
1661  }
1662  
1663  pub fn all(conn: &mut SqliteConnection) -> Option<Vec<Panel>> {
1664    use schema::tof_db_panel::dsl::*;
1665    let db_panels : Vec<DBPanel>;
1666    match tof_db_panel
1667        //.inner_join(tof_db_localtriggerboard.on(schema::tof_db_paddle::dsl::paddle_id.eq(schema::tof_db_localtriggerboard::dsl::paddle1_id)))
1668        .load::<DBPanel>(conn) {
1669      Err(err) => {
1670        error!("Unable to load Panels from db! {err}");
1671        return None;
1672      }
1673      Ok(pnls) => {
1674        db_panels = pnls;
1675      }
1676    }
1677    let paddles_op = Paddle::all(conn);
1678    match paddles_op {
1679      None => {
1680        return None;
1681      }
1682      Some(_) => ()
1683    }
1684    let paddles = paddles_op.unwrap();
1685    // This is not the best and fastest, but since our diesel skills 
1686    // are a merely 3, we can't do it right now.
1687    let mut panels = Vec::<Panel>::new();
1688    println!("Iterating over {} panels in the DB!", db_panels.len());
1689    for dbpanel in db_panels {
1690      let mut pnl  = Panel::new();
1691      for pdl in paddles.iter() {
1692        // this call ensures that the following unwraps
1693        // go through
1694        if !dbpanel.valid() {
1695          error!("Got unpopulated Panel from DB for Panel {}", dbpanel);
1696          continue;
1697        }
1698        if pdl.paddle_id == dbpanel.paddle0_id.unwrap() {
1699          pnl.panel_id     = dbpanel.panel_id as u8;        
1700          pnl.description  = dbpanel.description.clone();
1701          pnl.normal_x     = dbpanel.normal_x as u8;     
1702          pnl.normal_y     = dbpanel.normal_y as u8;     
1703          pnl.normal_z     = dbpanel.normal_z as u8;    
1704          pnl.paddle0      = pdl.clone();
1705        }
1706        if pdl.paddle_id == dbpanel.paddle1_id.unwrap() {
1707          pnl.paddle1   = Some(pdl.clone());
1708        }
1709        if pdl.paddle_id == dbpanel.paddle2_id.unwrap() {
1710          pnl.paddle2   = Some(pdl.clone());
1711        }
1712        if pdl.paddle_id == dbpanel.paddle3_id.unwrap() {
1713          pnl.paddle3   = Some(pdl.clone());
1714        }
1715        if pdl.paddle_id == dbpanel.paddle4_id.unwrap() {
1716          pnl.paddle4   = Some(pdl.clone());
1717        }
1718        if pdl.paddle_id == dbpanel.paddle5_id.unwrap() {
1719          pnl.paddle5   = Some(pdl.clone());
1720        }
1721        if pdl.paddle_id == dbpanel.paddle6_id.unwrap() {
1722          pnl.paddle6   = Some(pdl.clone());
1723        }
1724        if pdl.paddle_id == dbpanel.paddle7_id.unwrap() {
1725          pnl.paddle7   = Some(pdl.clone());
1726        }
1727        if pdl.paddle_id == dbpanel.paddle8_id.unwrap() {
1728          pnl.paddle8   = Some(pdl.clone());
1729        }
1730        if pdl.paddle_id == dbpanel.paddle9_id.unwrap() {
1731          pnl.paddle9   = Some(pdl.clone());
1732        }
1733        if pdl.paddle_id == dbpanel.paddle10_id.unwrap() {
1734          pnl.paddle10   = Some(pdl.clone());
1735        }
1736        if pdl.paddle_id == dbpanel.paddle11_id.unwrap() {
1737          pnl.paddle11   = Some(pdl.clone());
1738        }
1739      }
1740      panels.push(pnl);
1741    }
1742    Some(panels)
1743  }
1744}
1745
1746impl fmt::Display for Panel {
1747  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1748    let mut repr = String::from("<Panel");
1749    repr += &(format!("\n  id    : {}",self.panel_id));
1750    repr += &(format!("\n  descr : {}",self.description));
1751    repr += "\n  orientation:";
1752    repr += &(format!("\n   [{},{},{}]", self.normal_x, self.normal_y, self.normal_z));
1753    repr += &(format!("\n  paddle list ({}) paddles)", self.get_npaddles()));
1754    repr += &(format!("\n   {}",self.paddle0));
1755    if self.paddle1.is_some() {
1756      repr += &(format!("\n   {}",self.paddle1.as_ref().unwrap()));
1757    }
1758    if self.paddle2.is_some() { 
1759      repr += &(format!("\n   {}",self.paddle2.as_ref().unwrap()));
1760    }
1761    if self.paddle3.is_some() { 
1762      repr += &(format!("\n   {}",self.paddle3.as_ref().unwrap()));
1763    }
1764    if self.paddle4.is_some() {
1765      repr += &(format!("\n   {}",self.paddle4.as_ref().unwrap()));
1766    }
1767    if self.paddle5.is_some() {
1768      repr += &(format!("\n   {}",self.paddle5.as_ref().unwrap()));
1769    }
1770    if self.paddle6.is_some()  {
1771      repr += &(format!("\n   {}",self.paddle6.as_ref().unwrap()));
1772    }
1773    if self.paddle7.is_some() {
1774      repr += &(format!("\n   {}",self.paddle7.as_ref().unwrap()));
1775    }
1776    if self.paddle8.is_some() {
1777      repr += &(format!("\n   {}",self.paddle8.as_ref().unwrap()));
1778    }
1779    if self.paddle9.is_some() {
1780      repr += &(format!("\n   {}",self.paddle9.as_ref().unwrap()));
1781    }
1782    if self.paddle10.is_some() {
1783      repr += &(format!("\n   {}",self.paddle10.as_ref().unwrap()));
1784    }
1785    if self.paddle11.is_some() {
1786      repr += &(format!("\n   {}",self.paddle11.as_ref().unwrap()));
1787    }
1788    repr += ">";
1789    write!(f, "{}", repr)
1790  }
1791}
1792
1793
1794
1795