use super::data_lifecycle::{ReaderDataLifecycleQosPolicy, WriterDataLifecycleQosPolicy};
use super::deadline::DeadlineQosPolicy;
use super::destination_order::DestinationOrderQosPolicy;
use super::durability::DurabilityQosPolicy;
use super::durability_service::DurabilityServiceQosPolicy;
use super::generic_data::{GroupDataQosPolicy, TopicDataQosPolicy, UserDataQosPolicy};
use super::history::HistoryQosPolicy;
use super::latency_budget::LatencyBudgetQosPolicy;
use super::lifespan::LifespanQosPolicy;
use super::liveliness::LivelinessQosPolicy;
use super::ownership::OwnershipQosPolicy;
use super::ownership_strength::OwnershipStrengthQosPolicy;
use super::partition::PartitionQosPolicy;
use super::presentation::PresentationQosPolicy;
use super::reliability::ReliabilityQosPolicy;
use super::resource_limits::ResourceLimitsQosPolicy;
use super::time_based_filter::TimeBasedFilterQosPolicy;
use super::transport_priority::TransportPriorityQosPolicy;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WriterQos {
pub durability: DurabilityQosPolicy,
pub durability_service: DurabilityServiceQosPolicy,
pub deadline: DeadlineQosPolicy,
pub latency_budget: LatencyBudgetQosPolicy,
pub liveliness: LivelinessQosPolicy,
pub reliability: ReliabilityQosPolicy,
pub destination_order: DestinationOrderQosPolicy,
pub history: HistoryQosPolicy,
pub resource_limits: ResourceLimitsQosPolicy,
pub transport_priority: TransportPriorityQosPolicy,
pub lifespan: LifespanQosPolicy,
pub ownership: OwnershipQosPolicy,
pub ownership_strength: OwnershipStrengthQosPolicy,
pub presentation: PresentationQosPolicy,
pub partition: PartitionQosPolicy,
pub writer_data_lifecycle: WriterDataLifecycleQosPolicy,
pub user_data: UserDataQosPolicy,
pub topic_data: TopicDataQosPolicy,
pub group_data: GroupDataQosPolicy,
}
impl Default for WriterQos {
fn default() -> Self {
Self {
durability: DurabilityQosPolicy::default(),
durability_service: DurabilityServiceQosPolicy::default(),
deadline: DeadlineQosPolicy::default(),
latency_budget: LatencyBudgetQosPolicy::default(),
liveliness: LivelinessQosPolicy::default(),
reliability: ReliabilityQosPolicy {
kind: super::reliability::ReliabilityKind::Reliable,
max_blocking_time: crate::duration::Duration::from_millis(100),
},
destination_order: DestinationOrderQosPolicy::default(),
history: HistoryQosPolicy::default(),
resource_limits: ResourceLimitsQosPolicy::default(),
transport_priority: TransportPriorityQosPolicy::default(),
lifespan: LifespanQosPolicy::default(),
ownership: OwnershipQosPolicy::default(),
ownership_strength: OwnershipStrengthQosPolicy::default(),
presentation: PresentationQosPolicy::default(),
partition: PartitionQosPolicy::default(),
writer_data_lifecycle: WriterDataLifecycleQosPolicy::default(),
user_data: UserDataQosPolicy::default(),
topic_data: TopicDataQosPolicy::default(),
group_data: GroupDataQosPolicy::default(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ReaderQos {
pub durability: DurabilityQosPolicy,
pub deadline: DeadlineQosPolicy,
pub latency_budget: LatencyBudgetQosPolicy,
pub liveliness: LivelinessQosPolicy,
pub reliability: ReliabilityQosPolicy,
pub destination_order: DestinationOrderQosPolicy,
pub history: HistoryQosPolicy,
pub resource_limits: ResourceLimitsQosPolicy,
pub ownership: OwnershipQosPolicy,
pub time_based_filter: TimeBasedFilterQosPolicy,
pub presentation: PresentationQosPolicy,
pub partition: PartitionQosPolicy,
pub reader_data_lifecycle: ReaderDataLifecycleQosPolicy,
pub user_data: UserDataQosPolicy,
pub topic_data: TopicDataQosPolicy,
pub group_data: GroupDataQosPolicy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum InconsistentReason {
HistoryDepth,
ResourceLimits,
FilterVsDeadline,
HistoryDepthExceedsResourceLimit,
ExclusiveOwnershipWithoutStrength,
}
impl WriterQos {
pub fn check_consistency(&self) -> Result<(), InconsistentReason> {
if matches!(self.history.kind, super::history::HistoryKind::KeepLast)
&& self.history.depth <= 0
{
return Err(InconsistentReason::HistoryDepth);
}
if !self.resource_limits.is_consistent() {
return Err(InconsistentReason::ResourceLimits);
}
if matches!(self.history.kind, super::history::HistoryKind::KeepLast)
&& self.resource_limits.max_samples_per_instance >= 0
&& self.history.depth > self.resource_limits.max_samples_per_instance
{
return Err(InconsistentReason::HistoryDepthExceedsResourceLimit);
}
Ok(())
}
}
impl ReaderQos {
pub fn check_consistency(&self) -> Result<(), InconsistentReason> {
if matches!(self.history.kind, super::history::HistoryKind::KeepLast)
&& self.history.depth <= 0
{
return Err(InconsistentReason::HistoryDepth);
}
if !self.resource_limits.is_consistent() {
return Err(InconsistentReason::ResourceLimits);
}
if self.time_based_filter.minimum_separation > self.deadline.period {
return Err(InconsistentReason::FilterVsDeadline);
}
if matches!(self.history.kind, super::history::HistoryKind::KeepLast)
&& self.resource_limits.max_samples_per_instance >= 0
&& self.history.depth > self.resource_limits.max_samples_per_instance
{
return Err(InconsistentReason::HistoryDepthExceedsResourceLimit);
}
Ok(())
}
}
use alloc::vec::Vec;
use crate::compatibility::{CompatibilityResult, IncompatibleReason};
#[must_use]
pub fn check_compatibility(offered: &WriterQos, requested: &ReaderQos) -> CompatibilityResult {
let mut reasons: Vec<IncompatibleReason> = Vec::new();
if !offered.durability.is_compatible_with(requested.durability) {
reasons.push(IncompatibleReason::Durability);
}
if !offered
.reliability
.is_compatible_with(requested.reliability)
{
reasons.push(IncompatibleReason::Reliability);
}
if !offered.deadline.is_compatible_with(requested.deadline) {
reasons.push(IncompatibleReason::Deadline);
}
if !offered
.latency_budget
.is_compatible_with(requested.latency_budget)
{
reasons.push(IncompatibleReason::LatencyBudget);
}
if !offered.liveliness.is_compatible_with(requested.liveliness) {
reasons.push(IncompatibleReason::Liveliness);
}
if !offered
.destination_order
.is_compatible_with(requested.destination_order)
{
reasons.push(IncompatibleReason::DestinationOrder);
}
if !offered
.presentation
.is_compatible_with(requested.presentation)
{
reasons.push(IncompatibleReason::Presentation);
}
if !offered.ownership.is_compatible_with(requested.ownership) {
reasons.push(IncompatibleReason::Ownership);
}
if !offered.partition.is_compatible_with(&requested.partition) {
reasons.push(IncompatibleReason::Partition);
}
CompatibilityResult::from_reasons(reasons)
}
#[cfg(test)]
#[allow(clippy::panic, clippy::field_reassign_with_default)]
mod tests {
use super::*;
use crate::duration::Duration;
use crate::policies::{DurabilityKind, ReliabilityKind};
#[test]
fn defaults_are_compatible() {
let writer = WriterQos::default();
let reader = ReaderQos::default();
assert!(check_compatibility(&writer, &reader).is_compatible());
}
#[test]
fn reliable_writer_besteffort_reader_is_compatible() {
let mut writer = WriterQos::default();
writer.reliability.kind = ReliabilityKind::Reliable;
let reader = ReaderQos::default();
assert!(check_compatibility(&writer, &reader).is_compatible());
}
#[test]
fn besteffort_writer_reliable_reader_is_incompatible() {
let mut writer = WriterQos::default();
writer.reliability.kind = ReliabilityKind::BestEffort;
let mut reader = ReaderQos::default();
reader.reliability.kind = ReliabilityKind::Reliable;
let res = check_compatibility(&writer, &reader);
assert!(!res.is_compatible());
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::Reliability));
}
CompatibilityResult::Compatible => panic!("expected incompatible"),
}
}
#[test]
fn volatile_writer_transient_local_reader_fails_durability() {
let writer = WriterQos::default();
let mut reader = ReaderQos::default();
reader.durability.kind = DurabilityKind::TransientLocal;
let res = check_compatibility(&writer, &reader);
assert!(!res.is_compatible());
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::Durability));
}
CompatibilityResult::Compatible => panic!("expected incompatible"),
}
}
#[test]
fn multiple_incompatibilities_are_all_reported() {
let mut writer = WriterQos::default();
writer.reliability.kind = ReliabilityKind::BestEffort;
let mut reader = ReaderQos::default();
reader.durability.kind = DurabilityKind::Transient;
reader.reliability.kind = ReliabilityKind::Reliable;
reader.deadline.period = Duration::ZERO;
let res = check_compatibility(&writer, &reader);
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::Durability));
assert!(reasons.contains(&IncompatibleReason::Reliability));
assert!(reasons.contains(&IncompatibleReason::Deadline));
}
CompatibilityResult::Compatible => panic!("expected incompatible"),
}
}
use super::super::history::HistoryKind;
use super::super::resource_limits::ResourceLimitsQosPolicy;
#[test]
fn writer_default_is_consistent() {
assert!(WriterQos::default().check_consistency().is_ok());
}
#[test]
fn writer_keep_last_zero_depth_inconsistent() {
let mut w = WriterQos::default();
w.history.kind = HistoryKind::KeepLast;
w.history.depth = 0;
assert_eq!(w.check_consistency(), Err(InconsistentReason::HistoryDepth));
w.history.depth = -1;
assert_eq!(w.check_consistency(), Err(InconsistentReason::HistoryDepth));
}
#[test]
fn writer_keep_all_depth_zero_is_ok() {
let mut w = WriterQos::default();
w.history.kind = HistoryKind::KeepAll;
w.history.depth = 0;
assert!(w.check_consistency().is_ok());
}
#[test]
fn writer_resource_limits_inconsistent() {
let mut w = WriterQos::default();
w.resource_limits = ResourceLimitsQosPolicy {
max_samples: 10,
max_instances: 5,
max_samples_per_instance: 100,
};
assert_eq!(
w.check_consistency(),
Err(InconsistentReason::ResourceLimits)
);
}
#[test]
fn reader_default_is_consistent() {
assert!(ReaderQos::default().check_consistency().is_ok());
}
#[test]
fn reader_keep_last_negative_depth_inconsistent() {
let mut r = ReaderQos::default();
r.history.kind = HistoryKind::KeepLast;
r.history.depth = -7;
assert_eq!(r.check_consistency(), Err(InconsistentReason::HistoryDepth));
}
#[test]
fn reader_resource_limits_inconsistent() {
let mut r = ReaderQos::default();
r.resource_limits = ResourceLimitsQosPolicy {
max_samples: 2,
max_instances: 1,
max_samples_per_instance: 50,
};
assert_eq!(
r.check_consistency(),
Err(InconsistentReason::ResourceLimits)
);
}
#[test]
fn reader_filter_gt_deadline_inconsistent() {
let mut r = ReaderQos::default();
r.deadline.period = Duration::from_millis(100);
r.time_based_filter.minimum_separation = Duration::from_millis(500);
assert_eq!(
r.check_consistency(),
Err(InconsistentReason::FilterVsDeadline)
);
}
#[test]
fn reader_filter_eq_deadline_is_ok() {
let mut r = ReaderQos::default();
r.deadline.period = Duration::from_millis(250);
r.time_based_filter.minimum_separation = Duration::from_millis(250);
assert!(r.check_consistency().is_ok());
}
#[test]
fn latency_budget_mismatch_reported() {
let mut writer = WriterQos::default();
writer.latency_budget.duration = Duration::from_millis(100);
let mut reader = ReaderQos::default();
reader.latency_budget.duration = Duration::from_millis(10);
let res = check_compatibility(&writer, &reader);
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::LatencyBudget));
}
CompatibilityResult::Compatible => panic!("expected LatencyBudget mismatch"),
}
}
#[test]
fn liveliness_lease_mismatch_reported() {
use super::super::liveliness::LivelinessKind;
let mut writer = WriterQos::default();
writer.liveliness.kind = LivelinessKind::Automatic;
writer.liveliness.lease_duration = Duration::from_secs(10);
let mut reader = ReaderQos::default();
reader.liveliness.kind = LivelinessKind::Automatic;
reader.liveliness.lease_duration = Duration::from_secs(1);
let res = check_compatibility(&writer, &reader);
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::Liveliness));
}
CompatibilityResult::Compatible => panic!("expected Liveliness mismatch"),
}
}
#[test]
fn destination_order_mismatch_reported() {
use super::super::destination_order::DestinationOrderKind;
let writer = WriterQos::default();
let mut reader = ReaderQos::default();
reader.destination_order.kind = DestinationOrderKind::BySourceTimestamp;
let res = check_compatibility(&writer, &reader);
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::DestinationOrder));
}
CompatibilityResult::Compatible => panic!("expected DestinationOrder mismatch"),
}
}
#[test]
fn ownership_mismatch_reported() {
use super::super::ownership::OwnershipKind;
let writer = WriterQos::default();
let mut reader = ReaderQos::default();
reader.ownership.kind = OwnershipKind::Exclusive;
let res = check_compatibility(&writer, &reader);
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::Ownership));
}
CompatibilityResult::Compatible => panic!("expected Ownership mismatch"),
}
}
#[test]
fn partition_mismatch_reported() {
use alloc::string::ToString;
let mut writer = WriterQos::default();
writer.partition.names.push("alpha".to_string());
let mut reader = ReaderQos::default();
reader.partition.names.push("beta".to_string());
let res = check_compatibility(&writer, &reader);
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::Partition));
}
CompatibilityResult::Compatible => panic!("expected Partition mismatch"),
}
}
#[test]
fn presentation_mismatch_reported() {
use super::super::presentation::PresentationAccessScope;
let writer = WriterQos::default();
let mut reader = ReaderQos::default();
reader.presentation.access_scope = PresentationAccessScope::Topic;
reader.presentation.coherent_access = true;
let res = check_compatibility(&writer, &reader);
match res {
CompatibilityResult::Incompatible(reasons) => {
assert!(reasons.contains(&IncompatibleReason::Presentation));
}
CompatibilityResult::Compatible => panic!("expected Presentation mismatch"),
}
}
#[test]
fn writer_qos_clone_and_eq() {
let a = WriterQos::default();
let b = a.clone();
assert_eq!(a, b);
}
}