1use std::{future::Future, io};
2
3use futures::lock::{Mutex, MutexGuard};
4use tokio::io::{AsyncRead, AsyncWrite};
5
6use crate::AsyncMavlinkReader;
7#[cfg(feature = "mav2-message-signing")]
8use crate::SigningConfig;
9use crate::error::{MessageReadError, MessageWriteError};
10use crate::static_dialect::StaticDialect;
11use crate::{
12 Dialect, MAVLinkMessageRaw, MavFrame, MavHeader, MavlinkVersion, Message,
13 connectable::ConnectionAddress,
14};
15
16#[doc(hidden)]
18pub(crate) trait AsyncTransport: Send + Sync {
19 type Reader: AsyncRead + Unpin + Send;
21 type Writer: AsyncWrite + Unpin + Send;
23
24 fn reader(&self) -> &Mutex<AsyncMavlinkReader<Self::Reader>>;
26
27 fn lock_writer(&self) -> impl Future<Output = Option<MutexGuard<'_, Self::Writer>>> + Send;
29
30 fn retry_receive(&self, _error: &MessageReadError) -> bool {
32 false
33 }
34}
35
36pub type AsyncConnection<M> = AsyncDialectConnection<StaticDialect<M>>;
38
39pub struct AsyncDialectConnection<D> {
41 transport: Transport,
42 dialect: D,
43 state: crate::connection_shared::ConnectionState,
44}
45
46pub(crate) enum Transport {
47 #[cfg(feature = "transport-tcp")]
48 Tcp(super::tcp::AsyncTcpConnection),
49 #[cfg(feature = "transport-udp")]
50 Udp(super::udp::AsyncUdpConnection),
51 #[cfg(feature = "transport-direct-serial")]
52 Serial(super::direct_serial::AsyncSerialConnection),
53 File(super::file::AsyncFileConnection),
54}
55
56impl<D> AsyncDialectConnection<D> {
57 pub(crate) fn new(transport: Transport, dialect: D) -> Self {
58 Self {
59 transport,
60 dialect,
61 state: crate::connection_shared::ConnectionState::new(),
62 }
63 }
64}
65
66pub trait AsyncMavConnection<M: Sync + Send>: Send + Sync {
68 fn recv(&self) -> impl Future<Output = Result<(MavHeader, M), MessageReadError>> + Send;
72
73 fn recv_raw(&self) -> impl Future<Output = Result<MAVLinkMessageRaw, MessageReadError>> + Send;
77
78 fn try_recv(&self) -> impl Future<Output = Result<(MavHeader, M), MessageReadError>> + Send;
82
83 fn send(
85 &self,
86 header: &MavHeader,
87 data: &M,
88 ) -> impl Future<Output = Result<usize, MessageWriteError>> + Send;
89
90 fn send_raw(
92 &self,
93 data: &MAVLinkMessageRaw,
94 ) -> impl Future<Output = Result<usize, MessageWriteError>> + Send;
95
96 fn set_protocol_version(&mut self, version: MavlinkVersion);
98 fn protocol_version(&self) -> MavlinkVersion;
100
101 fn set_allow_recv_any_version(&mut self, allow: bool);
105
106 fn allow_recv_any_version(&self) -> bool;
108
109 fn send_frame(
111 &self,
112 frame: &MavFrame<M>,
113 ) -> impl Future<Output = Result<usize, MessageWriteError>> + Send
114 where
115 M: Message,
116 {
117 self.send(&frame.header, &frame.msg)
118 }
119
120 fn recv_frame(&self) -> impl Future<Output = Result<MavFrame<M>, MessageReadError>> + Send
122 where
123 M: Message,
124 {
125 async move {
126 let (header, msg) = self.recv().await?;
127 Ok(MavFrame {
128 header,
129 msg,
130 protocol_version: self.protocol_version(),
131 })
132 }
133 }
134
135 fn send_default(
137 &self,
138 data: &M,
139 ) -> impl Future<Output = Result<usize, MessageWriteError>> + Send {
140 async move { self.send(&MavHeader::default(), data).await }
141 }
142
143 #[cfg(feature = "mav2-message-signing")]
145 fn setup_signing(&mut self, signing_data: Option<SigningConfig>);
146}
147
148impl<D: Dialect + Send + Sync> AsyncMavConnection<D::Message> for AsyncDialectConnection<D>
149where
150 D::Message: Send + Sync,
151{
152 async fn recv(&self) -> Result<(MavHeader, D::Message), MessageReadError> {
153 dispatch_transport!(&self.transport, |transport| {
154 let mut reader = transport.reader().lock().await;
155 loop {
156 match crate::connection_shared::read_message_async_with_dialect(
157 &mut reader,
158 &self.state,
159 &self.dialect,
160 )
161 .await
162 {
163 Ok(message) => return Ok(message),
164 Err(error) if transport.retry_receive(&error) => {}
165 Err(error) => return Err(error),
166 }
167 }
168 })
169 }
170
171 async fn recv_raw(&self) -> Result<MAVLinkMessageRaw, MessageReadError> {
172 dispatch_transport!(&self.transport, |transport| {
173 let mut reader = transport.reader().lock().await;
174 loop {
175 match crate::connection_shared::read_raw_message_async_with_dialect(
176 &mut reader,
177 &self.state,
178 &self.dialect,
179 )
180 .await
181 {
182 Ok(message) => return Ok(message),
183 Err(error) if transport.retry_receive(&error) => {}
184 Err(error) => return Err(error),
185 }
186 }
187 })
188 }
189
190 async fn try_recv(&self) -> Result<(MavHeader, D::Message), MessageReadError> {
191 dispatch_transport!(&self.transport, |transport| {
192 let mut reader = transport.reader().lock().await;
193 crate::connection_shared::read_message_async_with_dialect(
194 &mut reader,
195 &self.state,
196 &self.dialect,
197 )
198 .await
199 })
200 }
201
202 async fn send(
203 &self,
204 header: &MavHeader,
205 data: &D::Message,
206 ) -> Result<usize, MessageWriteError> {
207 dispatch_transport!(&self.transport, |transport| {
208 let Some(mut writer) = transport.lock_writer().await else {
209 return Ok(0);
210 };
211 let header = self.state.next_header(header);
212 crate::connection_shared::write_message_async_with_dialect(
213 &mut *writer,
214 &self.state,
215 header,
216 &self.dialect,
217 data,
218 )
219 .await
220 })
221 }
222
223 async fn send_raw(&self, data: &MAVLinkMessageRaw) -> Result<usize, MessageWriteError> {
224 dispatch_transport!(&self.transport, |transport| {
225 let Some(mut writer) = transport.lock_writer().await else {
226 return Ok(0);
227 };
228 crate::connection_shared::write_raw_message_async(&mut *writer, data).await
229 })
230 }
231
232 fn set_protocol_version(&mut self, version: MavlinkVersion) {
233 self.state.set_protocol_version(version);
234 }
235
236 fn protocol_version(&self) -> MavlinkVersion {
237 self.state.protocol_version()
238 }
239
240 fn set_allow_recv_any_version(&mut self, allow: bool) {
241 self.state.set_allow_recv_any_version(allow);
242 }
243
244 fn allow_recv_any_version(&self) -> bool {
245 self.state.allow_recv_any_version()
246 }
247
248 #[cfg(feature = "mav2-message-signing")]
249 fn setup_signing(&mut self, signing_data: Option<SigningConfig>) {
250 self.state.setup_signing(signing_data);
251 }
252}
253
254pub async fn connect_async<M: Message + Sync + Send>(
275 address: &str,
276) -> io::Result<AsyncConnection<M>> {
277 ConnectionAddress::parse_address(address)?
278 .connect_async()
279 .await
280}
281
282pub async fn connect_async_with_dialect<D: Dialect + Send + Sync>(
286 address: &str,
287 dialect: D,
288) -> io::Result<AsyncDialectConnection<D>>
289where
290 D::Message: Send + Sync,
291{
292 ConnectionAddress::parse_address(address)?
293 .connect_async_with_dialect(dialect)
294 .await
295}
296
297pub trait AsyncConnectable: Sync {
301 fn connect_async<M: Message + Sync + Send>(
303 &self,
304 ) -> impl Future<Output = io::Result<AsyncConnection<M>>> + Send {
305 self.connect_async_with_dialect(StaticDialect::<M>::new())
306 }
307
308 fn connect_async_with_dialect<D: Dialect + Send + Sync>(
310 &self,
311 dialect: D,
312 ) -> impl Future<Output = io::Result<AsyncDialectConnection<D>>> + Send
313 where
314 D::Message: Send + Sync;
315}
316
317impl AsyncConnectable for ConnectionAddress {
318 async fn connect_async_with_dialect<D: Dialect + Send + Sync>(
319 &self,
320 dialect: D,
321 ) -> io::Result<AsyncDialectConnection<D>>
322 where
323 D::Message: Send + Sync,
324 {
325 match self {
326 #[cfg(feature = "transport-tcp")]
327 Self::Tcp(c) => c.connect_async_with_dialect(dialect).await,
328 #[cfg(feature = "transport-udp")]
329 Self::Udp(c) => c.connect_async_with_dialect(dialect).await,
330 #[cfg(feature = "transport-direct-serial")]
331 Self::Serial(c) => c.connect_async_with_dialect(dialect).await,
332 Self::File(c) => c.connect_async_with_dialect(dialect).await,
333 }
334 }
335}