use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyDict};
use spvirit_codec::epics_decode::{PvaPacket, command_name};
#[pyclass(name = "Packet", module = "spvirit.lowlevel", frozen)]
pub struct PyPacket {
bytes: Vec<u8>,
magic: u8,
version: u8,
command: u8,
flags_raw: u8,
is_application: bool,
is_control: bool,
is_segmented: u8,
is_client: bool,
is_server: bool,
is_msb: bool,
payload_length: u32,
}
impl PyPacket {
pub(crate) fn raw(&self) -> &[u8] {
&self.bytes
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
if bytes.len() >= 8 {
let pkt = PvaPacket::new(&bytes);
return Self {
magic: pkt.header.magic,
version: pkt.header.version,
command: pkt.header.command,
flags_raw: pkt.header.flags.raw,
is_application: pkt.header.flags.is_application,
is_control: pkt.header.flags.is_control,
is_segmented: pkt.header.flags.is_segmented,
is_client: pkt.header.flags.is_client,
is_server: pkt.header.flags.is_server,
is_msb: pkt.header.flags.is_msb,
payload_length: pkt.header.payload_length,
bytes,
};
}
Self {
magic: 0,
version: 0,
command: 0,
flags_raw: 0,
is_application: false,
is_control: false,
is_segmented: 0,
is_client: false,
is_server: false,
is_msb: false,
payload_length: 0,
bytes,
}
}
}
#[pymethods]
impl PyPacket {
#[getter]
fn magic(&self) -> u8 {
self.magic
}
#[getter]
fn version(&self) -> u8 {
self.version
}
#[getter]
fn command(&self) -> u8 {
self.command
}
#[getter]
fn command_name(&self) -> &'static str {
command_name(self.command)
}
#[getter]
fn flags(&self) -> u8 {
self.flags_raw
}
#[getter]
fn is_application(&self) -> bool {
self.is_application
}
#[getter]
fn is_control(&self) -> bool {
self.is_control
}
#[getter]
fn is_segmented(&self) -> u8 {
self.is_segmented
}
#[getter]
fn is_client(&self) -> bool {
self.is_client
}
#[getter]
fn is_server(&self) -> bool {
self.is_server
}
#[getter]
fn is_msb(&self) -> bool {
self.is_msb
}
#[getter]
fn payload_length(&self) -> u32 {
self.payload_length
}
#[getter]
fn bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.bytes)
}
#[getter]
fn payload<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
let start = 8.min(self.bytes.len());
PyBytes::new(py, &self.bytes[start..])
}
fn __len__(&self) -> usize {
self.bytes.len()
}
fn details<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
let mut pkt = PvaPacket::new(&self.bytes);
let d = PyDict::new(py);
if let Some(cmd) = pkt.decode_payload() {
crate::codec::fill_command_details(py, &cmd, &d)?;
}
Ok(d)
}
fn decode<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
crate::codec::decode_packet(py, &self.bytes)
}
fn __repr__(&self) -> String {
format!(
"Packet(command={} ({}), flags=0x{:02x}, payload_length={})",
self.command,
self.command_name(),
self.flags_raw,
self.payload_length
)
}
}