Skip to main content

km003c_lib/
message.rs

1use crate::adc::{AdcDataRaw, AdcDataSimple};
2use crate::adcqueue::{AdcQueueData, GraphSampleRate};
3use crate::auth::{self, HardwareId, StreamingAuthResult};
4use crate::constants::*;
5use crate::error::KMError;
6use crate::packet::{
7    Attribute, AttributeSet, CtrlHeader, DataHeader, LogicalPacket, PacketType, RawPacket, StreamingAuthHeader,
8};
9use crate::pd::{PdEventStream, PdStatus, PdStatusRaw};
10use bytes::Bytes;
11use num_enum::FromPrimitive;
12use uom::si::electric_current::milliampere;
13use uom::si::electric_potential::millivolt;
14use zerocopy::{FromBytes, IntoBytes};
15
16const PD_MONITOR_ENABLED_PARAMETER: u16 = 1;
17const PD_MONITOR_DISABLED_PARAMETER: u16 = 0;
18
19/// Represents parsed payload data from logical packets
20#[derive(Debug, Clone, PartialEq)]
21#[cfg_attr(feature = "python", derive(pyo3::IntoPyObject))]
22pub enum PayloadData {
23    Adc(AdcDataSimple),
24    AdcQueue(AdcQueueData),
25    PdStatus(PdStatus),
26    PdEvents(PdEventStream),
27    Unknown { attribute: Attribute, data: Vec<u8> },
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum Packet {
32    /// Data response with parsed payload data
33    DataResponse { payloads: Vec<PayloadData> },
34    /// Request data with attribute set
35    GetData { attribute_mask: u16 },
36    /// Start AdcQueue graph mode with sample rate
37    /// Logical rate index: 0=2SPS, 1=10SPS, 2=50SPS, 3=1000SPS.
38    /// `CtrlHeader` places this attribute in the wire header, producing byte 2
39    /// values 0, 2, 4, and 6 respectively.
40    StartGraph { rate_index: u16 },
41    /// Stop AdcQueue graph mode
42    StopGraph,
43    /// Accept response
44    Accept { id: u8 },
45    /// Reject response
46    Reject { id: u8 },
47    /// NotReadable response - memory address not accessible
48    NotReadable { id: u8 },
49    /// Connect command
50    Connect,
51    /// Disconnect command
52    Disconnect,
53    /// Enable PD monitor/sniffer
54    EnablePdMonitor,
55    /// Disable PD monitor/sniffer
56    DisablePdMonitor,
57    /// MemoryRead command (0x44) - read device memory with encrypted payload
58    MemoryRead {
59        /// Memory address to read from
60        address: u32,
61        /// Number of bytes to read
62        size: u32,
63    },
64    /// Decrypted data returned by the unframed MemoryRead data transfer
65    MemoryReadResponse {
66        /// Raw data read from memory (e.g., 12-byte HardwareID)
67        data: Vec<u8>,
68    },
69    /// StreamingAuth command (0x4C) - authenticate for AdcQueue streaming
70    StreamingAuth {
71        /// HardwareID to authenticate with
72        hardware_id: HardwareId,
73    },
74    /// StreamingAuth response (0x4C) - authentication result
75    StreamingAuthResponse(StreamingAuthResult),
76    /// Generic packet for types we haven't specifically implemented yet
77    Generic(RawPacket),
78}
79
80impl Packet {
81    /// Get ADC data from the packet, if present
82    pub fn get_adc(&self) -> Option<&AdcDataSimple> {
83        match self {
84            Self::DataResponse { payloads } => payloads.iter().find_map(|p| match p {
85                PayloadData::Adc(adc) => Some(adc),
86                _ => None,
87            }),
88            _ => None,
89        }
90    }
91
92    /// Get AdcQueue data from the packet, if present
93    pub fn get_adc_queue(&self) -> Option<&AdcQueueData> {
94        match self {
95            Self::DataResponse { payloads } => payloads.iter().find_map(|p| match p {
96                PayloadData::AdcQueue(queue) => Some(queue),
97                _ => None,
98            }),
99            _ => None,
100        }
101    }
102
103    /// Get PD status from the packet, if present
104    pub fn get_pd_status(&self) -> Option<&PdStatus> {
105        match self {
106            Self::DataResponse { payloads } => payloads.iter().find_map(|p| match p {
107                PayloadData::PdStatus(pd) => Some(pd),
108                _ => None,
109            }),
110            _ => None,
111        }
112    }
113
114    /// Get PD events from the packet, if present
115    pub fn get_pd_events(&self) -> Option<&PdEventStream> {
116        match self {
117            Self::DataResponse { payloads } => payloads.iter().find_map(|p| match p {
118                PayloadData::PdEvents(events) => Some(events),
119                _ => None,
120            }),
121            _ => None,
122        }
123    }
124
125    /// Check if packet has a specific payload type
126    pub fn has_payload(&self, attr: Attribute) -> bool {
127        match self {
128            Self::DataResponse { payloads } => payloads.iter().any(|p| match p {
129                PayloadData::Adc(_) => attr == Attribute::Adc,
130                PayloadData::AdcQueue(_) => attr == Attribute::AdcQueue,
131                PayloadData::PdStatus(_) | PayloadData::PdEvents(_) => attr == Attribute::PdPacket,
132                PayloadData::Unknown { attribute, .. } => *attribute == attr,
133            }),
134            _ => false,
135        }
136    }
137}
138
139impl TryFrom<RawPacket> for Packet {
140    type Error = KMError;
141
142    fn try_from(raw_packet: RawPacket) -> Result<Self, Self::Error> {
143        Self::from_raw(raw_packet, None)
144    }
145}
146
147impl Packet {
148    /// Parse a raw packet using the configured AdcQueue graph rate.
149    ///
150    /// Live device users should supply the rate used with `StartGraph` because
151    /// auxiliary AdcQueue voltage fields use rate-dependent units.
152    pub fn from_raw_with_graph_rate(raw_packet: RawPacket, rate: GraphSampleRate) -> Result<Self, KMError> {
153        Self::from_raw(raw_packet, Some(rate))
154    }
155
156    fn from_raw(raw_packet: RawPacket, graph_rate: Option<GraphSampleRate>) -> Result<Self, KMError> {
157        match raw_packet {
158            RawPacket::Ctrl { header, payload } => {
159                let packet_type = PacketType::from_primitive(header.packet_type());
160                let attribute_set = AttributeSet::from_raw(header.attribute());
161
162                match packet_type {
163                    PacketType::GetData => Ok(Packet::GetData {
164                        attribute_mask: attribute_set.raw(),
165                    }),
166                    PacketType::StartGraph => Ok(Packet::StartGraph {
167                        rate_index: attribute_set.raw(),
168                    }),
169                    PacketType::StopGraph => Ok(Packet::StopGraph),
170                    PacketType::Accept => Ok(Packet::Accept { id: header.id() }),
171                    PacketType::Rejected => Ok(Packet::Reject { id: header.id() }),
172                    PacketType::NotReadable => Ok(Packet::NotReadable { id: header.id() }),
173                    PacketType::Connect => Ok(Packet::Connect),
174                    PacketType::Disconnect => Ok(Packet::Disconnect),
175                    _ => Ok(Packet::Generic(RawPacket::Ctrl { header, payload })),
176                }
177            }
178            RawPacket::SimpleData { header, payload } => {
179                let packet_type = PacketType::from_primitive(header.packet_type());
180
181                match packet_type {
182                    // MemoryRead response (0xC4 = 0x44 | 0x80) - confirmation
183                    PacketType::MemoryRead => {
184                        // This is just a confirmation, return as Accept-like
185                        Ok(Packet::Accept { id: header.id() })
186                    }
187                    // StreamingAuth response (0x4C) - encrypted result
188                    PacketType::StreamingAuth => {
189                        let auth_header = StreamingAuthHeader::from_bytes(header.into_bytes());
190                        if let Some(result) = auth::parse_streaming_auth_response_parts(auth_header, &payload) {
191                            Ok(Packet::StreamingAuthResponse(result))
192                        } else {
193                            Ok(Packet::Generic(RawPacket::SimpleData { header, payload }))
194                        }
195                    }
196                    _ => Ok(Packet::Generic(RawPacket::SimpleData { header, payload })),
197                }
198            }
199            RawPacket::Data { logical_packets, .. } => {
200                // Parse logical packets into PayloadData
201                let mut payloads = Vec::new();
202
203                for lp in logical_packets {
204                    let payload_data = match lp.attribute {
205                        Attribute::Adc => {
206                            // Parse ADC data (44 bytes)
207                            if lp.payload.len() < ADC_DATA_SIZE {
208                                return Err(KMError::InvalidPacket(format!(
209                                    "ADC payload too small: expected {}, got {}",
210                                    ADC_DATA_SIZE,
211                                    lp.payload.len()
212                                )));
213                            }
214
215                            let adc_data_raw = AdcDataRaw::ref_from_bytes(&lp.payload[..ADC_DATA_SIZE])
216                                .map_err(|_| KMError::InvalidPacket("Failed to parse ADC data".to_string()))?;
217                            let adc_data = AdcDataSimple::from(*adc_data_raw);
218                            PayloadData::Adc(adc_data)
219                        }
220                        Attribute::AdcQueue => {
221                            // Parse AdcQueue data (multiple 20-byte samples)
222                            // Note: Extended header size field (typically 20) indicates size per sample,
223                            // not total payload size. Actual payload contains N samples.
224                            let adcqueue = if let Some(rate) = graph_rate {
225                                AdcQueueData::from_bytes_with_rate(lp.payload.as_ref(), rate)?
226                            } else {
227                                AdcQueueData::from_bytes(lp.payload.as_ref())?
228                            };
229                            PayloadData::AdcQueue(adcqueue)
230                        }
231                        Attribute::PdPacket => {
232                            // Determine if this is PD status or PD events
233                            if lp.payload.len() == PD_STATUS_SIZE {
234                                // PD Status (12 bytes)
235                                let pd_status_raw = PdStatusRaw::ref_from_bytes(lp.payload.as_ref())
236                                    .map_err(|_| KMError::InvalidPacket("Failed to parse PD status".to_string()))?;
237                                PayloadData::PdStatus(PdStatus::from(*pd_status_raw))
238                            } else {
239                                // PD Event Stream
240                                let pd_events = PdEventStream::from_bytes(Bytes::from(lp.payload))?;
241                                PayloadData::PdEvents(pd_events)
242                            }
243                        }
244                        _ => PayloadData::Unknown {
245                            attribute: lp.attribute,
246                            data: lp.payload.to_vec(),
247                        },
248                    };
249
250                    payloads.push(payload_data);
251                }
252
253                Ok(Packet::DataResponse { payloads })
254            }
255        }
256    }
257}
258
259impl Packet {
260    /// Convert a high-level packet to a raw packet with the given transaction ID
261    pub fn to_raw_packet(self, id: u8) -> Result<RawPacket, KMError> {
262        let raw_packet = match self {
263            Packet::DataResponse { payloads } => {
264                let mut logical_packets = Vec::new();
265
266                for payload in payloads {
267                    match payload {
268                        PayloadData::Adc(adc) => {
269                            let adc_raw = AdcDataRaw::from(adc);
270                            logical_packets.push(LogicalPacket {
271                                attribute: Attribute::Adc,
272                                next: false, // Will be fixed below
273                                chunk: 0,
274                                size: ADC_DATA_SIZE as u16,
275                                payload: adc_raw.as_bytes().to_vec(),
276                            });
277                        }
278                        PayloadData::PdStatus(pd_status) => {
279                            // Reconstruct PdStatusRaw
280                            let timestamp_bytes = pd_status.timestamp_ticks.to_le_bytes();
281                            let mut raw_bytes = Vec::with_capacity(12);
282                            raw_bytes.push(pd_status.type_id);
283                            raw_bytes.extend_from_slice(&timestamp_bytes[..3]); // 24-bit
284                            raw_bytes.extend_from_slice(&(pd_status.vbus.get::<millivolt>() as u16).to_le_bytes());
285                            raw_bytes.extend_from_slice(&(pd_status.ibus.get::<milliampere>() as i16).to_le_bytes());
286                            raw_bytes.extend_from_slice(&(pd_status.cc1.get::<millivolt>() as u16).to_le_bytes());
287                            raw_bytes.extend_from_slice(&(pd_status.cc2.get::<millivolt>() as u16).to_le_bytes());
288
289                            logical_packets.push(LogicalPacket {
290                                attribute: Attribute::PdPacket,
291                                next: false, // Will be fixed below
292                                chunk: 0,
293                                size: PD_STATUS_SIZE as u16,
294                                payload: raw_bytes,
295                            });
296                        }
297                        PayloadData::AdcQueue(_) => {
298                            return Err(KMError::UnsupportedSerialization { packet: "AdcQueue" });
299                        }
300                        PayloadData::PdEvents(_) => {
301                            return Err(KMError::UnsupportedSerialization {
302                                packet: "PdEventStream",
303                            });
304                        }
305                        PayloadData::Unknown { attribute, data } => {
306                            logical_packets.push(LogicalPacket {
307                                attribute,
308                                next: false, // Will be fixed below
309                                chunk: 0,
310                                size: data.len() as u16,
311                                payload: data,
312                            });
313                        }
314                    }
315                }
316
317                // Set `next` flag on all packets except the last
318                if let Some((last, rest)) = logical_packets.split_last_mut() {
319                    for lp in rest {
320                        lp.next = true;
321                    }
322                    last.next = false;
323                }
324
325                // Calculate total payload size
326                let total_size: usize = logical_packets.iter().map(|lp| 4 + lp.payload.len()).sum();
327
328                let header = DataHeader::new()
329                    .with_packet_type(PacketType::PutData.into())
330                    .with_reserved_flag(true)
331                    .with_id(id)
332                    .with_obj_count_words((total_size / 4) as u16);
333
334                RawPacket::Data {
335                    header,
336                    logical_packets,
337                }
338            }
339            Packet::GetData { attribute_mask } => RawPacket::Ctrl {
340                header: CtrlHeader::new()
341                    .with_packet_type(PacketType::GetData.into())
342                    .with_reserved_flag(false)
343                    .with_id(id)
344                    .with_attribute(attribute_mask),
345                payload: Vec::new(),
346            },
347            Packet::StartGraph { rate_index } => RawPacket::Ctrl {
348                header: CtrlHeader::new()
349                    .with_packet_type(PacketType::StartGraph.into())
350                    .with_reserved_flag(false)
351                    .with_id(id)
352                    .with_attribute(rate_index),
353                payload: Vec::new(),
354            },
355            Packet::StopGraph => RawPacket::Ctrl {
356                header: CtrlHeader::new()
357                    .with_packet_type(PacketType::StopGraph.into())
358                    .with_reserved_flag(false)
359                    .with_id(id)
360                    .with_attribute(0),
361                payload: Vec::new(),
362            },
363            Packet::Accept { id: accept_id } => RawPacket::Ctrl {
364                header: CtrlHeader::new()
365                    .with_packet_type(PacketType::Accept.into())
366                    .with_reserved_flag(false)
367                    .with_id(accept_id)
368                    .with_attribute(0),
369                payload: Vec::new(),
370            },
371            Packet::Reject { id: reject_id } => RawPacket::Ctrl {
372                header: CtrlHeader::new()
373                    .with_packet_type(PacketType::Rejected.into())
374                    .with_reserved_flag(false)
375                    .with_id(reject_id)
376                    .with_attribute(0),
377                payload: Vec::new(),
378            },
379            Packet::NotReadable { id: nr_id } => RawPacket::Ctrl {
380                header: CtrlHeader::new()
381                    .with_packet_type(PacketType::NotReadable.into())
382                    .with_reserved_flag(false)
383                    .with_id(nr_id)
384                    .with_attribute(0),
385                payload: Vec::new(),
386            },
387            Packet::Connect => RawPacket::Ctrl {
388                header: CtrlHeader::new()
389                    .with_packet_type(PacketType::Connect.into())
390                    .with_reserved_flag(false)
391                    .with_id(id)
392                    .with_attribute(0),
393                payload: Vec::new(),
394            },
395            Packet::Disconnect => RawPacket::Ctrl {
396                header: CtrlHeader::new()
397                    .with_packet_type(PacketType::Disconnect.into())
398                    .with_reserved_flag(false)
399                    .with_id(id)
400                    .with_attribute(0),
401                payload: Vec::new(),
402            },
403            Packet::EnablePdMonitor => RawPacket::Ctrl {
404                header: CtrlHeader::new()
405                    .with_packet_type(PacketType::EnablePdMonitor.into())
406                    .with_reserved_flag(false)
407                    .with_id(id)
408                    .with_attribute(PD_MONITOR_ENABLED_PARAMETER),
409                payload: Vec::new(),
410            },
411            Packet::DisablePdMonitor => RawPacket::Ctrl {
412                header: CtrlHeader::new()
413                    .with_packet_type(PacketType::DisablePdMonitor.into())
414                    .with_reserved_flag(false)
415                    .with_id(id)
416                    .with_attribute(PD_MONITOR_DISABLED_PARAMETER),
417                payload: Vec::new(),
418            },
419            Packet::MemoryRead { address, size } => {
420                // Build encrypted MemoryRead payload
421                let encrypted_payload = auth::build_memory_read_payload(address, size);
422                RawPacket::SimpleData {
423                    header: DataHeader::new()
424                        .with_packet_type(PacketType::MemoryRead.into())
425                        .with_reserved_flag(false)
426                        .with_id(id)
427                        .with_obj_count_words(0x0101), // Attribute for MemoryRead
428                    payload: encrypted_payload.to_vec(),
429                }
430            }
431            Packet::MemoryReadResponse { .. } => {
432                return Err(KMError::UnsupportedSerialization {
433                    packet: "MemoryReadResponse",
434                });
435            }
436            Packet::StreamingAuth { hardware_id } => {
437                // Build encrypted StreamingAuth payload
438                let encrypted_payload = auth::build_streaming_auth_payload(&hardware_id);
439                RawPacket::SimpleData {
440                    header: DataHeader::new()
441                        .with_packet_type(PacketType::StreamingAuth.into())
442                        .with_reserved_flag(false)
443                        .with_id(id)
444                        .with_obj_count_words(0x0200), // Attribute for StreamingAuth
445                    payload: encrypted_payload.to_vec(),
446                }
447            }
448            Packet::StreamingAuthResponse(result) => {
449                // Response packets are typically not sent by client, but support for completeness
450                let encrypted_payload = auth::encrypt_streaming_auth_response_payload(&result.decrypted_payload);
451                let auth_header = StreamingAuthHeader::new()
452                    .with_packet_type(PacketType::StreamingAuth.into())
453                    .with_reserved_flag(false)
454                    .with_id(auth::STREAMING_AUTH_RESPONSE_ID)
455                    .with_attribute(result.attribute);
456                RawPacket::Ctrl {
457                    header: CtrlHeader::from_bytes(auth_header.into_bytes()),
458                    payload: encrypted_payload.to_vec(),
459                }
460            }
461            Packet::Generic(raw_packet) => raw_packet,
462        };
463
464        Ok(raw_packet)
465    }
466}
467
468// Python support for Packet
469#[cfg(feature = "python")]
470impl<'py> pyo3::IntoPyObject<'py> for Packet {
471    type Target = pyo3::PyAny;
472    type Output = pyo3::Bound<'py, Self::Target>;
473    type Error = pyo3::PyErr;
474
475    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
476        use pyo3::types::{PyDict, PyDictMethods};
477
478        let dict = PyDict::new(py);
479        match self {
480            Packet::DataResponse { payloads } => {
481                let inner = PyDict::new(py);
482                inner.set_item("payloads", payloads.into_pyobject(py)?)?;
483                dict.set_item("DataResponse", inner)?;
484            }
485            Packet::GetData { attribute_mask } => {
486                let inner = PyDict::new(py);
487                inner.set_item("attribute_mask", attribute_mask)?;
488                dict.set_item("GetData", inner)?;
489            }
490            Packet::StartGraph { rate_index } => {
491                let inner = PyDict::new(py);
492                inner.set_item("rate_index", rate_index)?;
493                dict.set_item("StartGraph", inner)?;
494            }
495            Packet::StopGraph => {
496                dict.set_item("StopGraph", py.None())?;
497            }
498            Packet::Accept { id } => {
499                let inner = PyDict::new(py);
500                inner.set_item("id", id)?;
501                dict.set_item("Accept", inner)?;
502            }
503            Packet::Reject { id } => {
504                let inner = PyDict::new(py);
505                inner.set_item("id", id)?;
506                dict.set_item("Reject", inner)?;
507            }
508            Packet::NotReadable { id } => {
509                let inner = PyDict::new(py);
510                inner.set_item("id", id)?;
511                dict.set_item("NotReadable", inner)?;
512            }
513            Packet::Connect => {
514                dict.set_item("Connect", py.None())?;
515            }
516            Packet::Disconnect => {
517                dict.set_item("Disconnect", py.None())?;
518            }
519            Packet::EnablePdMonitor => {
520                dict.set_item("EnablePdMonitor", py.None())?;
521            }
522            Packet::DisablePdMonitor => {
523                dict.set_item("DisablePdMonitor", py.None())?;
524            }
525            Packet::MemoryRead { address, size } => {
526                let inner = PyDict::new(py);
527                inner.set_item("address", address)?;
528                inner.set_item("size", size)?;
529                dict.set_item("MemoryRead", inner)?;
530            }
531            Packet::MemoryReadResponse { data } => {
532                let inner = PyDict::new(py);
533                inner.set_item("data", data)?;
534                dict.set_item("MemoryReadResponse", inner)?;
535            }
536            Packet::StreamingAuth { hardware_id } => {
537                let inner = PyDict::new(py);
538                inner.set_item("hardware_id", hex::encode(hardware_id.bytes))?;
539                dict.set_item("StreamingAuth", inner)?;
540            }
541            Packet::StreamingAuthResponse(result) => {
542                let inner = PyDict::new(py);
543                inner.set_item("success", result.success)?;
544                inner.set_item("auth_level", result.auth_level)?;
545                inner.set_item("attribute", result.attribute)?;
546                dict.set_item("StreamingAuthResponse", inner)?;
547            }
548            Packet::Generic(raw_packet) => {
549                dict.set_item("Generic", raw_packet.into_pyobject(py)?)?;
550            }
551        }
552        Ok(dict.into_any())
553    }
554}