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}