X-Git-Url: http://git.scottworley.com/tattlekey/blobdiff_plain/c5cbd7402015e45558995e00d9623f41ecdf9732..6c8d15f4b2c0bacf5cb9fe5ad527ce219b97e0e9:/server/src/main.rs diff --git a/server/src/main.rs b/server/src/main.rs index 37de6bd..500c2f5 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -14,11 +14,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::collections::HashMap; use std::net::UdpSocket; -use std::time::{Duration, SystemTime}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; const MESSAGE_SIZE: usize = 12; +const LOGFILENAME: &str = "log.csv"; #[derive(Eq, Debug, Hash, PartialEq)] struct MessageKey { @@ -30,19 +32,37 @@ struct MessageKey { #[derive(Debug)] struct Message { key: MessageKey, - t: SystemTime, + t: u64, +} +impl Serialize for Message { + // https://github.com/BurntSushi/rust-csv/issues/155 + // https://github.com/BurntSushi/rust-csv/issues/98 + // https://github.com/BurntSushi/rust-csv/pull/223 + // csv doesn't support #[serde(flatten)], so we implement this directly. :( + fn serialize(&self, serializer: S) -> Result { + let mut row = serializer.serialize_struct("Message", 4)?; + row.serialize_field("epoch", &self.key.epoch)?; + row.serialize_field("device", &self.key.device)?; + row.serialize_field("seq", &self.key.seq)?; + row.serialize_field("t", &self.t)?; + row.end() + } } impl From<&[u8; MESSAGE_SIZE]> for Message { fn from(value: &[u8; MESSAGE_SIZE]) -> Self { let ago = u32::from_be_bytes(value[8..=11].try_into().expect("I can't count")); + let press_time = SystemTime::now() - Duration::new(ago.into(), 0); Self { key: MessageKey { epoch: u32::from_be_bytes(value[0..=3].try_into().expect("I can't count")), device: u16::from_be_bytes(value[4..=5].try_into().expect("I can't count")), seq: u16::from_be_bytes(value[6..=7].try_into().expect("I can't count")), }, - t: SystemTime::now() - Duration::new(ago.into(), 0), + t: press_time + .duration_since(UNIX_EPOCH) + .expect("Bad time?") + .as_secs(), } } } @@ -58,17 +78,17 @@ impl TryFrom<&[u8]> for Message { #[derive(Debug)] struct Range { - start: SystemTime, - end: SystemTime, + start: u64, + end: u64, } impl Range { - fn new(t: &SystemTime) -> Self { + fn new(t: &u64) -> Self { Self { start: *t, end: *t } } - fn contains(&self, t: &SystemTime) -> bool { + fn contains(&self, t: &u64) -> bool { t > &self.start && t < &self.end } - fn extend(&mut self, t: &SystemTime) { + fn extend(&mut self, t: &u64) { if t < &self.start { self.start = *t; } @@ -78,9 +98,32 @@ impl Range { } } +fn merge_message(presses: &mut HashMap, message: Message) { + if let Some(r) = presses.get_mut(&message.key) { + if !r.contains(&message.t) { + r.extend(&message.t); + } + } else { + presses.insert(message.key, Range::new(&message.t)); + } +} + +fn open_log_for_writing() -> csv::Writer { + let log_file_exists = std::path::Path::new(LOGFILENAME).exists(); + let logfile = std::fs::OpenOptions::new() + .create_new(!log_file_exists) + .append(true) + .open(LOGFILENAME) + .expect("Coudln't open log file"); + csv::WriterBuilder::new() + .has_headers(!log_file_exists) + .from_writer(logfile) +} + fn main() { - let mut presses = HashMap::::new(); let socket = UdpSocket::bind("0.0.0.0:29803").expect("couldn't bind to address"); + let mut presses = HashMap::::new(); + let mut log = open_log_for_writing(); loop { let mut buf = [0; MESSAGE_SIZE]; match socket.recv_from(&mut buf) { @@ -92,15 +135,9 @@ fn main() { continue; } let message = Message::try_from(filled_buf).expect("I can't count"); - if let Some(r) = presses.get_mut(&message.key) { - if !r.contains(&message.t) { - r.extend(&message.t); - println!("Updated press: {:?}: {r:?}", message.key); - } - } else { - println!("Got new press: {:?}: {:?}", message.key, message.t); - presses.insert(message.key, Range::new(&message.t)); - } + log.serialize(&message).expect("Couldn't write log"); + log.flush().expect("Couldn't flush log"); + merge_message(&mut presses, message); } } }