diff --git a/README.md b/README.md index cf1d80b..4867eb2 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ StatusWord, and DataWord. ```rust use mil_std_1553b::*; - let message = Message::new() + let message: Message = Message::new() .with_command(CommandWord::new() .with_address(Address::Value(12)) .with_subaddress(SubAddress::Value(5)) @@ -37,7 +37,7 @@ StatusWord, and DataWord. .with_data(DataWord::new()).unwrap() .with_data(DataWord::new()).unwrap(); - assert_eq!(message.word_count(),3); + assert_eq!(message.length(),3); ``` ### Parsing a message @@ -50,7 +50,7 @@ how many data words will be parsed from the buffer. ```rust use mil_std_1553b::*; - let message = Message::parse_command(&[ + let message: Message = Message::read_command(&[ 0b10000011, 0b00001100, 0b00100010, @@ -59,7 +59,7 @@ how many data words will be parsed from the buffer. ]) .unwrap(); - assert_eq!(message.word_count(),2); + assert_eq!(message.length(),2); ``` #### Status messages @@ -67,7 +67,7 @@ how many data words will be parsed from the buffer. ```rust use mil_std_1553b::*; - let message = Message::parse_status(&[ + let message: Message = Message::read_status(&[ 0b10000011, 0b00001100, 0b01000010, @@ -76,7 +76,7 @@ how many data words will be parsed from the buffer. ]) .unwrap(); - assert_eq!(message.word_count(), 2); + assert_eq!(message.length(), 2); ``` ### Parsing a word @@ -107,16 +107,16 @@ from strings. - [x] Words have parsing tests - [x] Words have conversion tests - [x] Documentation exists for words -- [ ] Messages implemented +- [x] Messages implemented - [x] Message struct is created - [x] Messages can be constructed from words - [x] Messages can be parsed from binary - [x] Messages have parsing tests - - [ ] Messages have conversion tests + - [x] Messages have conversion tests - [x] Documentation exists for messages - [ ] Integration tests implemented - - [ ] Round-trip tests (binary -> struct -> binary) exist for messages - - [ ] Round-trip tests (binary -> struct -> binary) exist for words + - [x] Round-trip tests (binary -> struct -> binary) exist for messages + - [x] Round-trip tests (binary -> struct -> binary) exist for words - [ ] Configuration tests (JSON) exist for words - [ ] Configuration tests (JSON) exist for messages diff --git a/src/lib.rs b/src/lib.rs index e082d13..e040235 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,7 +49,7 @@ pub use crate::message::{Message, MessageDirection, MessageSide, MessageType, Pa pub use crate::errors::{Error, MessageError, Result, SubsystemError, SystemError, TerminalError}; -pub use crate::word::{CommandWord, DataWord, StatusWord, Word, WordType}; +pub use crate::word::{CommandWord, DataWord, Header, StatusWord, Word, WordType}; pub use crate::flags::{ Address, BroadcastReceived, DynamicBusAcceptance, Instrumentation, ModeCode, Reserved, diff --git a/src/message/messages.rs b/src/message/messages.rs index 666b892..7bee8e8 100644 --- a/src/message/messages.rs +++ b/src/message/messages.rs @@ -1,6 +1,6 @@ use crate::word::WordType; use crate::word::{CommandWord, DataWord, StatusWord}; -use crate::{errors::*, Packet, Word}; +use crate::{errors::*, Header, Packet, Word}; /// A message sent between two terminals on the bus /// @@ -9,7 +9,7 @@ use crate::{errors::*, Packet, Word}; /// /// * Command or status words are always the first word. /// * Data words are limited based on the command word count. -/// * Messages can't exceed [max message size][Message::MAX_WORDS]. +/// * For status words, data words are parsed to the end of the buffer /// /// Messages do not validate larger messaging patterns that /// require context about previous messages or terminal type. @@ -19,7 +19,7 @@ use crate::{errors::*, Packet, Word}; /// ```rust /// # use mil_std_1553b::*; /// # fn try_main() -> Result<()> { -/// let message = Message::new() +/// let message: Message = Message::new() /// .with_command(CommandWord::new() /// .with_address(Address::Value(12)) /// .with_subaddress(SubAddress::Value(5)) @@ -30,38 +30,71 @@ use crate::{errors::*, Packet, Word}; /// .with_data(DataWord::new())?; /// /// assert!(message.is_full()); -/// assert_eq!(message.word_count(),3); -/// assert_eq!(message.data_count(),2); -/// assert_eq!(message.data_expected(),2); +/// assert_eq!(message.length(),3); +/// assert_eq!(message.count(),2); /// # Ok(()) /// # } /// ``` /// #[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct Message { +pub struct Message { count: usize, - words: [WordType; Self::MAX_WORDS], + words: [WordType; WORDS], } -impl Message { - /// The maximum number of words that a message can hold - /// - /// For messages which begin with a [StatusWord], this value - /// is equal to **one more than** the number of [DataWords][DataWord] - /// that the message will accept before it is full. For messages - /// which begin with a [CommandWord], this value is the maximum - /// number of words which may be returned by the - /// [word_count][CommandWord::word_count] method. - pub const MAX_WORDS: usize = 33; - +impl Message { /// Create a new message struct pub fn new() -> Self { Self { count: 0, - words: [WordType::None; Self::MAX_WORDS], + words: [WordType::None; WORDS], } } + /// Constructor method to add a command word to the message + /// + /// # Arguments + /// + /// * `word` - A word to add + /// + pub fn with_command>(mut self, word: T) -> Result { + self.add_command(word.into())?; + Ok(self) + } + + /// Constructor method to add a status word to the message + /// + /// # Arguments + /// + /// * `word` - A word to add + /// + pub fn with_status>(mut self, word: T) -> Result { + self.add_status(word.into())?; + Ok(self) + } + + /// Constructor method to add a data word to the message + /// + /// # Arguments + /// + /// * `word` - A word to add + /// + pub fn with_data>(mut self, word: T) -> Result { + self.add_data(word.into())?; + Ok(self) + } + + /// Constructor method to add a word to the message + /// + /// # Arguments + /// + /// * `word` - A word to add + /// + pub fn with_word(mut self, word: T) -> Result { + self.add(word)?; + Ok(self) + } + /// Parse a slice of bytes into a command message /// /// This method interpretes the byte array as a series @@ -71,7 +104,9 @@ impl Message { /// /// Each word is a triplet containing 3-bit sync, 16-bit word, /// and 1-bit parity. It is assumed that the message - /// being parsed is aligned to the beginning of the slice. + /// being parsed is aligned to the beginning of the slice + /// (the leftmost three bits of the first byte are the sync + /// field of the command word). /// /// # Arguments /// @@ -82,7 +117,7 @@ impl Message { /// ```rust /// # use mil_std_1553b::*; /// # fn try_main() -> Result<()> { - /// let message = Message::parse_command(&[ + /// let message: Message = Message::read_command(&[ /// 0b10000011, /// 0b00001100, /// 0b01010110, @@ -91,34 +126,13 @@ impl Message { /// ])?; /// /// assert!(message.is_full()); - /// assert!(message.has_command()); - /// assert_eq!(message.word_count(),2); - /// assert_eq!(message.data_count(),1); + /// assert!(message.is_command()); + /// assert_eq!(message.length(),2); + /// assert_eq!(message.count(),1); /// # Ok(()) /// # } - pub fn parse_command(data: &[u8]) -> Result { - // get the first word as a command word - let mut message = Self::new().with_command(Packet::parse(data, 0)?.to_command()?)?; - - // get the number of data words expected - let num = message.data_expected(); - - let sbit = 20; // starting data bit - let ebit = 20 * (num + 1); // ending data bit - - // iterate chunks of 20 bits for each word - for bit in (sbit..ebit).step_by(20) { - let index = bit / 8; // byte index in the slice - let offset = bit % 8; // bit index in the last byte - - // get a trimmed slice to parse - let bytes = &data[index..]; - - // parse as a data word and add - message.add_data(Packet::parse(bytes, offset)?.to_data()?)?; - } - - Ok(message) + pub fn read_command(data: &[u8]) -> Result { + Self::read::(data) } /// Parse a slice of bytes into a status message @@ -130,18 +144,22 @@ impl Message { /// byte array. Slice the input data to avoid parsing /// any unwanted words. /// + /// It is assumed that the message being parsed is aligned + /// to the beginning of the slice (the leftmost three bits + /// of the first byte are the sync field of the status word). + /// /// # Arguments /// /// * `data` - A slice of bytes to parse /// - /// See [parse_command][Message::parse_command] for more information. + /// See [read_command][Message::read_command] for more information. /// /// ## Example /// /// ```rust /// # use mil_std_1553b::*; /// # fn try_main() -> Result<()> { - /// let message = Message::parse_status(&[ + /// let message: Message = Message::read_status(&[ /// 0b10000011, /// 0b00001100, /// 0b01010110, @@ -152,133 +170,47 @@ impl Message { /// // the message is not full because we haven't hit /// // the maximum number of words. /// assert!(!message.is_full()); - /// assert!(message.has_status()); - /// assert_eq!(message.word_count(),2); - /// assert_eq!(message.data_count(),1); + /// assert!(message.is_status()); + /// assert_eq!(message.length(),2); + /// assert_eq!(message.count(),1); /// # Ok(()) /// # } /// ``` - pub fn parse_status(data: &[u8]) -> Result { - // get the first word as a status word - let mut message = Self::new().with_status(Packet::parse(data, 0)?.to_status()?)?; - - let bits = data.len() * 8; - let step = 20; - let sbit = step; - let ebit = (bits / step) * step; - - // iterate chunks of 20 bits for each word - for bit in (sbit..ebit).step_by(step) { - let index = bit / 8; // byte index in the slice - let offset = bit % 8; // offset into a byte - - // get a trimmed slice to parse - let bytes = &data[index..]; - - // parse as a data word and add - message.add_data(Packet::parse(bytes, offset)?.to_data()?)?; - } - - Ok(message) + pub fn read_status(data: &[u8]) -> Result { + Self::read::(data) } - /// Check if the message is full + /// Get the command word from the message /// - /// This method will return false for status messages - /// until the [maximum number of data words][Message::MAX_WORDS] - /// has been added. - #[must_use = "Returned value is not used"] - pub fn is_full(&self) -> bool { - if self.has_command() { - self.data_count() == self.data_expected() + /// Returns `None` if this message doesn't + /// have a command word. + /// + /// # Arguments + /// + /// * `index` - An index + /// + pub fn command(&self) -> Option<&CommandWord> { + if let Some(WordType::Command(w)) = &self.words.get(0) { + Some(w) } else { - self.count == self.words.len() - } - } - - /// Check if the message is empty - #[must_use = "Returned value is not used"] - pub fn is_empty(&self) -> bool { - self.count == 0 - } - - /// Clear all words from the message - pub fn clear(&mut self) { - self.count = 0; - self.words = [WordType::None; Self::MAX_WORDS]; - } - - /// Get the last word in the message - pub fn last(&self) -> Option<&WordType> { - match self.count { - 0 => None, - i => self.words.get(i - 1), - } - } - - /// Get the first word in the message - pub fn first(&self) -> Option<&WordType> { - match self.count { - 0 => None, - _ => self.words.get(0), + None } } - /// Get the number of words - pub fn word_count(&self) -> usize { - self.count - } - - /// Get the number of data words - pub fn data_count(&self) -> usize { - self.words - .iter() - .take_while(|w| w.is_some()) - .filter(|w| w.is_data()) - .count() - } - - /// Get the expected number of data words - pub fn data_expected(&self) -> usize { - self.first().map(WordType::data_count).unwrap_or(0) - } - - /// Check if message has data words - #[must_use = "Returned value is not used"] - pub fn has_data(&self) -> bool { - self.data_count() > 0 - } - - /// Check if message can contain more data words - #[must_use = "Returned value is not used"] - pub fn has_space(&self) -> bool { - self.data_count() < self.data_expected() - } - - /// Check if message starts with a command word - #[must_use = "Returned value is not used"] - pub fn has_command(&self) -> bool { - self.first().map(WordType::is_command).unwrap_or(false) - } - - /// Check if message starts with a status word - #[must_use = "Returned value is not used"] - pub fn has_status(&self) -> bool { - self.first().map(WordType::is_status).unwrap_or(false) - } - - /// Add a word to the message, returning size on success + /// Get the status word from the message + /// + /// Returns `None` if this message doesn't + /// have a status word. /// /// # Arguments /// - /// * `word` - A word to add + /// * `index` - An index /// - pub fn add>(&mut self, word: T) -> Result<()> { - match word.into() { - WordType::Data(v) => self.add_data(v), - WordType::Status(v) => self.add_status(v), - WordType::Command(v) => self.add_command(v), - _ => Err(Error::WordIsInvalid), + pub fn status(&self) -> Option<&StatusWord> { + if let Some(WordType::Status(w)) = &self.words.get(0) { + Some(w) + } else { + None } } @@ -299,102 +231,214 @@ impl Message { } } - /// Constructor method to add a word to the message + /// Add a word to the message /// /// # Arguments /// /// * `word` - A word to add /// - pub fn with_word>(mut self, word: T) -> Result { - self.add(word)?; - Ok(self) + pub fn add(&mut self, word: T) -> Result<()> { + match word.into() { + WordType::Data(v) => self.add_data(v), + WordType::Status(v) => self.add_status(v), + WordType::Command(v) => self.add_command(v), + _ => Err(Error::WordIsInvalid), + } } - /// Add a data word, returning the size of the message on success + /// Add a data word + /// + /// Performs basic checks for message validity before + /// Adding the data word. This method will return an error + /// if the status word- + /// + /// * Is the first word in the message. + /// * If the message is full. + /// * If the parity bit on the word is wrong. /// /// # Arguments /// /// * `word` - A word to add /// - pub fn add_data(&mut self, word: DataWord) -> Result<()> { - if self.is_full() && self.has_command() { + fn add_data(&mut self, word: DataWord) -> Result<()> { + if self.is_full() && self.is_command() { Err(Error::MessageIsFull) } else if self.is_empty() { Err(Error::FirstWordIsData) + } else if self.words.len() <= self.count { + Err(Error::MessageIsFull) } else { - self.words[self.count] = WordType::Data(word); + self.words[self.count] = word.into(); self.count += 1; Ok(()) } } - /// Constructor method to add a data word to the message + /// Add a status word /// - /// # Arguments + /// Performs basic checks for message validity before + /// Adding the status word. This method will return an error + /// if the status word- /// - /// * `word` - A word to add - /// - pub fn with_data>(mut self, word: T) -> Result { - self.add_data(word.into())?; - Ok(self) - } - - /// Add a status word, returning the size of the message on success + /// * Is not the first word in the message. + /// * If the message is full. + /// * If the parity bit on the word is wrong. /// /// # Arguments /// /// * `word` - A word to add /// - pub fn add_status(&mut self, word: StatusWord) -> Result<()> { + fn add_status(&mut self, word: StatusWord) -> Result<()> { if !self.is_empty() { Err(Error::StatusWordNotFirst) } else if !word.check_parity() { Err(Error::InvalidWord) + } else if self.words.len() <= self.count { + Err(Error::MessageIsFull) } else { - self.words[self.count] = WordType::Status(word); + self.words[self.count] = word.into(); self.count += 1; Ok(()) } } - /// Constructor method to add a status word to the message + /// Add a command word /// - /// # Arguments + /// Performs basic checks for message validity before + /// Adding the command word. This method will return an error + /// if the command word- /// - /// * `word` - A word to add - /// - pub fn with_status>(mut self, word: T) -> Result { - self.add_status(word.into())?; - Ok(self) - } - - /// Add a command word, returning the size of the message on success + /// * Is not the first word in the message. + /// * If the message is full. + /// * If the parity bit on the word is wrong. /// /// # Arguments /// /// * `word` - A word to add /// - pub fn add_command(&mut self, word: CommandWord) -> Result<()> { + fn add_command(&mut self, word: CommandWord) -> Result<()> { if !self.is_empty() { Err(Error::CommandWordNotFirst) } else if !word.check_parity() { Err(Error::InvalidWord) + } else if self.words.len() <= self.count { + Err(Error::MessageIsFull) } else { - self.words[self.count] = WordType::Command(word); + self.words[self.count] = word.into(); self.count += 1; Ok(()) } } - /// Constructor method to add a command word to the message - /// - /// # Arguments - /// - /// * `word` - A word to add + /// Check if message starts with a command word + #[must_use = "Returned value is not used"] + pub fn is_command(&self) -> bool { + self.command().is_some() + } + + /// Check if message starts with a status word + #[must_use = "Returned value is not used"] + pub fn is_status(&self) -> bool { + self.status().is_some() + } + + /// Check if the message is full /// - pub fn with_command>(mut self, word: T) -> Result { - self.add_command(word.into())?; - Ok(self) + /// This method will return false for status messages + /// until the maximum number of data words has been reached. + #[must_use = "Returned value is not used"] + pub fn is_full(&self) -> bool { + if let Some(w) = self.command() { + self.count() == w.word_count().into() + } else { + self.count == self.words.len() + } + } + + /// Check if the message is empty + #[must_use = "Returned value is not used"] + pub fn is_empty(&self) -> bool { + self.count == 0 + } + + /// Clear all words from the message + pub fn clear(&mut self) { + self.count = 0; + self.words = [WordType::None; WORDS]; + } + + /// Get the number of data words + pub fn count(&self) -> usize { + self.words.iter().filter(|w| w.is_data()).count() + } + + /// Get the total number of words + pub fn length(&self) -> usize { + self.words.iter().filter(|w| w.is_some()).count() + } + + /// Read bytes as a message + pub fn read(data: &[u8]) -> Result { + // estimate word count from given data + let estimate = ((data.len() * 8) / 20) - 1; + + // parse the specified header word + let word = Packet::read(data, 0)?.as_word::()?; + + // get the number of expected words or an + // estimate if the header is a status word. + let count = word.count().unwrap_or(estimate); + + // create a new message with the header word + let mut message: Self = Self::new().with_word(word)?; + + // return if no data words + if count == 0 { + return Ok(message); + } + + // the expected number of bytes to parse + let expected = ((count * 20) + 7) / 8; + + // return error if data is too small + if data.len() < expected { + return Err(Error::InvalidMessage); + } + + let start = 1; // skip the service word + let end = count + 1; // adjust for service word + + for index in start..end { + let b = index * 20; // offset in bits + let i = b / 8; // byte offset (whole) + let o = b % 8; // byte offset (fraction) + let bytes = &data[i..]; + + // use a packet to parse the bytes and convert to a word + message.add_data(Packet::read(bytes, o)?.try_into()?)?; + } + + Ok(message) + } + + /// Get the message as a byte array + pub fn write(&self, bytes: &mut [u8]) -> Result<()> { + let count = ((self.length() * 20) + 7) / 8; + + if bytes.len() < count { + return Err(Error::OutOfBounds); + } + + for (index, word) in self.words.iter().take_while(|w| w.is_some()).enumerate() { + let b = index * 20; + let i = b / 8; + let o = b % 8; + + let packet = Packet::try_from(word)?; + packet.write(&mut bytes[i..], o)?; + } + + Ok(()) } } @@ -409,17 +453,91 @@ mod tests { use super::*; #[test] - fn test_parse_command_three_data_words() { - let message = Message::parse_command(&[ + fn test_message_write_bytes_buffer_5() { + let data = [0b10000011, 0b00001100, 0b00100010, 0b11010000, 0b11010010]; + + let message: Message<2> = Message::read_command(&data).unwrap(); + + let mut buffer: [u8; 5] = [0; 5]; + let result = message.write(&mut buffer); + + assert!(result.is_ok()); + assert_eq!(buffer, data); + } + + #[test] + fn test_message_write_bytes_10() { + let data = [ + 0b10000011, 0b00001100, 0b01110010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, + 0b11100010, 0b11001110, 0b11011110, + ]; + + let message: Message<4> = Message::read_command(&data).unwrap(); + + let mut buffer: [u8; 10] = [0; 10]; + let result = message.write(&mut buffer); + + assert!(result.is_ok()); + assert_eq!(buffer, data); + } + + #[test] + fn test_message_write_bytes_buffer_too_small() { + let data = [ + 0b10000011, 0b00001100, 0b01110010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, + 0b11100010, 0b11001110, 0b11011110, + ]; + + let message: Message<4> = Message::read_command(&data).unwrap(); + + let mut buffer: [u8; 9] = [0; 9]; + let result = message.write(&mut buffer); + + assert!(result.is_err()); + } + + #[test] + fn test_parse_words_wrong_word_size() { + let result: Result> = Message::read_command(&[ + 0b10000011, 0b00001100, 0b01110010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, + 0b11100010, 0b11001110, 0b11011110, + ]); + + assert_eq!(result, Err(Error::MessageIsFull)); + } + + #[test] + fn test_parse_words_right_word_size() { + let result: Result> = Message::read_command(&[ + 0b10000011, 0b00001100, 0b01110010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, + 0b11100010, 0b11001110, 0b11011110, + ]); + + assert!(result.is_ok()); + } + + #[test] + fn test_parse_words_wrong_byte_size() { + let result: Result> = Message::read_command(&[ + 0b10000011, 0b00001100, 0b01110010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, + 0b11100010, 0b11001110, + ]); + + assert_eq!(result, Err(Error::MessageIsFull)); + } + + #[test] + fn test_read_command_three_data_words() { + let message: Message<4> = Message::read_command(&[ 0b10000011, 0b00001100, 0b01110010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, 0b11100010, 0b11001110, 0b11011110, ]) .unwrap(); assert!(message.is_full()); - assert!(message.has_command()); - assert_eq!(message.word_count(), 4); - assert_eq!(message.data_count(), 3); + assert!(message.is_command()); + assert_eq!(message.length(), 4); + assert_eq!(message.count(), 3); let word0 = message .get(0) @@ -440,43 +558,43 @@ mod tests { } #[test] - fn test_parse_command_two_data_words() { - let message = Message::parse_command(&[ + fn test_read_command_two_data_words() { + let message: Message<3> = Message::read_command(&[ 0b10000011, 0b00001100, 0b01000010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, 0b11100000, ]) .unwrap(); assert!(message.is_full()); - assert!(message.has_command()); - assert_eq!(message.word_count(), 3); - assert_eq!(message.data_count(), 2); + assert!(message.is_command()); + assert_eq!(message.length(), 3); + assert_eq!(message.count(), 2); } #[test] - fn test_parse_command_one_data_word() { - let message = - Message::parse_command(&[0b10000011, 0b00001100, 0b00100010, 0b11010000, 0b11010010]) + fn test_read_command_one_data_word() { + let message: Message = + Message::read_command(&[0b10000011, 0b00001100, 0b00100010, 0b11010000, 0b11010010]) .unwrap(); assert!(message.is_full()); - assert!(message.has_command()); - assert_eq!(message.word_count(), 2); - assert_eq!(message.data_count(), 1); + assert!(message.is_command()); + assert_eq!(message.length(), 2); + assert_eq!(message.count(), 1); } #[test] - fn test_parse_status_three_data_words() { - let message = Message::parse_status(&[ + fn test_read_status_three_data_words() { + let message: Message = Message::read_status(&[ 0b10000011, 0b00001100, 0b01110010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, 0b11100010, 0b11001110, 0b11011110, ]) .unwrap(); assert!(!message.is_full()); - assert!(message.has_status()); - assert_eq!(message.word_count(), 4); - assert_eq!(message.data_count(), 3); + assert!(message.is_status()); + assert_eq!(message.length(), 4); + assert_eq!(message.count(), 3); let word0 = message .get(0) @@ -497,60 +615,63 @@ mod tests { } #[test] - fn test_parse_status_two_data_words() { - let message = Message::parse_status(&[ + fn test_read_status_two_data_words() { + let message: Message = Message::read_status(&[ 0b10000011, 0b00001100, 0b01000010, 0b11010000, 0b11010010, 0b00101111, 0b00101101, 0b11100000, ]) .unwrap(); assert!(!message.is_full()); - assert!(message.has_status()); - assert_eq!(message.word_count(), 3); - assert_eq!(message.data_count(), 2); + assert!(message.is_status()); + assert_eq!(message.length(), 3); + assert_eq!(message.count(), 2); } #[test] - fn test_parse_status_one_data_word() { - let message = - Message::parse_status(&[0b10000011, 0b00001100, 0b00100010, 0b11010000, 0b11010010]) + fn test_read_status_one_data_word() { + let message: Message = + Message::read_status(&[0b10000011, 0b00001100, 0b00100010, 0b11010000, 0b11010010]) .unwrap(); assert!(!message.is_full()); - assert!(message.has_status()); - assert_eq!(message.word_count(), 2); - assert_eq!(message.data_count(), 1); + assert!(message.is_status()); + assert_eq!(message.length(), 2); + assert_eq!(message.count(), 1); } #[test] fn test_create_message() { - let message = Message::new(); + let message: Message = Message::new(); assert_eq!(message.is_full(), false); assert_eq!(message.is_empty(), true); - assert_eq!(message.first(), None); - assert_eq!(message.last(), None); + assert_eq!(message.command(), None); + assert_eq!(message.status(), None); + assert_eq!(message.get(0), None); - assert_eq!(message.word_count(), 0); - assert_eq!(message.data_count(), 0); + assert_eq!(message.length(), 0); + assert_eq!(message.count(), 0); } #[test] fn test_message_command_data() { - let mut message = Message::new(); + let mut message: Message = Message::new(); message .add(CommandWord::from_value(0b0001100001100010)) .unwrap(); - assert_eq!(message.word_count(), 1); - assert_eq!(message.data_count(), 0); - assert_eq!(message.data_expected(), 2); + let expected = message.command().map(CommandWord::word_count).unwrap_or(0); + + assert_eq!(message.length(), 1); + assert_eq!(message.count(), 0); + assert_eq!(expected, 2); } #[test] fn test_message_command_add_data() { - let mut message = Message::new(); + let mut message: Message = Message::new(); message .add(CommandWord::from_value(0b0001100001100010)) @@ -558,26 +679,25 @@ mod tests { message.add(DataWord::from(0b0110100001101001)).unwrap(); - assert_eq!(message.word_count(), 2); - assert_eq!(message.data_count(), 1); + assert_eq!(message.length(), 2); + assert_eq!(message.count(), 1); } #[test] fn test_message_status_no_data() { - let mut message = Message::new(); + let mut message: Message = Message::new(); message .add(StatusWord::from_value(0b0001100000000010)) .unwrap(); - assert_eq!(message.word_count(), 1); - assert_eq!(message.data_count(), 0); - assert_eq!(message.data_expected(), 0); + assert_eq!(message.length(), 1); + assert_eq!(message.count(), 0); } #[test] fn test_message_status_add_data() { - let mut message = Message::new(); + let mut message: Message = Message::new(); message .add(StatusWord::from_value(0b0001100000000000)) @@ -585,12 +705,7 @@ mod tests { message.add(DataWord::from(0b0110100001101001)).unwrap(); - assert_eq!(message.word_count(), 2); - assert_eq!(message.data_count(), 1); - - // status words don't have a word count field, and the - // number of data words following a status word is set - // by an earlier request. - assert_eq!(message.data_expected(), 0); + assert_eq!(message.length(), 2); + assert_eq!(message.count(), 1); } } diff --git a/src/message/packets.rs b/src/message/packets.rs index 9a5447b..1a0d901 100644 --- a/src/message/packets.rs +++ b/src/message/packets.rs @@ -1,5 +1,6 @@ use crate::errors::{parity, Error, Result}; use crate::word::{CommandWord, DataWord, StatusWord, Word}; +use crate::WordType; /// A packet of data parsed from binary /// @@ -24,9 +25,14 @@ use crate::word::{CommandWord, DataWord, StatusWord, Word}; /// #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct Packet { - sync: u8, - bytes: [u8; 2], - parity: u8, + /// The 3-bit sync pattern of a word + pub sync: u8, + + /// The 16-bit body of a word + pub body: [u8; 2], + + /// The 1-bit parity of a word + pub parity: u8, } impl Packet { @@ -41,21 +47,17 @@ impl Packet { /// # Arguments /// /// * `sync` - The leading 3 bit sync field as a u8 - /// * `bytes` - Two bytes of data following sync + /// * `body` - Two bytes of data following sync /// * `parity` - One bit parity field for the data as u8 /// - pub fn new(sync: u8, bytes: [u8; 2], parity: u8) -> Self { - Self { - sync, - bytes, - parity, - } + pub fn new(sync: u8, body: [u8; 2], parity: u8) -> Self { + Self { sync, body, parity } } - /// Parse a slice of bytes into sync, word, and parity + /// Parse a slice of bytes into sync, body, and parity /// /// This method interpretes the first 20 bits of the byte - /// array as a triplet: 3-bit sync, 16-bit word, and 1-bit + /// array as a triplet: 3-bit sync, 16-bit body, and 1-bit /// parity, given a bit offset at which to parse. /// /// # Arguments @@ -68,66 +70,81 @@ impl Packet { /// ```rust /// # use mil_std_1553b::*; /// # fn try_main() -> Result<()> { - /// let packet = Packet::parse(&[ + /// let packet = Packet::read(&[ /// 0b00000000, - /// 0b00000011, - /// 0b11000000, + /// 0b00001111, /// 0b00000000, - /// 0b11000000 - /// ],14)?; + /// 0b00000011 + /// ],12)?; /// - /// assert_eq!(packet.sync(), 0b00000111); - /// assert_eq!(packet.bytes(), [0b10000000,0b00000001]); - /// assert_eq!(packet.parity(), 0b00000001); + /// assert_eq!(packet.sync, 0b00000111); + /// assert_eq!(packet.body, [0b10000000,0b00000001]); + /// assert_eq!(packet.parity, 0b00000001); /// # Ok(()) /// # } - pub fn parse(data: &[u8], offset: usize) -> Result { - let m = offset % 8; // sub-byte offset - let c = offset / 8; // byte offset - - // 3 bytes needed for parsing a word - // unless the offset > 4, then 4 bytes. - let i = if m > 4 { 4 } else { 3 }; - - // need a minimum of byte offset + 3 - // bytes to parse a word. - if data.len() < c + i { + pub fn read(data: &[u8], offset: usize) -> Result { + if offset > 12 { return Err(Error::OutOfBounds); } - let bytes = &data[c..]; - let mut buffer: [u8; 3] = [0, 0, 0]; + let buf: [u8; 4] = match data.len() { + 3 => [data[0], data[1], data[2], 0], + i if i > 3 => [data[0], data[1], data[2], data[3]], + _ => return Err(Error::OutOfBounds), + }; - let r = 8 - m.try_into().unwrap_or(8); - let l = m; + let mut v: u32 = u32::from_be_bytes(buf); - buffer[0] |= bytes[0] << l; - buffer[1] |= bytes[1] << l; - buffer[2] |= bytes[2] << l; + v <<= offset; + v >>= 12; - if l > 0 { - buffer[0] |= bytes[1].checked_shr(r).unwrap_or(0); - buffer[1] |= bytes[2].checked_shr(r).unwrap_or(0); - } + let s = ((v & 0b11100000000000000000) >> 17) as u8; + let w1 = ((v & 0b00011111111000000000) >> 9) as u8; + let w2 = ((v & 0b00000000000111111110) >> 1) as u8; + let p = (v & 0b00000000000000000001) as u8; - if l > 4 { - buffer[2] |= bytes[3].checked_shr(r).unwrap_or(0); - } + Ok(Self::new(s, [w1, w2], p)) + } + + /// Write the packet to a byte array + pub fn write(&self, bytes: &mut [u8], offset: usize) -> Result<()> { + let mut v: u32 = 0; + let mut m: u32 = 0; + let o = offset.clamp(0, 12); + + v |= ((self.sync & 0b00000111) as u32) << 29; + v |= (self.body[0] as u32) << 21; + v |= (self.body[1] as u32) << 13; + v |= ((self.parity & 0b00000001) as u32) << 12; + + v >>= o; + + m |= (bytes[0] as u32) << 24; + m |= (bytes[1] as u32) << 16; + v |= m & !(u32::MAX >> o); - let mut sync: u8 = 0; - let mut word: [u8; 2] = [0, 0]; - let mut parity: u8 = 0; + let e = if offset > 4 { 4 } else { 3 }; - sync |= (buffer[0] & 0b11100000) >> 5; + if bytes.len() < e { + return Err(Error::OutOfBounds); + } + + let result = v.to_be_bytes(); + bytes[..e].copy_from_slice(&result[..e]); - word[0] |= (buffer[0] & 0b00011111) << 3; - word[0] |= (buffer[1] & 0b11100000) >> 5; - word[1] |= (buffer[1] & 0b00011111) << 3; - word[1] |= (buffer[2] & 0b11100000) >> 5; + Ok(()) + } - parity |= (buffer[2] & 0b00010000) >> 4; + /// Check the parity flag is correct + #[must_use = "Result of check is never used"] + pub fn check_parity(&self) -> bool { + parity(u16::from_be_bytes(self.body)) == self.parity + } - Ok(Self::new(sync, word, parity)) + /// Check the sync flag is correct + #[must_use = "Result of check is never used"] + pub fn check_sync(&self) -> bool { + self.sync == Self::DATA_SYNC || self.sync == Self::SERV_SYNC } /// Check if this packet is a data packet @@ -148,82 +165,98 @@ impl Packet { self.check_parity() && self.check_sync() } - /// Convert this packet into a data word - pub fn to_data(&self) -> Result { - if self.is_data() { - DataWord::new() - .with_bytes(self.bytes) - .with_parity(self.parity) - .build() - } else { - Err(Error::PacketIsInvalid) - } + /// Convert this packet into a word + pub fn as_word(&self) -> Result { + T::new() + .with_bytes(self.body) + .with_parity(self.parity) + .build() } +} - /// Convert this packet into a status word - pub fn to_status(&self) -> Result { - if self.is_service() { - StatusWord::new() - .with_bytes(self.bytes) - .with_parity(self.parity) - .build() - } else { - Err(Error::PacketIsInvalid) +impl TryFrom<&WordType> for Packet { + type Error = Error; + + fn try_from(word: &WordType) -> Result { + match word { + WordType::None => Err(Error::InvalidWord), + _ => Ok(Self::new( + match word.is_data() { + true => Self::DATA_SYNC, + false => Self::SERV_SYNC, + }, + word.bytes(), + word.parity(), + )), } } +} + +impl TryFrom for Packet { + type Error = Error; + + fn try_from(word: WordType) -> Result { + Self::try_from(&word) + } +} + +impl TryFrom<&Packet> for CommandWord { + type Error = Error; - /// Convert this packet into a command word - pub fn to_command(&self) -> Result { - if self.is_service() { - CommandWord::new() - .with_bytes(self.bytes) - .with_parity(self.parity) - .build() + fn try_from(value: &Packet) -> Result { + if value.is_service() { + value.as_word() } else { Err(Error::PacketIsInvalid) } } +} - /// Get the first byte as a u16 - pub fn first(&self) -> u16 { - self.bytes[0] as u16 - } +impl TryFrom for CommandWord { + type Error = Error; - /// Get the second byte as a u16 - pub fn second(&self) -> u16 { - self.bytes[1] as u16 + fn try_from(value: Packet) -> Result { + CommandWord::try_from(&value) } +} - /// Get the sync flag - pub fn sync(&self) -> u8 { - self.sync - } +impl TryFrom<&Packet> for StatusWord { + type Error = Error; - /// Get the bytes as an array - pub fn bytes(&self) -> [u8; 2] { - self.bytes + fn try_from(value: &Packet) -> Result { + if value.is_service() { + value.as_word() + } else { + Err(Error::PacketIsInvalid) + } } +} - /// Get the parity bit as a u8 - pub fn parity(&self) -> u8 { - self.parity - } +impl TryFrom for StatusWord { + type Error = Error; - /// Get the combined bytes as a u16 - pub fn data(&self) -> u16 { - (self.first() << 8) | self.second() + fn try_from(value: Packet) -> Result { + StatusWord::try_from(&value) } +} - /// Check the parity flag is correct - #[must_use = "Result of check is never used"] - pub fn check_parity(&self) -> bool { - parity(self.data()) == self.parity() +impl TryFrom<&Packet> for DataWord { + type Error = Error; + + fn try_from(value: &Packet) -> Result { + if value.is_data() { + value.as_word() + } else { + Err(Error::PacketIsInvalid) + } } +} - /// Check the sync flag is correct - #[must_use = "Result of check is never used"] - pub fn check_sync(&self) -> bool { - self.sync == Self::DATA_SYNC || self.sync == Self::SERV_SYNC +impl TryFrom for DataWord { + type Error = Error; + + fn try_from(value: Packet) -> Result { + DataWord::try_from(&value) } } @@ -233,50 +266,118 @@ mod tests { use crate::flags::{Address, BroadcastReceived, SubAddress}; #[test] - fn test_packet_parse_offset_14() { - let packet = Packet::parse( - &[0b00000000, 0b00000011, 0b11000000, 0b00000000, 0b11000000], - 14, - ) - .unwrap(); + fn test_packet_command_bad_sync() { + let result1: Result = Packet::read(&[0b10000000, 0b00000000, 0b00010000], 0) + .unwrap() + .try_into(); + + let result2: Result = Packet::read(&[0b00100000, 0b00000000, 0b00010000], 0) + .unwrap() + .try_into(); + + assert!(result1.is_ok()); + assert_eq!(result2, Err(Error::PacketIsInvalid)); + } + + #[test] + fn test_packet_read_offset_13() { + let result = Packet::read( + &[0b00000000, 0b00000111, 0b10000000, 0b00000001, 0b10000000], + 13, + ); + + assert!(result.is_err()); + } + + #[test] + fn test_packet_read_offset_12() { + let packet = Packet::read(&[0b00000000, 0b00001111, 0b00000000, 0b00000011], 12).unwrap(); assert_eq!(packet.sync, 0b00000111); - assert_eq!(packet.bytes, [0b10000000, 0b00000001]); + assert_eq!(packet.body, [0b10000000, 0b00000001]); assert_eq!(packet.parity, 0b00000001); } #[test] - fn test_packet_parse_offset_6() { - let packet = Packet::parse(&[0b00000011, 0b11000000, 0b00000000, 0b11000000], 6).unwrap(); + fn test_packet_read_offset_6() { + let packet = Packet::read(&[0b00000011, 0b11000000, 0b00000000, 0b11000000], 6).unwrap(); assert_eq!(packet.sync, 0b00000111); - assert_eq!(packet.bytes, [0b10000000, 0b00000001]); + assert_eq!(packet.body, [0b10000000, 0b00000001]); assert_eq!(packet.parity, 0b00000001); } #[test] - fn test_packet_parse_offset_4() { - let packet = Packet::parse(&[0b00001111, 0b00000000, 0b00000011], 4).unwrap(); + fn test_packet_read_offset_4() { + let packet = Packet::read(&[0b00001111, 0b00000000, 0b00000011], 4).unwrap(); assert_eq!(packet.sync, 0b00000111); - assert_eq!(packet.bytes, [0b10000000, 0b00000001]); + assert_eq!(packet.body, [0b10000000, 0b00000001]); assert_eq!(packet.parity, 0b00000001); } #[test] - fn test_packet_parse_offset_0() { - let packet = Packet::parse(&[0b11110000, 0b00000000, 0b00110000], 0).unwrap(); + fn test_packet_read_offset_0() { + let packet = Packet::read(&[0b11110000, 0b00000000, 0b00110000], 0).unwrap(); assert_eq!(packet.sync, 0b00000111); - assert_eq!(packet.bytes, [0b10000000, 0b00000001]); + assert_eq!(packet.body, [0b10000000, 0b00000001]); assert_eq!(packet.parity, 0b00000001); } #[test] - fn test_packet_value() { - let packet = Packet::new(Packet::DATA_SYNC, [0b01000000, 0b00100000], 1); - let value = packet.data(); - assert_eq!(value, 0b0100000000100000); + fn test_packet_write_offset_12() { + let packet = Packet::read(&[0b11110000, 0b00000000, 0b00110000], 0).unwrap(); + + let mut buffer = [0, 0, 0, 0]; + let result = packet.write(&mut buffer, 12); + + assert!(result.is_ok()); + assert_eq!(buffer, [0b00000000, 0b00001111, 0b00000000, 0b00000011]); + } + + #[test] + fn test_packet_write_offset_7() { + let packet = Packet::read(&[0b11110000, 0b00000000, 0b00110000], 0).unwrap(); + + let mut buffer = [0, 0, 0, 0]; + let result = packet.write(&mut buffer, 7); + + assert!(result.is_ok()); + assert_eq!(buffer, [0b00000001, 0b11100000, 0b00000000, 0b01100000]); + } + + #[test] + fn test_packet_write_offset_5() { + let packet = Packet::read(&[0b11110000, 0b00000000, 0b00110000], 0).unwrap(); + + let mut buffer = [0b10101000, 0, 0, 0]; + let result = packet.write(&mut buffer, 5); + + assert!(result.is_ok()); + assert_eq!(buffer, [0b10101111, 0b10000000, 0b00000001, 0b10000000]); + } + + #[test] + fn test_packet_write_offset_4() { + let packet = Packet::read(&[0b11110000, 0b00000000, 0b00110000], 0).unwrap(); + + let mut buffer = [0b10100000, 0, 0, 0]; + let result = packet.write(&mut buffer, 4); + + assert!(result.is_ok()); + assert_eq!(buffer, [0b10101111, 0b00000000, 0b00000011, 0b00000000]); + } + + #[test] + fn test_packet_write_offset_0() { + let packet = Packet::read(&[0b11110000, 0b00000000, 0b00110000], 0).unwrap(); + + let mut buffer = [0, 0, 0]; + let result = packet.write(&mut buffer, 0); + + assert!(result.is_ok()); + assert_eq!(buffer, [0b11110000, 0b00000000, 0b00110000]); } #[test] @@ -350,7 +451,7 @@ mod tests { #[test] fn test_packet_convert_command() { let packet = Packet::new(Packet::SERV_SYNC, [0b00011000, 0b01100010], 0); - let word = packet.to_command().unwrap(); + let word = CommandWord::try_from(packet).unwrap(); assert_eq!(word.address(), Address::new(3)); assert_eq!(word.subaddress(), SubAddress::new(3)); @@ -362,60 +463,60 @@ mod tests { #[test] fn test_packet_convert_status() { let packet = Packet::new(Packet::SERV_SYNC, [0b00011000, 0b00010000], 0); - let word = packet.to_status().unwrap(); + let word = StatusWord::try_from(packet).unwrap(); assert_eq!(word.address(), Address::new(3)); assert_eq!(word.broadcast_received(), BroadcastReceived::Received); } #[test] - fn test_packet_parse_word_alternate() { - let packet = Packet::parse(&[0b00010101, 0b01010101, 0b01000000], 0).unwrap(); + fn test_packet_read_word_alternate() { + let packet = Packet::read(&[0b00010101, 0b01010101, 0b01000000], 0).unwrap(); assert_eq!(packet.sync, 0b00000000); - assert_eq!(packet.bytes, [0b10101010, 0b10101010]); + assert_eq!(packet.body, [0b10101010, 0b10101010]); assert_eq!(packet.parity, 0b00000000); } #[test] - fn test_packet_parse_word_ones() { - let packet = Packet::parse(&[0b00011111, 0b11111111, 0b11100000], 0).unwrap(); + fn test_packet_read_word_ones() { + let packet = Packet::read(&[0b00011111, 0b11111111, 0b11100000], 0).unwrap(); assert_eq!(packet.sync, 0b00000000); - assert_eq!(packet.bytes, [0b11111111, 0b11111111]); + assert_eq!(packet.body, [0b11111111, 0b11111111]); assert_eq!(packet.parity, 0b00000000); } #[test] - fn test_packet_parse_word_zeroes() { - let packet = Packet::parse(&[0b11100000, 0b00000000, 0b00010000], 0).unwrap(); + fn test_packet_read_word_zeroes() { + let packet = Packet::read(&[0b11100000, 0b00000000, 0b00010000], 0).unwrap(); assert_eq!(packet.sync, 0b00000111); - assert_eq!(packet.bytes, [0b00000000, 0b00000000]); + assert_eq!(packet.body, [0b00000000, 0b00000000]); assert_eq!(packet.parity, 0b00000001); } #[test] - fn test_packet_parse_sync_zeroes() { - let packet = Packet::parse(&[0b00011111, 0b11111111, 0b11111111], 0).unwrap(); + fn test_packet_read_sync_zeroes() { + let packet = Packet::read(&[0b00011111, 0b11111111, 0b11111111], 0).unwrap(); assert_eq!(packet.sync, 0b00000000); - assert_eq!(packet.bytes, [0b11111111, 0b11111111]); + assert_eq!(packet.body, [0b11111111, 0b11111111]); assert_eq!(packet.parity, 0b00000001); } #[test] - fn test_packet_parse_sync_ones() { - let packet = Packet::parse(&[0b11100000, 0b00000000, 0b00000000], 0).unwrap(); + fn test_packet_read_sync_ones() { + let packet = Packet::read(&[0b11100000, 0b00000000, 0b00000000], 0).unwrap(); assert_eq!(packet.sync, 0b00000111); - assert_eq!(packet.bytes, [0b00000000, 0b00000000]); + assert_eq!(packet.body, [0b00000000, 0b00000000]); assert_eq!(packet.parity, 0b00000000); } #[test] - fn test_packet_parse_parity_one() { - let packet = Packet::parse( + fn test_packet_read_parity_one() { + let packet = Packet::read( &[ 0b00000000, 0b00000000, 0b00010000, // 20th ], @@ -424,13 +525,13 @@ mod tests { .unwrap(); assert_eq!(packet.sync, 0b00000000); - assert_eq!(packet.bytes, [0b00000000, 0b00000000]); + assert_eq!(packet.body, [0b00000000, 0b00000000]); assert_eq!(packet.parity, 0b00000001); } #[test] - fn test_packet_parse_parity_one_right() { - let packet = Packet::parse( + fn test_packet_read_parity_one_right() { + let packet = Packet::read( &[ 0b00000000, 0b00000000, 0b00001000, // 21st ], @@ -439,13 +540,13 @@ mod tests { .unwrap(); assert_eq!(packet.sync, 0b00000000); - assert_eq!(packet.bytes, [0b00000000, 0b00000000]); + assert_eq!(packet.body, [0b00000000, 0b00000000]); assert_eq!(packet.parity, 0b00000000); } #[test] - fn test_packet_parse_parity_one_left() { - let packet = Packet::parse( + fn test_packet_read_parity_one_left() { + let packet = Packet::read( &[ 0b00000000, 0b00000000, 0b00100000, // 19th ], @@ -454,13 +555,13 @@ mod tests { .unwrap(); assert_eq!(packet.sync, 0b00000000); - assert_eq!(packet.bytes, [0b00000000, 0b00000001]); + assert_eq!(packet.body, [0b00000000, 0b00000001]); assert_eq!(packet.parity, 0b00000000); } #[test] - fn test_packet_parse_parity_zero() { - let packet = Packet::parse( + fn test_packet_read_parity_zero() { + let packet = Packet::read( &[ 0b11111111, 0b11111111, 0b11101111, // 20th ], @@ -469,7 +570,7 @@ mod tests { .unwrap(); assert_eq!(packet.sync, 0b00000111); - assert_eq!(packet.bytes, [0b11111111, 0b11111111]); + assert_eq!(packet.body, [0b11111111, 0b11111111]); assert_eq!(packet.parity, 0b00000000); } } diff --git a/src/word/enums.rs b/src/word/enums.rs index 331ceb9..112b35a 100644 --- a/src/word/enums.rs +++ b/src/word/enums.rs @@ -1,3 +1,5 @@ +use crate::Word; + use super::{CommandWord, DataWord, StatusWord}; /// Container enum for the different kinds of words @@ -47,10 +49,22 @@ impl WordType { matches!(self, Self::None) } - /// Get the number of associated data words - pub fn data_count(&self) -> usize { + /// Get the word as a byte array + pub fn bytes(&self) -> [u8; 2] { + match self { + Self::Command(w) => w.into(), + Self::Status(w) => w.into(), + Self::Data(w) => w.into(), + _ => [0, 0], + } + } + + /// Get the parity bit of the word + pub fn parity(&self) -> u8 { match self { - Self::Command(c) => c.count(), + Self::Command(w) => w.parity(), + Self::Status(w) => w.parity(), + Self::Data(w) => w.parity(), _ => 0, } } diff --git a/src/word/mod.rs b/src/word/mod.rs index fa32383..0fc2659 100644 --- a/src/word/mod.rs +++ b/src/word/mod.rs @@ -4,4 +4,4 @@ mod enums; mod words; pub use enums::WordType; -pub use words::{CommandWord, DataWord, StatusWord, Word}; +pub use words::{CommandWord, DataWord, Header, StatusWord, Word}; diff --git a/src/word/words.rs b/src/word/words.rs index 66dc34d..e296a7a 100644 --- a/src/word/words.rs +++ b/src/word/words.rs @@ -1,11 +1,20 @@ use crate::errors::{parity, Error, MessageError, Result, SubsystemError, TerminalError}; -use crate::fields::*; use crate::flags::*; +use crate::{fields::*, WordType}; + +/// Common functionality for service words +pub trait Header +where + Self: Sized + Into, +{ + /// The number of data words expected + fn count(&self) -> Option; +} /// Common functionality for all words pub trait Word where - Self: Sized, + Self: Sized + Into, { /// Create an empty word fn new() -> Self; @@ -32,7 +41,7 @@ where fn from_bytes(data: [u8; 2]) -> Self; /// Get the internal data as a slice - fn as_bytes(&self) -> &[u8]; + fn as_bytes(&self) -> [u8; 2]; /// Get the internal data as u16 fn as_value(&self) -> u16; @@ -795,6 +804,18 @@ impl DataWord { } } +impl Header for CommandWord { + fn count(&self) -> Option { + Some(self.word_count() as usize) + } +} + +impl Header for StatusWord { + fn count(&self) -> Option { + None + } +} + impl Word for CommandWord { fn new() -> Self { Self { @@ -839,8 +860,8 @@ impl Word for CommandWord { Self::new().with_bytes(data) } - fn as_bytes(&self) -> &[u8] { - &self.data + fn as_bytes(&self) -> [u8; 2] { + self.data } fn as_value(&self) -> u16 { @@ -922,8 +943,8 @@ impl Word for StatusWord { Self::new().with_bytes(data) } - fn as_bytes(&self) -> &[u8] { - &self.data + fn as_bytes(&self) -> [u8; 2] { + self.data } fn as_value(&self) -> u16 { @@ -1005,8 +1026,8 @@ impl Word for DataWord { Self::new().with_bytes(data) } - fn as_bytes(&self) -> &[u8] { - &self.data + fn as_bytes(&self) -> [u8; 2] { + self.data } fn as_value(&self) -> u16 { @@ -1266,7 +1287,7 @@ mod tests { .build() .unwrap(); - assert_eq!(word.as_bytes(), &[0b01001000, 0b01001001]); + assert_eq!(word.as_bytes(), [0b01001000, 0b01001001]); assert_eq!(word.as_value(), 0b0100100001001001u16); assert_eq!(word.as_string(), Ok("HI")); assert_eq!(word.parity(), 0); @@ -1278,7 +1299,7 @@ mod tests { .with_value(0b0100100001001001u16) .with_calculated_parity(); - assert_eq!(word.as_bytes(), &[0b01001000, 0b01001001]); + assert_eq!(word.as_bytes(), [0b01001000, 0b01001001]); assert_eq!(word.as_value(), 0b0100100001001001u16); assert_eq!(word.as_string(), Ok("HI")); assert_eq!(word.parity(), 0); @@ -1291,7 +1312,7 @@ mod tests { .unwrap() .with_calculated_parity(); - assert_eq!(word.as_bytes(), &[0b01001000, 0b01001001]); + assert_eq!(word.as_bytes(), [0b01001000, 0b01001001]); assert_eq!(word.as_value(), 0b0100100001001001u16); assert_eq!(word.as_string(), Ok("HI")); assert_eq!(word.parity(), 0); @@ -1592,6 +1613,19 @@ mod tests { assert_eq!(data, [0b01101000, 0b01101001]); } + #[test] + fn test_data_roundtrip() { + let word1 = DataWord::from_value(0b0110100001101001); + let data1 = word1.as_bytes(); + + let word2 = DataWord::from_bytes(data1); + let data2 = word2.as_bytes(); + + assert_eq!(data1, [0b01101000, 0b01101001]); + assert_eq!(data2, [0b01101000, 0b01101001]); + assert_eq!(word1, word2); + } + #[test] fn test_command_bytes() { let word = CommandWord::from_value(0b0110100001101001); @@ -1599,10 +1633,36 @@ mod tests { assert_eq!(data, [0b01101000, 0b01101001]); } + #[test] + fn test_command_roundtrip() { + let word1 = CommandWord::from_value(0b0110100001101001); + let data1 = word1.as_bytes(); + + let word2 = CommandWord::from_bytes(data1); + let data2 = word2.as_bytes(); + + assert_eq!(data1, [0b01101000, 0b01101001]); + assert_eq!(data2, [0b01101000, 0b01101001]); + assert_eq!(word1, word2); + } + #[test] fn test_status_bytes() { let word = StatusWord::from_value(0b0110100001101001); let data = word.as_bytes(); assert_eq!(data, [0b01101000, 0b01101001]); } + + #[test] + fn test_status_roundtrip() { + let word1 = StatusWord::from_value(0b0110100001101001); + let data1 = word1.as_bytes(); + + let word2 = StatusWord::from_bytes(data1); + let data2 = word2.as_bytes(); + + assert_eq!(data1, [0b01101000, 0b01101001]); + assert_eq!(data2, [0b01101000, 0b01101001]); + assert_eq!(word1, word2); + } }