framed_serial/lib.rs
1//! Add frames to serial connections. Useful for embedded devices. Can be built with `no_std`.
2//!
3//! The main type of interest is [`FramedConnection`](struct.FramedConnection.html), which takes ownership
4//! of a serial connection and allows sending and receiving complete frames.
5//!
6//! To use with the standard library, put this in your `Cargo.toml`:
7//!
8//! ```toml
9//! [dependencies]
10//! framed-serial = "0.1"
11//! ```
12//!
13//! To use in an embedded device with `no_std`, put this in your `Cargo.toml`:
14//!
15//! ```toml
16//! [dependencies]
17//! framed-serial = {version = "0.1", default-features = false, features = ["collections"]}
18//! ```
19//!
20//! Example usage:
21//!
22//! ```
23//! #[cfg(feature = "std")]
24//! extern crate serial;
25//! extern crate framed_serial;
26//! #[cfg(feature = "std")]
27//! use serial::SerialPort;
28//!
29//! #[cfg(feature = "std")]
30//! fn wait_for_frame() -> Result<(),framed_serial::Error> {
31//!
32//! let device = match std::env::var("DEVICE") {
33//! Ok(val) => val,
34//! Err(_) => "/dev/ttyACM0".to_string(),
35//! };
36//! println!("opening device {}", device);
37//! let mut raw = serial::open(&device).expect("open serial port");
38//!
39//! // Async processing depends on this being short.
40//! raw.set_timeout(std::time::Duration::from_millis(100)).expect("set_timeout");
41//!
42//! let my_ser = framed_serial::SerialWrap::new(raw);
43//! let mut conn = framed_serial::FramedConnection::new(my_ser);
44//!
45//! // Loop until we get a frame. This requires a connected device
46//! // sending with FramedConnection.
47//! loop {
48//! let tick_state = conn.tick()?;
49//! if tick_state.recv_is_done {
50//! let data = conn.get_frame()?;
51//! println!("{:?}", data);
52//! break;
53//! }
54//! }
55//! Ok(())
56//! }
57//!
58//! // This example requires std to compile. To run successfully, it further requires a connected
59//! // serial device on /dev/ttyACM0 implementing `FramedConnection`. Use conditional compilation
60//! // to run only if the `device_connected` feature was specified at compile time.
61//!
62//! #[cfg(feature = "device_connected")]
63//! fn main() {
64//! wait_for_frame().unwrap();
65//! }
66//! #[cfg(not(feature = "device_connected"))]
67//! fn main() {
68//! // Do nothing if device is not connected.
69//! }
70//! ```
71#![cfg_attr(not(feature = "std"), no_std)]
72#![cfg_attr(feature = "collections", feature(collections))]
73#![deny(missing_docs)]
74extern crate embedded_serial;
75extern crate byteorder;
76
77#[cfg(feature = "collections")]
78extern crate collections;
79
80#[cfg(feature = "std")]
81extern crate serial;
82
83#[cfg(feature = "std")]
84mod core {
85 pub use std::mem;
86 pub use std::fmt;
87 pub use std::result;
88}
89
90use embedded_serial::{NonBlockingTx, NonBlockingRx};
91use byteorder::ByteOrder;
92
93#[cfg(feature = "collections")]
94use collections::vec::Vec;
95
96#[cfg(feature = "std")]
97mod serialwrap;
98
99#[cfg(feature = "std")]
100pub use serialwrap::SerialWrap;
101
102#[cfg(not(feature = "std"))]
103use collections::String;
104
105use core::fmt::Display;
106
107#[cfg(not(feature = "std"))]
108use core::fmt::Debug;
109
110#[cfg(feature = "std")]
111use std::error::Error as StdError;
112
113/// A replacement for std::error::Error
114#[cfg(not(feature = "std"))]
115pub trait StdError: Debug + Display {
116 /// A short description of the error.
117 ///
118 /// The description should not contain newlines or sentence-ending
119 /// punctuation, to facilitate embedding in larger user-facing
120 /// strings.
121 fn description(&self) -> &str;
122
123 /// The lower-level cause of this error, if any.
124 fn cause(&self) -> Option<&StdError> { None }
125}
126
127/// A marker which appears only rarely in stream, used to catch frame start.
128pub const SENTINEL: u8 = 0xFF;
129
130struct HeaderState {
131 bytes: [u8; 2],
132 index: usize,
133}
134
135struct DataState {
136 length: usize,
137}
138
139enum RecvState {
140 Unknown,
141 Header(HeaderState),
142 Data(DataState),
143}
144
145enum WhatNext {
146 Sentinel,
147 Header,
148 Data,
149}
150
151struct SendingState{
152 what_next: WhatNext,
153 index: usize,
154 header_bytes: [u8; 2],
155 frame: Vec<u8>,
156}
157
158enum SendState {
159 NotSending,
160 Sending(SendingState),
161}
162
163/// The result of a `tick()`. Check for progress indication.
164pub struct TickProgress {
165 /// State of ongoing receive.
166 pub recv_is_done: bool,
167 /// State of ongoing send.
168 pub send_is_done: bool,
169}
170
171/// Error type.
172#[derive(Debug)]
173pub struct Error {
174 descr: String,
175}
176
177impl Error {
178 /// create a new Error
179 pub fn new(s: String) -> Error {
180 Error { descr: s }
181 }
182}
183
184impl StdError for Error {
185 fn description(&self) -> &str {
186 return &self.descr;
187 }
188}
189
190type Result<T> = core::result::Result<T,Error>;
191
192impl Display for Error {
193 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
194 write!(f, "Error: {}", self.description())
195 }
196}
197
198/// Wrapper around a serial port to provide framed connections.
199///
200/// See the module level documentation for more information.
201pub struct FramedConnection<S>
202 where S : NonBlockingRx + NonBlockingTx,
203{
204 serial: S,
205 recv_buf: Vec<u8>,
206 recv_state: RecvState,
207 send_state: SendState,
208}
209
210impl<S> FramedConnection<S>
211 where S : NonBlockingRx + NonBlockingTx,
212{
213 /// Create a new `FramedConnection`. Takes ownership of the serial device.
214 pub fn new(s:S) -> FramedConnection<S> {
215 FramedConnection {
216 serial:s,
217 recv_buf: Vec::new(),
218 recv_state: FramedConnection::<S>::_start_recv_state(),
219 send_state: FramedConnection::<S>::_start_send_state(),
220 }
221 }
222
223 fn _start_recv_state() -> RecvState {
224 RecvState::Unknown
225 }
226
227 fn _start_send_state() -> SendState {
228 SendState::NotSending
229 }
230
231 /// Schedule a frame to be sent. Returns `Err(Error)` if the frame is too long,
232 /// otherwise returns immediately with `Ok(())`.
233 pub fn schedule_send(&mut self, frame: Vec<u8>) -> Result<()> {
234 match self.send_state {
235 SendState::NotSending => {}
236 SendState::Sending(_) => {
237 return Err(Error::new("Previous send in progress. Hint: block_until_send_done().".into()));
238 }
239 }
240
241 if frame.len() > u16::max_value() as usize {
242 return Err(Error::new("frame data too long".into()));
243 }
244 let mut buf = [0; 2];
245 byteorder::LittleEndian::write_u16(&mut buf, frame.len() as u16);
246 self.send_state = SendState::Sending( {
247 SendingState{
248 what_next: WhatNext::Sentinel,
249 index: 0,
250 header_bytes: buf,
251 frame: frame,
252 }});
253 Ok(())
254 }
255
256 /// Wait until previous send is done.
257 pub fn block_until_send_done(&mut self) -> Result<()> {
258 loop {
259 match self.send_state {
260 SendState::NotSending => {break;}
261 SendState::Sending(_) => {}
262 }
263 self.tick()?;
264 }
265 Ok(())
266 }
267
268 /// Service the connection.
269 pub fn tick(&mut self) -> Result<TickProgress> {
270 Ok(TickProgress {
271 send_is_done: self._send_tick()?,
272 recv_is_done: self._recv_tick()?,
273 })
274 }
275
276 /// return bool to describe whether send is done.
277 fn _send_tick(&mut self) -> Result<bool> {
278 match self.send_state {
279 SendState::NotSending => {
280 return Ok(true);
281 },
282 SendState::Sending(ref mut s) => {
283 loop {
284 // while we are not blocked on send, keep sending.
285 let byte = match s.what_next {
286 WhatNext::Sentinel => SENTINEL,
287 WhatNext::Header => s.header_bytes[s.index],
288 WhatNext::Data => s.frame[s.index],
289 };
290 match self.serial.putc_try(byte) {
291 Ok(Some(_)) => {
292 s.index += 1;
293 let mut new_next: Option<WhatNext> = None;
294 match s.what_next {
295 WhatNext::Sentinel => {
296 new_next = Some(WhatNext::Header);
297 s.index = 0;
298 },
299 WhatNext::Header => {
300 if s.index == 2 {
301 new_next = Some(WhatNext::Data);
302 s.index = 0;
303 }
304 },
305 WhatNext::Data => {
306 if s.index == s.frame.len() {
307 // don't send more
308 break;
309 }
310 },
311 }
312 if let Some(nn) = new_next {
313 s.what_next = nn;
314 }
315 },
316 Ok(None) => {
317 return Ok(false);
318 },
319 Err(_) => {
320 return Err(Error::new("unexpected error during putc_try()".into()));
321 }
322 }
323 }
324 }
325 }
326 // we have completed sending a frame
327 self.send_state = SendState::NotSending;
328 Ok(true)
329 }
330
331 /// return bool to describe whether recv is done.
332 fn _recv_tick(&mut self) -> Result<bool> {
333
334 loop {
335 // While we get characters, keep looping.
336
337 if self.is_frame_complete() {
338 return Ok(true);
339 }
340
341 match self.serial.getc_try() {
342 Ok(Some(byte)) => {
343 let mut new_state: Option<RecvState> = None;
344 match self.recv_state {
345 RecvState::Unknown => {
346 if byte == SENTINEL {
347 new_state = Some(RecvState::Header(HeaderState{bytes: [0, 0], index: 0}))
348 }
349 },
350 RecvState::Header(ref mut hs) => {
351 hs.bytes[hs.index] = byte;
352 hs.index += 1;
353 if hs.index == 2 {
354 let ds = DataState {
355 length: byteorder::LittleEndian::read_u16(&hs.bytes) as usize,
356 };
357 new_state = Some(RecvState::Data(ds));
358 }
359 },
360 RecvState::Data(ref mut ds) => {
361 self.recv_buf.push(byte);
362 if self.recv_buf.len() == ds.length {
363 // this frame is complete, stop polling for new data
364 return Ok(true);
365 }
366 },
367 };
368 if let Some(ns) = new_state {
369 self.recv_state=ns;
370 }
371 },
372 Ok(None) => {
373 // no more data available
374 break;
375 },
376 Err(_) => {
377 return Err(Error::new("unexpected error during getc_try()".into()))
378 },
379 };
380
381 }
382 Ok(false)
383 }
384
385 /// Check if frame is complete.
386 fn is_frame_complete(&mut self) -> bool {
387 match self.recv_state {
388 RecvState::Unknown | RecvState::Header(_) => false,
389 RecvState::Data(ref ds) => ds.length == self.recv_buf.len(),
390 }
391 }
392
393 /// Get completed frame.
394 pub fn get_frame(&mut self) -> Result<Vec<u8>> {
395 let frame = match self.recv_state {
396 RecvState::Unknown | RecvState::Header(_) => {
397 return Err(Error::new("frame not available".into()));
398 },
399 RecvState::Data(ref ds) => {
400 if self.recv_buf.len() == ds.length {
401 let mut frame = Vec::with_capacity(0);
402 core::mem::swap(&mut self.recv_buf,&mut frame);
403 frame
404 } else {
405 return Err(Error::new("frame not available".into()));
406 }
407 },
408 };
409 self.recv_state = FramedConnection::<S>::_start_recv_state();
410 Ok(frame)
411 }
412
413}