diff --git a/Cargo.toml b/Cargo.toml index f505392..77af606 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ host = "x86_64-unknown-linux-gnu" sdl3 = {version = "0.17", features = ["ttf"]} serde = "1.0" gtfs-structures = "0.47" +chrono = "*" zip = "8.3" csv = "1.4" diff --git a/src/gtfs/mod.rs b/src/gtfs/mod.rs index 40b8475..52d49e4 100644 --- a/src/gtfs/mod.rs +++ b/src/gtfs/mod.rs @@ -1,11 +1,12 @@ mod loader; mod utils; pub mod structs; +use chrono::{DateTime, Local, NaiveDate, NaiveDateTime}; use std::{ - collections::{HashMap}, + collections::{HashMap, HashSet}, 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}}; @@ -31,11 +32,64 @@ pub struct Gtfs { impl Gtfs { - pub fn _get_next_stops(&self) -> Vec> { - let arrivals = Vec::::new(); + pub fn get_next_arrivals_for(&self, target_datetime: &DateTime) -> Box> { + let naive_target = target_datetime.naive_local(); + let target_date = naive_target.date(); + + // Find which calendars apply + let mut active_service_ids: HashSet = 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()); + } + } - return arrivals; -} + // 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 = 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 pub fn load(src_file: &str, prefs: &Preferences) -> Gtfs { @@ -53,9 +107,8 @@ impl Gtfs { }; - load_gtfs(&mut gtfs, &mut zip_reader, &prefs.route_numbers, &prefs.stop_codes); + load_gtfs(&mut gtfs, &mut zip_reader, &prefs.route_numbers, &prefs.stop_codes); - return gtfs; + return gtfs; + } } - -} \ No newline at end of file diff --git a/src/gtfs/structs.rs b/src/gtfs/structs.rs index dec0987..c285dea 100644 --- a/src/gtfs/structs.rs +++ b/src/gtfs/structs.rs @@ -8,9 +8,10 @@ pub struct Preferences { pub stop_codes: HashSet, } +#[derive(Debug)] pub struct Arrival<'a> { pub route: &'a Route, pub stop: &'a Stop, - pub timepoint: &'a TimepointType, + pub departure_time: u32, } \ No newline at end of file diff --git a/src/gtfs/utils.rs b/src/gtfs/utils.rs index 602406b..3ee86df 100644 --- a/src/gtfs/utils.rs +++ b/src/gtfs/utils.rs @@ -23,4 +23,4 @@ pub fn route_ids_from_numbers(gtfs: &Gtfs, route_numbers: &HashSet) -> H } } return ids; -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 8c3d00c..12bed50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,18 @@ mod gtfs; mod renderer; -use std::{collections::HashSet, os, process, thread, time::Duration}; -use sdl3::{EventPump, event::{self, Event, EventWatchCallback}, sys::init::SDL_Quit}; +use std::{collections::HashSet, process, thread, time::SystemTime}; +use chrono::{DateTime, Local}; +use sdl3::event::Event; + use crate::{gtfs::Gtfs, renderer::Screen}; const SRC_FILE: &str = "/home/nahuel/Downloads/GTFS_Realtime.zip"; - -fn refresh_thread() { - +fn refresh_schedule(gtfs: &Gtfs) { + let current_timestamp = SystemTime::now(); + let datetime: DateTime = 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 println!("Loading GTFS data..."); - //let _gtfs = Gtfs::load(SRC_FILE, >fs_prefs); + let gtfs = Gtfs::load(SRC_FILE, >fs_prefs); // Init screen println!("Initializing screen..."); - let mut screen = Screen::init(&screen_prefs); + let screen = Screen::init(&screen_prefs); println!("Startup done."); // 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(>fs) }); + + refresh_schedule(>fs); // Main event loop let mut event_pump = screen.get_context().event_pump().unwrap();