Skip to main content

gondola_core/database/
tracker_pulse.rs

1//! Database access & entities for gaps-online-software
2//!
3//! A local .sqlite database is shipped with gaps-online-software,
4//! pre-populated with relevant meta data for the GAPS experiment.
5// This file is part of gaps-online-software and published 
6// under the GPLv3 license
7
8use crate::prelude::*;
9
10use crate::database::schema;
11use diesel::prelude::*;
12
13use std::io::{
14  self,
15  BufRead,
16  BufReader
17};
18
19/// The db insert companion to TrackerStripPulse
20#[derive(Debug,PartialEq, Clone, Insertable)]
21#[diesel(table_name = schema::tof_db_trackerstrippulse)]
22#[allow(non_snake_case)]
23#[cfg_attr(feature="pybindings", pyclass)]
24struct NewTrackerStripPulse {
25  pub strip_id            : i32,    
26  pub volume_id           : i64,    
27  pub utc_timestamp_start : i64,
28  pub utc_timestamp_stop  : i64,
29  pub name                : Option<String>,
30  pub pulse_chn           : i32,
31  pub pulse_avg           : f32,
32  pub pulse_is_mean       : bool,
33}
34
35impl NewTrackerStripPulse {
36  pub fn from(gain : &TrackerStripPulse) -> Self {
37    Self {
38      strip_id            : gain.strip_id            ,    
39      volume_id           : gain.volume_id           ,    
40      utc_timestamp_start : gain.utc_timestamp_start ,
41      utc_timestamp_stop  : gain.utc_timestamp_stop  ,
42      name                : gain.name.clone()        ,
43      pulse_chn           : gain.pulse_chn           ,
44      pulse_avg           : gain.pulse_avg           ,
45      pulse_is_mean       : gain.pulse_is_mean       ,
46    }
47  }
48}
49
50/// Common noise subtraction - pulse channels on the wafers and get the average adc. 
51/// The gain is available as well. Data from Mengjiao's group 
52#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
53#[diesel(table_name = schema::tof_db_trackerstrippulse)]
54#[diesel(primary_key(data_id))]
55#[allow(non_snake_case)]
56#[cfg_attr(feature="pybindings", pyclass)]
57pub struct TrackerStripPulse {   
58  pub data_id              : i32,
59  pub strip_id             : i32,    
60  pub volume_id            : i64,    
61  pub utc_timestamp_start  : i64,    
62  pub utc_timestamp_stop   : i64,    
63  pub name                 : Option<String>, 
64  pub pulse_chn            : i32,
65  pub pulse_avg            : f32,
66  pub pulse_is_mean        : bool,
67} 
68
69impl TrackerStripPulse {
70
71  pub fn new() -> Self {
72    Self {
73      data_id             : 0,
74      strip_id            : 0,    
75      volume_id           : 0,    
76      utc_timestamp_start : 0,   
77      utc_timestamp_stop  : 0,
78      name                : None, 
79      pulse_chn           : 0,
80      pulse_avg           : 0.0,
81      pulse_is_mean       : false,
82    }
83  }
84  
85  pub fn parse_from_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<Self>> {
86    let file = File::open(path)?;
87    let reader = BufReader::new(file);
88    let mut pulses = Vec::<Self>::new();
89    let hid_vid_map = get_hid_vid_maps().unwrap().1;
90    let mut n_entries  = 0u64;
91    let mut mean_pulse = 0.0f64;
92    //let mut mean_avg   = 0.0f64;
93    let mut all_strip_ids : Vec<_> = hid_vid_map.keys().collect();
94    for line in reader.lines() {
95      let line = line?;
96      if line.starts_with("#") || line.starts_with("Layer") || line.starts_with("layer") {
97        continue;
98      }
99      let mut pulse = Self::new();
100      let parts: Vec<&str> = line.split_whitespace().collect();
101      if parts.len() == 6 {
102        // Parse the first number as a standard decimal
103        let layer   = parts[0].parse::<u8>()
104          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
105        let row     = parts[1].parse::<u8>()
106          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
107        let module   = parts[2].parse::<u8>()
108          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
109        let channel  = parts[3].parse::<u8>()
110          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
111        pulse.strip_id  = TrackerStrip::create_stripid(layer, row, module, channel) as i32; 
112        pulse.volume_id = *hid_vid_map.get(&(pulse.strip_id as u32)).unwrap() as i64; // critical error is good here,
113        pulse.pulse_chn = parts[4].parse::<i32>()
114          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
115        pulse.pulse_avg = parts[5].parse::<f32>()
116          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
117        mean_pulse += pulse.pulse_chn as f64;
118        //mean_avg   += pulse.pulse_avg as f64;
119        all_strip_ids.retain(|x| *x != &(pulse.strip_id as u32));
120        pulses.push(pulse); 
121        n_entries += 1;
122      }
123    }
124    mean_pulse /= n_entries as f64;
125    //mean_avg   /= n_entries as f64;
126    // it seems we should create an entry even for the 
127    // strips which are not in the file 
128    //for stripid in all_strip_ids {
129    //  let mut pulse = Self::new(); 
130    //  pulse.strip_id = *stripid as i32;
131    //  pulse.volume_id = *hid_vid_map.get(&(pulse.strip_id as u32)).unwrap() as i64; // critical error is good here,
132    //  pulse.pulse_is_mean = true;
133    //  pulse.pulse_avg = 0.0; // for some reason, don't set it here 
134    //  pulse.pulse_chn  = mean_pulse.floor() as i32;
135    //  pulses.push(pulse);
136    //}
137    Ok(pulses)
138  }
139  
140  pub fn all_names() -> Result<Vec<String>, ConnectionError> {
141    let mut conn = connect_to_db()?;
142    let mut names = Vec::<String>::new();
143    let unique_names =
144      schema::tof_db_trackerstrippulse::table.select(
145      schema::tof_db_trackerstrippulse::name)
146      .distinct()
147      .load::<Option<String>>(&mut conn).expect("Error getting names from db!");
148    for k in unique_names {
149      if let Some(n) = k {
150        names.push(n);
151      }
152    }
153    Ok(names)
154  }
155  
156  /// Get Tracker strip cmn noise data for a certain dataset 
157  ///
158  /// # Returns:
159  ///   * HashMap<u32 [strip id], TrackerStripTransferFn> 
160  pub fn as_dict_by_name(fname : &str) -> Result<HashMap<u32,Self>, ConnectionError> {
161    use schema::tof_db_trackerstrippulse::dsl::*;
162    let mut strips = HashMap::<u32, Self>::new();
163    if fname == "" {
164      match Self::all() {
165        None => {
166          error!("Unable to retrive ANY TrackerStripCMNNoise Data (pulser)");
167          return Ok(strips);
168        }
169        Some(_strips) => {
170          for k in _strips {
171            strips.insert(k.strip_id as u32, k);
172          }
173          return Ok(strips);
174        }
175      }
176    }
177    let mut conn = connect_to_db()?;
178    match tof_db_trackerstrippulse.filter(
179      schema::tof_db_trackerstrippulse::name.eq(fname)).load::<Self>(&mut conn) {
180      Err(err) => {
181        error!("We can't find any tracker strip common noise information with that name in the database! {err}");
182        return Ok(strips);
183      }
184      Ok(peds_) => {
185        for s in peds_ {
186          strips.insert(s.strip_id as u32, s );
187        }
188      }
189    }
190    return Ok(strips);
191  }
192
193  /// Get all tracker strip transfer functions from the database
194  ///
195  /// # Returns:
196  ///   * HashMap<u32 [strip id], TrackeStripTransferFunction> 
197  pub fn all() -> Option<Vec<Self>> {
198    use schema::tof_db_trackerstrippulse::dsl::*;
199    let mut conn = connect_to_db().ok()?;
200    match tof_db_trackerstrippulse.load::<Self>(&mut conn) {
201      Err(err) => {
202        error!("Unable to load tracker transfer functions from db! {err}");
203        return None;
204      }
205      Ok(strips) => {
206        return Some(strips);
207      }
208    }
209  }
210}
211
212impl fmt::Display for TrackerStripPulse {
213  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214    let mut repr = format!("<TrackerStripPulse [{}]:", self.strip_id);
215    repr += &(format!("\n   vid              : {}", self.volume_id));
216    repr += "\n   UTC Timestamps (Begin/End):";
217    repr += &(format!("\n   {}/{}", self.utc_timestamp_start, self.utc_timestamp_stop));    
218    if self.name.is_some() {
219      repr += &(format!("\n   name     : {}", self.name.clone().unwrap())); 
220    }
221    if self.pulse_is_mean {
222      repr += &(String::from("\n -- Pulse is mean value!"));
223    }
224    repr += &(format!("\n   pulse ch : {} pulse avg : {}>", self.pulse_chn, self.pulse_avg));
225    write!(f, "{}", repr)
226  }
227}
228
229#[cfg(feature="pybindings")]
230#[pymethods]
231impl TrackerStripPulse {
232  
233  #[staticmethod]
234  #[pyo3(name="all")]
235  pub fn all_py() -> Option<Vec<Self>> {
236    Self::all()
237  } 
238 
239  #[staticmethod]
240  #[pyo3(name="all_names")]
241  /// Get all names for registered datasets. These
242  /// can be used in .as_dict_by_name() to query 
243  /// the db for a set of values
244  pub fn all_names_py() -> Option<Vec<String>> {
245    match Self::all_names() {
246      Err(_) => {
247        return None;
248      }
249      Ok(names) => {
250        return Some(names);
251      }
252    }
253  }
254
255  #[staticmethod]
256  #[pyo3(name="as_dict_by_name")]
257  pub fn all_as_dict_py(name : &str) -> Option<HashMap<u32,Self>> {
258    match Self::as_dict_by_name(name) {
259      Err(err) => {
260        error!("Unable to retrieve tracker strip gain dictionary. {err}. Did you laod the setup-env.sh shell?");
261        return None;
262      }
263      Ok(_data) => {
264        return Some(_data);
265      }
266    }
267  } 
268  
269  #[getter]
270  fn get_strip_id     (&self) -> i32 {    
271    self.strip_id
272  }
273  
274  #[getter]
275  fn get_volume_id    (&self) -> i64 {    
276    self.volume_id
277  }
278  
279  #[getter]
280  fn get_utc_timestamp_start(&self) -> i64 {
281    self.utc_timestamp_start
282  }
283  
284  #[getter]
285  fn get_utc_timestamp_stop(&self) -> i64 {
286    self.utc_timestamp_stop
287  }
288  
289  #[setter]
290  fn set_utc_timestamp_start(&mut self, value : i64) {
291    self.utc_timestamp_start = value;
292  }
293  
294  #[setter]
295  fn set_utc_timestamp_stop(&mut self, value : i64) {
296    self.utc_timestamp_stop = value;
297  }
298
299  #[getter]
300  fn get_name(&self) -> Option<String> {
301    self.name.clone()
302  }
303  
304  #[setter]
305  fn set_name(&mut self, value : String) {
306    self.name = Some(value);
307  }
308  
309  #[getter]
310  fn get_pulse_chn(&self) -> i32 {
311    self.pulse_chn
312  }
313  
314  #[getter]
315  fn get_pulse_avg(&self) -> f32 {
316    self.pulse_avg
317  } 
318
319  #[getter]
320  fn get_pulse_is_mean(&self) -> bool {
321    self.pulse_is_mean
322  }
323
324  #[staticmethod]
325  #[pyo3(name="parse_from_file")]
326  fn parse_from_file_py(fname : &str) -> Option<Vec<Self>> {
327    let result = Self::parse_from_file(fname);
328    if result.is_ok() {
329      return Some(result.unwrap());
330    } else {
331      error!("An error occured when parsing {} : '{}'", fname, result.unwrap_err());
332      return None;
333    }
334  } 
335}
336
337#[cfg(feature="pybindings")]
338pythonize!(TrackerStripPulse);
339
340#[cfg_attr(feature="pybindings", pyfunction)]
341pub fn create_trk_pulse_table( db_path: &str, pulses: Vec<TrackerStripPulse>) { 
342  use schema::tof_db_trackerstrippulse::dsl::*;
343  let mut conn = SqliteConnection::establish(db_path).ok().unwrap(); 
344  let mut _query_result = diesel::sql_query("
345      CREATE TABLE IF NOT EXISTS tof_db_trackerstrippulse (
346          data_id INTEGER PRIMARY KEY AUTOINCREMENT,
347          strip_id INTEGER NOT NULL,
348          volume_id BIGINT NOT NULL,
349          utc_timestamp_start BIGINT NOT NULL,
350          utc_timestamp_stop BIGINT NOT NULL,
351          name TEXT,
352          pulse_chn INTEGER,
353          pulse_avg FLOAT,
354          pulse_is_mean BOOL
355      )
356  ").execute(&mut conn);
357  let mut new_pulses = Vec::<NewTrackerStripPulse>::new();
358  for p in pulses {
359    let np = NewTrackerStripPulse::from(&p);
360    new_pulses.push(np);
361  }
362  _query_result = diesel::insert_into(tof_db_trackerstrippulse)
363    .values(&new_pulses)
364    .execute(&mut conn);
365}
366
367