From 68ddbaca9f562df432eed7caa8bb1351fe1381f7 Mon Sep 17 00:00:00 2001 From: dragonn Date: Thu, 7 Nov 2024 11:54:13 +0100 Subject: [PATCH 1/4] feat: add lsb decoding to identification_number parsing --- src/frames/mod.rs | 3 ++ src/user_data/mod.rs | 95 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/frames/mod.rs b/src/frames/mod.rs index dbee406..727fa96 100644 --- a/src/frames/mod.rs +++ b/src/frames/mod.rs @@ -160,8 +160,11 @@ impl<'a> TryFrom<&'a [u8]> for Frame<'a> { } let control_field = *data.get(4).ok_or(FrameError::LengthShort)?; + println!("control_field: {:x?}", control_field); let address_field = *data.get(5).ok_or(FrameError::LengthShort)?; + println!("address_field: {:x?}", address_field); + match control_field { 0x53 => Ok(Frame::ControlFrame { function: Function::try_from(control_field)?, diff --git a/src/user_data/mod.rs b/src/user_data/mod.rs index 120dc63..c52e9ec 100644 --- a/src/user_data/mod.rs +++ b/src/user_data/mod.rs @@ -146,7 +146,9 @@ impl From for Direction { ControlInformation::HashProcedure(_) => Self::MasterToSlave, ControlInformation::SendErrorStatus => Self::SlaveToMaster, ControlInformation::SendAlarmStatus => Self::SlaveToMaster, - ControlInformation::ResponseWithVariableDataStructure => Self::SlaveToMaster, + ControlInformation::ResponseWithVariableDataStructure { lsb_order: _ } => { + Self::SlaveToMaster + } ControlInformation::ResponseWithFixedDataStructure => Self::SlaveToMaster, } } @@ -174,7 +176,7 @@ pub enum ControlInformation { HashProcedure(u8), SendErrorStatus, SendAlarmStatus, - ResponseWithVariableDataStructure, + ResponseWithVariableDataStructure { lsb_order: bool }, ResponseWithFixedDataStructure, } @@ -201,7 +203,9 @@ impl ControlInformation { 0x90..=0x97 => Ok(Self::HashProcedure(byte - 0x90)), 0x70 => Ok(Self::SendErrorStatus), 0x71 => Ok(Self::SendAlarmStatus), - 0x72 | 0x76 => Ok(Self::ResponseWithVariableDataStructure), + 0x72 | 0x76 => Ok(Self::ResponseWithVariableDataStructure { + lsb_order: byte & 0x04 != 0, + }), 0x73 | 0x77 => Ok(Self::ResponseWithFixedDataStructure), _ => Err(ApplicationLayerError::InvalidControlInformation { byte }), } @@ -568,12 +572,16 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> { return Err(ApplicationLayerError::MissingControlInformation); } + println!("Control Information: {:x?}", data); + let control_information = ControlInformation::from( *data .first() .ok_or(ApplicationLayerError::InsufficientData)?, )?; + println!("Control Information: {:?}", control_information); + match control_information { ControlInformation::ResetAtApplicationLevel => { let subcode = ApplicationResetSubcode::from( @@ -600,16 +608,23 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> { ControlInformation::HashProcedure(_) => todo!(), ControlInformation::SendErrorStatus => todo!(), ControlInformation::SendAlarmStatus => todo!(), - ControlInformation::ResponseWithVariableDataStructure => { + ControlInformation::ResponseWithVariableDataStructure { lsb_order } => { let mut iter = data.iter().skip(1); + let mut identification_number_bytes = [ + *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, + *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, + *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, + *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, + ]; + if lsb_order { + identification_number_bytes.reverse(); + } + Ok(UserDataBlock::VariableDataStructure { fixed_data_header: FixedDataHeader { - identification_number: IdentificationNumber::from_bcd_hex_digits([ - *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, - *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, - *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, - *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, - ])?, + identification_number: IdentificationNumber::from_bcd_hex_digits( + identification_number_bytes, + )?, manufacturer: ManufacturerCode::from_id(u16::from_le_bytes([ *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, @@ -817,4 +832,64 @@ mod tests { ); Ok(()) } + + #[test] + fn test_lsb_frame() { + use crate::frames::Frame; + + let lsb_frame: &[u8] = &[ + 0x68, 0x64, 0x64, 0x68, 0x8, 0x7f, 0x76, 0x9, 0x67, 0x1, 0x6, 0x0, 0x0, 0x51, 0x4, + 0x50, 0x0, 0x0, 0x0, 0x2, 0x6c, 0x38, 0x1c, 0xc, 0xf, 0x0, 0x80, 0x87, 0x32, 0x8c, + 0x20, 0xf, 0x0, 0x0, 0x0, 0x0, 0xc, 0x14, 0x13, 0x32, 0x82, 0x58, 0xbc, 0x10, 0x15, + 0x0, 0x25, 0x81, 0x25, 0x8c, 0x20, 0x13, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x30, 0x13, 0x0, + 0x0, 0x1, 0x61, 0x8c, 0x40, 0x13, 0x0, 0x0, 0x16, 0x88, 0xa, 0x3c, 0x1, 0x10, 0xa, + 0x2d, 0x0, 0x80, 0xa, 0x5a, 0x7, 0x18, 0xa, 0x5e, 0x6, 0x53, 0xc, 0x22, 0x0, 0x16, 0x7, + 0x26, 0x3c, 0x22, 0x0, 0x0, 0x33, 0x81, 0x4, 0x7e, 0x0, 0x0, 0x67, 0xc, 0xc, 0x16, + ]; + let non_lsb_frame: &[u8] = &[ + 0x68, 0xc7, 0xc7, 0x68, 0x8, 0x38, 0x72, 0x56, 0x73, 0x23, 0x72, 0x2d, 0x2c, 0x34, 0x4, + 0x87, 0x0, 0x0, 0x0, 0x4, 0xf, 0x7f, 0x1c, 0x1, 0x0, 0x4, 0xff, 0x7, 0x8a, 0xad, 0x8, + 0x0, 0x4, 0xff, 0x8, 0x6, 0xfe, 0x5, 0x0, 0x4, 0x14, 0x4e, 0x55, 0xb, 0x0, 0x84, 0x40, + 0x14, 0x0, 0x0, 0x0, 0x0, 0x84, 0x80, 0x40, 0x14, 0x0, 0x0, 0x0, 0x0, 0x4, 0x22, 0x76, + 0x7f, 0x0, 0x0, 0x34, 0x22, 0x8b, 0x2c, 0x0, 0x0, 0x2, 0x59, 0x61, 0x1b, 0x2, 0x5d, + 0x5f, 0x10, 0x2, 0x61, 0x2, 0xb, 0x4, 0x2d, 0x55, 0x0, 0x0, 0x0, 0x14, 0x2d, 0x83, 0x0, + 0x0, 0x0, 0x4, 0x3b, 0x6, 0x1, 0x0, 0x0, 0x14, 0x3b, 0xaa, 0x1, 0x0, 0x0, 0x4, 0xff, + 0x22, 0x0, 0x0, 0x0, 0x0, 0x4, 0x6d, 0x6, 0x2c, 0x1f, 0x3a, 0x44, 0xf, 0xcf, 0x11, 0x1, + 0x0, 0x44, 0xff, 0x7, 0xb, 0x69, 0x8, 0x0, 0x44, 0xff, 0x8, 0x54, 0xd3, 0x5, 0x0, 0x44, + 0x14, 0x11, 0xf3, 0xa, 0x0, 0xc4, 0x40, 0x14, 0x0, 0x0, 0x0, 0x0, 0xc4, 0x80, 0x40, + 0x14, 0x0, 0x0, 0x0, 0x0, 0x54, 0x2d, 0x3a, 0x0, 0x0, 0x0, 0x54, 0x3b, 0x28, 0x1, 0x0, + 0x0, 0x42, 0x6c, 0x1, 0x3a, 0x2, 0xff, 0x1a, 0x1, 0x1a, 0xc, 0x78, 0x56, 0x73, 0x23, + 0x72, 0x4, 0xff, 0x16, 0xe6, 0x84, 0x1e, 0x0, 0x4, 0xff, 0x17, 0xc1, 0xd5, 0xb4, 0x0, + 0x12, 0x16, + ]; + //println!("lsb_frame: {:x?}", non_lsb_frame); + let frames = [(lsb_frame, 9670106), (non_lsb_frame, 72237356)]; + + for (frame, expected_iden_nr) in frames { + let frame = Frame::try_from(frame).unwrap(); + println!("frame: {:?}", frame); + + if let Frame::LongFrame { + function, + address, + data, + } = frame + { + println!("data: {:x?}", data); + let user_data_block = UserDataBlock::try_from(data).unwrap(); + if let UserDataBlock::VariableDataStructure { + fixed_data_header, + variable_data_block, + } = user_data_block + { + println!("fixed_data_header: {:?}", fixed_data_header); + println!("variable_data_block: {:x?}", variable_data_block); + assert_eq!( + fixed_data_header.identification_number.number, + expected_iden_nr + ); + } + } + } + } } From b88990590a48bc34169ae7fbcf4ad47a22136e9d Mon Sep 17 00:00:00 2001 From: dragonn Date: Thu, 7 Nov 2024 13:01:17 +0100 Subject: [PATCH 2/4] feat: add lsb order decoding in data records --- src/user_data/data_information.rs | 25 ++++++++-- src/user_data/data_record.rs | 77 ++++++++++++++++++----------- src/user_data/mod.rs | 23 +++++++-- src/user_data/variable_user_data.rs | 10 +++- 4 files changed, 97 insertions(+), 38 deletions(-) diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index e041b69..2e0f18f 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -1,5 +1,6 @@ use super::data_information::{self}; use super::variable_user_data::DataRecordError; +use super::FixedDataHeader; #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, PartialEq)] @@ -457,6 +458,7 @@ fn bcd_to_value_internal( data: &[u8], num_digits: usize, sign: i32, + lsb_order: bool, ) -> Result { if data.len() < (num_digits + 1) / 2 { return Err(DataRecordError::InsufficientData); @@ -466,7 +468,12 @@ fn bcd_to_value_internal( let mut current_weight = 1.0; for i in 0..num_digits { - let byte = data.get(i / 2).ok_or(DataRecordError::InsufficientData)?; + let index = if lsb_order { + (num_digits - i - 1) / 2 + } else { + i / 2 + }; + let byte = data.get(index).ok_or(DataRecordError::InsufficientData)?; let digit = if i % 2 == 0 { byte & 0x0F @@ -487,14 +494,22 @@ fn bcd_to_value_internal( } impl DataFieldCoding { - pub fn parse<'a>(&self, input: &'a [u8]) -> Result, DataRecordError> { + pub fn parse<'a>( + &self, + input: &'a [u8], + fixed_data_header: Option<&'a FixedDataHeader>, + ) -> Result, DataRecordError> { + println!("DataFieldCoding: {:?}", self); + println!("Input: {:x?}", input); + let lsb_order = fixed_data_header.map(|x| x.lsb_order).unwrap_or(false); + macro_rules! bcd_to_value { ($data:expr, $num_digits:expr) => {{ - bcd_to_value_internal($data, $num_digits, 1) + bcd_to_value_internal($data, $num_digits, 1, lsb_order) }}; ($data:expr, $num_digits:expr, $sign:expr) => {{ - bcd_to_value_internal($data, $num_digits, $sign) + bcd_to_value_internal($data, $num_digits, $sign, lsb_order) }}; } @@ -875,7 +890,7 @@ mod tests { #[test] fn test_bcd_to_value_unsigned() { let data = [0x54, 0x76, 0x98]; - let result = bcd_to_value_internal(&data, 6, 1); + let result = bcd_to_value_internal(&data, 6, 1, false); assert_eq!( result.unwrap(), Data { diff --git a/src/user_data/data_record.rs b/src/user_data/data_record.rs index 379990d..34ae156 100644 --- a/src/user_data/data_record.rs +++ b/src/user_data/data_record.rs @@ -2,6 +2,7 @@ use super::{ data_information::{Data, DataFieldCoding, DataInformation, DataInformationBlock, DataType}, value_information::{ValueInformation, ValueInformationBlock, ValueLabel}, variable_user_data::DataRecordError, + FixedDataHeader, }; #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, PartialEq)] @@ -29,6 +30,44 @@ impl DataRecord<'_> { } } +impl<'a> DataRecord<'a> { + fn parse( + data: &'a [u8], + fixed_data_header: Option<&'a FixedDataHeader>, + ) -> Result { + let data_record_header = DataRecordHeader::try_from(data)?; + let mut offset = data_record_header + .raw_data_record_header + .data_information_block + .get_size(); + let mut data_out = Data { + value: Some(DataType::ManufacturerSpecific(data)), + size: data.len(), + }; + if let Some(x) = &data_record_header + .raw_data_record_header + .value_information_block + { + offset += x.get_size(); + if let Some(data_info) = &data_record_header + .processed_data_record_header + .data_information + { + data_out = data_info.data_field_coding.parse( + data.get(offset..) + .ok_or(DataRecordError::InsufficientData)?, + fixed_data_header, + )?; + } + } + println!("{:?}", data_out); + Ok(DataRecord { + data_record_header, + data: data_out, + }) + } +} + #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, PartialEq)] pub struct DataRecordHeader<'a> { @@ -121,37 +160,19 @@ impl<'a> TryFrom<&'a [u8]> for DataRecordHeader<'a> { } } +impl<'a> TryFrom<(&'a [u8], &'a FixedDataHeader)> for DataRecord<'a> { + type Error = DataRecordError; + fn try_from( + (data, fixed_data_header): (&'a [u8], &'a FixedDataHeader), + ) -> Result { + Self::parse(data, Some(fixed_data_header)) + } +} + impl<'a> TryFrom<&'a [u8]> for DataRecord<'a> { type Error = DataRecordError; fn try_from(data: &'a [u8]) -> Result { - let data_record_header = DataRecordHeader::try_from(data)?; - let mut offset = data_record_header - .raw_data_record_header - .data_information_block - .get_size(); - let mut data_out = Data { - value: Some(DataType::ManufacturerSpecific(data)), - size: data.len(), - }; - if let Some(x) = &data_record_header - .raw_data_record_header - .value_information_block - { - offset += x.get_size(); - if let Some(data_info) = &data_record_header - .processed_data_record_header - .data_information - { - data_out = data_info.data_field_coding.parse( - data.get(offset..) - .ok_or(DataRecordError::InsufficientData)?, - )?; - } - } - Ok(DataRecord { - data_record_header, - data: data_out, - }) + Self::parse(data, None) } } diff --git a/src/user_data/mod.rs b/src/user_data/mod.rs index c52e9ec..d72eedf 100644 --- a/src/user_data/mod.rs +++ b/src/user_data/mod.rs @@ -17,6 +17,7 @@ pub mod variable_user_data; pub struct DataRecords<'a> { offset: usize, data: &'a [u8], + fixed_data_header: Option<&'a FixedDataHeader>, } #[cfg(feature = "serde")] @@ -44,7 +45,11 @@ impl<'a> Iterator for DataRecords<'a> { self.offset += 1; } _ => { - let record = DataRecord::try_from(self.data.get(self.offset..)?); + let record = if let Some(fixed_data_header) = self.fixed_data_header { + DataRecord::try_from((self.data.get(self.offset..)?, fixed_data_header)) + } else { + DataRecord::try_from(self.data.get(self.offset..)?) + }; if let Ok(record) = record { self.offset += record.get_size(); return Some(Ok(record)); @@ -60,8 +65,12 @@ impl<'a> Iterator for DataRecords<'a> { impl<'a> DataRecords<'a> { #[must_use] - pub const fn new(data: &'a [u8]) -> Self { - DataRecords { offset: 0, data } + pub const fn new(data: &'a [u8], fixed_data_header: Option<&'a FixedDataHeader>) -> Self { + DataRecords { + offset: 0, + data, + fixed_data_header, + } } } @@ -517,6 +526,7 @@ pub struct FixedDataHeader { pub access_number: u8, pub status: StatusField, pub signature: u16, + pub lsb_order: bool, } #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, PartialEq)] @@ -644,6 +654,7 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> { *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, *iter.next().ok_or(ApplicationLayerError::InsufficientData)?, ]), + lsb_order, }, variable_data_block: data .get(13..data.len()) @@ -888,6 +899,12 @@ mod tests { fixed_data_header.identification_number.number, expected_iden_nr ); + + let data_records = + DataRecords::try_from((variable_data_block, &fixed_data_header)).unwrap(); + for record in data_records.flatten() { + println!("record: {:?}", record); + } } } } diff --git a/src/user_data/variable_user_data.rs b/src/user_data/variable_user_data.rs index 6e86fe4..57c20cf 100644 --- a/src/user_data/variable_user_data.rs +++ b/src/user_data/variable_user_data.rs @@ -1,5 +1,5 @@ use super::data_information::{self}; -use super::DataRecords; +use super::{DataRecords, FixedDataHeader}; #[derive(Debug, PartialEq)] pub enum DataRecordError { @@ -20,7 +20,13 @@ impl From for VariableUserDataError { impl<'a> From<&'a [u8]> for DataRecords<'a> { fn from(data: &'a [u8]) -> Self { - DataRecords::new(data) + DataRecords::new(data, None) + } +} + +impl<'a> From<(&'a [u8], &'a FixedDataHeader)> for DataRecords<'a> { + fn from((data, fixed_data_header): (&'a [u8], &'a FixedDataHeader)) -> Self { + DataRecords::new(data, Some(fixed_data_header)) } } From 2086105212bd1c672099573002c8a56948844b21 Mon Sep 17 00:00:00 2001 From: dragonn Date: Thu, 7 Nov 2024 13:15:25 +0100 Subject: [PATCH 3/4] cleanup debug printing --- src/frames/mod.rs | 2 -- src/user_data/data_information.rs | 2 -- src/user_data/data_record.rs | 2 +- src/user_data/mod.rs | 37 ++++++++++++++++--------------- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/frames/mod.rs b/src/frames/mod.rs index 727fa96..e079e0f 100644 --- a/src/frames/mod.rs +++ b/src/frames/mod.rs @@ -160,10 +160,8 @@ impl<'a> TryFrom<&'a [u8]> for Frame<'a> { } let control_field = *data.get(4).ok_or(FrameError::LengthShort)?; - println!("control_field: {:x?}", control_field); let address_field = *data.get(5).ok_or(FrameError::LengthShort)?; - println!("address_field: {:x?}", address_field); match control_field { 0x53 => Ok(Frame::ControlFrame { diff --git a/src/user_data/data_information.rs b/src/user_data/data_information.rs index 2e0f18f..e9d3440 100644 --- a/src/user_data/data_information.rs +++ b/src/user_data/data_information.rs @@ -499,8 +499,6 @@ impl DataFieldCoding { input: &'a [u8], fixed_data_header: Option<&'a FixedDataHeader>, ) -> Result, DataRecordError> { - println!("DataFieldCoding: {:?}", self); - println!("Input: {:x?}", input); let lsb_order = fixed_data_header.map(|x| x.lsb_order).unwrap_or(false); macro_rules! bcd_to_value { diff --git a/src/user_data/data_record.rs b/src/user_data/data_record.rs index 34ae156..0d74bdf 100644 --- a/src/user_data/data_record.rs +++ b/src/user_data/data_record.rs @@ -60,7 +60,7 @@ impl<'a> DataRecord<'a> { )?; } } - println!("{:?}", data_out); + Ok(DataRecord { data_record_header, data: data_out, diff --git a/src/user_data/mod.rs b/src/user_data/mod.rs index d72eedf..366997d 100644 --- a/src/user_data/mod.rs +++ b/src/user_data/mod.rs @@ -582,16 +582,12 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> { return Err(ApplicationLayerError::MissingControlInformation); } - println!("Control Information: {:x?}", data); - let control_information = ControlInformation::from( *data .first() .ok_or(ApplicationLayerError::InsufficientData)?, )?; - println!("Control Information: {:?}", control_information); - match control_information { ControlInformation::ResetAtApplicationLevel => { let subcode = ApplicationResetSubcode::from( @@ -847,6 +843,7 @@ mod tests { #[test] fn test_lsb_frame() { use crate::frames::Frame; + use crate::user_data::data_information::DataType; let lsb_frame: &[u8] = &[ 0x68, 0x64, 0x64, 0x68, 0x8, 0x7f, 0x76, 0x9, 0x67, 0x1, 0x6, 0x0, 0x0, 0x51, 0x4, @@ -873,39 +870,43 @@ mod tests { 0x72, 0x4, 0xff, 0x16, 0xe6, 0x84, 0x1e, 0x0, 0x4, 0xff, 0x17, 0xc1, 0xd5, 0xb4, 0x0, 0x12, 0x16, ]; - //println!("lsb_frame: {:x?}", non_lsb_frame); - let frames = [(lsb_frame, 9670106), (non_lsb_frame, 72237356)]; - for (frame, expected_iden_nr) in frames { + let frames = [ + (lsb_frame, 9670106, Some(DataType::Number(808732.0))), + (non_lsb_frame, 72237356, Some(DataType::Number(568714.0))), + ]; + + for (frame, expected_iden_nr, data_record_value) in frames { let frame = Frame::try_from(frame).unwrap(); - println!("frame: {:?}", frame); if let Frame::LongFrame { - function, - address, + function: _, + address: _, data, } = frame { - println!("data: {:x?}", data); let user_data_block = UserDataBlock::try_from(data).unwrap(); if let UserDataBlock::VariableDataStructure { fixed_data_header, variable_data_block, } = user_data_block { - println!("fixed_data_header: {:?}", fixed_data_header); - println!("variable_data_block: {:x?}", variable_data_block); assert_eq!( fixed_data_header.identification_number.number, expected_iden_nr ); - let data_records = - DataRecords::try_from((variable_data_block, &fixed_data_header)).unwrap(); - for record in data_records.flatten() { - println!("record: {:?}", record); - } + let mut data_records = + DataRecords::try_from((variable_data_block, &fixed_data_header)) + .unwrap() + .flatten(); + data_records.next().unwrap(); + assert_eq!(data_records.next().unwrap().data.value, data_record_value); + } else { + panic!("UserDataBlock is not a variable data structure"); } + } else { + panic!("Frame is not a long frame"); } } } From c88a50a1a906b0964dea0f185a02a26330159617 Mon Sep 17 00:00:00 2001 From: Michael Aebli Date: Fri, 22 Nov 2024 00:01:17 +0100 Subject: [PATCH 4/4] remove white space --- src/frames/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/frames/mod.rs b/src/frames/mod.rs index e079e0f..66c732f 100644 --- a/src/frames/mod.rs +++ b/src/frames/mod.rs @@ -158,11 +158,8 @@ impl<'a> TryFrom<&'a [u8]> for Frame<'a> { if *data.last().ok_or(FrameError::LengthShort)? != 0x16 { return Err(FrameError::InvalidStopByte); } - let control_field = *data.get(4).ok_or(FrameError::LengthShort)?; - let address_field = *data.get(5).ok_or(FrameError::LengthShort)?; - match control_field { 0x53 => Ok(Frame::ControlFrame { function: Function::try_from(control_field)?,