feat: add periodic image saving

This commit is contained in:
Noa Aarts 2024-10-11 16:05:16 +02:00
parent 6da19152ca
commit 0502f3dacb
Signed by: noa
GPG key ID: 1850932741EFF672
6 changed files with 1085 additions and 6 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/target
.direnv/
recordings/

1030
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,8 @@ edition = "2021"
async-trait = "0.1.83"
atoi_radix10 = "0.0.1"
bytes = "1.6.0"
chrono = "0.4.38"
image = "0.25.2"
tokio = { version = "1.38", features = ["full"] }
tokio-test = "*"

View file

@ -111,8 +111,8 @@ To set a pixel using RGB, use ({SET_PX_RGB_BIN:02X}) (u8 canvas) (x as u16_le) (
writer.write_all(help_text.as_bytes()).await
}
Response::Size(x, y) => {
writer.write_u16_le(x).await?;
writer.write_u16_le(y).await
writer.write_u16(x).await?;
writer.write_u16(y).await
}
Response::GetPixel(_, _, c) => {
writer.write_u8(c[0]).await?;

View file

@ -1,5 +1,7 @@
use std::cell::SyncUnsafeCell;
use image::{DynamicImage, GenericImage, GenericImageView, ImageBuffer, Pixel, Rgb, Rgba};
use crate::Coordinate;
pub trait Grid<I, V> {
@ -63,6 +65,21 @@ impl<T> Grid<Coordinate, T> for Flut<T> {
}
}
impl GenericImageView for Flut<u32> {
type Pixel = Rgb<u8>;
fn dimensions(&self) -> (u32, u32) {
let (x, y) = self.get_size();
(x as u32, y as u32)
}
fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
let pixel = self.get_unchecked(x as u16, y as u16);
let [r, g, b, _a] = pixel.to_be_bytes();
Rgb::from([r, g, b])
}
}
#[cfg(test)]
#[allow(clippy::needless_return)]
mod tests {

View file

@ -7,17 +7,24 @@ mod grid;
mod text_protocol;
use std::{
alloc::System,
fmt::Debug,
fs::{create_dir_all, File},
io::{self, Error, ErrorKind},
path::Path,
sync::{atomic::AtomicU64, Arc},
time::Duration,
time::{Duration, SystemTime},
};
use binary_protocol::BinaryParser;
use chrono::Local;
use grid::{Flut, Grid};
use image::{codecs::jpeg::JpegEncoder, save_buffer, DynamicImage, GenericImageView, SubImage};
use text_protocol::TextParser;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt, BufReader, BufWriter},
net::TcpListener,
time::{interval, Instant},
};
extern crate test;
@ -261,6 +268,30 @@ where
}
}
async fn save_image_frames(grids: Arc<[grid::Flut<u32>]>) -> io::Result<()> {
let base_dir = Path::new("./recordings");
let mut timer = interval(Duration::from_secs(5));
create_dir_all(base_dir)?;
loop {
timer.tick().await;
for grid in grids.as_ref() {
let p = base_dir.join(format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")));
println!("timer ticked, grid writing to {:?}", p);
let mut file_writer = File::create(p)?;
let encoder = JpegEncoder::new_with_quality(&mut file_writer, 50);
grid.view(0, 0, grid.width(), grid.height()).to_image();
let sub_image = SubImage::new(grid, 0, 0, grid.width(), grid.height());
let image = sub_image.to_image();
match image.write_with_encoder(encoder) {
Ok(_) => {}
Err(err) => eprintln!("{}", err),
}
}
}
}
async fn handle_flut(flut_listener: TcpListener, grids: Arc<[grid::Flut<u32>]>) -> io::Result<()> {
let mut handles = Vec::new();
loop {
@ -293,8 +324,10 @@ async fn main() {
let handles = vec![
// log the amount of changed pixels each second
(tokio::spawn(listen_handle())),
// save frames every 5 seconds
(tokio::spawn(save_image_frames(grids.clone()))),
// accept and handle flut connections
(tokio::spawn(handle_flut(flut_listener, grids))),
(tokio::spawn(handle_flut(flut_listener, grids.clone()))),
];
for handle in handles {