Compare commits

..

No commits in common. "35ef64a829d01b67c5373f4d6528d76648afda38" and "07bc399bbc68755cc79b0d4eb43a0400e7e3fb7e" have entirely different histories.

5 changed files with 39 additions and 139 deletions

88
Cargo.lock generated
View file

@ -17,18 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@ -599,18 +587,6 @@ dependencies = [
"zune-inflate", "zune-inflate",
] ]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]] [[package]]
name = "fast-srgb8" name = "fast-srgb8"
version = "1.0.0" version = "1.0.0"
@ -746,18 +722,6 @@ name = "hashbrown"
version = "0.14.5" version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown",
]
[[package]] [[package]]
name = "heck" name = "heck"
@ -1033,17 +997,6 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "linux-embedded-hal" name = "linux-embedded-hal"
version = "0.4.0" version = "0.4.0"
@ -1463,7 +1416,6 @@ dependencies = [
"mime", "mime",
"minijinja", "minijinja",
"palette", "palette",
"rusqlite",
"serde", "serde",
"strum", "strum",
"thiserror", "thiserror",
@ -1757,20 +1709,6 @@ dependencies = [
"bytemuck", "bytemuck",
] ]
[[package]]
name = "rusqlite"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
dependencies = [
"bitflags 2.6.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -2293,12 +2231,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.2.0" version = "0.2.0"
@ -2510,26 +2442,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "zune-core" name = "zune-core"
version = "0.4.12" version = "0.4.12"

View file

@ -16,7 +16,6 @@ linux-embedded-hal = { version = "0.4.0"}
mime = "0.3.17" mime = "0.3.17"
minijinja = "2.1.0" minijinja = "2.1.0"
palette = "0.7.6" palette = "0.7.6"
rusqlite = { version = "0.32.1", features = ["bundled"] }
serde = { version = "1.0.204", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
strum = { version = "0.26.3", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] }
thiserror = "1.0.63" thiserror = "1.0.63"

View file

@ -1,4 +1,4 @@
use crate::display::{create_display_thread, EInkPanel}; use crate::display::EInkPanel;
use crate::dither::{DitherMethod, DitheredImage}; use crate::dither::{DitherMethod, DitheredImage};
use crate::eink::Palette; use crate::eink::Palette;
use axum::async_trait; use axum::async_trait;
@ -10,10 +10,10 @@ use image::{ImageReader, RgbImage};
use std::io::Cursor; use std::io::Cursor;
use std::str; use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::sync::mpsc;
use std::sync::Arc; use std::sync::Arc;
use std::thread::JoinHandle;
use std::time::Duration; use std::time::Duration;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::task::JoinHandle;
use tracing::{error, info, instrument}; use tracing::{error, info, instrument};
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
@ -24,17 +24,18 @@ pub enum ApiError {
#[derive(Clone)] #[derive(Clone)]
pub struct AppState { pub struct AppState {
display_channel: mpsc::Sender<Box<DitheredImage>>, display_channel: Sender<DisplaySetCommand>,
display_task: Arc<JoinHandle<()>>, display_task: Arc<JoinHandle<()>>,
} }
impl AppState { impl AppState {
#[must_use] #[must_use]
pub fn new(disp: Box<dyn EInkPanel + Send>) -> Self { pub fn new(disp: Box<dyn EInkPanel + Send>) -> Self {
let (handle, tx) = create_display_thread(disp); let (tx, rx) = mpsc::channel(2);
let task = tokio::spawn(display_task(rx, disp));
Self { Self {
display_channel: tx, display_channel: tx,
display_task: Arc::new(handle), display_task: Arc::new(task),
} }
} }
} }
@ -64,6 +65,25 @@ 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 /// API routes for axum
/// Start with the basics: Send an image, crop it, dither, and upload. /// Start with the basics: Send an image, crop it, dither, and upload.
/// we defer the upload to a separate task. /// we defer the upload to a separate task.
@ -143,7 +163,10 @@ async fn set_image(
let mut dither = img_req.dither_method.get_ditherer(); let mut dither = img_req.dither_method.get_ditherer();
dither.dither(&img_req.image, &mut buf); dither.dither(&img_req.image, &mut buf);
} }
ctx.display_channel.send(Box::new(buf))?; let cmd = DisplaySetCommand { img: Box::new(buf) };
ctx.display_channel
.send_timeout(cmd, Duration::from_secs(10))
.await?;
Ok(StatusCode::OK) Ok(StatusCode::OK)
} }

View file

@ -1,16 +1,14 @@
// Manages display hardware
use crate::dither::DitheredImage;
use anyhow::Result;
use epd_waveshare::{epd7in3f::Epd7in3f, prelude::WaveshareDisplay}; 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::SpiModeFlags;
use linux_embedded_hal::spidev::SpidevOptions; use linux_embedded_hal::spidev::SpidevOptions;
use linux_embedded_hal::{CdevPin, Delay, SpidevDevice}; use linux_embedded_hal::{CdevPin, Delay, SpidevDevice};
use std::sync::mpsc;
use std::thread;
use tracing::instrument; use tracing::instrument;
use tracing::span; use tracing::{debug, warn};
use tracing::{debug, error, info, warn, Level};
use anyhow::Result;
use linux_embedded_hal::gpio_cdev::{Chip, LineRequestFlags};
use crate::dither::DitheredImage;
pub trait EInkPanel { pub trait EInkPanel {
fn display(&mut self, buf: &DitheredImage) -> Result<()>; fn display(&mut self, buf: &DitheredImage) -> Result<()>;
@ -90,34 +88,3 @@ impl EInkPanel for FakeEInk {
Ok(()) 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)
}

View file

@ -1,6 +1,7 @@
pub mod api; pub mod api;
pub mod display; pub mod display;
pub mod dither; pub mod dither;
pub mod errors;
pub mod eink; pub mod eink;
use std::path::PathBuf; use std::path::PathBuf;
@ -17,11 +18,9 @@ use tracing::{error, info};
/// Application config, including sqlite db path, scan folders, and scheduling. /// Application config, including sqlite db path, scan folders, and scheduling.
#[derive(Deserialize, Debug)] #[derive(Deserialize)]
struct Config { struct Config {
database_path: PathBuf,
search_paths: Vec<PathBuf>,
} }
/// Display images on E-Ink Displays /// Display images on E-Ink Displays