Initial implementation of 'next arrivals'. Needs more work.

This commit is contained in:
Nahuel Lofeudo 2026-04-27 21:00:45 +01:00
parent c83ac39bac
commit 431f21a8b8
5 changed files with 81 additions and 20 deletions

View File

@ -8,6 +8,7 @@ host = "x86_64-unknown-linux-gnu"
sdl3 = {version = "0.17", features = ["ttf"]} sdl3 = {version = "0.17", features = ["ttf"]}
serde = "1.0" serde = "1.0"
gtfs-structures = "0.47" gtfs-structures = "0.47"
chrono = "*"
zip = "8.3" zip = "8.3"
csv = "1.4" csv = "1.4"

View File

@ -1,11 +1,12 @@
mod loader; mod loader;
mod utils; mod utils;
pub mod structs; pub mod structs;
use chrono::{DateTime, Local, NaiveDate, NaiveDateTime};
use std::{ use std::{
collections::{HashMap}, collections::{HashMap, HashSet},
fs::File, fs::File,
}; };
use gtfs_structures::{Agency, Calendar, CalendarDate, RawStopTime, RawTrip, Route, Stop, TimepointType}; use gtfs_structures::{Agency, Calendar, CalendarDate, Exception, RawStopTime, RawTrip, Route, Stop, TimepointType, Trip};
use crate::gtfs::{loader::load_gtfs, structs::{Arrival, Preferences}}; use crate::gtfs::{loader::load_gtfs, structs::{Arrival, Preferences}};
@ -31,11 +32,64 @@ pub struct Gtfs {
impl Gtfs { impl Gtfs {
pub fn _get_next_stops(&self) -> Vec<Arrival<'_>> { pub fn get_next_arrivals_for(&self, target_datetime: &DateTime<Local>) -> Box<Vec<Arrival>> {
let arrivals = Vec::<Arrival>::new(); let naive_target = target_datetime.naive_local();
let target_date = naive_target.date();
return arrivals; // Find which calendars apply
let mut active_service_ids: HashSet<String> = HashSet::new();
for (id, calendar) in self.calendar.iter() {
if calendar.valid_weekday(target_date)
&& calendar.start_date <= target_date
&& calendar.end_date > target_date {
active_service_ids.insert(id.to_string());
} }
}
// Are there any exceptions for the calendars above?
for (_calendar_id, exceptions) in self.calendar_dates.iter() {
for exception in exceptions.iter() {
if exception.date.eq(&target_date) {
match exception.exception_type {
Exception::Added => {
active_service_ids.insert(exception.service_id.clone());
}
Exception::Deleted => {
active_service_ids.remove(&exception.service_id);
}
}
}
}
}
// Find the trips happening on these calendars
let mut trips: HashMap<&String, &RawTrip> = HashMap::new();
for (id, trip) in self.trips.iter() {
if active_service_ids.contains(&trip.service_id) {
trips.insert(&trip.id, trip);
}
}
// Finally, find the arrivals for the active trips on the chosen stops
let mut arrivals: Vec<Arrival> = Vec::new();
let current_timestamp = target_datetime.timestamp();
for (id, stop_time) in self.stop_times.iter() {
if trips.contains_key(&stop_time.trip_id) {
let stop_timestamp = stop_time.departure_time.or(stop_time.arrival_time).unwrap();
if current_timestamp < stop_timestamp.into() {
let arrival: Arrival = Arrival {
route: self.routes.get(&self.trips.get(&stop_time.trip_id).unwrap().route_id).unwrap(),
stop: self.stops.get(&stop_time.stop_id).unwrap(),
departure_time: stop_timestamp
};
arrivals.push(arrival);
}
}
}
return Box::from(arrivals);
}
/// Load a GTFS structure from a zip file /// Load a GTFS structure from a zip file
pub fn load(src_file: &str, prefs: &Preferences) -> Gtfs { pub fn load(src_file: &str, prefs: &Preferences) -> Gtfs {
@ -57,5 +111,4 @@ impl Gtfs {
return gtfs; return gtfs;
} }
} }

View File

@ -8,9 +8,10 @@ pub struct Preferences {
pub stop_codes: HashSet<String>, pub stop_codes: HashSet<String>,
} }
#[derive(Debug)]
pub struct Arrival<'a> { pub struct Arrival<'a> {
pub route: &'a Route, pub route: &'a Route,
pub stop: &'a Stop, pub stop: &'a Stop,
pub timepoint: &'a TimepointType, pub departure_time: u32,
} }

View File

@ -1,14 +1,18 @@
mod gtfs; mod gtfs;
mod renderer; mod renderer;
use std::{collections::HashSet, os, process, thread, time::Duration}; use std::{collections::HashSet, process, thread, time::SystemTime};
use sdl3::{EventPump, event::{self, Event, EventWatchCallback}, sys::init::SDL_Quit}; use chrono::{DateTime, Local};
use sdl3::event::Event;
use crate::{gtfs::Gtfs, renderer::Screen}; use crate::{gtfs::Gtfs, renderer::Screen};
const SRC_FILE: &str = "/home/nahuel/Downloads/GTFS_Realtime.zip"; const SRC_FILE: &str = "/home/nahuel/Downloads/GTFS_Realtime.zip";
fn refresh_schedule(gtfs: &Gtfs) {
fn refresh_thread() { let current_timestamp = SystemTime::now();
let datetime: DateTime<Local> = current_timestamp.clone().into();
let next_arrivals = gtfs.get_next_arrivals_for(&datetime);
print!("Next arrivals: {:#?}", next_arrivals);
} }
@ -27,15 +31,17 @@ fn main() {
// Init GTFS static info // Init GTFS static info
println!("Loading GTFS data..."); println!("Loading GTFS data...");
//let _gtfs = Gtfs::load(SRC_FILE, &gtfs_prefs); let gtfs = Gtfs::load(SRC_FILE, &gtfs_prefs);
// Init screen // Init screen
println!("Initializing screen..."); println!("Initializing screen...");
let mut screen = Screen::init(&screen_prefs); let screen = Screen::init(&screen_prefs);
println!("Startup done."); println!("Startup done.");
// Start an asynchronous refresh thread // Start an asynchronous refresh thread
let _update_thread = thread::Builder::new().name("updater".to_string()).spawn(refresh_thread); //let _update_thread = thread::Builder::new().name("updater".to_string()).spawn(move || { refresh_schedule(&gtfs) });
refresh_schedule(&gtfs);
// Main event loop // Main event loop
let mut event_pump = screen.get_context().event_pump().unwrap(); let mut event_pump = screen.get_context().event_pump().unwrap();