mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-07-29 09:41:45 +08:00
fix: 修复 LCUS HID 继电器协议错误 #263
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::os::fd::AsRawFd;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -9,12 +8,10 @@ use tokio::time::sleep;
|
|||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use super::traits::AtxKeyBackend;
|
use super::traits::AtxKeyBackend;
|
||||||
use super::types::AtxKeyConfig;
|
use super::types::{AtxKeyConfig, LCUS_RELAY_MAX_CHANNEL};
|
||||||
use crate::error::{AppError, Result};
|
use crate::error::{AppError, Result};
|
||||||
|
|
||||||
const USB_RELAY_MAX_CHANNEL: u8 = 8;
|
const LCUS_RELAY_COMMAND_LEN: usize = 4;
|
||||||
const USB_RELAY_REPORT_LEN: usize = 9;
|
|
||||||
const HIDIOCSFEATURE_9: libc::c_ulong = 0xC009_4806;
|
|
||||||
|
|
||||||
pub struct HidrawLinuxRelayBackend {
|
pub struct HidrawLinuxRelayBackend {
|
||||||
config: AtxKeyConfig,
|
config: AtxKeyConfig,
|
||||||
@@ -34,13 +31,13 @@ impl HidrawLinuxRelayBackend {
|
|||||||
fn validate_config(&self) -> Result<()> {
|
fn validate_config(&self) -> Result<()> {
|
||||||
if self.config.pin == 0 {
|
if self.config.pin == 0 {
|
||||||
return Err(AppError::Config(
|
return Err(AppError::Config(
|
||||||
"USB relay channel must be 1-based (>= 1)".to_string(),
|
"LCUS HID relay channel must be 1-based (>= 1)".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if self.config.pin > USB_RELAY_MAX_CHANNEL as u32 {
|
if self.config.pin > LCUS_RELAY_MAX_CHANNEL as u32 {
|
||||||
return Err(AppError::Config(format!(
|
return Err(AppError::Config(format!(
|
||||||
"USB HID relay channel must be <= {}",
|
"LCUS HID relay channel must be <= {}",
|
||||||
USB_RELAY_MAX_CHANNEL
|
LCUS_RELAY_MAX_CHANNEL
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -49,20 +46,19 @@ impl HidrawLinuxRelayBackend {
|
|||||||
fn send_command(&self, on: bool) -> Result<()> {
|
fn send_command(&self, on: bool) -> Result<()> {
|
||||||
let channel = u8::try_from(self.config.pin).map_err(|_| {
|
let channel = u8::try_from(self.config.pin).map_err(|_| {
|
||||||
AppError::Config(format!(
|
AppError::Config(format!(
|
||||||
"USB relay channel {} exceeds max {}",
|
"LCUS HID relay channel {} exceeds max {}",
|
||||||
self.config.pin,
|
self.config.pin, LCUS_RELAY_MAX_CHANNEL
|
||||||
u8::MAX
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
if channel == 0 {
|
if channel == 0 {
|
||||||
return Err(AppError::Config(
|
return Err(AppError::Config(
|
||||||
"USB relay channel must be 1-based (>= 1)".to_string(),
|
"LCUS HID relay channel must be 1-based (>= 1)".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if channel > USB_RELAY_MAX_CHANNEL {
|
if channel > LCUS_RELAY_MAX_CHANNEL {
|
||||||
return Err(AppError::Config(format!(
|
return Err(AppError::Config(format!(
|
||||||
"USB HID relay channel must be <= {}",
|
"LCUS HID relay channel must be <= {}",
|
||||||
USB_RELAY_MAX_CHANNEL
|
LCUS_RELAY_MAX_CHANNEL
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,44 +66,22 @@ impl HidrawLinuxRelayBackend {
|
|||||||
let mut guard = self.handle.lock().unwrap();
|
let mut guard = self.handle.lock().unwrap();
|
||||||
let device = guard
|
let device = guard
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or_else(|| AppError::Internal("USB relay not initialized".to_string()))?;
|
.ok_or_else(|| AppError::Internal("LCUS HID relay not initialized".to_string()))?;
|
||||||
|
|
||||||
if let Err(feature_err) = Self::send_feature_report(device, &cmd) {
|
device
|
||||||
debug!(
|
.write_all(&cmd)
|
||||||
"USB relay feature report failed ({}), falling back to hidraw write",
|
.map_err(|e| AppError::Internal(format!("LCUS HID relay write failed: {}", e)))?;
|
||||||
feature_err
|
|
||||||
);
|
|
||||||
device.write_all(&cmd).map_err(|write_err| {
|
|
||||||
AppError::Internal(format!(
|
|
||||||
"USB relay feature report failed: {}; raw write failed: {}",
|
|
||||||
feature_err, write_err
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
device
|
device
|
||||||
.flush()
|
.flush()
|
||||||
.map_err(|e| AppError::Internal(format!("USB relay flush failed: {}", e)))?;
|
.map_err(|e| AppError::Internal(format!("LCUS HID relay flush failed: {}", e)))?;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_command(channel: u8, on: bool) -> [u8; USB_RELAY_REPORT_LEN] {
|
pub fn build_command(channel: u8, on: bool) -> [u8; LCUS_RELAY_COMMAND_LEN] {
|
||||||
let mut cmd = [0x00; USB_RELAY_REPORT_LEN];
|
let state = if on { 1 } else { 0 };
|
||||||
cmd[1] = if on { 0xFF } else { 0xFD };
|
let checksum = 0xA0u8.wrapping_add(channel).wrapping_add(state);
|
||||||
cmd[2] = channel;
|
[0xA0, channel, state, checksum]
|
||||||
cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_feature_report(
|
|
||||||
device: &File,
|
|
||||||
report: &[u8; USB_RELAY_REPORT_LEN],
|
|
||||||
) -> std::io::Result<()> {
|
|
||||||
let rc = unsafe { libc::ioctl(device.as_raw_fd(), HIDIOCSFEATURE_9 as _, report.as_ptr()) };
|
|
||||||
if rc < 0 {
|
|
||||||
Err(std::io::Error::last_os_error())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +91,7 @@ impl AtxKeyBackend for HidrawLinuxRelayBackend {
|
|||||||
self.validate_config()?;
|
self.validate_config()?;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Initializing USB relay ATX backend on {} channel {}",
|
"Initializing LCUS HID relay ATX backend on {} channel {}",
|
||||||
self.config.device, self.config.pin
|
self.config.device, self.config.pin
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -125,14 +99,14 @@ impl AtxKeyBackend for HidrawLinuxRelayBackend {
|
|||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(&self.config.device)
|
.open(&self.config.device)
|
||||||
.map_err(|e| AppError::Internal(format!("USB relay device open failed: {}", e)))?;
|
.map_err(|e| AppError::Internal(format!("LCUS HID relay device open failed: {}", e)))?;
|
||||||
|
|
||||||
*self.handle.lock().unwrap() = Some(device);
|
*self.handle.lock().unwrap() = Some(device);
|
||||||
self.send_command(false)?;
|
self.send_command(false)?;
|
||||||
self.initialized.store(true, Ordering::Relaxed);
|
self.initialized.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"USB relay channel {} configured successfully",
|
"LCUS HID relay channel {} configured successfully",
|
||||||
self.config.pin
|
self.config.pin
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -140,7 +114,9 @@ impl AtxKeyBackend for HidrawLinuxRelayBackend {
|
|||||||
|
|
||||||
async fn pulse(&self, duration: Duration) -> Result<()> {
|
async fn pulse(&self, duration: Duration) -> Result<()> {
|
||||||
if !self.is_initialized() {
|
if !self.is_initialized() {
|
||||||
return Err(AppError::Internal("USB relay not initialized".to_string()));
|
return Err(AppError::Internal(
|
||||||
|
"LCUS HID relay not initialized".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.send_command(true)?;
|
self.send_command(true)?;
|
||||||
@@ -177,14 +153,14 @@ mod tests {
|
|||||||
use super::HidrawLinuxRelayBackend;
|
use super::HidrawLinuxRelayBackend;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn usb_relay_command_format() {
|
fn lcus_hid_relay_command_format() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
HidrawLinuxRelayBackend::build_command(1, true),
|
HidrawLinuxRelayBackend::build_command(1, true),
|
||||||
[0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
[0xA0, 0x01, 0x01, 0xA2]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
HidrawLinuxRelayBackend::build_command(1, false),
|
HidrawLinuxRelayBackend::build_command(1, false),
|
||||||
[0x00, 0xFD, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
[0xA0, 0x01, 0x00, 0xA1]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,21 +25,16 @@ pub use controller::{AtxController, AtxControllerConfig};
|
|||||||
pub use executor::timing;
|
pub use executor::timing;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
ActiveLevel, AtxAction, AtxDevices, AtxDriverType, AtxInputBinding, AtxKeyConfig,
|
ActiveLevel, AtxAction, AtxDevices, AtxDriverType, AtxInputBinding, AtxKeyConfig,
|
||||||
AtxOutputBinding, AtxPowerRequest, AtxState, HddStatus, PowerStatus,
|
AtxOutputBinding, AtxPowerRequest, AtxState, HddStatus, PowerStatus, LCUS_RELAY_MAX_CHANNEL,
|
||||||
};
|
};
|
||||||
pub use wol::{list_wol_history, record_wol_history, send_wol};
|
pub use wol::{list_wol_history, record_wol_history, send_wol};
|
||||||
|
|
||||||
#[cfg(any(unix, test))]
|
#[cfg(any(unix, test))]
|
||||||
fn hidraw_uevent_is_usb_relay(uevent: &str) -> bool {
|
fn hidraw_uevent_is_usb_relay(uevent: &str) -> bool {
|
||||||
let upper = uevent.to_ascii_uppercase();
|
let upper = uevent.to_ascii_uppercase();
|
||||||
upper.contains("000016C0:000005DF")
|
upper.contains("00005131:00002007")
|
||||||
|| upper.contains("00005131:00002007")
|
|
||||||
|| upper.contains("16C0:05DF")
|
|
||||||
|| upper.contains("5131:2007")
|
|| upper.contains("5131:2007")
|
||||||
|| upper.contains("PRODUCT=16C0/5DF")
|
|
||||||
|| upper.contains("PRODUCT=5131/2007")
|
|| upper.contains("PRODUCT=5131/2007")
|
||||||
|| upper.contains("USBRELAY")
|
|
||||||
|| upper.contains("USB RELAY")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@@ -94,14 +89,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hidraw_uevent_detects_usb_relay_id() {
|
fn test_hidraw_uevent_rejects_non_lcus_usb_relay_id() {
|
||||||
assert!(hidraw_uevent_is_usb_relay(
|
assert!(!hidraw_uevent_is_usb_relay(
|
||||||
"HID_ID=0003:000016C0:000005DF\nHID_NAME=www.dcttech.com USBRelay2\n"
|
"HID_ID=0003:000016C0:000005DF\nHID_NAME=www.dcttech.com USBRelay2\n"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hidraw_uevent_detects_5131_usb_relay_id() {
|
fn test_hidraw_uevent_detects_lcus_hid_relay_id() {
|
||||||
assert!(hidraw_uevent_is_usb_relay(
|
assert!(hidraw_uevent_is_usb_relay(
|
||||||
"HID_ID=0003:00005131:00002007\n"
|
"HID_ID=0003:00005131:00002007\n"
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use tokio::time::sleep;
|
|||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use super::traits::{validate_serial_config, AtxKeyBackend, SharedSerialHandle};
|
use super::traits::{validate_serial_config, AtxKeyBackend, SharedSerialHandle};
|
||||||
use super::types::AtxKeyConfig;
|
use super::types::{AtxKeyConfig, LCUS_RELAY_MAX_CHANNEL};
|
||||||
use crate::error::{AppError, Result};
|
use crate::error::{AppError, Result};
|
||||||
|
|
||||||
pub struct SerialRelayBackend {
|
pub struct SerialRelayBackend {
|
||||||
@@ -44,15 +44,23 @@ impl SerialRelayBackend {
|
|||||||
fn send_command(&self, on: bool) -> Result<()> {
|
fn send_command(&self, on: bool) -> Result<()> {
|
||||||
let channel = u8::try_from(self.config.pin).map_err(|_| {
|
let channel = u8::try_from(self.config.pin).map_err(|_| {
|
||||||
AppError::Config(format!(
|
AppError::Config(format!(
|
||||||
"Serial relay channel {} exceeds max {}",
|
"LCUS serial relay channel {} exceeds max {}",
|
||||||
self.config.pin,
|
self.config.pin, LCUS_RELAY_MAX_CHANNEL
|
||||||
u8::MAX
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
if channel == 0 {
|
||||||
|
return Err(AppError::Config(
|
||||||
|
"LCUS serial relay channel must be 1-based (>= 1)".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if channel > LCUS_RELAY_MAX_CHANNEL {
|
||||||
|
return Err(AppError::Config(format!(
|
||||||
|
"LCUS serial relay channel must be <= {}",
|
||||||
|
LCUS_RELAY_MAX_CHANNEL
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let state = if on { 1 } else { 0 };
|
let cmd = Self::build_command(channel, on);
|
||||||
let checksum = 0xA0u8.wrapping_add(channel).wrapping_add(state);
|
|
||||||
let cmd = [0xA0, channel, state, checksum];
|
|
||||||
|
|
||||||
let serial_handle = self
|
let serial_handle = self
|
||||||
.serial_handle
|
.serial_handle
|
||||||
@@ -60,16 +68,22 @@ impl SerialRelayBackend {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| AppError::Internal("Serial relay not initialized".to_string()))?;
|
.ok_or_else(|| AppError::Internal("LCUS serial relay not initialized".to_string()))?;
|
||||||
let mut port = serial_handle.lock().unwrap();
|
let mut port = serial_handle.lock().unwrap();
|
||||||
|
|
||||||
port.write_all(&cmd)
|
port.write_all(&cmd)
|
||||||
.map_err(|e| AppError::Internal(format!("Serial relay write failed: {}", e)))?;
|
.map_err(|e| AppError::Internal(format!("LCUS serial relay write failed: {}", e)))?;
|
||||||
port.flush()
|
port.flush()
|
||||||
.map_err(|e| AppError::Internal(format!("Serial relay flush failed: {}", e)))?;
|
.map_err(|e| AppError::Internal(format!("LCUS serial relay flush failed: {}", e)))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_command(channel: u8, on: bool) -> [u8; 4] {
|
||||||
|
let state = if on { 1 } else { 0 };
|
||||||
|
let checksum = 0xA0u8.wrapping_add(channel).wrapping_add(state);
|
||||||
|
[0xA0, channel, state, checksum]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -78,7 +92,7 @@ impl AtxKeyBackend for SerialRelayBackend {
|
|||||||
validate_serial_config(&self.config)?;
|
validate_serial_config(&self.config)?;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Initializing Serial relay ATX backend on {} channel {}",
|
"Initializing LCUS serial relay ATX backend on {} channel {}",
|
||||||
self.config.device, self.config.pin
|
self.config.device, self.config.pin
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -92,7 +106,7 @@ impl AtxKeyBackend for SerialRelayBackend {
|
|||||||
self.initialized.store(true, Ordering::Relaxed);
|
self.initialized.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Serial relay channel {} configured successfully",
|
"LCUS serial relay channel {} configured successfully",
|
||||||
self.config.pin
|
self.config.pin
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -101,12 +115,12 @@ impl AtxKeyBackend for SerialRelayBackend {
|
|||||||
async fn pulse(&self, duration: Duration) -> Result<()> {
|
async fn pulse(&self, duration: Duration) -> Result<()> {
|
||||||
if !self.is_initialized() {
|
if !self.is_initialized() {
|
||||||
return Err(AppError::Internal(
|
return Err(AppError::Internal(
|
||||||
"Serial relay not initialized".to_string(),
|
"LCUS serial relay not initialized".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Pulse serial relay on {} pin {}",
|
"Pulse LCUS serial relay on {} channel {}",
|
||||||
self.config.device, self.config.pin
|
self.config.device, self.config.pin
|
||||||
);
|
);
|
||||||
self.send_command(true)?;
|
self.send_command(true)?;
|
||||||
@@ -131,6 +145,23 @@ impl AtxKeyBackend for SerialRelayBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::SerialRelayBackend;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lcus_serial_relay_command_format() {
|
||||||
|
assert_eq!(
|
||||||
|
SerialRelayBackend::build_command(1, true),
|
||||||
|
[0xA0, 0x01, 0x01, 0xA2]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
SerialRelayBackend::build_command(1, false),
|
||||||
|
[0xA0, 0x01, 0x00, 0xA1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for SerialRelayBackend {
|
impl Drop for SerialRelayBackend {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.is_initialized() {
|
if self.is_initialized() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use serialport::SerialPort;
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::types::AtxKeyConfig;
|
use super::types::{AtxKeyConfig, LCUS_RELAY_MAX_CHANNEL};
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
|
|
||||||
pub type SharedSerialHandle = Arc<Mutex<Box<dyn SerialPort>>>;
|
pub type SharedSerialHandle = Arc<Mutex<Box<dyn SerialPort>>>;
|
||||||
@@ -36,10 +36,10 @@ pub fn validate_serial_config(config: &AtxKeyConfig) -> Result<()> {
|
|||||||
"Serial ATX channel must be 1-based (>= 1)".to_string(),
|
"Serial ATX channel must be 1-based (>= 1)".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if config.pin > u8::MAX as u32 {
|
if config.pin > LCUS_RELAY_MAX_CHANNEL as u32 {
|
||||||
return Err(crate::error::AppError::Config(format!(
|
return Err(crate::error::AppError::Config(format!(
|
||||||
"Serial ATX channel must be <= {}",
|
"LCUS serial relay channel must be <= {}",
|
||||||
u8::MAX
|
LCUS_RELAY_MAX_CHANNEL
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if config.baud_rate == 0 {
|
if config.baud_rate == 0 {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
|
pub const LCUS_RELAY_MAX_CHANNEL: u8 = 8;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum PowerStatus {
|
pub enum PowerStatus {
|
||||||
|
|||||||
@@ -608,19 +608,39 @@ impl AtxConfigUpdate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::atx::AtxDriverType::UsbRelay => {
|
crate::atx::AtxDriverType::UsbRelay => {
|
||||||
Self::validate_device_path(&config.device, "ATX USB relay")?;
|
Self::validate_device_path(&config.device, "ATX LCUS HID relay")?;
|
||||||
Self::validate_output_channel(&config.power, "power", 1, 8)?;
|
Self::validate_output_channel(
|
||||||
Self::validate_output_channel(&config.reset, "reset", 1, 8)?;
|
&config.power,
|
||||||
|
"power",
|
||||||
|
1,
|
||||||
|
crate::atx::LCUS_RELAY_MAX_CHANNEL as u32,
|
||||||
|
)?;
|
||||||
|
Self::validate_output_channel(
|
||||||
|
&config.reset,
|
||||||
|
"reset",
|
||||||
|
1,
|
||||||
|
crate::atx::LCUS_RELAY_MAX_CHANNEL as u32,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
crate::atx::AtxDriverType::Serial => {
|
crate::atx::AtxDriverType::Serial => {
|
||||||
Self::validate_device_path(&config.device, "ATX serial")?;
|
Self::validate_device_path(&config.device, "ATX LCUS serial relay")?;
|
||||||
if config.baud_rate == 0 {
|
if config.baud_rate == 0 {
|
||||||
return Err(AppError::BadRequest(
|
return Err(AppError::BadRequest(
|
||||||
"ATX baud_rate must be greater than 0".to_string(),
|
"ATX baud_rate must be greater than 0".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Self::validate_output_channel(&config.power, "power", 1, u8::MAX as u32)?;
|
Self::validate_output_channel(
|
||||||
Self::validate_output_channel(&config.reset, "reset", 1, u8::MAX as u32)?;
|
&config.power,
|
||||||
|
"power",
|
||||||
|
1,
|
||||||
|
crate::atx::LCUS_RELAY_MAX_CHANNEL as u32,
|
||||||
|
)?;
|
||||||
|
Self::validate_output_channel(
|
||||||
|
&config.reset,
|
||||||
|
"reset",
|
||||||
|
1,
|
||||||
|
crate::atx::LCUS_RELAY_MAX_CHANNEL as u32,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
crate::atx::AtxDriverType::None => {}
|
crate::atx::AtxDriverType::None => {}
|
||||||
};
|
};
|
||||||
@@ -1295,6 +1315,27 @@ mod tests {
|
|||||||
assert!(update.validate_with_current(¤t).is_err());
|
assert!(update.validate_with_current(¤t).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_atx_validate_with_current_rejects_lcus_serial_channel_over_8() {
|
||||||
|
let mut current = AtxConfig::default();
|
||||||
|
current.enabled = true;
|
||||||
|
current.driver = crate::atx::AtxDriverType::Serial;
|
||||||
|
current.device = "/dev/null".to_string();
|
||||||
|
current.power.enabled = true;
|
||||||
|
current.power.pin = 1;
|
||||||
|
current.baud_rate = 9600;
|
||||||
|
|
||||||
|
let mut update = empty_atx_update();
|
||||||
|
update.power = Some(AtxOutputBindingUpdate {
|
||||||
|
enabled: None,
|
||||||
|
device: None,
|
||||||
|
pin: Some(9),
|
||||||
|
active_level: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(update.validate_with_current(¤t).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_atx_validate_with_current_rejects_enabled_none_driver() {
|
fn test_atx_validate_with_current_rejects_enabled_none_driver() {
|
||||||
let mut current = AtxConfig::default();
|
let mut current = AtxConfig::default();
|
||||||
|
|||||||
Reference in New Issue
Block a user