From b3f251e4ccc79acb6cc52947690114dab64c9a3c Mon Sep 17 00:00:00 2001 From: Nahuel Lofeudo Date: Wed, 13 May 2026 20:41:15 +0100 Subject: [PATCH] Checkpoint --- Cross.toml | 8 +++++ src/main.rs | 23 ++++++++++--- src/renderer/mod.rs | 72 ++++++++++++++++++++++++++++++++++++++--- src/renderer/structs.rs | 12 +++++++ 4 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 Cross.toml diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..898c929 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,8 @@ +[build] +default-target = "aarch64-unknown-linux-gnu" # use this target if none is explicitly provided +pre-build = [ # additional commands to run prior to building the package + "dpkg --add-architecture $CROSS_DEB_ARCH", + "apt update", + "apt --assume-yes install apt-utils:$CROSS_DEB_ARCH", + "apt --assume-yes install libsdl3-dev:$CROSS_DEB_ARCH" +] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b88c350..8736a07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ mod gtfs; mod renderer; -use std::{collections::HashSet, ops::Add, process, thread::Builder, time::SystemTime}; +use std::{collections::{HashSet, btree_map::Entry}, ops::Add, process, thread::Builder, time::SystemTime}; use chrono::{DateTime, Duration, Local, NaiveTime}; use log::{Metadata, Record, debug, error, info}; use sdl3::event::Event; -use crate::{gtfs::structs::{Arrival, Gtfs}, renderer::structs::Screen}; +use crate::{gtfs::structs::{Arrival, Gtfs}, renderer::structs::{DisplayData, DisplayEntry, Screen}}; const SRC_FILE: &str = "/home/nahuel/Downloads/GTFS_Realtime.zip"; const NUM_ARRIVALS: usize = 4; @@ -15,7 +15,7 @@ struct RefreshDataEvent { } -fn refresh_schedule(gtfs: &Gtfs) -> Vec> { +fn refresh_schedule<'a>(gtfs: &'a Gtfs, screen: &'a Screen<'a>) -> Vec> { let current_timestamp = SystemTime::now(); let datetime: DateTime = current_timestamp.clone().into(); let mut next_arrivals: Vec> = gtfs.get_next_arrivals_for(&datetime); @@ -29,6 +29,21 @@ fn refresh_schedule(gtfs: &Gtfs) -> Vec> { } next_arrivals.sort(); + + // Create the DisplayData structure to render the information to screen + let current_time = Local::now().time(); + let display_data: DisplayData = DisplayData { + lines: Vec::>::from(next_arrivals.iter().map(|arrival| { + DisplayEntry { + destination: &String::from("Foo"), + route: &arrival.route.short_name.or(arrival.route.long_name).unwrap(), + due_in: (arrival.departure_time - current_time).num_minutes().try_into().unwrap() + } + }).collect()), + status: None + }; + + screen.update_information(&display_data); return next_arrivals; } @@ -112,7 +127,7 @@ fn main() { let refresh_data = event.as_user_event_type::(); if refresh_data.is_some() { debug!("Received user event: {:#?}", refresh_data.unwrap()); - let _data: Vec> = refresh_schedule(>fs); + let _data: Vec> = refresh_schedule(>fs, &screen); debug!("-------------------------------- Refresh done."); } } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index e175998..a28bec0 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,32 +1,94 @@ pub mod structs; +use std::cmp::min; use sdl3::{Sdl, pixels::Color, rect::Rect}; -use structs::{Prefs}; +use structs::{Prefs, DisplayData}; use crate::renderer::structs::Screen; +const LINE_COUNT: i32 = 6; +const COLOR_LCD_AMBER : Color = Color::RGB(0xf4, 0xcb, 0x60); +const COLOR_LCD_GREEN : Color = Color::RGB(0xb3, 0xff, 0x00); +const COLOR_LCD_RED : Color = Color::RGB(0xff, 0x3a, 0x4a); + +//const COLOR_BACKGROUND = pygame.Color(0, 0, 0) +const UPDATE_INTERVAL_SECONDS: u32 = 62; +const TEXT_SIZE: u32 = 160; // Size of the font in pixels + +// Offsets of each part within a line +const XOFFSET_ROUTE: u32 = 24; +const XOFFSET_DESTINATION: u32 = 300; +const XOFFSEET_TIME_LEFT: u32 = 1606; +const INTER_LINE_SPACE: i32 = -15; + + impl Screen<'_> { pub fn get_context(&self) -> &Sdl { return &self.context; } + fn color_for(&self, due_in: i32) -> Color { + if due_in > 15 { return COLOR_LCD_GREEN }; + if due_in > 10 { return COLOR_LCD_AMBER }; + return COLOR_LCD_RED; + } + + + pub fn update_information(&mut self, display_data: &DisplayData) { + self.do_clear(); + let num_arrivals: i32 = min(if display_data.status.is_some() {LINE_COUNT - 1} else {LINE_COUNT}, display_data.lines.len().try_into().unwrap()); + for line in 0..num_arrivals { + // Compose a line of text with all the information + let entry = display_data.lines.get(line as usize).unwrap(); + let line: u32 = line.try_into().unwrap(); + let due_in_mins = (entry.due_in / 60) as i32; + let arrival_color: Color = self.color_for(due_in_mins); + + self.do_print_at(line, entry.route, XOFFSET_ROUTE); + self.do_print_at(line, entry.destination, XOFFSET_DESTINATION); + self.do_print_at(line, &due_in_mins.to_string(), XOFFSEET_TIME_LEFT); + }; + + if display_data.status.is_none() { + self.do_print_at(5, display_data.status.as_ref().unwrap(), 0); + } + self.do_update(); + } + + pub fn print(&mut self, line: u32, text: &str) { + self.do_print_at(line, text, 0); + self.do_update(); + } + + + fn do_print_at(&mut self, line: u32, text: &str, left: u32) -> u32 { let rendered_text = self.font.render(text).solid(Color::RED).unwrap(); let texture_creator = self.canvas.texture_creator(); let texture = rendered_text.as_texture(&texture_creator).unwrap(); - let _ = self.canvas.copy(&texture, + self.canvas.copy(&texture, Rect::new(0, 0, rendered_text.width(), rendered_text.height()), - Rect::new(0, (line * rendered_text.height()).try_into().unwrap(), rendered_text.width(), rendered_text.height())); - self.canvas.present(); + Rect::new(left.try_into().unwrap(), (line * rendered_text.height()).try_into().unwrap(), rendered_text.width(), rendered_text.height())); + return left + rendered_text.width(); } pub fn clear(&mut self) { + self.do_clear(); + self.do_update(); + } + + + fn do_update (&mut self) { + self.canvas.present(); + } + + + fn do_clear(&mut self) { self.canvas.set_draw_color(Color::BLACK); self.canvas.clear(); - self.canvas.present(); } /// Initialize video, allocate buffers and load fonts diff --git a/src/renderer/structs.rs b/src/renderer/structs.rs index 50cbd62..e4be284 100644 --- a/src/renderer/structs.rs +++ b/src/renderer/structs.rs @@ -12,3 +12,15 @@ pub struct Prefs { pub screen_width: u32, pub screen_height: u32, } + + +pub struct DisplayEntry<'a> { + pub route: &'a String, + pub destination: &'a String, + pub due_in: i32, +} + +pub struct DisplayData<'a> { + pub lines: Vec>, + pub status: Option +}