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 runtime;
pub mod validator;

View File

@ -10,9 +10,7 @@ use uuid::Uuid;
use validator::Validate;
use crate::{
connection::{BatchSetPointValueReq, SetPointValueReqItem},
control::validator::{validate_manual_control, ControlAction},
telemetry::ValueType,
util::{
pagination::{PaginatedResponse, PaginationParams},
response::ApiErr,
@ -73,47 +71,15 @@ async fn send_equipment_command(
let context = validate_manual_control(&state, equipment_id, action).await?;
let pulse_ms = 300u64;
let high_value = pulse_value(true, context.command_value_type.as_ref());
let low_value = pulse_value(false, context.command_value_type.as_ref());
let high_result = state
.connection_manager
.write_point_values_batch(BatchSetPointValueReq {
items: vec![SetPointValueReqItem {
point_id: context.command_point.point_id,
value: high_value,
}],
})
crate::control::command::send_pulse_command(
&state.connection_manager,
context.command_point.point_id,
context.command_value_type.as_ref(),
pulse_ms,
)
.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 {
ControlAction::Start => crate::event::AppEvent::EquipmentStartCommandSent {
equipment_id,
@ -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(
State(state): State<AppState>,
Path(unit_id): Path<Uuid>,