diff --git a/src/config.rs b/src/config.rs index 4ee0a0e..3ebde60 100644 --- a/src/config.rs +++ b/src/config.rs @@ -115,6 +115,7 @@ pub mod components { UdpServer = 7, TcpServer = 8, TcpSppClient = 9, + CameraHandler = 10, } pub const CONTROLLER_ID: UniqueApidTargetId = @@ -137,6 +138,8 @@ pub mod components { UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::TcpServer as u32); pub const TCP_SPP_CLIENT: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::TcpSppClient as u32); + pub const CAMERA_HANDLER: UniqueApidTargetId = + UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::CameraHandler as u32); } pub mod tasks { diff --git a/src/handlers/camera.rs b/src/handlers/camera.rs index 629bbd2..54e0b75 100644 --- a/src/handlers/camera.rs +++ b/src/handlers/camera.rs @@ -24,7 +24,6 @@ /// v Y /// /// see also https://opssat1.esoc.esa.int/dmsf/files/6/view - use crate::requests::CompositeRequest; use derive_new::new; use log::debug; @@ -37,6 +36,7 @@ use serde::{Deserialize, Serialize}; use std::io::Error; use std::process::Command; use std::sync::mpsc; +use satrs::pus::action::ActionReplyPus; const DEFAULT_SINGLE_CAM_PARAMS: CameraPictureParameters = CameraPictureParameters { R: 8, @@ -78,6 +78,10 @@ const BALANCED_SINGLE_FLATSAT_CAM_PARAMS: CameraPictureParameters = CameraPictur W: 1000, }; +// TODO copy as action +// TODO ls -l via cfdp +// TODO howto downlink + #[derive(Debug)] pub enum CameraActionIds { DefaultSingle = 1, @@ -126,19 +130,36 @@ pub struct CameraPictureParameters { } #[allow(dead_code)] -#[derive(new)] +#[derive(Debug)] pub struct IMS100BatchHandler { id: UniqueApidTargetId, // mode_interface: MpscModeLeafInterface, - composite_request_receiver: mpsc::Receiver>, + composite_request_rx: mpsc::Receiver>, // hk_reply_sender: mpsc::Sender>, - tm_sender: mpsc::Sender, + tm_tx: mpsc::Sender, + action_reply_tx: mpsc::Sender>, stamp_helper: TimeStampHelper, } #[allow(non_snake_case)] #[allow(dead_code)] impl IMS100BatchHandler { + pub fn new( + id: UniqueApidTargetId, + composite_request_rx: mpsc::Receiver>, + tm_tx: mpsc::Sender, + action_reply_tx: mpsc::Sender>, + stamp_helper: TimeStampHelper, + ) -> Self { + Self { + id, + composite_request_rx, + tm_tx, + action_reply_tx, + stamp_helper, + } + } + pub fn periodic_operation(&mut self) { self.stamp_helper.update_from_now(); // Handle requests. @@ -148,13 +169,13 @@ impl IMS100BatchHandler { pub fn handle_composite_requests(&mut self) { loop { - match self.composite_request_receiver.try_recv() { + match self.composite_request_rx.try_recv() { Ok(ref msg) => match &msg.message { CompositeRequest::Hk(hk_request) => { self.handle_hk_request(&msg.requestor_info, hk_request); } CompositeRequest::Action(action_request) => { - self.handle_action_request(&msg.requestor_info, action_request); + self.handle_action_request(&msg.requestor_info, action_request).unwrap(); } }, Err(_) => {} @@ -236,6 +257,21 @@ impl IMS100BatchHandler { Ok(()) } + pub fn list_current_images(&self) -> std::io::Result> { + let output = Command::new("ls").arg("-l") + .arg("*.png") + .output()?; + + if output.status.success() { + let output_str = String::from_utf8(output.stdout).unwrap(); + let files: Vec = output_str.lines().map(|s| s.to_string()).collect(); + Ok(files) + } + else { + Err(Error::other("Error getting file list.")) + } + } + pub fn take_picture_from_str( &mut self, R: &str, @@ -277,34 +313,40 @@ impl IMS100BatchHandler { #[cfg(test)] mod tests { + use crate::handlers::camera::{ + CameraActionIds, CameraPictureParameters, IMS100BatchHandler, + DEFAULT_SINGLE_FLATSAT_CAM_PARAMS, + }; + use crate::requests::CompositeRequest; + use ops_sat_rs::config::components::CAMERA_HANDLER; + use ops_sat_rs::TimeStampHelper; + use satrs::action::{ActionRequest, ActionRequestVariant}; + use satrs::request::{GenericMessage, MessageMetadata}; + use satrs::tmtc::PacketAsVec; use std::sync::mpsc; use std::sync::mpsc::{Receiver, Sender}; - use satrs::action::{ActionRequest, ActionRequestVariant}; - use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId}; - use satrs::tmtc::PacketAsVec; - use ops_sat_rs::TimeStampHelper; - use crate::handlers::camera::{CameraActionIds, CameraPictureParameters, DEFAULT_SINGLE_FLATSAT_CAM_PARAMS, IMS100BatchHandler}; - use crate::requests::CompositeRequest; + use satrs::pus::action::ActionReplyPus; - fn create_handler() -> (IMS100BatchHandler, Sender>, Receiver) { - let id = UniqueApidTargetId::new(1,1); - let (req_tx,req_rx) = mpsc::channel(); + fn create_handler() -> (IMS100BatchHandler, Sender>, Receiver, Receiver>) { + let (composite_request_tx, composite_request_rx) = mpsc::channel(); let (tm_tx, tm_rx) = mpsc::channel(); + let (action_reply_tx, action_reply_rx) = mpsc::channel(); let time_helper = TimeStampHelper::default(); - let mut cam_handler: IMS100BatchHandler = IMS100BatchHandler::new(id, req_rx, tm_tx, time_helper); - (cam_handler, req_tx, tm_rx) + let cam_handler: IMS100BatchHandler = + IMS100BatchHandler::new(CAMERA_HANDLER, composite_request_rx, tm_tx, action_reply_tx, time_helper); + (cam_handler, composite_request_tx, tm_rx, action_reply_rx) } #[test] fn command_line_execution() { - let (mut cam_handler, mut req_tx, mut tm_rx) = create_handler(); - cam_handler.take_picture(DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap(); + let (mut cam_handler, req_tx, tm_rx, action_reply_rx) = create_handler(); + cam_handler + .take_picture(DEFAULT_SINGLE_FLATSAT_CAM_PARAMS) + .unwrap(); } #[test] fn serialize_and_deserialize_command() { - let (mut cam_handler, mut req_tx, mut tm_rx) = create_handler(); - let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap(); println!("{}", data); let param: CameraPictureParameters = serde_json::from_str(&data).unwrap(); @@ -313,23 +355,33 @@ mod tests { #[test] fn test_action_req() { - let (mut cam_handler, mut req_tx, mut tm_rx) = create_handler(); + let (mut cam_handler, req_tx, tm_rx, action_reply_rx) = create_handler(); let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap(); - let req = ActionRequest::new(CameraActionIds::CustomParameters as u32, ActionRequestVariant::VecData(data.as_bytes().to_vec())); + let req = ActionRequest::new( + CameraActionIds::CustomParameters as u32, + ActionRequestVariant::VecData(data.as_bytes().to_vec()), + ); - cam_handler.handle_action_request(&MessageMetadata::new(1,1),&req).unwrap(); + cam_handler + .handle_action_request(&MessageMetadata::new(1, 1), &req) + .unwrap(); } #[test] fn test_action_req_channel() { - let (mut cam_handler, mut req_tx, mut tm_rx) = create_handler(); + let (mut cam_handler, req_tx, tm_rx, action_reply_rx) = create_handler(); let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap(); - let req = ActionRequest::new(CameraActionIds::CustomParameters as u32, ActionRequestVariant::VecData(data.as_bytes().to_vec())); + let req = ActionRequest::new( + CameraActionIds::CustomParameters as u32, + ActionRequestVariant::VecData(data.as_bytes().to_vec()), + ); let req = CompositeRequest::Action(req); - req_tx.send(GenericMessage::new(MessageMetadata::new(1,1), req)).unwrap(); + req_tx + .send(GenericMessage::new(MessageMetadata::new(1, 1), req)) + .unwrap(); cam_handler.periodic_operation(); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index d637744..014c607 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use satrs::spacepackets::time::TimeWriter; pub mod config; +#[derive(Debug)] pub struct TimeStampHelper { stamper: CdsTime, time_stamp: [u8; 7], diff --git a/src/main.rs b/src/main.rs index 4a629be..3db7543 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::{ }; use log::info; +use ops_sat_rs::config::components::CAMERA_HANDLER; use ops_sat_rs::config::{ components::{CONTROLLER_ID, TCP_SERVER, TCP_SPP_CLIENT, UDP_SERVER}, tasks::{FREQ_MS_CTRL, FREQ_MS_PUS_STACK}, @@ -13,7 +14,9 @@ use ops_sat_rs::config::{ }; use ops_sat_rs::config::{tasks::FREQ_MS_UDP_TMTC, OBSW_SERVER_ADDR, SERVER_PORT}; use satrs::hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer}; +use ops_sat_rs::TimeStampHelper; +use crate::handlers::camera::IMS100BatchHandler; use crate::tmtc::tc_source::TcSourceTaskDynamic; use crate::tmtc::tm_sink::TmFunnelDynamic; use crate::{controller::ExperimentController, pus::test::create_test_service}; @@ -62,12 +65,16 @@ fn main() { // let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel(); let (controller_composite_tx, controller_composite_rx) = mpsc::channel(); // let (controller_action_reply_tx, controller_action_reply_rx) = mpsc::channel(); + let (camera_composite_tx, camera_composite_rx) = mpsc::channel(); // Some request are targetable. This map is used to retrieve sender handles based on a target ID. let mut request_map = GenericRequestRouter::default(); request_map .composite_router_map .insert(CONTROLLER_ID.id(), controller_composite_tx); + request_map + .composite_router_map + .insert(CAMERA_HANDLER.id(), camera_composite_tx); let pus_router = PusTcMpscRouter { test_tc_sender: pus_test_tx, @@ -161,7 +168,7 @@ fn main() { let mut controller = ExperimentController::new( controller_composite_rx, - pus_action_reply_tx, + pus_action_reply_tx.clone(), stop_signal.clone(), ); @@ -173,6 +180,13 @@ fn main() { ) .expect("creating TCP SPP client failed"); + let timestamp_helper = TimeStampHelper::default(); + let mut camera_handler: IMS100BatchHandler = + IMS100BatchHandler::new(CAMERA_HANDLER, camera_composite_rx, tm_funnel_tx.clone(), pus_action_reply_tx.clone(), timestamp_helper); + + // Main Task Thread Definitions + + // Main Experiment Control Task info!("Starting CTRL task"); let ctrl_stop_signal = stop_signal.clone(); let jh_ctrl_thread = thread::Builder::new() @@ -186,6 +200,7 @@ fn main() { }) .unwrap(); + // TMTC and UDP Task info!("Starting TMTC and UDP task"); let tmtc_stop_signal = stop_signal.clone(); let jh_udp_tmtc = thread::Builder::new() @@ -203,6 +218,7 @@ fn main() { }) .unwrap(); + // TCP Server Task let tcp_server_stop_signal = stop_signal.clone(); info!("Starting TCP server task"); let jh_tcp_server = thread::Builder::new() @@ -218,6 +234,7 @@ fn main() { }) .unwrap(); + // TCP SPP Client Task // We could also move this to the existing TCP server thread, but we would have to adapt // the server code for this so we do not block anymore and we pause manually if both the client // and server are IDLE and have nothing to do.. @@ -236,6 +253,7 @@ fn main() { }) .unwrap(); + // TM Funnel Task info!("Starting TM funnel task"); let funnel_stop_signal = stop_signal.clone(); let jh_tm_funnel = thread::Builder::new() @@ -248,7 +266,8 @@ fn main() { }) .unwrap(); - info!("Starting PUS handlers thread"); + // PUS Handler Task + info!("Starting PUS handlers task"); let pus_stop_signal = stop_signal.clone(); let jh_pus_handler = thread::Builder::new() .name("ops-sat pus".to_string()) @@ -261,6 +280,20 @@ fn main() { }) .unwrap(); + // Camera Handler Task + info!("Starting camera handler task"); + let camera_stop_signal = stop_signal.clone(); + let jh_camera_handler = thread::Builder::new() + .name("ops-sat camera".to_string()) + .spawn(move || loop { + camera_handler.periodic_operation(); + if camera_stop_signal.load(std::sync::atomic::Ordering::Relaxed) { + break; + } + }) + .unwrap(); + + // Join Threads jh_ctrl_thread .join() .expect("Joining Controller thread failed"); @@ -279,4 +312,7 @@ fn main() { jh_pus_handler .join() .expect("Joining PUS handlers thread failed"); + jh_camera_handler + .join() + .expect("Joining camera handler thread failed"); } diff --git a/src/pus/action.rs b/src/pus/action.rs index f81d069..b87aebc 100644 --- a/src/pus/action.rs +++ b/src/pus/action.rs @@ -275,8 +275,8 @@ mod tests { use satrs::pus::test_util::{ TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1, }; - use satrs::pus::{TcInMemory, verification}; use satrs::pus::verification::test_util::TestVerificationReporter; + use satrs::pus::{verification, TcInMemory}; use satrs::request::MessageMetadata; use satrs::ComponentId; use satrs::{ @@ -429,7 +429,13 @@ mod tests { .verif_reporter() .check_next_is_acceptance_success(id, accepted_token.request_id()); self.pus_packet_tx - .send(EcssTcAndToken::new(TcInMemory::Vec(PacketAsVec::new(self.service.service_helper.id(), tc.to_vec().unwrap().into())), accepted_token)) + .send(EcssTcAndToken::new( + TcInMemory::Vec(PacketAsVec::new( + self.service.service_helper.id(), + tc.to_vec().unwrap().into(), + )), + accepted_token, + )) .unwrap(); } } @@ -713,4 +719,4 @@ mod tests { tmtc_err::REQUEST_TIMEOUT.raw() as u64, ); } -} \ No newline at end of file +}