mavio/protocol/
endpoint.rs

1use core::marker::PhantomData;
2
3use crate::error::Result;
4use crate::protocol::{
5    Behold, ComponentId, Frame, MavLinkId, MaybeVersioned, Message, Sequence, Sequencer, SystemId,
6    Versioned, Versionless, V1, V2,
7};
8
9/// MAVLink device with defined `ID` and internal frame sequence counter.
10///
11/// # Examples
12///
13/// ```no_run
14/// #[cfg(not(feature = "dlct-minimal"))]
15/// # fn main() {}
16/// #[cfg(feature = "dlct-minimal")]
17/// # fn main() {
18/// use mavio::dialects::minimal::messages::Heartbeat;
19/// use mavio::prelude::*;
20///
21/// // Create a `MAVLink2` device with system and component ids
22/// let device = Endpoint::v2(MavLinkId::new(17, 42));
23/// device.advance(3).discard();
24///
25/// // Build a new frame from the provided message
26/// let frame = device.next_frame(&Heartbeat::default()).unwrap();
27///
28/// assert_eq!(frame.sequence(), 3, "should be correct sequence number");
29/// assert_eq!(frame.system_id(), 17, "should be the defined system `ID`");
30/// assert_eq!(frame.component_id(), 42, "should be the defined component `ID`");
31/// }
32/// ```
33#[derive(Debug)]
34pub struct Endpoint<V: MaybeVersioned> {
35    id: MavLinkId,
36    sequencer: Sequencer,
37    _version: PhantomData<V>,
38}
39
40impl Endpoint<Versionless> {
41    /// Creates a new device with specified [`MavLinkId`].
42    pub fn new<V: MaybeVersioned>(id: MavLinkId) -> Endpoint<V> {
43        Endpoint {
44            id,
45            sequencer: Sequencer::new(),
46            _version: PhantomData,
47        }
48    }
49
50    /// Creates a MAVLink1 device with specified [`MavLinkId`].
51    #[inline]
52    pub fn v1(id: MavLinkId) -> Endpoint<V1> {
53        Endpoint::new(id)
54    }
55
56    /// Creates a MAVLink2 device with specified [`MavLinkId`].
57    #[inline]
58    pub fn v2(id: MavLinkId) -> Endpoint<V2> {
59        Endpoint::new(id)
60    }
61
62    /// Creates a device without a specified MAVLink protocol version.
63    #[inline]
64    pub fn versionless(id: MavLinkId) -> Endpoint<Versionless> {
65        Endpoint::new(id)
66    }
67
68    /// Produces a next versionless frame from MAVLink message.
69    ///
70    /// The actual protocol version still has to be specified as a generic parameter using
71    /// [turbofish](https://turbo.fish/about) syntax.
72    pub fn next_frame<V: Versioned>(&self, message: &dyn Message) -> Result<Frame<Versionless>> {
73        Ok(self._next_frame::<V>(message)?.into_versionless())
74    }
75}
76
77impl<V: MaybeVersioned> Endpoint<V> {
78    /// Device `ID`.
79    #[inline(always)]
80    pub fn id(&self) -> MavLinkId {
81        self.id
82    }
83
84    /// MAVLink system `ID`.
85    #[inline(always)]
86    pub fn system_id(&self) -> SystemId {
87        self.id.system
88    }
89
90    /// MAVLink component `ID`.
91    #[inline(always)]
92    pub fn component_id(&self) -> ComponentId {
93        self.id.component
94    }
95
96    /// Next MAVLink frame sequence.
97    #[inline(always)]
98    pub fn next_sequence(&self) -> Sequence {
99        self.sequencer.next()
100    }
101
102    /// Returns a reference to internal [`Sequencer`].
103    #[inline(always)]
104    pub fn sequencer(&self) -> &Sequencer {
105        &self.sequencer
106    }
107
108    /// Skips `increment` items in sequence and return the updated current value.
109    ///
110    /// The return value is wrapped in [`Behold`] since it is not guaranteed in multithreaded
111    /// environments, that the [`Endpoint::next_frame`] will use the same value of a sequence in
112    /// this thread.
113    pub fn advance(&self, increment: Sequence) -> Behold<Sequence> {
114        self.sequencer.advance(increment)
115    }
116
117    /// Forks existing endpoint.
118    ///
119    /// Forking is similar to cloning, except the internal frame [`Sequencer`] will be forked to
120    /// start from the next value. This method is available for all targets, while cloning is
121    /// possible only for `alloc` targets.
122    ///
123    /// See [`Sequencer::fork`] for details.
124    pub fn fork(&self) -> Self {
125        Self {
126            id: self.id,
127            sequencer: self.sequencer.fork(),
128            _version: PhantomData,
129        }
130    }
131
132    /// Synchronizes this endpoint with another one.
133    ///
134    /// Synchronizes internal sequencer with the sequencer of the `other` [`Endpoint`].
135    ///
136    /// See [`Sequencer::sync`] for details.
137    pub fn sync<Version: MaybeVersioned>(&self, other: &Endpoint<Version>) {
138        self.sequencer.sync(other.sequencer())
139    }
140
141    /// <sup>`alloc`</sup>
142    /// Joins another endpoint with current one.
143    ///
144    /// From this moment internal sequencers will share the same counter. The current sequencer will
145    /// be synced with the one it joins.
146    ///
147    /// Available only when `alloc` feature is enabled.
148    ///
149    /// See [`Sequencer::join`] for details.
150    #[cfg(feature = "alloc")]
151    pub fn join<Version: MaybeVersioned>(&self, other: &mut Endpoint<Version>) {
152        self.sequencer.join(&mut other.sequencer)
153    }
154
155    fn _next_frame<Version: Versioned>(&self, message: &dyn Message) -> Result<Frame<Version>> {
156        let frame = Frame::builder()
157            .sequence(self.next_sequence())
158            .system_id(self.system_id())
159            .component_id(self.component_id())
160            .version(Version::v())
161            .message(message)?
162            .build();
163        Ok(frame)
164    }
165}
166
167impl<V: Versioned> Endpoint<V> {
168    /// Produces a next frame from MAVLink message.
169    pub fn next_frame(&self, message: &dyn Message) -> Result<Frame<V>> {
170        self._next_frame::<V>(message)
171    }
172}
173
174#[cfg(feature = "alloc")]
175impl<V: MaybeVersioned> Clone for Endpoint<V> {
176    fn clone(&self) -> Self {
177        Self {
178            id: self.id,
179            sequencer: self.sequencer.clone(),
180            _version: PhantomData,
181        }
182    }
183}