| // 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 num_traits::ToPrimitive; |
| use uwb_uci_packets::{parse_diagnostics_ntf, Packet}; |
| |
| use crate::error::{Error, Result}; |
| use crate::params::fira_app_config_params::UwbAddress; |
| use crate::params::uci_packets::{ |
| ControleeStatus, DataRcvStatusCode, DeviceState, ExtendedAddressDlTdoaRangingMeasurement, |
| ExtendedAddressOwrAoaRangingMeasurement, ExtendedAddressTwoWayRangingMeasurement, |
| FiraComponent, RangingMeasurementType, RawUciMessage, ReasonCode, SessionId, SessionState, |
| ShortAddressDlTdoaRangingMeasurement, ShortAddressOwrAoaRangingMeasurement, |
| ShortAddressTwoWayRangingMeasurement, StatusCode, |
| }; |
| |
| /// enum of all UCI notifications with structured fields. |
| #[derive(Debug, Clone, PartialEq)] |
| pub enum UciNotification { |
| /// CoreNotificationPacket equivalent. |
| Core(CoreNotification), |
| /// SessionNotificationPacket 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, |
| /// uwb_uci_packets::SessionState. |
| session_state: SessionState, |
| /// uwb_uci_packets::Reasoncode. |
| reason_code: ReasonCode, |
| }, |
| /// SessionUpdateControllerMulticastListNtf equivalent. |
| UpdateControllerMulticastList { |
| /// SessionId : u32 |
| session_id: SessionId, |
| /// count of controlees: u8 |
| remaining_multicast_list_size: usize, |
| /// list of controlees. |
| status_list: Vec<ControleeStatus>, |
| }, |
| /// (Short/Extended)Mac()SessionInfoNtf equivalent |
| SessionInfo(SessionRangeData), |
| } |
| |
| /// 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_id: SessionId, |
| |
| /// 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(Vec<ShortAddressOwrAoaRangingMeasurement>), |
| |
| /// OWR for AoA measurement with extended address. |
| ExtendedAddressOwrAoa(Vec<ExtendedAddressOwrAoaRangingMeasurement>), |
| } |
| |
| /// The DATA_RCV packet |
| #[derive(Debug, Clone)] |
| pub struct DataRcvNotification { |
| /// The identifier of the session on which data transfer is happening. |
| pub session_id: SessionId, |
| |
| /// The status of the data rx. |
| pub status: DataRcvStatusCode, |
| |
| /// The sequence number of the data packet. |
| pub uci_sequence_num: u32, |
| |
| /// MacAddress of the sender of the application data. |
| pub source_address: UwbAddress, |
| |
| /// Identifier for the source FiraComponent. |
| pub source_fira_component: FiraComponent, |
| |
| /// Identifier for the destination FiraComponent. |
| pub dest_fira_component: FiraComponent, |
| |
| /// Application Payload Data |
| pub payload: Vec<u8>, |
| } |
| |
| impl TryFrom<uwb_uci_packets::UciDataPacketPacket> for DataRcvNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::UciDataPacketPacket, |
| ) -> std::result::Result<Self, Self::Error> { |
| match evt.specialize() { |
| uwb_uci_packets::UciDataPacketChild::UciDataRcv(evt) => Ok(DataRcvNotification { |
| session_id: evt.get_session_id(), |
| status: evt.get_status(), |
| uci_sequence_num: evt.get_uci_sequence_number(), |
| source_address: UwbAddress::Extended(evt.get_source_mac_address().to_le_bytes()), |
| source_fira_component: evt.get_source_fira_component(), |
| dest_fira_component: evt.get_dest_fira_component(), |
| payload: evt.get_data().to_vec(), |
| }), |
| _ => { |
| error!("Unknown UciData packet: {:?}", evt); |
| 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::UciNotificationPacket> for UciNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::UciNotificationPacket, |
| ) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::UciNotificationChild; |
| match evt.specialize() { |
| UciNotificationChild::CoreNotification(evt) => Ok(Self::Core(evt.try_into()?)), |
| UciNotificationChild::SessionConfigNotification(evt) => { |
| Ok(Self::Session(evt.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()), |
| _ => { |
| error!("Unknown UciNotificationPacket: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::CoreNotificationPacket> for CoreNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::CoreNotificationPacket, |
| ) -> 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 CoreNotificationPacket: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::SessionConfigNotificationPacket> for SessionNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::SessionConfigNotificationPacket, |
| ) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::SessionConfigNotificationChild; |
| match evt.specialize() { |
| SessionConfigNotificationChild::SessionStatusNtf(evt) => Ok(Self::Status { |
| session_id: evt.get_session_id(), |
| session_state: evt.get_session_state(), |
| reason_code: evt.get_reason_code(), |
| }), |
| SessionConfigNotificationChild::SessionUpdateControllerMulticastListNtf(evt) => { |
| Ok(Self::UpdateControllerMulticastList { |
| session_id: evt.get_session_id(), |
| remaining_multicast_list_size: evt.get_remaining_multicast_list_size() as usize, |
| status_list: evt.get_controlee_status().clone(), |
| }) |
| } |
| _ => { |
| error!("Unknown SessionNotificationPacket: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::SessionControlNotificationPacket> for SessionNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::SessionControlNotificationPacket, |
| ) -> std::result::Result<Self, Self::Error> { |
| use uwb_uci_packets::SessionControlNotificationChild; |
| match evt.specialize() { |
| SessionControlNotificationChild::SessionInfoNtf(evt) => evt.try_into(), |
| _ => { |
| error!("Unknown SessionInfoNtfPacket: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| } |
| |
| impl TryFrom<uwb_uci_packets::SessionInfoNtfPacket> for SessionNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::SessionInfoNtfPacket, |
| ) -> std::result::Result<Self, Self::Error> { |
| let raw_ranging_data = evt.clone().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) => { |
| RangingMeasurements::ShortAddressOwrAoa( |
| evt.get_owr_aoa_ranging_measurements().clone(), |
| ) |
| } |
| SessionInfoNtfChild::ExtendedMacOwrAoaSessionInfoNtf(evt) => { |
| RangingMeasurements::ExtendedAddressOwrAoa( |
| evt.get_owr_aoa_ranging_measurements().clone(), |
| ) |
| } |
| SessionInfoNtfChild::ShortMacDlTDoASessionInfoNtf(evt) => { |
| match ShortAddressDlTdoaRangingMeasurement::parse(&evt.clone().to_vec()) { |
| 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.clone().to_vec()) { |
| 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 SessionInfoNtfPacket: {:?}", evt); |
| return Err(Error::Unknown); |
| } |
| }; |
| Ok(Self::SessionInfo(SessionRangeData { |
| sequence_number: evt.get_sequence_number(), |
| session_id: evt.get_session_id(), |
| 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::AndroidNotificationPacket> for UciNotification { |
| type Error = Error; |
| fn try_from( |
| evt: uwb_uci_packets::AndroidNotificationPacket, |
| ) -> 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 AndroidNotificationPacket: {:?}", evt); |
| } |
| Err(Error::Unknown) |
| } |
| } |
| |
| fn vendor_notification(evt: uwb_uci_packets::UciNotificationPacket) -> Result<UciNotification> { |
| Ok(UciNotification::Vendor(RawUciMessage { |
| gid: evt.get_group_id().to_u32().ok_or_else(|| { |
| error!("Failed to get gid from packet: {:?}", evt); |
| Error::Unknown |
| })?, |
| oid: evt.get_opcode().to_u32().ok_or_else(|| { |
| error!("Failed to get opcode from packet: {:?}", evt); |
| Error::Unknown |
| })?, |
| payload: get_vendor_uci_payload(evt)?, |
| })) |
| } |
| |
| fn get_vendor_uci_payload(evt: uwb_uci_packets::UciNotificationPacket) -> 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()), |
| } |
| } |
| _ => { |
| error!("Unknown UciVendor packet: {:?}", evt); |
| Err(Error::Unknown) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::params::uci_packets::OwrAoaStatusCode; |
| |
| #[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, |
| }, |
| ]); |
| let extended_ranging_measurements_copy = extended_ranging_measurements.clone(); |
| assert_eq!(extended_ranging_measurements, extended_ranging_measurements_copy); |
| 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::CoreNotificationPacket::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::CoreNotificationPacket::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_id: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| two_way_ranging_measurements: vec![extended_measurement.clone()], |
| } |
| .build(); |
| let raw_ranging_data = extended_two_way_session_info_ntf.clone().to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtfPacket::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_id: 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_id: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| two_way_ranging_measurements: vec![short_measurement.clone()], |
| } |
| .build(); |
| let raw_ranging_data = short_two_way_session_info_ntf.clone().to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtfPacket::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_id: 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: OwrAoaStatusCode::UciStatusSuccess, |
| 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_id: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| owr_aoa_ranging_measurements: vec![extended_measurement.clone()], |
| } |
| .build(); |
| let raw_ranging_data = extended_owr_aoa_session_info_ntf.clone().to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtfPacket::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_id: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ExtendedAddressOwrAoa(vec![ |
| 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: OwrAoaStatusCode::UciStatusSuccess, |
| 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_id: 0x11, |
| rcr_indicator: 0x12, |
| current_ranging_interval: 0x13, |
| owr_aoa_ranging_measurements: vec![short_measurement.clone()], |
| } |
| .build(); |
| let raw_ranging_data = short_owr_aoa_session_info_ntf.clone().to_vec(); |
| let range_notification = |
| uwb_uci_packets::SessionInfoNtfPacket::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_id: 0x11, |
| ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa, |
| current_ranging_interval_ms: 0x13, |
| ranging_measurements: RangingMeasurements::ShortAddressOwrAoa(vec![ |
| 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_id: 0x20, |
| session_state: uwb_uci_packets::SessionState::SessionStateActive, |
| reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands, |
| } |
| .build(); |
| let session_notification_packet = |
| uwb_uci_packets::SessionConfigNotificationPacket::try_from(session_status_ntf).unwrap(); |
| let session_notification = |
| SessionNotification::try_from(session_notification_packet).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: 0x20, |
| session_state: uwb_uci_packets::SessionState::SessionStateActive, |
| reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands, |
| }) |
| ); |
| } |
| |
| #[test] |
| fn test_session_notification_casting_from_session_update_controller_multicast_list_ntf_packet() |
| { |
| let controlee_status = uwb_uci_packets::ControleeStatus { |
| mac_address: 0xc0a8, |
| subsession_id: 0x30, |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusOkMulticastListUpdate, |
| }; |
| let another_controlee_status = uwb_uci_packets::ControleeStatus { |
| mac_address: 0xc0a9, |
| subsession_id: 0x31, |
| status: uwb_uci_packets::MulticastUpdateStatusCode::StatusErrorKeyFetchFail, |
| }; |
| let session_update_controller_multicast_list_ntf = |
| uwb_uci_packets::SessionUpdateControllerMulticastListNtfBuilder { |
| session_id: 0x32, |
| remaining_multicast_list_size: 0x2, |
| controlee_status: vec![controlee_status.clone(), another_controlee_status.clone()], |
| } |
| .build(); |
| let session_notification_packet = |
| uwb_uci_packets::SessionConfigNotificationPacket::try_from( |
| session_update_controller_multicast_list_ntf, |
| ) |
| .unwrap(); |
| let session_notification = |
| SessionNotification::try_from(session_notification_packet).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::UpdateControllerMulticastList { |
| session_id: 0x32, |
| remaining_multicast_list_size: 0x2, |
| status_list: vec![controlee_status, another_controlee_status], |
| }) |
| ); |
| } |
| |
| #[test] |
| #[allow(non_snake_case)] //override snake case for vendor_A |
| fn test_vendor_notification_casting() { |
| let vendor_9_empty_notification: uwb_uci_packets::UciNotificationPacket = |
| uwb_uci_packets::UciVendor_9_NotificationBuilder { opcode: 0x40, payload: None } |
| .build() |
| .into(); |
| let vendor_A_nonempty_notification: uwb_uci_packets::UciNotificationPacket = |
| uwb_uci_packets::UciVendor_A_NotificationBuilder { |
| opcode: 0x41, |
| payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")), |
| } |
| .build() |
| .into(); |
| let uci_notification_from_vendor_9 = |
| UciNotification::try_from(vendor_9_empty_notification).unwrap(); |
| let uci_notification_from_vendor_A = |
| UciNotification::try_from(vendor_A_nonempty_notification).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(), |
| }) |
| ); |
| } |
| } |