1#![allow(unused_parens)]
2
3use crate::constants::*;
4use crate::error::KMError;
5use bytes::Bytes;
6use modular_bitfield::prelude::*;
7use num_enum::{FromPrimitive, IntoPrimitive};
8
9#[bitfield(bytes = 4)]
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct CtrlHeader {
12 pub packet_type: B7,
13 pub reserved_flag: bool,
16 pub id: u8,
17 #[skip]
18 unused: bool,
19 pub attribute: B15,
20}
21
22#[bitfield(bytes = 4)]
23#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct DataHeader {
25 pub packet_type: B7,
26 pub reserved_flag: bool,
31 pub id: u8,
32 #[skip]
33 unused: B6,
34 pub obj_count_words: B10,
35}
36
37#[bitfield(bytes = 4)]
39#[derive(Debug, Clone, Copy, PartialEq)]
40pub(crate) struct StreamingAuthHeader {
41 pub packet_type: B7,
42 pub reserved_flag: bool,
43 pub id: u8,
44 pub attribute: u16,
45}
46
47#[bitfield(bytes = 4)]
48#[derive(Debug, Clone, Copy, PartialEq)]
49pub struct ExtendedHeader {
50 pub attribute: B15,
51 pub next: bool,
52 pub chunk: B6,
53 pub size: B10,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoPrimitive, FromPrimitive)]
60#[repr(u8)]
61pub enum PacketType {
62 Sync = 0x01,
65 Connect = 0x02,
66 Disconnect = 0x03,
67 Reset = 0x04,
68 Accept = 0x05,
69 Rejected = 0x06,
70 Finished = 0x07,
71 JumpAprom = 0x08,
72 JumpDfu = 0x09,
73 GetStatus = 0x0A,
74 Error = 0x0B,
75 GetData = 0x0C,
76 GetFile = 0x0D,
77 StartGraph = 0x0E, StopGraph = 0x0F, EnablePdMonitor = 0x10, DisablePdMonitor = 0x11, MemoryRead = 0x44, StreamingAuth = 0x4C, NotReadable = 0x27, Head = 64,
91 PutData = 65,
92 #[num_enum(catch_all)]
93 Unknown(u8),
94}
95
96impl PacketType {
97 pub fn is_ctrl_type(&self) -> bool {
98 let value: u8 = (*self).into();
99 value < 0x40
100 }
101}
102
103#[cfg(feature = "python")]
105impl<'py> pyo3::IntoPyObject<'py> for PacketType {
106 type Target = pyo3::PyAny;
107 type Output = pyo3::Bound<'py, Self::Target>;
108 type Error = pyo3::PyErr;
109
110 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, <Self as pyo3::IntoPyObject<'py>>::Error> {
111 let value: u8 = self.into();
113 Ok(value.into_pyobject(py).unwrap().into_any())
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoPrimitive, FromPrimitive)]
121#[repr(u16)]
122pub enum Attribute {
123 None = 0,
124 Adc = 0x1,
125 AdcQueue = 0x2,
126 AdcQueue10k = 0x4,
127 Settings = 0x8,
128 PdPacket = 0x10,
131 PdTrace = 0x20,
133
134 LogMetadata = 0x200,
136
137 #[num_enum(catch_all)]
138 Unknown(u16),
139}
140
141#[cfg(feature = "python")]
143impl<'py> pyo3::IntoPyObject<'py> for Attribute {
144 type Target = pyo3::PyAny;
145 type Output = pyo3::Bound<'py, Self::Target>;
146 type Error = pyo3::PyErr;
147
148 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
149 let value: u16 = self.into();
151 Ok(value.into_pyobject(py).unwrap().into_any())
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158#[cfg_attr(feature = "python", derive(pyo3::IntoPyObject))]
159pub struct AttributeSet {
160 mask: u16,
161}
162
163impl AttributeSet {
164 pub const fn empty() -> Self {
165 Self { mask: 0 }
166 }
167
168 pub const fn from_raw(mask: u16) -> Self {
169 Self { mask }
170 }
171
172 pub fn single(attr: Attribute) -> Self {
173 Self { mask: attr.into() }
174 }
175
176 pub fn from_attributes<I>(attrs: I) -> Self
177 where
178 I: IntoIterator<Item = Attribute>,
179 {
180 Self {
181 mask: attrs
182 .into_iter()
183 .map(|a| {
184 let val: u16 = a.into();
185 val
186 })
187 .fold(0, |acc, x| acc | x),
188 }
189 }
190
191 pub fn with(mut self, attr: Attribute) -> Self {
192 let val: u16 = attr.into();
193 self.mask |= val;
194 self
195 }
196
197 pub fn contains(&self, attr: Attribute) -> bool {
198 let val: u16 = attr.into();
199 self.mask & val != 0
200 }
201
202 pub fn contains_any<I>(&self, attrs: I) -> bool
204 where
205 I: IntoIterator<Item = Attribute>,
206 {
207 attrs.into_iter().any(|a| self.contains(a))
208 }
209
210 pub fn contains_all<I>(&self, attrs: I) -> bool
212 where
213 I: IntoIterator<Item = Attribute>,
214 {
215 attrs.into_iter().all(|a| self.contains(a))
216 }
217
218 pub fn without(mut self, attr: Attribute) -> Self {
220 let val: u16 = attr.into();
221 self.mask &= !val;
222 self
223 }
224
225 pub fn iter(&self) -> impl Iterator<Item = Attribute> + '_ {
227 (0..16).filter_map(move |bit| {
228 let value = 1u16 << bit;
229 if self.mask & value != 0 {
230 Some(Attribute::from_primitive(value))
231 } else {
232 None
233 }
234 })
235 }
236
237 pub fn to_vec(&self) -> Vec<Attribute> {
238 self.iter().collect()
239 }
240
241 pub const fn raw(&self) -> u16 {
242 self.mask
243 }
244
245 pub fn is_empty(&self) -> bool {
246 self.mask == 0
247 }
248
249 pub fn len(&self) -> usize {
250 self.mask.count_ones() as usize
251 }
252}
253
254impl From<Attribute> for AttributeSet {
255 fn from(attr: Attribute) -> Self {
256 Self::single(attr)
257 }
258}
259
260impl FromIterator<Attribute> for AttributeSet {
261 fn from_iter<I: IntoIterator<Item = Attribute>>(iter: I) -> Self {
262 Self::from_attributes(iter)
263 }
264}
265
266#[derive(Debug, Clone, PartialEq)]
270#[cfg_attr(
271 feature = "python",
272 pyo3::pyclass(get_all, skip_from_py_object, name = "LogicalPacket")
273)]
274pub struct LogicalPacket {
275 pub attribute: Attribute,
276 pub next: bool,
277 pub chunk: u8,
278 pub size: u16,
279 pub payload: Vec<u8>,
280}
281
282#[derive(Debug, Clone, PartialEq)]
283pub enum RawPacket {
284 Ctrl {
285 header: CtrlHeader,
286 payload: Vec<u8>,
287 },
288 SimpleData {
289 header: DataHeader,
290 payload: Vec<u8>,
291 },
292 Data {
293 header: DataHeader,
294 logical_packets: Vec<LogicalPacket>,
295 },
296}
297
298impl RawPacket {
299 pub fn id(&self) -> u8 {
300 match self {
301 RawPacket::Ctrl { header, .. } => header.id(),
302 RawPacket::SimpleData { header, .. } => header.id(),
303 RawPacket::Data { header, .. } => header.id(),
304 }
305 }
306
307 pub fn packet_type(&self) -> PacketType {
308 match self {
309 RawPacket::Ctrl { header, .. } => PacketType::from_primitive(header.packet_type()),
310 RawPacket::SimpleData { header, .. } => PacketType::from_primitive(header.packet_type()),
311 RawPacket::Data { header, .. } => PacketType::from_primitive(header.packet_type()),
312 }
313 }
314
315 pub fn get_attribute(&self) -> Option<Attribute> {
317 match self {
318 RawPacket::Ctrl { header, .. } => Some(Attribute::from_primitive(header.attribute())),
319 _ => None,
320 }
321 }
322
323 pub fn get_attribute_set(&self) -> Option<AttributeSet> {
325 match self {
326 RawPacket::Ctrl { header, .. } => Some(AttributeSet::from_raw(header.attribute())),
327 _ => None,
328 }
329 }
330
331 pub fn logical_packets(&self) -> Option<&[LogicalPacket]> {
333 match self {
334 RawPacket::Data { logical_packets, .. } => Some(logical_packets),
335 _ => None,
336 }
337 }
338
339 pub fn validate_correlation(&self, request_mask: u16) -> Result<(), KMError> {
344 match self {
345 RawPacket::Data { logical_packets, .. } => {
346 let request_set = AttributeSet::from_raw(request_mask);
347
348 for lp in logical_packets {
350 if !request_set.contains(lp.attribute) {
351 let expected: Vec<u16> = request_set.iter().map(|a| a.into()).collect();
352 let actual: Vec<u16> = logical_packets.iter().map(|lp| lp.attribute.into()).collect();
353
354 return Err(KMError::AttributeMismatch { expected, actual });
355 }
356 }
357
358 Ok(())
359 }
360 _ => Ok(()), }
362 }
363
364 pub fn is_empty_response(&self) -> bool {
366 matches!(self, RawPacket::Data { logical_packets, .. } if logical_packets.is_empty())
367 }
368}
369
370impl TryFrom<Bytes> for RawPacket {
371 type Error = KMError;
372
373 fn try_from(mut bytes: Bytes) -> Result<Self, Self::Error> {
374 if bytes.len() < MAIN_HEADER_SIZE {
376 return Err(KMError::InvalidPacket(format!(
377 "Packet too short for header: expected {}, got {}",
378 MAIN_HEADER_SIZE,
379 bytes.len()
380 )));
381 }
382
383 let first_byte = bytes[0]; let package_type_byte = first_byte & 0x7F;
387 let is_ctrl_packet = PacketType::from_primitive(package_type_byte).is_ctrl_type();
388
389 let header_bytes: [u8; 4] = bytes
390 .split_to(4) .as_ref()
392 .try_into()
393 .unwrap(); let mut payload = bytes;
395
396 if is_ctrl_packet {
397 let header = CtrlHeader::from_bytes(header_bytes);
398 Ok(RawPacket::Ctrl {
399 header,
400 payload: payload.to_vec(),
401 })
402 } else {
403 let header = DataHeader::from_bytes(header_bytes);
404 let packet_type = PacketType::from_primitive(header.packet_type());
405
406 if packet_type == PacketType::PutData {
408 if payload.is_empty() {
413 return Ok(RawPacket::Data {
414 header,
415 logical_packets: vec![],
416 });
417 }
418
419 if payload.len() < EXTENDED_HEADER_SIZE {
420 return Ok(RawPacket::SimpleData {
424 header,
425 payload: payload.to_vec(),
426 });
427 }
428
429 let mut logical_packets = Vec::new();
431
432 loop {
433 if payload.len() < EXTENDED_HEADER_SIZE {
434 return Err(KMError::InvalidPacket(format!(
435 "Insufficient bytes for extended header: need {}, got {}",
436 EXTENDED_HEADER_SIZE,
437 payload.len()
438 )));
439 }
440
441 let ext_header_bytes = payload.split_to(4);
443 let ext_header_array: [u8; 4] = ext_header_bytes
444 .as_ref()
445 .try_into()
446 .map_err(|_| KMError::InvalidPacket("Failed to extract extended header bytes".to_string()))?;
447 let ext = ExtendedHeader::from_bytes(ext_header_array);
448
449 let has_next = ext.next();
450 let attribute = Attribute::from_primitive(ext.attribute());
451 let payload_size = if attribute == Attribute::PdTrace {
453 crate::pd_trace::payload_size(&payload)?
454 } else {
455 ext.size() as usize
456 * if attribute == Attribute::AdcQueue {
457 ext.chunk() as usize
458 } else {
459 1
460 }
461 };
462
463 let logical_payload = if !has_next && attribute == Attribute::AdcQueue {
467 let all = payload.clone();
469 payload = Bytes::new();
470 all
471 } else {
472 if payload.len() < payload_size {
474 return Err(KMError::InvalidPacket(format!(
475 "Insufficient payload bytes: expected {}, got {}",
476 payload_size,
477 payload.len()
478 )));
479 }
480 let chunk = payload.slice(..payload_size);
481 payload = payload.slice(payload_size..);
482 chunk
483 };
484
485 logical_packets.push(LogicalPacket {
486 attribute,
487 next: has_next,
488 chunk: ext.chunk(),
489 size: ext.size(),
490 payload: logical_payload.to_vec(),
491 });
492
493 if !has_next {
495 break;
496 }
497 }
498
499 if logical_packets.is_empty() {
500 return Err(KMError::InvalidPacket(
501 "PutData packet must have at least one logical packet".to_string(),
502 ));
503 }
504
505 Ok(RawPacket::Data {
506 header,
507 logical_packets,
508 })
509 } else {
510 Ok(RawPacket::SimpleData {
511 header,
512 payload: payload.to_vec(),
513 })
514 }
515 }
516 }
517}
518
519#[cfg(feature = "python")]
521impl<'py> pyo3::IntoPyObject<'py> for RawPacket {
522 type Target = pyo3::PyAny;
523 type Output = pyo3::Bound<'py, Self::Target>;
524 type Error = pyo3::PyErr;
525
526 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
527 use pyo3::types::{PyDict, PyDictMethods, PyListMethods};
528
529 let dict = PyDict::new(py);
530 match self {
531 RawPacket::Ctrl { header, payload } => {
532 let inner = PyDict::new(py);
533 let header_dict = PyDict::new(py);
534 header_dict.set_item("packet_type", header.packet_type())?;
535 header_dict.set_item("reserved_flag", header.reserved_flag())?;
536 header_dict.set_item("id", header.id())?;
537 header_dict.set_item("attribute", header.attribute())?;
538 inner.set_item("header", header_dict)?;
539 inner.set_item("payload", payload)?;
540 dict.set_item("Ctrl", inner)?;
541 }
542 RawPacket::SimpleData { header, payload } => {
543 let inner = PyDict::new(py);
544 let header_dict = PyDict::new(py);
545 header_dict.set_item("packet_type", header.packet_type())?;
546 header_dict.set_item("reserved_flag", header.reserved_flag())?;
547 header_dict.set_item("id", header.id())?;
548 header_dict.set_item("obj_count_words", header.obj_count_words())?;
549 inner.set_item("header", header_dict)?;
550 inner.set_item("payload", payload)?;
551 dict.set_item("SimpleData", inner)?;
552 }
553 RawPacket::Data {
554 header,
555 logical_packets,
556 } => {
557 let inner = PyDict::new(py);
558 let header_dict = PyDict::new(py);
559 header_dict.set_item("packet_type", header.packet_type())?;
560 header_dict.set_item("reserved_flag", header.reserved_flag())?;
561 header_dict.set_item("id", header.id())?;
562 header_dict.set_item("obj_count_words", header.obj_count_words())?;
563 inner.set_item("header", header_dict)?;
564 let lp_list = pyo3::types::PyList::empty(py);
566 for lp in logical_packets {
567 let lp_dict = PyDict::new(py);
568 lp_dict.set_item("attribute", u16::from(lp.attribute))?;
569 lp_dict.set_item("next", lp.next)?;
570 lp_dict.set_item("chunk", lp.chunk)?;
571 lp_dict.set_item("size", lp.size)?;
572 lp_dict.set_item("payload", lp.payload)?;
573 lp_list.append(lp_dict)?;
574 }
575 inner.set_item("logical_packets", lp_list)?;
576 dict.set_item("Data", inner)?;
577 }
578 }
579 Ok(dict.into_any())
580 }
581}
582
583impl From<RawPacket> for Bytes {
584 fn from(packet: RawPacket) -> Self {
585 let (header_bytes, payload) = match packet {
586 RawPacket::Ctrl { header, payload } => (header.into_bytes(), payload),
587 RawPacket::SimpleData { header, payload } => (header.into_bytes(), payload),
588 RawPacket::Data {
589 header,
590 logical_packets,
591 } => {
592 let mut full_payload = Vec::new();
594
595 for logical_packet in logical_packets {
596 let ext = ExtendedHeader::new()
598 .with_attribute(logical_packet.attribute.into())
599 .with_next(logical_packet.next)
600 .with_chunk(logical_packet.chunk)
601 .with_size(logical_packet.size);
602
603 full_payload.extend_from_slice(&ext.into_bytes());
604 full_payload.extend_from_slice(&logical_packet.payload);
605 }
606
607 (header.into_bytes(), full_payload)
608 }
609 };
610
611 let mut message = Vec::with_capacity(4 + payload.len());
613 message.extend_from_slice(&header_bytes);
614 message.extend_from_slice(payload.as_ref());
615
616 Bytes::from(message)
617 }
618}