| // Copyright 2022, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use std::convert::{TryFrom, TryInto}; |
| |
| use log::{debug, error}; |
| use pdl_runtime::Packet; |
| use uwb_uci_packets::{ |
| parse_diagnostics_ntf, radar_bytes_per_sample_value, RadarDataRcv, RadarSweepDataRaw, |
| UCI_PACKET_HEADER_LEN, UCI_RADAR_SEQUENCE_NUMBER_LEN, UCI_RADAR_TIMESTAMP_LEN, |
| UCI_RADAR_VENDOR_DATA_LEN_LEN, |
| }; |
| |
| use crate::error::{Error, Result}; |
| use crate::params::fira_app_config_params::UwbAddress; |
| use crate::params::uci_packets::{ |
| BitsPerSample, ControleeStatusV1, ControleeStatusV2, CreditAvailability, DataRcvStatusCode, |
| DataTransferNtfStatusCode, DataTransferPhaseConfigUpdateStatusCode, DeviceState, |
| ExtendedAddressDlTdoaRangingMeasurement, ExtendedAddressOwrAoaRangingMeasurement, |
| ExtendedAddressTwoWayRangingMeasurement, RadarDataType, RangingMeasurementType, RawUciMessage, |
| SessionId, SessionState, SessionToken, SessionUpdateControllerMulticastListNtfV1Payload, |
| SessionUpdateControllerMulticastListNtfV2Payload, ShortAddressDlTdoaRangingMeasurement, |
| ShortAddressOwrAoaRangingMeasurement, ShortAddressTwoWayRangingMeasurement, StatusCode, |
| UCIMajorVersion, |
| }; |
| |
| /// enum of all UCI notifications with structured fields. |
| #[derive(Debug, Clone, PartialEq)] |
| pub enum UciNotification { |
| /// CoreNotification equivalent. |
| Core(CoreNotification), |
| /// SessionNotification equivalent. |
| Session(SessionNotification), |
| /// UciVendor_X_Notification equivalent. |
| Vendor(RawUciMessage), |
| } |
| |
| /// UCI CoreNotification. |
| #[derive(Debug, Clone, PartialEq, Eq)] |
| pub enum CoreNotification { |
| /// DeviceStatusNtf equivalent. |
| DeviceStatus(DeviceState), |
| /// GenericErrorPacket equivalent. |
| GenericError(StatusCode), |
| } |
| |
| /// UCI SessionNotification. |
| #[derive(Debug, Clone, PartialEq)] |
| pub enum SessionNotification { |
| /// SessionStatusNtf equivalent. |
| Status { |
| /// SessionId : u32 |
| session_id: SessionId, |
| /// SessionToken : u32 |
| session_token: SessionToken, |
| /// uwb_uci_packets::SessionState. |
| session_state: SessionState, |
| /// uwb_uci_packets::Reasoncode. |
| reason_code: u8, |
| }, |
| /// SessionUpdateControllerMulticastListNtfV1 equivalent. |
| UpdateControllerMulticastListV1 { |
| /// SessionToken : u32 |
| session_token: SessionToken, |
| /// count of controlees: u8 |
| remaining_multicast_list_size: usize, |
| /// list of controlees. |
| status_list: Vec<ControleeStatusV1>, |
| }, |
| /// SessionUpdateControllerMulticastListNtfV2 equivalent. |
| UpdateControllerMulticastListV2 { |
| /// SessionToken : u32 |
| session_token: SessionToken, |
| /// list of controlees. |
| status_list: Vec<ControleeStatusV2>, |
| }, |
| /// (Short/Extended)Mac()SessionInfoNtf equivalent |
| SessionInfo(SessionRangeData), |
| /// DataCreditNtf equivalent. |
| DataCredit { |
| /// SessionToken : u32 |
| session_token: SessionToken, |
| /// Credit Availability (for sending Data packets on UWB Session) |
| credit_availability: CreditAvailability, |
| }, |
| /// DataTransferStatusNtf equivalent. |
| DataTransferStatus { |
| /// SessionToken : u32 |
| session_token: SessionToken, |
| /// Sequence Number: u16 |
| uci_sequence_number: u16, |
| /// Data Transfer Status Code |
| status: DataTransferNtfStatusCode, |
| /// Transmission count |
| tx_count: u8, |
| }, |
| /// SessionDataTransferPhaseConfigNtf equivalent. |
| DataTransferPhaseConfig { |
| /// SessionToken : u32 |
| session_token: SessionToken, |
| /// status |
| status: DataTransferPhaseConfigUpdateStatusCode, |
| }, |
| } |
| |
| /// The session range data. |
| #[derive(Debug, Clone, PartialEq)] |
| pub struct SessionRangeData { |
| /// The sequence counter that starts with 0 when the session is started. |
| pub sequence_number: u32, |
| |
| /// The identifier of the session. |
| pub session_token: SessionToken, |
| |
| /// The current ranging interval setting in the unit of ms. |
| pub current_ranging_interval_ms: u32, |
| |
| /// The ranging measurement type. |
| pub ranging_measurement_type: RangingMeasurementType, |
| |
| /// The ranging measurement data. |
| pub ranging_measurements: RangingMeasurements, |
| |
| /// Indication that a RCR was sent/received in the current ranging round. |
| pub rcr_indicator: u8, |
| |
| /// The raw data of the notification message. |
| /// (b/243555651): It's not at FiRa specification, only used by vendor's extension. |
| pub raw_ranging_data: Vec<u8>, |
| } |
| |
| /// The ranging measurements. |
| #[derive(Debug, Clone, PartialEq)] |
| pub enum RangingMeasurements { |
| /// A Two-Way measurement with short address. |
| ShortAddressTwoWay(Vec<ShortAddressTwoWayRangingMeasurement>), |
| |
| /// A Two-Way measurement with extended address. |
| ExtendedAddressTwoWay(Vec<ExtendedAddressTwoWayRangingMeasurement>), |
| |
| /// Dl-TDoA measurement with short address. |
| ShortAddressDltdoa(Vec<ShortAddressDlTdoaRangingMeasurement>), |
| |
| /// Dl-TDoA measurement with extended address. |
| ExtendedAddressDltdoa(Vec<ExtendedAddressDlTdoaRangingMeasurement>), |
| |
| /// OWR for AoA measurement with short address. |
| ShortAddressOwrAoa(ShortAddressOwrAoaRangingMeasurement), |
| |
| /// OWR for AoA measurement with extended address. |
| ExtendedAddressOwrAoa(ExtendedAddressOwrAoaRangingMeasurement), |
| } |
| |
| /// The DATA_RCV packet |
| #[derive(Debug, Clone, std::cmp::PartialEq)] |
| pub struct DataRcvNotification { |
| /// The identifier of the session on which data transfer is happening. |
| pub session_token: SessionToken, |
| |
| /// The status of the data rx. |
| pub status: StatusCode, |
| |
| /// The sequence number of the data packet. |
| pub uci_sequence_num: u16, |
| |
| /// MacAddress of the sender of the application data. |
| pub source_address: UwbAddress, |
| |
| /// Application Payload Data |
| pub payload: Vec<u8>, |
| } |
| |
| /// The Radar sweep data struct |
| #[derive(Debug, Clone, std::cmp::PartialEq)] |
| pub struct RadarSweepData { |
| /// Counter of a single radar sweep per receiver. Starting |
| /// with 0 when the radar session is started. |
| pub sequence_number: u32, |
| |
| /// Timestamp when this radar sweep is received. Unit is |
| /// based on the PRF. |
| pub timestamp: u32, |
| |
| /// The radar vendor specific data. |
| pub vendor_specific_data: Vec<u8>, |
| |
| /// The radar sample data. |
| pub sample_data: Vec<u8>, |
| } |
| |
| /// The RADAR_DATA_RCV packet |
| #[derive(Debug, Clone, std::cmp::PartialEq)] |
| pub struct RadarDataRcvNotification { |
| /// The identifier of the session on which radar data transfer is happening. |
| pub session_token: SessionToken, |
| |
| /// The status of the radar data rx. |
| pub status: DataRcvStatusCode, |
| |
| /// The radar data type. |
| pub radar_data_type: RadarDataType, |
| |
| /// The number of sweeps. |
| pub number_of_sweeps: u8, |
| |
| /// Number of samples captured for each radar sweep. |
| pub samples_per_sweep: u8, |
| |
| /// Bits per sample in the radar sweep. |
| pub bits_per_sample: BitsPerSample, |
| |
| /// Defines the start offset with respect to 0cm distance. Unit in samples. |
| pub sweep_offset: u16, |
| |
| /// Radar sweep data. |
| pub sweep_data: Vec<RadarSweepData>, |
| } |
| |
| impl From<&uwb_uci_packets::RadarSweepDataRaw> for RadarSweepData { |
| fn from(evt: &uwb_uci_packets::RadarSweepDataRaw) -> Self { |
| Self { |
| sequence_number: evt.sequence_number, |
| timestamp: evt.timestamp, |
| vendor_specific_data: evt.vendor_specific_data.clone(), |
| sample_data: evt.sample_data.clone(), |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::UciDataPacket> for RadarDataRcvNotification { |
| type Error = Error; |
| fn try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error> { |
| match evt.specialize() { |
| uwb_uci_packets::UciDataPacketChild::RadarDataRcv(evt) => parse_radar_data(evt), |
| _ => Err(Error::Unknown), |
| } |
| } |
| } |
| |
| fn parse_radar_data(data: RadarDataRcv) -> Result<RadarDataRcvNotification> { |
| let session_token = data.get_session_handle(); |
| let status = data.get_status(); |
| let radar_data_type = data.get_radar_data_type(); |
| let number_of_sweeps = data.get_number_of_sweeps(); |
| let samples_per_sweep = data.get_samples_per_sweep(); |
| let bits_per_sample = data.get_bits_per_sample(); |
| let bytes_per_sample_value = radar_bytes_per_sample_value(bits_per_sample); |
| let sweep_offset = data.get_sweep_offset(); |
| |
| Ok(RadarDataRcvNotification { |
| session_token, |
| status, |
| radar_data_type, |
| number_of_sweeps, |
| samples_per_sweep, |
| bits_per_sample, |
| sweep_offset, |
| sweep_data: parse_radar_sweep_data( |
| number_of_sweeps, |
| samples_per_sweep, |
| bytes_per_sample_value, |
| data.get_sweep_data().clone(), |
| )?, |
| }) |
| } |
| |
| fn parse_radar_sweep_data( |
| number_of_sweeps: u8, |
| samples_per_sweep: u8, |
| bytes_per_sample_value: u8, |
| data: Vec<u8>, |
| ) -> Result<Vec<RadarSweepData>> { |
| let mut radar_sweep_data: Vec<RadarSweepData> = Vec::new(); |
| let mut sweep_data_cursor = 0; |
| for _ in 0..number_of_sweeps { |
| let vendor_data_len_index = |
| sweep_data_cursor + UCI_RADAR_SEQUENCE_NUMBER_LEN + UCI_RADAR_TIMESTAMP_LEN; |
| if data.len() <= vendor_data_len_index { |
| error!("Invalid radar sweep data length for vendor, data: {:?}", &data); |
| return Err(Error::BadParameters); |
| } |
| let vendor_specific_data_len = data[vendor_data_len_index] as usize; |
| let sweep_data_len = UCI_RADAR_SEQUENCE_NUMBER_LEN |
| + UCI_RADAR_TIMESTAMP_LEN |
| + UCI_RADAR_VENDOR_DATA_LEN_LEN |
| + vendor_specific_data_len |
| + samples_per_sweep as usize * bytes_per_sample_value as usize; |
| if data.len() < sweep_data_cursor + sweep_data_len { |
| error!("Invalid radar sweep data length, data: {:?}", &data); |
| return Err(Error::BadParameters); |
| } |
| radar_sweep_data.push( |
| (&RadarSweepDataRaw::parse( |
| &data[sweep_data_cursor..sweep_data_cursor + sweep_data_len], |
| ) |
| .map_err(|e| { |
| error!("Failed to parse raw Radar Sweep Data {:?}, data: {:?}", e, &data); |
| Error::BadParameters |
| })?) |
| .into(), |
| ); |
| |
| sweep_data_cursor += sweep_data_len; |
| } |
| |
| Ok(radar_sweep_data) |
| } |
| |
| impl TryFrom<uwb_uci_packets::UciDataPacket> for DataRcvNotification { |
| type Error = Error; |
| fn try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error> { |
| match evt.specialize() { |
| uwb_uci_packets::UciDataPacketChild::UciDataRcv(evt) => Ok(DataRcvNotification { |
| session_token: evt.get_session_token(), |
| status: evt.get_status(), |
| uci_sequence_num: evt.get_uci_sequence_number(), |
| source_address: UwbAddress::Extended(evt.get_source_mac_address().to_le_bytes()), |
| payload: evt.get_data().to_vec(), |
| }), |
| _ => Err(Error::Unknown), |
| } |
| } |
| } |
| |
| impl UciNotification { |
| pub(crate) fn need_retry(&self) -> bool { |
| matches!( |
| self, |
| Self::Core(CoreNotification::GenericError(StatusCode::UciStatusCommandRetry)) |
| ) |
| } |
| } |
| |
| impl TryFrom<(uwb_uci_packets::UciNotification, UCIMajorVersion, bool)> for UciNotification { |
| type Error = Error; |
| fn try_from( |
| pair: (uwb_uci_packets::UciNotification, UCIMajorVersion, bool), |
| ) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::UciNotificationChild; |
| let evt = pair.0; |
| let uci_fira_major_ver = pair.1; |
| let is_multicast_list_ntf_v2_supported = pair.2; |
| |
| match evt.specialize() { |
| UciNotificationChild::CoreNotification(evt) => Ok(Self::Core(evt.try_into()?)), |
| UciNotificationChild::SessionConfigNotification(evt) => Ok(Self::Session( |
| (evt, uci_fira_major_ver, is_multicast_list_ntf_v2_supported).try_into()?, |
| )), |
| UciNotificationChild::SessionControlNotification(evt) => { |
| Ok(Self::Session(evt.try_into()?)) |
| } |
| UciNotificationChild::AndroidNotification(evt) => evt.try_into(), |
| UciNotificationChild::UciVendor_9_Notification(evt) => vendor_notification(evt.into()), |
| UciNotificationChild::UciVendor_A_Notification(evt) => vendor_notification(evt.into()), |
| UciNotificationChild::UciVendor_B_Notification(evt) => vendor_notification(evt.into()), |
| UciNotificationChild::UciVendor_E_Notification(evt) => vendor_notification(evt.into()), |
| UciNotificationChild::UciVendor_F_Notification(evt) => vendor_notification(evt.into()), |
| UciNotificationChild::TestNotification(evt) => vendor_notification(evt.into()), |
| _ => { |
| error!("Unknown UciNotification: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::CoreNotification> for CoreNotification { |
| type Error = Error; |
| fn try_from(evt: uwb_uci_packets::CoreNotification) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::CoreNotificationChild; |
| match evt.specialize() { |
| CoreNotificationChild::DeviceStatusNtf(evt) => { |
| Ok(Self::DeviceStatus(evt.get_device_state())) |
| } |
| CoreNotificationChild::GenericError(evt) => Ok(Self::GenericError(evt.get_status())), |
| _ => { |
| error!("Unknown CoreNotification: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<(uwb_uci_packets::SessionConfigNotification, UCIMajorVersion, bool)> |
| for SessionNotification |
| { |
| type Error = Error; |
| fn try_from( |
| pair: (uwb_uci_packets::SessionConfigNotification, UCIMajorVersion, bool), |
| ) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::SessionConfigNotificationChild; |
| let evt = pair.0; |
| let uci_fira_major_ver = pair.1; |
| let is_multicast_list_ntf_v2_supported = pair.2; |
| match evt.specialize() { |
| SessionConfigNotificationChild::SessionStatusNtf(evt) => Ok(Self::Status { |
| //no sessionId recieved, assign from sessionIdToToken map in uci_manager |
| session_id: 0, |
| session_token: evt.get_session_token(), |
| session_state: evt.get_session_state(), |
| reason_code: evt.get_reason_code(), |
| }), |
| SessionConfigNotificationChild::SessionUpdateControllerMulticastListNtf(evt) |
| if uci_fira_major_ver == UCIMajorVersion::V1 |
| || !is_multicast_list_ntf_v2_supported => |
| { |
| let payload = evt.get_payload(); |
| let multicast_update_list_payload_v1 = |
| SessionUpdateControllerMulticastListNtfV1Payload::parse(payload).map_err( |
| |e| { |
| error!( |
| "Failed to parse Multicast list ntf v1 {:?}, payload: {:?}", |
| e, &payload |
| ); |
| Error::BadParameters |
| }, |
| )?; |
| Ok(Self::UpdateControllerMulticastListV1 { |
| session_token: evt.get_session_token(), |
| remaining_multicast_list_size: multicast_update_list_payload_v1 |
| .remaining_multicast_list_size |
| as usize, |
| status_list: multicast_update_list_payload_v1.controlee_status, |
| }) |
| } |
| SessionConfigNotificationChild::SessionUpdateControllerMulticastListNtf(evt) |
| if uci_fira_major_ver == UCIMajorVersion::V2 => |
| { |
| let payload = evt.get_payload(); |
| let multicast_update_list_payload_v2 = |
| SessionUpdateControllerMulticastListNtfV2Payload::parse(payload).map_err( |
| |e| { |
| error!( |
| "Failed to parse Multicast list ntf v2 {:?}, payload: {:?}", |
| e, &payload |
| ); |
| Error::BadParameters |
| }, |
| )?; |
| Ok(Self::UpdateControllerMulticastListV2 { |
| session_token: evt.get_session_token(), |
| status_list: multicast_update_list_payload_v2.controlee_status, |
| }) |
| } |
| SessionConfigNotificationChild::SessionDataTransferPhaseConfigNtf(evt) => { |
| Ok(Self::DataTransferPhaseConfig { |
| session_token: evt.get_session_token(), |
| status: evt.get_status(), |
| }) |
| } |
| _ => { |
| error!("Unknown SessionConfigNotification: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::SessionControlNotification> for SessionNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::SessionControlNotification, |
| ) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::SessionControlNotificationChild; |
| match evt.specialize() { |
| SessionControlNotificationChild::SessionInfoNtf(evt) => evt.try_into(), |
| SessionControlNotificationChild::DataCreditNtf(evt) => Ok(Self::DataCredit { |
| session_token: evt.get_session_token(), |
| credit_availability: evt.get_credit_availability(), |
| }), |
| SessionControlNotificationChild::DataTransferStatusNtf(evt) => { |
| Ok(Self::DataTransferStatus { |
| session_token: evt.get_session_token(), |
| uci_sequence_number: evt.get_uci_sequence_number(), |
| status: evt.get_status(), |
| tx_count: evt.get_tx_count(), |
| }) |
| } |
| _ => { |
| error!("Unknown SessionControlNotification: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::SessionInfoNtf> for SessionNotification { |
| type Error = Error; |
| fn try_from(evt: uwb_uci_packets::SessionInfoNtf) -> std::result::Result<Self, Self::Error> { |
| let raw_ranging_data = evt.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); |
| use uwb_uci_packets::SessionInfoNtfChild; |
| let ranging_measurements = match evt.specialize() { |
| SessionInfoNtfChild::ShortMacTwoWaySessionInfoNtf(evt) => { |
| RangingMeasurements::ShortAddressTwoWay( |
| evt.get_two_way_ranging_measurements().clone(), |
| ) |
| } |
| SessionInfoNtfChild::ExtendedMacTwoWaySessionInfoNtf(evt) => { |
| RangingMeasurements::ExtendedAddressTwoWay( |
| evt.get_two_way_ranging_measurements().clone(), |
| ) |
| } |
| SessionInfoNtfChild::ShortMacOwrAoaSessionInfoNtf(evt) => { |
| if evt.get_owr_aoa_ranging_measurements().clone().len() == 1 { |
| RangingMeasurements::ShortAddressOwrAoa( |
| match evt.get_owr_aoa_ranging_measurements().clone().pop() { |
| Some(r) => r, |
| None => { |
| error!( |
| "Unable to parse ShortAddress OwrAoA measurement: {:?}", |
| evt |
| ); |
| return Err(Error::BadParameters); |
| } |
| }, |
| ) |
| } else { |
| error!("Wrong count of OwrAoA ranging measurements {:?}", evt); |
| return Err(Error::BadParameters); |
| } |
| } |
| SessionInfoNtfChild::ExtendedMacOwrAoaSessionInfoNtf(evt) => { |
| if evt.get_owr_aoa_ranging_measurements().clone().len() == 1 { |
| RangingMeasurements::ExtendedAddressOwrAoa( |
| match evt.get_owr_aoa_ranging_measurements().clone().pop() { |
| Some(r) => r, |
| None => { |
| error!( |
| "Unable to parse ExtendedAddress OwrAoA measurement: {:?}", |
| evt |
| ); |
| return Err(Error::BadParameters); |
| } |
| }, |
| ) |
| } else { |
| error!("Wrong count of OwrAoA ranging measurements {:?}", evt); |
| return Err(Error::BadParameters); |
| } |
| } |
| SessionInfoNtfChild::ShortMacDlTDoASessionInfoNtf(evt) => { |
| match ShortAddressDlTdoaRangingMeasurement::parse( |
| evt.get_dl_tdoa_measurements(), |
| evt.get_no_of_ranging_measurements(), |
| ) { |
| Some(v) => { |
| if v.len() == evt.get_no_of_ranging_measurements().into() { |
| RangingMeasurements::ShortAddressDltdoa(v) |
| } else { |
| error!("Wrong count of ranging measurements {:?}", evt); |
| return Err(Error::BadParameters); |
| } |
| } |
| None => return Err(Error::BadParameters), |
| } |
| } |
| SessionInfoNtfChild::ExtendedMacDlTDoASessionInfoNtf(evt) => { |
| match ExtendedAddressDlTdoaRangingMeasurement::parse( |
| evt.get_dl_tdoa_measurements(), |
| evt.get_no_of_ranging_measurements(), |
| ) { |
| Some(v) => { |
| if v.len() == evt.get_no_of_ranging_measurements().into() { |
| RangingMeasurements::ExtendedAddressDltdoa(v) |
| } else { |
| error!("Wrong count of ranging measurements {:?}", evt); |
| return Err(Error::BadParameters); |
| } |
| } |
| None => return Err(Error::BadParameters), |
| } |
| } |
| _ => { |
| error!("Unknown SessionInfoNtf: {:?}", evt); |
| return Err(Error::Unknown); |
| } |
| }; |
| Ok(Self::SessionInfo(SessionRangeData { |
| sequence_number: evt.get_sequence_number(), |
| session_token: evt.get_session_token(), |
| current_ranging_interval_ms: evt.get_current_ranging_interval(), |
| ranging_measurement_type: evt.get_ranging_measurement_type(), |
| ranging_measurements, |
| rcr_indicator: evt.get_rcr_indicator(), |
| raw_ranging_data, |
| })) |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::AndroidNotification> for UciNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::AndroidNotification, |
| ) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::AndroidNotificationChild; |
| |
| // (b/241336806): Currently we don't process the diagnostic packet, just log it only. |
| if let AndroidNotificationChild::AndroidRangeDiagnosticsNtf(ntf) = evt.specialize() { |
| debug!("Received diagnostic packet: {:?}", parse_diagnostics_ntf(ntf)); |
| } else { |
| error!("Received unknown AndroidNotification: {:?}", evt); |
| } |
| Err(Error::Unknown) |
| } |
| } |
| |
| fn vendor_notification(evt: uwb_uci_packets::UciNotification) -> Result<UciNotification> { |
| Ok(UciNotification::Vendor(RawUciMessage { |
| gid: evt.get_group_id().into(), |
| oid: evt.get_opcode().into(), |
| payload: get_vendor_uci_payload(evt)?, |
| })) |
| } |
| |
| fn get_vendor_uci_payload(evt: uwb_uci_packets::UciNotification) -> Result<Vec<u8>> { |
| match evt.specialize() { |
| uwb_uci_packets::UciNotificationChild::UciVendor_9_Notification(evt) => { |
| match evt.specialize() { |
| uwb_uci_packets::UciVendor_9_NotificationChild::Payload(payload) => { |
| Ok(payload.to_vec()) |
| } |
| uwb_uci_packets::UciVendor_9_NotificationChild::None => Ok(Vec::new()), |
| } |
| } |
| uwb_uci_packets::UciNotificationChild::UciVendor_A_Notification(evt) => { |
| match evt.specialize() { |
| uwb_uci_packets::UciVendor_A_NotificationChild::Payload(payload) => { |
| Ok(payload.to_vec()) |
| } |
| uwb_uci_packets::UciVendor_A_NotificationChild::None => Ok(Vec::new()), |
| } |
| } |
| uwb_uci_packets::UciNotificationChild::UciVendor_B_Notification(evt) => { |
| match evt.specialize() { |
| uwb_uci_packets::UciVendor_B_NotificationChild::Payload(payload) => { |
| Ok(payload.to_vec()) |
| } |
| uwb_uci_packets::UciVendor_B_NotificationChild::None => Ok(Vec::new()), |
| } |
| } |
| uwb_uci_packets::UciNotificationChild::UciVendor_E_Notification(evt) => { |
| match evt.specialize() { |
| uwb_uci_packets::UciVendor_E_NotificationChild::Payload(payload) => { |
| Ok(payload.to_vec()) |
| } |
| uwb_uci_packets::UciVendor_E_NotificationChild::None => Ok(Vec::new()), |
| } |
| } |
| uwb_uci_packets::UciNotificationChild::UciVendor_F_Notification(evt) => { |
| match evt.specialize() { |
| uwb_uci_packets::UciVendor_F_NotificationChild::Payload(payload) => { |
| Ok(payload.to_vec()) |
| } |
| uwb_uci_packets::UciVendor_F_NotificationChild::None => Ok(Vec::new()), |
| } |
| } |
| uwb_uci_packets::UciNotificationChild::TestNotification(evt) => match evt.specialize() { |
| uwb_uci_packets::TestNotificationChild::Payload(payload) => Ok(payload.to_vec()), |
| uwb_uci_packets::TestNotificationChild::None => Ok(Vec::new()), |
| }, |
| _ => { |
| error!("Unknown UciVendor packet: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use bytes::{BufMut, BytesMut}; |
| |
| #[test] |
| fn test_ranging_measurements_trait() { |
| let empty_short_ranging_measurements = RangingMeasurements::ShortAddressTwoWay(vec![]); |
| assert_eq!(empty_short_ranging_measurements, empty_short_ranging_measurements); |
| let extended_ranging_measurements = RangingMeasurements::ExtendedAddressTwoWay(vec![ |
| ExtendedAddressTwoWayRangingMeasurement { |
| mac_address: 0x1234_5678_90ab, |
| status: StatusCode::UciStatusOk, |
| nlos: 0, |
| distance: 4, |
| aoa_azimuth: 5, |
| aoa_azimuth_fom: 6, |
| aoa_elevation: 7, |
| aoa_elevation_fom: 8, |
| aoa_destination_azimuth: 9, |
| aoa_destination_azimuth_fom: 10, |
| aoa_destination_elevation: 11, |
| aoa_destination_elevation_fom: 12, |
| slot_index: 0, |
| rssi: u8::MAX, |
| }, |
| ]); |
| assert_eq!(extended_ranging_measurements, extended_ranging_measurements.clone()); |
| let empty_extended_ranging_measurements = |
| RangingMeasurements::ExtendedAddressTwoWay(vec![]); |
| assert_eq!(empty_short_ranging_measurements, empty_short_ranging_measurements); |
| //short and extended measurements are unequal even if both are empty: |
| assert_ne!(empty_short_ranging_measurements, empty_extended_ranging_measurements); |
| } |
| #[test] |
| fn test_core_notification_casting_from_generic_error() { |
| let generic_error_packet = uwb_uci_packets::GenericErrorBuilder { |
| status: uwb_uci_packets::StatusCode::UciStatusRejected, |
| } |
| .build(); |
| let core_notification = |
| uwb_uci_packets::CoreNotification::try_from(generic_error_packet).unwrap(); |
| let core_notification = CoreNotification::try_from(core_notification).unwrap(); |
| let uci_notification_from_generic_error = UciNotification::Core(core_notification); |
| assert_eq!( |
| uci_notification_from_generic_error, |
| UciNotification::Core(CoreNotification::GenericError( |
| uwb_uci_packets::StatusCode::UciStatusRejected |
| )) |
| ); |
| } |
| #[test] |
| fn test_core_notification_casting_from_device_status_ntf() { |
| let device_status_ntf_packet = uwb_uci_packets::DeviceStatusNtfBuilder { |
| device_state: uwb_uci_packets::DeviceState::DeviceStateActive, |
| } |
| .build(); |
| let core_notification = |
| uwb_uci_packets::CoreNotification::try_from(device_status_ntf_packet).unwrap(); |
| let uci_notification = CoreNotification::try_from(core_notification).unwrap(); |
| let uci_notification_from_device_status_ntf = UciNotification::Core(uci_notification); |
| assert_eq!( |
| uci_notification_from_device_status_ntf, |
| UciNotification::Core(CoreNotification::DeviceStatus( |
| uwb_uci_packets::DeviceState::DeviceStateActive |
| )) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_extended_mac_two_way_session_info_ntf() { |
| let extended_measurement = uwb_uci_packets::ExtendedAddressTwoWayRangingMeasurement { |
| mac_address: 0x1234_5678_90ab, |
| status: StatusCode::UciStatusOk, |
| nlos: 0, |
| distance: 4, |
| aoa_azimuth: 5, |
| aoa_azimuth_fom: 6, |
| aoa_elevation: 7, |
| aoa_elevation_fom: 8, |
| aoa_destination_azimuth: 9, |
| aoa_destination_azimuth_fom: 10, |
| aoa_destination_elevation: 11, |
| aoa_destination_elevation_fom: 12, |
| slot_index: 0, |
| rssi: u8::MAX, |
| }; |
| let extended_two_way_session_info_ntf = |
| uwb_uci_packets::ExtendedMacTwoWaySessionInfoNtfBuilder { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| two_way_ranging_measurements: vec![extended_measurement.clone()], |
| vendor_data: vec![], |
| } |
| .build(); |
| let raw_ranging_data = |
| extended_two_way_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtf::try_from(extended_two_way_session_info_ntf).unwrap(); |
| let session_notification = SessionNotification::try_from(range_notification).unwrap(); |
| let uci_notification_from_extended_two_way_session_info_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_extended_two_way_session_info_ntf, |
| UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::TwoWay, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ExtendedAddressTwoWay(vec![ |
| extended_measurement |
| ]), |
| rcr_indicator: 0x12, |
| raw_ranging_data, |
| })) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_short_mac_two_way_session_info_ntf() { |
| let short_measurement = uwb_uci_packets::ShortAddressTwoWayRangingMeasurement { |
| mac_address: 0x1234, |
| status: StatusCode::UciStatusOk, |
| nlos: 0, |
| distance: 4, |
| aoa_azimuth: 5, |
| aoa_azimuth_fom: 6, |
| aoa_elevation: 7, |
| aoa_elevation_fom: 8, |
| aoa_destination_azimuth: 9, |
| aoa_destination_azimuth_fom: 10, |
| aoa_destination_elevation: 11, |
| aoa_destination_elevation_fom: 12, |
| slot_index: 0, |
| rssi: u8::MAX, |
| }; |
| let short_two_way_session_info_ntf = uwb_uci_packets::ShortMacTwoWaySessionInfoNtfBuilder { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| two_way_ranging_measurements: vec![short_measurement.clone()], |
| vendor_data: vec![0x02, 0x01], |
| } |
| .build(); |
| let raw_ranging_data = |
| short_two_way_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtf::try_from(short_two_way_session_info_ntf).unwrap(); |
| let session_notification = SessionNotification::try_from(range_notification).unwrap(); |
| let uci_notification_from_short_two_way_session_info_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_short_two_way_session_info_ntf, |
| UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::TwoWay, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ShortAddressTwoWay(vec![ |
| short_measurement |
| ]), |
| rcr_indicator: 0x12, |
| raw_ranging_data, |
| })) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_extended_mac_owr_aoa_session_info_ntf() { |
| let extended_measurement = uwb_uci_packets::ExtendedAddressOwrAoaRangingMeasurement { |
| mac_address: 0x1234_5678_90ab, |
| status: StatusCode::UciStatusOk, |
| nlos: 0, |
| frame_sequence_number: 1, |
| block_index: 1, |
| aoa_azimuth: 5, |
| aoa_azimuth_fom: 6, |
| aoa_elevation: 7, |
| aoa_elevation_fom: 8, |
| }; |
| let extended_owr_aoa_session_info_ntf = |
| uwb_uci_packets::ExtendedMacOwrAoaSessionInfoNtfBuilder { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| owr_aoa_ranging_measurements: vec![extended_measurement.clone()], |
| vendor_data: vec![], |
| } |
| .build(); |
| let raw_ranging_data = |
| extended_owr_aoa_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtf::try_from(extended_owr_aoa_session_info_ntf).unwrap(); |
| let session_notification = SessionNotification::try_from(range_notification).unwrap(); |
| let uci_notification_from_extended_owr_aoa_session_info_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_extended_owr_aoa_session_info_ntf, |
| UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ExtendedAddressOwrAoa( |
| extended_measurement |
| ), |
| rcr_indicator: 0x12, |
| raw_ranging_data, |
| })) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_short_mac_owr_aoa_session_info_ntf() { |
| let short_measurement = uwb_uci_packets::ShortAddressOwrAoaRangingMeasurement { |
| mac_address: 0x1234, |
| status: StatusCode::UciStatusOk, |
| nlos: 0, |
| frame_sequence_number: 1, |
| block_index: 1, |
| aoa_azimuth: 5, |
| aoa_azimuth_fom: 6, |
| aoa_elevation: 7, |
| aoa_elevation_fom: 8, |
| }; |
| let short_owr_aoa_session_info_ntf = uwb_uci_packets::ShortMacOwrAoaSessionInfoNtfBuilder { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| owr_aoa_ranging_measurements: vec![short_measurement.clone()], |
| vendor_data: vec![], |
| } |
| .build(); |
| let raw_ranging_data = |
| short_owr_aoa_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtf::try_from(short_owr_aoa_session_info_ntf).unwrap(); |
| let session_notification = SessionNotification::try_from(range_notification).unwrap(); |
| let uci_notification_from_short_owr_aoa_session_info_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_short_owr_aoa_session_info_ntf, |
| UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ShortAddressOwrAoa(short_measurement), |
| rcr_indicator: 0x12, |
| raw_ranging_data, |
| })) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_session_status_ntf() { |
| let session_status_ntf = uwb_uci_packets::SessionStatusNtfBuilder { |
| session_token: 0x20, |
| session_state: uwb_uci_packets::SessionState::SessionStateActive, |
| reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands |
| .into(), |
| } |
| .build(); |
| let session_notification_packet = |
| uwb_uci_packets::SessionConfigNotification::try_from(session_status_ntf).unwrap(); |
| let uci_fira_major_version = UCIMajorVersion::V1; |
| let session_notification = SessionNotification::try_from(( |
| session_notification_packet, |
| uci_fira_major_version, |
| false, |
| )) |
| .unwrap(); |
| let uci_notification_from_session_status_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_session_status_ntf, |
| UciNotification::Session(SessionNotification::Status { |
| session_id: 0x0, |
| session_token: 0x20, |
| session_state: uwb_uci_packets::SessionState::SessionStateActive, |
| reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands |
| .into(), |
| }) |
| ); |
| } |
| |
| fn write_multicast_ntf_v1_payload( |
| payload: &SessionUpdateControllerMulticastListNtfV1Payload, |
| buffer: &mut BytesMut, |
| ) { |
| buffer.put_u8(payload.remaining_multicast_list_size); |
| buffer.put_u8(payload.controlee_status.len() as u8); |
| for elem in &payload.controlee_status { |
| write_v1_controlee_status(elem, buffer); |
| } |
| } |
| |
| fn write_v1_controlee_status(status: &ControleeStatusV1, buffer: &mut BytesMut) { |
| for elem in &status.mac_address { |
| buffer.put_u8(*elem); |
| } |
| buffer.put_u32_le(status.subsession_id); |
| buffer.put_u8(u8::from(status.status)); |
| } |
| |
| fn write_multicast_ntf_v2_payload( |
| payload: &SessionUpdateControllerMulticastListNtfV2Payload, |
| buffer: &mut BytesMut, |
| ) { |
| buffer.put_u8(payload.controlee_status.len() as u8); |
| for elem in &payload.controlee_status { |
| write_v2_controlee_status(elem, buffer); |
| } |
| } |
| |
| fn write_v2_controlee_status(status: &ControleeStatusV2, buffer: &mut BytesMut) { |
| for elem in &status.mac_address { |
| buffer.put_u8(*elem); |
| } |
| buffer.put_u8(u8::from(status.status)); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_session_update_controller_multicast_list_ntf_v1_packet( |
| ) { |
| let controlee_status_v1 = uwb_uci_packets::ControleeStatusV1 { |
| mac_address: [0x0c, 0xa8], |
| subsession_id: 0x30, |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate, |
| }; |
| let another_controlee_status_v1 = uwb_uci_packets::ControleeStatusV1 { |
| mac_address: [0x0c, 0xa9], |
| subsession_id: 0x31, |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusErrorKeyFetchFail, |
| }; |
| let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV1Payload { |
| remaining_multicast_list_size: 0x2, |
| controlee_status: vec![ |
| controlee_status_v1.clone(), |
| another_controlee_status_v1.clone(), |
| ], |
| }; |
| let mut buf = BytesMut::new(); |
| write_multicast_ntf_v1_payload(&payload, &mut buf); |
| let session_update_controller_multicast_list_ntf_v1 = |
| uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder { |
| session_token: 0x32, |
| payload: Some(buf.freeze()), |
| } |
| .build(); |
| let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from( |
| session_update_controller_multicast_list_ntf_v1, |
| ) |
| .unwrap(); |
| let uci_fira_major_version = UCIMajorVersion::V1; |
| let session_notification = SessionNotification::try_from(( |
| session_notification_packet, |
| uci_fira_major_version, |
| false, |
| )) |
| .unwrap(); |
| let uci_notification_from_session_update_controller_multicast_list_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_session_update_controller_multicast_list_ntf, |
| UciNotification::Session(SessionNotification::UpdateControllerMulticastListV1 { |
| session_token: 0x32, |
| remaining_multicast_list_size: 0x2, |
| status_list: vec![controlee_status_v1, another_controlee_status_v1], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn test_cast_failed_from_session_update_controller_multicast_list_ntf_v1_packet_v2_payload() { |
| let controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 { |
| mac_address: [0x0c, 0xa8], |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate, |
| }; |
| let another_controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 { |
| mac_address: [0x0c, 0xa9], |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusErrorKeyFetchFail, |
| }; |
| let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV2Payload { |
| controlee_status: vec![controlee_status_v2, another_controlee_status_v2], |
| }; |
| let mut buf = BytesMut::new(); |
| write_multicast_ntf_v2_payload(&payload, &mut buf); |
| let session_update_controller_multicast_list_ntf_v1 = |
| uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder { |
| session_token: 0x32, |
| payload: Some(buf.freeze()), |
| } |
| .build(); |
| let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from( |
| session_update_controller_multicast_list_ntf_v1, |
| ) |
| .unwrap(); |
| let uci_fira_major_version = UCIMajorVersion::V1; |
| let session_notification = SessionNotification::try_from(( |
| session_notification_packet, |
| uci_fira_major_version, |
| false, |
| )); |
| assert_eq!(session_notification, Err(Error::BadParameters)); |
| } |
| |
| #[test] |
| fn test_cast_failed_from_session_update_controller_multicast_list_ntf_v2_packet_v1_payload() { |
| let controlee_status_v1 = uwb_uci_packets::ControleeStatusV1 { |
| mac_address: [0x0c, 0xa8], |
| subsession_id: 0x30, |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate, |
| }; |
| let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV1Payload { |
| remaining_multicast_list_size: 0x4, |
| controlee_status: vec![controlee_status_v1], |
| }; |
| let mut buf = BytesMut::new(); |
| write_multicast_ntf_v1_payload(&payload, &mut buf); |
| let session_update_controller_multicast_list_ntf_v1 = |
| uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder { |
| session_token: 0x32, |
| payload: Some(buf.freeze()), |
| } |
| .build(); |
| let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from( |
| session_update_controller_multicast_list_ntf_v1, |
| ) |
| .unwrap(); |
| let uci_fira_major_version = UCIMajorVersion::V2; |
| let session_notification = SessionNotification::try_from(( |
| session_notification_packet, |
| uci_fira_major_version, |
| true, |
| )); |
| assert_eq!(session_notification, Err(Error::BadParameters)); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_session_update_controller_multicast_list_ntf_v2_packet( |
| ) { |
| let controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 { |
| mac_address: [0x0c, 0xa8], |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate, |
| }; |
| let another_controlee_status_v2 = uwb_uci_packets::ControleeStatusV2 { |
| mac_address: [0x0c, 0xa9], |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusErrorKeyFetchFail, |
| }; |
| let payload = uwb_uci_packets::SessionUpdateControllerMulticastListNtfV2Payload { |
| controlee_status: vec![ |
| controlee_status_v2.clone(), |
| another_controlee_status_v2.clone(), |
| ], |
| }; |
| let mut buf = BytesMut::new(); |
| write_multicast_ntf_v2_payload(&payload, &mut buf); |
| let session_update_controller_multicast_list_ntf_v2 = |
| uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder { |
| session_token: 0x32, |
| payload: Some(buf.freeze()), |
| } |
| .build(); |
| let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from( |
| session_update_controller_multicast_list_ntf_v2, |
| ) |
| .unwrap(); |
| let uci_fira_major_version = UCIMajorVersion::V2; |
| let session_notification = SessionNotification::try_from(( |
| session_notification_packet, |
| uci_fira_major_version, |
| true, |
| )) |
| .unwrap(); |
| let uci_notification_from_session_update_controller_multicast_list_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_session_update_controller_multicast_list_ntf, |
| UciNotification::Session(SessionNotification::UpdateControllerMulticastListV2 { |
| session_token: 0x32, |
| status_list: vec![controlee_status_v2, another_controlee_status_v2], |
| }) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_session_data_transfer_phase_config_ntf_packet() { |
| let session_data_transfer_phase_config_ntf = |
| uwb_uci_packets::SessionDataTransferPhaseConfigNtfBuilder { |
| session_token: 0x32, |
| status: DataTransferPhaseConfigUpdateStatusCode::UciDtpcmConfigSuccessStatusOk, |
| } |
| .build(); |
| let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from( |
| session_data_transfer_phase_config_ntf, |
| ) |
| .unwrap(); |
| let uci_fira_major_version = UCIMajorVersion::V1; |
| let session_notification = SessionNotification::try_from(( |
| session_notification_packet, |
| uci_fira_major_version, |
| false, |
| )) |
| .unwrap(); |
| let uci_notification_from_session_data_transfer_phase_config_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_session_data_transfer_phase_config_ntf, |
| UciNotification::Session(SessionNotification::DataTransferPhaseConfig { |
| session_token: 0x32, |
| status: DataTransferPhaseConfigUpdateStatusCode::UciDtpcmConfigSuccessStatusOk |
| }) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_short_mac_dl_tdoa_session_info_ntf_packet() { |
| let dl_tdoa_measurements = vec![ |
| 0x0a, 0x01, 0x33, 0x05, // 2(Mac address), Status, Message Type |
| 0x53, 0x05, 0x02, 0x05, // 2(Message control), 2(Block Index) |
| 0x07, 0x09, 0x0a, 0x01, // Round Index, NLoS, 2(AoA Azimuth) |
| 0x02, 0x05, 0x07, 0x09, // AoA Azimuth FOM, 2(AoA Elevation), AoA Elevation FOM |
| 0x0a, 0x01, 0x02, 0x05, // RSSI, 3(Tx Timestamp..) |
| 0x07, 0x09, 0x0a, 0x01, // 4(Tx Timestamp..) |
| 0x02, 0x05, 0x07, 0x09, // Tx Timestamp, 3(Rx Timestamp..) |
| 0x05, 0x07, 0x09, 0x0a, // 2(Rx Timestamp), 2(Anchor Cfo) |
| 0x01, 0x02, 0x05, 0x07, // 2(Cfo), 2(Initiator Reply Time..) |
| 0x09, 0x05, 0x07, 0x09, // 2(Initiator Reply Time), 2(Responder Reply Time..) |
| 0x0a, 0x01, 0x02, 0x05, // 2(Responder Reply Time), 2(Initiator-Responder ToF) |
| 0x07, 0x09, 0x07, 0x09, // 4(Anchor Location..) |
| 0x05, 0x07, 0x09, 0x0a, // 4(Anchor Location..) |
| 0x01, 0x02, 0x05, 0x07, // 2(Anchor Location..), 2(Active Ranging Rounds..) |
| 0x09, 0x0a, 0x01, 0x02, // 4(Active Ranging Rounds..) |
| 0x05, 0x07, 0x09, 0x05, // 4(Active Ranging Rounds) |
| ]; |
| let short_mac_dl_tdoa_session_info_ntf = |
| uwb_uci_packets::ShortMacDlTDoASessionInfoNtfBuilder { |
| current_ranging_interval: 0x13, |
| dl_tdoa_measurements: dl_tdoa_measurements.clone(), |
| no_of_ranging_measurements: 1, |
| rcr_indicator: 0x12, |
| sequence_number: 0x10, |
| session_token: 0x11, |
| } |
| .build(); |
| let raw_ranging_data = |
| short_mac_dl_tdoa_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); |
| let short_measurement = |
| ShortAddressDlTdoaRangingMeasurement::parse(&dl_tdoa_measurements, 1).unwrap(); |
| let range_notification_packet = |
| uwb_uci_packets::SessionInfoNtf::try_from(short_mac_dl_tdoa_session_info_ntf).unwrap(); |
| let session_notification = |
| SessionNotification::try_from(range_notification_packet).unwrap(); |
| let uci_notification_from_short_mac_dl_tdoa_session_info_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_short_mac_dl_tdoa_session_info_ntf, |
| UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::DlTdoa, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ShortAddressDltdoa(short_measurement), |
| rcr_indicator: 0x12, |
| raw_ranging_data, |
| })) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_extended_mac_dltdoa_session_info_ntf_packet() { |
| let dl_tdoa_measurements = vec![ |
| // All Fields in Little Endian (LE) |
| 0x0a, 0x01, 0x33, 0x05, // 4(Mac address..) |
| 0x33, 0x05, 0x02, 0x05, // 4(Mac address) |
| 0x07, 0x09, 0x0a, 0x01, // Status, Message Type, 2(Message control), |
| 0x02, 0x05, 0x07, 0x09, // 2(Block Index), Round Index, NLoS, |
| 0x0a, 0x01, 0x02, 0x05, // 2(AoA Azimuth), AoA Azimuth FOM, 1(AoA Elevation..) |
| 0x07, 0x09, 0x0a, // 1(AoA Elevation), AoA Elevation FOM, RSSI, |
| 0x01, 0x02, 0x05, 0x07, // 4(Tx Timestamp..) |
| 0x09, 0x05, 0x07, 0x09, // 4(Tx Timestamp), |
| 0x0a, 0x01, 0x02, 0x05, // 4(Rx Timestamp..) |
| 0x07, 0x09, 0x05, 0x07, // 4(Rx Timestamp) |
| 0x09, 0x0a, 0x01, 0x02, // 2(Anchor Cfo), 2(Cfo), |
| 0x05, 0x07, 0x09, 0x05, // 4(Initiator Reply Time) |
| 0x07, 0x09, 0x0a, 0x01, // 4(Responder Reply Time), |
| 0x02, 0x05, 0x02, 0x05, // 2(Initiator-Responder ToF), 2(Active Ranging Rounds) |
| ]; |
| let extended_mac_dl_tdoa_session_info_ntf = |
| uwb_uci_packets::ExtendedMacDlTDoASessionInfoNtfBuilder { |
| current_ranging_interval: 0x13, |
| dl_tdoa_measurements: dl_tdoa_measurements.clone(), |
| no_of_ranging_measurements: 1, |
| rcr_indicator: 0x12, |
| sequence_number: 0x10, |
| session_token: 0x11, |
| } |
| .build(); |
| let raw_ranging_data = extended_mac_dl_tdoa_session_info_ntf.clone().to_bytes() |
| [UCI_PACKET_HEADER_LEN..] |
| .to_vec(); |
| let short_measurement = |
| ExtendedAddressDlTdoaRangingMeasurement::parse(&dl_tdoa_measurements, 1).unwrap(); |
| let range_notification_packet = |
| uwb_uci_packets::SessionInfoNtf::try_from(extended_mac_dl_tdoa_session_info_ntf) |
| .unwrap(); |
| let session_notification = |
| SessionNotification::try_from(range_notification_packet).unwrap(); |
| let uci_notification_from_extended_mac_dl_tdoa_session_info_ntf = |
| UciNotification::Session(session_notification); |
| assert_eq!( |
| uci_notification_from_extended_mac_dl_tdoa_session_info_ntf, |
| UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { |
| sequence_number: 0x10, |
| session_token: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::DlTdoa, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ExtendedAddressDltdoa(short_measurement), |
| rcr_indicator: 0x12, |
| raw_ranging_data, |
| })) |
| ); |
| } |
| |
| #[test] |
| #[allow(non_snake_case)] //override snake case for vendor_A |
| fn test_vendor_notification_casting() { |
| let vendor_9_empty_notification: uwb_uci_packets::UciNotification = |
| uwb_uci_packets::UciVendor_9_NotificationBuilder { opcode: 0x40, payload: None } |
| .build() |
| .into(); |
| let vendor_A_nonempty_notification: uwb_uci_packets::UciNotification = |
| uwb_uci_packets::UciVendor_A_NotificationBuilder { |
| opcode: 0x41, |
| payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")), |
| } |
| .build() |
| .into(); |
| let vendor_B_nonempty_notification: uwb_uci_packets::UciNotification = |
| uwb_uci_packets::UciVendor_B_NotificationBuilder { |
| opcode: 0x41, |
| payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")), |
| } |
| .build() |
| .into(); |
| let vendor_E_nonempty_notification: uwb_uci_packets::UciNotification = |
| uwb_uci_packets::UciVendor_E_NotificationBuilder { |
| opcode: 0x41, |
| payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")), |
| } |
| .build() |
| .into(); |
| let vendor_F_nonempty_notification: uwb_uci_packets::UciNotification = |
| uwb_uci_packets::UciVendor_F_NotificationBuilder { |
| opcode: 0x41, |
| payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")), |
| } |
| .build() |
| .into(); |
| let uci_fira_major_version = UCIMajorVersion::V1; |
| let uci_notification_from_vendor_9 = UciNotification::try_from(( |
| vendor_9_empty_notification, |
| uci_fira_major_version.clone(), |
| false, |
| )) |
| .unwrap(); |
| let uci_notification_from_vendor_A = UciNotification::try_from(( |
| vendor_A_nonempty_notification, |
| uci_fira_major_version.clone(), |
| false, |
| )) |
| .unwrap(); |
| let uci_notification_from_vendor_B = UciNotification::try_from(( |
| vendor_B_nonempty_notification, |
| uci_fira_major_version.clone(), |
| false, |
| )) |
| .unwrap(); |
| let uci_notification_from_vendor_E = UciNotification::try_from(( |
| vendor_E_nonempty_notification, |
| uci_fira_major_version.clone(), |
| false, |
| )) |
| .unwrap(); |
| let uci_notification_from_vendor_F = UciNotification::try_from(( |
| vendor_F_nonempty_notification, |
| uci_fira_major_version, |
| false, |
| )) |
| .unwrap(); |
| assert_eq!( |
| uci_notification_from_vendor_9, |
| UciNotification::Vendor(RawUciMessage { |
| gid: 0x9, // per enum GroupId in uci_packets.pdl |
| oid: 0x40, |
| payload: vec![], |
| }) |
| ); |
| assert_eq!( |
| uci_notification_from_vendor_A, |
| UciNotification::Vendor(RawUciMessage { |
| gid: 0xa, |
| oid: 0x41, |
| payload: b"Placeholder notification.".to_owned().into(), |
| }) |
| ); |
| assert_eq!( |
| uci_notification_from_vendor_B, |
| UciNotification::Vendor(RawUciMessage { |
| gid: 0xb, |
| oid: 0x41, |
| payload: b"Placeholder notification.".to_owned().into(), |
| }) |
| ); |
| assert_eq!( |
| uci_notification_from_vendor_E, |
| UciNotification::Vendor(RawUciMessage { |
| gid: 0xe, |
| oid: 0x41, |
| payload: b"Placeholder notification.".to_owned().into(), |
| }) |
| ); |
| assert_eq!( |
| uci_notification_from_vendor_F, |
| UciNotification::Vendor(RawUciMessage { |
| gid: 0xf, |
| oid: 0x41, |
| payload: b"Placeholder notification.".to_owned().into(), |
| }) |
| ); |
| } |
| |
| #[test] |
| fn test_test_to_vendor_notification_casting() { |
| let test_notification: uwb_uci_packets::UciNotification = |
| uwb_uci_packets::TestNotificationBuilder { opcode: 0x22, payload: None }.build().into(); |
| let uci_fira_major_version = UCIMajorVersion::V1; |
| let test_uci_notification = |
| UciNotification::try_from((test_notification, uci_fira_major_version, false)).unwrap(); |
| assert_eq!( |
| test_uci_notification, |
| UciNotification::Vendor(RawUciMessage { |
| gid: 0x0d, // per enum Test GroupId in uci_packets.pdl |
| oid: 0x22, |
| payload: vec![], |
| }) |
| ); |
| } |
| } |