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