diff --git a/POC_in_rust/Cargo.toml b/POC_in_rust/Cargo.toml index a048bdd..1ac4834 100644 --- a/POC_in_rust/Cargo.toml +++ b/POC_in_rust/Cargo.toml @@ -4,11 +4,14 @@ version = "0.1.0" edition = "2024" [dependencies] +chrono = "0.4.41" env_logger = "0.11.8" futures-lite = "2.6.0" iced = {"git"= "https://github.com/iced-rs/iced.git", "features"=["tokio", "auto-detect-theme", "fira-sans", "tiny-skia", "wgpu", "sipper"]} lazy_static = "1.5.0" nusb = "0.1.13" +reqwest = { version = "0.12.15", features = ["json", "multipart", "http2", "cookies"] } +reqwest-middleware = "0.4.2" serde = {version = "1.0.219", features=["derive"]} serde_json = "1.0.140" tokio = {version = "1.45.0", features=["time", "fs", "io-util", "macros"]} diff --git a/POC_in_rust/config.json b/POC_in_rust/config.json new file mode 100644 index 0000000..de0a1a9 --- /dev/null +++ b/POC_in_rust/config.json @@ -0,0 +1,8 @@ +{ + "DURATION": 600, + "SAVING_INTERVAL": 60, + "STATION_ID": 2, + "HOST": "https://beta.alkator.cz", + "LOGIN": "station_register@alkator.cz", + "PASSWORD": "password_heslo" +} \ No newline at end of file diff --git a/POC_in_rust/src/approx_time.rs b/POC_in_rust/src/approx_time.rs new file mode 100644 index 0000000..166cb4a --- /dev/null +++ b/POC_in_rust/src/approx_time.rs @@ -0,0 +1,25 @@ + +use std::time::{Instant, SystemTime}; +use serde::{Serialize, Serializer, Deserialize, Deserializer, de::Error}; + +pub fn serialize(instant: &Instant, serializer: S) -> Result +where + S: Serializer, +{ + let system_now = SystemTime::now(); + let instant_now = Instant::now(); + let approx = system_now - (instant_now - *instant); + approx.serialize(serializer) +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let de = SystemTime::deserialize(deserializer)?; + let system_now = SystemTime::now(); + let instant_now = Instant::now(); + let duration = system_now.duration_since(de).map_err(Error::custom)?; + let approx = instant_now - duration; + Ok(approx) +} diff --git a/POC_in_rust/src/card_reader.rs b/POC_in_rust/src/card_reader.rs new file mode 100644 index 0000000..b93724e --- /dev/null +++ b/POC_in_rust/src/card_reader.rs @@ -0,0 +1,66 @@ +use iced::task::{Never, Sipper, sipper}; +use tokio::time::sleep; +use std::time::Duration; + +use nusb::{transfer::{ RequestBuffer, TransferError }, Interface}; + +static USB_VENDOR: u16 = 0xffff; +static USB_PRODUCT: u16 = 0x0035; + + +fn get_interface() -> Option { + let dev = nusb::list_devices().ok()?.find(|dev| dev.vendor_id() == USB_VENDOR && dev.product_id() == USB_PRODUCT)?; + let device = dev.open().ok()?; + device.detach_and_claim_interface(0).ok() +} + + +pub fn connect() -> impl Sipper { + sipper(async |mut output|{ + loop { + let interface = loop { + if let Some(interface) = get_interface(){ + break interface; + } + sleep(Duration::new(0, 100)).await; + }; + output.send(Event::Connected).await; + let mut card_id = 0; + loop { + let buf = RequestBuffer::new(8); + let data = interface.interrupt_in(0x81, buf).await; + match data.status { + Err(TransferError::Disconnected) => { + output.send(Event::Disconnected).await; + break; + }, + Ok(()) => { + let code = data.data[2]; + match code { + 40 => { + output.send(Event::ReadedCardId(card_id)).await; + card_id = 0; + }, + 0 => (), + x @ 30..=38 => { + card_id *= 10; + card_id += x as usize - 29; + }, + 39 => { + card_id *= 10; + }, + _ => panic!(), + } + }, + _ => todo!(), + } + } + } + }) +} + +pub enum Event{ + Disconnected, + Connected, + ReadedCardId(usize), +} diff --git a/POC_in_rust/src/config.rs b/POC_in_rust/src/config.rs new file mode 100644 index 0000000..41b88ea --- /dev/null +++ b/POC_in_rust/src/config.rs @@ -0,0 +1,33 @@ +use std::fs::File; +use std::io::BufReader; +use std::error::Error; +use std::path::Path; +use serde::{Deserialize}; +use lazy_static::lazy_static; + +#[allow(non_snake_case)] +#[derive(Deserialize)] +pub struct Config { + pub DURATION: u64, + pub SAVING_INTERVAL: u64, + pub STATION_ID: usize, + pub HOST: String, + pub LOGIN: String, + pub PASSWORD: String, +} + +fn read_config_from_file>(path: P) -> Result> { + // Open the file in read-only mode with buffer. + let file = File::open(path)?; + let reader = BufReader::new(file); + + // Read the JSON contents of the file as an instance of `User`. + let config = serde_json::from_reader(reader)?; + + // Return the `User`. + Ok(config) +} + +lazy_static! { + pub static ref CONFIG: Config = read_config_from_file("config.json").unwrap(); +} \ No newline at end of file diff --git a/POC_in_rust/src/db_update.rs b/POC_in_rust/src/db_update.rs new file mode 100644 index 0000000..e69de29 diff --git a/POC_in_rust/src/main.rs b/POC_in_rust/src/main.rs index 43174aa..a8756db 100644 --- a/POC_in_rust/src/main.rs +++ b/POC_in_rust/src/main.rs @@ -7,10 +7,14 @@ use iced::{Element, Subscription, Theme, Center, Task}; use tokio::fs::File; use tokio::io::AsyncWriteExt; use std::fs::read_to_string; +use reqwest::Client; +use chrono::{DateTime, Local}; + mod card_reader; mod approx_time; mod config; +mod db_update; use config::CONFIG; @@ -45,6 +49,39 @@ async fn dump_state(json: String) -> Result<(), tokio::io::Error> { Ok(()) } +#[derive(Serialize)] +struct RegisterRacer{ + card_id: usize, + time: String, + station_id: usize, +} + +async fn station_register(card_id: usize, time: DateTime::) -> Result<(), reqwest::Error>{ + let client = reqwest::Client::builder() + .cookie_store(true) + .build() + .unwrap(); + let result = client + .post(CONFIG.HOST.clone() + "/api/login") + .form(&[ + ("login", CONFIG.LOGIN.as_str()), + ("password", CONFIG.PASSWORD.as_str()), + ]); + .send() + .await?; + let register_racer = RegisterRacer{ + card_id: card_id, + station_id: CONFIG.STATION_ID, + time: format!("{}", time.format("%d.%m.%Y %H:%M:%S.%f")), + }; + client + .post(CONFIG.HOST.clone() + "/api/station/register") + .json(®ister_racer) + .send() + .await?; + Ok(()) +} + impl State{ fn update(&mut self, message: Message) -> Task{ match message { @@ -67,12 +104,15 @@ impl State{ }, Message::Nothing => Task::none(), Message::CardReader(card_reader::Event::ReadedCardId(card_id)) => { + let time = Instant::now(); + let datetime = Local::now(); if let Some(racer) = self.db.get(&card_id){ - self.racers.push(RacerTime{time: Instant::now(), racer: racer.clone()}) + let racer_time = RacerTime{time, racer: racer.clone()}; + self.racers.push(racer_time); }else{ println!("Racer {} not found in db!", card_id) - }; - Task::none() + } + Task::perform(station_register(card_id, datetime), |_| Message::Nothing) }, Message::CardReader(card_reader::Event::Connected) => { println!("Card Reader Connected!");