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}