station data saving, fixes
This commit is contained in:
parent
bb0ddb40f1
commit
eb59bd978c
@ -3,6 +3,7 @@
|
||||
<!--
|
||||
IDEAS:
|
||||
- Achievements (i.e. "take a train which is delayed by at least 60 minutes" xd)
|
||||
- Bonus bei Verspätung, Gesamtverspätung sammeln
|
||||
- Show a map in the end (no need for world map, just use coordinates on flat black rectangle)
|
||||
-->
|
||||
<head>
|
||||
@ -22,7 +23,12 @@ body {
|
||||
background: light-dark(#ECE, #000);
|
||||
}
|
||||
|
||||
.DraggingTop {
|
||||
z-index: 15;
|
||||
}
|
||||
|
||||
#Header {
|
||||
z-index: 5;
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
display: flex;
|
||||
@ -271,6 +277,7 @@ function draggingOnDown(x, y, e) {
|
||||
if (isInGame) return;
|
||||
if (draggingElement !== null) return;
|
||||
draggingElement = e;
|
||||
draggingElement.classList.add("DraggingTop");
|
||||
draggingStartPos = [x, y];
|
||||
}
|
||||
function draggingOnMove(x, y) {
|
||||
@ -283,6 +290,7 @@ function draggingOnMove(x, y) {
|
||||
}
|
||||
function draggingOnUp(x, y) {
|
||||
if (draggingElement === null) return;
|
||||
draggingElement.classList.remove("DraggingTop");
|
||||
if (!isInGame) {
|
||||
if (draggingEndCallback) draggingEndCallback(x, y);
|
||||
}
|
||||
@ -558,7 +566,6 @@ async function reloadDepartures() {
|
||||
if (evaNumber === stopEvaNumber) foundCurrent = false;
|
||||
}
|
||||
for (const [stop, evaNumber] of result[0]) {
|
||||
console.log(stop, ": ", evaNumber);
|
||||
let stopElem = document.createElement("li");
|
||||
stopElem.innerText = stop;
|
||||
stopElem.style.wordWrap = "nowrap";
|
||||
|
@ -1,8 +1,10 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub struct TransportModesSet(u32);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum TransportMode {
|
||||
Bus,
|
||||
Tram,
|
||||
|
@ -1,12 +1,12 @@
|
||||
pub mod ratelimit;
|
||||
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
use std::{collections::HashSet, sync::Arc, time::Duration};
|
||||
|
||||
use axum::{Json, extract::Path, response::Html, routing::get};
|
||||
use rand::seq::IndexedRandom;
|
||||
use ratelimit::Ratelimit;
|
||||
use reqwest::StatusCode;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::{sync::Mutex, time::sleep};
|
||||
|
||||
use crate::{
|
||||
bahn_api::{
|
||||
@ -28,7 +28,19 @@ pub async fn main() {
|
||||
|
||||
let ratelimit = Ratelimit::new(10, 10);
|
||||
|
||||
let stations = Arc::new(Mutex::new(Stations::new()));
|
||||
let stations = Arc::new(Mutex::new(Stations::load().await.unwrap().unwrap()));
|
||||
{
|
||||
let stations = Arc::clone(&stations);
|
||||
tokio::task::spawn(async move {
|
||||
loop {
|
||||
#[cfg(debug_assertions)]
|
||||
sleep(Duration::from_secs(30)).await;
|
||||
#[cfg(not(debug_assertions))]
|
||||
sleep(Duration::from_secs(900)).await;
|
||||
stations.lock().await.save().await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
axum::serve(
|
||||
tokio::net::TcpListener::bind(
|
||||
|
@ -1 +1,3 @@
|
||||
pub mod stations;
|
||||
pub mod stations_files;
|
||||
pub mod stations_saving;
|
||||
|
@ -60,6 +60,9 @@ impl Stations {
|
||||
self.stations.insert(eva_number, station);
|
||||
}
|
||||
}
|
||||
pub(super) fn insert_raw(&mut self, eva_number: StationEvaNumber, station: Station) {
|
||||
self.stations.insert(eva_number, station);
|
||||
}
|
||||
|
||||
pub fn get(&self, eva_number: &StationEvaNumber) -> Option<&Station> {
|
||||
self.stations.get(eva_number)
|
||||
|
71
src/storage/stations_files.rs
Normal file
71
src/storage/stations_files.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::bahn_api::{
|
||||
basic_types::station_ids::{DbStopId, StationEvaNumber},
|
||||
transport_modes::{TransportMode, TransportModesSet},
|
||||
};
|
||||
|
||||
use super::stations::Station;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct StationJsonRef<'a> {
|
||||
name: &'a str,
|
||||
db_station_id: &'a str,
|
||||
related_stations: Vec<&'a str>,
|
||||
lat_lon: Option<(f64, f64)>,
|
||||
transport_modes: Vec<TransportMode>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct StationJson {
|
||||
name: String,
|
||||
db_station_id: String,
|
||||
related_stations: Vec<String>,
|
||||
lat_lon: Option<(f64, f64)>,
|
||||
transport_modes: Vec<TransportMode>,
|
||||
}
|
||||
|
||||
pub fn station_to_string(
|
||||
Station {
|
||||
name,
|
||||
db_station_id,
|
||||
related_stations,
|
||||
lat_lon,
|
||||
transport_modes,
|
||||
has_been_changed: _,
|
||||
}: &Station,
|
||||
) -> String {
|
||||
serde_json::to_string(&StationJsonRef {
|
||||
name: &name,
|
||||
db_station_id: &db_station_id.0,
|
||||
related_stations: related_stations.iter().map(|v| v.0.as_str()).collect(),
|
||||
lat_lon: *lat_lon,
|
||||
transport_modes: transport_modes
|
||||
.as_ref()
|
||||
.map(|v| v.iter().collect())
|
||||
.unwrap_or_else(Vec::default),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn station_from_string(string: &str) -> Result<Station, serde_json::Error> {
|
||||
let StationJson {
|
||||
name,
|
||||
db_station_id,
|
||||
related_stations,
|
||||
lat_lon,
|
||||
transport_modes,
|
||||
} = serde_json::from_str(string)?;
|
||||
Ok(Station {
|
||||
name,
|
||||
db_station_id: DbStopId(db_station_id),
|
||||
related_stations: related_stations.into_iter().map(StationEvaNumber).collect(),
|
||||
lat_lon,
|
||||
transport_modes: Some(
|
||||
transport_modes
|
||||
.into_iter()
|
||||
.fold(TransportModesSet::new(), |set, m| set.with(m)),
|
||||
)
|
||||
.filter(|set| set.len() > 0),
|
||||
has_been_changed: false,
|
||||
})
|
||||
}
|
57
src/storage/stations_saving.rs
Normal file
57
src/storage/stations_saving.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use crate::bahn_api::basic_types::station_ids::StationEvaNumber;
|
||||
|
||||
use super::{
|
||||
stations::Stations,
|
||||
stations_files::{station_from_string, station_to_string},
|
||||
};
|
||||
|
||||
impl Stations {
|
||||
pub async fn save(&mut self) {
|
||||
for (key, station) in self.stations.iter_mut() {
|
||||
if station.has_been_changed {
|
||||
eprintln!("Saving station {:?} ({:?}).", station.name, key.0);
|
||||
station.has_been_changed = false;
|
||||
if key.0.chars().all(|ch| ch.is_ascii_alphanumeric()) {
|
||||
if let Err(e) = tokio::fs::write(
|
||||
format!("stations/{}", key.0.as_str()),
|
||||
station_to_string(station),
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!(
|
||||
"[stations/save] could not save station {:?}: {e}.",
|
||||
station.name
|
||||
);
|
||||
}
|
||||
} else {
|
||||
eprintln!(
|
||||
"[stations/save] skipping station {:?} because key {:?} contained at least one non-ascii-alphanumeric character.",
|
||||
station.name, key.0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn load() -> Result<Result<Self, serde_json::Error>, tokio::io::Error> {
|
||||
let mut dir_entries = tokio::fs::read_dir("stations/").await?;
|
||||
let mut s = Self::new();
|
||||
while let Some(file) = dir_entries.next_entry().await? {
|
||||
if let Some(name) = file
|
||||
.file_name()
|
||||
.to_str()
|
||||
.filter(|name| name.chars().all(|ch| ch.is_ascii_alphanumeric()))
|
||||
{
|
||||
let eva_number = StationEvaNumber(name.to_owned());
|
||||
let mut station =
|
||||
match station_from_string(&tokio::fs::read_to_string(file.path()).await?) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Ok(Err(e)),
|
||||
};
|
||||
station.has_been_changed = false;
|
||||
s.insert_raw(eva_number, station);
|
||||
}
|
||||
}
|
||||
Ok(Ok(s))
|
||||
}
|
||||
}
|
1
stations/.gitkeep
Normal file
1
stations/.gitkeep
Normal file
@ -0,0 +1 @@
|
||||
station data goes here, program will not start without this folder
|
Loading…
x
Reference in New Issue
Block a user