From 8496cfd72f14c7d3d4b93cf93e4b4214032a5e3a Mon Sep 17 00:00:00 2001 From: Nahuel Lofeudo Date: Sat, 13 Jun 2026 08:06:12 +0100 Subject: [PATCH] Second set of commits for realtime support --- Cargo.toml | 2 ++ src/gtfs/realtime.rs | 55 ++++++++++++++++++++++++++++++++++---------- src/gtfs/structs.rs | 7 ++++++ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fcc1a40..8e98337 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,12 @@ host = "x86_64-unknown-linux-gnu" chrono = "0.4" csv = "1.4" gtfs-structures = "0.47" +gtfs-realtime = "0.2" log = "0.4" reqwest = { version = "0.11", features = ["blocking"] } sdl3 = {version = "0.17", features = ["ttf"]} serde = { version = "1.0", features = ["derive"]} +serde_json = "1.0" time-format = "1.2" yaml_serde = "0.10" zip = "8.3" diff --git a/src/gtfs/realtime.rs b/src/gtfs/realtime.rs index 7a24992..99f64c3 100644 --- a/src/gtfs/realtime.rs +++ b/src/gtfs/realtime.rs @@ -1,15 +1,14 @@ /*** * Implementation of GTFS-R polling */ - -use std::{str::FromStr, time::Duration}; - use reqwest::{blocking::Client, header::{HeaderName, HeaderValue}}; +use std::{collections::HashMap, str::FromStr, time::Duration}; +use serde_json::{Value, de::Read}; -use crate::gtfs::structs::{Arrival, Error, Gtfs}; +use crate::gtfs::{self, structs::{Arrival, Error, Gtfs}}; impl Gtfs<'_> { - fn realtime_update (&self, arrivals: Vec>) -> Result{ + fn realtime_update (&self, arrivals: Vec>) -> Result<(), gtfs::structs::Error>{ // Poll GTFS-R API let client = Client::builder() @@ -17,20 +16,52 @@ impl Gtfs<'_> { .connect_timeout(Duration::from_secs(5)) .build()?; - let mut response = client - .get(self.preferences.realtime_url) - .header("x-api-key", self.preferences.realtime_api_key) + let response = client + .get(&self.preferences.realtime_url) + .header("x-api-key", &self.preferences.realtime_api_key) .send()?; // Parse response - let response = json::rdr + let realtime_info: gtfs_realtime::FeedMessage + = serde_json::from_reader(response)?; + + + // Build a map of (trip, stop) -> Arrival for faster lookup + let mut lookup: HashMap<(&String, &String), mut Arrival<'_>> = HashMap::new(); + for arrival in arrivals { + lookup.insert((&(arrival.trip.id), &(arrival.stop.id)), arrival); + } // Match deltas to existing arrivals + for entity in realtime_info.entity { + if entity.trip_update.is_some() && entity.stop.is_some() { + let trip_update = entity.trip_update.unwrap(); + + // Look up the entry in arrivals for the current + for stop_update in trip_update.stop_time_update { + if stop_update.stop_id.is_none() { + continue; + } + let trip_id = trip_update.trip.trip_id.as_ref().unwrap(); + let key = (trip_id, &(stop_update.stop_id.unwrap())); + if lookup.contains_key(&key) { + + // Update the arrival time + let arrival_value = lookup.get(&key); + let arrival: &&mut Arrival<'_> = arrival_value.unwrap(); + if let Some(update) = stop_update.departure.or(stop_update.arrival) { + let new_time = (((arrival.departure_time as i64) + + (update.delay.unwrap_or(0) as i64))) as u32; + arrival.departure_time = new_time; + } + } + } + } + } - // Update the arrival times - + return Ok(()); + } } -} \ No newline at end of file diff --git a/src/gtfs/structs.rs b/src/gtfs/structs.rs index 42187f5..62851c3 100644 --- a/src/gtfs/structs.rs +++ b/src/gtfs/structs.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use gtfs_structures::{Agency, Calendar, CalendarDate, RawStopTime, RawTrip, Route, Stop}; use log::error; use serde::{Serialize, Deserialize}; +use serde_json::value; use zip::result::ZipError; @@ -28,6 +29,12 @@ impl From for Error { } } +impl From for Error { + fn from(value: serde_json::Error) -> Self { + return Error { _message: value.to_string() } + } +} + // This is to store the preferences for the GTFS(-R) side of the code. #[derive(Debug, PartialEq, Serialize, Deserialize)]