use anyhow::{bail, Result};
use clap::{Parser, Subcommand};
use clap_num::maybe_hex;
use usbrelay_rs::sainsmart::SainSmartFourChannelRelay;
#[derive(Parser)]
#[command(version, about, long_about=None)]
struct Cli {
#[arg(short, long, default_value = "i:0x0403:0x6001", verbatim_doc_comment)]
device: String,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
On { channels: Vec<u8> },
Off { channels: Vec<u8> },
Get,
Set {
#[arg(value_parser=maybe_hex::<u8>)]
next_pins: u8,
},
}
fn set_and_check_pins(relay: &mut SainSmartFourChannelRelay, next_pins: u8) -> Result<()> {
relay.set(next_pins)?;
let pins = relay.read()?;
if pins != next_pins {
bail!("Failed to set pins, expected 0x{next_pins:02X}, got 0x{pins:02X}");
}
Ok(())
}
struct ClearPins(u8);
struct SetPins(u8);
fn frob_pins(relay: &mut SainSmartFourChannelRelay, clear: ClearPins, set: SetPins) -> Result<()> {
let pins = relay.read()?;
let mut next_pins = pins;
next_pins &= !clear.0;
next_pins |= set.0;
next_pins &= SainSmartFourChannelRelay::MASK;
set_and_check_pins(relay, next_pins)
}
fn main() -> Result<()> {
let cli = Cli::parse();
let mut relay = SainSmartFourChannelRelay::new(&cli.device)?;
match &cli.command {
Commands::On { channels } => {
let mut set_pins = SetPins(0);
let clear_pins = ClearPins(0);
for channel in channels.iter() {
set_pins.0 |= 1 << channel;
}
frob_pins(&mut relay, clear_pins, set_pins)?;
}
Commands::Off { channels } => {
let set_pins = SetPins(0);
let mut clear_pins = ClearPins(0);
for channel in channels.iter() {
clear_pins.0 &= !(1 << channel);
}
frob_pins(&mut relay, clear_pins, set_pins)?;
}
Commands::Get => {
let pins = relay.read()?;
println!("0x{pins:02X}");
}
Commands::Set { mut next_pins } => {
next_pins &= SainSmartFourChannelRelay::MASK;
set_and_check_pins(&mut relay, next_pins)?;
}
}
Ok(())
}