serial_unit_testing/serial/mod.rs
1/*
2 * File: src/serial/mod.rs
3 * Date: 30.09.2018
4 * Author: MarkAtk
5 *
6 * MIT License
7 *
8 * Copyright (c) 2018 MarkAtk
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy of
11 * this software and associated documentation files (the "Software"), to deal in
12 * the Software without restriction, including without limitation the rights to
13 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14 * of the Software, and to permit persons to whom the Software is furnished to do
15 * so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29use std::boxed::Box;
30use std::str;
31use std::time::Duration;
32use serialport;
33use crate::utils;
34use crate::error::{Result, Error};
35
36pub mod settings;
37mod loopback;
38
39use loopback::Loopback;
40
41/// A serial port connection.
42///
43/// This struct handles the complete communication with a serial device regardless of the platform.
44pub struct Serial {
45 port: Box<dyn serialport::SerialPort>,
46 read_buffer: Vec<u8>
47}
48
49impl Serial {
50 /// Open a new connection with default settings.
51 ///
52 /// The port name is platform specific, e.g. starts with `COM` on Windows and `/dev/tty` or similar on UNIX systems.
53 ///
54 /// Default settings are:
55 /// - Baud rate: 9600
56 /// - Timeout: 1000 (ms)
57 /// - Data bits: 8
58 /// - Parity: None,
59 /// - Stop bits: 1,
60 /// - Flow control: None
61 ///
62 /// # Example
63 ///
64 /// ```
65 /// use serial_unit_testing::serial::Serial;
66 /// use serial_unit_testing::error::Result;
67 ///
68 /// fn main() -> Result<()> {
69 /// let mut serial = Serial::open("/dev/ttyACM0")?;
70 /// serial.write("Hello World!")?;
71 ///
72 /// Ok(())
73 /// }
74 ///
75 /// ```
76 pub fn open(port_name: &str) -> Result<Serial> {
77 let settings: settings::Settings = Default::default();
78
79 Serial::open_with_settings(port_name, settings)
80 }
81
82 /// Open a new connection with given settings.
83 ///
84 /// The port name is platform specific, e.g. starts with `COM` on Windows and `/dev/tty` or similar on UNIX systems.
85 ///
86 /// # Example
87 ///
88 /// ```
89 /// use serial_unit_testing::serial::Serial;
90 /// use serial_unit_testing::serial::settings::Settings;
91 /// use serial_unit_testing::error::Result;
92 ///
93 /// fn main() -> Result<()> {
94 /// let mut settings = Settings::default();
95 /// settings.baud_rate = 115200;
96 ///
97 /// let mut serial = Serial::open_with_settings("/dev/ttyACM0", &settings)?;
98 /// serial.write("Hello World!")?;
99 ///
100 /// Ok(())
101 /// }
102 ///
103 /// ```
104 pub fn open_with_settings(port_name: &str, settings: settings::Settings) -> Result<Serial> {
105 let serial_port_settings = settings.into();
106
107 if port_name == "loopback" {
108 let port = Loopback::open(serial_port_settings);
109
110 return Ok(Serial {
111 port,
112 read_buffer: vec![0; 1000]
113 });
114 }
115
116 match serialport::open_with_settings(&port_name, &serial_port_settings) {
117 Ok(port) => {
118 Ok(Serial { port, read_buffer: vec![0; 1000] })
119 },
120 Err(e) => Err(Error::from(e))
121 }
122 }
123
124 /// Get the current serial settings.
125 pub fn settings(&self) -> settings::Settings {
126 return self.port.settings().into();
127 }
128
129 /// Get the port name if any exists.
130 ///
131 /// Virtual ports may not have a name and the name may be shortened.
132 pub fn name(&self) -> Option<String> {
133 return self.port.name();
134 }
135
136 /// Set the baud rate.
137 pub fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
138 match self.port.set_baud_rate(baud_rate) {
139 Ok(_) => Ok(()),
140 Err(e) => Err(Error::from(e))
141 }
142 }
143
144 /// Get the baud rate.
145 ///
146 /// This will return the actual device baud rate, which may differ from the last specified value.
147 pub fn baud_rate(&self) -> Result<u32> {
148 match self.port.baud_rate() {
149 Ok(value) => Ok(value),
150 Err(e) => Err(Error::from(e))
151 }
152 }
153
154 /// Set the number of data bits.
155 pub fn set_data_bits(&mut self, data_bits: settings::DataBits) -> Result<()> {
156 match self.port.set_data_bits(data_bits.into()) {
157 Ok(_) => Ok(()),
158 Err(e) => Err(Error::from(e))
159 }
160 }
161
162 /// Get the number of data bits.
163 pub fn data_bits(&self) -> Result<settings::DataBits> {
164 match self.port.data_bits() {
165 Ok(value) => Ok(settings::DataBits::from(value)),
166 Err(e) => Err(Error::from(e))
167 }
168 }
169
170 /// Set the parity checking mode
171 pub fn set_parity(&mut self, parity: settings::Parity) -> Result<()> {
172 match self.port.set_parity(parity.into()) {
173 Ok(_) => Ok(()),
174 Err(e) => Err(Error::from(e))
175 }
176 }
177
178 /// Get the parity checking mode
179 pub fn parity(&self) -> Result<settings::Parity> {
180 match self.port.parity() {
181 Ok(value) => Ok(settings::Parity::from(value)),
182 Err(e) => Err(Error::from(e))
183 }
184 }
185
186 /// Set the number of stop bits.
187 pub fn set_stop_bits(&mut self, stop_bits: settings::StopBits) -> Result<()> {
188 match self.port.set_stop_bits(stop_bits.into()) {
189 Ok(_) => Ok(()),
190 Err(e) => Err(Error::from(e))
191 }
192 }
193
194 /// Get the number of stop bits.
195 pub fn stop_bits(&self) -> Result<settings::StopBits> {
196 match self.port.stop_bits() {
197 Ok(value) => Ok(settings::StopBits::from(value)),
198 Err(e) => Err(Error::from(e))
199 }
200 }
201
202 /// Set the flow control.
203 pub fn set_flow_control(&mut self, flow_control: settings::FlowControl) -> Result<()> {
204 match self.port.set_flow_control(flow_control.into()) {
205 Ok(_) => Ok(()),
206 Err(e) => Err(Error::from(e))
207 }
208 }
209
210 /// Get the flow control.
211 pub fn flow_control(&self) -> Result<settings::FlowControl> {
212 match self.port.flow_control() {
213 Ok(value) => Ok(settings::FlowControl::from(value)),
214 Err(e) => Err(Error::from(e))
215 }
216 }
217
218 /// Set the I/O timeout.
219 pub fn set_timeout(&mut self, timeout: u64) -> Result<()> {
220 match self.port.set_timeout(Duration::from_millis(timeout)) {
221 Ok(_) => Ok(()),
222 Err(e) => Err(Error::from(e))
223 }
224 }
225
226 /// Get the I/O timeout.
227 pub fn timeout(&self) -> u64 {
228 return self.port.timeout().as_millis() as u64;
229 }
230
231 /// Write text to the serial port.
232 ///
233 /// This is the same as using `Serial::write_format` with `TextFormat::Text` as format specifier.
234 pub fn write(&mut self, text: &str) -> Result<usize> {
235 match self.port.write(text.as_bytes()) {
236 Ok(count) => Ok(count),
237 Err(e) => Err(Error::from(e))
238 }
239 }
240
241 /// Write data in the given format.
242 ///
243 /// For a list of supported formats see `TextFormat`. `TextFormat::Text` is the same as using `Serial::write`.
244 ///
245 /// # Example
246 ///
247 /// ```
248 /// use serial_unit_testing::serial::Serial;
249 /// use serial_unit_testing::utils::TextFormat;
250 /// use serial_unit_testing::error::Result;
251 ///
252 /// fn main() -> Result<()> {
253 /// let mut serial = Serial::open("/dev/ttyACM0")?;
254 /// serial.write_format("0a5f", TextFormat::Hex)?;
255 ///
256 /// Ok(())
257 /// }
258 ///
259 /// ```
260 pub fn write_format(&mut self, text: &str, text_format: utils::TextFormat) -> Result<usize> {
261 let bytes = match text_format {
262 utils::TextFormat::Binary => utils::bytes_from_binary_string(text)?,
263 utils::TextFormat::Octal => utils::bytes_from_octal_string(text)?,
264 utils::TextFormat::Decimal => utils::bytes_from_decimal_string(text)?,
265 utils::TextFormat::Hex => utils::bytes_from_hex_string(text)?,
266 _ => {
267 let mut bytes = Vec::new();
268 bytes.extend_from_slice(text.as_bytes());
269
270 bytes
271 }
272 };
273
274 match self.port.write(bytes.as_slice()) {
275 Ok(count) => Ok(count),
276 Err(e) => Err(Error::from(e))
277 }
278 }
279
280 /// Read any amount of data.
281 ///
282 /// At least one byte of data must be read to return data. The method fails when no data could be read in the timeout duration.
283 ///
284 /// # Example
285 ///
286 /// ```
287 /// use serial_unit_testing::serial::Serial;
288 /// use serial_unit_testing::error::Result;
289 ///
290 /// fn main() -> Result<()> {
291 /// let mut serial = Serial::open("/dev/ttyACM0")?;
292 /// let data = serial.read().unwrap();
293 ///
294 /// Ok(())
295 /// }
296 /// ```
297 pub fn read(&mut self) -> Result<&[u8]> {
298 let length = match self.port.read(&mut self.read_buffer) {
299 Ok(length) => length,
300 Err(e) => return Err(Error::from(e))
301 };
302
303 Ok(&self.read_buffer[..length])
304 }
305
306 /// Read a string.
307 ///
308 /// At least one character must be read to return successfully. The method fails when no characters could be read in the timeout duration.
309 pub fn read_str(&mut self) -> Result<String> {
310 self.read_str_with_format(utils::TextFormat::Text)
311 }
312
313 /// Read a string until desired substring is found.
314 ///
315 /// At least one character and desired substring must be read to return successfully. The method fails when no characters could be read in the timeout
316 /// duration.
317 pub fn read_str_until(&mut self, desired: &str) -> Result<String> {
318 let mut result = String::new();
319
320 loop {
321 match self.read_str() {
322 Ok(chunk) => result += &chunk,
323 Err(e) => return Err(Error::from(e))
324 }
325
326 if result.contains(desired) {
327 break;
328 }
329 }
330
331 Ok(result)
332 }
333
334 /// Read a string with minimum length until desired substring is found.
335 ///
336 /// At least the amount of characters given by `min_length` must be read to return successfully. The method fails when no characters could be read in the
337 /// timeout duration.
338 pub fn read_min_str(&mut self, min_length: usize) -> Result<String> {
339 self.read_min_str_with_format(min_length, utils::TextFormat::Text)
340 }
341
342 /// Read a string with minimum length.
343 ///
344 /// At least the amount of characters given by `min_length` and desired substring must be read to return successfully. The method fails when no characters
345 /// could be read in the timeout duration.
346 pub fn read_min_str_until(&mut self, min_length: usize, desired: &str) -> Result<String> {
347 let mut result = String::new();
348
349 loop {
350 match self.read_str() {
351 Ok(chunk) => result += &chunk,
352 Err(e) if e.is_timeout() => {
353 if result.len() >= min_length && result.contains(desired) {
354 break;
355 }
356
357 return Err(Error::from(e));
358 },
359 Err(e) => return Err(Error::from(e))
360 }
361
362 if result.len() >= min_length && result.contains(desired) {
363 break;
364 }
365 }
366
367 Ok(result)
368 }
369
370 /// Read a string as given format.
371 ///
372 /// The bytes received will be formatted into the given string format.
373 ///
374 /// At least one character must be read to return successfully. The method fails when no characters could be read in the timeout duration.
375 pub fn read_str_with_format(&mut self, format: utils::TextFormat) -> Result<String> {
376 let data = self.read()?;
377
378 utils::radix_string(data, &format)
379 }
380
381 /// Read a string as given format.
382 ///
383 /// The bytes received will be formatted into the given string format.
384 ///
385 /// At least the amount of characters (not bytes) given by `min_length` must be read to return successfully.
386 /// The method fails when no characters could be read in the timeout duration.
387 pub fn read_min_str_with_format(&mut self, min_length: usize, format: utils::TextFormat) -> Result<String> {
388 let mut response = String::new();
389
390 loop {
391 match self.read() {
392 Ok(bytes) => {
393 let new_text = utils::radix_string(bytes, &format)?;
394
395 response.push_str(new_text.as_str());
396
397 if response.len() >= min_length {
398 break;
399 }
400 },
401 Err(e) if e.is_timeout() => {
402 if response.len() < min_length {
403 return Err(e);
404 }
405
406 break;
407 },
408 Err(e) => return Err(e)
409 }
410 }
411
412 Ok(response)
413 }
414
415 /// Read any amount of data in given timeout duration.
416 ///
417 /// This function can be used to use a different timeout for a single read. Otherwise see the timeout property of serial.
418 ///
419 /// At least one byte of data must be read to return data. The method fails when no data could be read in the timeout duration.
420 pub fn read_with_timeout(&mut self, timeout: Duration) -> Result<&[u8]> {
421 // remember old timeout
422 let old_timeout = self.port.timeout();
423 if let Err(e) = self.port.set_timeout(timeout) {
424 return Err(Error::from(e));
425 }
426
427 let length = self.port.read(&mut self.read_buffer)?;
428
429 if let Err(e) = self.port.set_timeout(old_timeout) {
430 return Err(Error::from(e));
431 }
432
433 Ok(&self.read_buffer[..length])
434 }
435
436 /// Read a string in given timeout duration.
437 ///
438 /// This function can be used to use a different timeout for a single read. Otherwise see the timeout property of serial.
439 ///
440 /// At least one character must be read to return successfully. The method fails when no data could be read in the timeout duration.
441 pub fn read_str_with_timeout(&mut self, timeout: Duration) -> Result<String> {
442 // remember old timeout
443 let old_timeout = self.port.timeout();
444 if let Err(e) = self.port.set_timeout(timeout) {
445 return Err(Error::from(e));
446 }
447
448 let length = self.port.read(&mut self.read_buffer)?;
449
450 if let Err(e) = self.port.set_timeout(old_timeout) {
451 return Err(Error::from(e));
452 }
453
454 match str::from_utf8(&self.read_buffer[..length]) {
455 Ok(text) => Ok(text.to_string()),
456 Err(e) => Err(Error::from(e))
457 }
458 }
459
460 /// Read a string with minimum length in given timeout duration.
461 ///
462 /// This function can be used to use a different timeout for a single read. Otherwise see the timeout property of serial.
463 ///
464 /// At least the amount of characters given by `min_length` must be read to return successfully. The method fails when no characters could be read in the
465 /// given timeout duration.
466 pub fn read_min_str_with_timeout(&mut self, min_length: usize, timeout: Duration) -> Result<String> {
467 self.read_min_str_with_format_and_timeout(min_length, utils::TextFormat::Text, timeout)
468 }
469
470 /// Read a string until desired substring is found in given timeout duration.
471 ///
472 /// This function can be used to use a different timeout for a single read. Otherwise see the timeout property of serial.
473 ///
474 /// At least one character and desired substring must be read to return successfully. The method fails when no characters could be read in the timeout
475 /// duration.
476 pub fn read_str_until_with_timeout(&mut self, desired: &str, timeout: Duration) -> Result<String> {
477 let mut result = String::new();
478
479 loop {
480 match self.read_str_with_timeout(timeout) {
481 Ok(chunk) => result += &chunk,
482 Err(e) => return Err(Error::from(e))
483 }
484
485 if result.contains(desired) {
486 break;
487 }
488 }
489
490 Ok(result)
491 }
492
493 /// Read a string as given format in given timeout duration.
494 ///
495 /// The bytes received will be formatted into the given string format.
496 /// This function can be used to use a different timeout for a single read. Otherwise see the timeout property of serial.
497 ///
498 /// At least one character must be read to return successfully. The method fails when no characters could be read in the timeout duration.
499 pub fn read_str_with_format_and_timeout(&mut self, format: utils::TextFormat, timeout: Duration) -> Result<String> {
500 // remember old timeout
501 let old_timeout = self.port.timeout();
502 if let Err(e) = self.port.set_timeout(timeout) {
503 return Err(Error::from(e));
504 }
505
506 let length = self.port.read(&mut self.read_buffer)?;
507 let data = &self.read_buffer[..length];
508
509 if let Err(e) = self.port.set_timeout(old_timeout) {
510 return Err(Error::from(e));
511 }
512
513 utils::radix_string(data, &format)
514 }
515
516 /// Read a string with minimum length as given format in given timeout duration.
517 ///
518 /// The bytes received will be formatted into the given string format.
519 /// This function can be used to use a different timeout for a single read. Otherwise see the timeout property of serial.
520 ///
521 /// At least the amount of characters given by `min_length` must be read to return successfully. The method fails when no characters could be read in the
522 /// given timeout duration.
523 pub fn read_min_str_with_format_and_timeout(&mut self, min_length: usize, format: utils::TextFormat, timeout: Duration) -> Result<String> {
524 // remember old timeout
525 let old_timeout = self.port.timeout();
526 if let Err(e) = self.port.set_timeout(timeout) {
527 return Err(Error::from(e));
528 }
529
530 let mut response = String::new();
531
532 loop {
533 match self.read() {
534 Ok(bytes) => {
535 let new_text = utils::radix_string(bytes, &format)?;
536
537 response.push_str(new_text.as_str());
538
539 if response.len() >= min_length {
540 break;
541 }
542 },
543 Err(e) if e.is_timeout() => {
544 if response.len() < min_length {
545 if let Err(e) = self.port.set_timeout(old_timeout) {
546 return Err(Error::from(e));
547 }
548
549 return Err(e);
550 }
551
552 break;
553 },
554 Err(e) => {
555 if let Err(e) = self.port.set_timeout(old_timeout) {
556 return Err(Error::from(e));
557 }
558
559 return Err(e);
560 }
561 }
562 }
563
564 if let Err(e) = self.port.set_timeout(old_timeout) {
565 return Err(Error::from(e));
566 }
567
568 Ok(response)
569 }
570
571 /// Send text to the serial and check if the response matches the desired response.
572 ///
573 /// The check will return early if the beginning of the responses does not match.
574 ///
575 /// Returns whether the actual response matches the desired response and the actual response. Fails with an timeout error or internal serial error.
576 ///
577 /// # Example
578 ///
579 /// ```
580 /// use serial_unit_testing::serial::Serial;
581 /// use serial_unit_testing::error::Result;
582 ///
583 /// fn main() -> Result<()> {
584 /// let mut serial = Serial::open("/dev/ttyACM0")?;
585 /// let (result, actual_response) = serial.check("hello", "world")?;
586 ///
587 /// Ok(())
588 /// }
589 /// ```
590 pub fn check(&mut self, text: &str, desired_response: &str) -> Result<(bool, String)> {
591 let settings: CheckSettings = Default::default();
592
593 self.check_with_settings(text, desired_response, &settings)
594 }
595
596 /// Check if a response matches the desired response.
597 ///
598 /// The check will return early if the beginning of the responses does not match.
599 ///
600 /// Returns whether the actual response matches the desired response and the actual response. Fails with an timeout error or internal serial error.
601 pub fn check_read(&mut self, desired_response: &str) -> Result<(bool, String)> {
602 let settings: CheckSettings = Default::default();
603
604 self.check_read_with_settings(desired_response, &settings)
605 }
606
607 /// Send text to the serial and check if the response matches the desired response with given settings.
608 ///
609 /// The check will return early if the beginning of the responses does not match.
610 ///
611 /// Returns whether the actual response matches the desired response and the actual response. Fails with an timeout error or internal serial error.
612 pub fn check_with_settings(&mut self, text: &str, desired_response: &str, settings: &CheckSettings) -> Result<(bool, String)> {
613 self.write_format(text, settings.input_format)?;
614
615 self.check_read_with_settings(desired_response, settings)
616 }
617
618 /// Check if a response matches desired response with given settings.
619 ///
620 /// The check will return early if the beginning of the responses does not match.
621 ///
622 /// Returns whether the actual response matches the desired response and the actual response. Fails with an timeout error or internal serial error.
623 pub fn check_read_with_settings(&mut self, desired_response: &str, settings: &CheckSettings) -> Result<(bool, String)> {
624 let mut response = String::new();
625
626 // convert hex to upper case because actual hex output is returned in upper case letters
627 let compare = if settings.output_format == utils::TextFormat::Hex {
628 desired_response.to_uppercase()
629 } else {
630 desired_response.to_string()
631 };
632
633 loop {
634 match self.read() {
635 Ok(bytes) => {
636 let mut new_text = utils::radix_string(bytes, &settings.output_format)?;
637
638 if settings.ignore_case {
639 new_text = new_text.to_lowercase();
640 }
641
642 response.push_str(new_text.as_str());
643
644 if compare == response {
645 break;
646 }
647
648 if compare.starts_with(response.as_str()) == false {
649 break;
650 }
651 },
652 Err(e) if e.is_timeout() => {
653 if response.len() == 0 {
654 return Err(e);
655 }
656
657 break;
658 },
659 Err(e) => return Err(e)
660 }
661 }
662
663 Ok((compare == response, response))
664 }
665}
666
667/// Settings for running tests on a serial port.
668pub struct CheckSettings {
669 /// Ignore response case mode.
670 pub ignore_case: bool,
671 /// Format of the data written to the serial port.
672 pub input_format: utils::TextFormat,
673 /// Format of the data received by the serial port.
674 pub output_format: utils::TextFormat
675}
676
677impl Default for CheckSettings {
678 fn default() -> CheckSettings {
679 CheckSettings {
680 ignore_case: false,
681 input_format: utils::TextFormat::Text,
682 output_format: utils::TextFormat::Text
683 }
684 }
685}