2025-05-09 20:34:37 +02:00

165 lines
5.0 KiB
Rust

use iced::time::{self, Duration, Instant, milliseconds, seconds};
use std::collections::HashMap;
use std::fmt::{Formatter, Error as fmtError};
use serde::{Deserialize, Serialize};
use iced::widget::{center, column, row, text};
use iced::{Element, Subscription, Theme, Center, Task};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use std::fs::read_to_string;
mod card_reader;
mod approx_time;
mod config;
use config::CONFIG;
#[derive(Serialize, Deserialize)]
struct RacerTime{
#[serde(with = "approx_time")]
time: Instant,
racer: Racer,
}
#[derive(Serialize, Deserialize)]
struct State{
racers: Vec<RacerTime>,
db: HashMap<usize, Racer>,
#[serde(with = "approx_time")]
time: Instant,
}
impl Default for State {
fn default() -> Self {
Self {
racers: vec![RacerTime{time: Instant::now(), racer: Racer{starting_number:11, card_id: 0}}],
db: Default::default(),
time: Instant::now(),
}
}
}
async fn dump_state(json: String) -> Result<(), tokio::io::Error> {
let mut file = File::create("state.json").await?;
file.write_all(json.as_bytes()).await?;
Ok(())
}
impl State{
fn update(&mut self, message: Message) -> Task<Message>{
match message {
Message::RacerAdded(racer) => {
self.db.insert(racer.card_id, racer);
Task::none()
},
Message::Tick(time) => {
if let Some(racer_time) = self.racers.iter().next() {
if time - racer_time.time > Duration::new(CONFIG.DURATION + 10, 0){
self.racers.remove(0);
}
}
self.time = time;
Task::none()
},
Message::DumpState(_) => {
let json = serde_json::to_string(self).unwrap();
Task::perform(dump_state(json), |_| Message::Nothing)
},
Message::Nothing => Task::none(),
Message::CardReader(card_reader::Event::ReadedCardId(card_id)) => {
if let Some(racer) = self.db.get(&card_id){
self.racers.push(RacerTime{time: Instant::now(), racer: racer.clone()})
}else{
println!("Racer {} not found in db!", card_id)
};
Task::none()
},
Message::CardReader(card_reader::Event::Connected) => {
println!("Card Reader Connected!");
Task::none()
},
Message::CardReader(card_reader::Event::Disconnected) => {
println!("Card Reader Disconnected!");
Task::none()
},
}
}
fn view(&self) -> Element<Message> {
let mut content = column![
row![
text("Číslo závodníka").width(120).align_x(Center).align_y(Center), text("čas").size(20).width(120).align_x(Center).align_y(Center)
],
];
for racer_time in &self.racers {
const MINUTE: u64 = 60;
let time = self.time - racer_time.time;
let duration = Duration::new(CONFIG.DURATION, 0);
let (duration, negative) = if duration < time {
(time - duration, true)
} else {
(duration - time, false)
};
let seconds = duration.as_secs();
let duration = text!(
"{}{:0>2}:{:0>2}.{:0>2}",
if negative {"-"} else {""},
seconds / MINUTE,
seconds % MINUTE,
duration.subsec_millis() / 10,
).size(20).width(120).align_x(Center).align_y(Center);
content = content.push(
row![
text(racer_time.racer.starting_number.to_string()).size(20).width(120).align_x(Center).align_y(Center), duration
]
);
}
center(content).into()
}
fn subscription(&self) -> Subscription<Message> {
Subscription::batch(vec![
time::every(milliseconds(10)).map(Message::Tick),
time::every(seconds(CONFIG.SAVING_INTERVAL)).map(Message::DumpState),
Subscription::run(card_reader::connect).map(Message::CardReader),
])
}
fn theme(&self) -> Theme {
Theme::Dark
}
}
#[derive(Serialize, Deserialize, Clone)]
struct Racer{
starting_number: usize,
card_id: usize,
}
enum Message{
CardReader(card_reader::Event),
RacerAdded(Racer),
Tick(Instant),
DumpState(Instant),
Nothing,
}
impl std::fmt::Debug for Message {
// Required method
fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), fmtError>{
todo!()
}
}
pub fn main() -> iced::Result {
let init_state = || -> State {read_to_string("state.json").ok().map(|s| serde_json::from_str(&s).ok()).flatten().unwrap_or_else(||Default::default())} ;
env_logger::init();
iced::application(init_state, State::update, State::view)
.subscription(State::subscription)
.theme(State::theme)
.run()
}