Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
parkpow
GitHub Repository: parkpow/deep-license-plate-recognition
Path: blob/master/gate-controller/src-tauri/src/relay/hw348.rs
1085 views
1
use std::process::Command;
2
use std::path::PathBuf;
3
#[cfg(target_os = "windows")]
4
use std::os::windows::process::CommandExt;
5
6
// ==============================
7
// ===== HELPER / DISCOVERY =====
8
// ==============================
9
10
#[derive(serde::Serialize, serde::Deserialize, Debug)]
11
pub struct UsbRelayInfo {
12
pub serial_number: String,
13
pub relay_type: String,
14
}
15
16
pub fn list_hw348_relays() -> Result<Vec<UsbRelayInfo>, String> {
17
let usbrelay_exe_path = get_usbrelay_path()?;
18
let output = Command::new(&usbrelay_exe_path)
19
.arg("-list")
20
.creation_flags(0x08000000)
21
.output()
22
.map_err(|e| e.to_string())?;
23
24
if !output.status.success() {
25
return Err(format!(
26
"usbrelay.exe failed: {}",
27
String::from_utf8_lossy(&output.stderr)
28
));
29
}
30
31
let stdout = String::from_utf8_lossy(&output.stdout);
32
let relays = stdout
33
.lines()
34
.skip(2)
35
.filter(|line| !line.trim().is_empty())
36
.filter_map(|line| {
37
let parts: Vec<&str> = line.split_whitespace().collect();
38
if parts.len() >= 2 {
39
Some(UsbRelayInfo {
40
serial_number: parts[0].to_string(),
41
relay_type: parts[1].to_string(),
42
})
43
} else {
44
None
45
}
46
})
47
.collect();
48
49
Ok(relays)
50
}
51
52
// ==============================
53
// ====== RELAY ACTION LOGIC ====
54
// ==============================
55
56
pub fn trigger_hw348_action(
57
serial_number: &str,
58
action: &str,
59
channel: u8,
60
max_channels: u8,
61
) -> Result<(), String> {
62
if channel == 0 || channel > max_channels {
63
return Err(format!(
64
"Invalid channel '{}' for relay '{}'. Must be between 1 and {}.",
65
channel, serial_number, max_channels
66
));
67
}
68
match action {
69
"on" => run_usbrelay_command(serial_number, "-on", channel),
70
"off" => run_usbrelay_command(serial_number, "-off", channel),
71
_ => Err("Invalid action for HW348 relay.".to_string()),
72
}
73
}
74
75
// ==============================
76
// ===== USB RELAY HELPERS ======
77
// ==============================
78
79
fn run_usbrelay_command(serial_number: &str, action: &str, channel: u8) -> Result<(), String> {
80
let usbrelay_exe_path = get_usbrelay_path()?;
81
let output = Command::new(&usbrelay_exe_path)
82
.arg("-serial")
83
.arg(serial_number)
84
.arg(action)
85
.arg(channel.to_string())
86
.creation_flags(0x08000000)
87
.output()
88
.map_err(|e| e.to_string())?;
89
90
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
91
let stderr = String::from_utf8_lossy(&output.stderr).to_lowercase();
92
93
if !output.status.success() || stderr.contains("error") || stderr.contains("failed") || stdout.contains("error") {
94
let full_error = format!("{}{}", stderr.trim(), stdout.trim()).trim().to_string();
95
return Err(
96
if full_error.is_empty() {
97
format!("USB relay command failed with exit code {}.", output.status.code().unwrap_or(-1))
98
} else {
99
full_error
100
},
101
);
102
}
103
104
Ok(())
105
}
106
107
// ==============================
108
// ===== PATH HELPERS ============
109
// ==============================
110
111
fn get_usbrelay_path() -> Result<PathBuf, String> {
112
let mut path = std::env::current_exe().map_err(|e| e.to_string())?;
113
path.pop(); // remove exe itself
114
path.push("usbrelay");
115
path.push("usbrelay.exe");
116
117
if !path.exists() {
118
return Err(format!("usbrelay.exe not found at {:?}", path));
119
}
120
121
Ok(path)
122
}
123
124