1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::fmt::{self, Display, Formatter};

/// Data Link Escape.
const DLE: u8 = 0x10;
/// Start of TeXt.
const STX: u8 = 0x02;
/// End of TeXt.
const ETX: u8 = 0x03;
/// A control character that is use for negative acknowledgement.
const NAK: u8 = 0x15;

/// Header of messages.
const HEADER: [u8; 2] = [DLE, STX];
/// Footer of messages.
const FOOTER: [u8; 2] = [DLE, ETX];
/// Represents a communication error.
const NEGATIVE_ACKNOWLEDGEMENT: [u8; 2] = [DLE, NAK];

const HEADER_LENGTH: usize = HEADER.len();
const FOOTER_LENGTH: usize = FOOTER.len();
/// The length of BCC, Block Check Character.
const BCC_LENGTH: usize = 1;
/// The length of the shortest valid response.
const LENGTH_MIN: usize = HEADER_LENGTH + FOOTER_LENGTH + BCC_LENGTH;

/// Constructs a message to send a sensor.
pub(crate) fn parse_command(command: &[u8]) -> Vec<u8> {
    let mut buf = HEADER.to_vec();

    let mut bcc = 0;

    // Append data part.
    // If data has DLE, we need to append DLE twice in a row.
    for byte in command.iter().copied() {
        if byte == DLE {
            buf.push(byte);
        }
        buf.push(byte);
        bcc ^= byte;
    }

    for byte in FOOTER.iter().copied() {
        buf.push(byte);
        if byte != DLE {
            bcc ^= byte;
        }
    }

    buf.push(bcc);

    buf
}

/// Extract data part of a received message from a sensor.
/// # Returns
/// `Ok(data)` if succeeds, `Err(reason)` if an invalid message is specified.
pub(crate) fn parse_reception(message: &[u8]) -> Result<Vec<u8>, ParseError> {
    if message == &NEGATIVE_ACKNOWLEDGEMENT[..] {
        return Err(ParseError::NAK);
    }

    let len = message.len();
    if len < LENGTH_MIN {
        return Err(ParseError::TooShort(len));
    }

    // Message is long enough here, so indexing and unwrap() never panic below.

    // Integrity of header and footer.

    // Extract data part.
    let mut buf = vec![];
    let mut is_previous_dle = false;
    let mut expected_bcc = 0;

    for &byte in &message[HEADER_LENGTH..message.len() - FOOTER_LENGTH - BCC_LENGTH] {
        if is_previous_dle {
            if byte != DLE {
                return Err(ParseError::WrongDLE);
            }
            is_previous_dle = false;
        } else {
            buf.push(byte);
            expected_bcc ^= byte;
            is_previous_dle = byte == DLE;
        }
    }

    FOOTER
        .iter()
        .filter(|&&byte| byte != DLE)
        .for_each(|&byte| expected_bcc ^= byte);

    // Integrity of BCC.
    let actual_bcc = *message.last().unwrap();
    if expected_bcc != actual_bcc {
        return Err(ParseError::WrongBCC);
    }

    Ok(buf)
}

/// Represents an error that occurred during parsing a received message from a sensor.
#[derive(Debug)]
pub enum ParseError {
    /// Received an negative acknowledgement.
    NAK,
    /// Received a too short message.
    TooShort(usize),
    /// DLE must be contained twice or more in a row, but was not.
    WrongDLE,
    /// An communication error was detected.
    WrongBCC,
}

impl Display for ParseError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            ParseError::NAK => write!(f, "Received an negative acknowledgement."),
            ParseError::TooShort(l) => write!(f, "Received too short ({} bytes) message,", l),
            ParseError::WrongDLE => write!(
                f,
                "DLE must be contained twice or more in a row, but was not."
            ),
            ParseError::WrongBCC => write!(f, "Wrong BCC."),
        }
    }
}

impl std::error::Error for ParseError {}