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)]
62#[repr(u8)]
63pub enum PacketType {
64 Sync = 0x01,
67 Connect = 0x02,
68 Disconnect = 0x03,
69 Reset = 0x04,
70 Accept = 0x05,
71 Rejected = 0x06,
72 Finished = 0x07,
73 JumpAprom = 0x08,
74 JumpDfu = 0x09,
75 GetStatus = 0x0A,
76 Error = 0x0B,
77 GetData = 0x0C,
78 GetFile = 0x0D,
79 StartGraph = 0x0E, StopGraph = 0x0F, EnablePdMonitor = 0x10, DisablePdMonitor = 0x11, MemoryRead = 0x44, StreamingAuth = 0x4C, NotReadable = 0x27, Unknown26 = 26,
93 Unknown58 = 58,
94
95 Head = 64,
97 PutData = 65,
98 #[num_enum(catch_all)]
99 Unknown(u8),
100}
101
102impl PacketType {
103 pub fn is_ctrl_type(&self) -> bool {
104 let value: u8 = (*self).into();
105 value < 0x40
106 }
107}
108
109#[cfg(feature = "python")]
111impl<'py> pyo3::IntoPyObject<'py> for PacketType {
112 type Target = pyo3::PyAny;
113 type Output = pyo3::Bound<'py, Self::Target>;
114 type Error = pyo3::PyErr;
115
116 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, <Self as pyo3::IntoPyObject<'py>>::Error> {
117 let value: u8 = self.into();
119 Ok(value.into_pyobject(py).unwrap().into_any())
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoPrimitive, FromPrimitive)]
129#[repr(u16)]
130pub enum Attribute {
131 None = 0,
132 Adc = 0x1,
133 AdcQueue = 0x2,
134 AdcQueue10k = 0x4,
135 Settings = 0x8,
136 PdPacket = 0x10,
139
140 Unknown512 = 512, Unknown1609 = 1609, Unknown11046 = 11046, Unknown26817 = 26817, #[num_enum(catch_all)]
148 Unknown(u16),
149}
150
151#[cfg(feature = "python")]
153impl<'py> pyo3::IntoPyObject<'py> for Attribute {
154 type Target = pyo3::PyAny;
155 type Output = pyo3::Bound<'py, Self::Target>;
156 type Error = pyo3::PyErr;
157
158 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
159 let value: u16 = self.into();
161 Ok(value.into_pyobject(py).unwrap().into_any())
162 }
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168#[cfg_attr(feature = "python", derive(pyo3::IntoPyObject))]
169pub struct AttributeSet {
170 mask: u16,
171}
172
173impl AttributeSet {
174 pub const fn empty() -> Self {
175 Self { mask: 0 }
176 }
177
178 pub const fn from_raw(mask: u16) -> Self {
179 Self { mask }
180 }
181
182 pub fn single(attr: Attribute) -> Self {
183 Self { mask: attr.into() }
184 }
185
186 pub fn from_attributes<I>(attrs: I) -> Self
187 where
188 I: IntoIterator<Item = Attribute>,
189 {
190 Self {
191 mask: attrs
192 .into_iter()
193 .map(|a| {
194 let val: u16 = a.into();
195 val
196 })
197 .fold(0, |acc, x| acc | x),
198 }
199 }
200
201 pub fn with(mut self, attr: Attribute) -> Self {
202 let val: u16 = attr.into();
203 self.mask |= val;
204 self
205 }
206
207 pub fn contains(&self, attr: Attribute) -> bool {
208 let val: u16 = attr.into();
209 self.mask & val != 0
210 }
211
212 pub fn contains_any<I>(&self, attrs: I) -> bool
214 where
215 I: IntoIterator<Item = Attribute>,
216 {
217 attrs.into_iter().any(|a| self.contains(a))
218 }
219
220 pub fn contains_all<I>(&self, attrs: I) -> bool
222 where
223 I: IntoIterator<Item = Attribute>,
224 {
225 attrs.into_iter().all(|a| self.contains(a))
226 }
227
228 pub fn without(mut self, attr: Attribute) -> Self {
230 let val: u16 = attr.into();
231 self.mask &= !val;
232 self
233 }
234
235 pub fn iter(&self) -> impl Iterator<Item = Attribute> + '_ {
237 (0..16).filter_map(move |bit| {
238 let value = 1u16 << bit;
239 if self.mask & value != 0 {
240 Some(Attribute::from_primitive(value))
241 } else {
242 None
243 }
244 })
245 }
246
247 pub fn to_vec(&self) -> Vec<Attribute> {
248 self.iter().collect()
249 }
250
251 pub const fn raw(&self) -> u16 {
252 self.mask
253 }
254
255 pub fn is_empty(&self) -> bool {
256 self.mask == 0
257 }
258
259 pub fn len(&self) -> usize {
260 self.mask.count_ones() as usize
261 }
262}
263
264impl From<Attribute> for AttributeSet {
265 fn from(attr: Attribute) -> Self {
266 Self::single(attr)
267 }
268}
269
270impl FromIterator<Attribute> for AttributeSet {
271 fn from_iter<I: IntoIterator<Item = Attribute>>(iter: I) -> Self {
272 Self::from_attributes(iter)
273 }
274}
275
276#[derive(Debug, Clone, PartialEq)]
280#[cfg_attr(
281 feature = "python",
282 pyo3::pyclass(get_all, skip_from_py_object, name = "LogicalPacket")
283)]
284pub struct LogicalPacket {
285 pub attribute: Attribute,
286 pub next: bool,
287 pub chunk: u8,
288 pub size: u16,
289 pub payload: Vec<u8>,
290}
291
292#[derive(Debug, Clone, PartialEq)]
293pub enum RawPacket {
294 Ctrl {
295 header: CtrlHeader,
296 payload: Vec<u8>,
297 },
298 SimpleData {
299 header: DataHeader,
300 payload: Vec<u8>,
301 },
302 Data {
303 header: DataHeader,
304 logical_packets: Vec<LogicalPacket>,
305 },
306}
307
308impl RawPacket {
309 pub fn id(&self) -> u8 {
310 match self {
311 RawPacket::Ctrl { header, .. } => header.id(),
312 RawPacket::SimpleData { header, .. } => header.id(),
313 RawPacket::Data { header, .. } => header.id(),
314 }
315 }
316
317 pub fn packet_type(&self) -> PacketType {
318 match self {
319 RawPacket::Ctrl { header, .. } => PacketType::from_primitive(header.packet_type()),
320 RawPacket::SimpleData { header, .. } => PacketType::from_primitive(header.packet_type()),
321 RawPacket::Data { header, .. } => PacketType::from_primitive(header.packet_type()),
322 }
323 }
324
325 pub fn get_attribute(&self) -> Option<Attribute> {
327 match self {
328 RawPacket::Ctrl { header, .. } => Some(Attribute::from_primitive(header.attribute())),
329 _ => None,
330 }
331 }
332
333 pub fn get_attribute_set(&self) -> Option<AttributeSet> {
335 match self {
336 RawPacket::Ctrl { header, .. } => Some(AttributeSet::from_raw(header.attribute())),
337 _ => None,
338 }
339 }
340
341 pub fn logical_packets(&self) -> Option<&[LogicalPacket]> {
343 match self {
344 RawPacket::Data { logical_packets, .. } => Some(logical_packets),
345 _ => None,
346 }
347 }
348
349 pub fn validate_correlation(&self, request_mask: u16) -> Result<(), KMError> {
354 match self {
355 RawPacket::Data { logical_packets, .. } => {
356 let request_set = AttributeSet::from_raw(request_mask);
357
358 for lp in logical_packets {
360 if !request_set.contains(lp.attribute) {
361 let expected: Vec<u16> = request_set.iter().map(|a| a.into()).collect();
362 let actual: Vec<u16> = logical_packets.iter().map(|lp| lp.attribute.into()).collect();
363
364 return Err(KMError::AttributeMismatch { expected, actual });
365 }
366 }
367
368 Ok(())
369 }
370 _ => Ok(()), }
372 }
373
374 pub fn is_empty_response(&self) -> bool {
376 matches!(self, RawPacket::Data { logical_packets, .. } if logical_packets.is_empty())
377 }
378}
379
380impl TryFrom<Bytes> for RawPacket {
381 type Error = KMError;
382
383 fn try_from(mut bytes: Bytes) -> Result<Self, Self::Error> {
384 if bytes.len() < MAIN_HEADER_SIZE {
386 return Err(KMError::InvalidPacket(format!(
387 "Packet too short for header: expected {}, got {}",
388 MAIN_HEADER_SIZE,
389 bytes.len()
390 )));
391 }
392
393 let first_byte = bytes[0]; let package_type_byte = first_byte & 0x7F;
397 let is_ctrl_packet = PacketType::from_primitive(package_type_byte).is_ctrl_type();
398
399 let header_bytes: [u8; 4] = bytes
400 .split_to(4) .as_ref()
402 .try_into()
403 .unwrap(); let mut payload = bytes;
405
406 if is_ctrl_packet {
407 let header = CtrlHeader::from_bytes(header_bytes);
408 Ok(RawPacket::Ctrl {
409 header,
410 payload: payload.to_vec(),
411 })
412 } else {
413 let header = DataHeader::from_bytes(header_bytes);
414 let packet_type = PacketType::from_primitive(header.packet_type());
415
416 if packet_type == PacketType::PutData {
418 if header.obj_count_words() == 0 || payload.is_empty() {
420 return Ok(RawPacket::Data {
422 header,
423 logical_packets: vec![],
424 });
425 }
426
427 if payload.len() < EXTENDED_HEADER_SIZE {
428 return Ok(RawPacket::SimpleData {
432 header,
433 payload: payload.to_vec(),
434 });
435 }
436
437 let mut logical_packets = Vec::new();
439
440 loop {
441 if payload.len() < EXTENDED_HEADER_SIZE {
442 return Err(KMError::InvalidPacket(format!(
443 "Insufficient bytes for extended header: need {}, got {}",
444 EXTENDED_HEADER_SIZE,
445 payload.len()
446 )));
447 }
448
449 let ext_header_bytes = payload.split_to(4);
451 let ext_header_array: [u8; 4] = ext_header_bytes
452 .as_ref()
453 .try_into()
454 .map_err(|_| KMError::InvalidPacket("Failed to extract extended header bytes".to_string()))?;
455 let ext = ExtendedHeader::from_bytes(ext_header_array);
456
457 let has_next = ext.next();
458 let attribute = Attribute::from_primitive(ext.attribute());
459 let payload_size = ext.size() as usize
461 * if attribute == Attribute::AdcQueue {
462 ext.chunk() as usize
463 } else {
464 1
465 };
466
467 let logical_payload = if !has_next && attribute == Attribute::AdcQueue {
471 let all = payload.clone();
473 payload = Bytes::new();
474 all
475 } else {
476 if payload.len() < payload_size {
478 return Err(KMError::InvalidPacket(format!(
479 "Insufficient payload bytes: expected {}, got {}",
480 payload_size,
481 payload.len()
482 )));
483 }
484 let chunk = payload.slice(..payload_size);
485 payload = payload.slice(payload_size..);
486 chunk
487 };
488
489 logical_packets.push(LogicalPacket {
490 attribute,
491 next: has_next,
492 chunk: ext.chunk(),
493 size: ext.size(),
494 payload: logical_payload.to_vec(),
495 });
496
497 if !has_next {
499 break;
500 }
501 }
502
503 if logical_packets.is_empty() {
504 return Err(KMError::InvalidPacket(
505 "PutData packet must have at least one logical packet".to_string(),
506 ));
507 }
508
509 Ok(RawPacket::Data {
510 header,
511 logical_packets,
512 })
513 } else {
514 Ok(RawPacket::SimpleData {
515 header,
516 payload: payload.to_vec(),
517 })
518 }
519 }
520 }
521}
522
523#[cfg(feature = "python")]
525impl<'py> pyo3::IntoPyObject<'py> for RawPacket {
526 type Target = pyo3::PyAny;
527 type Output = pyo3::Bound<'py, Self::Target>;
528 type Error = pyo3::PyErr;
529
530 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
531 use pyo3::types::{PyDict, PyDictMethods, PyListMethods};
532
533 let dict = PyDict::new(py);
534 match self {
535 RawPacket::Ctrl { header, payload } => {
536 let inner = PyDict::new(py);
537 let header_dict = PyDict::new(py);
538 header_dict.set_item("packet_type", header.packet_type())?;
539 header_dict.set_item("reserved_flag", header.reserved_flag())?;
540 header_dict.set_item("id", header.id())?;
541 header_dict.set_item("attribute", header.attribute())?;
542 inner.set_item("header", header_dict)?;
543 inner.set_item("payload", payload)?;
544 dict.set_item("Ctrl", inner)?;
545 }
546 RawPacket::SimpleData { header, payload } => {
547 let inner = PyDict::new(py);
548 let header_dict = PyDict::new(py);
549 header_dict.set_item("packet_type", header.packet_type())?;
550 header_dict.set_item("reserved_flag", header.reserved_flag())?;
551 header_dict.set_item("id", header.id())?;
552 header_dict.set_item("obj_count_words", header.obj_count_words())?;
553 inner.set_item("header", header_dict)?;
554 inner.set_item("payload", payload)?;
555 dict.set_item("SimpleData", inner)?;
556 }
557 RawPacket::Data {
558 header,
559 logical_packets,
560 } => {
561 let inner = PyDict::new(py);
562 let header_dict = PyDict::new(py);
563 header_dict.set_item("packet_type", header.packet_type())?;
564 header_dict.set_item("reserved_flag", header.reserved_flag())?;
565 header_dict.set_item("id", header.id())?;
566 header_dict.set_item("obj_count_words", header.obj_count_words())?;
567 inner.set_item("header", header_dict)?;
568 let lp_list = pyo3::types::PyList::empty(py);
570 for lp in logical_packets {
571 let lp_dict = PyDict::new(py);
572 lp_dict.set_item("attribute", u16::from(lp.attribute))?;
573 lp_dict.set_item("next", lp.next)?;
574 lp_dict.set_item("chunk", lp.chunk)?;
575 lp_dict.set_item("size", lp.size)?;
576 lp_dict.set_item("payload", lp.payload)?;
577 lp_list.append(lp_dict)?;
578 }
579 inner.set_item("logical_packets", lp_list)?;
580 dict.set_item("Data", inner)?;
581 }
582 }
583 Ok(dict.into_any())
584 }
585}
586
587impl From<RawPacket> for Bytes {
588 fn from(packet: RawPacket) -> Self {
589 let (header_bytes, payload) = match packet {
590 RawPacket::Ctrl { header, payload } => (header.into_bytes(), payload),
591 RawPacket::SimpleData { header, payload } => (header.into_bytes(), payload),
592 RawPacket::Data {
593 header,
594 logical_packets,
595 } => {
596 let mut full_payload = Vec::new();
598
599 for logical_packet in logical_packets {
600 let ext = ExtendedHeader::new()
602 .with_attribute(logical_packet.attribute.into())
603 .with_next(logical_packet.next)
604 .with_chunk(logical_packet.chunk)
605 .with_size(logical_packet.size);
606
607 full_payload.extend_from_slice(&ext.into_bytes());
608 full_payload.extend_from_slice(&logical_packet.payload);
609 }
610
611 (header.into_bytes(), full_payload)
612 }
613 };
614
615 let mut message = Vec::with_capacity(4 + payload.len());
617 message.extend_from_slice(&header_bytes);
618 message.extend_from_slice(payload.as_ref());
619
620 Bytes::from(message)
621 }
622}