From 869afe3a77478bcb93e32c980cc3b27be3d5f7b7 Mon Sep 17 00:00:00 2001 From: John Goerzen Date: Sun, 20 Sep 2020 18:18:20 -0500 Subject: [PATCH] checkpointing --- src/xb.rs | 133 +++++++++++++------------------ src/{txpacket.rs => xbpacket.rs} | 31 +++++++ 2 files changed, 86 insertions(+), 78 deletions(-) rename src/{txpacket.rs => xbpacket.rs} (81%) diff --git a/src/xb.rs b/src/xb.rs index e8fba8d..1a22606 100644 --- a/src/xb.rs +++ b/src/xb.rs @@ -27,6 +27,7 @@ use std::thread; use std::time::{Duration, Instant}; use format_escape_default::format_escape_default; use std::path::PathBuf; +use bytes::Bytes; pub fn mkerror(msg: &str) -> Error { Error::new(ErrorKind::Other, msg) @@ -42,59 +43,14 @@ pub struct ReceivedFrames(pub Vec, pub Option<(String, String)>); pub struct XB { ser: XBSer, - // Lines coming from the radio - readerlinesrx: crossbeam_channel::Receiver, + /// My 64-bit MAC address + mymac: u64, - // Frames going to the app - readeroutput: crossbeam_channel::Sender, + /// Frames to transmit + writerrx: crossbeam_channel::Receiver, - // Blocks to transmit - txblockstx: crossbeam_channel::Sender>, - txblocksrx: crossbeam_channel::Receiver>, - - // Whether or not to read quality data from the radio - readqual: bool, - - // The wait before transmitting. Initialized from - // [`txwait`]. - txwait: Duration, - - // The transmit prevention timeout. Initialized from - // [`eotwait`]. - eotwait: Duration, - - // The maximum transmit time. - txslot: Option, - - // Extra data, to send before the next frame. - extradata: Vec, - - // Maximum packet size + /// Maximum packet size maxpacketsize: usize, - - // Whether or not to always try to cram as much as possible into each TX frame - pack: bool, - - // Whether we must delay before transmit. The Instant - // reflects the moment when the delay should end. - txdelay: Option, - - // When the current TX slot ends, if any. - txslotend: Option, -} - -/// Reads the lines from the radio and sends them down the channel to -/// the processing bits. -fn readerlinesthread(mut ser: XBSer, tx: crossbeam_channel::Sender) { - loop { - let line = ser.readln().expect("Error reading line"); - if let Some(l) = line { - tx.send(l).unwrap(); - } else { - debug!("{:?}: EOF", ser.portname); - return; - } - } } /// Assert that a given response didn't indicate an EOF, and that it @@ -110,53 +66,74 @@ pub fn assert_response(resp: String, expected: String) -> io::Result<()> { } impl XB { - /// Creates a new XB. Returns an instance to be used for sending, - /// as well as a separate receiver to be used in a separate thread to handle - /// incoming frames. The bool specifies whether or not to read the quality - /// parameters after a read. - pub fn new(ser: XBSer) -> (XB, crossbeam_channel::Receiver) { + /** Creates a new XB. Returns an instance to be used for reading, + as well as a separate sender to be used in a separate thread to handle + outgoing frames. This will spawn a thread to handle the writing to XBee. + + If initfile is given, its lines will be sent to the radio, one at a time, + expecting OK after each one, to initialize it. + + May panic if an error occurs during initialization. + */ + pub fn new(ser: XBSer, initfile: Option) -> (XB, crossbeam_channel::Sender) { + // FIXME: make this maximum of 5 configurable + let (writertx, writerrx) = crossbeam_channel::bounded(5); debug!("Configuring radio"); thread::sleep(Duration::from_msecs(1100)); - ser.swrite.lock().unwrap().write_all(b"+++")?; + ser.swrite.lock().unwrap().write_all(b"+++").unwrap(); ser.swrite.lock().unwrap().flush(); - assert_response(ser.readln()?, "OK"); + assert_response(ser.readln().unwrap(), "OK"); + + if let Some(file) = initfile { + let f = fs::File::open(file).unwrap(); + let reader = BufReader::new(f); + for line in reader.lines() { + if line.len() > 0 { + self.ser.writeln(line).unwrap(); + assert_response(ser.readln().unwrap(), "OK") + } + } + } // Enter API mode - ser.writeln("ATAP 1")?; - assert_response(ser.readln()?, "OK"); + ser.writeln("ATAP 1").unwrap(); + assert_response(ser.readln().unwrap, "OK"); // Standard API output mode - ser.writeln("ATAO 0")?; - assert_response(ser.readln()?, "OK"); + ser.writeln("ATAO 0").unwrap(); + assert_response(ser.readln().unwrap(), "OK"); // Get our own MAC address - ser.writeln("ATSH")?; - let serialhigh = ser.readln()?; + ser.writeln("ATSH").unwrap(); + let serialhigh = ser.readln().unwrap(); + let serialhighu64 = u64::from(u32::from_be_bytes(hex::decode(serialhigh).unwrap())); - ser.writeln("ATSL")?; - let seriallow = ser.readln()?; + ser.writeln("ATSL").unwrap(); + let seriallow = ser.readln().unwrap(); + let seriallowu64 = u64::from(u32::from_be_bytes(hex::decode(seriallow).unwrap())); + + let mymac = serialhighu64 << 32 | seriallowu64; // Get maximum packet size - ser.writeln("ATNP")?; - let maxpacket = ser.readln()?; + ser.writeln("ATNP").unwrap(); + let maxpacket = ser.readln().unwrap(); + let maxpacketsize = usize::from(u16::from_be_bytes(hex::decode(maxpacket).unwrap())); + // Exit command mode - ser.writeln("ATCN")?; - assert_response(ser.readln()?, "OK"); + ser.writeln("ATCN").unwrap(); + assert_response(ser.readln().unwrap(), "OK"); let ser2 = ser.clone(); - (XB { readqual, ser, readeroutput, readerlinesrx, txblockstx, txblocksrx, maxpacketsize, pack, - txdelay: None, - txwait: Duration::from_millis(txwait), - eotwait: Duration::from_millis(eotwait), - txslot: if txslot > 0 { - Some(Duration::from_millis(txslot)) - } else { None }, - txslotend: None, - extradata: vec![]}, readeroutputreader) + (XB { + ser, + mymac, + maxpacketsize, + writerrx, + }, writertx) } pub fn mainloop(&mut self) -> io::Result<()> { diff --git a/src/txpacket.rs b/src/xbpacket.rs similarity index 81% rename from src/txpacket.rs rename to src/xbpacket.rs index abfcc89..19df6b3 100644 --- a/src/txpacket.rs +++ b/src/xbpacket.rs @@ -95,6 +95,7 @@ impl XBTXRequest { fullframe.put_u16(lenu16); fullframe.put_slice(self.innerframe); fullframe.put_u8(xbchecksum(self.innerframe)); + Ok(fullframe.freeze()) } else { Err(TXGenError::InvalidLen) } @@ -106,3 +107,33 @@ pub fn xbchecksum(data: &[u8]) -> u8 { let sumu64 = data.into_iter().map(|x| u64::from(x)).sum(); 0xffu8 - (sumu64 as u8) } + +/** Return a 48-bit MAC given the 64-bit MAC. Truncates the most significant bits. + +# Example + +``` +use xbnet::xbpacket::*; + +let mac64 = 0x123456789abcdeffu64; +let mac48 = mac64to48(mac64); +assert_eq!([0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff], mac48); +assert_eq(mac64, mac48to64(mac48, mac64)); +``` +*/ +pub fn mac64to48(mac64: u64) -> [u8; 6] { + let macbytes = mac64.to_be_bytes; + macbytes[2..] +} + +/** Return a 64-bit MAC given a pattern 64-bit MAC and a 48-bit MAC. The 16 most +significant bits from the pattern will be used to complete the 48-bit MAC to 64-bit. +*/ +pub fn mac48to64(mac48: &[u8; 6], pattern64: u64) -> u64 { + let mut mac64bytes = [0u8; 8]; + mac64bytes[2..] = mac48; + let mut mac64 = u64::from_be_bytes(mac64bytes); + mac64 |= pattern64 & 0xffff000000000000; + mac64 +} +