diff --git a/src/control/command.rs b/src/control/command.rs new file mode 100644 index 0000000..cd40d37 --- /dev/null +++ b/src/control/command.rs @@ -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, + 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) }, + } +} diff --git a/src/control/mod.rs b/src/control/mod.rs index 77d9fe5..d4bb8c9 100644 --- a/src/control/mod.rs +++ b/src/control/mod.rs @@ -1,3 +1,4 @@ +pub mod command; pub mod engine; pub mod runtime; pub mod validator; diff --git a/src/handler/control.rs b/src/handler/control.rs index b12ff04..6562ba2 100644 --- a/src/handler/control.rs +++ b/src/handler/control.rs @@ -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,46 +71,14 @@ 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, - }], - }) - .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)), - )); - } + 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))?; let event = match action { 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( State(state): State, Path(unit_id): Path,