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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
use crate::{Error, DATA_SIZE_BYTES, FRAME_SIZE_BYTES, METADATA_SIZE_BYTES}; use core::convert::TryInto; /// Represents a packet of information. /// /// ## Data layout /// /// ```md /// |TX|RX|LL|__|DDD...| /// ``` /// * `TX` (u16): Device (Transmitting) ID. Because we use the same RX and TX /// address for all our devices, we need to embed this ID in the `Frame`. /// * `RX` (u16): Controller (Receiving) ID. Usually 0x0, which is the default /// controller ID but If we want to communicate directly to another ID we can /// specify that here. /// * `LL` (u16): = Packet length. Should be zero instead of in an init message. /// This can go up to the `BUFFER_SIZE * PACKET_SIZE - 1` in length, /// anything over will be ignored. /// * `__` (u16): 2 bytes = Reserved for future /// * `D...` (24 bytes) = Serialized data /// /// ## Sending Frames in multiple parts /// /// Start all messages (including single and multipart messages) with an `LL` /// value greater than 0, where the value is the number of Frames required to /// send the message. All subsequent messages have `LL == 0x0` until entire /// buffer has been sent #[derive(Debug, Eq, PartialEq, Default)] pub struct Frame(pub [u8; FRAME_SIZE_BYTES]); impl Frame { /// Creates a Frame with a specified transmitting ID. Panics if the /// transmitting ID value is invalid or the size of the buffer is smaller /// than 2. /// /// # Arguments /// /// * `tx_id` - The transmitting ID. To create a Frame with completely /// zeroed data, use [`Frame::default()`]. /// /// # Examples /// /// ``` /// # use commune::*; /// // Creates a new Frame with the transmitting ID of 1 /// let frame = Frame::new(0x01); /// ``` pub fn new(tx_id: u16) -> Self { let mut msg = Frame([0; FRAME_SIZE_BYTES]); msg.insert_at(0, tx_id) .expect("Index 0 should be in range."); msg } /// Extracts a u16 value at an index from the Frame. Returns a result /// containing the extracted value, or an error. /// /// # Arguments /// /// * `index` - The position at which the value and the value one position /// to the right will be read. /// /// # Examples /// /// ``` /// # use commune::*; /// # let mut frame = Frame::default(); /// # frame.insert_at(2, 12); /// // Assume we already have the value 12 at index 2 in a variable named `frame`. /// let value = frame.extract_at(2).unwrap(); /// assert_eq!(value, 12); /// ``` pub fn extract_at(&self, index: usize) -> Result<u16, Error> { let bytes = self.0.get(index..index + 2); if bytes.is_none() { return Err(Error::OutOfBounds); } Ok(u16::from_be_bytes( bytes.unwrap().try_into().map_err(|_| Error::U16IntoBytes)?, )) } /// Inserts a u16 value at an index in the `Frame`. Returns a unit value on /// success. /// /// # Arguments /// /// * `index` - The position at which the value and the value one position /// to the right will be written /// * `value` - the u16 value to write to the position of our index /// /// # Examples /// /// ``` /// # use commune::*; /// # use core::convert::TryInto; /// // Creates a frame with the value 12 at index 2 /// let mut frame = Frame::default(); /// frame.insert_at(2, 12); /// /// // Gets the value from our frame slice /// let value = u16::from_be_bytes(frame.0[2..4].try_into().unwrap()); /// assert_eq!(value, 12); /// ``` pub fn insert_at(&mut self, index: usize, value: u16) -> Result<(), Error> { // Checks that index is in range if index < (FRAME_SIZE_BYTES - 1) { // Minus 1 because we are targeting 2 bytes. Assigns bytes at range // specified let value_bytes = value.to_be_bytes(); self.0[index] = value_bytes[0]; self.0[index + 1] = value_bytes[1]; Ok(()) } else { Err(Error::OutOfBounds) } } /// Gets the transmitting ID /// /// # Examples /// ``` /// # use commune::*; /// // Creates a Frame with the embedded TX ID of 2 /// let frame = Frame::new(2); /// /// // Gets the TX ID /// let id = frame.tx_id(); /// assert_eq!(id, 2); /// ``` pub fn tx_id(&self) -> u16 { self.extract_at(0) .expect("This should always be in bounds.") } /// Sets the transmitting ID /// /// # Arguments /// /// * `tx_id` - the transmitting ID to set /// /// # Examples /// ``` /// # use commune::*; /// // Creates a default blank Frame /// let mut frame = Frame::default(); /// /// // Sets the TX ID /// let frame = frame.set_tx_id(5); /// assert_eq!(frame.tx_id(), 5); /// ``` pub fn set_tx_id(mut self, tx_id: u16) -> Self { self.insert_at(0, tx_id).expect("u16 value should be valid"); self } /// Gets the receiving ID /// /// # Examples /// ``` /// # use commune::*; /// // Creates a Frame with the RX ID of 420 /// let frame = Frame::default().set_rx_id(420); /// /// // Gets the RX ID /// let id = frame.rx_id(); /// assert_eq!(id, 420); /// ``` pub fn rx_id(&self) -> u16 { self.extract_at(2) .expect("This should always be in bounds.") } /// Sets the receiving ID /// /// # Arguments /// /// * `rx_id` - the receiving ID to set /// /// # Examples /// ``` /// # use commune::*; /// // Creates a default blank Frame /// let mut frame = Frame::default(); /// /// // Sets the RX ID to 420 /// frame = frame.set_tx_id(420); /// assert_eq!(frame.tx_id(), 420); /// ``` pub fn set_rx_id(mut self, rx_id: u16) -> Self { self.insert_at(2, rx_id).expect("u16 value should be valid"); self } /// Gets non-meta data /// /// # Examples /// ``` /// # use commune::*; /// // Creates a Frame manually, with all zeros in the metadata, and all ones in the regular data /// let frame = Frame([0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); /// /// // Gets all data. Notice the zeros in the metadata are stripped. /// let data = frame.data(); /// assert_eq!(data, [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); /// ``` pub fn data(&self) -> [u8; DATA_SIZE_BYTES] { let mut new_data: [u8; DATA_SIZE_BYTES] = [0; DATA_SIZE_BYTES]; for (i, v) in self.0[METADATA_SIZE_BYTES..FRAME_SIZE_BYTES] .iter() .enumerate() { new_data[i] = *v; } new_data } /// Sets non-meta data /// /// # Examples /// /// ``` /// # use commune::*; /// // Creates a default blank Frame. /// let mut frame = Frame::default(); /// let data = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; /// frame = frame.set_data(&data); /// assert_eq!(frame.data(), data); /// ``` pub fn set_data(mut self, data: &[u8]) -> Self { // get exact slice length as to not move out of index let upper_bound = data.len(); for i in 0..upper_bound { self.0[i + METADATA_SIZE_BYTES] = data[i]; } self } /// Gets multipart information. Returns a [MultipartStatus]. /// /// # Examples /// /// ``` /// # use commune::*; /// // Creates a Frame with a multipart status /// let frame = Frame::default().set_multipart(MultipartStatus::Start(3)); /// let multipart = frame.multipart(); /// assert_eq!(multipart, MultipartStatus::Start(3)); /// ``` pub fn multipart(&self) -> MultipartStatus { let status = self .extract_at(4) .expect("should always be valid multipart"); match status { 0 => MultipartStatus::Continue, _ => MultipartStatus::Start(status), } } /// Sets the multipart status of a message. Transmissions must always begin /// with a Start message. This has the effect of making it "one-indexed" /// instead of zero, as 0x00 is the code for continue, whereas anything /// greater than is a start message. `MultipartStatus::Start(0)` is invalid. /// Send single messages with `MultipartStatus::Start(1)`. /// /// # Examples /// /// ``` /// # use commune::*; /// // Creates a default blank Frame /// let mut frame = Frame::default(); /// frame = frame.set_multipart(MultipartStatus::Start(5)); /// assert_eq!(frame.multipart(), MultipartStatus::Start(5)) /// ``` pub fn set_multipart(mut self, status: MultipartStatus) -> Self { if let MultipartStatus::Start(total_frames) = status { self.insert_at(4, total_frames).unwrap(); } self } } /// Start and identify the number of packets this multipart transmission #[derive(PartialEq, Debug)] pub enum MultipartStatus { Start(u16), Continue, } #[cfg(test)] mod tests { use super::*; use crate::{device::Device, tests::TestData}; const DATA: [u8; 24] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, ]; #[test] fn create_new_frame_with_tx_id() { let frame = Frame::new(0x2710); assert_eq!(frame.0[0..2], [0x27, 0x10]); assert_eq!(frame.tx_id(), 0x2710) } #[test] fn create_new_frame_with_rx_id() { let frame = Frame::new(0x2710).set_rx_id(0x1111); assert_eq!(frame.0[2..4], [0x11, 0x11]); assert_eq!(frame.rx_id(), 0x1111) } #[test] fn create_new_frame_with_data() { let frame = Frame::new(0x2710); let frame = frame.set_data(&DATA); assert_eq!(&frame.0[METADATA_SIZE_BYTES..FRAME_SIZE_BYTES], &DATA); assert_eq!(&frame.data(), &DATA); } #[test] fn create_queue_with_correct_amount_of_packets() { let mut device = Device::new(0x1); let data = TestData::default(); device.serialize(0x0, &data).unwrap(); // should take 90 bytes of data. Verify we have the correct amount of // packets for data this size assert_eq!(device.tx.len(), 4); } }