diff --git a/src/config.rs b/src/config.rs index 30e0614..ef11200 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,6 +38,7 @@ pub enum GroupId { Hk = 1, Mode = 2, Action = 3, + Controller = 4, } pub const TEST_EVENT: EventU32TypedSev = @@ -212,6 +213,27 @@ pub mod mode_err { pub const WRONG_MODE: ResultU16 = ResultU16::new(GroupId::Mode as u8, 0); } +pub mod ctrl_err { + use super::*; + use satrs::res_code::ResultU16; + + #[resultcode] + pub const INVALID_CMD_FORMAT: ResultU16 = ResultU16::new(GroupId::Controller as u8, 0); + #[resultcode] + pub const SHELL_CMD_IO_ERROR: ResultU16 = ResultU16::new(GroupId::Controller as u8, 1); + #[resultcode] + pub const SHELL_CMD_EXECUTION_FAILURE: ResultU16 = ResultU16::new(GroupId::Controller as u8, 2); + #[resultcode] + pub const SHELL_CMD_INVALID_FORMAT: ResultU16 = ResultU16::new(GroupId::Controller as u8, 3); + + pub const CTRL_ERR_RESULTS: &[ResultU16Info] = &[ + INVALID_CMD_FORMAT_EXT, + SHELL_CMD_IO_ERROR_EXT, + SHELL_CMD_EXECUTION_FAILURE_EXT, + SHELL_CMD_INVALID_FORMAT_EXT, + ]; +} + pub mod pool { use satrs::pool::{StaticMemoryPool, StaticPoolConfig}; diff --git a/src/controller.rs b/src/controller.rs index 69c27c8..a738c5b 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,16 +1,23 @@ use num_enum::TryFromPrimitive; use satrs::{ - action::ActionRequest, + action::{ActionRequest, ActionRequestVariant}, + params::Params, pus::action::{ActionReplyPus, ActionReplyVariant}, request::{GenericMessage, MessageMetadata}, + res_code::ResultU16, }; use std::{ env::temp_dir, path::{Path, PathBuf}, + process::Command, sync::{atomic::AtomicBool, mpsc, Arc}, }; -use ops_sat_rs::config::{action_err::INVALID_ACTION_ID, HOME_PATH, STOP_FILE_NAME}; +use ops_sat_rs::config::{ + action_err::INVALID_ACTION_ID, + ctrl_err::{SHELL_CMD_EXECUTION_FAILURE, SHELL_CMD_INVALID_FORMAT, SHELL_CMD_IO_ERROR}, + HOME_PATH, STOP_FILE_NAME, +}; use crate::requests::CompositeRequest; @@ -18,6 +25,7 @@ use crate::requests::CompositeRequest; #[repr(u32)] pub enum ActionId { StopExperiment = 1, + ExecuteShellCommandBlocking = 2, } pub struct ExperimentController { @@ -69,34 +77,104 @@ impl ExperimentController { self.check_stop_file(); } + pub fn send_completion_success(&self, requestor: &MessageMetadata, action_req: &ActionRequest) { + let result = self.action_reply_tx.send(GenericMessage::new_action_reply( + *requestor, + action_req.action_id, + ActionReplyVariant::Completed, + )); + if result.is_err() { + log::error!("sending action reply failed"); + } + } + + pub fn send_completion_failure( + &self, + requestor: &MessageMetadata, + action_req: &ActionRequest, + error_code: ResultU16, + params: Option, + ) { + let result = self.action_reply_tx.send(GenericMessage::new_action_reply( + *requestor, + action_req.action_id, + ActionReplyVariant::CompletionFailed { error_code, params }, + )); + if result.is_err() { + log::error!("sending action reply failed"); + } + } + pub fn handle_action_request(&mut self, requestor: MessageMetadata, action_req: ActionRequest) { - let action_id = ActionId::try_from(action_req.action_id); - if action_id.is_err() { + let send_completion_failure = |error_code: ResultU16, params: Option| { let result = self.action_reply_tx.send(GenericMessage::new_action_reply( requestor, action_req.action_id, - ActionReplyVariant::CompletionFailed { - error_code: INVALID_ACTION_ID, - params: None, - }, + ActionReplyVariant::CompletionFailed { error_code, params }, )); if result.is_err() { log::error!("sending action reply failed"); } + }; + let action_id = ActionId::try_from(action_req.action_id); + if action_id.is_err() { + send_completion_failure(INVALID_ACTION_ID, None); return; } - let action_id = action_id.unwrap(); - match action_id { + match action_id.unwrap() { ActionId::StopExperiment => { self.stop_signal .store(true, std::sync::atomic::Ordering::Relaxed); - let result = self.action_reply_tx.send(GenericMessage::new_action_reply( - requestor, - action_req.action_id, - ActionReplyVariant::Completed, - )); - if result.is_err() { - log::error!("sending action reply failed"); + self.send_completion_success(&requestor, &action_req); + } + ActionId::ExecuteShellCommandBlocking => { + self.handle_shell_command_execution(&requestor, &action_req); + } + } + } + + pub fn handle_shell_command_execution( + &self, + requestor: &MessageMetadata, + action_req: &ActionRequest, + ) { + if let ActionRequestVariant::VecData(data) = &action_req.variant { + match String::from_utf8(data.to_vec()) { + Ok(cmd_str) => { + log::info!("executing command: {}", cmd_str); + match Command::new(cmd_str).status() { + Ok(status) => { + if status.success() { + self.send_completion_success(requestor, action_req); + } else { + log::warn!("execution of command failed: {}", status); + self.send_completion_failure( + requestor, + action_req, + SHELL_CMD_EXECUTION_FAILURE, + Some(status.to_string().into()), + ); + } + } + Err(e) => { + log::warn!("execution of command failed with IO error: {}", e); + self.send_completion_failure( + requestor, + action_req, + SHELL_CMD_IO_ERROR, + Some(e.to_string().into()), + ); + } + } + } + Err(e) => { + log::warn!("invalid utf-8 data in action request: {}", e); + self.send_completion_failure( + requestor, + action_req, + SHELL_CMD_INVALID_FORMAT, + Some(e.to_string().into()), + ); } } }