oa_gateway_dds/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Runtime settings for a DDS session. This is not the host TOML section.

use std::fmt;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;

use oa_gateway_adapter::OnPanic;
use oa_gateway_uci::validate::Mode as ValidateMode;
use oa_gateway_uci::Schema;

/// Largest DDS sample accepted from the domain, in bytes, before it is
/// unwrapped or converted.
///
/// Matches OWP's default frame limit. DDS has no handshake of its own to
/// cap a sample's size the way a WebSocket frame limit does, so this is
/// a plain length check instead of a transport-level setting.
pub const DEFAULT_MAX_SAMPLE_SIZE: usize = 16 * 1024 * 1024;

/// Which [`crate::DdsProvider`] the host should construct.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DdsProviderKind {
    /// Pure-Rust rustdds. The only value in this build.
    #[default]
    Rustdds,
}

impl fmt::Display for DdsProviderKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            Self::Rustdds => "rustdds",
        })
    }
}

impl FromStr for DdsProviderKind {
    type Err = String;

    /// # Errors
    ///
    /// Returns a message if `s` is not `rustdds`.
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "rustdds" => Ok(Self::Rustdds),
            other => Err(format!("unknown dds provider '{other}'; expected rustdds")),
        }
    }
}

/// Runtime settings for a DDS session.
///
/// This is not the host TOML section. `qos` is already a path the host
/// has checked exists. There is no `enabled` flag here — spawning is
/// the host's decision. [`Self::provider`] is a closed enum; the only
/// value in this build is [`DdsProviderKind::Rustdds`].
#[derive(Debug, Clone)]
pub struct DdsConfig {
    /// Which provider to construct. Unknown names are refused before
    /// this struct is built.
    pub provider: DdsProviderKind,
    /// DDS domain id the participant joins.
    pub domain_id: u16,
    /// QoS file the provider interprets. rustdds parses a documented
    /// DDS-XML subset; a later FFI provider may pass the same path to
    /// its own loader.
    pub qos: PathBuf,
    /// Engine topic names and DDS topic names, bridged both ways.
    pub topics: Vec<String>,
    /// Peel A-GRA Rx/Tx wrappers on inbound samples and publish the
    /// wrapper plus the inner message.
    pub unwrap_ma_payloads: bool,
    /// Skip outbound samples that originated on this adapter.
    pub suppress_echo: bool,
    /// Panic while joined to the domain: abort `run`, or treat it as a
    /// failed session.
    pub on_panic: OnPanic,
    /// Rejoin the domain after the session ends or panics.
    pub reconnect: bool,
    /// Sleep between rejoin attempts.
    pub reconnect_delay: Duration,
    /// Compiled UCI schema used to check inbound samples. `None` skips
    /// the check: there is nothing to check against.
    pub schema: Option<Arc<Schema>>,
    /// What to do about an inbound sample that does not follow
    /// [`Self::schema`]. Has no effect without one.
    pub validate: ValidateMode,
    /// Largest inbound sample accepted, in bytes, before it is unwrapped
    /// or converted. An oversized sample is dropped and logged.
    pub max_sample_size: usize,
}

impl Default for DdsConfig {
    fn default() -> Self {
        Self {
            provider: DdsProviderKind::Rustdds,
            domain_id: 0,
            qos: PathBuf::from("config/dds-qos.xml"),
            topics: vec!["demo".into()],
            unwrap_ma_payloads: true,
            suppress_echo: true,
            on_panic: OnPanic::Abort,
            reconnect: false,
            reconnect_delay: Duration::from_secs(1),
            schema: None,
            validate: ValidateMode::default(),
            max_sample_size: DEFAULT_MAX_SAMPLE_SIZE,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn provider_reads_back() {
        assert_eq!(
            DdsProviderKind::Rustdds
                .to_string()
                .parse::<DdsProviderKind>()
                .unwrap(),
            DdsProviderKind::Rustdds
        );
        let err = DdsProviderKind::from_str("cyclone").unwrap_err();
        assert!(err.contains("rustdds"), "{err}");
    }
}