gondola_core/packets/
tof_packet.rs

1//! TofPacket provides a wrapper to write objects which implement
2//! TofPackable into files
3// This file is part of gaps-online-software and published 
4// under the GPLv3 license
5
6use crate::prelude::*;
7
8/// Internal Tof communication protocol.
9/// Simple, yet powerful
10///
11/// A TofPacket has the following layout
12/// on disk
13/// HEAD    : u16 = 0xAAAA
14/// TYPE    : u8  = PacketType
15/// SIZE    : u32
16/// PAYLOAD : [u8;6-SIZE]
17/// TAIL    : u16 = 0x5555 
18///
19/// The total packet size is thus 13 + SIZE
20#[derive(Debug, Clone)]
21#[cfg_attr(feature="pybindings", pyclass)]
22pub struct TofPacket {
23  /// Type of the structure encoded in payload
24  pub packet_type        : TofPacketType,
25  /// The bytestream encoded structure
26  pub payload            : Vec<u8>,
27  // fields which won't get serialized
28  /// mark a packet as not eligible to be written to disk
29  pub no_write_to_disk   : bool,
30  /// mark a packet as not eligible to be sent over network 
31  /// FIXME - future extension
32  pub no_send_over_nw    : bool,
33  /// creation_time for the instance
34  // FIXME - do we really need the last 2?
35  pub creation_time      : Instant,
36  pub valid              : bool, // will be always valid, unless invalidated
37  pub tof_paddles        : Arc<HashMap<u8,  TofPaddle>>, 
38}
39
40impl fmt::Display for TofPacket {
41  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42    let p_len = self.payload.len();
43    if p_len < 4 {
44      write!(f, "<TofPacket: type {:?}, payload size {}>", self.packet_type, p_len)
45    } else {
46      write!(f, "<TofPacket: type {:?}, payload [ {} {} {} {} .. {} {} {} {}] of size {} >",
47             self.packet_type,
48             self.payload[0], self.payload[1], self.payload[2], self.payload[3],
49             self.payload[p_len-4], self.payload[p_len-3], self.payload[p_len - 2], self.payload[p_len-1], p_len ) 
50    }
51  }
52}
53
54impl Default for TofPacket {
55  fn default() -> Self {
56    Self::new()
57  }
58}
59
60/// Implement because TofPacket saves the creation time, 
61/// which never will be the same for 2 different instances
62impl PartialEq for TofPacket {
63  fn eq(&self, other: &Self) -> bool {
64    (self.packet_type == other.packet_type)           &&
65    (self.payload == other.payload)                   &&
66    (self.no_write_to_disk == other.no_write_to_disk) &&
67    (self.no_send_over_nw == other.no_send_over_nw)   &&
68    (self.valid == other.valid)
69  }
70}
71
72impl TofPacket {
73
74  pub fn new() -> Self {
75    let creation_time = Instant::now();
76    Self {
77      packet_type      : TofPacketType::Unknown,
78      payload          : Vec::<u8>::new(),
79      no_write_to_disk : false,
80      no_send_over_nw  : false,
81      creation_time    : creation_time,
82      valid            : true,
83      tof_paddles      : Arc::new(HashMap::<u8, TofPaddle>::new()),
84    }
85  }
86
87  /// Generate a bytestream of self for ZMQ, prefixed with 
88  /// BRCT so all RBs will see it
89  pub fn zmq_payload_brdcast(&self) -> Vec<u8> {
90    let mut payload     = String::from("BRCT").into_bytes(); 
91    let mut stream  = self.to_bytestream();
92    payload.append(&mut stream);
93    payload
94  }
95  
96  /// Generate a bytestream of self for ZMQ, prefixed with 
97  /// RBX, to address only a certain board
98  pub fn zmq_payload_rb(&self, rb_id : u8) -> Vec<u8> {
99    let mut payload     = format!("RB{:02}", rb_id).into_bytes(); 
100    let mut stream  = self.to_bytestream();
101    payload.append(&mut stream);
102    payload
103  }
104
105  /// Unpack the TofPacket and return its content
106  pub fn unpack<T>(&self) -> Result<T, SerializationError>
107    where T: TofPackable + Serialization {
108
109    // first check TOF_PACKET_ALT, if that is != UNKNOWN, 
110    // call from_bytestream_alt. If it is UNKNOWN, proceed
111    // "as usual"
112    let mut pos : usize = 0;
113    if T::TOF_PACKET_TYPE_ALT != TofPacketType::Unknown {
114      if T::TOF_PACKET_TYPE_ALT == self.packet_type {
115        let unpacked : T = T::from_bytestream_alt(&self.payload, &mut pos)?;
116        return Ok(unpacked); 
117      } else if T::TOF_PACKET_TYPE == self.packet_type {
118        let unpacked : T = T::from_bytestream(&self.payload, &mut pos)?;
119        return Ok(unpacked); 
120      } else {
121        error!("This packet of type {} is neither for a {} nor a {}  packet!", self.packet_type, T::TOF_PACKET_TYPE, T::TOF_PACKET_TYPE_ALT);
122        return Err(SerializationError::IncorrectPacketType); 
123      }
124    } else { // TOF_PACKET_ALT is UNKNOWN, so we just proceed as usual
125      if T::TOF_PACKET_TYPE != self.packet_type {
126        error!("This bytestream is not for a {} packet!", self.packet_type);
127        return Err(SerializationError::IncorrectPacketType);
128      } else {
129        let unpacked : T = T::from_bytestream(&self.payload, &mut pos)?;
130        Ok(unpacked)
131      }
132    }
133  }
134  
135  pub fn age(&self) -> u64 {
136    self.creation_time.elapsed().as_secs()
137  }
138}
139
140
141impl Serialization for TofPacket {
142  const HEAD : u16 = 0xaaaa;
143  const TAIL : u16 = 0x5555;
144  const SIZE : usize = 0; // FIXME - size/prelude_size 
145
146  fn from_bytestream(stream : &Vec<u8>, pos : &mut usize)
147  -> Result<Self, SerializationError> {
148    if stream.len() < 2 {
149      return Err(SerializationError::StreamTooShort);
150    }
151    let head = parse_u16(stream, pos);
152    if Self::HEAD != head {
153      error!("TofPacket does not start with HEAD signature! {}", Self::HEAD);
154      return Err(SerializationError::HeadInvalid);
155    }
156    let packet_type : TofPacketType;
157    let packet_type_enc = parse_u8(stream, pos);
158    match TofPacketType::try_from(packet_type_enc) {
159      Ok(pt) => packet_type = pt,
160      Err(_) => {
161        error!("Can not decode packet with packet type {}", packet_type_enc);
162        return Err(SerializationError::UnknownPayload);}
163    }
164    let payload_size = parse_u32(stream, pos) as usize;
165    *pos += payload_size; 
166    let tail = parse_u16(stream, pos);
167    if Self::TAIL != tail {
168      error!("Packet does not end with TAIL signature");
169      return Err(SerializationError::TailInvalid);
170    }
171    *pos -= 2; // for tail parsing
172    *pos -= payload_size;
173
174    let mut tp = TofPacket::new();
175    tp.packet_type = packet_type;
176    tp.payload.extend_from_slice(&stream[*pos..*pos+payload_size]);
177    // Fix position marker
178    *pos += 2 + payload_size;
179    Ok(tp) 
180  }
181  
182  fn to_bytestream(&self) 
183    -> Vec<u8> {
184    let mut bytestream = Vec::<u8>::with_capacity(6 + self.payload.len());
185    bytestream.extend_from_slice(&TofPacket::HEAD.to_le_bytes());
186    let p_type = self.packet_type as u8;
187    bytestream.push(p_type);
188    // payload size of 32 bit accomodates up to 4 GB packet
189    // a 16 bit size would only hold 65k, which might be not
190    // good enough if we sent multiple events in a batch in 
191    // the same TofPacket (in case we do that)
192    let payload_len = self.payload.len() as u32;
193    //let foo = &payload_len.to_le_bytes();
194    //debug!("TofPacket binary payload: {foo:?}");
195    bytestream.extend_from_slice(&payload_len.to_le_bytes());
196    bytestream.extend_from_slice(self.payload.as_slice());
197    bytestream.extend_from_slice(&TofPacket::TAIL.to_le_bytes());
198    bytestream
199  }
200}
201
202#[cfg(feature="random")]
203impl FromRandom for TofPacket {
204
205  fn from_random() -> Self {
206    // FIXME - this should be an actual, realistic
207    // distribution
208    let choices = [
209      TofPacketType::TofEvent,
210      TofPacketType::TofEvent,
211      TofPacketType::TofEvent,
212      TofPacketType::RBWaveform,
213      TofPacketType::RBWaveform,
214      TofPacketType::TofEvent,
215      TofPacketType::TofEvent,
216      TofPacketType::TofEvent,
217      TofPacketType::TofEvent,
218      TofPacketType::TofEvent,
219      TofPacketType::TofEvent,
220      TofPacketType::MasterTrigger,
221      TofPacketType::MasterTrigger,
222      TofPacketType::MasterTrigger,
223      TofPacketType::RBMoniData,
224      TofPacketType::PBMoniData,
225      TofPacketType::LTBMoniData,
226      TofPacketType::PAMoniData,
227      TofPacketType::CPUMoniData,
228      TofPacketType::MtbMoniData,
229    ];
230    let mut rng  = rand::rng();
231    let idx = rng.random_range(0..choices.len());
232    let packet_type = choices[idx];
233    match packet_type {
234      TofPacketType::TofEvent => {
235        let te = TofEvent::from_random();
236        return te.pack()
237      }
238      TofPacketType::RBWaveform => {
239        let te = RBWaveform::from_random();
240        return te.pack()
241      }
242      TofPacketType::RBMoniData => {
243        let te = RBMoniData::from_random();
244        return te.pack()
245      }
246      TofPacketType::PAMoniData => {
247        let te = PAMoniData::from_random();
248        return te.pack()
249      }
250      TofPacketType::LTBMoniData => {
251        let te = LTBMoniData::from_random();
252        return te.pack()
253      }
254      TofPacketType::PBMoniData => {
255        let te = PBMoniData::from_random();
256        return te.pack()
257      }
258      TofPacketType::CPUMoniData => {
259        let te = CPUMoniData::from_random();
260        return te.pack()
261      }
262      TofPacketType::MtbMoniData  => {
263        let te = MtbMoniData::from_random();
264        return te.pack()
265      }
266      _ => {
267        let te = TofEvent::from_random();
268        return te.pack()
269      }
270    }
271  }
272}
273
274impl Frameable for TofPacket {
275  const CRFRAMEOBJECT_TYPE : CRFrameObjectType = CRFrameObjectType::TofPacket;
276}
277
278#[cfg(feature="pybindings")]
279#[pymethods]
280impl TofPacket {
281
282  #[getter]
283  fn get_packet_type(&self) -> TofPacketType {
284    self.packet_type
285  }
286
287  fn get_paddle(&self, paddle_id : u8) -> PyResult<TofPaddle> {
288    match self.tof_paddles.get(&paddle_id) {
289      None => {
290        let msg = "TofPacket does not contain a reference to paddle {paddle_id}!";
291        return Err(PyValueError::new_err(msg));
292      }
293      Some(paddle) => {
294        return Ok(paddle.clone());
295      }
296    }
297  }
298
299  // FIXME - trust in te process that it referenceces te input vector and not clones it
300  /// Factory function for TofPackets
301  ///
302  /// # Arguments:
303  ///
304  ///   * stream    : bytes presumably representing
305  ///                 a TofPacket
306  ///   * start_pos : the assumed position of 
307  ///                 HEAD identifier in the
308  ///                 bytestream (start of 
309  ///                 TofPacket)
310  #[staticmethod]
311  #[pyo3(name = "from_bytestream")]
312  fn from_bytestream_py<'_py>(stream : Vec<u8>, start_pos : usize) -> PyResult<Self>{
313    let mut pos = start_pos;  
314    match Self::from_bytestream(&stream, &mut pos) {
315      Ok(tp) => {
316        return Ok(tp);
317      }
318      Err(err) => {
319        let err_msg = format!("Unable to TofPacket from bytestream! {err}");
320        return Err(PyIOError::new_err(err_msg));
321      }
322    }
323  }
324
325  #[pyo3(name="to_bytestream")]
326  fn to_bytestream_py(&self) -> Vec<u8> {
327    self.to_bytestream()
328  }
329
330  #[getter]
331  #[pyo3(name="payload")]
332  fn get_payload_py(&self) -> Vec<u8> {
333    self.payload.clone()
334  }
335
336  #[pyo3(name="unpack")]
337  fn unpack_py(&self,py: Python) -> PyResult<Py<PyAny>> {
338    match self.packet_type {
339      TofPacketType::Unknown               => {
340        let msg = "TofPacket is of type 'Unknown' and thus can't be unpacked!";
341        return Err(PyValueError::new_err(msg));
342      }, 
343      TofPacketType::RBEvent               => {
344        match self.unpack::<RBEvent>() {
345          Ok(data) => {
346            return Ok(Py::new(py, data)?.into_any());
347          }
348          Err(err) => {
349            return Err(PyValueError::new_err(err.to_string()));
350          }
351        }
352      }, 
353      TofPacketType::TofEventDeprecated  => {
354        match self.unpack::<TofEvent>() {
355          Ok(data) => {
356            return Ok(Py::new(py, data)?.into_any());
357          }
358          Err(err) => {
359            return Err(PyValueError::new_err(err.to_string()));
360          }
361        }
362      }, 
363      TofPacketType::RBWaveform               => {
364        match self.unpack::<RBWaveform>() {
365          Ok(data) => {
366            return Ok(Py::new(py, data)?.into_any());
367          }
368          Err(err) => {
369            return Err(PyValueError::new_err(err.to_string()));
370          }
371        }
372      }, 
373      TofPacketType::TofEvent               => {
374        match self.unpack::<TofEvent>() {
375          Ok(data) => {
376            return Ok(Py::new(py, data)?.into_any());
377          }
378          Err(err) => {
379            return Err(PyValueError::new_err(err.to_string()));
380          }
381        }
382      }, 
383      TofPacketType::DataSinkHB               => {
384        match self.unpack::<DataSinkHB>() {
385          Ok(data) => {
386            return Ok(Py::new(py, data)?.into_any());
387          }
388          Err(err) => {
389            return Err(PyValueError::new_err(err.to_string()));
390          }
391        }
392      }, 
393      //TofPacketType::MasterTrigger         => {}, 
394      //TofPacketType::TriggerConfig         => {},
395      //TofPacketType::MasterTriggerHB       => {}, 
396      //TofPacketType::EventBuilderHB        => {},
397      //TofPacketType::RBChannelMaskConfig   => {},
398      //TofPacketType::TofRBConfig           => {},
399      //TofPacketType::AnalysisEngineConfig  => {},
400      //TofPacketType::RBEventHeader         => {},    
401      //TofPacketType::TOFEventBuilderConfig => {},
402      //TofPacketType::DataPublisherConfig   => {},
403      //TofPacketType::TofRunConfig          => {},
404      //TofPacketType::CPUMoniData           => {},
405      //TofPacketType::MtbMoniData           => {},
406      //TofPacketType::RBMoniData            => {},
407      //TofPacketType::PBMoniData            => {},
408      //TofPacketType::LTBMoniData           => {},
409      //TofPacketType::PAMoniData            => {},
410      //TofPacketType::RBEventMemoryView     => {}, 
411      //TofPacketType::RBCalibration         => {},
412      //TofPacketType::TofCommand            => {},
413      //TofPacketType::TofCommandV2          => {},
414      //TofPacketType::TofResponse           => {},
415      //TofPacketType::RBCommand             => {},
416      //TofPacketType::RBPing                => {},
417      //TofPacketType::PreampBiasConfig      => {},
418      //TofPacketType::RunConfig             => {},
419      //TofPacketType::LTBThresholdConfig    => {},
420      //TofPacketType::TofDetectorStatus     => {},
421      //TofPacketType::ConfigBinary          => {},
422      //TofPacketType::LiftofRBBinary        => {},
423      //TofPacketType::LiftofBinaryService   => {},
424      //TofPacketType::LiftofCCBinary        => {},
425      //TofPacketType::RBCalibrationFlightV  => {},
426      //TofPacketType::RBCalibrationFlightT  => {},
427      //TofPacketType::BfswAckPacket         => {},
428      //TofPacketType::MultiPacket           => {},
429      _               => {
430        match self.unpack::<TofEvent>() {
431          Ok(data) => {
432            return Ok(Py::new(py, data)?.into_any());
433          }
434          Err(err) => {
435            return Err(PyValueError::new_err(err.to_string()));
436          }
437        }
438      }, 
439    }
440  }
441}
442
443#[cfg(feature="pybindings")]
444pythonize!(TofPacket);
445