now only some tests are missing

This commit is contained in:
Robin Müller 2024-04-26 18:53:11 +02:00
parent c89db2e2d7
commit ec69c7d581
3 changed files with 217 additions and 54 deletions

View File

@ -227,6 +227,13 @@ pub mod ctrl_err {
pub const SHELL_CMD_EXECUTION_FAILURE: ResultU16 = ResultU16::new(GroupId::Controller as u8, 2); pub const SHELL_CMD_EXECUTION_FAILURE: ResultU16 = ResultU16::new(GroupId::Controller as u8, 2);
#[resultcode] #[resultcode]
pub const SHELL_CMD_INVALID_FORMAT: ResultU16 = ResultU16::new(GroupId::Controller as u8, 3); pub const SHELL_CMD_INVALID_FORMAT: ResultU16 = ResultU16::new(GroupId::Controller as u8, 3);
// TODO: Probably could be in a dedicated modules for these returnvalues.
#[resultcode]
pub const FILESYSTEM_COPY_ERROR: ResultU16 = ResultU16::new(GroupId::Controller as u8, 4);
#[resultcode]
pub const IMAGE_NOT_FOUND_FOR_COPY: ResultU16 = ResultU16::new(GroupId::Controller as u8, 5);
#[resultcode]
pub const INVALID_LOGFILE_PATH: ResultU16 = ResultU16::new(GroupId::Controller as u8, 6);
pub const CTRL_ERR_RESULTS: &[ResultU16Info] = &[ pub const CTRL_ERR_RESULTS: &[ResultU16Info] = &[
INVALID_CMD_FORMAT_EXT, INVALID_CMD_FORMAT_EXT,

View File

@ -1,9 +1,9 @@
use crate::logger::LOGFILE_PATH; use crate::logger::LOGFILE_PATH;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use ops_sat_rs::config::{ use ops_sat_rs::config::{
action_err::INVALID_ACTION_ID, HOME_FOLDER_EXPERIMENT, HOME_PATH, STOP_FILE_NAME, action_err::INVALID_ACTION_ID, HOME_FOLDER_EXPERIMENT, TO_GROUND_FOLDER_EXPERIMENT,
TO_GROUND_FOLDER_EXPERIMENT,
}; };
use ops_sat_rs::config::{HOME_PATH, STOP_FILE_NAME, TO_GROUND_LP_FOLDER_EXPERIMENT};
use satrs::action::ActionRequestVariant; use satrs::action::ActionRequestVariant;
use satrs::{ use satrs::{
action::ActionRequest, action::ActionRequest,
@ -13,14 +13,15 @@ use satrs::{
res_code::ResultU16, res_code::ResultU16,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::env::temp_dir;
use std::{ use std::{
env::temp_dir,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
sync::{atomic::AtomicBool, mpsc, Arc}, sync::{atomic::AtomicBool, mpsc, Arc},
}; };
use ops_sat_rs::config::ctrl_err::{ use ops_sat_rs::config::ctrl_err::{
FILESYSTEM_COPY_ERROR, IMAGE_NOT_FOUND_FOR_COPY, INVALID_LOGFILE_PATH,
SHELL_CMD_EXECUTION_FAILURE, SHELL_CMD_INVALID_FORMAT, SHELL_CMD_IO_ERROR, SHELL_CMD_EXECUTION_FAILURE, SHELL_CMD_INVALID_FORMAT, SHELL_CMD_IO_ERROR,
}; };
@ -41,12 +42,34 @@ pub enum ActionId {
ExecuteShellCommandBlocking = 4, ExecuteShellCommandBlocking = 4,
} }
#[derive(Debug)]
pub struct ControllerPathCollection {
pub stop_file_home_path: PathBuf,
pub stop_file_tmp_path: PathBuf,
pub to_ground_dir: PathBuf,
pub to_ground_low_prio_dir: PathBuf,
}
impl Default for ControllerPathCollection {
fn default() -> Self {
let mut home_path_stop_file = PathBuf::new();
home_path_stop_file.push(HOME_PATH.as_path());
home_path_stop_file.push(STOP_FILE_NAME);
let mut tmp_path_stop_file = temp_dir();
tmp_path_stop_file.push(STOP_FILE_NAME);
Self {
stop_file_home_path: home_path_stop_file,
stop_file_tmp_path: tmp_path_stop_file,
to_ground_dir: PathBuf::from(TO_GROUND_FOLDER_EXPERIMENT),
to_ground_low_prio_dir: PathBuf::from(TO_GROUND_LP_FOLDER_EXPERIMENT),
}
}
}
pub struct ExperimentController { pub struct ExperimentController {
pub composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>, pub composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
pub action_reply_tx: mpsc::Sender<GenericMessage<ActionReplyPus>>, pub action_reply_tx: mpsc::Sender<GenericMessage<ActionReplyPus>>,
pub stop_signal: Arc<AtomicBool>, pub stop_signal: Arc<AtomicBool>,
home_path_stop_file: PathBuf, pub paths: ControllerPathCollection,
tmp_path_stop_file: PathBuf,
} }
impl ExperimentController { impl ExperimentController {
@ -54,18 +77,13 @@ impl ExperimentController {
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>, composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
action_reply_tx: mpsc::Sender<GenericMessage<ActionReplyPus>>, action_reply_tx: mpsc::Sender<GenericMessage<ActionReplyPus>>,
stop_signal: Arc<AtomicBool>, stop_signal: Arc<AtomicBool>,
paths: ControllerPathCollection,
) -> Self { ) -> Self {
let mut home_path_stop_file = PathBuf::new();
home_path_stop_file.push(HOME_PATH.as_path());
home_path_stop_file.push(STOP_FILE_NAME);
let mut tmp_path_stop_file = temp_dir();
tmp_path_stop_file.push(STOP_FILE_NAME);
Self { Self {
composite_request_rx, composite_request_rx,
action_reply_tx, action_reply_tx,
stop_signal, stop_signal,
home_path_stop_file, paths,
tmp_path_stop_file,
} }
} }
} }
@ -117,45 +135,77 @@ impl ExperimentController {
} }
ActionId::DownlinkLogfile => self.handle_downlink_logfile(&requestor, &action_req), ActionId::DownlinkLogfile => self.handle_downlink_logfile(&requestor, &action_req),
// downlink images, default will be the last image, otherwise specified counting down (2 = second to last image, etc.) // downlink images, default will be the last image, otherwise specified counting down (2 = second to last image, etc.)
ActionId::DownlinkImages => { ActionId::DownlinkImages => self.handle_downlink_cam_image(&requestor, &action_req),
log::info!("Copying images into low priority downlink folder"); }
if let Ok(image_path) = match action_req.variant { }
ActionRequestVariant::VecData(data) => {
let index = data[0]; pub fn handle_downlink_cam_image(
get_latest_image(index as usize) &self,
} requestor: &MessageMetadata,
_ => get_latest_image(0), action_req: &ActionRequest,
} { ) {
if let Ok(image_path) = image_path.clone() log::info!("copying images into low priority downlink folder");
.into_os_string() let image_path_result = match &action_req.variant {
.into_string() ActionRequestVariant::VecData(data) => {
{ let index = data[0];
if std::fs::copy(image_path, TO_GROUND_FOLDER_EXPERIMENT).is_err() { get_latest_image(index as usize)
log::error!("Copying logfile into downlink path failed") }
} _ => get_latest_image(0),
} };
} match image_path_result {
Ok(image_path) => self.handle_file_copy(
requestor,
action_req,
&image_path,
&self.paths.to_ground_dir,
),
Err(e) => {
log::warn!("could not retrieve image path: {}", e);
self.send_completion_failure(
requestor,
action_req,
IMAGE_NOT_FOUND_FOR_COPY,
Some(e.to_string().into()),
);
} }
} }
} }
pub fn handle_downlink_logfile(&self, requestor: &MessageMetadata, action_req: &ActionRequest) { pub fn handle_downlink_logfile(&self, requestor: &MessageMetadata, action_req: &ActionRequest) {
log::info!("copying logfile into downlink folder"); log::info!("copying logfile into {:?}", self.paths.to_ground_dir);
if let Some(logfile_path) = LOGFILE_PATH.get() { if let Some(logfile_path) = LOGFILE_PATH.get() {
if let Ok(logfile_path) = <PathBuf as Clone>::clone(logfile_path) self.handle_file_copy(
.into_os_string() requestor,
.into_string() action_req,
{ logfile_path,
if std::fs::copy(logfile_path.as_str(), TO_GROUND_FOLDER_EXPERIMENT).is_err() { &self.paths.to_ground_dir,
log::warn!("copying logfile into downlink path failed") )
}
self.send_completion_success(requestor, action_req)
}
} else { } else {
log::warn!("downlink path emtpy") log::error!("downlink path emtpy");
self.send_completion_failure(requestor, action_req, INVALID_LOGFILE_PATH, None);
} }
} }
pub fn handle_file_copy(
&self,
requestor: &MessageMetadata,
action_req: &ActionRequest,
source_path: &Path,
target_path: &Path,
) {
if let Err(e) = std::fs::copy(source_path, target_path) {
log::warn!("copying logfile into downlink path failed: {}", e);
self.send_completion_failure(
requestor,
action_req,
FILESYSTEM_COPY_ERROR,
Some(e.to_string().into()),
);
return;
}
self.send_completion_success(requestor, action_req)
}
pub fn send_completion_success(&self, requestor: &MessageMetadata, action_req: &ActionRequest) { pub fn send_completion_success(&self, requestor: &MessageMetadata, action_req: &ActionRequest) {
let result = self.action_reply_tx.send(GenericMessage::new_action_reply( let result = self.action_reply_tx.send(GenericMessage::new_action_reply(
*requestor, *requestor,
@ -257,8 +307,8 @@ impl ExperimentController {
.store(true, std::sync::atomic::Ordering::Relaxed); .store(true, std::sync::atomic::Ordering::Relaxed);
} }
}; };
check_at_path(self.tmp_path_stop_file.as_path()); check_at_path(self.paths.stop_file_tmp_path.as_path());
check_at_path(self.home_path_stop_file.as_path()); check_at_path(self.paths.stop_file_home_path.as_path());
} }
} }
@ -293,6 +343,7 @@ pub fn get_latest_image(index: usize) -> Result<PathBuf, std::io::Error> {
} }
Err(std::io::Error::other("No latest image found")) Err(std::io::Error::other("No latest image found"))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::{mpsc, Arc}; use std::sync::{mpsc, Arc};
@ -304,15 +355,54 @@ mod tests {
fn init() { fn init() {
env_logger::builder().is_test(true).init(); env_logger::builder().is_test(true).init();
} }
pub struct ControllerTestbench {
pub composite_req_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
pub action_reply_rx: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
pub stop_signal: Arc<AtomicBool>,
pub ctrl: ExperimentController,
}
impl ControllerTestbench {
pub fn new() -> Self {
init();
let (composite_req_tx, composite_req_rx) = mpsc::channel();
let (action_reply_tx, action_reply_rx) = mpsc::channel();
let stop_signal = Arc::new(AtomicBool::new(false));
let test_tmp_dir = tempfile::tempdir().expect("creating tmpdir failed");
let base_dir = PathBuf::from(test_tmp_dir.path());
let mut stop_file_tmp_path = base_dir.clone();
stop_file_tmp_path.push(STOP_FILE_NAME);
let mut stop_file_home_path = base_dir.clone();
stop_file_home_path.push("home");
stop_file_home_path.push(STOP_FILE_NAME);
let mut to_ground_dir = base_dir.clone();
to_ground_dir.push("toGround");
let mut to_ground_low_prio_dir = base_dir.clone();
to_ground_low_prio_dir.push("toGroundLP");
let test_paths = ControllerPathCollection {
stop_file_home_path,
stop_file_tmp_path,
to_ground_dir,
to_ground_low_prio_dir,
};
ControllerTestbench {
composite_req_tx,
action_reply_rx,
stop_signal: stop_signal.clone(),
ctrl: ExperimentController::new(
composite_req_rx,
action_reply_tx,
stop_signal,
test_paths,
),
}
}
}
#[test] #[test]
fn test_shell_cmd_exection() { fn test_shell_cmd_exection() {
init(); let mut testbench = ControllerTestbench::new();
let (composite_req_tx, composite_req_rx) = mpsc::channel();
let (action_reply_tx, action_reply_rx) = mpsc::channel();
let stop_signal = Arc::default();
let mut exp_ctrl =
ExperimentController::new(composite_req_rx, action_reply_tx, stop_signal);
let named_temp_file = NamedTempFile::new().expect("creating temp file failed"); let named_temp_file = NamedTempFile::new().expect("creating temp file failed");
let args = vec![named_temp_file let args = vec![named_temp_file
.path() .path()
@ -325,15 +415,17 @@ mod tests {
action_id: ActionId::ExecuteShellCommandBlocking as u32, action_id: ActionId::ExecuteShellCommandBlocking as u32,
variant: satrs::action::ActionRequestVariant::VecData(cmd_serialized.into_bytes()), variant: satrs::action::ActionRequestVariant::VecData(cmd_serialized.into_bytes()),
}; };
composite_req_tx testbench
.composite_req_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(1, 2), MessageMetadata::new(1, 2),
CompositeRequest::Action(action_req), CompositeRequest::Action(action_req),
)) ))
.expect("sending action request failed"); .expect("sending action request failed");
exp_ctrl.perform_operation(); testbench.ctrl.perform_operation();
assert!(!named_temp_file.path().exists()); assert!(!named_temp_file.path().exists());
let action_reply = action_reply_rx let action_reply = testbench
.action_reply_rx
.try_recv() .try_recv()
.expect("receiving action reply failed"); .expect("receiving action reply failed");
assert_eq!( assert_eq!(
@ -351,3 +443,59 @@ mod tests {
} }
} }
} }
// Need to think about the value of this again. This is not easy to do in Rust..
/*
pub trait ActionHelperHook {
fn is_valid_action_id(&self, action_id: satrs::action::ActionId) -> bool;
fn send_reply(&self, action_reply: GenericActionReplyPus) -> Result<(), GenericSendError>;
}
pub struct ActionHelper<Hook: ActionHelperHook> {
pub requestor: MessageMetadata,
pub action_id: satrs::action::ActionId,
pub user_hook: Hook,
}
impl<Hook: ActionHelperHook> ActionHelper<Hook> {
fn new(
&mut self,
requestor: MessageMetadata,
action_id: satrs::action::ActionId,
) -> Result<Option<Self>, GenericSendError> {
if !self.user_hook.is_valid_action_id(action_id) {
self.report_completion_failed(INVALID_ACTION_ID, None)?;
return Ok(None);
}
Ok(Some(Self {
requestor,
action_id
}))
}
fn report_completion_success(&self) -> Result<(), GenericSendError> {
self.user_hook.send_reply(GenericMessage::new_action_reply(
self.requestor,
self.action_id,
ActionReplyVariant::Completed,
))?;
Ok(())
}
fn report_completion_failed(
&self,
error_code: ResultU16,
params: Option<Params>,
) -> Result<(), GenericSendError> {
self.user_hook.send_reply(GenericMessage::new_action_reply(
self.requestor,
self.action_id,
ActionReplyVariant::CompletionFailed { error_code, params },
))?;
Ok(())
}
}
*/

View File

@ -1,5 +1,7 @@
use std::{ use std::{
env::temp_dir,
net::{IpAddr, SocketAddr}, net::{IpAddr, SocketAddr},
path::PathBuf,
sync::{atomic::AtomicBool, mpsc, Arc}, sync::{atomic::AtomicBool, mpsc, Arc},
thread, thread,
time::Duration, time::Duration,
@ -12,7 +14,7 @@ use ops_sat_rs::config::{
create_low_priority_ground_dir, create_low_priority_ground_dir,
pool::create_sched_tc_pool, pool::create_sched_tc_pool,
tasks::{FREQ_MS_CAMERA_HANDLING, FREQ_MS_CTRL, FREQ_MS_PUS_STACK, STOP_CHECK_FREQUENCY}, tasks::{FREQ_MS_CAMERA_HANDLING, FREQ_MS_CTRL, FREQ_MS_PUS_STACK, STOP_CHECK_FREQUENCY},
VALID_PACKET_ID_LIST, VERSION, HOME_PATH, STOP_FILE_NAME, VALID_PACKET_ID_LIST, VERSION,
}; };
use ops_sat_rs::config::{components::CAMERA_HANDLER, tasks::FREQ_MS_EVENT_HANDLING}; use ops_sat_rs::config::{components::CAMERA_HANDLER, tasks::FREQ_MS_EVENT_HANDLING};
use ops_sat_rs::config::{tasks::FREQ_MS_UDP_TMTC, OBSW_SERVER_ADDR, SERVER_PORT}; use ops_sat_rs::config::{tasks::FREQ_MS_UDP_TMTC, OBSW_SERVER_ADDR, SERVER_PORT};
@ -22,7 +24,7 @@ use satrs::{
pus::event_man::EventRequestWithToken, pus::event_man::EventRequestWithToken,
}; };
use crate::tmtc::tm_sink::TmFunnelDynamic; use crate::{controller::ControllerPathCollection, tmtc::tm_sink::TmFunnelDynamic};
use crate::{controller::ExperimentController, pus::test::create_test_service}; use crate::{controller::ExperimentController, pus::test::create_test_service};
use crate::{ use crate::{
events::EventHandler, events::EventHandler,
@ -185,10 +187,16 @@ fn main() {
stop_signal.clone(), stop_signal.clone(),
); );
let mut home_path_stop_file = PathBuf::new();
home_path_stop_file.push(HOME_PATH.as_path());
home_path_stop_file.push(STOP_FILE_NAME);
let mut tmp_path_stop_file = temp_dir();
tmp_path_stop_file.push(STOP_FILE_NAME);
let mut controller = ExperimentController::new( let mut controller = ExperimentController::new(
controller_composite_rx, controller_composite_rx,
pus_action_reply_tx.clone(), pus_action_reply_tx.clone(),
stop_signal.clone(), stop_signal.clone(),
ControllerPathCollection::default(),
); );
let mut tcp_spp_client = TcpSppClientStd::new( let mut tcp_spp_client = TcpSppClientStd::new(