use std::cell::RefCell;
use std::iter;
use std::rc::Rc;
use log::warn;
use thiserror::Error;
use crate::core::{Address, ChunkCount, Data, Message, Offset, Operation, Page, PageFlipStyle, PageId, SignBus, SignType, State};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum SignError {
#[error("Sign bus failed to process message")]
Bus {
#[from]
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error(
"Sign did not respond properly according to the protocol: Expected {}, got {}",
expected,
actual
)]
UnexpectedResponse {
expected: String,
actual: String,
},
}
#[derive(Debug)]
pub struct Sign {
address: Address,
sign_type: SignType,
bus: Rc<RefCell<dyn SignBus>>,
}
impl Sign {
pub fn new(bus: Rc<RefCell<dyn SignBus>>, address: Address, sign_type: SignType) -> Self {
Sign { address, sign_type, bus }
}
pub fn address(&self) -> Address {
self.address
}
pub fn sign_type(&self) -> SignType {
self.sign_type
}
pub fn width(&self) -> u32 {
self.sign_type.dimensions().0
}
pub fn height(&self) -> u32 {
self.sign_type.dimensions().1
}
pub fn create_page<'a>(&self, id: PageId) -> Page<'a> {
let (x, y) = self.sign_type.dimensions();
Page::new(id, x, y)
}
pub fn configure(&self) -> Result<(), SignError> {
self.ensure_unconfigured()?;
let config = self.sign_type.to_bytes();
self.send_data(
&iter::once(config),
Operation::ReceiveConfig,
State::ConfigReceived,
State::ConfigFailed,
)
}
pub fn configure_if_needed(&self) -> Result<(), SignError> {
let response = self.send_message(Message::Hello(self.address))?;
match response {
Some(Message::ReportState(address, State::ConfigReceived))
| Some(Message::ReportState(address, State::ShowingPages))
| Some(Message::ReportState(address, State::PageLoaded))
| Some(Message::ReportState(address, State::PageShowInProgress))
| Some(Message::ReportState(address, State::PageShown))
| Some(Message::ReportState(address, State::PageLoadInProgress))
if address == self.address => {}
_ => self.configure()?,
}
Ok(())
}
pub fn send_pages<'a, I>(&self, pages: I) -> Result<PageFlipStyle, SignError>
where
I: IntoIterator<Item = &'a Page<'a>>,
<I as IntoIterator>::IntoIter: Clone,
{
let data = pages.into_iter().map(Page::as_bytes);
self.send_data(&data, Operation::ReceivePixels, State::PixelsReceived, State::PixelsFailed)?;
self.send_message_expect_response(Message::PixelsComplete(self.address), &None)?;
let response = self.send_message(Message::QueryState(self.address))?;
match response {
Some(Message::ReportState(address, state)) if address == self.address && state == State::ShowingPages => {
Ok(PageFlipStyle::Automatic)
}
_ => Ok(PageFlipStyle::Manual),
}
}
pub fn load_next_page(&self) -> Result<(), SignError> {
self.switch_page(State::PageLoaded, State::PageShown, Operation::LoadNextPage)
}
pub fn show_loaded_page(&self) -> Result<(), SignError> {
self.switch_page(State::PageShown, State::PageLoaded, Operation::ShowLoadedPage)
}
pub fn shut_down(&self) -> Result<(), SignError> {
self.send_message_expect_response(Message::Goodbye(self.address), &None)
}
fn send_message(&self, message: Message<'_>) -> Result<Option<Message<'_>>, SignError> {
let mut bus = self.bus.borrow_mut();
Ok(bus.process_message(message)?)
}
fn send_message_expect_response(
&self,
message: Message<'_>,
expected_response: &Option<Message<'_>>,
) -> Result<(), SignError> {
let response = self.send_message(message)?;
verify_response(expected_response, &response)
}
fn ensure_unconfigured(&self) -> Result<(), SignError> {
let response = self.send_message(Message::Hello(self.address))?;
match response {
Some(Message::ReportState(address, State::Unconfigured)) if address == self.address => {}
Some(Message::ReportState(address, State::ReadyToReset)) if address == self.address => {
self.send_message_expect_response(
Message::RequestOperation(self.address, Operation::FinishReset),
&Some(Message::AckOperation(self.address, Operation::FinishReset)),
)?;
self.send_message_expect_response(
Message::Hello(self.address),
&Some(Message::ReportState(self.address, State::Unconfigured)),
)?;
}
_ => {
self.send_message_expect_response(
Message::RequestOperation(self.address, Operation::StartReset),
&Some(Message::AckOperation(self.address, Operation::StartReset)),
)?;
self.send_message_expect_response(
Message::Hello(self.address),
&Some(Message::ReportState(self.address, State::ReadyToReset)),
)?;
self.send_message_expect_response(
Message::RequestOperation(self.address, Operation::FinishReset),
&Some(Message::AckOperation(self.address, Operation::FinishReset)),
)?;
self.send_message_expect_response(
Message::Hello(self.address),
&Some(Message::ReportState(self.address, State::Unconfigured)),
)?;
}
};
Ok(())
}
fn send_data<'a, I>(&self, data: &I, operation: Operation, success: State, failure: State) -> Result<(), SignError>
where
I: Iterator<Item = &'a [u8]> + Clone,
{
const MAX_ATTEMPTS: u32 = 3;
let mut attempts = 1;
loop {
self.send_message_expect_response(
Message::RequestOperation(self.address, operation),
&Some(Message::AckOperation(self.address, operation)),
)?;
let mut chunks_sent = 0;
for item in data.clone() {
for (i, chunk) in item.chunks(16).enumerate() {
self.send_message_expect_response(
Message::SendData(Offset((i * 16) as u16), Data::try_new(chunk).unwrap()),
&None,
)?;
chunks_sent += 1;
}
}
self.send_message_expect_response(Message::DataChunksSent(ChunkCount(chunks_sent)), &None)?;
let response = self.send_message(Message::QueryState(self.address))?;
if response == Some(Message::ReportState(self.address, failure)) && attempts < MAX_ATTEMPTS {
attempts += 1;
} else {
verify_response(&Some(Message::ReportState(self.address, success)), &response)?;
break;
}
}
Ok(())
}
fn switch_page(&self, target: State, trigger: State, operation: Operation) -> Result<(), SignError> {
loop {
let response = self.send_message(Message::QueryState(self.address))?;
match response {
Some(Message::ReportState(address, state)) if address == self.address && state == State::ShowingPages => {
warn!("Sign flips its own pages automatically; show_loaded_page/load_next_page have no effect.");
break;
}
Some(Message::ReportState(address, state)) if address == self.address && state == target => {
break;
}
Some(Message::ReportState(address, state)) if address == self.address && state == trigger => {
self.send_message_expect_response(
Message::RequestOperation(self.address, operation),
&Some(Message::AckOperation(self.address, operation)),
)?;
}
Some(Message::ReportState(address, State::PageLoadInProgress))
| Some(Message::ReportState(address, State::PageShowInProgress))
if address == self.address => {}
_ => {
return Err(SignError::UnexpectedResponse {
expected: format!("Some(ReportState({:?}, Page*))", self.address),
actual: format!("{:?}", response),
});
}
};
}
Ok(())
}
}
fn verify_response(expected: &Option<Message<'_>>, response: &Option<Message<'_>>) -> Result<(), SignError> {
if response == expected {
Ok(())
} else {
Err(SignError::UnexpectedResponse {
expected: format!("{:?}", expected),
actual: format!("{:?}", response),
})
}
}