diff --git a/examples/announce/src/main.rs b/examples/announce/src/main.rs index aad44a7..7194067 100644 --- a/examples/announce/src/main.rs +++ b/examples/announce/src/main.rs @@ -9,6 +9,9 @@ struct Cli { text: Vec, } +/// example: `cargo run -- --text "Hallo, +/// CCCB"` + fn main() { let cli = Cli::parse(); println!("starting with args: {:?}", &cli); diff --git a/src/command.rs b/src/command.rs index 86ec9a6..b1c9318 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,5 +1,6 @@ -use crate::{BitVec, Header, Packet, PixelGrid, TILE_SIZE, ToPacket}; -use crate::command_codes::DisplayCommandCode; +use crate::{BitVec, Packet, PixelGrid, TILE_SIZE}; +use crate::command_codes::CommandCode; +use crate::packet::Header; /// A window #[derive(Debug, Copy, Clone)] @@ -33,53 +34,142 @@ pub enum Command { BitmapLinearWin(Origin, PixelGrid), } -fn offset_and_payload(command: DisplayCommandCode, offset: Offset, payload: Vec) -> Packet { +fn offset_and_payload(command: CommandCode, offset: Offset, payload: Vec) -> Packet { Packet(Header(command.to_primitive(), offset, payload.len() as u16, 0, 0), payload) } -fn window_and_payload(command: DisplayCommandCode, window: Window, payload: Vec) -> Packet { +fn window_and_payload(command: CommandCode, window: Window, payload: Vec) -> Packet { let Window(Origin(x, y), Size(w, h)) = window; Packet(Header(command.to_primitive(), x, y, w, h), payload.into()) } -fn command_code_only(code: DisplayCommandCode) -> Packet { +fn command_code_only(code: CommandCode) -> Packet { Packet(Header(code.to_primitive(), 0x0000, 0x0000, 0x0000, 0x0000), vec!()) } -impl ToPacket for Command { - fn to_packet(self) -> Packet { +impl Into for Command { + fn into(self) -> Packet { match self { - Command::Clear => command_code_only(DisplayCommandCode::Clear), - Command::FadeOut => command_code_only(DisplayCommandCode::FadeOut), - Command::HardReset => command_code_only(DisplayCommandCode::HardReset), + Command::Clear => command_code_only(CommandCode::Clear), + Command::FadeOut => command_code_only(CommandCode::FadeOut), + Command::HardReset => command_code_only(CommandCode::HardReset), Command::CharBrightness(window, payload) => { - window_and_payload(DisplayCommandCode::CharBrightness, window, payload) + window_and_payload(CommandCode::CharBrightness, window, payload) } Command::Brightness(brightness) => { - Packet(Header(DisplayCommandCode::Brightness.to_primitive(), 0x00000, 0x0000, 0x0000, 0x0000), vec!(brightness)) + Packet(Header(CommandCode::Brightness.to_primitive(), 0x00000, 0x0000, 0x0000, 0x0000), vec!(brightness)) } Command::BitmapLinear(offset, bits) => { - offset_and_payload(DisplayCommandCode::BitmapLinear, offset, bits.into()) + offset_and_payload(CommandCode::BitmapLinear, offset, bits.into()) } Command::BitmapLinearWin(Origin(pixel_x, pixel_y), pixels) => { debug_assert_eq!(pixel_x % 8, 0); debug_assert_eq!(pixels.width % 8, 0); - Packet(Header(0x0013, pixel_x / TILE_SIZE, pixel_y, pixels.width as u16 / TILE_SIZE, pixels.height as u16), pixels.into()) + Packet( + Header(CommandCode::BitmapLinearWin.to_primitive(), pixel_x / TILE_SIZE, pixel_y, pixels.width as u16 / TILE_SIZE, pixels.height as u16), + pixels.into()) } Command::BitmapLinearAnd(offset, bits) => { - offset_and_payload(DisplayCommandCode::BitmapLinearAnd, offset, bits.into()) + offset_and_payload(CommandCode::BitmapLinearAnd, offset, bits.into()) } Command::BitmapLinearOr(offset, bits) => { - offset_and_payload(DisplayCommandCode::BitmapLinearOr, offset, bits.into()) + offset_and_payload(CommandCode::BitmapLinearOr, offset, bits.into()) } Command::BitmapLinearXor(offset, bits) => { - offset_and_payload(DisplayCommandCode::BitmapLinearXor, offset, bits.into()) + offset_and_payload(CommandCode::BitmapLinearXor, offset, bits.into()) } Command::Cp437Data(window, payload) => { - window_and_payload(DisplayCommandCode::Cp437data, window, payload) + window_and_payload(CommandCode::Cp437Data, window, payload) } } } } + +#[derive(Debug)] +pub enum TryFromPacketError { + InvalidCommand(u16), + ExtraneousPayload(usize, usize), + ExtraneousHeaderValues, +} + +fn check_empty_header(packet: &Packet) -> Option { + let Packet(Header(_, a, b, c, d), _) = &packet; + if *a != 0 || *b != 0 || *c != 0 || *d != 0 { + Some(TryFromPacketError::ExtraneousHeaderValues) + } else { + None + } +} + +fn check_command_only(packet: &Packet) -> Option { + let Packet(_, payload) = packet; + if payload.len() != 0 { + Some(TryFromPacketError::ExtraneousPayload(0, payload.len())) + } else { + check_empty_header(packet) + } +} + +impl TryFrom for Command { + type Error = TryFromPacketError; + + fn try_from(value: Packet) -> Result { + let Packet(Header(command_u16, a, b, c, d), payload) = &value; + let command_code = match CommandCode::from_primitive(*command_u16) { + None => return Err(TryFromPacketError::InvalidCommand(*command_u16)), + Some(value) => value + }; + + match command_code { + CommandCode::Clear => { + if let Some(err) = check_command_only(&value) { + return Err(err); + } + Ok(Command::Clear) + } + CommandCode::Brightness => { + if let Some(err) = check_empty_header(&value) { + return Err(err); + } + Ok(Command::Brightness(payload[0])) + } + CommandCode::HardReset => { + if let Some(err) = check_command_only(&value) { + return Err(err); + } + Ok(Command::HardReset) + } + CommandCode::FadeOut => { + if let Some(err) = check_command_only(&value) { + return Err(err); + } + Ok(Command::FadeOut) + } + CommandCode::Cp437Data => { + Ok(Command::Cp437Data( + Window(Origin(*a, *b), Size(*c, *d)), + payload.clone(), + )) + } + CommandCode::CharBrightness => { + Ok(Command::CharBrightness( + Window(Origin(*a, *b), Size(*c, *d)), + payload.clone(), + )) + } + CommandCode::BitmapLegacy => { todo!() } + CommandCode::BitmapLinear => { todo!() } + CommandCode::BitmapLinearWin => { + Ok(Command::BitmapLinearWin( + Origin(*a * TILE_SIZE, *b), + PixelGrid::load(*c as usize * TILE_SIZE as usize, *d as usize, payload), + )) + } + CommandCode::BitmapLinearAnd => { todo!() } + CommandCode::BitmapLinearOr => { todo!() } + CommandCode::BitmapLinearXor => { todo!() } + } + } +} diff --git a/src/command_codes.rs b/src/command_codes.rs index bd4199a..8847bec 100644 --- a/src/command_codes.rs +++ b/src/command_codes.rs @@ -3,9 +3,9 @@ use num_derive::{FromPrimitive, ToPrimitive}; #[repr(u16)] #[derive(Debug, FromPrimitive, ToPrimitive)] -pub enum DisplayCommandCode { +pub enum CommandCode { Clear = 0x0002, - Cp437data = 0x0003, + Cp437Data = 0x0003, CharBrightness = 0x0005, Brightness = 0x0007, HardReset = 0x000b, @@ -18,7 +18,7 @@ pub enum DisplayCommandCode { BitmapLinearXor = 0x0016, } -impl DisplayCommandCode { +impl CommandCode { pub fn from_primitive(value: u16) -> Option { FromPrimitive::from_u16(value) } diff --git a/src/connection.rs b/src/connection.rs index 92b8dfc..f687d5f 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,14 +1,10 @@ use std::net::{ToSocketAddrs, UdpSocket}; -use crate::{Packet}; +use crate::Packet; pub struct Connection { socket: UdpSocket, } -pub trait ToPacket { - fn to_packet(self) -> Packet; -} - impl Connection { /// Open a new UDP socket and create a display instance pub fn open(addr: impl ToSocketAddrs) -> std::io::Result { @@ -18,9 +14,10 @@ impl Connection { } /// Send a command to the display - pub fn send(&self, packet: impl ToPacket) -> std::io::Result<()> { - let packet = packet.to_packet(); - self.socket.send(&*packet.to_bytes())?; + pub fn send(&self, packet: impl Into) -> std::io::Result<()> { + let packet = packet.into(); + let data: Vec = packet.into(); + self.socket.send(&*data)?; Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index a3bfff3..1289b41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,12 +5,12 @@ mod packet; mod command; mod command_codes; -pub use crate::connection::{Connection, ToPacket}; +pub use crate::connection::{Connection}; pub use crate::pixel_grid::{PixelGrid}; pub use crate::bit_vec::{BitVec}; pub use crate::packet::{Packet, Header, Payload}; pub use crate::command::{Command, Size, Origin, Window}; -pub use crate::command_codes::{DisplayCommandCode}; +pub use crate::command_codes::{CommandCode}; pub const TILE_SIZE: u16 = 8; pub const TILE_WIDTH: u16 = 56; diff --git a/src/packet.rs b/src/packet.rs index 8473dba..3b6655f 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -4,16 +4,16 @@ pub type Payload = Vec; pub struct Packet(pub Header, pub Payload); -impl Packet { - pub fn to_bytes(&self) -> Vec { +impl Into> for Packet { + fn into(self) -> Vec { let Packet(Header(mode, a, b, c, d), payload) = self; let mut packet = vec!(0u8; 10 + payload.len()); - packet[0..=1].copy_from_slice(&u16::to_be_bytes(*mode)); - packet[2..=3].copy_from_slice(&u16::to_be_bytes(*a)); - packet[4..=5].copy_from_slice(&u16::to_be_bytes(*b)); - packet[6..=7].copy_from_slice(&u16::to_be_bytes(*c)); - packet[8..=9].copy_from_slice(&u16::to_be_bytes(*d)); + packet[0..=1].copy_from_slice(&u16::to_be_bytes(mode)); + packet[2..=3].copy_from_slice(&u16::to_be_bytes(a)); + packet[4..=5].copy_from_slice(&u16::to_be_bytes(b)); + packet[6..=7].copy_from_slice(&u16::to_be_bytes(c)); + packet[8..=9].copy_from_slice(&u16::to_be_bytes(d)); packet[10..].copy_from_slice(&*payload);