Skip to main content

gondola_core/io/caraspace/
writer.rs

1// This file is part of gaps-online-software and published 
2// under the GPLv3 license
3
4use crate::prelude::*;
5
6/// Write CRFrames to disk.
7///
8/// Operates sequentially, frames can 
9/// be added one at a time, then will
10/// be synced to disk.
11#[cfg_attr(feature="pybindings", pyclass)]
12pub struct CRWriter {
13
14  pub file            : File,
15  /// location to store the file
16  pub file_path       : String,
17  /// The maximum number of packets 
18  /// for a single file. Ater this 
19  /// number is reached, a new 
20  /// file is started.
21  pub pkts_per_file   : usize,
22  /// The maximum number of (Mega)bytes
23  /// per file. After this a new file 
24  /// is started
25  pub mbytes_per_file     : usize,
26  /// the name of the current file we are writing to
27  pub file_name           : String,
28  pub run_id              : u32,
29  file_id                 : usize,
30  /// internal packet counter, number of 
31  /// packets which went through the writer
32  n_packets               : usize,
33  /// internal counter for bytes written in 
34  /// this file
35  file_nbytes_wr          : usize,
36  /// it can also take a timestamp, in case we 
37  /// don't want to use the current time when the 
38  /// file is written
39  pub file_timestamp      : Option<String>,
40  /// if writing a new file every gcu seconds, keep 
41  /// the first gcutimestamp 
42  pub first_gcu_timestamp : Option<u64>,
43  /// Write a new file every gcu seconds (from telemetry packet header) 
44  pub file_len_gcu_sec    : Option<u32>,
45}
46
47impl CRWriter {
48
49  /// Instantiate a new PacketWriter 
50  ///
51  /// # Arguments
52  ///
53  /// * file_path        : Path to store the file under
54  /// * run_id           : Run ID for this file (will be written in filename)
55  /// * subrun_id        : Sub-Run ID for this file (will be written in filename. 
56  ///                      If None, a generic "0" will be used
57  /// * timestamp        : The writer will add an automatic timestamp to the current file
58  ///                      based on the current time. This option allows to overwrite 
59  ///                      that behaviour
60  /// * file_len_gcu_sec : If not None, this will use the gcu time from added packets 
61  ///                      as the timestamp in the filename, and will write a new file 
62  ///                      every given number of seconds
63  pub fn new(mut file_path : String, run_id : u32,
64             subrun_id : Option<u64>, timestamp : Option<String>, file_len_gcu_sec : Option<u32>) -> Self {
65    if file_len_gcu_sec.is_some() {
66      if timestamp.is_none() {
67        panic!("If the writer should operate in the mode where it writes a new file every x seconds depending on the delta of gcu times in the packets, it needs the first timestamp given. This needs to be the first timestamp of the packets to be written");
68      }
69    }
70    let file : File;
71    let file_name : String;
72    if !file_path.ends_with("/") {
73      file_path += "/";
74    }
75    let filename : String;
76    let first_timestamp = timestamp.clone();
77    if let Some(subrun) = subrun_id {
78      filename = format!("{}{}", file_path, get_runfilename(run_id, subrun, None, timestamp, false));
79    } else {
80      filename = format!("{}{}", file_path, get_runfilename(run_id, 0, None, timestamp, false));
81    }
82    let path     = Path::new(&filename); 
83    //println!("Writing to file {filename}");
84    file = OpenOptions::new().create(true).append(true).open(path).expect("Unable to open file {filename}");
85    file_name = filename;
86    let mut first_gcu_timestamp = None;
87    if first_timestamp.is_some() {
88      first_gcu_timestamp = get_unix_timestamp(&first_timestamp.unwrap(), None);
89    }
90    Self {
91      file,
92      file_path           : file_path,
93      pkts_per_file       : 0,
94      mbytes_per_file     : 420,
95      run_id              : run_id,
96      file_nbytes_wr      : 0,    
97      file_id             : 1,
98      n_packets           : 0,
99      file_name           : file_name,
100      file_timestamp      : None,
101      first_gcu_timestamp : first_gcu_timestamp,
102      file_len_gcu_sec    : file_len_gcu_sec,
103    }
104  }
105
106  pub fn get_file(&self, timestamp : Option<String>) -> File { 
107    let file : File;
108    let filename = format!("{}{}", self.file_path, get_runfilename(self.run_id, self.file_id as u64, None, timestamp, false));
109    //let filename = self.file_path.clone() + &get_runfilename(runid,self.file_id as u64, None);
110    let path     = Path::new(&filename); 
111    info!("Writing to file {filename}");
112    file = OpenOptions::new().create(true).append(true).open(path).expect("Unable to open file {filename}");
113    file
114  }
115
116  /// Induce serialization to disk for a CRFrame
117  ///
118  ///
119  pub fn add_frame(&mut self, frame : &CRFrame) {
120    let buffer = frame.to_bytestream();
121    self.file_nbytes_wr += buffer.len();
122    match self.file.write_all(buffer.as_slice()) {
123      Err(err) => error!("Writing to file to path {} failed! {}", self.file_path, err),
124      Ok(_)    => ()
125    }
126    self.n_packets += 1;
127    let mut newfile = false;
128    if self.pkts_per_file != 0 {
129      if self.n_packets == self.pkts_per_file {
130        newfile = true;
131        self.n_packets = 0;
132      }
133    } else if self.mbytes_per_file != 0 {
134      // multiply by mebibyte
135      if self.file_nbytes_wr >= self.mbytes_per_file * 1_048_576 {
136        newfile = true;
137        self.file_nbytes_wr = 0;
138      }
139    } else if self.file_len_gcu_sec.is_some() {
140      if frame.timestamp.clone().unwrap() - self.first_gcu_timestamp.unwrap() as f64 > self.file_len_gcu_sec.unwrap() as f64 {
141        newfile = true; 
142        self.file_timestamp = get_utc_timestamp_from_unix(frame.timestamp.clone().unwrap()); 
143        //println!("starting new file with {}", self.file_timestamp.clone().unwrap());
144        self.first_gcu_timestamp = Some(frame.timestamp.unwrap() as u64);
145      }
146    }
147    if newfile {
148        //let filename = self.file_prefix.clone() + "_" + &self.file_id.to_string() + ".tof.gaps";
149        match self.file.sync_all() {
150          Err(err) => {
151            error!("Unable to sync file to disc! {err}");
152          },
153          Ok(_) => ()
154        }
155        self.file = self.get_file(self.file_timestamp.clone());
156        self.file_id += 1;
157        //let path  = Path::new(&filename);
158        //println!("==> [TOFPACKETWRITER] Will start a new file {}", path.display());
159        //self.file = OpenOptions::new().create(true).append(true).open(path).expect("Unable to open file {filename}");
160        //self.n_packets = 0;
161        //self.file_id += 1;
162      }
163  debug!("CRFrame written!");
164  }
165}
166
167impl Default for CRWriter {
168  fn default() -> Self {
169    CRWriter::new(String::from(""),0,None, None, None)
170  }
171}
172
173impl fmt::Display for CRWriter {
174  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175    let mut repr = String::from("<CRWriter:");
176    repr += &(format!("\n  path: {}", self.file_path)); 
177    repr += ">";
178    write!(f, "{}", repr)
179  }
180}
181
182#[cfg(feature="pybindings")]
183#[pymethods]
184impl CRWriter {
185  
186  #[new]
187  #[pyo3(signature = (filename, run_id, subrun_id = None, timestamp = None, file_len_gcu_sec = None))]
188  fn new_py(filename : String, run_id : u32, subrun_id : Option<u64>, timestamp : Option<String>, file_len_gcu_sec : Option<u32>) -> Self {
189    Self::new(filename, run_id, subrun_id, timestamp, file_len_gcu_sec)
190  }
191
192  #[getter] 
193  #[pyo3(name="current_filename")] 
194  fn get_current_filename_py(&self) -> String { 
195    self.file_name.clone()
196  }
197
198  fn set_mbytes_per_file(&mut self, fsize : usize) {
199    self.mbytes_per_file = fsize;
200  }
201
202  fn set_file_timestamp(&mut self, timestamp : String) {
203    self.file_timestamp = Some(timestamp);
204  }
205  
206  #[pyo3(name="add_frame")]
207  fn add_frame_py(&mut self, frame : CRFrame) {
208    self.add_frame(&frame);  
209  }
210}
211
212#[cfg(feature="pybindings")]
213pythonize_display!(CRWriter);
214