improv-setup/src/main.rs

180 lines
4.9 KiB
Rust
Raw Normal View History

2024-10-15 23:48:06 +02:00
use anyhow::{
Context,
Result,
};
use clap::{
Parser,
Subcommand,
};
use env_logger;
use hex;
use improv_setup::improv::{
IMPROV_HEADER,
IMPROV_VERSION,
PacketType,
RPCCommand,
CurrentState,
calculate_checksum,
};
use log::{
debug,
log_enabled,
info,
Level,
};
use tokio_serial;
#[derive(Subcommand, Clone)]
enum DeviceCommands {
/// Set wifi credentials
SetWifi {
/// SSID of the network
ssid: String,
/// Password for the SSID
password: String,
},
}
#[derive(Subcommand, Clone)]
enum Commands {
/// List available serial devices
ListDevices,
/// Device
Device {
/// Path to the serial device
path: String,
/// Baud rate used for connecting to the serial device
#[arg(long, default_value_t = 115200)]
baud_rate: u32,
#[command(subcommand)]
device_command: Option<DeviceCommands>,
},
}
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
fn to_ascii_debug(bytes: &Vec<u8>) -> String {
let mut out = String::new();
for b in bytes {
if b.is_ascii_graphic() {
out += &b.escape_ascii().to_string();
}
else {
out.push_str(".");
}
}
return out;
}
fn find_begin_of_improv_packet(buffer: &Vec<u8>) -> Result<usize, String> {
let mut improv_header_char: usize = 0;
for (i, b) in buffer.iter().enumerate() {
if b == &IMPROV_HEADER[improv_header_char] {
improv_header_char += 1;
if improv_header_char >= IMPROV_HEADER.len() {
return Ok(i - (IMPROV_HEADER.len() - 1));
}
}
else {
improv_header_char = 0;
if b == &IMPROV_HEADER[improv_header_char] {
improv_header_char += 1;
}
}
}
return Err(String::from("Improv header not found"));
}
#[tokio::main]
async fn main() -> Result<()>{
env_logger::init();
let cli = Cli::parse();
let command: Commands = match &cli.command {
Some(command) => command.clone(),
None => Commands::ListDevices,
};
match &command {
Commands::ListDevices => {
println!("{}", tokio_serial::available_ports()
.unwrap()
.iter()
.map(|serialport| serialport.port_name.clone())
.fold(String::new(), |a, b| a + &b + &String::from("\n")));
},
Commands::Device {path, baud_rate, device_command} => {
let mut rpc_command_request_current_state: Vec<u8> = Vec::new();
rpc_command_request_current_state.extend(IMPROV_HEADER);
rpc_command_request_current_state.push(IMPROV_VERSION);
rpc_command_request_current_state.push(PacketType::RPCCommand as u8);
let mut data: Vec<u8> = Vec::new();
data.push(RPCCommand::RequestCurrentState as u8); // command
data.push(0x00); // command data length
rpc_command_request_current_state.push(data.len().try_into().unwrap()); // length
rpc_command_request_current_state.extend(data.clone()); // data
let checksum: u8 = calculate_checksum(&rpc_command_request_current_state);
println!("{}", hex::encode([checksum.clone()]));
rpc_command_request_current_state.push(checksum);
println!("{}", hex::encode(&rpc_command_request_current_state));
println!("{}", to_ascii_debug(&rpc_command_request_current_state));
let mut serial_interface = tokio_serial::new(path, *baud_rate).open().unwrap();
serial_interface.write(&rpc_command_request_current_state).unwrap();
let mut buffer: Vec<u8> = Vec::new();
serial_interface.read_to_end(&mut buffer);
println!("{}", hex::encode(&buffer));
println!("{}", to_ascii_debug(&buffer));
println!("{}", std::str::from_utf8(&buffer).unwrap_or(""));
let improv_packet_offset = find_begin_of_improv_packet(&buffer).unwrap();
println!("{}", improv_packet_offset);
let mut packet_index = improv_packet_offset;
// skip header
packet_index += 6;
// version
println!("Version: {}", &buffer[packet_index]);
packet_index += 1;
// type
let packet_type = PacketType::try_from(&buffer[packet_index]).unwrap();
println!("Type: {}", &packet_type);
packet_index += 1;
if packet_type == PacketType::CurrentState {
// skip length
packet_index +=1;
let current_state = CurrentState::try_from(&buffer[packet_index]).unwrap();
println!("Current state: {}", &current_state);
packet_index += 1;
}
},
};
Ok(())
}