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]
69 fn magic(&self) -> u8 {
70 self.magic
71 }
72 #[getter]
73 fn version(&self) -> u8 {
74 self.version
75 }
76 #[getter]
77 fn command(&self) -> u8 {
78 self.command
79 }
80 #[getter]
81 fn command_name(&self) -> &'static str {
82 command_name(self.command)
83 }
84 #[getter]
85 fn flags(&self) -> u8 {
86 self.flags_raw
87 }
88 #[getter]
89 fn is_application(&self) -> bool {
90 self.is_application
91 }
92 #[getter]
93 fn is_control(&self) -> bool {
94 self.is_control
95 }
96 #[getter]
97 fn is_segmented(&self) -> u8 {
98 self.is_segmented
99 }
100 #[getter]
101 fn is_client(&self) -> bool {
102 self.is_client
103 }
104 #[getter]
105 fn is_server(&self) -> bool {
106 self.is_server
107 }
108 #[getter]
109 fn is_msb(&self) -> bool {
110 self.is_msb
111 }
112 #[getter]
113 fn payload_length(&self) -> u32 {
114 self.payload_length
115 }
116
117 #[getter]
118 fn bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
119 PyBytes::new(py, &self.bytes)
120 }
121
122 #[getter]
123 fn payload<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
124 let start = 8.min(self.bytes.len());
125 PyBytes::new(py, &self.bytes[start..])
126 }
127
128 fn __len__(&self) -> usize {
129 self.bytes.len()
130 }
131
132 fn details<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
135 let mut pkt = PvaPacket::new(&self.bytes);
136 let d = PyDict::new(py);
137 if let Some(cmd) = pkt.decode_payload() {
138 crate::codec::fill_command_details(py, &cmd, &d)?;
139 }
140 Ok(d)
141 }
142
143 fn decode<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
146 crate::codec::decode_packet(py, &self.bytes)
147 }
148
149 fn __repr__(&self) -> String {
150 format!(
151 "Packet(command={} ({}), flags=0x{:02x}, payload_length={})",
152 self.command,
153 self.command_name(),
154 self.flags_raw,
155 self.payload_length
156 )
157 }
158}