use std::fmt::{self, Display, Formatter}; use crate::{serial::SerialDevice, warnln}; #[derive(Clone, Copy, PartialEq, Eq)] enum PrinterState { MagicBytes1 = 0x00, MagicBytes2 = 0x01, Identification = 0x02, Compression = 0x03, LengthLow = 0x04, LengthHigh = 0x05, Data = 0x06, ChecksumLow = 0x07, ChecksumHigh = 0x08, KeepAlive = 0x09, Status = 0x0a, Other = 0xff, } impl PrinterState { pub fn description(&self) -> &'static str { match self { PrinterState::MagicBytes1 => "Magic Bytes 1", PrinterState::MagicBytes2 => "Magic Bytes 2", PrinterState::Identification => "Identification", PrinterState::Compression => "Compression", PrinterState::LengthLow => "Length Low", PrinterState::LengthHigh => "Length High", PrinterState::Data => "Data", PrinterState::ChecksumLow => "Checksum Low", PrinterState::ChecksumHigh => "Checksum High", PrinterState::KeepAlive => "Keep Alive", PrinterState::Status => "Status", PrinterState::Other => "Other", } } fn from_u8(value: u8) -> Self { match value { 0x00 => PrinterState::MagicBytes1, 0x01 => PrinterState::MagicBytes2, 0x02 => PrinterState::Identification, 0x03 => PrinterState::Compression, 0x04 => PrinterState::LengthLow, 0x05 => PrinterState::LengthHigh, 0x06 => PrinterState::Data, 0x07 => PrinterState::ChecksumLow, 0x08 => PrinterState::ChecksumHigh, 0x09 => PrinterState::KeepAlive, 0x0a => PrinterState::Status, _ => PrinterState::Other, } } } impl Display for PrinterState { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.description()) } } #[derive(Clone, Copy, PartialEq, Eq)] enum PrinterCommand { Init = 0x01, Start = 0x02, Data = 0x04, Status = 0x0f, Other = 0xff, } impl PrinterCommand { pub fn description(&self) -> &'static str { match self { PrinterCommand::Init => "Init", PrinterCommand::Start => "Start", PrinterCommand::Data => "Data", PrinterCommand::Status => "Status", PrinterCommand::Other => "Other", } } fn from_u8(value: u8) -> Self { match value { 0x01 => PrinterCommand::Init, 0x02 => PrinterCommand::Start, 0x04 => PrinterCommand::Data, 0x0f => PrinterCommand::Status, _ => PrinterCommand::Other, } } } impl Display for PrinterCommand { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.description()) } } pub struct PrinterDevice { state: PrinterState, command: PrinterCommand, compression: bool, command_length: u16, length_left: u16, checksum: u16, status: u8, byte_out: u8, data: [u8; 0x280], } impl PrinterDevice { pub fn new() -> Self { Self { state: PrinterState::MagicBytes1, command: PrinterCommand::Other, compression: false, command_length: 0, length_left: 0, checksum: 0x0, status: 0x0, byte_out: 0x0, data: [0x00; 0x280], } } pub fn reset(&mut self) { self.state = PrinterState::MagicBytes1; self.command = PrinterCommand::Other; self.compression = false; self.command_length = 0; self.length_left = 0; self.checksum = 0x0; self.status = 0x0; self.byte_out = 0x0; self.data = [0x00; 0x280] } fn run_command(&mut self, command: PrinterCommand) { match command { PrinterCommand::Init => { self.status = 0x00; self.byte_out = self.status; } PrinterCommand::Start => { self.byte_out = self.status; self.status = 0x06; } PrinterCommand::Data => { if self.command_length == 0x280 { println!("Printer: Going to copy the image for printing"); } // in case the command is of size 0 we assume this is // an EOF and we ignore this data operation else if self.command_length == 0x0 { } else { warnln!( "Printer: Wrong size for data: {:04x} bytes", self.command_length ); } self.status = 0x08; self.byte_out = self.status; } PrinterCommand::Status => { self.byte_out = self.status; // in case the current status is printing let's // mark it as done if self.status == 0x06 { // @TODO: check if this value should be 0x04 instead // this seems to be a bug with the print demo self.status = 0x00; } } PrinterCommand::Other => { warnln!("Printer: Invalid command: {:02x}", self.state as u8); } } } } impl SerialDevice for PrinterDevice { fn send(&mut self) -> u8 { self.byte_out } fn receive(&mut self, byte: u8) { self.byte_out = 0x00; match self.state { PrinterState::MagicBytes1 => { if byte != 0x88 { warnln!("Printer: Invalid magic byte 1: {:02x}", byte); return; } self.command = PrinterCommand::Other; self.command_length = 0; } PrinterState::MagicBytes2 => { if byte != 0x33 { if byte != 0x88 { self.state = PrinterState::MagicBytes1; } warnln!("Printer: Invalid magic byte 2: {:02x}", byte); return; } } PrinterState::Identification => self.command = PrinterCommand::from_u8(byte), PrinterState::Compression => { self.compression = byte & 0x01 == 0x01; if self.compression { warnln!("Printer: Using compressed data, currently unsupported"); } } PrinterState::LengthLow => self.length_left = byte as u16, PrinterState::LengthHigh => self.length_left |= (byte as u16) << 8, PrinterState::Data => { self.data[self.command_length as usize] = byte; self.command_length += 1; self.length_left -= 1; } PrinterState::ChecksumLow => self.checksum = byte as u16, PrinterState::ChecksumHigh => { self.checksum |= (byte as u16) << 8; self.byte_out = 0x81; } PrinterState::KeepAlive => { self.run_command(self.command); } PrinterState::Status => { self.state = PrinterState::MagicBytes1; return; } PrinterState::Other => { warnln!("Printer: Invalid state: {:02x}", self.state as u8); return; } } if self.state != PrinterState::Data { self.state = PrinterState::from_u8(self.state as u8 + 1); } if self.state == PrinterState::Data && self.length_left == 0 { self.state = PrinterState::from_u8(self.state as u8 + 1); } } fn allow_slave(&self) -> bool { false } } impl Default for PrinterDevice { fn default() -> Self { Self::new() } }