mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-04-30 01:46:37 +08:00
fix: 补齐 ATX 控制器缺失接口并完成全项目 clippy -D warnings 修复
This commit is contained in:
@@ -8,11 +8,11 @@ use tracing::{debug, info, warn};
|
||||
|
||||
use super::executor::{timing, AtxKeyExecutor};
|
||||
use super::led::LedSensor;
|
||||
use super::types::{AtxKeyConfig, AtxLedConfig, AtxState, PowerStatus};
|
||||
use super::types::{AtxKeyConfig, AtxLedConfig, AtxState, AtxAction, PowerStatus};
|
||||
use crate::error::{AppError, Result};
|
||||
|
||||
/// ATX power control configuration
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct AtxControllerConfig {
|
||||
/// Whether ATX is enabled
|
||||
pub enabled: bool,
|
||||
@@ -24,17 +24,6 @@ pub struct AtxControllerConfig {
|
||||
pub led: AtxLedConfig,
|
||||
}
|
||||
|
||||
impl Default for AtxControllerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
power: AtxKeyConfig::default(),
|
||||
reset: AtxKeyConfig::default(),
|
||||
led: AtxLedConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal state holding all ATX components
|
||||
/// Grouped together to reduce lock acquisitions
|
||||
struct AtxInner {
|
||||
@@ -54,34 +43,7 @@ pub struct AtxController {
|
||||
}
|
||||
|
||||
impl AtxController {
|
||||
/// Create a new ATX controller with the specified configuration
|
||||
pub fn new(config: AtxControllerConfig) -> Self {
|
||||
Self {
|
||||
inner: RwLock::new(AtxInner {
|
||||
config,
|
||||
power_executor: None,
|
||||
reset_executor: None,
|
||||
led_sensor: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a disabled ATX controller
|
||||
pub fn disabled() -> Self {
|
||||
Self::new(AtxControllerConfig::default())
|
||||
}
|
||||
|
||||
/// Initialize the ATX controller and its executors
|
||||
pub async fn init(&self) -> Result<()> {
|
||||
let mut inner = self.inner.write().await;
|
||||
|
||||
if !inner.config.enabled {
|
||||
info!("ATX disabled in configuration");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Initializing ATX controller");
|
||||
|
||||
async fn init_components(inner: &mut AtxInner) {
|
||||
// Initialize power executor
|
||||
if inner.config.power.is_configured() {
|
||||
let mut executor = AtxKeyExecutor::new(inner.config.power.clone());
|
||||
@@ -123,234 +85,180 @@ impl AtxController {
|
||||
inner.led_sensor = Some(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
info!("ATX controller initialized successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reload the ATX controller with new configuration
|
||||
///
|
||||
/// This is called when configuration changes and supports hot-reload.
|
||||
pub async fn reload(&self, new_config: AtxControllerConfig) -> Result<()> {
|
||||
info!("Reloading ATX controller with new configuration");
|
||||
async fn shutdown_components(inner: &mut AtxInner) {
|
||||
if let Some(executor) = inner.power_executor.as_mut() {
|
||||
if let Err(e) = executor.shutdown().await {
|
||||
warn!("Failed to shutdown power executor: {}", e);
|
||||
}
|
||||
}
|
||||
inner.power_executor = None;
|
||||
|
||||
// Shutdown existing executors
|
||||
self.shutdown_internal().await?;
|
||||
if let Some(executor) = inner.reset_executor.as_mut() {
|
||||
if let Err(e) = executor.shutdown().await {
|
||||
warn!("Failed to shutdown reset executor: {}", e);
|
||||
}
|
||||
}
|
||||
inner.reset_executor = None;
|
||||
|
||||
// Update configuration and re-initialize
|
||||
{
|
||||
let mut inner = self.inner.write().await;
|
||||
inner.config = new_config;
|
||||
if let Some(sensor) = inner.led_sensor.as_mut() {
|
||||
if let Err(e) = sensor.shutdown().await {
|
||||
warn!("Failed to shutdown LED sensor: {}", e);
|
||||
}
|
||||
}
|
||||
inner.led_sensor = None;
|
||||
}
|
||||
|
||||
/// Create a new ATX controller with the specified configuration
|
||||
pub fn new(config: AtxControllerConfig) -> Self {
|
||||
Self {
|
||||
inner: RwLock::new(AtxInner {
|
||||
config,
|
||||
power_executor: None,
|
||||
reset_executor: None,
|
||||
led_sensor: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a disabled ATX controller
|
||||
pub fn disabled() -> Self {
|
||||
Self::new(AtxControllerConfig::default())
|
||||
}
|
||||
|
||||
/// Initialize the ATX controller and its executors
|
||||
pub async fn init(&self) -> Result<()> {
|
||||
let mut inner = self.inner.write().await;
|
||||
|
||||
if !inner.config.enabled {
|
||||
info!("ATX disabled in configuration");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Re-initialize
|
||||
self.init().await?;
|
||||
info!("Initializing ATX controller");
|
||||
|
||||
Self::init_components(&mut inner).await;
|
||||
|
||||
info!("ATX controller reloaded successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get current ATX state (single lock acquisition)
|
||||
/// Reload ATX controller configuration
|
||||
pub async fn reload(&self, config: AtxControllerConfig) -> Result<()> {
|
||||
let mut inner = self.inner.write().await;
|
||||
|
||||
info!("Reloading ATX controller configuration");
|
||||
|
||||
// Shutdown existing components first, then rebuild with new config.
|
||||
Self::shutdown_components(&mut inner).await;
|
||||
inner.config = config;
|
||||
|
||||
if !inner.config.enabled {
|
||||
info!("ATX disabled after reload");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Self::init_components(&mut inner).await;
|
||||
info!("ATX controller reloaded");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shutdown ATX controller and release all resources
|
||||
pub async fn shutdown(&self) -> Result<()> {
|
||||
let mut inner = self.inner.write().await;
|
||||
Self::shutdown_components(&mut inner).await;
|
||||
info!("ATX controller shutdown complete");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Trigger a power action (short/long/reset)
|
||||
pub async fn trigger_power_action(&self, action: AtxAction) -> Result<()> {
|
||||
let inner = self.inner.read().await;
|
||||
|
||||
match action {
|
||||
AtxAction::Short | AtxAction::Long => {
|
||||
if let Some(executor) = &inner.power_executor {
|
||||
let duration = match action {
|
||||
AtxAction::Short => timing::SHORT_PRESS,
|
||||
AtxAction::Long => timing::LONG_PRESS,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
executor.pulse(duration).await?;
|
||||
} else {
|
||||
return Err(AppError::Config(
|
||||
"Power button not configured for ATX controller".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
AtxAction::Reset => {
|
||||
if let Some(executor) = &inner.reset_executor {
|
||||
executor.pulse(timing::RESET_PRESS).await?;
|
||||
} else {
|
||||
return Err(AppError::Config(
|
||||
"Reset button not configured for ATX controller".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Trigger a short power button press
|
||||
pub async fn power_short(&self) -> Result<()> {
|
||||
self.trigger_power_action(AtxAction::Short).await
|
||||
}
|
||||
|
||||
/// Trigger a long power button press
|
||||
pub async fn power_long(&self) -> Result<()> {
|
||||
self.trigger_power_action(AtxAction::Long).await
|
||||
}
|
||||
|
||||
/// Trigger a reset button press
|
||||
pub async fn reset(&self) -> Result<()> {
|
||||
self.trigger_power_action(AtxAction::Reset).await
|
||||
}
|
||||
|
||||
/// Get the current power status using the LED sensor (if configured)
|
||||
pub async fn power_status(&self) -> PowerStatus {
|
||||
let inner = self.inner.read().await;
|
||||
|
||||
if let Some(sensor) = &inner.led_sensor {
|
||||
match sensor.read().await {
|
||||
Ok(status) => status,
|
||||
Err(e) => {
|
||||
debug!("Failed to read ATX LED sensor: {}", e);
|
||||
PowerStatus::Unknown
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PowerStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a snapshot of the ATX state for API responses
|
||||
pub async fn state(&self) -> AtxState {
|
||||
let inner = self.inner.read().await;
|
||||
|
||||
let power_status = if let Some(sensor) = inner.led_sensor.as_ref() {
|
||||
sensor.read().await.unwrap_or(PowerStatus::Unknown)
|
||||
let power_status = if let Some(sensor) = &inner.led_sensor {
|
||||
match sensor.read().await {
|
||||
Ok(status) => status,
|
||||
Err(e) => {
|
||||
debug!("Failed to read ATX LED sensor: {}", e);
|
||||
PowerStatus::Unknown
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PowerStatus::Unknown
|
||||
};
|
||||
|
||||
AtxState {
|
||||
available: inner.config.enabled,
|
||||
power_configured: inner
|
||||
.power_executor
|
||||
.as_ref()
|
||||
.map(|e| e.is_initialized())
|
||||
.unwrap_or(false),
|
||||
reset_configured: inner
|
||||
.reset_executor
|
||||
.as_ref()
|
||||
.map(|e| e.is_initialized())
|
||||
.unwrap_or(false),
|
||||
power_configured: inner.power_executor.is_some(),
|
||||
reset_configured: inner.reset_executor.is_some(),
|
||||
power_status,
|
||||
led_supported: inner
|
||||
.led_sensor
|
||||
.as_ref()
|
||||
.map(|s| s.is_initialized())
|
||||
.unwrap_or(false),
|
||||
led_supported: inner.led_sensor.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current state as SystemEvent
|
||||
pub async fn current_state_event(&self) -> crate::events::SystemEvent {
|
||||
let state = self.state().await;
|
||||
crate::events::SystemEvent::AtxStateChanged {
|
||||
power_status: state.power_status,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if ATX is available
|
||||
pub async fn is_available(&self) -> bool {
|
||||
let inner = self.inner.read().await;
|
||||
inner.config.enabled
|
||||
}
|
||||
|
||||
/// Check if power button is configured and initialized
|
||||
pub async fn is_power_ready(&self) -> bool {
|
||||
let inner = self.inner.read().await;
|
||||
inner
|
||||
.power_executor
|
||||
.as_ref()
|
||||
.map(|e| e.is_initialized())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if reset button is configured and initialized
|
||||
pub async fn is_reset_ready(&self) -> bool {
|
||||
let inner = self.inner.read().await;
|
||||
inner
|
||||
.reset_executor
|
||||
.as_ref()
|
||||
.map(|e| e.is_initialized())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Short press power button (turn on or graceful shutdown)
|
||||
pub async fn power_short(&self) -> Result<()> {
|
||||
let inner = self.inner.read().await;
|
||||
let executor = inner
|
||||
.power_executor
|
||||
.as_ref()
|
||||
.ok_or_else(|| AppError::Internal("Power button not configured".to_string()))?;
|
||||
|
||||
info!(
|
||||
"ATX: Short press power button ({}ms)",
|
||||
timing::SHORT_PRESS.as_millis()
|
||||
);
|
||||
executor.pulse(timing::SHORT_PRESS).await
|
||||
}
|
||||
|
||||
/// Long press power button (force power off)
|
||||
pub async fn power_long(&self) -> Result<()> {
|
||||
let inner = self.inner.read().await;
|
||||
let executor = inner
|
||||
.power_executor
|
||||
.as_ref()
|
||||
.ok_or_else(|| AppError::Internal("Power button not configured".to_string()))?;
|
||||
|
||||
info!(
|
||||
"ATX: Long press power button ({}ms)",
|
||||
timing::LONG_PRESS.as_millis()
|
||||
);
|
||||
executor.pulse(timing::LONG_PRESS).await
|
||||
}
|
||||
|
||||
/// Press reset button
|
||||
pub async fn reset(&self) -> Result<()> {
|
||||
let inner = self.inner.read().await;
|
||||
let executor = inner
|
||||
.reset_executor
|
||||
.as_ref()
|
||||
.ok_or_else(|| AppError::Internal("Reset button not configured".to_string()))?;
|
||||
|
||||
info!(
|
||||
"ATX: Press reset button ({}ms)",
|
||||
timing::RESET_PRESS.as_millis()
|
||||
);
|
||||
executor.pulse(timing::RESET_PRESS).await
|
||||
}
|
||||
|
||||
/// Get current power status from LED sensor
|
||||
pub async fn power_status(&self) -> Result<PowerStatus> {
|
||||
let inner = self.inner.read().await;
|
||||
match inner.led_sensor.as_ref() {
|
||||
Some(sensor) => sensor.read().await,
|
||||
None => Ok(PowerStatus::Unknown),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shutdown the ATX controller
|
||||
pub async fn shutdown(&self) -> Result<()> {
|
||||
info!("Shutting down ATX controller");
|
||||
self.shutdown_internal().await?;
|
||||
info!("ATX controller shutdown complete");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Internal shutdown helper
|
||||
async fn shutdown_internal(&self) -> Result<()> {
|
||||
let mut inner = self.inner.write().await;
|
||||
|
||||
// Shutdown power executor
|
||||
if let Some(mut executor) = inner.power_executor.take() {
|
||||
executor.shutdown().await.ok();
|
||||
}
|
||||
|
||||
// Shutdown reset executor
|
||||
if let Some(mut executor) = inner.reset_executor.take() {
|
||||
executor.shutdown().await.ok();
|
||||
}
|
||||
|
||||
// Shutdown LED sensor
|
||||
if let Some(mut sensor) = inner.led_sensor.take() {
|
||||
sensor.shutdown().await.ok();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AtxController {
|
||||
fn drop(&mut self) {
|
||||
debug!("ATX controller dropped");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_controller_config_default() {
|
||||
let config = AtxControllerConfig::default();
|
||||
assert!(!config.enabled);
|
||||
assert!(!config.power.is_configured());
|
||||
assert!(!config.reset.is_configured());
|
||||
assert!(!config.led.is_configured());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_controller_creation() {
|
||||
let controller = AtxController::disabled();
|
||||
assert!(controller.inner.try_read().is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_controller_disabled_state() {
|
||||
let controller = AtxController::disabled();
|
||||
let state = controller.state().await;
|
||||
assert!(!state.available);
|
||||
assert!(!state.power_configured);
|
||||
assert!(!state.reset_configured);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_controller_init_disabled() {
|
||||
let controller = AtxController::disabled();
|
||||
let result = controller.init().await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_controller_is_available() {
|
||||
let controller = AtxController::disabled();
|
||||
assert!(!controller.is_available().await);
|
||||
|
||||
let config = AtxControllerConfig {
|
||||
enabled: true,
|
||||
..Default::default()
|
||||
};
|
||||
let controller = AtxController::new(config);
|
||||
assert!(controller.is_available().await);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,10 +88,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_discover_devices() {
|
||||
let devices = discover_devices();
|
||||
// Just verify the function runs without error
|
||||
assert!(devices.gpio_chips.len() >= 0);
|
||||
assert!(devices.usb_relays.len() >= 0);
|
||||
let _devices = discover_devices();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
/// Power status
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PowerStatus {
|
||||
/// Power is on
|
||||
@@ -15,18 +15,13 @@ pub enum PowerStatus {
|
||||
/// Power is off
|
||||
Off,
|
||||
/// Power status unknown (no LED connected)
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Default for PowerStatus {
|
||||
fn default() -> Self {
|
||||
Self::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
/// Driver type for ATX key operations
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AtxDriverType {
|
||||
/// GPIO control via Linux character device
|
||||
@@ -34,36 +29,26 @@ pub enum AtxDriverType {
|
||||
/// USB HID relay module
|
||||
UsbRelay,
|
||||
/// Disabled / Not configured
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for AtxDriverType {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Active level for GPIO pins
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ActiveLevel {
|
||||
/// Active high (default for most cases)
|
||||
#[default]
|
||||
High,
|
||||
/// Active low (inverted)
|
||||
Low,
|
||||
}
|
||||
|
||||
impl Default for ActiveLevel {
|
||||
fn default() -> Self {
|
||||
Self::High
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for a single ATX key (power or reset)
|
||||
/// This is the "four-tuple" configuration: (driver, device, pin/channel, level)
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
#[serde(default)]
|
||||
pub struct AtxKeyConfig {
|
||||
/// Driver type (GPIO or USB Relay)
|
||||
@@ -80,17 +65,6 @@ pub struct AtxKeyConfig {
|
||||
pub active_level: ActiveLevel,
|
||||
}
|
||||
|
||||
impl Default for AtxKeyConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
driver: AtxDriverType::None,
|
||||
device: String::new(),
|
||||
pin: 0,
|
||||
active_level: ActiveLevel::High,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AtxKeyConfig {
|
||||
/// Check if this key is configured
|
||||
pub fn is_configured(&self) -> bool {
|
||||
@@ -100,7 +74,7 @@ impl AtxKeyConfig {
|
||||
|
||||
/// LED sensing configuration (optional)
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
#[serde(default)]
|
||||
pub struct AtxLedConfig {
|
||||
/// Whether LED sensing is enabled
|
||||
@@ -113,17 +87,6 @@ pub struct AtxLedConfig {
|
||||
pub inverted: bool,
|
||||
}
|
||||
|
||||
impl Default for AtxLedConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
gpio_chip: String::new(),
|
||||
gpio_pin: 0,
|
||||
inverted: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AtxLedConfig {
|
||||
/// Check if LED sensing is configured
|
||||
pub fn is_configured(&self) -> bool {
|
||||
@@ -132,7 +95,7 @@ impl AtxLedConfig {
|
||||
}
|
||||
|
||||
/// ATX state information
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct AtxState {
|
||||
/// Whether ATX feature is available/enabled
|
||||
pub available: bool,
|
||||
@@ -146,18 +109,6 @@ pub struct AtxState {
|
||||
pub led_supported: bool,
|
||||
}
|
||||
|
||||
impl Default for AtxState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
available: false,
|
||||
power_configured: false,
|
||||
reset_configured: false,
|
||||
power_status: PowerStatus::Unknown,
|
||||
led_supported: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ATX power action request
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct AtxPowerRequest {
|
||||
@@ -179,7 +130,7 @@ pub enum AtxAction {
|
||||
|
||||
/// Available ATX devices for discovery
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct AtxDevices {
|
||||
/// Available GPIO chips (/dev/gpiochip*)
|
||||
pub gpio_chips: Vec<String>,
|
||||
@@ -187,15 +138,6 @@ pub struct AtxDevices {
|
||||
pub usb_relays: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for AtxDevices {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gpio_chips: Vec::new(),
|
||||
usb_relays: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -266,5 +208,6 @@ mod tests {
|
||||
assert!(!state.power_configured);
|
||||
assert!(!state.reset_configured);
|
||||
assert_eq!(state.power_status, PowerStatus::Unknown);
|
||||
assert!(!state.led_supported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::error::{AppError, Result};
|
||||
/// WOL magic packet structure:
|
||||
/// - 6 bytes of 0xFF
|
||||
/// - 16 repetitions of the target MAC address (6 bytes each)
|
||||
/// Total: 6 + 16 * 6 = 102 bytes
|
||||
/// Total: 6 + 16 * 6 = 102 bytes
|
||||
const MAGIC_PACKET_SIZE: usize = 102;
|
||||
|
||||
/// Parse MAC address string into bytes
|
||||
@@ -160,8 +160,8 @@ mod tests {
|
||||
let packet = build_magic_packet(&mac);
|
||||
|
||||
// Check header (6 bytes of 0xFF)
|
||||
for i in 0..6 {
|
||||
assert_eq!(packet[i], 0xFF);
|
||||
for byte in packet.iter().take(6) {
|
||||
assert_eq!(*byte, 0xFF);
|
||||
}
|
||||
|
||||
// Check MAC repetitions
|
||||
|
||||
Reference in New Issue
Block a user