Path: blob/master/gate-controller/src-tauri/src/relay/hw348.rs
1085 views
use std::process::Command;1use std::path::PathBuf;2#[cfg(target_os = "windows")]3use std::os::windows::process::CommandExt;45// ==============================6// ===== HELPER / DISCOVERY =====7// ==============================89#[derive(serde::Serialize, serde::Deserialize, Debug)]10pub struct UsbRelayInfo {11pub serial_number: String,12pub relay_type: String,13}1415pub fn list_hw348_relays() -> Result<Vec<UsbRelayInfo>, String> {16let usbrelay_exe_path = get_usbrelay_path()?;17let output = Command::new(&usbrelay_exe_path)18.arg("-list")19.creation_flags(0x08000000)20.output()21.map_err(|e| e.to_string())?;2223if !output.status.success() {24return Err(format!(25"usbrelay.exe failed: {}",26String::from_utf8_lossy(&output.stderr)27));28}2930let stdout = String::from_utf8_lossy(&output.stdout);31let relays = stdout32.lines()33.skip(2)34.filter(|line| !line.trim().is_empty())35.filter_map(|line| {36let parts: Vec<&str> = line.split_whitespace().collect();37if parts.len() >= 2 {38Some(UsbRelayInfo {39serial_number: parts[0].to_string(),40relay_type: parts[1].to_string(),41})42} else {43None44}45})46.collect();4748Ok(relays)49}5051// ==============================52// ====== RELAY ACTION LOGIC ====53// ==============================5455pub fn trigger_hw348_action(56serial_number: &str,57action: &str,58channel: u8,59max_channels: u8,60) -> Result<(), String> {61if channel == 0 || channel > max_channels {62return Err(format!(63"Invalid channel '{}' for relay '{}'. Must be between 1 and {}.",64channel, serial_number, max_channels65));66}67match action {68"on" => run_usbrelay_command(serial_number, "-on", channel),69"off" => run_usbrelay_command(serial_number, "-off", channel),70_ => Err("Invalid action for HW348 relay.".to_string()),71}72}7374// ==============================75// ===== USB RELAY HELPERS ======76// ==============================7778fn run_usbrelay_command(serial_number: &str, action: &str, channel: u8) -> Result<(), String> {79let usbrelay_exe_path = get_usbrelay_path()?;80let output = Command::new(&usbrelay_exe_path)81.arg("-serial")82.arg(serial_number)83.arg(action)84.arg(channel.to_string())85.creation_flags(0x08000000)86.output()87.map_err(|e| e.to_string())?;8889let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();90let stderr = String::from_utf8_lossy(&output.stderr).to_lowercase();9192if !output.status.success() || stderr.contains("error") || stderr.contains("failed") || stdout.contains("error") {93let full_error = format!("{}{}", stderr.trim(), stdout.trim()).trim().to_string();94return Err(95if full_error.is_empty() {96format!("USB relay command failed with exit code {}.", output.status.code().unwrap_or(-1))97} else {98full_error99},100);101}102103Ok(())104}105106// ==============================107// ===== PATH HELPERS ============108// ==============================109110fn get_usbrelay_path() -> Result<PathBuf, String> {111let mut path = std::env::current_exe().map_err(|e| e.to_string())?;112path.pop(); // remove exe itself113path.push("usbrelay");114path.push("usbrelay.exe");115116if !path.exists() {117return Err(format!("usbrelay.exe not found at {:?}", path));118}119120Ok(path)121}122123124