flipdot_core/
message.rs

1use std::fmt::{self, Display, Formatter};
2
3use derive_more::{Display, LowerHex, UpperHex};
4
5use crate::{Address, Data, Frame, MsgType};
6
7/// High-level representation of a sign bus communication message.
8///
9/// Ascribes meaning to a [`Frame`] and is freely convertible to and from one
10/// (with `Unknown` to allow round-tripping unknown message types). This is the
11/// primary way that messages are represented in `flipdot`.
12///
13/// # Examples
14///
15/// ```
16/// use flipdot_core::{Address, Message, PageFlipStyle, SignBus, State};
17/// use flipdot_testing::{VirtualSign, VirtualSignBus};
18///
19/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
20/// #
21/// let mut bus = VirtualSignBus::new(vec![VirtualSign::new(Address(3), PageFlipStyle::Manual)]);
22///
23/// // Message is the currency used to send and receive messages on a bus:
24/// let response = bus.process_message(Message::QueryState(Address(3)))?;
25/// assert_eq!(Some(Message::ReportState(Address(3), State::Unconfigured)), response);
26/// #
27/// # Ok(()) }
28/// ```
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30#[non_exhaustive]
31pub enum Message<'a> {
32    /// Send a chunk of data, with the first member indicating the offset.
33    ///
34    /// E.g. when sending 32 bytes of data in two 16-byte chunks, the first
35    /// message would have offset 0 and the second would have offset 16.
36    /// No response is expected.
37    SendData(Offset, Data<'a>),
38    /// Notifies that we are done sending data, and specifies how many chunks were sent.
39    ///
40    /// No response is expected.
41    DataChunksSent(ChunkCount),
42
43    /// Initially discovers the sign with the given address on the bus.
44    ///
45    /// A `ReportState` message with the sign's current state is expected.
46    Hello(Address),
47    /// Queries the sign with the given address for its current state.
48    ///
49    /// A `ReportState` message with the sign's current state is expected.
50    QueryState(Address),
51    /// Indicates the state of the sign with the given address.
52    ///
53    /// Sent by the sign in response to a `Hello` or`QueryState` message.
54    ReportState(Address, State),
55
56    /// Requests that the sign with the given address perform an operation.
57    ///
58    /// An `AckOperation` response is expected.
59    RequestOperation(Address, Operation),
60    /// Sent by the sign with the given address indicating that it will perform
61    /// the given operation.
62    AckOperation(Address, Operation),
63
64    /// Indicates that the pixel data for the sign with the given address has been
65    /// fully transferred.
66    ///
67    /// This indicates that the sign should load it into memory in preparation to show.
68    /// No response is expected.
69    PixelsComplete(Address),
70
71    /// Notifies the sign with the given address to blank its display and shut down.
72    ///
73    /// The sign will not be usable for 30 seconds after receiving this message.
74    /// Generally optional as disconnecting switched power from the sign should
75    /// have the same effect. No response is expected.
76    Goodbye(Address),
77
78    /// Wraps a [`Frame`] that does not correspond to any known message.
79    Unknown(Frame<'a>),
80}
81
82/// The memory offset for data sent via a [`SendData`](Message::SendData) message.
83///
84/// # Examples
85///
86/// ```
87/// use flipdot_core::{Data, Message, Offset};
88///
89/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
90/// #
91/// let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
92/// let message1 = Message::SendData(Offset(0), Data::try_new(&data)?);
93/// let message2 = Message::SendData(Offset(16), Data::try_new(&data)?);
94/// // These two messages would send a total of 32 bytes, repeating the sequence twice.
95/// #
96/// # Ok(()) }
97/// ```
98#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Display, LowerHex, UpperHex)]
99pub struct Offset(pub u16);
100
101/// The number of chunks sent in [`SendData`](Message::SendData) messages, reported by [`DataChunksSent`](Message::DataChunksSent).
102///
103/// # Examples
104///
105/// ```
106/// use flipdot_core::{ChunkCount, Message};
107///
108/// // Assume we just sent three SendData messages. That should be followed with:
109/// let message = Message::DataChunksSent(ChunkCount(3));
110/// ```
111#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Display, LowerHex, UpperHex)]
112pub struct ChunkCount(pub u16);
113
114/// Possible states that a sign can be in during operation.
115///
116/// These are reported by the sign in a [`ReportState`](Message::ReportState) message
117/// in response to [`Hello`](Message::Hello) or [`QueryState`](Message::QueryState).
118#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
119#[non_exhaustive]
120pub enum State {
121    /// The initial state upon power on or after a reset.
122    /// No configuration or pixel data stored.
123    Unconfigured,
124    /// The sign is waiting for the ODK to send the 16-byte configuration data.
125    ConfigInProgress,
126    /// The configuration data was successfully received.
127    ConfigReceived,
128    /// Error encountered while reading the configuration data.
129    ConfigFailed,
130    /// The sign is waiting for the ODK to send the pixel data.
131    PixelsInProgress,
132    /// Pixel data was successfully received.
133    PixelsReceived,
134    /// Error encountered while reading the pixel data.
135    PixelsFailed,
136    /// Page was loaded into memory and is ready to be shown.
137    PageLoaded,
138    /// Page is in the process of being loaded into memory.
139    PageLoadInProgress,
140    /// Loaded page was successfully shown.
141    PageShown,
142    /// Page is in the process of being shown.
143    PageShowInProgress,
144    /// The sign is automatically flipping between pages.
145    /// Mutually exclusive with the Page* states above.
146    ShowingPages,
147    /// Sign is ready to reset back to the `Unconfigured` state.
148    ReadyToReset,
149}
150
151/// Operations that can be requested of a sign, which trigger actions and/or state changes.
152///
153/// These are requested by the ODK via a [`RequestOperation`](Message::RequestOperation) message.
154#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
155#[non_exhaustive]
156pub enum Operation {
157    /// Receive the 16-byte configuration data.
158    ReceiveConfig,
159    /// Receive one or more pages of pixel data.
160    ReceivePixels,
161    /// Show the page that's currently loaded in memory.
162    ShowLoadedPage,
163    /// Load the next stored page into memory in preparation to show.
164    LoadNextPage,
165    /// Begin the process of resetting back to the [`Unconfigured`](State::Unconfigured) state.
166    StartReset,
167    /// Finish the process of resetting back to the [`Unconfigured`](State::Unconfigured) state.
168    FinishReset,
169}
170
171impl Display for Message<'_> {
172    /// Provides a human-readable view of the message.
173    ///
174    /// This is useful, for example, when monitoring the traffic on a bus.
175    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
176        match *self {
177            Message::SendData(offset, ref data) => {
178                write!(f, "SendData [Offset {:04X}] ", offset)?;
179                for byte in data.get().iter() {
180                    write!(f, "{:02X} ", byte)?;
181                }
182            }
183
184            Message::DataChunksSent(chunks) => write!(f, "DataChunksSent [{:04X}]", chunks)?,
185
186            Message::Hello(address) => write!(f, "[Addr {:04X}] <-- Hello", address)?,
187            Message::QueryState(address) => write!(f, "[Addr {:04X}] <-- QueryState", address)?,
188            Message::ReportState(address, state) => write!(f, "[Addr {:04X}] --> ReportState [{:?}]", address, state)?,
189
190            Message::RequestOperation(address, op) => write!(f, "[Addr {:04X}] <-- RequestOperation [{:?}]", address, op)?,
191            Message::AckOperation(address, op) => write!(f, "[Addr {:04X}] --> AckOperation [{:?}]", address, op)?,
192
193            Message::PixelsComplete(address) => write!(f, "[Addr {:04X}] <-- PixelsComplete", address)?,
194
195            Message::Goodbye(address) => write!(f, "[Addr {:04X}] <-- Goodbye", address)?,
196
197            Message::Unknown(ref frame) => write!(f, "Unknown {}", frame)?,
198        }
199
200        Ok(())
201    }
202}
203
204impl<'a> From<Frame<'a>> for Message<'a> {
205    /// Converts a [`Frame`] into a `Message`.
206    ///
207    /// This cannot fail as all valid [`Frame`]s are representable as `Message`s (though perhaps `Unknown`).
208    /// The input [`Frame`] is consumed to allow efficiently reusing its data where possible.
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// # use flipdot_core::{Address, Data, Frame, Message, MsgType, State};
214    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
215    /// #
216    /// let frame = Frame::new(Address(0x12), MsgType(4), Data::try_new(vec![0x07])?);
217    /// let message = Message::from(frame);
218    /// assert_eq!(Message::ReportState(Address(0x12), State::ConfigReceived), message);
219    /// #
220    /// # Ok(()) }
221    /// ```
222    fn from(frame: Frame<'a>) -> Self {
223        match frame.data().len() {
224            0 => match frame.message_type() {
225                MsgType(1) => Message::DataChunksSent(ChunkCount(frame.address().0)),
226                _ => Message::Unknown(frame),
227            },
228
229            1 => match (frame.message_type(), frame.data()[0]) {
230                (MsgType(2), 0xFF) => Message::Hello(frame.address()),
231                (MsgType(2), 0x00) => Message::QueryState(frame.address()),
232                (MsgType(2), 0x55) => Message::Goodbye(frame.address()),
233
234                (MsgType(4), 0x0F) => Message::ReportState(frame.address(), State::Unconfigured),
235                (MsgType(4), 0x0D) => Message::ReportState(frame.address(), State::ConfigInProgress),
236                (MsgType(4), 0x07) => Message::ReportState(frame.address(), State::ConfigReceived),
237                (MsgType(4), 0x0C) => Message::ReportState(frame.address(), State::ConfigFailed),
238                (MsgType(4), 0x03) => Message::ReportState(frame.address(), State::PixelsInProgress),
239                (MsgType(4), 0x01) => Message::ReportState(frame.address(), State::PixelsReceived),
240                (MsgType(4), 0x0B) => Message::ReportState(frame.address(), State::PixelsFailed),
241                (MsgType(4), 0x10) => Message::ReportState(frame.address(), State::PageLoaded),
242                (MsgType(4), 0x13) => Message::ReportState(frame.address(), State::PageLoadInProgress),
243                (MsgType(4), 0x12) => Message::ReportState(frame.address(), State::PageShown),
244                (MsgType(4), 0x11) => Message::ReportState(frame.address(), State::PageShowInProgress),
245                (MsgType(4), 0x00) => Message::ReportState(frame.address(), State::ShowingPages),
246                (MsgType(4), 0x08) => Message::ReportState(frame.address(), State::ReadyToReset),
247
248                (MsgType(3), 0xA1) => Message::RequestOperation(frame.address(), Operation::ReceiveConfig),
249                (MsgType(3), 0xA2) => Message::RequestOperation(frame.address(), Operation::ReceivePixels),
250                (MsgType(3), 0xA9) => Message::RequestOperation(frame.address(), Operation::ShowLoadedPage),
251                (MsgType(3), 0xAA) => Message::RequestOperation(frame.address(), Operation::LoadNextPage),
252                (MsgType(3), 0xA6) => Message::RequestOperation(frame.address(), Operation::StartReset),
253                (MsgType(3), 0xA7) => Message::RequestOperation(frame.address(), Operation::FinishReset),
254
255                (MsgType(5), 0x95) => Message::AckOperation(frame.address(), Operation::ReceiveConfig),
256                (MsgType(5), 0x91) => Message::AckOperation(frame.address(), Operation::ReceivePixels),
257                (MsgType(5), 0x96) => Message::AckOperation(frame.address(), Operation::ShowLoadedPage),
258                (MsgType(5), 0x97) => Message::AckOperation(frame.address(), Operation::LoadNextPage),
259                (MsgType(5), 0x93) => Message::AckOperation(frame.address(), Operation::StartReset),
260                (MsgType(5), 0x94) => Message::AckOperation(frame.address(), Operation::FinishReset),
261
262                (MsgType(6), 0x00) => Message::PixelsComplete(frame.address()),
263
264                (_, _) => Message::Unknown(frame),
265            },
266
267            _ => match frame.message_type() {
268                MsgType(0) => Message::SendData(Offset(frame.address().0), frame.into_data()),
269                _ => Message::Unknown(frame),
270            },
271        }
272    }
273}
274
275impl<'a> From<Message<'a>> for Frame<'a> {
276    /// Converts a [`Message`] into a `Frame`.
277    ///
278    /// This cannot fail as all [`Message`]s can be represented as `Frame`s.
279    /// The input [`Message`] is consumed to allow efficiently reusing its data where possible.
280    ///
281    /// # Examples
282    ///
283    /// ```
284    /// # use flipdot_core::{Address, Data, Frame, Message, MsgType, State};
285    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
286    /// #
287    /// let message = Message::ReportState(Address(0xFF), State::ConfigReceived);
288    /// let frame = Frame::from(message);
289    /// assert_eq!(Frame::new(Address(0xFF), MsgType(4), Data::try_new(vec![0x07])?), frame);
290    /// #
291    /// # Ok(()) }
292    /// ```
293    fn from(message: Message<'a>) -> Self {
294        match message {
295            Message::SendData(Offset(offset), data) => Frame::new(Address(offset), MsgType(0), data),
296
297            Message::DataChunksSent(ChunkCount(chunks)) => Frame::new(Address(chunks), MsgType(1), Data::from(&[])),
298
299            Message::Hello(address) => Frame::new(address, MsgType(2), Data::from(&[0xFF])),
300            Message::Goodbye(address) => Frame::new(address, MsgType(2), Data::from(&[0x55])),
301            Message::QueryState(address) => Frame::new(address, MsgType(2), Data::from(&[0x00])),
302
303            Message::ReportState(address, State::Unconfigured) => Frame::new(address, MsgType(4), Data::from(&[0x0F])),
304            Message::ReportState(address, State::ConfigInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x0D])),
305            Message::ReportState(address, State::ConfigReceived) => Frame::new(address, MsgType(4), Data::from(&[0x07])),
306            Message::ReportState(address, State::ConfigFailed) => Frame::new(address, MsgType(4), Data::from(&[0x0C])),
307            Message::ReportState(address, State::PixelsInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x03])),
308            Message::ReportState(address, State::PixelsReceived) => Frame::new(address, MsgType(4), Data::from(&[0x01])),
309            Message::ReportState(address, State::PixelsFailed) => Frame::new(address, MsgType(4), Data::from(&[0x0B])),
310            Message::ReportState(address, State::PageLoaded) => Frame::new(address, MsgType(4), Data::from(&[0x10])),
311            Message::ReportState(address, State::PageLoadInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x13])),
312            Message::ReportState(address, State::PageShown) => Frame::new(address, MsgType(4), Data::from(&[0x12])),
313            Message::ReportState(address, State::PageShowInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x11])),
314            Message::ReportState(address, State::ShowingPages) => Frame::new(address, MsgType(4), Data::from(&[0x00])),
315            Message::ReportState(address, State::ReadyToReset) => Frame::new(address, MsgType(4), Data::from(&[0x08])),
316
317            Message::RequestOperation(address, Operation::ReceiveConfig) => Frame::new(address, MsgType(3), Data::from(&[0xA1])),
318            Message::RequestOperation(address, Operation::ReceivePixels) => Frame::new(address, MsgType(3), Data::from(&[0xA2])),
319            Message::RequestOperation(address, Operation::ShowLoadedPage) => Frame::new(address, MsgType(3), Data::from(&[0xA9])),
320            Message::RequestOperation(address, Operation::LoadNextPage) => Frame::new(address, MsgType(3), Data::from(&[0xAA])),
321            Message::RequestOperation(address, Operation::StartReset) => Frame::new(address, MsgType(3), Data::from(&[0xA6])),
322            Message::RequestOperation(address, Operation::FinishReset) => Frame::new(address, MsgType(3), Data::from(&[0xA7])),
323
324            Message::AckOperation(address, Operation::ReceiveConfig) => Frame::new(address, MsgType(5), Data::from(&[0x95])),
325            Message::AckOperation(address, Operation::ReceivePixels) => Frame::new(address, MsgType(5), Data::from(&[0x91])),
326            Message::AckOperation(address, Operation::ShowLoadedPage) => Frame::new(address, MsgType(5), Data::from(&[0x96])),
327            Message::AckOperation(address, Operation::LoadNextPage) => Frame::new(address, MsgType(5), Data::from(&[0x97])),
328            Message::AckOperation(address, Operation::StartReset) => Frame::new(address, MsgType(5), Data::from(&[0x93])),
329            Message::AckOperation(address, Operation::FinishReset) => Frame::new(address, MsgType(5), Data::from(&[0x94])),
330
331            Message::PixelsComplete(address) => Frame::new(address, MsgType(6), Data::from(&[0x00])),
332
333            Message::Unknown(frame) => frame,
334        }
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341
342    fn verify_roundtrip(frame: Frame<'_>, expected_message: Message<'_>) {
343        let orig_frame = frame.clone();
344
345        let converted_message = Message::from(frame);
346        assert_eq!(expected_message, converted_message);
347
348        let converted_frame = Frame::from(converted_message);
349        assert_eq!(orig_frame, converted_frame);
350    }
351
352    #[test]
353    fn frame_message_roundtrip() {
354        verify_roundtrip(
355            Frame::new(Address(16), MsgType(0), Data::from(&[0x00, 0x15, 0x51, 0xF7])),
356            Message::SendData(Offset(16), Data::from(&[0x00, 0x15, 0x51, 0xF7])),
357        );
358
359        verify_roundtrip(
360            Frame::new(Address(13), MsgType(1), Data::from(&[])),
361            Message::DataChunksSent(ChunkCount(13)),
362        );
363
364        verify_roundtrip(
365            Frame::new(Address(0x7F), MsgType(2), Data::from(&[0xFF])),
366            Message::Hello(Address(0x7F)),
367        );
368        verify_roundtrip(
369            Frame::new(Address(0x11), MsgType(2), Data::from(&[0x55])),
370            Message::Goodbye(Address(0x11)),
371        );
372        verify_roundtrip(
373            Frame::new(Address(0xFF), MsgType(2), Data::from(&[0x00])),
374            Message::QueryState(Address(0xFF)),
375        );
376
377        verify_roundtrip(
378            Frame::new(Address(0x01), MsgType(4), Data::from(&[0x0F])),
379            Message::ReportState(Address(0x01), State::Unconfigured),
380        );
381
382        verify_roundtrip(
383            Frame::new(Address(0x00), MsgType(3), Data::from(&[0xA1])),
384            Message::RequestOperation(Address(0x00), Operation::ReceiveConfig),
385        );
386        verify_roundtrip(
387            Frame::new(Address(0x01), MsgType(3), Data::from(&[0xA2])),
388            Message::RequestOperation(Address(0x01), Operation::ReceivePixels),
389        );
390        verify_roundtrip(
391            Frame::new(Address(0x11), MsgType(3), Data::from(&[0xA9])),
392            Message::RequestOperation(Address(0x11), Operation::ShowLoadedPage),
393        );
394        verify_roundtrip(
395            Frame::new(Address(0x02), MsgType(3), Data::from(&[0xAA])),
396            Message::RequestOperation(Address(0x02), Operation::LoadNextPage),
397        );
398        verify_roundtrip(
399            Frame::new(Address(0x22), MsgType(3), Data::from(&[0xA6])),
400            Message::RequestOperation(Address(0x22), Operation::StartReset),
401        );
402        verify_roundtrip(
403            Frame::new(Address(0x03), MsgType(3), Data::from(&[0xA7])),
404            Message::RequestOperation(Address(0x03), Operation::FinishReset),
405        );
406
407        verify_roundtrip(
408            Frame::new(Address(0xFF), MsgType(4), Data::from(&[0x0F])),
409            Message::ReportState(Address(0xFF), State::Unconfigured),
410        );
411        verify_roundtrip(
412            Frame::new(Address(0x91), MsgType(4), Data::from(&[0x0D])),
413            Message::ReportState(Address(0x91), State::ConfigInProgress),
414        );
415        verify_roundtrip(
416            Frame::new(Address(0xDC), MsgType(4), Data::from(&[0x07])),
417            Message::ReportState(Address(0xDC), State::ConfigReceived),
418        );
419        verify_roundtrip(
420            Frame::new(Address(0xA1), MsgType(4), Data::from(&[0x0C])),
421            Message::ReportState(Address(0xA1), State::ConfigFailed),
422        );
423        verify_roundtrip(
424            Frame::new(Address(0xF7), MsgType(4), Data::from(&[0x03])),
425            Message::ReportState(Address(0xF7), State::PixelsInProgress),
426        );
427        verify_roundtrip(
428            Frame::new(Address(0x0F), MsgType(4), Data::from(&[0x01])),
429            Message::ReportState(Address(0x0F), State::PixelsReceived),
430        );
431        verify_roundtrip(
432            Frame::new(Address(0x37), MsgType(4), Data::from(&[0x0B])),
433            Message::ReportState(Address(0x37), State::PixelsFailed),
434        );
435        verify_roundtrip(
436            Frame::new(Address(0x42), MsgType(4), Data::from(&[0x10])),
437            Message::ReportState(Address(0x42), State::PageLoaded),
438        );
439        verify_roundtrip(
440            Frame::new(Address(0x68), MsgType(4), Data::from(&[0x13])),
441            Message::ReportState(Address(0x68), State::PageLoadInProgress),
442        );
443        verify_roundtrip(
444            Frame::new(Address(0x1C), MsgType(4), Data::from(&[0x12])),
445            Message::ReportState(Address(0x1C), State::PageShown),
446        );
447        verify_roundtrip(
448            Frame::new(Address(0x9D), MsgType(4), Data::from(&[0x11])),
449            Message::ReportState(Address(0x9D), State::PageShowInProgress),
450        );
451        verify_roundtrip(
452            Frame::new(Address(0x87), MsgType(4), Data::from(&[0x08])),
453            Message::ReportState(Address(0x87), State::ReadyToReset),
454        );
455
456        verify_roundtrip(
457            Frame::new(Address(0xABCD), MsgType(5), Data::from(&[0x95])),
458            Message::AckOperation(Address(0xABCD), Operation::ReceiveConfig),
459        );
460        verify_roundtrip(
461            Frame::new(Address(0xFF00), MsgType(5), Data::from(&[0x91])),
462            Message::AckOperation(Address(0xFF00), Operation::ReceivePixels),
463        );
464        verify_roundtrip(
465            Frame::new(Address(0x0C0F), MsgType(5), Data::from(&[0x96])),
466            Message::AckOperation(Address(0x0C0F), Operation::ShowLoadedPage),
467        );
468        verify_roundtrip(
469            Frame::new(Address(0x11DD), MsgType(5), Data::from(&[0x97])),
470            Message::AckOperation(Address(0x11DD), Operation::LoadNextPage),
471        );
472        verify_roundtrip(
473            Frame::new(Address(0x1337), MsgType(5), Data::from(&[0x93])),
474            Message::AckOperation(Address(0x1337), Operation::StartReset),
475        );
476        verify_roundtrip(
477            Frame::new(Address(0x1987), MsgType(5), Data::from(&[0x94])),
478            Message::AckOperation(Address(0x1987), Operation::FinishReset),
479        );
480
481        verify_roundtrip(
482            Frame::new(Address(0xFFFF), MsgType(6), Data::from(&[0x00])),
483            Message::PixelsComplete(Address(0xFFFF)),
484        );
485
486        verify_roundtrip(
487            Frame::new(Address(0xF00D), MsgType(99), Data::from(&[])),
488            Message::Unknown(Frame::new(Address(0xF00D), MsgType(99), Data::from(&[]))),
489        );
490
491        verify_roundtrip(
492            Frame::new(Address(0xBEEF), MsgType(255), Data::from(&[0xAA])),
493            Message::Unknown(Frame::new(Address(0xBEEF), MsgType(255), Data::from(&[0xAA]))),
494        );
495
496        verify_roundtrip(
497            Frame::new(Address(0xABAB), MsgType(17), Data::from(&[0x7A, 0x1C])),
498            Message::Unknown(Frame::new(Address(0xABAB), MsgType(17), Data::from(&[0x7A, 0x1C]))),
499        );
500    }
501
502    #[test]
503    fn display() {
504        let message = Message::SendData(Offset(0x10), Data::from(&[0x20, 0xFF]));
505        let display = format!("{}", message);
506        assert_eq!("SendData [Offset 0010] 20 FF", display.trim());
507
508        let message = Message::DataChunksSent(ChunkCount(3));
509        let display = format!("{}", message);
510        assert_eq!("DataChunksSent [0003]", display);
511
512        let message = Message::Hello(Address(0x7F));
513        let display = format!("{}", message);
514        assert_eq!("[Addr 007F] <-- Hello", display);
515
516        let message = Message::QueryState(Address(5));
517        let display = format!("{}", message);
518        assert_eq!("[Addr 0005] <-- QueryState", display);
519
520        let message = Message::ReportState(Address(7), State::Unconfigured);
521        let display = format!("{}", message);
522        assert_eq!("[Addr 0007] --> ReportState [Unconfigured]", display);
523
524        let message = Message::RequestOperation(Address(16), Operation::ReceivePixels);
525        let display = format!("{}", message);
526        assert_eq!("[Addr 0010] <-- RequestOperation [ReceivePixels]", display);
527
528        let message = Message::AckOperation(Address(17), Operation::FinishReset);
529        let display = format!("{}", message);
530        assert_eq!("[Addr 0011] --> AckOperation [FinishReset]", display);
531
532        let message = Message::PixelsComplete(Address(32));
533        let display = format!("{}", message);
534        assert_eq!("[Addr 0020] <-- PixelsComplete", display);
535
536        let message = Message::Goodbye(Address(1));
537        let display = format!("{}", message);
538        assert_eq!("[Addr 0001] <-- Goodbye", display);
539
540        let message = Message::Unknown(Frame::new(Address(1), MsgType(2), Data::from(&[])));
541        let display = format!("{}", message);
542        assert_eq!("Unknown Type 02 | Addr 0001", display);
543
544        let message = Message::Unknown(Frame::new(Address(1), MsgType(2), Data::from(&[0x0B, 0x1C])));
545        let display = format!("{}", message);
546        assert_eq!("Unknown Type 02 | Addr 0001 | Data 0B 1C ", display);
547    }
548}