gondola_core/io/caraspace/
frame.rs

1//! Caraapace I/O system
2//!
3// The following file is part of gaps-online-software and published 
4// under the GPLv3 license
5
6use crate::prelude::*;
7
8/// The Caraspace object type determines the 
9/// kind of object we are able to put in 
10/// a frame and ultimate (de)serialize
11#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
12#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter)]
13#[repr(u8)]
14pub enum CRFrameObjectType {
15  Unknown          =  0u8,
16  TofPacket        = 10u8,
17  TelemetryPacket  = 20u8,
18}
19
20expand_and_test_enum!(CRFrameObjectType, test_crframeobjecttype_repr);
21
22//---------------------------------------------------
23
24/// All possible merged event types in the context of 
25/// CRFrame. This can be used to check against if a frame 
26/// contains any kind of merged event
27pub const MERGED_EVENT_TYPES : [&'static str;4] = [
28  "TelemetryPacketType.NoGapsTriggerEvent",
29  "TelemetryPacketType.BoringEvent",
30  "TelemetryPacketType.InterestingEvent",
31  "TelemetryPacketType.NoTofDataEvent"];
32
33/// A registry of all possible names for TelemetryEvents (aka "MergedEvent") 
34/// which can be stored in CRFrames. This provides the keys they are stored 
35/// under
36#[cfg(feature="pybindings")]
37#[pyfunction]
38pub fn get_all_telemetry_event_names() -> [&'static str;4] {
39  MERGED_EVENT_TYPES
40}
41
42//---------------------------------------------------
43
44/// A Caraspace object, that can be stored
45/// within a frame.
46///
47/// _For the connaiseur_: This is basically a 
48/// TofPacket on steroids_
49///
50///
51#[derive(Debug, Clone)]
52#[cfg_attr(feature="pybindings", pyclass)] 
53pub struct CRFrameObject {
54  pub version : u8,
55  pub ftype   : CRFrameObjectType,
56  /// serialized representation of the 
57  /// content object
58  pub payload : Vec<u8>,
59}
60
61impl CRFrameObject {
62  pub fn new() -> Self {
63    Self {
64      version          : 0,
65      ftype            : CRFrameObjectType::Unknown,
66      payload          : Vec::<u8>::new(),
67    }
68  }
69
70  /// Size of the serialized object, including
71  /// header and footer in bytes
72  pub fn size(&self) -> usize {
73    let size = self.payload.len() + 2 + 4; 
74    size
75  }
76
77  /// Unpack the TofPacket and return its content
78  pub fn extract<T>(&self) -> Result<T, SerializationError>
79    where T: Frameable + Serialization {
80    if T::CRFRAMEOBJECT_TYPE != self.ftype {
81      error!("This bytestream is not for a {} packet!", self.ftype);
82      return Err(SerializationError::IncorrectPacketType);
83    }
84    let unpacked : T = T::from_bytestream(&self.payload, &mut 0)?;
85    Ok(unpacked)
86  }
87}
88
89impl Serialization for CRFrameObject {
90  
91  /// Decode a serializable from a bytestream  
92  fn from_bytestream(stream : &Vec<u8>, 
93                     pos    : &mut usize)
94    -> Result<Self, SerializationError>
95    where Self : Sized {
96    if stream.len() < 2 {
97      return Err(SerializationError::HeadInvalid {});
98    }
99    let head = parse_u16(stream, pos);
100    if Self::HEAD != head {
101      error!("Packet does not start with CRHEAD signature");
102      return Err(SerializationError::HeadInvalid {});
103    }
104      let mut f_obj    = CRFrameObject::new();
105      f_obj.version    = parse_u8(stream, pos);
106      let ftype        = parse_u8(stream, pos);
107      f_obj.ftype      = CRFrameObjectType::from(ftype);
108      let payload_size = parse_u32(stream, pos);
109      *pos += payload_size as usize; 
110      let tail = parse_u16(stream, pos);
111      if Self::TAIL != tail {
112        error!("Packet does not end with CRTAIL signature");
113        return Err(SerializationError::TailInvalid {});
114      }
115      *pos -= 2; // for tail parsing
116      *pos -= payload_size as usize;
117      f_obj.payload.extend_from_slice(&stream[*pos..*pos+payload_size as usize]);
118      Ok(f_obj)
119  }
120  
121  /// Encode a serializable to a bytestream  
122  fn to_bytestream(&self) -> Vec<u8> {
123    let mut stream = Vec::<u8>::new();
124    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
125    stream.push(self.version);
126    stream.push(self.ftype as u8);
127    let size = self.payload.len() as u32;
128    stream.extend_from_slice(&size.to_le_bytes());
129    stream.extend_from_slice(&self.payload.as_slice());
130    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
131    stream
132  }
133}
134
135impl fmt::Display for CRFrameObject {
136  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137    let p_len = self.payload.len();
138    write!(f, "<CRFrameObject: type {:?}, payload [ {} {} {} {} .. {} {} {} {}] of size {} >",
139           self.ftype,
140           self.payload[0], self.payload[1], self.payload[2], self.payload[3],
141           self.payload[p_len-4], self.payload[p_len-3], self.payload[p_len - 2], self.payload[p_len-1], p_len ) 
142  }
143}
144
145//---------------------------------------------------
146
147#[cfg(feature="pybindings")]
148pythonize!(CRFrameObject);
149
150//---------------------------------------------------
151
152/// The central data container of the 
153/// caraspace suite. 
154///
155/// A CRFrame can hold multiple CRFrameObjects
156/// and is basically a little sclerite of 
157/// the entire skeleton.
158#[derive(Debug, Clone)]
159#[cfg_attr(feature="pybindings", pyclass)]
160pub struct CRFrame {
161  // the index holds name, position in frame as well as the type of 
162  // object stored in the frame
163  // FIXME - this needs to be HashMap<&str, (u64, CRFrameObjectType)>
164  pub index            : HashMap<String, (u64, CRFrameObjectType)>,
165  pub bytestorage      : Vec<u8>,
166  pub tof_paddles      : Arc<HashMap<u8,  TofPaddle>>, 
167  pub trk_strips       : Arc<HashMap<u32, TrackerStrip>>,
168  pub trk_masks        : Arc<HashMap<u32, TrackerStripMask>>,
169  pub trk_ped          : Arc<HashMap<u32, TrackerStripPedestal>>,
170  pub trk_tf           : Arc<HashMap<u32, TrackerStripTransferFunction>>,
171  pub trk_cmn          : Arc<HashMap<u32, TrackerStripCmnNoise>>, 
172  /// TRK calibration - convert to energy
173  pub do_trk_calib     : bool,
174  /// TRK subtract CMN 
175  pub subtract_trk_cmn : bool,
176}
177
178impl CRFrame {
179  
180  pub fn new() -> Self {
181    Self {
182      index            : HashMap::<String, (u64, CRFrameObjectType)>::new(),
183      bytestorage      : Vec::<u8>::new(),
184      tof_paddles      : Arc::new(HashMap::<u8, TofPaddle>::new()),
185      trk_strips       : Arc::new(HashMap::<u32, TrackerStrip>::new()),
186      trk_masks        : Arc::new(HashMap::<u32, TrackerStripMask>::new()),
187      trk_ped          : Arc::new(HashMap::<u32, TrackerStripPedestal>::new()),
188      trk_tf           : Arc::new(HashMap::<u32, TrackerStripTransferFunction>::new()),
189      trk_cmn          : Arc::new(HashMap::<u32, TrackerStripCmnNoise>::new()), 
190      do_trk_calib     : false,
191      subtract_trk_cmn : false,
192    }
193  }
194
195  pub fn serialize_index(&self) -> Vec<u8> {
196    let mut s_index  = Vec::<u8>::new();
197    // more than 255 frame items are not supported
198    let idx_size = self.index.len() as u8;
199    s_index.push(idx_size);
200    for k in &self.index {
201      let mut s_name  = Self::string_to_bytes(k.0.clone());
202      let s_pos   = k.1.0.to_le_bytes();
203      s_index.append(&mut s_name);
204      s_index.extend_from_slice(&s_pos);
205      s_index.push(k.1.1 as u8);
206    }
207    s_index
208  }
209
210  ///// Get the timestamp from the actual telemetry packet in the frame
211  //pub fn get_timestamp(&self) -> u64 {
212  //  todo!("Needs to be implemented!");
213  //  return 0
214  //}
215
216  fn string_to_bytes(value : String) -> Vec<u8> {
217    let mut stream  = Vec::<u8>::new();
218    let mut payload = value.into_bytes();
219    let string_size = payload.len() as u16; // limit size
220    stream.extend_from_slice(&string_size.to_le_bytes());
221    stream.append(&mut payload);
222    stream
223  }
224
225  pub fn parse_index(stream : &Vec<u8>, pos : &mut usize) -> HashMap<String, (u64, CRFrameObjectType)> {
226    let idx_size = parse_u8(stream, pos);
227    //println!("Found index of size {idx_size}");
228    let mut index    = HashMap::<String, (u64, CRFrameObjectType)>::new();
229    for _ in 0..idx_size as usize {
230      let name    = parse_string(stream, pos);
231      let obj_pos = parse_u64(stream, pos);
232      let obj_t   = CRFrameObjectType::from(stream[*pos]);
233      *pos += 1;
234      //println!("-- {} {} {}", name, obj_pos, obj_t);
235      index.insert(name.to_owned(), (obj_pos, obj_t));
236    }
237    index
238  }
239
240  /// Delete a CRFrameObject by this name from the frame
241  ///
242  /// To delete multiple objects, delete calls can be 
243  /// chained
244  /// 
245  /// # Arguments:
246  ///   * name : The name of the FrameObject to delte 
247  ///            (must be in index)
248  ///
249  /// # Returns:
250  ///   A complete copy of self, without the given object.
251  pub fn delete(&self, name : &str) -> Result<CRFrame, SerializationError> {
252    if !self.has(name) {
253      error!("There is no object with name {} in this frame!", name);
254      return Err(SerializationError::ObjectNotFound);
255    }
256    let mut new_frame = CRFrame::new();
257    for objname in self.index.keys() {
258      if objname == name {
259        continue;
260      }
261      let obj = self.get_fobject(&objname)?;
262      new_frame.put_fobject(obj, objname);
263    }
264    new_frame.tof_paddles = Arc::clone(&self.tof_paddles);
265    new_frame.trk_strips  = Arc::clone(&self.trk_strips);
266    Ok(new_frame)
267  }
268
269
270  /// Store any eligible object in the frame
271  ///
272  /// Eligible object must implement the "Frameable" trait
273  pub fn put<T: Serialization + Frameable>(&mut self, object : T, name : &str) {
274    let f_object = object.pack();
275    self.put_fobject(f_object, name);
276  }
277
278  fn put_fobject(&mut self, object : CRFrameObject, name : &str) {
279    let pos    = self.bytestorage.len() as u64;
280    self.index.insert(name.to_string(), (pos, object.ftype));
281    let mut stream = object.to_bytestream();
282    //self.put_stream(&mut stream, name);
283    //let pos    = self.bytestorage.len();
284    //self.index.insert(name, pos);
285    self.bytestorage.append(&mut stream);
286  }
287
288  /// Check if the frame contains an object with the given name
289  ///
290  /// # Arguments:
291  ///   * name : The name of the object as it appears in the index
292  pub fn has(&self, name : &str) -> bool {
293    self.index.contains_key(name)
294  }
295  
296  /// A list of TelemetryEvents (fka MergedEvent) in the frame
297  pub fn get_telemetry_event_names(&self) -> Vec<&str> {
298    let mut tevents = Vec::<&str>::new();
299    for k in MERGED_EVENT_TYPES {
300      if self.has(k) {
301        tevents.push(k);
302      }
303    }
304    tevents
305  }
306
307  //pub fn put_stream(&mut self, stream : &mut Vec<u8>, name : String) {
308  //  let pos    = self.bytestorage.len();
309  //  self.index.insert(name, pos);
310  //  self.bytestorage.append(stream);
311  //}
312
313  pub fn get_fobject(&self, name : &str) -> Result<CRFrameObject, SerializationError> {
314    let mut pos    : usize;
315    match self.index.get(name) {
316      None => {
317        error!("There is no object with name {} in this frame!", name);
318        return Err(SerializationError::ObjectNotFound);
319      }
320      Some(meta)  => {
321        //lookup = meta;
322        pos   = meta.0 as usize;
323      }
324    }
325    let cr_object = CRFrameObject::from_bytestream(&self.bytestorage, &mut pos)?;
326    Ok(cr_object)
327  }
328
329  pub fn get<T : Serialization + Frameable>(&self, name : &str) -> Result<T, SerializationError> {
330    
331    //let mut lookup : (usize, CRFrameObjectType);
332    let mut pos    : usize;
333    match self.index.get(name) {
334      None => {
335        return Err(SerializationError::ValueNotFound);
336      }
337      Some(meta)  => {
338        //lookup = meta;
339        pos   = meta.0 as usize;
340      }
341    }
342    let cr_object = CRFrameObject::from_bytestream(&self.bytestorage, &mut pos)?;
343    let result    = cr_object.extract::<T>()?;
344    Ok(result)
345  }
346
347  /// A verbose display of the frame content
348  pub fn show_frame(&self) -> String {
349    let mut repr = String::from("");
350    for k in &self.index {
351      repr += &(format!("\n -- {}@{}:{} --", k.0, k.1.0, k.1.1));
352      //match k.1.1 {
353      //  CRFrameObjectType::TelemetryPacket => {
354      //    repr += &(format!("\n -- -- {}", self.get<TelemetryPacket>
355      //  }
356      //  CRFrameObjectType::TofPacket => {
357      //  }
358      //}
359    }
360    repr 
361  }
362}
363
364impl Default for CRFrame {
365  fn default() -> Self {
366    Self::new()
367  }
368}
369
370impl Serialization for CRFrame {
371  /// Decode a serializable from a bytestream  
372  fn from_bytestream(stream : &Vec<u8>, 
373                     pos    : &mut usize)
374    -> Result<Self, SerializationError> {
375    if stream.len() < 2 {
376      return Err(SerializationError::HeadInvalid {});
377    }
378    let head = parse_u16(stream, pos);
379    if Self::HEAD != head {
380      error!("FrameObject does not start with HEAD signature");
381      return Err(SerializationError::HeadInvalid {});
382    }
383    let fr_size   = parse_u64(stream, pos) as usize; 
384    *pos += fr_size as usize;
385    let tail = parse_u16(stream, pos);
386    if Self::TAIL != tail {
387      error!("FrameObject does not end with TAIL signature");
388      return Err(SerializationError::TailInvalid {});
389    }
390    *pos -= fr_size - 2; // wind back
391    let mut frame = CRFrame::new();
392    let size    = parse_u64(stream, pos) as usize;
393    frame.index = Self::parse_index(stream, pos);
394    frame.bytestorage = stream[*pos..*pos + size].to_vec();
395    Ok(frame)
396  }
397  
398  /// Encode a serializable to a bytestream  
399  fn to_bytestream(&self) -> Vec<u8> {
400    let mut stream  = Vec::<u8>::new();
401    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
402    let mut s_index = self.serialize_index();
403    //let idx_size    = s_index.len() as u64;
404    let size = self.bytestorage.len() as u64 + s_index.len() as u64;
405    //println!("Will store frame with {size} bytes!");
406    stream.extend_from_slice(&size.to_le_bytes());
407    stream.append(&mut s_index);
408    stream.extend_from_slice(&self.bytestorage.as_slice());
409    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
410    stream
411  }
412}
413
414impl fmt::Display for CRFrame {
415  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416    let mut repr = String::from("<CRFrame : ");
417    repr += &self.show_frame();
418    repr += "\n>";
419    write!(f, "{}", repr)
420  }
421}
422
423//---------------------------------------------------
424
425#[cfg(feature="pybindings")]
426#[pymethods]
427impl CRFrame {
428  
429  /// Delete a CRFrameObject by this name from the frame
430  ///
431  /// To delete multiple objects, delete calls can be 
432  /// chained
433  /// 
434  /// # Arguments:
435  ///   * name : The name of the FrameObject to delte 
436  ///            (must be in index)
437  ///
438  /// # Returns:
439  ///   A complete copy of self, without the given object.
440  #[pyo3(name="delete")]
441  pub fn delete_py(&self, name : &str) -> PyResult<Self> {
442    if !self.has(name) {
443      let msg = format!("Frame does not contain {}", name);
444      return Err(PyKeyError::new_err(msg));
445    }
446    match self.delete(name) {
447      Ok(new_frame) => {
448        Ok(new_frame)
449      }
450      Err(err) => {
451        return Err(PyValueError::new_err(err.to_string()));
452      }
453    }
454  }
455  
456  #[getter]
457  /// A list of TelemetryEvents (fka MergedEvent) in the frame
458  fn telemetry_event_names(&self) -> Vec<&str> {
459    self.get_telemetry_event_names() 
460  }
461
462  /// Add a TelemetryPacket to the frame. 
463  ///
464  /// # Arguments:
465  ///   name : The name under which we store the TelemetryPacket within the index.
466  ///          If None given, use the default name, which is
467  ///          "TelemetryPacketType.<ValueOf(TelemetryPacketType)". This should be used in 
468  ///          all cases for which there is only a single TelemetryPacket within 
469  ///          the frame.
470  #[pyo3(signature = (packet, name = None))]
471  fn put_telemetrypacket(&mut self, packet : TelemetryPacket, name : Option<&str>) -> PyResult<()> {
472    if let Some(p_name) = name {
473      if self.has(p_name) {
474        let msg = format!("Frame already contains a TelemetryPacket named {}", p_name);
475        return Err(PyValueError::new_err(msg));
476      }
477      self.put(packet, p_name);
478      Ok(())
479    } else {
480      let name = format!("TelemetryPacketType.{}", packet.header.packet_type.as_ref());
481      let msg = format!("Frame already contains a TelemetryPacket named {}", name);
482      if self.has(&name) {
483        return Err(PyValueError::new_err(msg));
484      }
485      self.put(packet, name.as_str());
486      Ok(())
487    }
488  }
489  
490  /// Add a TofPacket to the frame. 
491  ///
492  /// # Arguments:
493  ///   name : The name under which we store the TofPacket within the index.
494  ///          If None given, use the default name, which is
495  ///          "TofPacketType.<ValueOf(TofPacketType)". This should be used in 
496  ///          all cases for which there is only a single TofPacket within 
497  ///          the frame.
498  #[pyo3(signature = (packet, name = None))]
499  fn put_tofpacket(&mut self, packet : TofPacket, name : Option<&str>) -> PyResult<()> {
500    if let Some(p_name) = name {
501      if self.has(p_name) {
502        let msg = format!("Frame already contains a TofPacket named {}", p_name);
503        return Err(PyValueError::new_err(msg));
504      }
505      self.put(packet, p_name);
506      Ok(())
507    } else {
508      let name = format!("TofPacketType.{}", packet.packet_type.as_ref());
509      let msg = format!("Frame already contains a TofPacket named {}", name);
510      if self.has(&name) {
511        return Err(PyValueError::new_err(msg));
512      }
513      self.put(packet, name.as_str());
514      Ok(())
515    }
516  }
517 
518  /// Retrieve a TofPacket from a frame
519  ///
520  /// # Arguments:
521  ///   * name : The name of the packet as it is stored in the 
522  ///            index
523  fn get_tofpacket(&mut self, name : &str) -> PyResult<TofPacket> {
524    let packet    = self.get::<TofPacket>(name).unwrap();
525    Ok(packet)
526  }
527  
528  /// Retrieve a TelemetryPacket from a frame
529  ///
530  /// # Arguments:
531  ///   * name : The name of the packet as it is stored in the 
532  ///            index
533  //#[pyo3(signature = (name = None))]
534  fn get_telemetrypacket(&mut self, name : &str) -> PyResult<TelemetryPacket> {
535    let packet    = self.get::<TelemetryPacket>(name).unwrap();
536    Ok(packet)
537  }
538 
539  /// Get a tofevent from the frame directly
540  fn get_tofevent(&mut self, name : &str) -> PyResult<TofEvent> {
541    let packet    = self.get::<TofPacket>(name).unwrap();
542    let mut event = packet.unpack::<TofEvent>().unwrap();
543    event.set_paddles(&self.tof_paddles);
544    //event.set_paddles(&self.paddles);
545    //py_event.event  = event;
546    Ok(event)
547  }
548
549  /// Get a TelemetryEvent ("MergedEvent") from the frame
550  ///
551  /// This automatically unpacks the event 
552  ///
553  /// # Arugments:
554  ///   * name           : in case there are multiple telemetry events in the same 
555  ///                      frame, choose the one to return by name. In case there 
556  ///                      are multiple events and no name is given, a ValueError 
557  ///                      is raised. In case the one with the given name does not exist,
558  ///                      a ValueError is raised as well.
559  ///   * always_exclude : never return the name given to always_exclude. This can be useful 
560  ///                      in case there are multiple events and no name is given.
561  ///
562  /// # Returns:
563  ///   TelemetryEvent -> if a name is given and the corresponding packe
564  ///                     packet is found OR no name is given and the 
565  ///                     frame contains any TelemetryEvent
566  ///   None           -> if no name is given, but the frame does not
567  ///                     contain any TelemetryEvent
568  #[pyo3(signature = (name = None, always_exclude = None))]
569  fn get_telemetryevent(&mut self, name : Option<&str>, always_exclude : Option<Vec<String>>) -> PyResult<Option<TelemetryEvent>> {
570    let name_ : &str;
571    match name {
572      None => {
573        let mut names = self.get_telemetry_event_names();
574        if let Some(to_exclude) = always_exclude {
575          let exclusion_set: HashSet<_> = to_exclude.into_iter().collect();
576          names.retain(|&x| !exclusion_set.contains(x));
577        }
578        if names.len() != 1 {
579          let msg = format!("Frame contains multiple or no TelemetryEvents {:?}. Please specify a name!", names);
580          return Err(PyValueError::new_err(msg)); 
581        } else {
582          name_ = names[0];
583        }
584      }
585      Some(n_) => {
586        name_ = n_;
587      }
588    }
589    // FIXME - better error catching
590    let packet    = self.get::<TelemetryPacket>(name_).unwrap();
591    match packet.unpack::<TelemetryEvent>() {
592      Err(err) => {
593        return Err(PyValueError::new_err(err.to_string())); 
594      }
595      Ok(mut event) => {
596        event.header  = packet.header;  
597        event.dehydrate(&self.tof_paddles, &self.trk_strips);
598        if self.do_trk_calib {
599          event.mask_strips(&self.trk_masks);
600          event.calibrate_tracker(self.subtract_trk_cmn, 
601                                  &self.trk_ped,
602                                  &self.trk_tf,
603                                  &self.trk_cmn);
604        }
605        Ok(Some(event))
606      }
607    }
608  }
609
610  /// Check if the frame contains an object with the given name
611  ///
612  /// # Arguments:
613  ///   * name : The name of the object as it appears in the index
614  #[pyo3(name="has")]
615  fn has_py(&self, name : &str) -> bool {
616    self.has(name)
617  }
618  
619  #[getter]
620  fn index(&self) -> HashMap<String, (u64, CRFrameObjectType)> {
621    self.index.clone()
622  }
623
624  #[getter]
625  #[pyo3(name="do_trk_calib")]
626  fn do_trk_calib_py(&self) -> bool {
627    self.do_trk_calib
628  }
629}
630
631#[cfg(feature="pybindings")]
632pythonize!(CRFrame);