use std::fmt::{self, Display, Formatter};
use derive_more::{Display, LowerHex, UpperHex};
use crate::{Address, Data, Frame, MsgType};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Message<'a> {
SendData(Offset, Data<'a>),
DataChunksSent(ChunkCount),
Hello(Address),
QueryState(Address),
ReportState(Address, State),
RequestOperation(Address, Operation),
AckOperation(Address, Operation),
PixelsComplete(Address),
Goodbye(Address),
Unknown(Frame<'a>),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Display, LowerHex, UpperHex)]
pub struct Offset(pub u16);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Display, LowerHex, UpperHex)]
pub struct ChunkCount(pub u16);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum State {
Unconfigured,
ConfigInProgress,
ConfigReceived,
ConfigFailed,
PixelsInProgress,
PixelsReceived,
PixelsFailed,
PageLoaded,
PageLoadInProgress,
PageShown,
PageShowInProgress,
ShowingPages,
ReadyToReset,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Operation {
ReceiveConfig,
ReceivePixels,
ShowLoadedPage,
LoadNextPage,
StartReset,
FinishReset,
}
impl Display for Message<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Message::SendData(offset, ref data) => {
write!(f, "SendData [Offset {:04X}] ", offset)?;
for byte in data.get().iter() {
write!(f, "{:02X} ", byte)?;
}
}
Message::DataChunksSent(chunks) => write!(f, "DataChunksSent [{:04X}]", chunks)?,
Message::Hello(address) => write!(f, "[Addr {:04X}] <-- Hello", address)?,
Message::QueryState(address) => write!(f, "[Addr {:04X}] <-- QueryState", address)?,
Message::ReportState(address, state) => write!(f, "[Addr {:04X}] --> ReportState [{:?}]", address, state)?,
Message::RequestOperation(address, op) => write!(f, "[Addr {:04X}] <-- RequestOperation [{:?}]", address, op)?,
Message::AckOperation(address, op) => write!(f, "[Addr {:04X}] --> AckOperation [{:?}]", address, op)?,
Message::PixelsComplete(address) => write!(f, "[Addr {:04X}] <-- PixelsComplete", address)?,
Message::Goodbye(address) => write!(f, "[Addr {:04X}] <-- Goodbye", address)?,
Message::Unknown(ref frame) => write!(f, "Unknown {}", frame)?,
}
Ok(())
}
}
impl<'a> From<Frame<'a>> for Message<'a> {
fn from(frame: Frame<'a>) -> Self {
match frame.data().len() {
0 => match frame.message_type() {
MsgType(1) => Message::DataChunksSent(ChunkCount(frame.address().0)),
_ => Message::Unknown(frame),
},
1 => match (frame.message_type(), frame.data()[0]) {
(MsgType(2), 0xFF) => Message::Hello(frame.address()),
(MsgType(2), 0x00) => Message::QueryState(frame.address()),
(MsgType(2), 0x55) => Message::Goodbye(frame.address()),
(MsgType(4), 0x0F) => Message::ReportState(frame.address(), State::Unconfigured),
(MsgType(4), 0x0D) => Message::ReportState(frame.address(), State::ConfigInProgress),
(MsgType(4), 0x07) => Message::ReportState(frame.address(), State::ConfigReceived),
(MsgType(4), 0x0C) => Message::ReportState(frame.address(), State::ConfigFailed),
(MsgType(4), 0x03) => Message::ReportState(frame.address(), State::PixelsInProgress),
(MsgType(4), 0x01) => Message::ReportState(frame.address(), State::PixelsReceived),
(MsgType(4), 0x0B) => Message::ReportState(frame.address(), State::PixelsFailed),
(MsgType(4), 0x10) => Message::ReportState(frame.address(), State::PageLoaded),
(MsgType(4), 0x13) => Message::ReportState(frame.address(), State::PageLoadInProgress),
(MsgType(4), 0x12) => Message::ReportState(frame.address(), State::PageShown),
(MsgType(4), 0x11) => Message::ReportState(frame.address(), State::PageShowInProgress),
(MsgType(4), 0x00) => Message::ReportState(frame.address(), State::ShowingPages),
(MsgType(4), 0x08) => Message::ReportState(frame.address(), State::ReadyToReset),
(MsgType(3), 0xA1) => Message::RequestOperation(frame.address(), Operation::ReceiveConfig),
(MsgType(3), 0xA2) => Message::RequestOperation(frame.address(), Operation::ReceivePixels),
(MsgType(3), 0xA9) => Message::RequestOperation(frame.address(), Operation::ShowLoadedPage),
(MsgType(3), 0xAA) => Message::RequestOperation(frame.address(), Operation::LoadNextPage),
(MsgType(3), 0xA6) => Message::RequestOperation(frame.address(), Operation::StartReset),
(MsgType(3), 0xA7) => Message::RequestOperation(frame.address(), Operation::FinishReset),
(MsgType(5), 0x95) => Message::AckOperation(frame.address(), Operation::ReceiveConfig),
(MsgType(5), 0x91) => Message::AckOperation(frame.address(), Operation::ReceivePixels),
(MsgType(5), 0x96) => Message::AckOperation(frame.address(), Operation::ShowLoadedPage),
(MsgType(5), 0x97) => Message::AckOperation(frame.address(), Operation::LoadNextPage),
(MsgType(5), 0x93) => Message::AckOperation(frame.address(), Operation::StartReset),
(MsgType(5), 0x94) => Message::AckOperation(frame.address(), Operation::FinishReset),
(MsgType(6), 0x00) => Message::PixelsComplete(frame.address()),
(_, _) => Message::Unknown(frame),
},
_ => match frame.message_type() {
MsgType(0) => Message::SendData(Offset(frame.address().0), frame.into_data()),
_ => Message::Unknown(frame),
},
}
}
}
impl<'a> From<Message<'a>> for Frame<'a> {
fn from(message: Message<'a>) -> Self {
match message {
Message::SendData(Offset(offset), data) => Frame::new(Address(offset), MsgType(0), data),
Message::DataChunksSent(ChunkCount(chunks)) => Frame::new(Address(chunks), MsgType(1), Data::from(&[])),
Message::Hello(address) => Frame::new(address, MsgType(2), Data::from(&[0xFF])),
Message::Goodbye(address) => Frame::new(address, MsgType(2), Data::from(&[0x55])),
Message::QueryState(address) => Frame::new(address, MsgType(2), Data::from(&[0x00])),
Message::ReportState(address, State::Unconfigured) => Frame::new(address, MsgType(4), Data::from(&[0x0F])),
Message::ReportState(address, State::ConfigInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x0D])),
Message::ReportState(address, State::ConfigReceived) => Frame::new(address, MsgType(4), Data::from(&[0x07])),
Message::ReportState(address, State::ConfigFailed) => Frame::new(address, MsgType(4), Data::from(&[0x0C])),
Message::ReportState(address, State::PixelsInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x03])),
Message::ReportState(address, State::PixelsReceived) => Frame::new(address, MsgType(4), Data::from(&[0x01])),
Message::ReportState(address, State::PixelsFailed) => Frame::new(address, MsgType(4), Data::from(&[0x0B])),
Message::ReportState(address, State::PageLoaded) => Frame::new(address, MsgType(4), Data::from(&[0x10])),
Message::ReportState(address, State::PageLoadInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x13])),
Message::ReportState(address, State::PageShown) => Frame::new(address, MsgType(4), Data::from(&[0x12])),
Message::ReportState(address, State::PageShowInProgress) => Frame::new(address, MsgType(4), Data::from(&[0x11])),
Message::ReportState(address, State::ShowingPages) => Frame::new(address, MsgType(4), Data::from(&[0x00])),
Message::ReportState(address, State::ReadyToReset) => Frame::new(address, MsgType(4), Data::from(&[0x08])),
Message::RequestOperation(address, Operation::ReceiveConfig) => Frame::new(address, MsgType(3), Data::from(&[0xA1])),
Message::RequestOperation(address, Operation::ReceivePixels) => Frame::new(address, MsgType(3), Data::from(&[0xA2])),
Message::RequestOperation(address, Operation::ShowLoadedPage) => Frame::new(address, MsgType(3), Data::from(&[0xA9])),
Message::RequestOperation(address, Operation::LoadNextPage) => Frame::new(address, MsgType(3), Data::from(&[0xAA])),
Message::RequestOperation(address, Operation::StartReset) => Frame::new(address, MsgType(3), Data::from(&[0xA6])),
Message::RequestOperation(address, Operation::FinishReset) => Frame::new(address, MsgType(3), Data::from(&[0xA7])),
Message::AckOperation(address, Operation::ReceiveConfig) => Frame::new(address, MsgType(5), Data::from(&[0x95])),
Message::AckOperation(address, Operation::ReceivePixels) => Frame::new(address, MsgType(5), Data::from(&[0x91])),
Message::AckOperation(address, Operation::ShowLoadedPage) => Frame::new(address, MsgType(5), Data::from(&[0x96])),
Message::AckOperation(address, Operation::LoadNextPage) => Frame::new(address, MsgType(5), Data::from(&[0x97])),
Message::AckOperation(address, Operation::StartReset) => Frame::new(address, MsgType(5), Data::from(&[0x93])),
Message::AckOperation(address, Operation::FinishReset) => Frame::new(address, MsgType(5), Data::from(&[0x94])),
Message::PixelsComplete(address) => Frame::new(address, MsgType(6), Data::from(&[0x00])),
Message::Unknown(frame) => frame,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn verify_roundtrip(frame: Frame<'_>, expected_message: Message<'_>) {
let orig_frame = frame.clone();
let converted_message = Message::from(frame);
assert_eq!(expected_message, converted_message);
let converted_frame = Frame::from(converted_message);
assert_eq!(orig_frame, converted_frame);
}
#[test]
fn frame_message_roundtrip() {
verify_roundtrip(
Frame::new(Address(16), MsgType(0), Data::from(&[0x00, 0x15, 0x51, 0xF7])),
Message::SendData(Offset(16), Data::from(&[0x00, 0x15, 0x51, 0xF7])),
);
verify_roundtrip(
Frame::new(Address(13), MsgType(1), Data::from(&[])),
Message::DataChunksSent(ChunkCount(13)),
);
verify_roundtrip(
Frame::new(Address(0x7F), MsgType(2), Data::from(&[0xFF])),
Message::Hello(Address(0x7F)),
);
verify_roundtrip(
Frame::new(Address(0x11), MsgType(2), Data::from(&[0x55])),
Message::Goodbye(Address(0x11)),
);
verify_roundtrip(
Frame::new(Address(0xFF), MsgType(2), Data::from(&[0x00])),
Message::QueryState(Address(0xFF)),
);
verify_roundtrip(
Frame::new(Address(0x01), MsgType(4), Data::from(&[0x0F])),
Message::ReportState(Address(0x01), State::Unconfigured),
);
verify_roundtrip(
Frame::new(Address(0x00), MsgType(3), Data::from(&[0xA1])),
Message::RequestOperation(Address(0x00), Operation::ReceiveConfig),
);
verify_roundtrip(
Frame::new(Address(0x01), MsgType(3), Data::from(&[0xA2])),
Message::RequestOperation(Address(0x01), Operation::ReceivePixels),
);
verify_roundtrip(
Frame::new(Address(0x11), MsgType(3), Data::from(&[0xA9])),
Message::RequestOperation(Address(0x11), Operation::ShowLoadedPage),
);
verify_roundtrip(
Frame::new(Address(0x02), MsgType(3), Data::from(&[0xAA])),
Message::RequestOperation(Address(0x02), Operation::LoadNextPage),
);
verify_roundtrip(
Frame::new(Address(0x22), MsgType(3), Data::from(&[0xA6])),
Message::RequestOperation(Address(0x22), Operation::StartReset),
);
verify_roundtrip(
Frame::new(Address(0x03), MsgType(3), Data::from(&[0xA7])),
Message::RequestOperation(Address(0x03), Operation::FinishReset),
);
verify_roundtrip(
Frame::new(Address(0xFF), MsgType(4), Data::from(&[0x0F])),
Message::ReportState(Address(0xFF), State::Unconfigured),
);
verify_roundtrip(
Frame::new(Address(0x91), MsgType(4), Data::from(&[0x0D])),
Message::ReportState(Address(0x91), State::ConfigInProgress),
);
verify_roundtrip(
Frame::new(Address(0xDC), MsgType(4), Data::from(&[0x07])),
Message::ReportState(Address(0xDC), State::ConfigReceived),
);
verify_roundtrip(
Frame::new(Address(0xA1), MsgType(4), Data::from(&[0x0C])),
Message::ReportState(Address(0xA1), State::ConfigFailed),
);
verify_roundtrip(
Frame::new(Address(0xF7), MsgType(4), Data::from(&[0x03])),
Message::ReportState(Address(0xF7), State::PixelsInProgress),
);
verify_roundtrip(
Frame::new(Address(0x0F), MsgType(4), Data::from(&[0x01])),
Message::ReportState(Address(0x0F), State::PixelsReceived),
);
verify_roundtrip(
Frame::new(Address(0x37), MsgType(4), Data::from(&[0x0B])),
Message::ReportState(Address(0x37), State::PixelsFailed),
);
verify_roundtrip(
Frame::new(Address(0x42), MsgType(4), Data::from(&[0x10])),
Message::ReportState(Address(0x42), State::PageLoaded),
);
verify_roundtrip(
Frame::new(Address(0x68), MsgType(4), Data::from(&[0x13])),
Message::ReportState(Address(0x68), State::PageLoadInProgress),
);
verify_roundtrip(
Frame::new(Address(0x1C), MsgType(4), Data::from(&[0x12])),
Message::ReportState(Address(0x1C), State::PageShown),
);
verify_roundtrip(
Frame::new(Address(0x9D), MsgType(4), Data::from(&[0x11])),
Message::ReportState(Address(0x9D), State::PageShowInProgress),
);
verify_roundtrip(
Frame::new(Address(0x87), MsgType(4), Data::from(&[0x08])),
Message::ReportState(Address(0x87), State::ReadyToReset),
);
verify_roundtrip(
Frame::new(Address(0xABCD), MsgType(5), Data::from(&[0x95])),
Message::AckOperation(Address(0xABCD), Operation::ReceiveConfig),
);
verify_roundtrip(
Frame::new(Address(0xFF00), MsgType(5), Data::from(&[0x91])),
Message::AckOperation(Address(0xFF00), Operation::ReceivePixels),
);
verify_roundtrip(
Frame::new(Address(0x0C0F), MsgType(5), Data::from(&[0x96])),
Message::AckOperation(Address(0x0C0F), Operation::ShowLoadedPage),
);
verify_roundtrip(
Frame::new(Address(0x11DD), MsgType(5), Data::from(&[0x97])),
Message::AckOperation(Address(0x11DD), Operation::LoadNextPage),
);
verify_roundtrip(
Frame::new(Address(0x1337), MsgType(5), Data::from(&[0x93])),
Message::AckOperation(Address(0x1337), Operation::StartReset),
);
verify_roundtrip(
Frame::new(Address(0x1987), MsgType(5), Data::from(&[0x94])),
Message::AckOperation(Address(0x1987), Operation::FinishReset),
);
verify_roundtrip(
Frame::new(Address(0xFFFF), MsgType(6), Data::from(&[0x00])),
Message::PixelsComplete(Address(0xFFFF)),
);
verify_roundtrip(
Frame::new(Address(0xF00D), MsgType(99), Data::from(&[])),
Message::Unknown(Frame::new(Address(0xF00D), MsgType(99), Data::from(&[]))),
);
verify_roundtrip(
Frame::new(Address(0xBEEF), MsgType(255), Data::from(&[0xAA])),
Message::Unknown(Frame::new(Address(0xBEEF), MsgType(255), Data::from(&[0xAA]))),
);
verify_roundtrip(
Frame::new(Address(0xABAB), MsgType(17), Data::from(&[0x7A, 0x1C])),
Message::Unknown(Frame::new(Address(0xABAB), MsgType(17), Data::from(&[0x7A, 0x1C]))),
);
}
#[test]
fn display() {
let message = Message::SendData(Offset(0x10), Data::from(&[0x20, 0xFF]));
let display = format!("{}", message);
assert_eq!("SendData [Offset 0010] 20 FF", display.trim());
let message = Message::DataChunksSent(ChunkCount(3));
let display = format!("{}", message);
assert_eq!("DataChunksSent [0003]", display);
let message = Message::Hello(Address(0x7F));
let display = format!("{}", message);
assert_eq!("[Addr 007F] <-- Hello", display);
let message = Message::QueryState(Address(5));
let display = format!("{}", message);
assert_eq!("[Addr 0005] <-- QueryState", display);
let message = Message::ReportState(Address(7), State::Unconfigured);
let display = format!("{}", message);
assert_eq!("[Addr 0007] --> ReportState [Unconfigured]", display);
let message = Message::RequestOperation(Address(16), Operation::ReceivePixels);
let display = format!("{}", message);
assert_eq!("[Addr 0010] <-- RequestOperation [ReceivePixels]", display);
let message = Message::AckOperation(Address(17), Operation::FinishReset);
let display = format!("{}", message);
assert_eq!("[Addr 0011] --> AckOperation [FinishReset]", display);
let message = Message::PixelsComplete(Address(32));
let display = format!("{}", message);
assert_eq!("[Addr 0020] <-- PixelsComplete", display);
let message = Message::Goodbye(Address(1));
let display = format!("{}", message);
assert_eq!("[Addr 0001] <-- Goodbye", display);
let message = Message::Unknown(Frame::new(Address(1), MsgType(2), Data::from(&[])));
let display = format!("{}", message);
assert_eq!("Unknown Type 02 | Addr 0001", display);
let message = Message::Unknown(Frame::new(Address(1), MsgType(2), Data::from(&[0x0B, 0x1C])));
let display = format!("{}", message);
assert_eq!("Unknown Type 02 | Addr 0001 | Data 0B 1C ", display);
}
}