use std::{cell::RefCell, iter, rc::Rc};
use embedded_graphics_core::{
Pixel,
draw_target::DrawTarget,
geometry::{OriginDimensions, Size},
pixelcolor::BinaryColor,
};
use flipdot::{Address, Page, PageFlipStyle, PageId, SerialSignBus, Sign, SignBus, SignError, SignType};
use flipdot_testing::{VirtualSign, VirtualSignBus};
#[derive(Debug)]
pub struct FlipdotDisplay {
page: Page<'static>,
sign: Sign,
}
#[derive(Debug)]
pub enum SignBusType<'a> {
Serial(&'a str),
Virtual,
}
impl<'a, T: AsRef<str>> From<&'a T> for SignBusType<'a> {
fn from(value: &'a T) -> Self {
let port = value.as_ref();
if port.eq_ignore_ascii_case("virtual") {
Self::Virtual
} else {
Self::Serial(port)
}
}
}
impl FlipdotDisplay {
pub fn try_new(bus_type: SignBusType<'_>, address: Address, sign_type: SignType) -> Result<Self, serial::Error> {
let bus: Rc<RefCell<dyn SignBus>> = match bus_type {
SignBusType::Virtual => {
let bus = VirtualSignBus::new(iter::once(VirtualSign::new(address, PageFlipStyle::Manual)));
Rc::new(RefCell::new(bus))
}
SignBusType::Serial(port) => {
let port = serial::open(port)?;
let bus = SerialSignBus::try_new(port)?;
Rc::new(RefCell::new(bus))
}
};
Ok(Self::new_with_bus(bus, address, sign_type))
}
pub fn new_with_bus(bus: Rc<RefCell<dyn SignBus>>, address: Address, sign_type: SignType) -> Self {
Sign::new(bus, address, sign_type).into()
}
pub fn flush(&self) -> Result<(), SignError> {
self.sign.configure_if_needed()?;
if self.sign.send_pages(iter::once(&self.page))? == PageFlipStyle::Manual {
self.sign.show_loaded_page()?;
}
Ok(())
}
}
impl From<Sign> for FlipdotDisplay {
fn from(sign: Sign) -> Self {
Self {
page: sign.create_page(PageId(0)),
sign,
}
}
}
impl DrawTarget for FlipdotDisplay {
type Color = BinaryColor;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(coord, color) in pixels.into_iter() {
if let Ok((x, y)) = coord.try_into() {
let size = self.size();
if x < size.width && y < size.height {
self.page.set_pixel(x, y, color.is_on());
}
}
}
Ok(())
}
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
self.page.set_all_pixels(color.is_on());
Ok(())
}
}
impl OriginDimensions for FlipdotDisplay {
fn size(&self) -> Size {
Size::new(self.sign.width(), self.sign.height())
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics::{
prelude::*,
primitives::{PrimitiveStyle, Triangle},
};
use std::error::Error;
#[test]
fn out_of_bounds_pixels() -> Result<(), Box<dyn Error>> {
let bus = VirtualSignBus::new(iter::once(VirtualSign::new(Address(3), PageFlipStyle::Manual)));
let bus = Rc::new(RefCell::new(bus));
let mut display = FlipdotDisplay::new_with_bus(bus.clone(), Address(3), SignType::Max3000Side90x7);
display.draw_iter([
Pixel(Point::new(-1, 0), BinaryColor::On),
Pixel(Point::new(0, -1), BinaryColor::On),
Pixel(Point::new(90, 0), BinaryColor::On),
Pixel(Point::new(0, 7), BinaryColor::On),
])?;
display.flush()?;
let bus = bus.borrow();
let page = &bus.sign(0).pages()[0];
assert_eq!(*page, Page::new(page.id(), page.width(), page.height()));
Ok(())
}
#[test]
fn draw_and_flush() -> Result<(), Box<dyn Error>> {
let bus = VirtualSignBus::new(iter::once(VirtualSign::new(Address(3), PageFlipStyle::Manual)));
let bus = Rc::new(RefCell::new(bus));
let mut display = FlipdotDisplay::new_with_bus(bus.clone(), Address(3), SignType::Max3000Side90x7);
Triangle::new(Point::new(0, 0), Point::new(45, 6), Point::new(89, 0))
.into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
.draw(&mut display)?;
assert!(bus.borrow().sign(0).pages().is_empty());
display.flush()?;
assert!(!bus.borrow().sign(0).pages().is_empty());
let actual = format!("{}", bus.borrow().sign(0).pages()[0]);
let expected = "\
+------------------------------------------------------------------------------------------+\n\
|@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|\n\
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |\n\
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |\n\
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |\n\
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |\n\
| @@@@@@@@@@@@@@@@@@@@@@ |\n\
| @@@@@@@ |\n\
+------------------------------------------------------------------------------------------+";
assert_eq!(actual, expected);
Ok(())
}
}