1use std::{io::Cursor, sync::Mutex, time::Duration};
2
3use crate::{
4 DEFAULT_RETRIES,
5 commands::{ErrResponse, ErrResponseV2, McuMgrCommand},
6 smp_errors::{DeviceError, MCUmgrErr},
7 transport::{ReceiveError, SendError, Transport},
8};
9
10use miette::{Diagnostic, IntoDiagnostic};
11use polonius_the_crab::prelude::*;
12use thiserror::Error;
13
14struct Transceiver {
15 transport: Box<dyn Transport + Send>,
16 next_seqnum: u8,
17 receive_buffer: Box<[u8; u16::MAX as usize]>,
18}
19
20struct Inner {
21 transceiver: Transceiver,
22 send_buffer: Box<[u8; u16::MAX as usize]>,
23 retries: u8,
24}
25
26pub struct Connection {
31 inner: Mutex<Inner>,
32}
33
34#[derive(Error, Debug, Diagnostic)]
36pub enum ExecuteError {
37 #[error("Sending failed")]
39 #[diagnostic(code(mcumgr_toolkit::connection::execute::send))]
40 SendFailed(#[from] SendError),
41 #[error("Receiving failed")]
43 #[diagnostic(code(mcumgr_toolkit::connection::execute::receive))]
44 ReceiveFailed(#[from] ReceiveError),
45 #[error("CBOR encoding failed")]
47 #[diagnostic(code(mcumgr_toolkit::connection::execute::encode))]
48 EncodeFailed(#[source] Box<dyn miette::Diagnostic + Send + Sync>),
49 #[error("CBOR decoding failed")]
51 #[diagnostic(code(mcumgr_toolkit::connection::execute::decode))]
52 DecodeFailed(#[source] Box<dyn miette::Diagnostic + Send + Sync>),
53 #[error("Device returned error code: {0}")]
55 #[diagnostic(code(mcumgr_toolkit::connection::execute::device_error))]
56 ErrorResponse(DeviceError),
57}
58
59impl ExecuteError {
60 pub fn command_not_supported(&self) -> bool {
62 if let Self::ErrorResponse(DeviceError::V1 { rc, .. }) = self {
63 *rc == MCUmgrErr::MGMT_ERR_ENOTSUP as i32
64 } else {
65 false
66 }
67 }
68}
69
70impl Transceiver {
71 fn transceive_command(
72 &mut self,
73 write_operation: bool,
74 group_id: u16,
75 command_id: u8,
76 data: &[u8],
77 ) -> Result<&'_ [u8], ExecuteError> {
78 let sequence_num = self.next_seqnum;
79 self.next_seqnum = self.next_seqnum.wrapping_add(1);
80
81 self.transport
82 .send_frame(write_operation, sequence_num, group_id, command_id, data)?;
83
84 self.transport
85 .receive_frame(
86 &mut self.receive_buffer,
87 write_operation,
88 sequence_num,
89 group_id,
90 command_id,
91 )
92 .map_err(Into::into)
93 }
94
95 fn transceive_command_with_retries(
96 &mut self,
97 write_operation: bool,
98 group_id: u16,
99 command_id: u8,
100 data: &[u8],
101 num_retries: u8,
102 ) -> Result<&'_ [u8], ExecuteError> {
103 let mut this = self;
104
105 let mut counter = 0;
106
107 polonius_loop!(|this| -> Result<&'polonius [u8], ExecuteError> {
108 let result = this.transceive_command(write_operation, group_id, command_id, data);
109
110 if counter >= num_retries {
111 polonius_return!(result)
112 }
113 counter += 1;
114
115 match result {
116 Ok(_) => polonius_return!(result),
117 Err(e) => {
118 let mut lowest_err: &dyn std::error::Error = &e;
119 while let Some(lower_err) = lowest_err.source() {
120 lowest_err = lower_err;
121 }
122 log::warn!("Retry transmission, error occurred: {lowest_err}");
123 }
124 }
125 })
126 }
127}
128
129impl Connection {
130 pub fn new<T: Transport + Send + 'static>(transport: T) -> Self {
132 Self {
133 inner: Mutex::new(Inner {
134 transceiver: Transceiver {
135 transport: Box::new(transport),
136 next_seqnum: rand::random(),
137 receive_buffer: Box::new([0; u16::MAX as usize]),
138 },
139 send_buffer: Box::new([0; u16::MAX as usize]),
140 retries: DEFAULT_RETRIES,
141 }),
142 }
143 }
144
145 pub fn set_timeout(
150 &self,
151 timeout: Duration,
152 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
153 self.inner
154 .lock()
155 .unwrap()
156 .transceiver
157 .transport
158 .set_timeout(timeout)
159 }
160
161 pub fn set_retries(&self, retries: u8) {
166 self.inner.lock().unwrap().retries = retries;
167 }
168
169 pub fn execute_command<R: McuMgrCommand>(
171 &self,
172 request: &R,
173 ) -> Result<R::Response, ExecuteError> {
174 self.execute_command_impl(request, true)
175 }
176
177 pub fn execute_command_without_retries<R: McuMgrCommand>(
181 &self,
182 request: &R,
183 ) -> Result<R::Response, ExecuteError> {
184 self.execute_command_impl(request, false)
185 }
186
187 fn execute_command_impl<R: McuMgrCommand>(
188 &self,
189 request: &R,
190 use_retries: bool,
191 ) -> Result<R::Response, ExecuteError> {
192 let mut lock_guard = self.inner.lock().unwrap();
193 let locked_self: &mut Inner = &mut lock_guard;
194
195 let mut cursor = Cursor::new(locked_self.send_buffer.as_mut_slice());
196 ciborium::into_writer(request.data(), &mut cursor)
197 .into_diagnostic()
198 .map_err(Into::into)
199 .map_err(ExecuteError::EncodeFailed)?;
200 let data_size = cursor.position() as usize;
201 let data = &locked_self.send_buffer[..data_size];
202
203 log::debug!("TX data: {}", hex::encode(data));
204
205 let write_operation = request.is_write_operation();
206 let group_id = request.group_id();
207 let command_id = request.command_id();
208
209 let response = locked_self.transceiver.transceive_command_with_retries(
210 write_operation,
211 group_id,
212 command_id,
213 data,
214 if use_retries { locked_self.retries } else { 0 },
215 )?;
216
217 log::debug!("RX data: {}", hex::encode(response));
218
219 let err: ErrResponse = ciborium::from_reader(Cursor::new(response))
220 .into_diagnostic()
221 .map_err(Into::into)
222 .map_err(ExecuteError::DecodeFailed)?;
223
224 if let Some(ErrResponseV2 { rc, group }) = err.err {
225 return Err(ExecuteError::ErrorResponse(DeviceError::V2 { group, rc }));
226 }
227
228 if let Some(rc) = err.rc {
229 if rc != MCUmgrErr::MGMT_ERR_EOK as i32 {
230 return Err(ExecuteError::ErrorResponse(DeviceError::V1 {
231 rc,
232 rsn: err.rsn,
233 }));
234 }
235 }
236
237 let decoded_response: R::Response = ciborium::from_reader(Cursor::new(response))
238 .into_diagnostic()
239 .map_err(Into::into)
240 .map_err(ExecuteError::DecodeFailed)?;
241
242 Ok(decoded_response)
243 }
244
245 pub fn execute_raw_command(
255 &self,
256 write_operation: bool,
257 group_id: u16,
258 command_id: u8,
259 data: &[u8],
260 use_retries: bool,
261 ) -> Result<Box<[u8]>, ExecuteError> {
262 let mut lock_guard = self.inner.lock().unwrap();
263 let locked_self: &mut Inner = &mut lock_guard;
264
265 locked_self
266 .transceiver
267 .transceive_command_with_retries(
268 write_operation,
269 group_id,
270 command_id,
271 data,
272 if use_retries { locked_self.retries } else { 0 },
273 )
274 .map(|val| val.into())
275 }
276}