extern crate bytes;
extern crate serial;
#[macro_use]
extern crate log;
use bytes::{BufMut, BytesMut};
use serial::prelude::*;
use std::io;
use std::io::{Error, ErrorKind};
use std::time::{Duration, Instant};
pub type RelayIndex = Vec<u8>;
pub type CardIndex = Vec<u8>;
pub struct Relay8x {
start_address: u8,
port: Box<SerialPort>,
}
#[derive(Debug)]
pub enum Relay8xCmdSet {
Init,
Set,
Toggle,
Reset,
}
impl Relay8xCmdSet {
pub fn encode(
self,
bytes: &mut BytesMut,
start_address: u8,
card: Option<u8>,
relays: Option<&RelayIndex>,
) -> io::Result<()> {
match self {
Relay8xCmdSet::Init => {
let cmd = 1; bytes.put_u8(cmd); bytes.put_u8(start_address); bytes.put_u8(0); let checksum = Relay8xCmdSet::checksummed(&bytes[..]); bytes.put_u8(checksum);
debug!(
"Init command: {:02x} {:02x} {:02x} {:02x}",
&bytes[0], &bytes[1], &bytes[2], &bytes[3]
);
}
Relay8xCmdSet::Set => {
let cmd = 6; bytes.put_u8(cmd); let address = Relay8xCmdSet::addressed(start_address, card);
bytes.put_u8(address); let relay_bin = Relay8xCmdSet::relay_as_u8(relays.unwrap());
debug!("Relays to set: {:08b}", relay_bin);
bytes.put_u8(relay_bin); let checksum = Relay8xCmdSet::checksummed(&bytes[..]); bytes.put_u8(checksum);
debug!(
"Set command: {:02x} {:02x} {:02x} {:02x}",
&bytes[0], &bytes[1], &bytes[2], &bytes[3]
);
}
Relay8xCmdSet::Toggle => {
let cmd = 8; bytes.put_u8(cmd); let address = Relay8xCmdSet::addressed(start_address, card);
bytes.put_u8(address); let relay_bin = Relay8xCmdSet::relay_as_u8(relays.unwrap());
debug!("Relays to set: {:08b}", relay_bin);
bytes.put_u8(relay_bin); let checksum = Relay8xCmdSet::checksummed(&bytes[..]); bytes.put_u8(checksum);
debug!(
"Toggle command: {:02x} {:02x} {:02x} {:02x}",
&bytes[0], &bytes[1], &bytes[2], &bytes[3]
);
}
Relay8xCmdSet::Reset => {
let cmd = 7; bytes.put_u8(cmd);
let address = Relay8xCmdSet::addressed(start_address, card);
bytes.put_u8(address); let relay_bin = Relay8xCmdSet::relay_as_u8(relays.unwrap());
debug!("Relays to set: {:08b}", relay_bin);
bytes.put_u8(relay_bin); let checksum = Relay8xCmdSet::checksummed(&bytes[..]); bytes.put_u8(checksum);
debug!(
"Reset command: {:02x} {:02x} {:02x} {:02x}",
&bytes[0], &bytes[1], &bytes[2], &bytes[3]
);
}
}
Ok(())
}
fn relay_as_u8(vec: &RelayIndex) -> u8 {
let mut relay_bin = 0b00000000;
vec.iter().rev().for_each(|x| {
relay_bin |= (1 << (x - 1)) as u8; });
relay_bin
}
fn checksummed(x: &[u8]) -> u8 {
x.iter().fold(0u8, |checksum, elem| checksum ^ elem)
}
fn addressed(address: u8, card: Option<u8>) -> u8 {
address + card.unwrap_or(1) - 1
}
}
impl Relay8x {
pub fn new(device_name: &str, address: u8) -> Result<Self, io::Error> {
let port = ::serial::open(device_name)?;
Ok(Self {
port: Box::new(port),
start_address: address,
})
}
pub fn configure_device(&mut self) -> io::Result<BytesMut> {
let port = &mut self.port;
port.reconfigure(&|settings| {
settings.set_baud_rate(::serial::Baud19200)?;
settings.set_char_size(::serial::Bits8);
settings.set_parity(::serial::ParityNone);
settings.set_stop_bits(::serial::Stop1);
settings.set_flow_control(::serial::FlowNone);
Ok(())
})?;
port.set_timeout(Duration::from_millis(1000))?;
let mut cmd = BytesMut::with_capacity(4);
Relay8xCmdSet::encode(
Relay8xCmdSet::Init,
&mut cmd,
self.start_address,
None,
None,
)?;
port.write(&cmd[..])?;
debug!("Wrote init message..");
let mut resp = BytesMut::new();
let now = Instant::now();
loop {
resp.put_u32_le(0);
port.read(&mut resp[..])?;
debug!(
"Response init: {:02x} {:02x} {:02x} {:02x}",
&resp[0], &resp[1], &resp[2], &resp[3]
);
if *resp.first().unwrap() == self.start_address {
break;
} else if now.elapsed().as_secs() > 30 {
return Err(Error::new(
ErrorKind::Other,
"Initialisation took to long..",
));
}
}
Ok(cmd)
}
pub fn set_relays(&mut self, cards: CardIndex, numbers: RelayIndex) -> io::Result<BytesMut> {
let port = &mut self.port;
let start_address = self.start_address;
let mut cmd = BytesMut::with_capacity(4);
for &card in cards.iter() {
Relay8xCmdSet::encode(
Relay8xCmdSet::Set,
&mut cmd,
start_address,
Some(card),
Some(&numbers),
)?;
port.write(&cmd[..])?;
let sent_cmd = cmd.clone();
port.read(&mut cmd[..])?;
debug!(
"Set Relays response: {:02x} {:02x} {:02x} {:02x}",
&cmd[0], &cmd[1], &cmd[2], &cmd[3]
);
Relay8x::check_response(&cmd, &sent_cmd)?;
cmd.clear();
}
Ok(cmd)
}
pub fn reset_relays(&mut self, cards: CardIndex, numbers: RelayIndex) -> io::Result<BytesMut> {
let port = &mut self.port;
let start_address = self.start_address;
let mut cmd = BytesMut::with_capacity(4);
for &card in cards.iter() {
Relay8xCmdSet::encode(
Relay8xCmdSet::Reset,
&mut cmd,
start_address,
Some(card),
Some(&numbers),
)?;
port.write(&cmd[..])?;
let sent_cmd = cmd.clone();
port.read(&mut cmd[..])?;
debug!(
"Reset Relays response: {:02x} {:02x} {:02x} {:02x}",
&cmd[0], &cmd[1], &cmd[2], &cmd[3]
);
Relay8x::check_response(&cmd, &sent_cmd)?;
cmd.clear();
}
Ok(cmd)
}
pub fn toggle_relays(&mut self, cards: CardIndex, numbers: RelayIndex) -> io::Result<BytesMut> {
let port = &mut self.port;
let start_address = self.start_address;
let mut cmd = BytesMut::with_capacity(4);
for &card in cards.iter() {
Relay8xCmdSet::encode(
Relay8xCmdSet::Toggle,
&mut cmd,
start_address,
Some(card),
Some(&numbers),
)?;
port.write(&cmd[..])?;
let sent_cmd = cmd.clone();
port.read(&mut cmd[..])?;
debug!(
"Toggle Relays response: {:02x} {:02x} {:02x} {:02x}",
&cmd[0], &cmd[1], &cmd[2], &cmd[3]
);
Relay8x::check_response(&cmd, &sent_cmd)?;
cmd.clear();
}
Ok(cmd)
}
fn check_response(msg: &BytesMut, sent_msg: &BytesMut) -> io::Result<()> {
let checker_byte = sent_msg.get(0).unwrap_or(&1);
let checked_bytes = msg.get(0).unwrap_or(&1);
if *checked_bytes != !checker_byte {
return Err(Error::new(
ErrorKind::Other,
format!(
"Bad first byte: is {}, should be {}",
checked_bytes, !checker_byte
),
));
}
let resp_addr = msg.get(1).unwrap_or(&0);
let sent_addr = sent_msg.get(1).unwrap_or(&1);
if resp_addr != sent_addr && *sent_addr != 0u8 {
return Err(Error::new(
ErrorKind::Other,
format!(
"Wrong Adress: 0x{:02x} instead of 0x{:02x}",
resp_addr, sent_addr
),
));
}
if *msg.get(3).unwrap_or(&0)
!= (*msg.get(0).unwrap_or(&1) ^ *msg.get(1).unwrap_or(&0) ^ *msg.get(2).unwrap_or(&0))
{
return Err(Error::new(ErrorKind::Other, "XOR in last byte is wrong"));
}
debug!("Check ok");
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn connect_to_card() {
let mut relay =
Relay8x::new(String::from("/dev/ttyUSB0"), 1).expect("Failed to connect to device");
let init_response = relay.init_device().expect("Failed to init device");
let expected_res = BytesMut::from(vec![
254,
relay.start_address,
11,
254 ^ relay.start_address ^ 11,
]);
assert_eq!(init_response, expected_res);
}
}