Skip to main content

km003c_lib/
message.rs

1use crate::adc::{AdcDataRaw, AdcDataSimple};
2use crate::adcqueue::{AdcQueueData, AdcQueueRawData, GraphSampleRate};
3use crate::auth::{self, AuthCredential, StreamingAuthResult};
4use crate::constants::*;
5use crate::error::KMError;
6use crate::offline::{LogMetadata, LogMetadataResponse};
7use crate::packet::{
8    Attribute, AttributeSet, CtrlHeader, DataHeader, LogicalPacket, PacketType, RawPacket, StreamingAuthHeader,
9};
10use crate::pd::{PdEventStream, PdStatus, PdStatusRaw};
11use crate::pd_trace::PdTrace;
12use crate::settings::Settings;
13use bytes::Bytes;
14use num_enum::FromPrimitive;
15use zerocopy::{FromBytes, IntoBytes};
16
17const PD_MONITOR_ENABLED_PARAMETER: u16 = 1;
18const PD_MONITOR_DISABLED_PARAMETER: u16 = 0;
19
20/// Represents parsed payload data from logical packets
21#[derive(Debug, Clone, PartialEq)]
22#[cfg_attr(feature = "python", derive(pyo3::IntoPyObject))]
23pub enum PayloadData {
24    Adc(AdcDataSimple),
25    AdcQueue(AdcQueueData),
26    AdcQueueRaw(AdcQueueRawData),
27    PdStatus(PdStatus),
28    PdEvents(PdEventStream),
29    PdTrace(PdTrace),
30    Settings(Settings),
31    LogMetadata(LogMetadataResponse),
32    Unknown { attribute: Attribute, data: Vec<u8> },
33}
34
35#[derive(Debug, Clone, PartialEq)]
36pub enum Packet {
37    /// Data response with parsed payload data
38    DataResponse { payloads: Vec<PayloadData> },
39    /// Request data with attribute set
40    GetData { attribute_mask: u16 },
41    /// Start AdcQueue graph mode with sample rate
42    /// Logical rate index: 0=2SPS, 1=10SPS, 2=50SPS, 3=1000SPS.
43    /// `CtrlHeader` places this attribute in the wire header, producing byte 2
44    /// values 0, 2, 4, and 6 respectively.
45    StartGraph { rate_index: u16 },
46    /// Stop AdcQueue graph mode
47    StopGraph,
48    /// Accept response
49    Accept { id: u8 },
50    /// Reject response
51    Reject { id: u8 },
52    /// NotReadable response - memory address not accessible
53    NotReadable { id: u8 },
54    /// Connect command
55    Connect,
56    /// Disconnect command
57    Disconnect,
58    /// Enable PD monitor/sniffer
59    EnablePdMonitor,
60    /// Disable PD monitor/sniffer
61    DisablePdMonitor,
62    /// MemoryRead command (0x44) - read device memory with encrypted payload
63    MemoryRead {
64        /// Memory address to read from
65        address: u32,
66        /// Number of bytes to read
67        size: u32,
68    },
69    /// StreamingAuth command (0x4C) - authenticate for AdcQueue streaming
70    StreamingAuth {
71        /// Device or calibration credential to authenticate with
72        credential: AuthCredential,
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 losslessly parsed AdcQueue data whose graph rate is unknown.
104    pub fn get_adc_queue_raw(&self) -> Option<&AdcQueueRawData> {
105        match self {
106            Self::DataResponse { payloads } => payloads.iter().find_map(|p| match p {
107                PayloadData::AdcQueueRaw(queue) => Some(queue),
108                _ => None,
109            }),
110            _ => None,
111        }
112    }
113
114    /// Get PD status from the packet, if present
115    pub fn get_pd_status(&self) -> Option<&PdStatus> {
116        match self {
117            Self::DataResponse { payloads } => payloads.iter().find_map(|p| match p {
118                PayloadData::PdStatus(pd) => Some(pd),
119                _ => None,
120            }),
121            _ => None,
122        }
123    }
124
125    /// Get PD events from the packet, if present
126    pub fn get_pd_events(&self) -> Option<&PdEventStream> {
127        match self {
128            Self::DataResponse { payloads } => payloads.iter().find_map(|p| match p {
129                PayloadData::PdEvents(events) => Some(events),
130                _ => None,
131            }),
132            _ => None,
133        }
134    }
135
136    /// Get the internal PD state-machine trace, if present.
137    pub fn get_pd_trace(&self) -> Option<&PdTrace> {
138        match self {
139            Self::DataResponse { payloads } => payloads.iter().find_map(|payload| match payload {
140                PayloadData::PdTrace(trace) => Some(trace),
141                _ => None,
142            }),
143            _ => None,
144        }
145    }
146
147    /// Get the read-only settings payload, if present.
148    pub fn get_settings(&self) -> Option<&Settings> {
149        match self {
150            Self::DataResponse { payloads } => payloads.iter().find_map(|payload| match payload {
151                PayloadData::Settings(settings) => Some(settings),
152                _ => None,
153            }),
154            _ => None,
155        }
156    }
157
158    /// Get the response to a LogMetadata request, if present.
159    pub fn get_log_metadata(&self) -> Option<&LogMetadataResponse> {
160        match self {
161            Self::DataResponse { payloads } => payloads.iter().find_map(|payload| match payload {
162                PayloadData::LogMetadata(metadata) => Some(metadata),
163                _ => None,
164            }),
165            _ => None,
166        }
167    }
168
169    /// Check if packet has a specific payload type
170    pub fn has_payload(&self, attr: Attribute) -> bool {
171        match self {
172            Self::DataResponse { payloads } => payloads.iter().any(|p| match p {
173                PayloadData::Adc(_) => attr == Attribute::Adc,
174                PayloadData::AdcQueue(_) => attr == Attribute::AdcQueue,
175                PayloadData::AdcQueueRaw(_) => attr == Attribute::AdcQueue,
176                PayloadData::PdStatus(_) | PayloadData::PdEvents(_) => attr == Attribute::PdPacket,
177                PayloadData::PdTrace(_) => attr == Attribute::PdTrace,
178                PayloadData::Settings(_) => attr == Attribute::Settings,
179                PayloadData::LogMetadata(_) => attr == Attribute::LogMetadata,
180                PayloadData::Unknown { attribute, .. } => *attribute == attr,
181            }),
182            _ => false,
183        }
184    }
185}
186
187impl TryFrom<RawPacket> for Packet {
188    type Error = KMError;
189
190    fn try_from(raw_packet: RawPacket) -> Result<Self, Self::Error> {
191        Self::from_raw(raw_packet, None)
192    }
193}
194
195impl Packet {
196    /// Parse a raw packet using the configured AdcQueue graph rate.
197    ///
198    /// Live device users should supply the rate used with `StartGraph` because
199    /// auxiliary AdcQueue voltage fields use rate-dependent units.
200    pub fn from_raw_with_graph_rate(raw_packet: RawPacket, rate: GraphSampleRate) -> Result<Self, KMError> {
201        Self::from_raw(raw_packet, Some(rate))
202    }
203
204    fn from_raw(raw_packet: RawPacket, graph_rate: Option<GraphSampleRate>) -> Result<Self, KMError> {
205        match raw_packet {
206            RawPacket::Ctrl { header, payload } => {
207                let packet_type = PacketType::from_primitive(header.packet_type());
208                let attribute_set = AttributeSet::from_raw(header.attribute());
209
210                match packet_type {
211                    PacketType::GetData => Ok(Packet::GetData {
212                        attribute_mask: attribute_set.raw(),
213                    }),
214                    PacketType::StartGraph => Ok(Packet::StartGraph {
215                        rate_index: attribute_set.raw(),
216                    }),
217                    PacketType::StopGraph => Ok(Packet::StopGraph),
218                    PacketType::Accept => Ok(Packet::Accept { id: header.id() }),
219                    PacketType::Rejected => Ok(Packet::Reject { id: header.id() }),
220                    PacketType::NotReadable => Ok(Packet::NotReadable { id: header.id() }),
221                    PacketType::Connect => Ok(Packet::Connect),
222                    PacketType::Disconnect => Ok(Packet::Disconnect),
223                    _ => Ok(Packet::Generic(RawPacket::Ctrl { header, payload })),
224                }
225            }
226            RawPacket::SimpleData { header, payload } => {
227                let packet_type = PacketType::from_primitive(header.packet_type());
228
229                match packet_type {
230                    PacketType::MemoryRead => {
231                        if !header.reserved_flag()
232                            && header.into_bytes()[2..4] == [0x01, 0x01]
233                            && let Some((address, size)) = auth::parse_memory_read_payload(&payload)
234                        {
235                            Ok(Packet::MemoryRead { address, size })
236                        } else {
237                            Ok(Packet::Generic(RawPacket::SimpleData { header, payload }))
238                        }
239                    }
240                    PacketType::StreamingAuth => {
241                        let auth_header = StreamingAuthHeader::from_bytes(header.into_bytes());
242                        if auth_header.attribute() == 0x0200
243                            && let Some(credential) = auth::parse_streaming_auth_request_payload(&payload)
244                        {
245                            Ok(Packet::StreamingAuth { credential })
246                        } else if let Some(result) = auth::parse_streaming_auth_response_parts(auth_header, &payload) {
247                            Ok(Packet::StreamingAuthResponse(result))
248                        } else {
249                            Ok(Packet::Generic(RawPacket::SimpleData { header, payload }))
250                        }
251                    }
252                    _ => Ok(Packet::Generic(RawPacket::SimpleData { header, payload })),
253                }
254            }
255            RawPacket::Data { logical_packets, .. } => {
256                // Parse logical packets into PayloadData
257                let mut payloads = Vec::new();
258
259                for lp in logical_packets {
260                    let payload_data = match lp.attribute {
261                        Attribute::Adc => {
262                            // Parse ADC data (44 bytes)
263                            if lp.payload.len() < ADC_DATA_SIZE {
264                                return Err(KMError::InvalidPacket(format!(
265                                    "ADC payload too small: expected {}, got {}",
266                                    ADC_DATA_SIZE,
267                                    lp.payload.len()
268                                )));
269                            }
270
271                            let adc_data_raw = AdcDataRaw::ref_from_bytes(&lp.payload[..ADC_DATA_SIZE])
272                                .map_err(|_| KMError::InvalidPacket("Failed to parse ADC data".to_string()))?;
273                            let adc_data = AdcDataSimple::from(*adc_data_raw);
274                            PayloadData::Adc(adc_data)
275                        }
276                        Attribute::AdcQueue => {
277                            // Parse AdcQueue data (multiple 20-byte samples)
278                            // Note: Extended header size field (typically 20) indicates size per sample,
279                            // not total payload size. Actual payload contains N samples.
280                            if let Some(rate) = graph_rate {
281                                PayloadData::AdcQueue(AdcQueueData::from_bytes_with_rate(lp.payload.as_ref(), rate)?)
282                            } else {
283                                PayloadData::AdcQueueRaw(AdcQueueRawData::from_bytes(lp.payload.as_ref())?)
284                            }
285                        }
286                        Attribute::PdPacket => {
287                            // Determine if this is PD status or PD events
288                            if lp.payload.len() == PD_STATUS_SIZE {
289                                // PD Status (12 bytes)
290                                let pd_status_raw = PdStatusRaw::ref_from_bytes(lp.payload.as_ref())
291                                    .map_err(|_| KMError::InvalidPacket("Failed to parse PD status".to_string()))?;
292                                PayloadData::PdStatus(PdStatus::from(*pd_status_raw))
293                            } else {
294                                // PD Event Stream
295                                let pd_events = PdEventStream::from_bytes(Bytes::from(lp.payload))?;
296                                PayloadData::PdEvents(pd_events)
297                            }
298                        }
299                        Attribute::PdTrace => PayloadData::PdTrace(PdTrace::from_bytes(&lp.payload)?),
300                        Attribute::Settings => PayloadData::Settings(Settings::from_bytes(lp.payload.as_ref())?),
301                        Attribute::LogMetadata => {
302                            if lp.payload.is_empty() {
303                                PayloadData::LogMetadata(LogMetadataResponse::Empty)
304                            } else {
305                                if !lp.payload.len().is_multiple_of(crate::offline::LOG_METADATA_SIZE) {
306                                    return Err(KMError::InvalidPacket(format!(
307                                        "LogMetadata payload length must be a multiple of {}, got {}",
308                                        crate::offline::LOG_METADATA_SIZE,
309                                        lp.payload.len()
310                                    )));
311                                }
312                                let entries = lp
313                                    .payload
314                                    .chunks_exact(crate::offline::LOG_METADATA_SIZE)
315                                    .map(LogMetadata::from_bytes)
316                                    .collect::<Result<Vec<_>, _>>()?;
317                                PayloadData::LogMetadata(LogMetadataResponse::Available(entries))
318                            }
319                        }
320                        _ => PayloadData::Unknown {
321                            attribute: lp.attribute,
322                            data: lp.payload.to_vec(),
323                        },
324                    };
325
326                    payloads.push(payload_data);
327                }
328
329                Ok(Packet::DataResponse { payloads })
330            }
331        }
332    }
333}
334
335impl Packet {
336    /// Convert a high-level packet to a raw packet with the given transaction ID
337    pub fn to_raw_packet(self, id: u8) -> Result<RawPacket, KMError> {
338        let raw_packet = match self {
339            Packet::DataResponse { payloads } => {
340                let mut logical_packets = Vec::new();
341
342                for payload in payloads {
343                    match payload {
344                        PayloadData::Adc(adc) => {
345                            let adc_raw = AdcDataRaw::from(adc);
346                            logical_packets.push(LogicalPacket {
347                                attribute: Attribute::Adc,
348                                next: false, // Will be fixed below
349                                chunk: 0,
350                                size: ADC_DATA_SIZE as u16,
351                                payload: adc_raw.as_bytes().to_vec(),
352                            });
353                        }
354                        PayloadData::PdStatus(pd_status) => {
355                            let raw = PdStatusRaw::from(pd_status);
356
357                            logical_packets.push(LogicalPacket {
358                                attribute: Attribute::PdPacket,
359                                next: false, // Will be fixed below
360                                chunk: 0,
361                                size: PD_STATUS_SIZE as u16,
362                                payload: raw.as_bytes().to_vec(),
363                            });
364                        }
365                        PayloadData::AdcQueue(_) => {
366                            return Err(KMError::UnsupportedSerialization { packet: "AdcQueue" });
367                        }
368                        PayloadData::AdcQueueRaw(queue) => {
369                            logical_packets.push(LogicalPacket {
370                                attribute: Attribute::AdcQueue,
371                                next: false,
372                                chunk: 0,
373                                size: 20,
374                                payload: queue.to_bytes(),
375                            });
376                        }
377                        PayloadData::PdEvents(_) => {
378                            return Err(KMError::UnsupportedSerialization {
379                                packet: "PdEventStream",
380                            });
381                        }
382                        PayloadData::PdTrace(trace) => {
383                            logical_packets.push(LogicalPacket {
384                                attribute: Attribute::PdTrace,
385                                next: false,
386                                chunk: 0,
387                                size: 0,
388                                payload: trace.to_bytes()?,
389                            });
390                        }
391                        PayloadData::Settings(settings) => {
392                            logical_packets.push(LogicalPacket {
393                                attribute: Attribute::Settings,
394                                next: false,
395                                chunk: 0,
396                                size: crate::settings::SETTINGS_SIZE as u16,
397                                payload: settings.to_bytes().to_vec(),
398                            });
399                        }
400                        PayloadData::LogMetadata(response) => {
401                            let payload = match response {
402                                LogMetadataResponse::Empty => Vec::new(),
403                                LogMetadataResponse::Available(metadata) => {
404                                    metadata.iter().flat_map(LogMetadata::to_bytes).collect()
405                                }
406                            };
407                            logical_packets.push(LogicalPacket {
408                                attribute: Attribute::LogMetadata,
409                                next: false,
410                                chunk: 0,
411                                size: payload.len() as u16,
412                                payload,
413                            });
414                        }
415                        PayloadData::Unknown { attribute, data } => {
416                            logical_packets.push(LogicalPacket {
417                                attribute,
418                                next: false, // Will be fixed below
419                                chunk: 0,
420                                size: data.len() as u16,
421                                payload: data,
422                            });
423                        }
424                    }
425                }
426
427                // Set `next` flag on all packets except the last
428                if let Some((last, rest)) = logical_packets.split_last_mut() {
429                    for lp in rest {
430                        lp.next = true;
431                    }
432                    last.next = false;
433                }
434
435                // Calculate the serialized size of all extended headers and payloads.
436                let total_size: usize = logical_packets.iter().map(|lp| 4 + lp.payload.len()).sum();
437
438                // Recorded PutData responses encode a word count relative to the
439                // complete packet: packet_len / 4 - 3. The field is not the raw
440                // payload length despite its historical `obj_count_words` name.
441                let obj_count_words = if logical_packets.is_empty() {
442                    0
443                } else {
444                    ((4 + total_size) / 4).saturating_sub(3) as u16
445                };
446
447                let header = DataHeader::new()
448                    .with_packet_type(PacketType::PutData.into())
449                    .with_reserved_flag(false)
450                    .with_id(id)
451                    .with_obj_count_words(obj_count_words);
452
453                RawPacket::Data {
454                    header,
455                    logical_packets,
456                }
457            }
458            Packet::GetData { attribute_mask } => RawPacket::Ctrl {
459                header: CtrlHeader::new()
460                    .with_packet_type(PacketType::GetData.into())
461                    .with_reserved_flag(false)
462                    .with_id(id)
463                    .with_attribute(attribute_mask),
464                payload: Vec::new(),
465            },
466            Packet::StartGraph { rate_index } => RawPacket::Ctrl {
467                header: CtrlHeader::new()
468                    .with_packet_type(PacketType::StartGraph.into())
469                    .with_reserved_flag(false)
470                    .with_id(id)
471                    .with_attribute(rate_index),
472                payload: Vec::new(),
473            },
474            Packet::StopGraph => RawPacket::Ctrl {
475                header: CtrlHeader::new()
476                    .with_packet_type(PacketType::StopGraph.into())
477                    .with_reserved_flag(false)
478                    .with_id(id)
479                    .with_attribute(0),
480                payload: Vec::new(),
481            },
482            Packet::Accept { id: accept_id } => RawPacket::Ctrl {
483                header: CtrlHeader::new()
484                    .with_packet_type(PacketType::Accept.into())
485                    .with_reserved_flag(false)
486                    .with_id(accept_id)
487                    .with_attribute(0),
488                payload: Vec::new(),
489            },
490            Packet::Reject { id: reject_id } => RawPacket::Ctrl {
491                header: CtrlHeader::new()
492                    .with_packet_type(PacketType::Rejected.into())
493                    .with_reserved_flag(false)
494                    .with_id(reject_id)
495                    .with_attribute(0),
496                payload: Vec::new(),
497            },
498            Packet::NotReadable { id: nr_id } => RawPacket::Ctrl {
499                header: CtrlHeader::new()
500                    .with_packet_type(PacketType::NotReadable.into())
501                    .with_reserved_flag(false)
502                    .with_id(nr_id)
503                    .with_attribute(0),
504                payload: Vec::new(),
505            },
506            Packet::Connect => RawPacket::Ctrl {
507                header: CtrlHeader::new()
508                    .with_packet_type(PacketType::Connect.into())
509                    .with_reserved_flag(false)
510                    .with_id(id)
511                    .with_attribute(0),
512                payload: Vec::new(),
513            },
514            Packet::Disconnect => RawPacket::Ctrl {
515                header: CtrlHeader::new()
516                    .with_packet_type(PacketType::Disconnect.into())
517                    .with_reserved_flag(false)
518                    .with_id(id)
519                    .with_attribute(0),
520                payload: Vec::new(),
521            },
522            Packet::EnablePdMonitor => RawPacket::Ctrl {
523                header: CtrlHeader::new()
524                    .with_packet_type(PacketType::EnablePdMonitor.into())
525                    .with_reserved_flag(false)
526                    .with_id(id)
527                    .with_attribute(PD_MONITOR_ENABLED_PARAMETER),
528                payload: Vec::new(),
529            },
530            Packet::DisablePdMonitor => RawPacket::Ctrl {
531                header: CtrlHeader::new()
532                    .with_packet_type(PacketType::DisablePdMonitor.into())
533                    .with_reserved_flag(false)
534                    .with_id(id)
535                    .with_attribute(PD_MONITOR_DISABLED_PARAMETER),
536                payload: Vec::new(),
537            },
538            Packet::MemoryRead { address, size } => {
539                // Build encrypted MemoryRead payload
540                let encrypted_payload = auth::build_memory_read_payload(address, size);
541                RawPacket::SimpleData {
542                    header: DataHeader::from_bytes([PacketType::MemoryRead.into(), id, 0x01, 0x01]),
543                    payload: encrypted_payload.to_vec(),
544                }
545            }
546            Packet::StreamingAuth { credential } => {
547                // Build encrypted StreamingAuth payload
548                let encrypted_payload = auth::build_streaming_auth_payload(&credential);
549                RawPacket::SimpleData {
550                    header: DataHeader::from_bytes([PacketType::StreamingAuth.into(), id, 0x00, 0x02]),
551                    payload: encrypted_payload.to_vec(),
552                }
553            }
554            Packet::StreamingAuthResponse(result) => {
555                // Response packets are typically not sent by client, but support for completeness
556                let encrypted_payload = auth::encrypt_streaming_auth_response_payload(&result.decrypted_payload);
557                let auth_header = StreamingAuthHeader::new()
558                    .with_packet_type(PacketType::StreamingAuth.into())
559                    .with_reserved_flag(false)
560                    .with_id(auth::STREAMING_AUTH_RESPONSE_ID)
561                    .with_attribute(result.attribute);
562                RawPacket::Ctrl {
563                    header: CtrlHeader::from_bytes(auth_header.into_bytes()),
564                    payload: encrypted_payload.to_vec(),
565                }
566            }
567            Packet::Generic(raw_packet) => raw_packet,
568        };
569
570        Ok(raw_packet)
571    }
572}
573
574// Python support for Packet
575#[cfg(feature = "python")]
576impl<'py> pyo3::IntoPyObject<'py> for Packet {
577    type Target = pyo3::PyAny;
578    type Output = pyo3::Bound<'py, Self::Target>;
579    type Error = pyo3::PyErr;
580
581    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
582        use pyo3::types::{PyDict, PyDictMethods};
583
584        let dict = PyDict::new(py);
585        match self {
586            Packet::DataResponse { payloads } => {
587                let inner = PyDict::new(py);
588                inner.set_item("payloads", payloads.into_pyobject(py)?)?;
589                dict.set_item("DataResponse", inner)?;
590            }
591            Packet::GetData { attribute_mask } => {
592                let inner = PyDict::new(py);
593                inner.set_item("attribute_mask", attribute_mask)?;
594                dict.set_item("GetData", inner)?;
595            }
596            Packet::StartGraph { rate_index } => {
597                let inner = PyDict::new(py);
598                inner.set_item("rate_index", rate_index)?;
599                dict.set_item("StartGraph", inner)?;
600            }
601            Packet::StopGraph => {
602                dict.set_item("StopGraph", py.None())?;
603            }
604            Packet::Accept { id } => {
605                let inner = PyDict::new(py);
606                inner.set_item("id", id)?;
607                dict.set_item("Accept", inner)?;
608            }
609            Packet::Reject { id } => {
610                let inner = PyDict::new(py);
611                inner.set_item("id", id)?;
612                dict.set_item("Reject", inner)?;
613            }
614            Packet::NotReadable { id } => {
615                let inner = PyDict::new(py);
616                inner.set_item("id", id)?;
617                dict.set_item("NotReadable", inner)?;
618            }
619            Packet::Connect => {
620                dict.set_item("Connect", py.None())?;
621            }
622            Packet::Disconnect => {
623                dict.set_item("Disconnect", py.None())?;
624            }
625            Packet::EnablePdMonitor => {
626                dict.set_item("EnablePdMonitor", py.None())?;
627            }
628            Packet::DisablePdMonitor => {
629                dict.set_item("DisablePdMonitor", py.None())?;
630            }
631            Packet::MemoryRead { address, size } => {
632                let inner = PyDict::new(py);
633                inner.set_item("address", address)?;
634                inner.set_item("size", size)?;
635                dict.set_item("MemoryRead", inner)?;
636            }
637            Packet::StreamingAuth { credential } => {
638                let inner = PyDict::new(py);
639                inner.set_item("credential", hex::encode(credential.as_bytes()))?;
640                dict.set_item("StreamingAuth", inner)?;
641            }
642            Packet::StreamingAuthResponse(result) => {
643                let inner = PyDict::new(py);
644                inner.set_item("success", result.success)?;
645                inner.set_item("auth_level", result.auth_level)?;
646                inner.set_item("attribute", result.attribute)?;
647                dict.set_item("StreamingAuthResponse", inner)?;
648            }
649            Packet::Generic(raw_packet) => {
650                dict.set_item("Generic", raw_packet.into_pyobject(py)?)?;
651            }
652        }
653        Ok(dict.into_any())
654    }
655}