1use pyo3::prelude::*;
5use pyo3::types::{PyBytes, PyDict};
6
7use spvirit_codec::epics_decode::{PvaPacket, command_name};
8
9#[pyclass(name = "Packet", module = "spvirit.lowlevel", frozen)]
11pub struct PyPacket {
12 bytes: Vec<u8>,
13 magic: u8,
14 version: u8,
15 command: u8,
16 flags_raw: u8,
17 is_application: bool,
18 is_control: bool,
19 is_segmented: u8,
20 is_client: bool,
21 is_server: bool,
22 is_msb: bool,
23 payload_length: u32,
24}
25
26impl PyPacket {
27 pub(crate) fn raw(&self) -> &[u8] {
28 &self.bytes
29 }
30
31 pub fn from_bytes(bytes: Vec<u8>) -> Self {
32 if bytes.len() >= 8 {
33 let pkt = PvaPacket::new(&bytes);
34 return Self {
35 magic: pkt.header.magic,
36 version: pkt.header.version,
37 command: pkt.header.command,
38 flags_raw: pkt.header.flags.raw,
39 is_application: pkt.header.flags.is_application,
40 is_control: pkt.header.flags.is_control,
41 is_segmented: pkt.header.flags.is_segmented,
42 is_client: pkt.header.flags.is_client,
43 is_server: pkt.header.flags.is_server,
44 is_msb: pkt.header.flags.is_msb,
45 payload_length: pkt.header.payload_length,
46 bytes,
47 };
48 }
49 Self {
50 magic: 0,
51 version: 0,
52 command: 0,
53 flags_raw: 0,
54 is_application: false,
55 is_control: false,
56 is_segmented: 0,
57 is_client: false,
58 is_server: false,
59 is_msb: false,
60 payload_length: 0,
61 bytes,
62 }
63 }
64}
65
66#[pymethods]
67impl PyPacket {
68 #[getter]
70 fn magic(&self) -> u8 {
71 self.magic
72 }
73 #[getter]
75 fn version(&self) -> u8 {
76 self.version
77 }
78 #[getter]
80 fn command(&self) -> u8 {
81 self.command
82 }
83 #[getter]
85 fn command_name(&self) -> &'static str {
86 command_name(self.command)
87 }
88 #[getter]
90 fn flags(&self) -> u8 {
91 self.flags_raw
92 }
93 #[getter]
95 fn is_application(&self) -> bool {
96 self.is_application
97 }
98 #[getter]
100 fn is_control(&self) -> bool {
101 self.is_control
102 }
103 #[getter]
105 fn is_segmented(&self) -> u8 {
106 self.is_segmented
107 }
108 #[getter]
110 fn is_client(&self) -> bool {
111 self.is_client
112 }
113 #[getter]
115 fn is_server(&self) -> bool {
116 self.is_server
117 }
118 #[getter]
120 fn is_msb(&self) -> bool {
121 self.is_msb
122 }
123 #[getter]
125 fn payload_length(&self) -> u32 {
126 self.payload_length
127 }
128
129 #[getter]
131 fn bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
132 PyBytes::new(py, &self.bytes)
133 }
134
135 #[getter]
137 fn payload<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
138 let start = 8.min(self.bytes.len());
139 PyBytes::new(py, &self.bytes[start..])
140 }
141
142 fn __len__(&self) -> usize {
143 self.bytes.len()
144 }
145
146 fn details<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
149 let mut pkt = PvaPacket::new(&self.bytes);
150 let d = PyDict::new(py);
151 if let Some(cmd) = pkt.decode_payload() {
152 crate::codec::fill_command_details(py, &cmd, &d)?;
153 }
154 Ok(d)
155 }
156
157 fn decode<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
160 crate::codec::decode_packet(py, &self.bytes)
161 }
162
163 fn __repr__(&self) -> String {
164 format!(
165 "Packet(command={} ({}), flags=0x{:02x}, payload_length={})",
166 self.command,
167 self.command_name(),
168 self.flags_raw,
169 self.payload_length
170 )
171 }
172}