caraspace/
writer.rs

1//! Writer for caraspace files. CRFrames can be 
2//! added sequentially to a file
3//!
4
5//use std::fmt;
6use chrono::{
7  DateTime,
8  Utc
9};
10
11use std::path::Path;
12use std::fs::{
13    File,
14    OpenOptions
15};
16use std::io::Write;
17
18use crate::frame::CRFrame;
19use crate::serialization::CRSerializeable;
20
21/// The TimeStamp format for Human readable timestamps
22pub static HUMAN_TIMESTAMP_FORMAT : &str = "%y%m%d_%H%M%S%Z";
23
24/// A standardized name for regular run files saved by
25/// the liftof suite
26///
27/// # Arguments
28///
29/// * run       : run id (identifier)
30/// * subrun    :  subrun id (identifier of file # within
31///                the run
32/// * rb_id     :  in case this should be used on the rb, 
33///                a rb id can be specified as well
34/// * timestamp :  substitute the current time with this timestamp
35///                (or basically any other string) instead.
36pub fn get_runfilename(run : u32, subrun : u64, rb_id : Option<u8>, timestamp : Option<String>) -> String {
37  let ts : String;
38  match timestamp {
39    Some(_ts) => {
40      ts = _ts;
41    }
42    None => {
43      ts = get_utc_timestamp();
44    }
45  }
46  let fname : String;
47  match rb_id {
48    None => {
49      fname = format!("Run{run}_{subrun}.{ts}.gaps");
50    }
51    Some(rbid) => {
52      fname = format!("Run{run}_{subrun}.{ts}.RB{rbid:02}.gaps");
53    }
54  }
55  fname
56}
57
58/// Get a human readable timestamp
59pub fn get_utc_timestamp() -> String {
60  let now: DateTime<Utc> = Utc::now();
61  //let timestamp_str = now.format("%Y_%m_%d-%H_%M_%S").to_string();
62  let timestamp_str = now.format(HUMAN_TIMESTAMP_FORMAT).to_string();
63  timestamp_str
64}
65
66/// Write CRFrames to disk.
67///
68/// Operates sequentially, frames can 
69/// be added one at a time, then will
70/// be synced to disk.
71pub struct CRWriter {
72
73  pub file            : File,
74  /// location to store the file
75  pub file_path       : String,
76  /// The maximum number of packets 
77  /// for a single file. Ater this 
78  /// number is reached, a new 
79  /// file is started.
80  pub pkts_per_file   : usize,
81  /// The maximum number of (Mega)bytes
82  /// per file. After this a new file 
83  /// is started
84  pub mbytes_per_file : usize,
85  pub file_name       : String,
86  pub run_id          : u32,
87  file_id             : usize,
88  /// internal packet counter, number of 
89  /// packets which went through the writer
90  n_packets           : usize,
91  /// internal counter for bytes written in 
92  /// this file
93  file_nbytes_wr      : usize,
94  /// it can also take a timestamp, in case we 
95  /// don't want to use the current time when the 
96  /// file is written
97  pub file_timestamp  : Option<String>,
98}
99
100impl CRWriter {
101
102  /// Instantiate a new PacketWriter 
103  ///
104  /// # Arguments
105  ///
106  /// * file_path       : Path to store the file under
107  /// * run_id          : Run ID for this file (will be written in filename)
108  /// * subrun_id       : Sub-Run ID for this file (will be written in filename. 
109  ///                     If None, a generic "0" will be used
110  /// * timestamp       : The writer will add an automatic timestamp to the current file
111  ///                     based on the current time. This option allows to overwrite 
112  ///                     that behaviour
113  pub fn new(mut file_path : String, run_id : u32, subrun_id : Option<u64>, timestamp : Option<String>) -> Self {
114    //let filename = file_prefix.clone() + "_0.tof.gaps";
115    let file : File;
116    let file_name : String;
117    if !file_path.ends_with("/") {
118      file_path += "/";
119    }
120    let filename : String;
121    if let Some(subrun) = subrun_id {
122      filename = format!("{}{}", file_path, get_runfilename(run_id, subrun, None, timestamp));
123    } else {
124      filename = format!("{}{}", file_path, get_runfilename(run_id, 0, None, timestamp));
125    }
126    let path     = Path::new(&filename); 
127    println!("Writing to file {filename}");
128    file = OpenOptions::new().create(true).append(true).open(path).expect("Unable to open file {filename}");
129    file_name = filename;
130    Self {
131      file,
132      file_path        : file_path,
133      pkts_per_file    : 0,
134      mbytes_per_file  : 420,
135      run_id           : run_id,
136      file_nbytes_wr   : 0,    
137      file_id          : 1,
138      n_packets        : 0,
139      file_name        : file_name,
140      file_timestamp   : None,
141    }
142  }
143
144  pub fn get_file(&self, timestamp : Option<String>) -> File { 
145    let file : File;
146    let filename = format!("{}{}", self.file_path, get_runfilename(self.run_id, self.file_id as u64, None, timestamp));
147    //let filename = self.file_path.clone() + &get_runfilename(runid,self.file_id as u64, None);
148    let path     = Path::new(&filename); 
149    info!("Writing to file {filename}");
150    file = OpenOptions::new().create(true).append(true).open(path).expect("Unable to open file {filename}");
151    file
152  }
153
154  /// Induce serialization to disk for a CRFrame
155  ///
156  ///
157  pub fn add_frame(&mut self, frame : &CRFrame) {
158    let buffer = frame.serialize();
159    self.file_nbytes_wr += buffer.len();
160    match self.file.write_all(buffer.as_slice()) {
161      Err(err) => error!("Writing to file to path {} failed! {}", self.file_path, err),
162      Ok(_)    => ()
163    }
164    self.n_packets += 1;
165    let mut newfile = false;
166    if self.pkts_per_file != 0 {
167      if self.n_packets == self.pkts_per_file {
168        newfile = true;
169        self.n_packets = 0;
170      }
171    } else if self.mbytes_per_file != 0 {
172      // multiply by mebibyte
173      if self.file_nbytes_wr >= self.mbytes_per_file * 1_048_576 {
174        newfile = true;
175        self.file_nbytes_wr = 0;
176      }
177    }
178    if newfile {
179        //let filename = self.file_prefix.clone() + "_" + &self.file_id.to_string() + ".tof.gaps";
180        match self.file.sync_all() {
181          Err(err) => {
182            error!("Unable to sync file to disc! {err}");
183          },
184          Ok(_) => ()
185        }
186        self.file = self.get_file(self.file_timestamp.clone());
187        self.file_id += 1;
188        //let path  = Path::new(&filename);
189        //println!("==> [TOFPACKETWRITER] Will start a new file {}", path.display());
190        //self.file = OpenOptions::new().create(true).append(true).open(path).expect("Unable to open file {filename}");
191        //self.n_packets = 0;
192        //self.file_id += 1;
193      }
194  debug!("CRFrame written!");
195  }
196}
197
198impl Default for CRWriter {
199  fn default() -> Self {
200    CRWriter::new(String::from(""),0,None, None)
201  }
202}
203