mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-23 11:26:45 +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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user