heliwatch: switch from json to beast
This commit is contained in:
parent
afca212069
commit
873f0b639f
|
@ -147,12 +147,6 @@ dependencies = [
|
|||
"safemem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -278,15 +272,6 @@ dependencies = [
|
|||
"generic-array 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.3.3"
|
||||
|
@ -608,10 +593,11 @@ dependencies = [
|
|||
name = "heliwatch"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"adsb",
|
||||
"beast",
|
||||
"csv",
|
||||
"futures",
|
||||
"geo",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
|
@ -719,19 +705,6 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
|
@ -780,7 +753,7 @@ dependencies = [
|
|||
"socket2 0.3.19",
|
||||
"widestring",
|
||||
"winapi",
|
||||
"winreg 0.6.2",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -804,15 +777,6 @@ dependencies = [
|
|||
"minidom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
|
@ -1500,41 +1464,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resolv-conf"
|
||||
version = "0.7.0"
|
||||
|
@ -2270,82 +2199,6 @@ version = "0.10.2+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.3"
|
||||
|
@ -2383,15 +2236,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.2.0"
|
||||
|
|
|
@ -54,7 +54,7 @@ impl Entry {
|
|||
self.last_update = Some(Instant::now());
|
||||
}
|
||||
|
||||
pub fn location(&self) -> Option<adsb::Position> {
|
||||
pub fn position(&self) -> Option<adsb::Position> {
|
||||
match (&self.cpr1, &self.cpr2) {
|
||||
(Some(cpr1), Some(cpr2)) => {
|
||||
let pos = adsb::cpr::get_position((cpr1, cpr2))?;
|
||||
|
@ -70,6 +70,17 @@ impl Entry {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flight(&self) -> Option<&str> {
|
||||
self.callsign.as_ref().map(|callsign| {
|
||||
callsign.split(char::is_whitespace)
|
||||
.next().unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn altitude_m(&self) -> Option<f64> {
|
||||
self.altitude.map(|altitude| altitude as f64 * 0.3048)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -57,7 +57,7 @@ async fn main() {
|
|||
update_min(&entry.ground_speed, &mut min_speed);
|
||||
update_max(&entry.ground_speed, &mut max_speed);
|
||||
|
||||
if let Some(pos) = entry.location() {
|
||||
if let Some(pos) = entry.position() {
|
||||
let altitude_m = entry.altitude.unwrap_or(0) as f64 * 0.3048;
|
||||
let distance_km = WGS84::from_degrees_and_meters(pos.latitude, pos.longitude, altitude_m)
|
||||
.distance(&home) / 1000.0;
|
||||
|
|
|
@ -6,10 +6,11 @@ edition = "2021"
|
|||
[dependencies]
|
||||
futures = "0.3"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
geo = "0.18"
|
||||
csv = "1.1"
|
||||
tokio-xmpp = "3"
|
||||
xmpp-parsers = "0.18"
|
||||
adsb = "0.3"
|
||||
beast = { path = "../beast" }
|
||||
|
|
|
@ -1,59 +1,15 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use serde::Deserialize;
|
||||
use tokio::sync::mpsc::{channel, Receiver};
|
||||
|
||||
use super::location::Locations;
|
||||
|
||||
/// ft
|
||||
const MAX_ALTITUDE: u32 = 5000;
|
||||
const MAX_ALTITUDE: u16 = 5000;
|
||||
/// s
|
||||
const STATE_TIMEOUT: u64 = 180;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Info {
|
||||
hex: String,
|
||||
flight: String,
|
||||
lat: f64,
|
||||
lon: f64,
|
||||
/// ft
|
||||
altitude: u32,
|
||||
track: u32,
|
||||
/// kts
|
||||
speed: u32,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
pub fn get_hex(&self) -> &str {
|
||||
let mut hex = &self.hex[..];
|
||||
while hex.len() > 0 && hex.chars().last().unwrap().is_whitespace() {
|
||||
hex = &hex[..hex.len() - 1];
|
||||
}
|
||||
hex
|
||||
}
|
||||
|
||||
pub fn get_flight(&self) -> Option<String> {
|
||||
let mut i = self.flight.len();
|
||||
while i > 0 && self.flight.chars().nth(i - 1).unwrap().is_whitespace() {
|
||||
i -= 1;
|
||||
}
|
||||
if i > 0 {
|
||||
Some(self.flight[0..i].to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_altitude_m(&self) -> f64 {
|
||||
self.altitude as f64 * 0.3048
|
||||
}
|
||||
|
||||
// pub fn get_speed_kph(&self) -> f64 {
|
||||
// self.speed as f64 * 1.852
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Event {
|
||||
pub action: Action,
|
||||
|
@ -69,41 +25,63 @@ pub enum Action {
|
|||
Ignored,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Info {
|
||||
pub hex: String,
|
||||
pub flight: Option<String>,
|
||||
pub altitude_m: Option<f64>,
|
||||
}
|
||||
|
||||
struct State {
|
||||
info: Option<Info>,
|
||||
info: Info,
|
||||
position: Option<adsb::Position>,
|
||||
location: Option<Arc<String>>,
|
||||
last: Instant,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(hex: String) -> Self {
|
||||
State {
|
||||
info: None,
|
||||
info: Info {
|
||||
hex,
|
||||
flight: None,
|
||||
altitude_m: None,
|
||||
},
|
||||
position: None,
|
||||
location: None,
|
||||
last: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, info: Info, locations: &Locations) -> Option<Event> {
|
||||
pub fn update(&mut self, entry: &beast::aircrafts::Entry, locations: &Locations) -> Option<Event> {
|
||||
self.last = Instant::now();
|
||||
|
||||
let coord = geo::Coordinate { x: info.lon, y: info.lat };
|
||||
if let Some(old_info) = self.info.replace(info) {
|
||||
let info = self.info.as_ref().unwrap();
|
||||
if old_info.lon != info.lon || old_info.lat != info.lat {
|
||||
if let Some(flight) = entry.flight() {
|
||||
self.info.flight = Some(flight.to_string());
|
||||
}
|
||||
if let Some(altitude_m) = entry.altitude_m() {
|
||||
self.info.altitude_m = Some(altitude_m);
|
||||
}
|
||||
|
||||
let pos = if let Some(pos) = entry.position() {
|
||||
pos
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let coord = geo::Coordinate { x: pos.longitude, y: pos.latitude };
|
||||
if let Some(old_pos) = self.position.replace(pos.clone()) {
|
||||
if old_pos.longitude != pos.longitude || old_pos.latitude != pos.latitude {
|
||||
let location = locations.find(&coord);
|
||||
if location != self.location {
|
||||
if let Some(location) = location {
|
||||
self.location = Some(location.clone());
|
||||
Some(Event {
|
||||
action: Action::Moved,
|
||||
info: self.info.clone().unwrap(),
|
||||
location,
|
||||
info: self.info.clone(),
|
||||
})
|
||||
} else {
|
||||
if self.location.is_some() {
|
||||
println!("{}: move to nowhere", info.flight);
|
||||
}
|
||||
// move to no location
|
||||
self.location = None;
|
||||
None
|
||||
|
@ -120,7 +98,7 @@ impl State {
|
|||
self.location = locations.find(&coord);
|
||||
Some(Event {
|
||||
action: Action::Appeared,
|
||||
info: self.info.clone().unwrap(),
|
||||
info: self.info.clone(),
|
||||
location: self.location.clone()
|
||||
.unwrap_or_else(|| Arc::new("irgendwo".to_owned())),
|
||||
})
|
||||
|
@ -128,68 +106,49 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
async fn fetch(url: &str) -> Result<Vec<Info>> {
|
||||
let values: Vec<serde_json::Value> = reqwest::get(url)
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
// skip invalid entries
|
||||
let result = values.into_iter()
|
||||
.filter_map(|value| serde_json::from_value(value).ok())
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn run(url: &'static str, locations: Locations) -> Receiver<Event> {
|
||||
pub fn run(host: &'static str, port: u16, locations: Locations) -> Receiver<Event> {
|
||||
let (tx, rx) = channel(1);
|
||||
|
||||
tokio::spawn(async move {
|
||||
let source = beast::aircrafts::Aircrafts::new();
|
||||
source.connect(host, port);
|
||||
|
||||
let mut states = HashMap::new();
|
||||
// ignore anything above MAX_ALTITUDE permanently
|
||||
let mut ignored = HashSet::new();
|
||||
// cache that lives longer than dump1090's
|
||||
let mut flights = HashMap::new();
|
||||
loop {
|
||||
let infos = match fetch(url).await {
|
||||
Ok(infos) => infos,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut events = vec![];
|
||||
for mut info in infos {
|
||||
if info.altitude > MAX_ALTITUDE {
|
||||
if !ignored.contains(&info.hex) {
|
||||
ignored.insert(info.hex.clone());
|
||||
if let Some(_) = states.remove(&info.hex) {
|
||||
for (icao_address, entry) in source.read().iter() {
|
||||
let hex = format!("{}", icao_address);
|
||||
let entry = entry.read().unwrap();
|
||||
|
||||
if entry.altitude.map(|altitude| altitude > MAX_ALTITUDE)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if !ignored.contains(&hex) {
|
||||
ignored.insert(hex.clone());
|
||||
if let Some(_) = states.remove(&hex) {
|
||||
events.push(Event {
|
||||
action: Action::Ignored,
|
||||
info,
|
||||
info: Info {
|
||||
hex,
|
||||
flight: entry.flight().map(|s| s.to_string()),
|
||||
altitude_m: entry.altitude_m(),
|
||||
},
|
||||
location: Arc::new("ignoriert".to_owned()),
|
||||
});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ignored.contains(&info.hex) {
|
||||
if ignored.contains(&hex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(flight) = info.get_flight() {
|
||||
flights.insert(info.hex.clone(), flight);
|
||||
} else if let Some(flight) = flights.get(&info.hex) {
|
||||
info.flight = flight.to_string();
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(event) = states.entry(info.hex.clone())
|
||||
.or_insert(State::new())
|
||||
.update(info, &locations) {
|
||||
tx.send(event).await.unwrap();
|
||||
if let Some(event) = states.entry(hex.clone())
|
||||
.or_insert(State::new(hex.clone()))
|
||||
.update(&entry, &locations) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +156,8 @@ pub fn run(url: &'static str, locations: Locations) -> Receiver<Event> {
|
|||
if state.last + Duration::from_secs(STATE_TIMEOUT) < Instant::now() {
|
||||
events.push(Event {
|
||||
action: Action::Disappeared,
|
||||
info: state.info.clone().unwrap(),
|
||||
info: state.info
|
||||
.clone(),
|
||||
location: Arc::new("weg".to_owned()),
|
||||
});
|
||||
false
|
||||
|
|
|
@ -21,7 +21,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||
tokio::task::LocalSet::new().run_until(async move {
|
||||
let aircrafts = aircrafts::Aircrafts::load("aircraftDatabase.csv");
|
||||
let locations = location::Locations::load("locations.json");
|
||||
let mut events = adsb::run("http://radiobert.serv.zentralwerk.org:8080/data.json", locations);
|
||||
let mut events = adsb::run("radiobert.serv.zentralwerk.org", 30005, locations);
|
||||
|
||||
let args: Vec<String> = args().collect();
|
||||
if args.len() != 4 {
|
||||
|
@ -36,13 +36,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||
while let Some(event) = events.recv().await {
|
||||
println!("event: {:?}", event);
|
||||
|
||||
let flight = if let Some(flight) = event.info.get_flight() {
|
||||
format!("Flug {} [{}]", flight, event.info.get_hex())
|
||||
let flight = if let Some(flight) = event.info.flight {
|
||||
format!("Flug {} [{}]", flight, event.info.hex)
|
||||
} else {
|
||||
format!("[{}]", event.info.get_hex())
|
||||
format!("[{}]", event.info.hex)
|
||||
};
|
||||
let identifier;
|
||||
if let Some(aircraft) = aircrafts.find(&event.info.get_hex()) {
|
||||
if let Some(aircraft) = aircrafts.find(&event.info.hex) {
|
||||
println!("aircraft: {:?}", aircraft);
|
||||
match (&aircraft.owner, &aircraft.registration, &aircraft.model) {
|
||||
(Some(owner), Some(registration), Some(model)) =>
|
||||
|
@ -70,13 +70,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||
adsb::Action::Appeared =>
|
||||
format!("{} ist {:.0}m über {} aufgetaucht",
|
||||
identifier,
|
||||
event.info.get_altitude_m(),
|
||||
event.info.altitude_m.unwrap_or(0.0),
|
||||
event.location
|
||||
),
|
||||
adsb::Action::Moved =>
|
||||
format!("{} fliegt jetzt {:.0}m über {}",
|
||||
identifier,
|
||||
event.info.get_altitude_m(),
|
||||
event.info.altitude_m.unwrap_or(0.0),
|
||||
event.location
|
||||
),
|
||||
adsb::Action::Disappeared =>
|
||||
|
@ -84,7 +84,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||
adsb::Action::Ignored =>
|
||||
format!("{} wird ab {:.0}m ignoriert",
|
||||
identifier,
|
||||
event.info.get_altitude_m()
|
||||
event.info.altitude_m.unwrap_or(0.0),
|
||||
),
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ pub struct Aircraft {
|
|||
|
||||
impl Aircraft {
|
||||
pub fn new(icao_address: &ICAOAddress, entry: &Entry) -> Self {
|
||||
let pos = entry.location();
|
||||
let pos = entry.position();
|
||||
Aircraft {
|
||||
hex: format!("{}", icao_address),
|
||||
flight: entry.callsign.clone(),
|
||||
|
|
Loading…
Reference in New Issue