use super::Config; use super::GenericError; use chrono::format::ParseError; use chrono::NaiveDateTime; use minreq::Response; use serde::Deserialize; use std::sync::mpsc; use std::sync::mpsc::Receiver; use std::thread; use std::thread::sleep; use std::thread::Thread; use std::time::{Duration, Instant}; pub trait Timestamped { fn timestamp(&self) -> Option; } #[derive(Deserialize, Debug)] pub struct ErrorModel { #[serde(rename(deserialize = "Message"))] pub message: Option, } #[derive(Deserialize, Debug)] pub struct TagModel { pub id: Option, pub imei: Option, #[serde(rename(deserialize = "batteryValue"))] pub battery_value: Option, #[serde(rename(deserialize = "btId"))] pub bt_id: Option, #[serde(rename(deserialize = "nfcId"))] pub nfc_id: Option, } #[derive(Deserialize, Debug, Clone)] pub struct StateModel { pub id: Option, pub timestamp: Option, #[serde(skip_deserializing)] pub state: Option, #[serde(rename(deserialize = "realState"))] pub real_state: Option, } impl Timestamped for StateModel { fn timestamp(&self) -> Option { self.timestamp.clone() } } #[derive(Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct LocationModel { pub timestamp: Option, #[serde(rename(deserialize = "type"))] pub loc_type: Option, #[serde(rename(deserialize = "coordinateLat"))] pub lat: Option, #[serde(rename(deserialize = "coordinateLng"))] pub lon: Option, pub accuracy: Option, } impl Timestamped for LocationModel { fn timestamp(&self) -> Option { self.timestamp.clone() } } pub struct API { config: Config, pub last_response_ping: u32, location_req_que: Vec, } impl API { pub fn new(config: Config) -> API { API { last_response_ping: config.throttle, config: config, location_req_que: Vec::new(), } } pub fn get_tags(&mut self) -> Result, GenericError> { let response = self.request(self.config.tags_url.clone())?; let tags = response.json(); if let Err(_) = tags { let err: ErrorModel = response.json()?; Err(GenericError::from(err)) } else { tags.map_err(GenericError::from) } } pub fn get_states(&mut self, tag_id: &String) -> Result, GenericError> { let response = self.request(str::replace(&self.config.states_url, "{tag}", &tag_id))?; Ok(response.json()?) } pub fn queue_location(&mut self, tag_id: &String, state_id: &String) { let url = str::replace(&self.config.locations_url, "{tag}", &tag_id); let url = str::replace(&url, "{state}", &state_id); self.location_req_que.push(url); //let response = self.request(url)?; //Ok(response.json()?) } pub fn begin_location_fetch(&mut self) -> Receiver> { let (sender, receiver) = mpsc::channel(); let locations = self.location_req_que.clone(); let config = self.config.clone(); thread::spawn(move || { for location in locations { let sender = sender.clone(); let config = config.clone(); thread::spawn(move || { println!("Send req!"); let response = minreq::get(location) .with_header("content-type", "application/json") .with_header("x-api-key", &config.api_key) .send() .map_err(GenericError::from) .and_then(|r| { String::from_utf8(r.into_bytes()).map_err(GenericError::from) }); sender.send(response).ok(); }); thread::sleep_ms(100); } }); self.location_req_que.clear(); receiver } pub fn get_between( list: Vec, from: Option, to: Option, inclusive_from: bool, config: &Config, ) -> Vec<(NaiveDateTime, T)> { let mut timestamped = Vec::new(); for item in list { if let Some(res) = API::parse_timestamp(&item.timestamp(), config) { match res { Ok(t) => { if let Some(to) = to { if t > to { continue; } } let mut too_old = false; if let Some(from) = from { if t < from { too_old = true; } } if too_old { if inclusive_from { timestamped.push((t.clone(), item)); } break; } timestamped.push((t.clone(), item)); } Err(e) => panic!(e), } } else { println!("Skipped item, did not have timestmap"); } } timestamped } fn request(&mut self, url: String) -> Result { let before = Instant::now(); let response = minreq::get(url) .with_header("content-type", "application/json") .with_header("x-api-key", &self.config.api_key) .send(); let after = Instant::now(); let min_time = Duration::new(0, self.config.throttle * 1_000_000); let duration = after.duration_since(before); self.last_response_ping = (duration.as_millis() as u32).max(self.config.throttle); if min_time > duration { sleep(min_time - duration); } response.map_err(GenericError::from) } fn parse_timestamp( timestamp: &Option, config: &Config, ) -> Option> { if let Some(timestamp) = ×tamp { Some(NaiveDateTime::parse_from_str( ×tamp, &config.timestamp_format, )) } else { None } } }