make display thread dedicated, move to display.rs
display thread should be dedicated to not steal resources from tokio
This commit is contained in:
parent
07bc399bbc
commit
0e3d324748
37
src/api.rs
37
src/api.rs
|
@ -1,4 +1,4 @@
|
|||
use crate::display::EInkPanel;
|
||||
use crate::display::{create_display_thread, EInkPanel};
|
||||
use crate::dither::{DitherMethod, DitheredImage};
|
||||
use crate::eink::Palette;
|
||||
use axum::async_trait;
|
||||
|
@ -10,10 +10,10 @@ use image::{ImageReader, RgbImage};
|
|||
use std::io::Cursor;
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{error, info, instrument};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -24,18 +24,17 @@ pub enum ApiError {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
display_channel: Sender<DisplaySetCommand>,
|
||||
display_channel: mpsc::Sender<Box<DitheredImage>>,
|
||||
display_task: Arc<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
#[must_use]
|
||||
pub fn new(disp: Box<dyn EInkPanel + Send>) -> Self {
|
||||
let (tx, rx) = mpsc::channel(2);
|
||||
let task = tokio::spawn(display_task(rx, disp));
|
||||
let (handle, tx) = create_display_thread(disp);
|
||||
Self {
|
||||
display_channel: tx,
|
||||
display_task: Arc::new(task),
|
||||
display_task: Arc::new(handle),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,25 +64,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DisplaySetCommand {
|
||||
img: Box<DitheredImage>,
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn display_task(
|
||||
mut rx: Receiver<DisplaySetCommand>,
|
||||
mut display: Box<dyn EInkPanel + Send>,
|
||||
) {
|
||||
while let Some(cmd) = rx.recv().await {
|
||||
info!("Got a display set command");
|
||||
if let Err(e) = display.display(&cmd.img) {
|
||||
error!("Error displaying command {e}");
|
||||
}
|
||||
info!("Done setting display");
|
||||
}
|
||||
}
|
||||
|
||||
/// API routes for axum
|
||||
/// Start with the basics: Send an image, crop it, dither, and upload.
|
||||
/// we defer the upload to a separate task.
|
||||
|
@ -163,10 +143,7 @@ async fn set_image(
|
|||
let mut dither = img_req.dither_method.get_ditherer();
|
||||
dither.dither(&img_req.image, &mut buf);
|
||||
}
|
||||
let cmd = DisplaySetCommand { img: Box::new(buf) };
|
||||
ctx.display_channel
|
||||
.send_timeout(cmd, Duration::from_secs(10))
|
||||
.await?;
|
||||
ctx.display_channel.send(Box::new(buf))?;
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
// Manages display hardware
|
||||
use crate::dither::DitheredImage;
|
||||
use anyhow::Result;
|
||||
use epd_waveshare::{epd7in3f::Epd7in3f, prelude::WaveshareDisplay};
|
||||
use linux_embedded_hal::gpio_cdev::{Chip, LineRequestFlags};
|
||||
use linux_embedded_hal::spidev::SpiModeFlags;
|
||||
use linux_embedded_hal::spidev::SpidevOptions;
|
||||
use linux_embedded_hal::{CdevPin, Delay, SpidevDevice};
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use tracing::instrument;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use anyhow::Result;
|
||||
use linux_embedded_hal::gpio_cdev::{Chip, LineRequestFlags};
|
||||
|
||||
use crate::dither::DitheredImage;
|
||||
use tracing::span;
|
||||
use tracing::{debug, error, info, warn, Level};
|
||||
|
||||
pub trait EInkPanel {
|
||||
fn display(&mut self, buf: &DitheredImage) -> Result<()>;
|
||||
|
@ -88,3 +90,34 @@ impl EInkPanel for FakeEInk {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a thread that can take dithered images and display them. This allows the display to be
|
||||
/// used in an async context since updating the display can take ~30 seconds.
|
||||
#[must_use]
|
||||
pub fn create_display_thread(
|
||||
mut display: Box<dyn EInkPanel + Send>,
|
||||
) -> (thread::JoinHandle<()>, mpsc::Sender<Box<DitheredImage>>) {
|
||||
let (tx, rx) = mpsc::channel::<Box<DitheredImage>>();
|
||||
let handle = thread::spawn(move || {
|
||||
let span = span!(Level::INFO, "display_thread");
|
||||
let _enter = span.enter();
|
||||
loop {
|
||||
let res = rx.recv();
|
||||
match res {
|
||||
Ok(img) => {
|
||||
info!("Received an image to display");
|
||||
if let Err(e) = display.display(&img) {
|
||||
error!("Error displaying image: {e}");
|
||||
return;
|
||||
}
|
||||
info!("Successfully set display image");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error reading image from channel: {e}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
(handle, tx)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue