From 5763f5f7050ca1a9117aa1541b1c1881ead2a1ee Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Mon, 19 Dec 2022 17:37:58 -0500 Subject: [PATCH] gotransit support --- .gitignore | 1 + .vscode/tasks.json | 20 +++++++++-- Cargo.toml | 3 +- src/cli.rs | 4 +++ src/main.rs | 38 ++++++++++---------- src/sources/gotransit.rs | 75 ++++++++++++++++++++++++++++++++++++++++ src/sources/mod.rs | 2 ++ src/sources/viarail.rs | 2 ++ 8 files changed, 124 insertions(+), 21 deletions(-) create mode 100644 src/sources/gotransit.rs diff --git a/.gitignore b/.gitignore index 1720240..d49a15f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ Cargo.lock /target #Cargo.lock +/test.xml diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c11f50a..8ba39ef 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -20,9 +20,25 @@ "args": [ "--", "-c", - "VA3UJF-1", + "TRAINS", "-p", - "23728" + "10410" + ] + }, + { + "type": "cargo", + "command": "run", + "problemMatcher": [ + "$rustc" + ], + "label": "rust: cargo run (dry run)", + "args": [ + "--", + "-c", + "N0CALL", + "-p", + "12345", + "--dry-run" ] } ] diff --git a/Cargo.toml b/Cargo.toml index 1bd32cb..845a3ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ reqwest = { version = "^0.11.13", features = ["json"] } serde = { version = "^1.0.126", features = ["derive"] } tokio = { version = "^1.23.0", features = ["macros", "rt-multi-thread"] } clap = { version = "^4.0.29", features = ["derive"] } -# chrono = "^0.4.23" aprs-encode = "^0.1.2" arrayvec = "^0.7" +serde-xml-rs = "^0.6.0" +regex = "^1.7.0" \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 8e6b399..62e1f64 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -11,4 +11,8 @@ pub struct Args { /// The password to use for the callsign #[clap(short, long)] pub password: String, + + /// Do a dry run (no publishing) + #[clap(short, long)] + pub dry_run: bool, } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ed154ab..ce15cc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,26 +32,28 @@ pub async fn main() { println!("{}", packet); // Push the packet to the server - let response = client - .post("http://rotate.aprs.net:8080/") - .header("Accept-Type", "text/plain") - .header("Content-Type", "application/octet-stream") - .body(format!( - "user {} pass {} vers aprs-trains 0.1.0\n{}", - args.callsign, args.password, packet - )) - .timeout(Duration::from_secs(3)) - .send() - .await; + if !args.dry_run { + let response = client + .post("http://rotate.aprs.net:8080/") + .header("Accept-Type", "text/plain") + .header("Content-Type", "application/octet-stream") + .body(format!( + "user {} pass {} vers aprs-trains 0.1.0\n{}", + args.callsign, args.password, packet + )) + .timeout(Duration::from_secs(3)) + .send() + .await; - if let Ok(response) = response { - println!("<- {}", response.status()); - } else { - let err = response.unwrap_err(); - if err.is_timeout() { - println!("<- TIMED OUT"); + if let Ok(response) = response { + println!("<- {}", response.status()); } else { - println!("<- ERR: {:?}", err); + let err = response.unwrap_err(); + if err.is_timeout() { + println!("<- TIMED OUT"); + } else { + println!("<- ERR: {:?}", err); + } } } } diff --git a/src/sources/gotransit.rs b/src/sources/gotransit.rs new file mode 100644 index 0000000..1b3a7df --- /dev/null +++ b/src/sources/gotransit.rs @@ -0,0 +1,75 @@ +//! Data source: http://gotracker.ca/gotracker/web/ + +use regex::Regex; + +use crate::train::TrainInfo; + +const CORRIDOR_IDS: [u8; 7] = [ + 65, // Barrie + 31, // Kitchener + 09, // Lakeshore East + 01, // Lakeshore West + 21, // Milton + 61, // Richmond Hill + 71, // Stouffville +]; + +#[derive(Debug, serde::Deserialize)] +struct InServiceTripPublic { + #[serde(rename = "EquipmentCode")] + equipment_code: String, + #[serde(rename = "Latitude")] + latitude: f32, + #[serde(rename = "Longitude")] + longitude: f32, + #[serde(rename = "StartStation")] + start_station: String, + #[serde(rename = "EndStation")] + end_station: String, + #[serde(rename = "TripNumber")] + trip_number: String, + #[serde(rename = "TripName")] + trip_name: String, + #[serde(rename = "CorridorCode")] + corridor_code: String, +} + +/// Get all trains from GO Transit +pub async fn get_trains() -> Result, reqwest::Error> { + // Allocate output + let mut output = Vec::new(); + + // Handle each corridor + for corridor_id in CORRIDOR_IDS.iter() { + // Make a request to the gotracker API + println!("Requesting data for GO transit corridor {}", corridor_id); + let response = reqwest::get(&format!("http://gotracker.ca/GOTracker/web/GODataAPIProxy.svc/TripLocation/Service/Lang/{:02}/en", corridor_id)).await?; + let response_text = response.text().await?; + + // Get only the data portion + if let Some(data_xml) = Regex::new(r"(.*)") + .unwrap() + .captures(&response_text) + { + let data_xml = data_xml.get(1).unwrap().as_str(); + + // Convert to something workable + let parsed_data: Vec = serde_xml_rs::from_str(data_xml).unwrap(); + + // Convert to TrainInfo format + for train in parsed_data { + output.push(TrainInfo { + identifier: format!("GO-{}{}", train.corridor_code, train.equipment_code), + speed: 0.0, + latitude: train.latitude, + longitude: train.longitude, + course: None, + status: format!("{} to {}", train.start_station, train.end_station), + }); + } + } + } + + // Return output + Ok(output) +} diff --git a/src/sources/mod.rs b/src/sources/mod.rs index 9b85809..ca1668f 100644 --- a/src/sources/mod.rs +++ b/src/sources/mod.rs @@ -1,6 +1,7 @@ use crate::train::TrainInfo; mod viarail; +mod gotransit; pub async fn get_trains() -> Result, reqwest::Error> { // Build output @@ -8,6 +9,7 @@ pub async fn get_trains() -> Result, reqwest::Error> { // Collect data output.extend(viarail::get_trains().await?); + output.extend(gotransit::get_trains().await?); // Return output Ok(output) diff --git a/src/sources/viarail.rs b/src/sources/viarail.rs index 3fc0307..8444235 100644 --- a/src/sources/viarail.rs +++ b/src/sources/viarail.rs @@ -1,3 +1,5 @@ +//! Data source: https://tsimobile.viarail.ca + use std::collections::HashMap; use crate::train::TrainInfo;