use crate::error::PfcpError;
use crate::ie::{Ie, IeType};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MeasurementInformation {
flags: u8,
}
impl MeasurementInformation {
pub const MBQE: u8 = 0x01;
pub const INAM: u8 = 0x02;
pub const RADI: u8 = 0x04;
pub const ISTM: u8 = 0x08;
pub const MNOP: u8 = 0x10;
pub const SSPOC: u8 = 0x20;
pub const ASPOC: u8 = 0x40;
pub const CIAM: u8 = 0x80;
pub fn new(flags: u8) -> Self {
MeasurementInformation { flags }
}
pub fn flags(&self) -> u8 {
self.flags
}
pub fn mbqe(&self) -> bool {
self.flags & Self::MBQE != 0
}
pub fn inam(&self) -> bool {
self.flags & Self::INAM != 0
}
pub fn radi(&self) -> bool {
self.flags & Self::RADI != 0
}
pub fn istm(&self) -> bool {
self.flags & Self::ISTM != 0
}
pub fn mnop(&self) -> bool {
self.flags & Self::MNOP != 0
}
pub fn sspoc(&self) -> bool {
self.flags & Self::SSPOC != 0
}
pub fn aspoc(&self) -> bool {
self.flags & Self::ASPOC != 0
}
pub fn ciam(&self) -> bool {
self.flags & Self::CIAM != 0
}
pub fn set_mbqe(&mut self) {
self.flags |= Self::MBQE;
}
pub fn clear_mbqe(&mut self) {
self.flags &= !Self::MBQE;
}
pub fn set_inam(&mut self) {
self.flags |= Self::INAM;
}
pub fn clear_inam(&mut self) {
self.flags &= !Self::INAM;
}
pub fn set_radi(&mut self) {
self.flags |= Self::RADI;
}
pub fn clear_radi(&mut self) {
self.flags &= !Self::RADI;
}
pub fn set_istm(&mut self) {
self.flags |= Self::ISTM;
}
pub fn clear_istm(&mut self) {
self.flags &= !Self::ISTM;
}
pub fn set_mnop(&mut self) {
self.flags |= Self::MNOP;
}
pub fn clear_mnop(&mut self) {
self.flags &= !Self::MNOP;
}
pub fn set_sspoc(&mut self) {
self.flags |= Self::SSPOC;
}
pub fn clear_sspoc(&mut self) {
self.flags &= !Self::SSPOC;
}
pub fn set_aspoc(&mut self) {
self.flags |= Self::ASPOC;
}
pub fn clear_aspoc(&mut self) {
self.flags &= !Self::ASPOC;
}
pub fn set_ciam(&mut self) {
self.flags |= Self::CIAM;
}
pub fn clear_ciam(&mut self) {
self.flags &= !Self::CIAM;
}
pub fn marshal(&self) -> Vec<u8> {
vec![self.flags]
}
pub fn unmarshal(data: &[u8]) -> Result<Self, PfcpError> {
if data.is_empty() {
return Err(PfcpError::invalid_length(
"Measurement Information",
IeType::MeasurementInformation,
1,
0,
));
}
Ok(MeasurementInformation { flags: data[0] })
}
pub fn to_ie(&self) -> Ie {
Ie::new(IeType::MeasurementInformation, self.marshal())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_measurement_information_new() {
let mi = MeasurementInformation::new(0x01);
assert_eq!(mi.flags(), 0x01);
}
#[test]
fn test_measurement_information_mbqe() {
let mi = MeasurementInformation::new(MeasurementInformation::MBQE);
assert!(mi.mbqe());
assert!(!mi.inam());
}
#[test]
fn test_measurement_information_inam() {
let mi = MeasurementInformation::new(MeasurementInformation::INAM);
assert!(!mi.mbqe());
assert!(mi.inam());
}
#[test]
fn test_measurement_information_radi() {
let mi = MeasurementInformation::new(MeasurementInformation::RADI);
assert!(mi.radi());
}
#[test]
fn test_measurement_information_istm() {
let mi = MeasurementInformation::new(MeasurementInformation::ISTM);
assert!(mi.istm());
}
#[test]
fn test_measurement_information_mnop() {
let mi = MeasurementInformation::new(MeasurementInformation::MNOP);
assert!(mi.mnop());
}
#[test]
fn test_measurement_information_sspoc() {
let mi = MeasurementInformation::new(MeasurementInformation::SSPOC);
assert!(mi.sspoc());
}
#[test]
fn test_measurement_information_aspoc() {
let mi = MeasurementInformation::new(MeasurementInformation::ASPOC);
assert!(mi.aspoc());
}
#[test]
fn test_measurement_information_ciam() {
let mi = MeasurementInformation::new(MeasurementInformation::CIAM);
assert!(mi.ciam());
}
#[test]
fn test_measurement_information_all_flags() {
let mi = MeasurementInformation::new(0xFF);
assert!(mi.mbqe());
assert!(mi.inam());
assert!(mi.radi());
assert!(mi.istm());
assert!(mi.mnop());
assert!(mi.sspoc());
assert!(mi.aspoc());
assert!(mi.ciam());
}
#[test]
fn test_measurement_information_set_flags() {
let mut mi = MeasurementInformation::new(0x00);
assert!(!mi.mbqe());
mi.set_mbqe();
assert!(mi.mbqe());
assert_eq!(mi.flags(), 0x01);
mi.set_inam();
assert!(mi.mbqe());
assert!(mi.inam());
assert_eq!(mi.flags(), 0x03);
mi.set_mnop();
assert_eq!(mi.flags(), 0x13);
}
#[test]
fn test_measurement_information_clear_flags() {
let mut mi = MeasurementInformation::new(0xFF);
assert!(mi.mbqe());
mi.clear_mbqe();
assert!(!mi.mbqe());
assert_eq!(mi.flags(), 0xFE);
mi.clear_inam();
assert!(!mi.inam());
assert_eq!(mi.flags(), 0xFC);
}
#[test]
fn test_measurement_information_marshal_unmarshal() {
let original = MeasurementInformation::new(0x05); let bytes = original.marshal();
assert_eq!(bytes.len(), 1);
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert_eq!(original, parsed);
assert_eq!(parsed.flags(), 0x05);
}
#[test]
fn test_measurement_information_marshal_all_flags() {
let mi = MeasurementInformation::new(0xFF);
let bytes = mi.marshal();
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert_eq!(mi, parsed);
assert_eq!(parsed.flags(), 0xFF);
}
#[test]
fn test_measurement_information_unmarshal_empty() {
let data = vec![];
let result = MeasurementInformation::unmarshal(&data);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, PfcpError::InvalidLength { .. }));
}
#[test]
fn test_measurement_information_to_ie() {
let mi = MeasurementInformation::new(0x03);
let ie = mi.to_ie();
assert_eq!(ie.ie_type, IeType::MeasurementInformation);
assert_eq!(ie.payload.len(), 1);
let parsed = MeasurementInformation::unmarshal(&ie.payload).unwrap();
assert_eq!(mi, parsed);
}
#[test]
fn test_measurement_information_round_trip_various() {
let values = vec![0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xFF, 0x15];
for flags_val in values {
let original = MeasurementInformation::new(flags_val);
let bytes = original.marshal();
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert_eq!(original, parsed, "Failed for flags 0x{:02x}", flags_val);
}
}
#[test]
fn test_measurement_information_5g_before_qos() {
let mi = MeasurementInformation::new(MeasurementInformation::MBQE);
let bytes = mi.marshal();
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert!(parsed.mbqe());
assert_eq!(mi, parsed);
}
#[test]
fn test_measurement_information_5g_pause_measurement() {
let mi = MeasurementInformation::new(MeasurementInformation::INAM);
let bytes = mi.marshal();
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert!(parsed.inam());
assert_eq!(mi, parsed);
}
#[test]
fn test_measurement_information_5g_combined() {
let mi = MeasurementInformation::new(
MeasurementInformation::MBQE | MeasurementInformation::MNOP,
);
let bytes = mi.marshal();
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert!(parsed.mbqe());
assert!(parsed.mnop());
assert!(!parsed.inam());
assert_eq!(mi, parsed);
}
#[test]
fn test_measurement_information_5g_charging_pause() {
let mi = MeasurementInformation::new(
MeasurementInformation::SSPOC | MeasurementInformation::ASPOC,
);
let bytes = mi.marshal();
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert!(parsed.sspoc());
assert!(parsed.aspoc());
assert_eq!(mi, parsed);
}
#[test]
fn test_measurement_information_5g_inactive_control() {
let mi = MeasurementInformation::new(
MeasurementInformation::ASPOC | MeasurementInformation::CIAM,
);
let bytes = mi.marshal();
let parsed = MeasurementInformation::unmarshal(&bytes).unwrap();
assert!(parsed.aspoc());
assert!(parsed.ciam());
assert_eq!(mi, parsed);
}
}