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  /// Extract trackerhits across the frame. 
202  ///
203  /// Sources for tracker hits can either be the regular 
204  /// TelemetryEvents, or all the different TrackerHits. 
205  ///
206  ///
207  // FIXME - this needs to return references with lifetimes, not 
208  // cloning the hits
209  pub fn get_tracker_hitseries(&self, source : TrackerHitSource, name : Option<String>) 
210    -> Result<Vec<TrackerHit>, SerializationError> {
211    let mut hits = Vec::<TrackerHit>::new();
212    let mut tracker_hit_name = String::from("Tracker");
213    if name.is_some() {
214      tracker_hit_name = name.unwrap();
215    }
216    match source {
217      TrackerHitSource::TelemetryEvent |
218      TrackerHitSource::Unknown => {
219      }
220      TrackerHitSource::TrackerPacket => {
221        for k in self.index.keys() {
222          if k.contains(&tracker_hit_name) {
223            let pack = self.get::<TelemetryPacket>(k)?;
224            let trk  = TrackerDAQEventPacket::from_telemetrypacket(&pack)?;
225            for ev in trk.events {
226              for h in ev.hits {
227                hits.push(h.clone());
228              }
229            }
230          }
231        }
232      }
233    }
234    Ok(hits) 
235  }
236
237  pub fn get_telemetrypacket_gcutime_range(&self) -> Option<(f64,f64,f64)> {
238    let mut times = Vec::<f64>::new();
239    for k in self.index.keys() {
240      let f_obj_idx = self.index.get(k).unwrap();
241      if f_obj_idx.1 == CRFrameObjectType::TelemetryPacket { 
242        // offset of 8 for serialzied CRFrameObject 
243        let ts = TelemetryPacket::get_gcutime_unpacked(&self.bytestorage[f_obj_idx.0 as usize + 8..].to_vec()); 
244        if ts.is_ok() {
245          times.push(ts.unwrap());
246        }
247      }
248    }
249    if times.len() > 0 {
250      let min_time = times.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
251      let max_time = times.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
252      let delta    = max_time - min_time;
253      return Some((*min_time,*max_time,delta));
254    }
255    None
256  }
257
258  pub fn serialize_index(&self) -> Vec<u8> {
259    let mut s_index  = Vec::<u8>::new();
260    // more than 255 frame items are not supported
261    let idx_size = self.index.len() as u8;
262    s_index.push(idx_size);
263    for k in &self.index {
264      let mut s_name  = Self::string_to_bytes(k.0.clone());
265      let s_pos   = k.1.0.to_le_bytes();
266      s_index.append(&mut s_name);
267      s_index.extend_from_slice(&s_pos);
268      s_index.push(k.1.1 as u8);
269    }
270    s_index
271  }
272
273  ///// Get the timestamp from the actual telemetry packet in the frame
274  //pub fn get_timestamp(&self) -> u64 {
275  //  todo!("Needs to be implemented!");
276  //  return 0
277  //}
278
279  pub fn string_to_bytes(value : String) -> Vec<u8> {
280    let mut stream  = Vec::<u8>::new();
281    let mut payload = value.into_bytes();
282    let string_size = payload.len() as u16; // limit size
283    stream.extend_from_slice(&string_size.to_le_bytes());
284    stream.append(&mut payload);
285    stream
286  }
287
288  pub fn parse_index(stream : &Vec<u8>, pos : &mut usize) -> HashMap<String, (u64, CRFrameObjectType)> {
289    let idx_size = parse_u8(stream, pos);
290    //println!("Found index of size {idx_size}");
291    let mut index    = HashMap::<String, (u64, CRFrameObjectType)>::new();
292    for _ in 0..idx_size as usize {
293      let name    = parse_string(stream, pos);
294      let obj_pos = parse_u64(stream, pos);
295      let obj_t   = CRFrameObjectType::from(stream[*pos]);
296      *pos += 1;
297      //println!("-- {} {} {}", name, obj_pos, obj_t);
298      index.insert(name.to_owned(), (obj_pos, obj_t));
299    }
300    index
301  }
302
303  /// Delete a CRFrameObject by this name from the frame
304  ///
305  /// To delete multiple objects, delete calls can be 
306  /// chained
307  /// 
308  /// # Arguments:
309  ///   * name : The name of the FrameObject to delte 
310  ///            (must be in index)
311  ///
312  /// # Returns:
313  ///   A complete copy of self, without the given object.
314  pub fn delete(&self, name : &str) -> Result<CRFrame, SerializationError> {
315    if !self.has(name) {
316      error!("There is no object with name {} in this frame!", name);
317      return Err(SerializationError::ObjectNotFound);
318    }
319    let mut new_frame = CRFrame::new();
320    for objname in self.index.keys() {
321      if objname == name {
322        continue;
323      }
324      let obj = self.get_fobject(&objname)?;
325      new_frame.put_fobject(obj, objname);
326    }
327    new_frame.tof_paddles = Arc::clone(&self.tof_paddles);
328    new_frame.trk_strips  = Arc::clone(&self.trk_strips);
329    Ok(new_frame)
330  }
331
332
333  /// Store any eligible object in the frame
334  ///
335  /// Eligible object must implement the "Frameable" trait
336  pub fn put<T: Serialization + Frameable>(&mut self, object : T, name : &str) {
337    let f_object = object.pack();
338    self.put_fobject(f_object, name);
339  }
340
341  fn put_fobject(&mut self, object : CRFrameObject, name : &str) {
342    let pos    = self.bytestorage.len() as u64;
343    self.index.insert(name.to_string(), (pos, object.ftype));
344    let mut stream = object.to_bytestream();
345    //self.put_stream(&mut stream, name);
346    //let pos    = self.bytestorage.len();
347    //self.index.insert(name, pos);
348    self.bytestorage.append(&mut stream);
349  }
350
351  /// Check if the frame contains an object with the given name
352  ///
353  /// # Arguments:
354  ///   * name : The name of the object as it appears in the index
355  pub fn has(&self, name : &str) -> bool {
356    self.index.contains_key(name)
357  }
358  
359  /// A list of TelemetryEvents (fka MergedEvent) in the frame
360  pub fn get_telemetry_event_names(&self) -> Vec<&str> {
361    let mut tevents = Vec::<&str>::new();
362    for k in MERGED_EVENT_TYPES {
363      if self.has(k) {
364        tevents.push(k);
365      }
366    }
367    tevents
368  }
369
370  //pub fn put_stream(&mut self, stream : &mut Vec<u8>, name : String) {
371  //  let pos    = self.bytestorage.len();
372  //  self.index.insert(name, pos);
373  //  self.bytestorage.append(stream);
374  //}
375
376  pub fn get_fobject(&self, name : &str) -> Result<CRFrameObject, SerializationError> {
377    let mut pos    : usize;
378    match self.index.get(name) {
379      None => {
380        error!("There is no object with name {} in this frame!", name);
381        return Err(SerializationError::ObjectNotFound);
382      }
383      Some(meta)  => {
384        //lookup = meta;
385        pos   = meta.0 as usize;
386      }
387    }
388    let cr_object = CRFrameObject::from_bytestream(&self.bytestorage, &mut pos)?;
389    Ok(cr_object)
390  }
391
392  /// A check if the frame contains anything which is a telemetrypacket. 
393  /// This can be used to check if we can get the time of the telemetrypacket 
394  /// directly
395  pub fn get_first_gcutime(&self) -> Option<f64> {
396    let times = self.get_telemetrypacket_gcutime_range()?; 
397    Some(times.0)
398  }
399
400  pub fn get<T : Serialization + Frameable>(&self, name : &str) -> Result<T, SerializationError> {
401    
402    //let mut lookup : (usize, CRFrameObjectType);
403    let mut pos    : usize;
404    match self.index.get(name) {
405      None => {
406        return Err(SerializationError::ValueNotFound);
407      }
408      Some(meta)  => {
409        //lookup = meta;
410        pos   = meta.0 as usize;
411      }
412    }
413    let cr_object = CRFrameObject::from_bytestream(&self.bytestorage, &mut pos)?;
414    let result    = cr_object.extract::<T>()?;
415    Ok(result)
416  }
417
418  /// A verbose display of the frame content
419  pub fn show_frame(&self) -> String {
420    let mut repr = String::from("");
421    for k in &self.index {
422      repr += &(format!("\n -- {}@{}:{} --", k.0, k.1.0, k.1.1));
423      //match k.1.1 {
424      //  CRFrameObjectType::TelemetryPacket => {
425      //    repr += &(format!("\n -- -- {}", self.get<TelemetryPacket>
426      //  }
427      //  CRFrameObjectType::TofPacket => {
428      //  }
429      //}
430    }
431    repr 
432  }
433}
434
435impl Default for CRFrame {
436  fn default() -> Self {
437    Self::new()
438  }
439}
440
441impl Serialization for CRFrame {
442  /// Decode a serializable from a bytestream  
443  fn from_bytestream(stream : &Vec<u8>, 
444                     pos    : &mut usize)
445    -> Result<Self, SerializationError> {
446    if stream.len() < 2 {
447      return Err(SerializationError::HeadInvalid {});
448    }
449    let head = parse_u16(stream, pos);
450    if Self::HEAD != head {
451      error!("FrameObject does not start with HEAD signature");
452      return Err(SerializationError::HeadInvalid {});
453    }
454    let fr_size   = parse_u64(stream, pos) as usize; 
455    *pos += fr_size as usize;
456    let tail = parse_u16(stream, pos);
457    if Self::TAIL != tail {
458      error!("FrameObject does not end with TAIL signature");
459      return Err(SerializationError::TailInvalid {});
460    }
461    *pos -= fr_size - 2; // wind back
462    let mut frame = CRFrame::new();
463    let size    = parse_u64(stream, pos) as usize;
464    frame.index = Self::parse_index(stream, pos);
465    frame.bytestorage = stream[*pos..*pos + size].to_vec();
466    Ok(frame)
467  }
468  
469  /// Encode a serializable to a bytestream  
470  fn to_bytestream(&self) -> Vec<u8> {
471    let mut stream  = Vec::<u8>::new();
472    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
473    let mut s_index = self.serialize_index();
474    //let idx_size    = s_index.len() as u64;
475    let size = self.bytestorage.len() as u64 + s_index.len() as u64;
476    //println!("Will store frame with {size} bytes!");
477    stream.extend_from_slice(&size.to_le_bytes());
478    stream.append(&mut s_index);
479    stream.extend_from_slice(&self.bytestorage.as_slice());
480    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
481    stream
482  }
483}
484
485impl fmt::Display for CRFrame {
486  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
487    let mut repr = String::from("<CRFrame : ");
488    repr += &self.show_frame();
489    repr += "\n>";
490    write!(f, "{}", repr)
491  }
492}
493
494//---------------------------------------------------
495
496#[cfg(feature="pybindings")]
497#[pymethods]
498impl CRFrame {
499  
500  /// Delete a CRFrameObject by this name from the frame
501  ///
502  /// To delete multiple objects, delete calls can be 
503  /// chained
504  /// 
505  /// # Arguments:
506  ///   * name : The name of the FrameObject to delte 
507  ///            (must be in index)
508  ///
509  /// # Returns:
510  ///   A complete copy of self, without the given object.
511  #[pyo3(name="delete")]
512  fn delete_py(&self, name : &str) -> PyResult<Self> {
513    if !self.has(name) {
514      let msg = format!("Frame does not contain {}", name);
515      return Err(PyKeyError::new_err(msg));
516    }
517    match self.delete(name) {
518      Ok(new_frame) => {
519        Ok(new_frame)
520      }
521      Err(err) => {
522        return Err(PyValueError::new_err(err.to_string()));
523      }
524    }
525  }
526  
527  #[getter]
528  /// A list of TelemetryEvents (fka MergedEvent) in the frame
529  fn telemetry_event_names(&self) -> Vec<&str> {
530    self.get_telemetry_event_names() 
531  }
532  
533  /// In case the frame contains at least one telemetrypacket, 
534  /// return the smallest of all gcutimes from all packets in the 
535  /// frame. 
536  ///
537  /// If there is no telemetrypacket in the frame, return None
538  #[pyo3(name="get_first_gcutime")]
539  fn get_first_gcutime_py(&self) -> Option<f64> {
540    self.get_first_gcutime()
541  }
542 
543  #[getter] 
544  fn get_n_objects(&self) -> usize {
545    self.index.len()
546  }
547
548  #[pyo3(name="get_telemetrypacket_gcutime_range")]
549  fn get_telemetrypacket_gcutime_range_py(&self) -> Option<(f64,f64,f64)> {
550    self.get_telemetrypacket_gcutime_range()
551  }
552
553
554  /// Add a TelemetryPacket to the frame. 
555  ///
556  /// # Arguments:
557  ///   name : The name under which we store the TelemetryPacket within the index.
558  ///          If None given, use the default name, which is
559  ///          "TelemetryPacketType.<ValueOf(TelemetryPacketType)". This should be used in 
560  ///          all cases for which there is only a single TelemetryPacket within 
561  ///          the frame.
562  #[pyo3(signature = (packet, name = None, record_timestamp = false))]
563  fn put_telemetrypacket(&mut self, packet : TelemetryPacket, name : Option<&str>, record_timestamp : bool) -> PyResult<()> {
564    if record_timestamp { 
565      self.timestamp = Some(packet.header.get_gcutime());
566    }
567    if let Some(p_name) = name {
568      if self.has(p_name) {
569        let msg = format!("Frame already contains a TelemetryPacket named {}", p_name);
570        return Err(PyValueError::new_err(msg));
571      }
572      self.put(packet, p_name);
573      Ok(())
574    } else {
575      let name = format!("TelemetryPacketType.{}", packet.header.packet_type.as_ref());
576      let msg = format!("Frame already contains a TelemetryPacket named {}", name);
577      if self.has(&name) {
578        return Err(PyValueError::new_err(msg));
579      }
580      self.put(packet, name.as_str());
581      Ok(())
582    }
583  }
584  
585  /// Add a TofPacket to the frame. 
586  ///
587  /// # Arguments:
588  ///   name : The name under which we store the TofPacket within the index.
589  ///          If None given, use the default name, which is
590  ///          "TofPacketType.<ValueOf(TofPacketType)". This should be used in 
591  ///          all cases for which there is only a single TofPacket within 
592  ///          the frame.
593  #[pyo3(signature = (packet, name = None))]
594  fn put_tofpacket(&mut self, packet : TofPacket, name : Option<&str>) -> PyResult<()> {
595    if let Some(p_name) = name {
596      if self.has(p_name) {
597        let msg = format!("Frame already contains a TofPacket named {}", p_name);
598        return Err(PyValueError::new_err(msg));
599      }
600      self.put(packet, p_name);
601      Ok(())
602    } else {
603      let name = format!("TofPacketType.{}", packet.packet_type.as_ref());
604      let msg = format!("Frame already contains a TofPacket named {}", name);
605      if self.has(&name) {
606        return Err(PyValueError::new_err(msg));
607      }
608      self.put(packet, name.as_str());
609      Ok(())
610    }
611  }
612 
613  /// Retrieve a TofPacket from a frame
614  ///
615  /// # Arguments:
616  ///   * name : The name of the packet as it is stored in the 
617  ///            index
618  fn get_tofpacket(&mut self, name : &str) -> PyResult<TofPacket> {
619    let packet    = self.get::<TofPacket>(name).unwrap();
620    Ok(packet)
621  }
622  
623  /// Retrieve a TelemetryPacket from a frame
624  ///
625  /// # Arguments:
626  ///   * name : The name of the packet as it is stored in the 
627  ///            index
628  //#[pyo3(signature = (name = None))]
629  fn get_telemetrypacket(&mut self, name : &str) -> PyResult<TelemetryPacket> {
630    let packet    = self.get::<TelemetryPacket>(name).unwrap();
631    Ok(packet)
632  }
633
634  fn get_mctree(&mut self, name : &str) -> PyResult<McEvent> {
635    let event = self.get::<McEvent>(name).unwrap();
636    Ok(event)
637  }
638
639  /// Get a tofevent from the frame directly
640  fn get_tofevent(&mut self, name : &str) -> PyResult<TofEvent> {
641    let packet    = self.get::<TofPacket>(name).unwrap();
642    let mut event = packet.unpack::<TofEvent>().unwrap();
643    event.set_paddles(&self.tof_paddles);
644    //event.set_paddles(&self.paddles);
645    //py_event.event  = event;
646    Ok(event)
647  }
648
649  /// Get a TelemetryEvent ("MergedEvent") from the frame
650  ///
651  /// This automatically unpacks the event 
652  ///
653  /// # Arugments:
654  ///   * name           : in case there are multiple telemetry events in the same 
655  ///                      frame, choose the one to return by name. In case there 
656  ///                      are multiple events and no name is given, a ValueError 
657  ///                      is raised. In case the one with the given name does not exist,
658  ///                      a ValueError is raised as well.
659  ///   * always_exclude : never return the name given to always_exclude. This can be useful 
660  ///                      in case there are multiple events and no name is given.
661  ///
662  /// # Returns:
663  ///   TelemetryEvent -> if a name is given and the corresponding packe
664  ///                     packet is found OR no name is given and the 
665  ///                     frame contains any TelemetryEvent
666  ///   None           -> if no name is given, but the frame does not
667  ///                     contain any TelemetryEvent
668  #[pyo3(signature = (name = None, always_exclude = None))]
669  fn get_telemetryevent(&mut self, name : Option<&str>, always_exclude : Option<Vec<String>>) -> PyResult<Option<TelemetryEvent>> {
670    let name_ : &str;
671    match name {
672      None => {
673        let mut names = self.get_telemetry_event_names();
674        if let Some(to_exclude) = always_exclude {
675          let exclusion_set: HashSet<_> = to_exclude.into_iter().collect();
676          names.retain(|&x| !exclusion_set.contains(x));
677        }
678        if names.len() != 1 {
679          let msg = format!("Frame contains multiple or no TelemetryEvents {:?}. Please specify a name!", names);
680          return Err(PyValueError::new_err(msg)); 
681        } else {
682          name_ = names[0];
683        }
684      }
685      Some(n_) => {
686        name_ = n_;
687      }
688    }
689    // FIXME - better error catching
690    let packet    = self.get::<TelemetryPacket>(name_).unwrap();
691    match packet.unpack::<TelemetryEvent>() {
692      Err(err) => {
693        return Err(PyValueError::new_err(err.to_string())); 
694      }
695      Ok(mut event) => {
696        event.header  = packet.header;  
697        event.hydrate(&self.tof_paddles, &self.trk_strips);
698        if self.do_trk_calib {
699        //  event.mask_strips(&self.trk_masks);
700        //  event.calibrate_tracker(self.subtract_trk_cmn, 
701        //                          &self.trk_ped,
702        //                          &self.trk_tf,
703        //                          &self.trk_cmn);
704        error!("Tracker calibration not implemented!");
705        }
706        Ok(Some(event))
707      }
708    }
709  }
710
711  /// Check if the frame contains an object with the given name
712  ///
713  /// # Arguments:
714  ///   * name : The name of the object as it appears in the index
715  #[pyo3(name="has")]
716  fn has_py(&self, name : &str) -> bool {
717    self.has(name)
718  }
719  
720  #[getter]
721  fn index(&self) -> HashMap<String, (u64, CRFrameObjectType)> {
722    self.index.clone()
723  }
724
725  #[getter]
726  #[pyo3(name="do_trk_calib")]
727  fn do_trk_calib_py(&self) -> bool {
728    self.do_trk_calib
729  }
730
731  #[pyo3(name="get_tracker_hitseries")]
732  #[pyo3(signature = (name = None))]
733  fn get_tracker_hitseries_py(&self, name : Option<String>) -> Vec<TrackerHit> {
734    self.get_tracker_hitseries(TrackerHitSource::TrackerPacket, name).unwrap()
735  }
736
737}
738
739
740
741#[cfg(feature="pybindings")]
742pythonize!(CRFrame);