Skip to main content

gondola_core/database/
tracker_mask.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
20
21/// Masking of unusable strips as curated by the tracker team 
22#[derive(Debug,PartialEq, Clone,Queryable, Selectable, serde::Serialize, serde::Deserialize)]
23#[diesel(table_name = schema::tof_db_trackerstripmask)]
24#[diesel(primary_key(data_id))]
25#[allow(non_snake_case)]
26#[cfg_attr(feature="pybindings", pyclass)]
27pub struct TrackerStripMask {
28  pub data_id             : i32,
29  pub strip_id            : i32,    
30  pub volume_id           : i64,    
31  pub utc_timestamp_start : i64,
32  pub utc_timestamp_stop  : i64,
33  pub name                : Option<String>, 
34  pub active              : bool,   
35}
36
37impl TrackerStripMask {
38
39  pub fn new() -> Self {
40    Self {
41      data_id             : 0,
42      strip_id            : 0,    
43      volume_id           : 0,    
44      utc_timestamp_start : 0,  
45      utc_timestamp_stop  : 0,
46      name                : None, 
47      active              : true
48    }
49  }
50 
51  pub fn all_names() -> Result<Vec<String>, ConnectionError> {
52    let mut conn = connect_to_db()?;
53    let mut names = Vec::<String>::new();
54    let unique_names =
55      schema::tof_db_trackerstripmask::table.select(
56      schema::tof_db_trackerstripmask::name)
57      .distinct()
58      .load::<Option<String>>(&mut conn).expect("Error getting names from db!");
59    for k in unique_names {
60      if let Some(n) = k {
61        names.push(n);
62      }
63    }
64    Ok(names)
65  }
66
67  /// Get Tracker strip mask 
68  ///
69  /// # Returns:
70  ///   * HashMap<u32 [strip id], TrackerStripMask> 
71  pub fn as_dict_by_name(fname : &str) -> Result<HashMap<u32,Self>, ConnectionError> {
72    use schema::tof_db_trackerstripmask::dsl::*;
73    let mut strips = HashMap::<u32, Self>::new();
74    if fname == "" {
75      match Self::all() {
76        None => {
77          error!("Unable to retrive ANY TrackerStripMask");
78          return Ok(strips);
79        }
80        Some(_strips) => {
81          for k in _strips {
82            strips.insert(k.strip_id as u32, k);
83          }
84          return Ok(strips);
85        }
86      }
87    }
88    let mut conn = connect_to_db()?;
89    match tof_db_trackerstripmask.filter(
90      schema::tof_db_trackerstripmask::name.eq(fname)).load::<Self>(&mut conn) {
91      Err(err) => {
92        error!("We can't find any tracker strip masks in the database! {err}");
93        return Ok(strips);
94      }
95      Ok(masks_) => {
96        for s in masks_ {
97          strips.insert(s.strip_id as u32, s );
98        }
99      }
100    }
101    return Ok(strips);
102  }
103
104  /// Get all tracker strip mask from the database
105  ///
106  /// # Returns:
107  ///   * HashMap<u32 [strip id], TrackeStripMask> 
108  pub fn all() -> Option<Vec<Self>> {
109    use schema::tof_db_trackerstripmask::dsl::*;
110    let mut conn = connect_to_db().ok()?;
111    match tof_db_trackerstripmask.load::<Self>(&mut conn) {
112      Err(err) => {
113        error!("Unable to load tracker strips from db! {err}");
114        return None;
115      }
116      Ok(strips) => {
117        return Some(strips);
118      }
119    }
120  }
121
122  /// Create a channel maks from a pulse file to mask all pulsed channels
123  ///
124  /// This is not the regular mask file, but will mask additional strips which 
125  /// have been re-purposed
126  pub fn parse_from_pulse_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<Self>> {
127    let file = File::open(path)?;
128    let reader = BufReader::new(file);
129    let mut results = Vec::new(); 
130    for line in reader.lines() {
131      let line = line?;
132      let parts: Vec<&str> = line.split_whitespace().collect();
133      if parts.len() == 6 {
134        let layer     = parts[0].parse::<u32>()
135          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
136        let row       = parts[1].parse::<u32>()
137          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
138        let module    = parts[2].parse::<u32>()
139          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
140        let channel   = parts[3].parse::<u32>()
141          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
142        let pulse_chn = parts[4].parse::<i32>()
143          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
144        let pulse_avg = parts[5].parse::<f32>()
145          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
146        results.push((layer, row, module, channel, pulse_chn, pulse_avg));
147      }
148    }
149    let mut masks = Vec::<Self>::new();
150    let hid_vid_map = get_hid_vid_maps().unwrap().1;
151    for k in results {
152      let layer     = k.0; 
153      let row       = k.1;
154      let module    = k.2;
155      let channel   = k.3;
156      let p_channel = k.4;
157      // discard for this - we don't need to know 
158      // this when just creating the mask
159      //let p_avg     = k.5;
160
161      let mut strip = TrackerStrip::new();
162      strip.module  = module as i32;
163      strip.row     = row    as i32;
164      strip.layer   = layer  as i32;
165      let mut active = false;
166      if p_channel < 0 || p_channel as u32 != channel {
167        active = true;
168      }
169      let mut mask  = TrackerStripMask::new();
170      mask.strip_id  = strip.get_stripid() as i32; 
171      mask.volume_id = *hid_vid_map.get(&(mask.strip_id as u32)).unwrap() as i64; // critical error is good here,
172      mask.active    = active;
173      masks.push(mask);
174    }
175    Ok(masks)
176  }
177
178  pub fn parse_from_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<Self>> {
179    let file = File::open(path)?;
180    let reader = BufReader::new(file);
181    let mut results = Vec::new(); 
182    for line in reader.lines() {
183      let line = line?;
184      let parts: Vec<&str> = line.split_whitespace().collect();
185      if parts.len() == 2 {
186        // Parse the first number as a standard decimal
187        let index = parts[0].parse::<u32>()
188          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
189  
190        // Parse the second number as Hex. 
191        // We strip the "0x" prefix before parsing.
192        let hex_str = parts[1].trim_start_matches("0x");
193        let value = u32::from_str_radix(hex_str, 16)
194          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
195  
196        results.push((index, value));
197      }
198    }
199    let mut masks = Vec::<Self>::new();
200    for k in results {
201      let layer     = (k.0 / 100) % 10; 
202      let row       = (k.0 / 10) % 10;  
203      let module    = k.0 % 10;         
204      let active_ch = k.1;
205
206      let mut strip = TrackerStrip::new();
207      strip.module  = module as i32;
208      strip.row     = row    as i32;
209      strip.layer   = layer  as i32;
210      for n in 0..32 {
211        let active    = ( active_ch >> n) & 0x1;
212        strip.channel = n;
213        let mut mask  = TrackerStripMask::new();
214        mask.strip_id = strip.get_stripid() as i32; 
215        mask.active  = active > 0;
216        masks.push(mask);
217      }
218    }
219    Ok(masks)
220  }
221}
222
223impl Default for TrackerStripMask {
224  fn default() -> Self {
225    Self::new()
226  }
227}
228
229impl fmt::Display for TrackerStripMask {
230  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231    let mut repr = format!("<TrackerStripMask [{}]:", self.strip_id);
232    repr += &(format!("\n   vid           : {}", self.volume_id));
233    repr += "\n   UTC Timestamps (Begin/End):";
234    repr += &(format!("\n   {}/{}", self.utc_timestamp_start, self.utc_timestamp_stop));    
235    if self.name.is_some() {
236      repr += &(format!("\n   name        : {}", self.name.clone().unwrap())); 
237    }
238    repr += &(format!("\n   active        : {}", self.active));   
239    write!(f, "{}", repr)
240  }
241}
242
243#[cfg(feature="pybindings")]
244#[pymethods]
245impl TrackerStripMask {
246  
247  #[staticmethod]
248  #[pyo3(name="all")]
249  pub fn all_py() -> Option<Vec<Self>> {
250    Self::all()
251  } 
252  
253  /// Create a channel maks from a pulse file to mask all pulsed channels
254  ///
255  /// This is not the regular mask file, but will mask additional strips which 
256  /// have been re-purposed
257  #[staticmethod]
258  #[pyo3(name="parse_from_pulse_file")]
259  pub fn parse_from_pulse_file_py(fname : &str) -> Option<Vec<Self>> {
260    let masks = Self::parse_from_pulse_file(fname);
261    if masks.is_ok() {
262      return Some(masks.unwrap());
263    } else {
264      error!("An error occured when parsing {}", fname);
265      return None;
266    }
267  }
268
269  #[staticmethod]
270  #[pyo3(name="parse_from_file")]
271  fn parse_from_file_py(fname : &str) -> Option<Vec<Self>> {
272    let masks = Self::parse_from_file(fname);
273    if masks.is_ok() {
274      return Some(masks.unwrap());
275    } else {
276      error!("An error occured when parsing {}", fname);
277      return None;
278    }
279  }
280
281  #[staticmethod]
282  #[pyo3(name="all_names")]
283  /// Get all names for registered datasets. These
284  /// can be used in .as_dict_by_name() to query 
285  /// the db for a set of values
286  pub fn all_names_py() -> Option<Vec<String>> {
287    match Self::all_names() {
288      Err(_) => {
289        return None;
290      }
291      Ok(names) => {
292        return Some(names);
293      }
294    }
295  }
296  
297  #[staticmethod]
298  #[pyo3(name="as_dict_by_name")]
299  pub fn all_as_dict_py(name : &str) -> Option<HashMap<u32,Self>> {
300    match Self::as_dict_by_name(name) {
301      Err(err) => {
302        error!("Unable to retrieve tracker strip mask dictionary. {err}. Did you laod the setup-env.sh shell?");
303        return None;
304      }
305      Ok(_data) => {
306        return Some(_data);
307      }
308    }
309  } 
310  
311  #[getter]
312  fn get_strip_id     (&self) -> i32 {    
313    self.strip_id
314  }
315  
316  #[getter]
317  fn get_volume_id    (&self) -> i64 {    
318    self.volume_id
319  }
320  
321  #[getter]
322  fn get_utc_timestamp_start(&self) -> i64 {
323    self.utc_timestamp_start
324  }
325  
326  #[getter]
327  fn get_utc_timestamp_stop(&self) -> i64 {
328    self.utc_timestamp_stop
329  }
330  
331  #[setter]
332  fn set_utc_timestamp_start(&mut self, value : i64) {
333    self.utc_timestamp_start = value;
334  }
335  
336  #[setter]
337  fn set_utc_timestamp_stop(&mut self, value : i64) {
338    self.utc_timestamp_stop = value;
339  }
340  
341  #[getter]
342  fn get_name    (&self) -> Option<String> {
343    self.name.clone()
344  }
345  
346  #[setter]
347  #[pyo3(name="name")]
348  fn set_name_py(&mut self, value : String) {
349    self.name = Some(value);
350  }
351  
352  #[getter]
353  fn get_active       (&self) -> bool { 
354    self.active
355  }
356}
357
358#[cfg(feature="pybindings")]
359pythonize!(TrackerStripMask);
360
361
362
363/// The db insert companion to TrackerStripMask
364#[derive(Debug,PartialEq, Clone, Insertable)]
365#[diesel(table_name = schema::tof_db_trackerstripmask)]
366#[allow(non_snake_case)]
367#[cfg_attr(feature="pybindings", pyclass)]
368struct NewTrackerStripMask {
369  pub strip_id            : i32,    
370  pub volume_id           : i64,    
371  pub utc_timestamp_start : i64,
372  pub utc_timestamp_stop  : i64,
373  pub name                : Option<String>, 
374  pub active              : bool,   
375}
376
377impl NewTrackerStripMask {
378  pub fn from(mask : &TrackerStripMask) -> Self {
379    Self {
380      strip_id            : mask.strip_id             ,    
381      volume_id           : mask.volume_id            ,    
382      utc_timestamp_start : mask.utc_timestamp_start  ,
383      utc_timestamp_stop  : mask.utc_timestamp_stop   ,
384      name                : mask.name.clone()         , 
385      active              : mask.active               ,   
386    }
387  }
388}
389
390#[cfg_attr(feature="pybindings", pyfunction)]
391pub fn create_trk_mask_table( db_path: &str, masks: Vec<TrackerStripMask>) { 
392  use schema::tof_db_trackerstripmask::dsl::*;
393  //use schema::tof_db_trackerstripmask;
394  let mut conn = SqliteConnection::establish(db_path).ok().unwrap();
395  
396  let mut _query_result = diesel::sql_query("
397      CREATE TABLE IF NOT EXISTS tof_db_trackerstripmask (
398          data_id INTEGER PRIMARY KEY AUTOINCREMENT,
399          strip_id INTEGER NOT NULL,
400          volume_id BIGINT NOT NULL,
401          utc_timestamp_start BIGINT NOT NULL,
402          utc_timestamp_stop BIGINT NOT NULL,
403          name TEXT,
404          active BOOLEAN NOT NULL
405      )
406  ").execute(&mut conn);
407  let mut new_masks = Vec::<NewTrackerStripMask>::new();
408  for m in masks {
409    let nm = NewTrackerStripMask::from(&m);
410    new_masks.push(nm);
411  }
412  _query_result = diesel::insert_into(tof_db_trackerstripmask)
413    .values(&new_masks)
414    .execute(&mut conn);
415}
416