X-Git-Url: http://git.scottworley.com/tattlekey/blobdiff_plain/dd98493cac996e5f131a3e235c202820110ac975..79d78cde52a33539a4739df85a234c0aa0c34735:/server/src/main.rs diff --git a/server/src/main.rs b/server/src/main.rs index 5a6315f..071c4b2 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,20 +1,109 @@ +// tattlekey: A one-key UDP keyboard +// Copyright (C) 2023 Scott Worley +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use serde::Serialize; +use std::collections::HashMap; use std::net::UdpSocket; +use std::time::{Duration, SystemTime}; const MESSAGE_SIZE: usize = 12; +#[derive(Eq, Debug, Hash, PartialEq, Serialize)] +struct MessageKey { + epoch: u32, + device: u16, + seq: u16, +} + +#[derive(Debug)] +struct Message { + key: MessageKey, + t: SystemTime, +} + +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")); + 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), + } + } +} +impl TryFrom<&[u8]> for Message { + type Error = std::array::TryFromSliceError; + fn try_from(value: &[u8]) -> Result { + match <[u8; MESSAGE_SIZE]>::try_from(value) { + Ok(correct_size) => Ok(Message::from(&correct_size)), + Err(e) => Err(e), + } + } +} + +#[derive(Debug)] +struct Range { + start: SystemTime, + end: SystemTime, +} +impl Range { + fn new(t: &SystemTime) -> Self { + Self { start: *t, end: *t } + } + fn contains(&self, t: &SystemTime) -> bool { + t > &self.start && t < &self.end + } + fn extend(&mut self, t: &SystemTime) { + if t < &self.start { + self.start = *t; + } + if t > &self.end { + self.end = *t; + } + } +} + fn main() { + let mut log = csv::Writer::from_writer(std::io::stdout()); + let mut presses = HashMap::::new(); let socket = UdpSocket::bind("0.0.0.0:29803").expect("couldn't bind to address"); loop { let mut buf = [0; MESSAGE_SIZE]; match socket.recv_from(&mut buf) { Err(e) => eprintln!("Didn't receive data: {e}"), Ok((number_of_bytes, src_addr)) => { - let filled_buf = &mut buf[..number_of_bytes]; + let filled_buf = &buf[..number_of_bytes]; if number_of_bytes != MESSAGE_SIZE { eprintln!("Ignoring short message ({number_of_bytes}) from {src_addr}"); continue; } - println!("Got packet from {src_addr}: {filled_buf:?}"); + let message = Message::try_from(filled_buf).expect("I can't count"); + log.serialize((&message.key, message.t)) + .expect("Couldn't write log"); + log.flush().expect("Couldn't flush log"); + 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)); + } } } }