mirror of
https://github.com/jgoerzen/xbnet.git
synced 2025-02-20 09:13:48 -04:00
checkpointing
This commit is contained in:
parent
c7c90a8ff3
commit
869afe3a77
133
src/xb.rs
133
src/xb.rs
@ -27,6 +27,7 @@ use std::thread;
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use format_escape_default::format_escape_default;
|
use format_escape_default::format_escape_default;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use bytes::Bytes;
|
||||||
|
|
||||||
pub fn mkerror(msg: &str) -> Error {
|
pub fn mkerror(msg: &str) -> Error {
|
||||||
Error::new(ErrorKind::Other, msg)
|
Error::new(ErrorKind::Other, msg)
|
||||||
@ -42,59 +43,14 @@ pub struct ReceivedFrames(pub Vec<u8>, pub Option<(String, String)>);
|
|||||||
pub struct XB {
|
pub struct XB {
|
||||||
ser: XBSer,
|
ser: XBSer,
|
||||||
|
|
||||||
// Lines coming from the radio
|
/// My 64-bit MAC address
|
||||||
readerlinesrx: crossbeam_channel::Receiver<String>,
|
mymac: u64,
|
||||||
|
|
||||||
// Frames going to the app
|
/// Frames to transmit
|
||||||
readeroutput: crossbeam_channel::Sender<ReceivedFrames>,
|
writerrx: crossbeam_channel::Receiver<Bytes>,
|
||||||
|
|
||||||
// Blocks to transmit
|
/// Maximum packet size
|
||||||
txblockstx: crossbeam_channel::Sender<Vec<u8>>,
|
|
||||||
txblocksrx: crossbeam_channel::Receiver<Vec<u8>>,
|
|
||||||
|
|
||||||
// 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<Duration>,
|
|
||||||
|
|
||||||
// Extra data, to send before the next frame.
|
|
||||||
extradata: Vec<u8>,
|
|
||||||
|
|
||||||
// Maximum packet size
|
|
||||||
maxpacketsize: usize,
|
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<Instant>,
|
|
||||||
|
|
||||||
// When the current TX slot ends, if any.
|
|
||||||
txslotend: Option<Instant>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<String>) {
|
|
||||||
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
|
/// 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 {
|
impl XB {
|
||||||
/// Creates a new XB. Returns an instance to be used for sending,
|
/** Creates a new XB. Returns an instance to be used for reading,
|
||||||
/// as well as a separate receiver to be used in a separate thread to handle
|
as well as a separate sender to be used in a separate thread to handle
|
||||||
/// incoming frames. The bool specifies whether or not to read the quality
|
outgoing frames. This will spawn a thread to handle the writing to XBee.
|
||||||
/// parameters after a read.
|
|
||||||
pub fn new(ser: XBSer) -> (XB, crossbeam_channel::Receiver<ReceivedFrames>) {
|
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<PathBuf>) -> (XB, crossbeam_channel::Sender<Bytes>) {
|
||||||
|
// FIXME: make this maximum of 5 configurable
|
||||||
|
let (writertx, writerrx) = crossbeam_channel::bounded(5);
|
||||||
|
|
||||||
debug!("Configuring radio");
|
debug!("Configuring radio");
|
||||||
thread::sleep(Duration::from_msecs(1100));
|
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();
|
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
|
// Enter API mode
|
||||||
ser.writeln("ATAP 1")?;
|
ser.writeln("ATAP 1").unwrap();
|
||||||
assert_response(ser.readln()?, "OK");
|
assert_response(ser.readln().unwrap, "OK");
|
||||||
|
|
||||||
// Standard API output mode
|
// Standard API output mode
|
||||||
ser.writeln("ATAO 0")?;
|
ser.writeln("ATAO 0").unwrap();
|
||||||
assert_response(ser.readln()?, "OK");
|
assert_response(ser.readln().unwrap(), "OK");
|
||||||
|
|
||||||
// Get our own MAC address
|
// Get our own MAC address
|
||||||
ser.writeln("ATSH")?;
|
ser.writeln("ATSH").unwrap();
|
||||||
let serialhigh = ser.readln()?;
|
let serialhigh = ser.readln().unwrap();
|
||||||
|
let serialhighu64 = u64::from(u32::from_be_bytes(hex::decode(serialhigh).unwrap()));
|
||||||
|
|
||||||
ser.writeln("ATSL")?;
|
ser.writeln("ATSL").unwrap();
|
||||||
let seriallow = ser.readln()?;
|
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
|
// Get maximum packet size
|
||||||
ser.writeln("ATNP")?;
|
ser.writeln("ATNP").unwrap();
|
||||||
let maxpacket = ser.readln()?;
|
let maxpacket = ser.readln().unwrap();
|
||||||
|
let maxpacketsize = usize::from(u16::from_be_bytes(hex::decode(maxpacket).unwrap()));
|
||||||
|
|
||||||
|
|
||||||
// Exit command mode
|
// Exit command mode
|
||||||
ser.writeln("ATCN")?;
|
ser.writeln("ATCN").unwrap();
|
||||||
assert_response(ser.readln()?, "OK");
|
assert_response(ser.readln().unwrap(), "OK");
|
||||||
|
|
||||||
let ser2 = ser.clone();
|
let ser2 = ser.clone();
|
||||||
|
|
||||||
(XB { readqual, ser, readeroutput, readerlinesrx, txblockstx, txblocksrx, maxpacketsize, pack,
|
(XB {
|
||||||
txdelay: None,
|
ser,
|
||||||
txwait: Duration::from_millis(txwait),
|
mymac,
|
||||||
eotwait: Duration::from_millis(eotwait),
|
maxpacketsize,
|
||||||
txslot: if txslot > 0 {
|
writerrx,
|
||||||
Some(Duration::from_millis(txslot))
|
}, writertx)
|
||||||
} else { None },
|
|
||||||
txslotend: None,
|
|
||||||
extradata: vec![]}, readeroutputreader)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mainloop(&mut self) -> io::Result<()> {
|
pub fn mainloop(&mut self) -> io::Result<()> {
|
||||||
|
@ -95,6 +95,7 @@ impl XBTXRequest {
|
|||||||
fullframe.put_u16(lenu16);
|
fullframe.put_u16(lenu16);
|
||||||
fullframe.put_slice(self.innerframe);
|
fullframe.put_slice(self.innerframe);
|
||||||
fullframe.put_u8(xbchecksum(self.innerframe));
|
fullframe.put_u8(xbchecksum(self.innerframe));
|
||||||
|
Ok(fullframe.freeze())
|
||||||
} else {
|
} else {
|
||||||
Err(TXGenError::InvalidLen)
|
Err(TXGenError::InvalidLen)
|
||||||
}
|
}
|
||||||
@ -106,3 +107,33 @@ pub fn xbchecksum(data: &[u8]) -> u8 {
|
|||||||
let sumu64 = data.into_iter().map(|x| u64::from(x)).sum();
|
let sumu64 = data.into_iter().map(|x| u64::from(x)).sum();
|
||||||
0xffu8 - (sumu64 as u8)
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user