refactor(control): extract pulse command helper to control/command.rs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-03-24 14:31:37 +08:00
parent 9194bd1dca
commit 628553f2b8
3 changed files with 59 additions and 55 deletions

50
src/control/command.rs Normal file
View File

@ -0,0 +1,50 @@
use crate::{
connection::{BatchSetPointValueReq, ConnectionManager, SetPointValueReqItem},
telemetry::ValueType,
};
use serde_json::json;
use std::sync::Arc;
use uuid::Uuid;
/// Write a pulse (high → delay → low) to a command point.
/// Returns Ok(()) on success, Err(msg) on any failure.
pub async fn send_pulse_command(
connection_manager: &Arc<ConnectionManager>,
point_id: Uuid,
value_type: Option<&ValueType>,
pulse_ms: u64,
) -> Result<(), String> {
let high = pulse_value(true, value_type);
let low = pulse_value(false, value_type);
let high_result = connection_manager
.write_point_values_batch(BatchSetPointValueReq {
items: vec![SetPointValueReqItem { point_id, value: high }],
})
.await?;
if !high_result.success {
return Err(format!("Pulse high write failed: {:?}", high_result.err_msg));
}
tokio::time::sleep(std::time::Duration::from_millis(pulse_ms)).await;
let low_result = connection_manager
.write_point_values_batch(BatchSetPointValueReq {
items: vec![SetPointValueReqItem { point_id, value: low }],
})
.await?;
if !low_result.success {
return Err(format!("Pulse low write failed: {:?}", low_result.err_msg));
}
Ok(())
}
fn pulse_value(high: bool, value_type: Option<&ValueType>) -> serde_json::Value {
match value_type {
Some(ValueType::Bool) => serde_json::Value::Bool(high),
_ => if high { json!(1) } else { json!(0) },
}
}

View File

@ -1,3 +1,4 @@
pub mod command;
pub mod engine; pub mod engine;
pub mod runtime; pub mod runtime;
pub mod validator; pub mod validator;

View File

@ -10,9 +10,7 @@ use uuid::Uuid;
use validator::Validate; use validator::Validate;
use crate::{ use crate::{
connection::{BatchSetPointValueReq, SetPointValueReqItem},
control::validator::{validate_manual_control, ControlAction}, control::validator::{validate_manual_control, ControlAction},
telemetry::ValueType,
util::{ util::{
pagination::{PaginatedResponse, PaginationParams}, pagination::{PaginatedResponse, PaginationParams},
response::ApiErr, response::ApiErr,
@ -73,46 +71,14 @@ async fn send_equipment_command(
let context = validate_manual_control(&state, equipment_id, action).await?; let context = validate_manual_control(&state, equipment_id, action).await?;
let pulse_ms = 300u64; let pulse_ms = 300u64;
let high_value = pulse_value(true, context.command_value_type.as_ref()); crate::control::command::send_pulse_command(
let low_value = pulse_value(false, context.command_value_type.as_ref()); &state.connection_manager,
context.command_point.point_id,
let high_result = state context.command_value_type.as_ref(),
.connection_manager pulse_ms,
.write_point_values_batch(BatchSetPointValueReq { )
items: vec![SetPointValueReqItem { .await
point_id: context.command_point.point_id, .map_err(|e| ApiErr::Internal(e, None))?;
value: high_value,
}],
})
.await
.map_err(|e| ApiErr::Internal(e, None))?;
if !high_result.success {
return Err(ApiErr::Internal(
"Failed to write pulse high level".to_string(),
Some(json!(high_result)),
));
}
tokio::time::sleep(std::time::Duration::from_millis(pulse_ms)).await;
let low_result = state
.connection_manager
.write_point_values_batch(BatchSetPointValueReq {
items: vec![SetPointValueReqItem {
point_id: context.command_point.point_id,
value: low_value,
}],
})
.await
.map_err(|e| ApiErr::Internal(e, None))?;
if !low_result.success {
return Err(ApiErr::Internal(
"Pulse reset failed after command high level succeeded".to_string(),
Some(json!(low_result)),
));
}
let event = match action { let event = match action {
ControlAction::Start => crate::event::AppEvent::EquipmentStartCommandSent { ControlAction::Start => crate::event::AppEvent::EquipmentStartCommandSent {
@ -138,19 +104,6 @@ async fn send_equipment_command(
}))) })))
} }
fn pulse_value(high: bool, value_type: Option<&ValueType>) -> serde_json::Value {
match value_type {
Some(ValueType::Bool) => serde_json::Value::Bool(high),
_ => {
if high {
json!(1)
} else {
json!(0)
}
}
}
}
pub async fn get_unit( pub async fn get_unit(
State(state): State<AppState>, State(state): State<AppState>,
Path(unit_id): Path<Uuid>, Path(unit_id): Path<Uuid>,