Skip to main content

maviola/core/io/
routing.rs

1use crate::core::io::ChannelInfo;
2use std::fmt::{Debug, Formatter};
3use std::sync::Arc;
4
5use crate::core::utils::UniqueId;
6use crate::protocol::{Frame, MaybeVersioned};
7
8/// <sup>[`serde`](https://serde.rs) | [`specta`](https://crates.io/crates/specta)</sup>
9/// Connection `ID`.
10///
11/// Identifies a particular connection.
12///
13/// This is an opaque identifier. It can be compared for equality with other connection `ID` and
14/// used as a key in hashmaps or hashsets.
15#[cfg_attr(feature = "specta", derive(specta::Type))]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[derive(Copy, Clone, Eq, PartialEq, Hash)]
18pub struct ConnectionId(UniqueId);
19
20/// Channel `ID`.
21///
22/// Identifies a channel within a particular connection.
23///
24/// This is an opaque identifier. It can be compared for equality with other channel `ID` and
25/// used as a key in hashmaps or hashsets.
26#[derive(Copy, Clone, Eq, PartialEq, Hash)]
27pub struct ChannelId {
28    connection: ConnectionId,
29    channel: UniqueId,
30}
31
32/// Incoming MAVLink frame.
33#[derive(Clone, Debug)]
34pub struct IncomingFrame<V: MaybeVersioned> {
35    frame: Frame<V>,
36    channel: ChannelInfo,
37}
38
39/// Outgoing MAVLink frame.
40#[derive(Clone, Debug)]
41pub struct OutgoingFrame<V: MaybeVersioned> {
42    frame: Arc<Frame<V>>,
43    scope: BroadcastScope,
44}
45
46/// Defines, how frame should be broadcast.
47#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
48pub enum BroadcastScope {
49    /// Broadcast to all channels (default value).
50    #[default]
51    All,
52    /// Broadcast only to this channel.
53    ExactChannel(ChannelId),
54    /// Broadcast to all channels except the specified one.
55    ExceptChannel(ChannelId),
56    /// Broadcast to all channels of its own connection except the specified channel.
57    ExceptChannelWithin(ChannelId),
58    /// Broadcast only to this connection.
59    ExactConnection(ConnectionId),
60    /// Broadcast to all connections except the specified one.
61    ExceptConnection(ConnectionId),
62}
63
64impl ConnectionId {
65    /// Creates a new unique connection identifier.
66    pub(crate) fn new() -> Self {
67        Self(UniqueId::new())
68    }
69
70    /// Returns `true` if the channel with provided `channel_id` belongs to this connection.
71    #[inline(always)]
72    pub fn contains(&self, channel_id: ChannelId) -> bool {
73        &channel_id.connection == self
74    }
75}
76
77impl ChannelId {
78    /// Creates a new unique channel identifier that belongs to the connection with the specified
79    /// `connection_id`.
80    pub(crate) fn new(connection_id: ConnectionId) -> Self {
81        Self {
82            connection: connection_id,
83            channel: UniqueId::new(),
84        }
85    }
86
87    /// Identifier of a connection withing this channel.
88    pub fn connection_id(&self) -> ConnectionId {
89        self.connection
90    }
91
92    /// Returns `true`, if this channel belongs to the connection with provided `connection_id`.
93    #[inline(always)]
94    pub fn belongs_to(&self, connection_id: ConnectionId) -> bool {
95        connection_id.contains(*self)
96    }
97}
98
99impl Debug for ConnectionId {
100    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
101        f.debug_tuple("ConnectionId").finish()
102    }
103}
104
105impl Debug for ChannelId {
106    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107        f.debug_tuple("ChannelId").finish()
108    }
109}
110
111impl From<ConnectionId> for ChannelId {
112    fn from(value: ConnectionId) -> Self {
113        Self::new(value)
114    }
115}
116
117impl<V: MaybeVersioned> IncomingFrame<V> {
118    /// Creates an incoming from MAVLink [`Frame`] and [`ChannelId`].
119    pub fn new(frame: Frame<V>, channel: ChannelInfo) -> Self {
120        Self { frame, channel }
121    }
122}
123
124impl<V: MaybeVersioned> From<IncomingFrame<V>> for (Frame<V>, ChannelInfo) {
125    fn from(value: IncomingFrame<V>) -> Self {
126        (value.frame, value.channel)
127    }
128}
129
130impl<V: MaybeVersioned> OutgoingFrame<V> {
131    /// Creates an outgoing frame from MAVLink [`Frame`].
132    pub fn new(frame: Frame<V>) -> Self {
133        Self {
134            frame: Arc::new(frame),
135            scope: BroadcastScope::All,
136        }
137    }
138
139    pub(crate) fn scoped(frame: Frame<V>, scope: BroadcastScope) -> Self {
140        Self {
141            frame: Arc::new(frame),
142            scope,
143        }
144    }
145
146    /// Reference to the underlying MAVLink [`Frame`].
147    #[inline]
148    pub fn frame(&self) -> &Frame<V> {
149        self.frame.as_ref()
150    }
151
152    /// Broadcast scope.
153    #[inline]
154    pub fn scope(&self) -> BroadcastScope {
155        self.scope
156    }
157
158    /// Set broadcast scope.
159    #[inline]
160    pub(crate) fn set_scope(&mut self, scope: BroadcastScope) {
161        self.scope = scope;
162    }
163
164    /// Matches frame against a particular connection and changes broadcast scope if necessary.
165    ///
166    /// The rules are the following:
167    ///
168    /// * If scope is [`BroadcastScope::ExceptConnection`] and `connection_id` equals the excluded
169    ///   `ID`, then method will keep connection untouched and return `false`.
170    /// * If scope is [`BroadcastScope::ExactConnection`] and `connection_id` equals the specified
171    ///   `ID`, then frame scope will be changed to [`BroadcastScope::All`] and `true` will be
172    ///   returned.
173    /// * Returns `true` for all other cases.
174    pub(crate) fn matches_connection_reroute(&mut self, connection_id: ConnectionId) -> bool {
175        match self.scope() {
176            BroadcastScope::ExceptConnection(conn_id) if connection_id == conn_id => false,
177            BroadcastScope::ExactConnection(conn_id) if connection_id == conn_id => {
178                self.set_scope(BroadcastScope::All);
179                true
180            }
181            _ => true,
182        }
183    }
184
185    pub(crate) fn should_send_to(&self, channel_id: ChannelId) -> bool {
186        match self.scope {
187            BroadcastScope::All => true,
188            BroadcastScope::ExactChannel(sender_id) => sender_id == channel_id,
189            BroadcastScope::ExceptChannel(sender_id) => sender_id != channel_id,
190            BroadcastScope::ExceptChannelWithin(sender_id) => {
191                channel_id.connection_id().contains(sender_id) && sender_id != channel_id
192            }
193            BroadcastScope::ExactConnection(conn_id) => conn_id.contains(channel_id),
194            BroadcastScope::ExceptConnection(conn_id) => !conn_id.contains(channel_id),
195        }
196    }
197}
198
199impl<V: MaybeVersioned> From<OutgoingFrame<V>> for Frame<V> {
200    fn from(value: OutgoingFrame<V>) -> Self {
201        value.frame.as_ref().clone()
202    }
203}