Skip to main content

gondola_core/packets/
telemetry_packet.rs

1//! Wrapper for all telemetry data - original implementation in 
2//! bfsw
3// The following file is part of gaps-online-software and published 
4// under the GPLv3 license
5
6use crate::prelude::*;
7
8/// A wrapper for packets from the telemetry stream
9///
10/// This is very compact and mostly used as an 
11/// intermediary
12#[derive(Debug, Clone, PartialEq)]
13#[cfg_attr(feature = "pybindings", pyclass, pyo3(name="TelemetryPacket"))]
14pub struct TelemetryPacket {
15  pub header       : TelemetryPacketHeader,
16  pub payload      : Vec<u8>,
17  pub tof_paddles  : Arc<HashMap<u8,  TofPaddle>>, 
18  pub trk_strips   : Arc<HashMap<u32, TrackerStrip>>,
19}
20
21#[cfg(feature="pybindings")]
22#[pymethods]
23impl TelemetryPacket {
24
25  #[staticmethod]
26  #[pyo3(name="get_gcutime_unpacked")]
27  // FIXME - this should just be "gcutime" 
28  fn get_gcutime_unpacked_py(stream : Vec<u8>) -> PyResult<f64> {
29    Ok(Self::get_gcutime_unpacked(&stream)?)
30  }
31
32  #[getter] 
33  #[pyo3(name="gcutime")]
34  fn get_gcutime_py(&self) -> f64 {
35    TelemetryPacketHeader::convert_telemetry_header_ts(self.header.timestamp)
36  }
37  
38  /// For a packet of any merged event type, retrieve the event id from 
39  #[getter]
40  #[pyo3(name="event_id")]
41  fn get_eventid_py(&self) -> PyResult<u16> {
42    let evid_opt = self.get_eventid();
43    match evid_opt {
44      Some(evid_res) => {
45        match evid_res { 
46          Ok(evid) => {
47            return Ok(evid);
48          }
49          Err(err) => {
50            return Err(PyValueError::new_err(err.to_string()));
51          }
52        }
53      }
54      None => {
55        return Err(PyValueError::new_err("This packet does not seem to contain a (useful) eventid!")); 
56      }
57    }
58  }
59
60  /// For a packet of any merged event type, retrieve the run id from 
61  /// the TOF part 
62  #[getter]
63  #[pyo3(name="run_id")]
64  fn get_runid_py(&self) -> PyResult<u16> {
65    let runid_opt = self.get_runid();
66    match runid_opt {
67      Some(runid_res) => {
68        match runid_res { 
69          Ok(runid) => {
70            return Ok(runid);
71          }
72          Err(err) => {
73            return Err(PyValueError::new_err(err.to_string()));
74          }
75        }
76      }
77      None => {
78        return Err(PyValueError::new_err("This packet does not seem to contain a (useful) runid!")); 
79      }
80    }
81  }
82
83  /// For a packet of type 80 (tracker standalooe) retrieve the GPS time from 
84  /// the tracker header 
85  #[pyo3(name="get_gpstime_tracker")]
86  fn get_gpstime_tracker_py(&self) -> PyResult<u64> {
87    let gpstime_opt = self.get_gpstime_tracker();
88    match gpstime_opt {
89      Some(gpstime_res) => {
90        match gpstime_res { 
91          Ok(gpstime) => {
92            return Ok(gpstime);
93          }
94          Err(err) => {
95            return Err(PyValueError::new_err(err.to_string()));
96          }
97        }
98      }
99      None => {
100        return Err(PyValueError::new_err("This packet does not seem to contain a GPS time!")); 
101      }
102    }
103  }
104  
105  /// In case this is any type of event packet which has a tof event, we can 
106  /// also get the GPS time (as long as it is assigned) 
107  #[pyo3(name="get_gpstime_tof")]
108  fn get_gpstime_tof_py(&self) -> PyResult<u64> {
109    let gpstime_opt = self.get_gpstime_tof();
110    match gpstime_opt {
111      Some(gpstime_res) => {
112        match gpstime_res { 
113          Ok(gpstime) => {
114            return Ok(gpstime);
115          }
116          Err(err) => {
117            return Err(PyValueError::new_err(err.to_string()));
118          }
119        }
120      }
121      None => {
122        return Err(PyValueError::new_err("This packet does not seem to contain a GPS time!")); 
123      }
124    }
125  }
126
127  /// Get a zero copy view of the payload 
128  /// Might be mostly useful for debugging purposes
129  #[getter]
130  fn payload<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
131    Ok(PyBytes::new(py, &self.payload))
132  }
133
134  #[getter]
135  fn header(&self) -> TelemetryPacketHeader {
136    // clone is fine here, since the packet header 
137    // is pretty small
138    self.header.clone()
139  }
140
141  #[getter]
142  fn packet_type(&self) -> TelemetryPacketType {
143    TelemetryPacketType::from(self.header.packet_type)
144  }
145
146  /// Check if this is either any of the different merged event 
147  /// types 
148  #[getter]
149  #[pyo3(name="is_event_packet")]
150  fn is_event_packet_py(&self) -> bool {
151    self.is_event_packet()
152  }
153
154  /// Check if this packet is a complete run configuration 
155  /// send by the TOF system
156  #[getter] 
157  #[pyo3(name="is_tof_toml_packet")]
158  fn is_tof_toml_packet_py(&self) -> bool {
159    if self.header.packet_type == TelemetryPacketType::AnyTofHK {
160      // check the TOF packet type 
161      // (self.payload contains a TOF packet) 
162      let tof_packet_type = TofPacketType::from(parse_u8(&self.payload, &mut 2));
163      return tof_packet_type == TofPacketType::LiftofSettings;
164    }
165    false
166  }
167
168
169  #[pyo3(name="to_bytestream")]
170  fn to_bytestream_py(&self) -> Vec<u8> {
171    self.to_bytestream()
172  }
173
174  #[staticmethod]
175  #[pyo3(name="from_bytestream")]
176  fn from_bytestream_py(stream : Vec<u8>, pos : usize) -> Result<Self, SerializationError> {
177    let mut pos_ = pos;
178    Self::from_bytestream(&stream, &mut pos_)
179  }
180}
181
182impl TelemetryPacket {
183
184  pub fn new() -> Self {
185    Self {
186      header      : TelemetryPacketHeader::new(),
187      payload     : Vec::<u8>::new(),
188      tof_paddles : Arc::new(HashMap::<u8, TofPaddle>::new()),
189      trk_strips  : Arc::new(HashMap::<u32,TrackerStrip>::new()),
190    }
191  }
192 
193  pub fn is_event_packet(&self) -> bool {
194    if self.header.packet_type == TelemetryPacketType::NoTofDataEvent
195      || self.header.packet_type == TelemetryPacketType::NoGapsTriggerEvent 
196      || self.header.packet_type == TelemetryPacketType::InterestingEvent 
197      || self.header.packet_type == TelemetryPacketType::BoringEvent {
198      true 
199    } else {
200      false
201    }
202  }
203
204  /// Unpack the TelemetryPacket and return its content
205  pub fn unpack<T>(&self) -> Result<T, SerializationError>
206    where T: TelemetryPackable + Serialization {
207    if !T::TEL_PACKET_TYPES_EVENT.contains(&self.header.packet_type) &&
208      T::TEL_PACKET_TYPE != self.header.packet_type {
209      error!("This bytestream is not for a {} packet!", self.header.packet_type);
210      return Err(SerializationError::IncorrectPacketType);
211    }
212    let unpacked : T = T::from_bytestream(&self.payload, &mut 0)?;
213    Ok(unpacked)
214  }
215
216  /// For a packet of any merged event type, retrieve the run id from 
217  /// the TOF part 
218  pub fn get_runid(&self) -> Option<Result<u16, SerializationError>> {
219    if !self.is_event_packet() {
220      return None;
221    }
222    if self.header.packet_type == TelemetryPacketType::NoTofDataEvent {
223      return None;
224    }
225    // the run id is right after the timestamp (6 byte) 
226    // and is 2 bytes long 
227    if self.payload.len() < 37 {
228      return Some(Err(SerializationError::StreamTooShort));
229    }
230    let mut pos   = 41usize;
231    let runid = parse_u16(&self.payload, &mut pos);
232    return Some(Ok(runid));
233  }
234  
235  /// For a packet of any merged event type, retrieve the event id from 
236  /// the TelemetryEvent  
237  pub fn get_eventid(&self) -> Option<Result<u16, SerializationError>> {
238    if !self.is_event_packet() {
239      return None;
240    }
241    // the event id is 10bytes into the actual telemetryevent payload
242    if self.payload.len() < 13 + 10 + 4{
243      return Some(Err(SerializationError::StreamTooShort));
244    }
245    let mut pos   = 10usize;
246    let evid = parse_u16(&self.payload, &mut pos);
247    return Some(Ok(evid));
248  }
249
250  /// For a packet of type 80 (tracker standalooe) retrieve the GPS time from 
251  /// the tracker header 
252  pub fn get_gpstime_tracker(&self) -> Option<Result<u64, SerializationError>> {
253    if self.header.packet_type != TelemetryPacketType::Tracker {
254      return None;
255    }
256    // the tracker header with the gps time is the first 
257    let mut pos = 10usize; // skip the first bytes in the TrackerHeader
258    if self.payload.len() < 16 { 
259      return Some(Err(SerializationError::StreamTooShort));
260    }
261    let lower = parse_u32(&self.payload, &mut pos);
262    let upper = parse_u16(&self.payload, &mut pos);
263    let ts    = make_systime(lower, upper);
264    return Some(Ok(ts));
265  }
266
267  /// In case this is any type of event packet which has a tof event, we can 
268  /// also get the GPS time (as long as it is assigned) 
269  pub fn get_gpstime_tof(&self) -> Option<Result<u64, SerializationError>> {
270    if !self.is_event_packet() {
271      return None;
272    }
273    if self.header.packet_type == TelemetryPacketType::NoTofDataEvent {
274      return None;
275    }
276    // then in the TelemetryEvent 
277    // version (1byte)
278    // flags0  (1byte)
279    // -- only for version 1 supported 
280    // + 8byte
281    // event id (4byte)
282    // tof dl   (1byte)
283    // tof nby  (2byte)
284    // -> 17 byte
285    // ------ TofPacket 2 + 1 + 4 overhead (7byte) 
286    // -> 24 byte 
287    // TofEvent 2 + 1 + 2 + 1 + 4 + 1 
288    // -> 35 byte 
289    if self.payload.len() < 41 {
290      return Some(Err(SerializationError::StreamTooShort));
291    }
292    //let mut pos = 42usize;
293    let mut pos = 35usize;
294    let ts32 = parse_u32(&self.payload, &mut pos);
295    let ts16 = parse_u16(&self.payload, &mut pos);
296    let ts   = 0x273000000000000 | (((ts16 as u64) << 32) | ts32 as u64);
297    return Some(Ok(ts));
298  }
299
300  /// Get the gcutime from a packet without unpacking the full thing
301  pub fn get_gcutime_unpacked(stream : &Vec<u8>) -> Result<f64, SerializationError> {
302    // it starts with the serialized header and the packet byte, 
303    // and then the timestamp is 32 bit. So we need to jump 3 bytes 
304    // and then read 4 
305    if stream.len() < 7 {
306      error!("Can not get gcutime from a bytestream shorter than 7 bytes!");
307      return Err(SerializationError::StreamTooShort);
308    }
309    let mut pos = 3usize;
310    let ts = parse_u32(stream, &mut pos);
311    Ok(TelemetryPacketHeader::convert_telemetry_header_ts(ts))
312  }
313
314} 
315
316impl Serialization for TelemetryPacket {
317
318  /// No "classical" head byte marker
319  const HEAD : u16 = 0;
320  /// No "classical" tail byte marker
321  const TAIL : u16 = 0;
322  /// variable size
323  const SIZE : usize = 0;
324
325  fn from_bytestream(stream : &Vec<u8>, pos : &mut usize) -> Result<Self, SerializationError> {
326    let mut tpacket: TelemetryPacket = TelemetryPacket::new();
327    let header: TelemetryPacketHeader  = TelemetryPacketHeader::from_bytestream(stream, pos)?;
328    tpacket.header = header;
329    // to note here: The payload does not contain the payload for the header, it was just parsed
330    tpacket.payload = stream[*pos..*pos + header.length as usize - TelemetryPacketHeader::SIZE].to_vec();
331    Ok(tpacket)
332  }
333
334  fn to_bytestream(&self) -> Vec<u8> {
335    let mut stream: Vec<u8> = Vec::<u8>::new();
336    let mut s_head = self.header.to_bytestream();
337    stream.append(&mut s_head);
338    stream.extend_from_slice(self.payload.as_slice());
339    stream
340  }
341}
342
343impl Default for TelemetryPacket { 
344  fn default() -> Self {
345    Self::new()
346  }
347}
348
349impl fmt::Display for TelemetryPacket {
350  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351    let mut repr: String = String::from("<TelemetryPacket:");
352    repr += &(format!("\n  Header      : {}",self.header));
353    repr += &(format!("\n  Payload len : {}>",self.payload.len()));
354    write!(f, "{}", repr)
355  }
356}
357
358impl Frameable for TelemetryPacket {
359  const CRFRAMEOBJECT_TYPE : CRFrameObjectType = CRFrameObjectType::TelemetryPacket;
360}
361
362
363#[cfg(feature="pybindings")]
364pythonize!(TelemetryPacket);
365
366