129 lines
5.0 KiB
Rust
129 lines
5.0 KiB
Rust
mod arrival;
|
|
mod loader;
|
|
mod utils;
|
|
mod refresher;
|
|
mod realtime;
|
|
pub mod structs;
|
|
use chrono::{DateTime, Local, Timelike};
|
|
use log::{debug, trace, warn};
|
|
use std::{collections::{HashMap, HashSet}, fs::File };
|
|
use gtfs_structures::{Exception, RawTrip};
|
|
use crate::gtfs::{loader::load_gtfs, structs::{Arrival, Gtfs, Preferences, Error}};
|
|
|
|
|
|
impl Gtfs<'_> {
|
|
|
|
pub fn get_next_arrivals_for(&self, target_datetime: &DateTime<Local>) -> Option<Vec<Arrival<'_>>> {
|
|
let naive_target = target_datetime.naive_local();
|
|
let target_date = naive_target.date();
|
|
|
|
// Find which calendars apply
|
|
debug!("Looking for calendars that apply to date {:#?}", target_date);
|
|
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());
|
|
debug!("Matched calendar: {:#?}", calendar);
|
|
}
|
|
}
|
|
debug!("Found {} services active today", active_service_ids.len());
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
debug!("After exceptions, there are now {} services active: {:#?}", active_service_ids.len(), active_service_ids);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
debug!("Found {} trips", trips.len());
|
|
|
|
// Finally, find the arrivals for the active trips on the chosen stops
|
|
let mut arrivals: Vec<Arrival> = Vec::new();
|
|
|
|
// Stop times are parsed as the number of seconds since midnight on the current day
|
|
let current_timestamp = target_datetime.time().hour() * 3600
|
|
+ target_datetime.time().minute() * 60
|
|
+ target_datetime.time().second();
|
|
|
|
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)?;
|
|
let trip= &self.trips.get(&stop_time.trip_id).unwrap();
|
|
if current_timestamp < stop_timestamp.into() {
|
|
let arrival: Arrival = Arrival {
|
|
route: self.routes.get(&self.trips.get(&stop_time.trip_id)?.route_id)?,
|
|
stop: self.stops.get(&stop_time.stop_id)?,
|
|
stop_time: stop_time,
|
|
trip: &trip,
|
|
departure_time: stop_timestamp.into()
|
|
};
|
|
trace!("{:#?}: Arrival to {:#?} for trip ID {:#?}.",
|
|
format!("{:02}:{:02}", (arrival.departure_time/3600) as u32, ((arrival.departure_time / 60) % 60) as u32),
|
|
arrival.trip.trip_headsign.as_ref().unwrap(),
|
|
arrival.trip.id);
|
|
arrivals.push(arrival);
|
|
}
|
|
}
|
|
}
|
|
|
|
arrivals.sort();
|
|
|
|
debug!("Found {} arrivals", arrivals.len());
|
|
|
|
// Update real-time deltas
|
|
|
|
let realtime_result = self.realtime_update(&mut arrivals);
|
|
if realtime_result.is_err() {
|
|
warn!("Unable to update realtime arrivals: {:#?}", realtime_result.err().unwrap()._message)
|
|
}
|
|
|
|
return Some(arrivals);
|
|
}
|
|
|
|
|
|
/// Load a GTFS structure from a zip file
|
|
pub fn load(prefs: &Preferences) -> Result<Gtfs<'_>, Error> {
|
|
|
|
_ = refresher::refresh(prefs);
|
|
|
|
// Open zip file
|
|
let zip_file = File::open(prefs.gtfs_file_path()?)?;
|
|
let mut zip_reader = zip::ZipArchive::new(zip_file)?;
|
|
|
|
let mut gtfs: Gtfs = Gtfs {
|
|
preferences: prefs,
|
|
agencies: Vec::new(),
|
|
calendar: HashMap::new(),
|
|
calendar_dates: HashMap::new(),
|
|
routes: HashMap::new(),
|
|
stops: HashMap::new(),
|
|
trips: HashMap::new(),
|
|
stop_times: HashMap::new(),
|
|
};
|
|
|
|
|
|
load_gtfs(&mut gtfs, &mut zip_reader, &prefs.route_numbers, &prefs.stop_codes);
|
|
|
|
return Ok(gtfs);
|
|
}
|
|
}
|