1use core::fmt::Display;
2use std::io::{self, Read, Write};
3use std::sync::MutexGuard;
4
5use crate::MavlinkReader;
6#[cfg(feature = "mav2-message-signing")]
7use crate::SigningConfig;
8use crate::connection_shared::{
9 ConnectionState, read_message_with_dialect, read_raw_message_with_dialect,
10 write_message_with_dialect, write_raw_message,
11};
12use crate::error::{MessageReadError, MessageWriteError};
13use crate::static_dialect::StaticDialect;
14use crate::{
15 Dialect, MAVLinkMessageRaw, MavFrame, MavHeader, MavlinkVersion, Message,
16 connectable::ConnectionAddress,
17};
18
19pub(crate) trait SyncTransport: Send + Sync {
21 type Reader: Read;
23 type Writer: Write;
25
26 fn reader(&self) -> MutexGuard<'_, MavlinkReader<Self::Reader>>;
28
29 fn lock_writer(&self) -> Option<MutexGuard<'_, Self::Writer>>;
31
32 fn retry_receive(&self, _error: &MessageReadError) -> bool {
34 false
35 }
36
37 fn set_nonblocking(
39 &self,
40 _reader: &mut MavlinkReader<Self::Reader>,
41 _enabled: bool,
42 ) -> io::Result<()> {
43 Ok(())
44 }
45}
46
47pub type Connection<M> = DialectConnection<StaticDialect<M>>;
49
50pub struct DialectConnection<D> {
52 transport: Transport,
53 dialect: D,
54 state: ConnectionState,
55}
56
57pub(crate) enum Transport {
58 #[cfg(feature = "transport-tcp")]
59 Tcp(super::tcp::TcpConnection),
60 #[cfg(feature = "transport-udp")]
61 Udp(super::udp::UdpConnection),
62 #[cfg(feature = "transport-direct-serial")]
63 Serial(super::direct_serial::SerialConnection),
64 File(super::file::FileConnection),
65}
66
67impl<D> DialectConnection<D> {
68 pub(crate) fn new(transport: Transport, dialect: D) -> Self {
69 Self {
70 transport,
71 dialect,
72 state: ConnectionState::new(),
73 }
74 }
75}
76
77pub trait MavConnection<M>: Send + Sync {
79 fn recv(&self) -> Result<(MavHeader, M), MessageReadError>;
88
89 fn recv_raw(&self) -> Result<MAVLinkMessageRaw, MessageReadError>;
98
99 fn try_recv(&self) -> Result<(MavHeader, M), MessageReadError>;
108
109 fn send(&self, header: &MavHeader, data: &M) -> Result<usize, MessageWriteError>;
115
116 fn send_raw(&self, data: &MAVLinkMessageRaw) -> Result<usize, MessageWriteError>;
122
123 fn set_protocol_version(&mut self, version: MavlinkVersion);
125 fn protocol_version(&self) -> MavlinkVersion;
127
128 fn set_allow_recv_any_version(&mut self, allow: bool);
132
133 fn allow_recv_any_version(&self) -> bool;
135
136 fn send_frame(&self, frame: &MavFrame<M>) -> Result<usize, MessageWriteError>
142 where
143 M: Message,
144 {
145 self.send(&frame.header, &frame.msg)
146 }
147
148 fn recv_frame(&self) -> Result<MavFrame<M>, MessageReadError>
154 where
155 M: Message,
156 {
157 let (header, msg) = self.recv()?;
158 Ok(MavFrame {
159 header,
160 msg,
161 protocol_version: self.protocol_version(),
162 })
163 }
164
165 fn send_default(&self, data: &M) -> Result<usize, MessageWriteError> {
171 self.send(&MavHeader::default(), data)
172 }
173
174 #[cfg(feature = "mav2-message-signing")]
176 fn setup_signing(&mut self, signing_data: Option<SigningConfig>);
177}
178
179impl<D: Dialect + Send + Sync> MavConnection<D::Message> for DialectConnection<D> {
180 fn recv(&self) -> Result<(MavHeader, D::Message), MessageReadError> {
181 dispatch_transport!(&self.transport, |transport| {
182 let mut reader = transport.reader();
183 loop {
184 match read_message_with_dialect(&mut reader, &self.state, &self.dialect) {
185 Ok(message) => return Ok(message),
186 Err(error) if transport.retry_receive(&error) => {}
187 Err(error) => return Err(error),
188 }
189 }
190 })
191 }
192
193 fn recv_raw(&self) -> Result<MAVLinkMessageRaw, MessageReadError> {
194 dispatch_transport!(&self.transport, |transport| {
195 let mut reader = transport.reader();
196 loop {
197 match read_raw_message_with_dialect(&mut reader, &self.state, &self.dialect) {
198 Ok(message) => return Ok(message),
199 Err(error) if transport.retry_receive(&error) => {}
200 Err(error) => return Err(error),
201 }
202 }
203 })
204 }
205
206 fn try_recv(&self) -> Result<(MavHeader, D::Message), MessageReadError> {
207 dispatch_transport!(&self.transport, |transport| {
208 let mut reader = transport.reader();
209 transport.set_nonblocking(&mut reader, true)?;
210 let result = read_message_with_dialect(&mut reader, &self.state, &self.dialect);
211 transport.set_nonblocking(&mut reader, false)?;
212 result
213 })
214 }
215
216 fn send(&self, header: &MavHeader, data: &D::Message) -> Result<usize, MessageWriteError> {
217 dispatch_transport!(&self.transport, |transport| {
218 let Some(mut writer) = transport.lock_writer() else {
219 return Ok(0);
220 };
221 let header = self.state.next_header(header);
222 write_message_with_dialect(&mut *writer, &self.state, header, &self.dialect, data)
223 })
224 }
225
226 fn send_raw(&self, data: &MAVLinkMessageRaw) -> Result<usize, MessageWriteError> {
227 dispatch_transport!(&self.transport, |transport| {
228 let Some(mut writer) = transport.lock_writer() else {
229 return Ok(0);
230 };
231 write_raw_message(&mut *writer, data)
232 })
233 }
234
235 fn set_protocol_version(&mut self, version: MavlinkVersion) {
236 self.state.set_protocol_version(version);
237 }
238
239 fn protocol_version(&self) -> MavlinkVersion {
240 self.state.protocol_version()
241 }
242
243 fn set_allow_recv_any_version(&mut self, allow: bool) {
244 self.state.set_allow_recv_any_version(allow);
245 }
246
247 fn allow_recv_any_version(&self) -> bool {
248 self.state.allow_recv_any_version()
249 }
250
251 #[cfg(feature = "mav2-message-signing")]
252 fn setup_signing(&mut self, signing_data: Option<SigningConfig>) {
253 self.state.setup_signing(signing_data);
254 }
255}
256
257pub fn connect<M: Message + Sync + Send>(address: &str) -> io::Result<Connection<M>> {
278 ConnectionAddress::parse_address(address)?.connect::<M>()
279}
280
281pub fn connect_with_dialect<D: Dialect + Send + Sync>(
285 address: &str,
286 dialect: D,
287) -> io::Result<DialectConnection<D>> {
288 ConnectionAddress::parse_address(address)?.connect_with_dialect(dialect)
289}
290
291pub trait Connectable: Display {
293 fn connect<M: Message>(&self) -> io::Result<Connection<M>> {
300 self.connect_with_dialect(StaticDialect::<M>::new())
301 }
302
303 fn connect_with_dialect<D: Dialect + Send + Sync>(
305 &self,
306 dialect: D,
307 ) -> io::Result<DialectConnection<D>>;
308}
309
310impl Connectable for ConnectionAddress {
311 fn connect_with_dialect<D: Dialect + Send + Sync>(
312 &self,
313 dialect: D,
314 ) -> io::Result<DialectConnection<D>> {
315 match self {
316 #[cfg(feature = "transport-tcp")]
317 Self::Tcp(c) => c.connect_with_dialect(dialect),
318 #[cfg(feature = "transport-udp")]
319 Self::Udp(c) => c.connect_with_dialect(dialect),
320 #[cfg(feature = "transport-direct-serial")]
321 Self::Serial(c) => c.connect_with_dialect(dialect),
322 Self::File(c) => c.connect_with_dialect(dialect),
323 }
324 }
325}