#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReliabilityKind {
BestEffort,
Reliable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DurabilityKind {
Volatile,
TransientLocal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HistoryKind {
KeepLast(u32),
KeepAll,
}
impl HistoryKind {
pub fn depth(&self) -> u32 {
match self {
HistoryKind::KeepLast(n) => *n,
HistoryKind::KeepAll => 0,
}
}
pub fn is_keep_all(&self) -> bool {
matches!(self, HistoryKind::KeepAll)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LivelinessKind {
Automatic,
ManualByTopic,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Duration {
pub sec: i32,
pub nsec: u32,
}
impl Duration {
pub const ZERO: Self = Self { sec: 0, nsec: 0 };
pub const INFINITE: Self = Self {
sec: i32::MAX,
nsec: u32::MAX,
};
pub const fn new(sec: i32, nsec: u32) -> Self {
Self { sec, nsec }
}
pub const fn from_millis(ms: u32) -> Self {
Self {
sec: (ms / 1000) as i32,
nsec: (ms % 1000) * 1_000_000,
}
}
pub fn total_ns(&self) -> i64 {
(self.sec as i64) * 1_000_000_000 + (self.nsec as i64)
}
pub fn is_infinite(&self) -> bool {
self.sec == i32::MAX
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct QosProfile {
pub history: HistoryKind,
pub reliability: ReliabilityKind,
pub durability: DurabilityKind,
pub deadline: Duration,
pub lifespan: Duration,
pub liveliness: LivelinessKind,
pub liveliness_lease: Duration,
pub avoid_ros_namespace: bool,
}
impl QosProfile {
pub const DEFAULT: Self = Self {
history: HistoryKind::KeepLast(10),
reliability: ReliabilityKind::Reliable,
durability: DurabilityKind::Volatile,
deadline: Duration::INFINITE,
lifespan: Duration::INFINITE,
liveliness: LivelinessKind::Automatic,
liveliness_lease: Duration::INFINITE,
avoid_ros_namespace: false,
};
pub const SENSOR_DATA: Self = Self {
history: HistoryKind::KeepLast(5),
reliability: ReliabilityKind::BestEffort,
durability: DurabilityKind::Volatile,
deadline: Duration::INFINITE,
lifespan: Duration::INFINITE,
liveliness: LivelinessKind::Automatic,
liveliness_lease: Duration::INFINITE,
avoid_ros_namespace: false,
};
pub const PARAMETERS: Self = Self {
history: HistoryKind::KeepLast(1000),
reliability: ReliabilityKind::Reliable,
durability: DurabilityKind::TransientLocal,
deadline: Duration::INFINITE,
lifespan: Duration::INFINITE,
liveliness: LivelinessKind::Automatic,
liveliness_lease: Duration::INFINITE,
avoid_ros_namespace: false,
};
pub const SERVICES_DEFAULT: Self = Self {
history: HistoryKind::KeepLast(10),
reliability: ReliabilityKind::Reliable,
durability: DurabilityKind::Volatile,
deadline: Duration::INFINITE,
lifespan: Duration::INFINITE,
liveliness: LivelinessKind::Automatic,
liveliness_lease: Duration::INFINITE,
avoid_ros_namespace: false,
};
pub const BEST_AVAILABLE: Self = Self {
history: HistoryKind::KeepLast(1),
reliability: ReliabilityKind::BestEffort,
durability: DurabilityKind::Volatile,
deadline: Duration::ZERO,
lifespan: Duration::INFINITE,
liveliness: LivelinessKind::Automatic,
liveliness_lease: Duration::INFINITE,
avoid_ros_namespace: false,
};
pub const REAL_TIME: Self = Self {
history: HistoryKind::KeepLast(1),
reliability: ReliabilityKind::BestEffort,
durability: DurabilityKind::Volatile,
deadline: Duration::from_millis(10), lifespan: Duration::from_millis(10),
liveliness: LivelinessKind::Automatic,
liveliness_lease: Duration::INFINITE,
avoid_ros_namespace: false,
};
pub const fn new() -> Self {
Self::DEFAULT
}
pub const fn with_reliability(mut self, r: ReliabilityKind) -> Self {
self.reliability = r;
self
}
pub const fn with_durability(mut self, d: DurabilityKind) -> Self {
self.durability = d;
self
}
pub const fn with_history(mut self, h: HistoryKind) -> Self {
self.history = h;
self
}
pub const fn with_deadline(mut self, d: Duration) -> Self {
self.deadline = d;
self
}
pub const fn with_lifespan(mut self, l: Duration) -> Self {
self.lifespan = l;
self
}
pub fn is_compatible_with_subscriber(&self, subscriber: &QosProfile) -> bool {
let rel_ok = !matches!(
(self.reliability, subscriber.reliability),
(ReliabilityKind::BestEffort, ReliabilityKind::Reliable)
);
let dur_ok = !matches!(
(self.durability, subscriber.durability),
(DurabilityKind::Volatile, DurabilityKind::TransientLocal)
);
rel_ok && dur_ok
}
pub fn history_depth(&self) -> u32 {
self.history.depth()
}
pub fn is_reliable(&self) -> bool {
self.reliability == ReliabilityKind::Reliable
}
pub fn is_transient_local(&self) -> bool {
self.durability == DurabilityKind::TransientLocal
}
}
impl Default for QosProfile {
fn default() -> Self {
Self::DEFAULT
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_preset_sensor_data() {
let qos = QosProfile::SENSOR_DATA;
assert_eq!(qos.reliability, ReliabilityKind::BestEffort);
assert_eq!(qos.durability, DurabilityKind::Volatile);
assert_eq!(qos.history_depth(), 5);
}
#[test]
fn test_preset_parameters() {
let qos = QosProfile::PARAMETERS;
assert_eq!(qos.reliability, ReliabilityKind::Reliable);
assert_eq!(qos.durability, DurabilityKind::TransientLocal);
assert!(qos.is_transient_local());
assert_eq!(qos.history_depth(), 1000);
}
#[test]
fn test_compatibility_reliable_pub_besteffort_sub() {
let pub_qos = QosProfile::DEFAULT; let sub_qos = QosProfile::SENSOR_DATA; assert!(pub_qos.is_compatible_with_subscriber(&sub_qos));
}
#[test]
fn test_incompatibility_besteffort_pub_reliable_sub() {
let pub_qos = QosProfile::SENSOR_DATA; let sub_qos = QosProfile::DEFAULT; assert!(!pub_qos.is_compatible_with_subscriber(&sub_qos));
}
#[test]
fn test_builder_pattern() {
let qos = QosProfile::new()
.with_reliability(ReliabilityKind::BestEffort)
.with_durability(DurabilityKind::TransientLocal)
.with_history(HistoryKind::KeepLast(20));
assert_eq!(qos.reliability, ReliabilityKind::BestEffort);
assert!(qos.is_transient_local());
assert_eq!(qos.history_depth(), 20);
}
#[test]
fn test_duration_from_millis() {
let d = Duration::from_millis(1500);
assert_eq!(d.sec, 1);
assert_eq!(d.nsec, 500_000_000);
let total = d.total_ns();
assert_eq!(total, 1_500_000_000);
}
#[test]
fn test_history_kind() {
let h = HistoryKind::KeepLast(10);
assert_eq!(h.depth(), 10);
assert!(!h.is_keep_all());
assert!(HistoryKind::KeepAll.is_keep_all());
}
#[test]
fn test_durability_incompatibility() {
let pub_qos = QosProfile::DEFAULT; let sub_qos = QosProfile::PARAMETERS; assert!(!pub_qos.is_compatible_with_subscriber(&sub_qos));
}
}