Skip to main content

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