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:
parent
9194bd1dca
commit
628553f2b8
|
|
@ -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) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue