diff --git a/Cargo.lock b/Cargo.lock index 55bea81..23f9bab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -304,6 +304,95 @@ dependencies = [ "log", ] +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.59", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -326,6 +415,20 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "homedir" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22074da8bba2ef26fc1737ae6c777b5baab5524c2dc403b5c6a76166766ccda5" +dependencies = [ + "cfg-if", + "nix", + "serde", + "widestring", + "windows-sys 0.48.0", + "wmi", +] + [[package]] name = "humantime" version = "2.1.0" @@ -410,6 +513,15 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "mio" version = "0.8.11" @@ -422,6 +534,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -482,6 +607,7 @@ dependencies = [ "derive-new", "env_logger", "fern", + "homedir", "humantime", "lazy_static", "log", @@ -514,6 +640,18 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -693,6 +831,15 @@ dependencies = [ "serde", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "0.6.14" @@ -893,6 +1040,24 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-implement", + "windows-interface", + "windows-targets 0.52.5", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -902,6 +1067,28 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.59", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.59", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1050,6 +1237,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "wmi" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f0a4062ca522aad4705a2948fd4061b3857537990202a8ddd5af21607f79a" +dependencies = [ + "chrono", + "futures", + "log", + "serde", + "thiserror", + "windows", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 46ab7bf..9626af7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ num_enum = "0.7" serde = "1" serde_json = "1" mio = "0.8" +homedir = "0.2" [dependencies.satrs] version = "0.2.0-rc.3" diff --git a/pytmtc/camera_params.py b/pytmtc/camera_params.py new file mode 100644 index 0000000..2562372 --- /dev/null +++ b/pytmtc/camera_params.py @@ -0,0 +1,31 @@ +import struct +from serde import Model, fields + +from common import EXPERIMENT_APID, UniqueId, make_addressable_id + +class CameraParameters(Model): + R: fields.Int() + G: fields.Int() + B: fields.Int() + N: fields.Int() + P: fields.Bool() + E: fields.Int() + W: fields.Int() + + def serialize_for_uplink(self) -> bytearray: + return self.to_json().encode('utf-8') + +# Example serialization +data = bytearray(make_addressable_id(EXPERIMENT_APID, UniqueId.CameraHandler)) +params = CameraParameters(8, 8, 8, 1, True, 200, 1000) +serialized = params.to_json().encode('utf-8') +byte_string = bytearray(struct.pack('!{}s'.format(len(serialized)), serialized)) +print(byte_string) +print(params.serialize_for_uplink()) +data.extend(params.serialize_for_uplink()) +print(data) + +# Example deserialization +data = '{"R": 100, "G": 150, "B": 200, "N": 3, "P": true, "E": 10, "W": 20}' +deserialized_params = CameraParameters.from_json(data) +print(deserialized_params) diff --git a/pytmtc/common.py b/pytmtc/common.py index 56b469f..b8a98dc 100644 --- a/pytmtc/common.py +++ b/pytmtc/common.py @@ -3,13 +3,29 @@ from __future__ import annotations import dataclasses import enum import struct - +from serde import Model, fields EXPERIMENT_ID = 278 EXPERIMENT_APID = 1024 + EXPERIMENT_ID +class UniqueId(enum.IntEnum): + + Controller = 0 + PusEventManagement = 1 + PusRouting = 2 + PusTest = 3 + PusAction = 4 + PusMode = 5 + PusHk = 6 + UdpServer = 7 + TcpServer = 8 + TcpSppClient = 9 + CameraHandler = 10 + + class EventSeverity(enum.IntEnum): + INFO = 0 LOW = 1 MEDIUM = 2 @@ -43,6 +59,12 @@ class AcsHkIds(enum.IntEnum): def make_addressable_id(target_id: int, unique_id: int) -> bytes: - byte_string = bytearray(struct.pack("!I", target_id)) - byte_string.extend(struct.pack("!I", unique_id)) + byte_string = bytearray(struct.pack("!I", unique_id)) + # byte_string = bytearray(struct.pack("!I", target_id)) + # byte_string.extend(struct.pack("!I", unique_id)) return byte_string + +def make_addressable_id_with_action_id(unique_id: int, action_id: int) -> bytes: + byte_string = bytearray(struct.pack("!I", unique_id)) + byte_string.extend(struct.pack("!I", action_id)) + return byte_string \ No newline at end of file diff --git a/pytmtc/pus_tc.py b/pytmtc/pus_tc.py index 3a8e83d..136d311 100644 --- a/pytmtc/pus_tc.py +++ b/pytmtc/pus_tc.py @@ -9,6 +9,10 @@ from tmtccmd.pus.tc.s200_fsfw_mode import Mode from tmtccmd.tmtc import DefaultPusQueueHelper from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice +from serde import Model, fields + +from camera_params import CameraParameters +from common import EXPERIMENT_APID, UniqueId, make_addressable_id, make_addressable_id_with_action_id _LOGGER = logging.getLogger(__name__) @@ -65,6 +69,38 @@ def create_cmd_definition_tree() -> CmdTreeNode: ) root_node.add_child(scheduler_node) + action_node = CmdTreeNode("action", "Action Node") + cam_node = CmdTreeNode( + "take_image", "Take Image with IMS Imager" + ) + cam_node.add_child( + CmdTreeNode( + "default_single", "Default Single Image Camera Parameters" + ) + ) + cam_node.add_child( + CmdTreeNode( + "balanced_single", "Balanced Single Image Camera Parameters" + ) + ) + cam_node.add_child( + CmdTreeNode( + "default_single_flatsat", "Default Single Image Camera Parameters for use on FlatSat" + ) + ) + cam_node.add_child( + CmdTreeNode( + "balanced_single_flatsat", "Balanced Single Image Camera Parameters for use on FlatSat" + ) + ) + cam_node.add_child( + CmdTreeNode( + "custom_params", "Custom Camera Parameters as specified from file" + ) + ) + action_node.add_child(cam_node) + root_node.add_child(action_node) + return root_node @@ -97,6 +133,26 @@ def pack_pus_telecommands(q: DefaultPusQueueHelper, cmd_path: str): ) if cmd_path_list[0] == "acs": assert len(cmd_path_list) >= 2 + if cmd_path_list[0] == "action": + assert len(cmd_path_list)>= 2 + if cmd_path_list[1] == "take_image": + assert len(cmd_path_list)>= 3 + q.add_log_cmd("Sending PUS take image action request with " + cmd_path_list[2] + " params.") + if cmd_path_list[2] == "default_single": + data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 1) + if cmd_path_list[2] == "balanced_single": + data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 2) + if cmd_path_list[2] == "default_single_flatsat": + data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 3) + if cmd_path_list[2] == "balanced_single_flatsat": + data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 4) + if cmd_path_list[2] == "custom": + data = make_addressable_id_with_action_id(UniqueId.CameraHandler, 5) + params = CameraParameters(8, 8, 8, 1, True, 200, 1000) + bytes = params.serialize_for_uplink() + data.extend(bytes) + print(data.hex(sep=",")) + return q.add_pus_tc(PusTelecommand(service=8, subservice=128, apid=EXPERIMENT_APID, app_data=data)) def handle_set_mode_cmd( diff --git a/pytmtc/tc_definitions.py b/pytmtc/tc_definitions.py index 74fbff8..acd2741 100644 --- a/pytmtc/tc_definitions.py +++ b/pytmtc/tc_definitions.py @@ -35,4 +35,12 @@ def tc_definitions() -> TmtcDefinitionWrapper: info="PUS Service 11 TC Scheduling", op_code_entry=srv_11, ) + srv_8 = OpCodeEntry() + srv_8.add("pic", "Action Request Image") + defs.add_service( + name=CoreServiceList.SERVICE_8, + info="PUS Service 8 Action", + op_code_entry=srv_8, + + ) return defs diff --git a/scripts/ims100_testapp b/scripts/ims100_testapp new file mode 100644 index 0000000..956a66b Binary files /dev/null and b/scripts/ims100_testapp differ diff --git a/src/config.rs b/src/config.rs index 3ebde60..a20e021 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,7 @@ use std::net::Ipv4Addr; use std::path::{Path, PathBuf}; pub const STOP_FILE_NAME: &str = "stop-experiment"; -pub const HOME_FOLER_EXPERIMENT: &str = "/home/exp278"; +pub const HOME_FOLDER_EXPERIMENT: &str = "/home/exp278"; pub const LOG_FOLDER: &str = "logs"; pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED; @@ -40,12 +40,13 @@ pub enum GroupId { lazy_static! { pub static ref HOME_PATH: PathBuf = { - let home_path_default = env::var("HOME").expect("HOME env variable not set"); let mut home_path = PathBuf::new(); - home_path.push(if Path::new(HOME_FOLER_EXPERIMENT).exists() { - HOME_FOLER_EXPERIMENT + let mut home_path_default = homedir::get_my_home().expect("Getting home dir from OS failed.").expect("No home dir found."); + + home_path.push(if Path::new(HOME_FOLDER_EXPERIMENT).exists() { + HOME_FOLDER_EXPERIMENT } else { - &home_path_default + home_path_default.to_str().expect("Error converting to string.") }); home_path }; diff --git a/src/handlers/camera.rs b/src/handlers/camera.rs index 54e0b75..11d6b20 100644 --- a/src/handlers/camera.rs +++ b/src/handlers/camera.rs @@ -36,7 +36,9 @@ use serde::{Deserialize, Serialize}; use std::io::Error; use std::process::Command; use std::sync::mpsc; -use satrs::pus::action::ActionReplyPus; +use satrs::pus::action::{ActionReplyPus, ActionReplyVariant}; + +const IMS_TESTAPP: &str = "scripts/ims100_testapp"; const DEFAULT_SINGLE_CAM_PARAMS: CameraPictureParameters = CameraPictureParameters { R: 8, @@ -83,7 +85,7 @@ const BALANCED_SINGLE_FLATSAT_CAM_PARAMS: CameraPictureParameters = CameraPictur // TODO howto downlink #[derive(Debug)] -pub enum CameraActionIds { +pub enum CameraActionId { DefaultSingle = 1, BalancedSingle = 2, DefaultSingleFlatSat = 3, @@ -91,25 +93,25 @@ pub enum CameraActionIds { CustomParameters = 5, } -impl TryFrom for CameraActionIds { +impl TryFrom for CameraActionId { type Error = (); fn try_from(value: u32) -> Result { match value { - value if value == CameraActionIds::DefaultSingle as u32 => { - Ok(CameraActionIds::DefaultSingle) + value if value == CameraActionId::DefaultSingle as u32 => { + Ok(CameraActionId::DefaultSingle) } - value if value == CameraActionIds::BalancedSingle as u32 => { - Ok(CameraActionIds::BalancedSingle) + value if value == CameraActionId::BalancedSingle as u32 => { + Ok(CameraActionId::BalancedSingle) } - value if value == CameraActionIds::DefaultSingleFlatSat as u32 => { - Ok(CameraActionIds::DefaultSingleFlatSat) + value if value == CameraActionId::DefaultSingleFlatSat as u32 => { + Ok(CameraActionId::DefaultSingleFlatSat) } - value if value == CameraActionIds::BalancedSingleFlatSat as u32 => { - Ok(CameraActionIds::BalancedSingleFlatSat) + value if value == CameraActionId::BalancedSingleFlatSat as u32 => { + Ok(CameraActionId::BalancedSingleFlatSat) } - value if value == CameraActionIds::CustomParameters as u32 => { - Ok(CameraActionIds::CustomParameters) + value if value == CameraActionId::CustomParameters as u32 => { + Ok(CameraActionId::CustomParameters) } _ => Err(()), } @@ -193,15 +195,15 @@ impl IMS100BatchHandler { pub fn handle_action_request( &mut self, - _requestor_info: &MessageMetadata, + requestor_info: &MessageMetadata, action_request: &ActionRequest, ) -> std::io::Result<()> { - let param = match CameraActionIds::try_from(action_request.action_id).unwrap() { - CameraActionIds::DefaultSingle => DEFAULT_SINGLE_CAM_PARAMS, - CameraActionIds::BalancedSingle => BALANCED_SINGLE_CAM_PARAMS, - CameraActionIds::DefaultSingleFlatSat => DEFAULT_SINGLE_FLATSAT_CAM_PARAMS, - CameraActionIds::BalancedSingleFlatSat => BALANCED_SINGLE_FLATSAT_CAM_PARAMS, - CameraActionIds::CustomParameters => match &action_request.variant { + let param = match CameraActionId::try_from(action_request.action_id).expect("Invalid action id") { + CameraActionId::DefaultSingle => DEFAULT_SINGLE_CAM_PARAMS, + CameraActionId::BalancedSingle => BALANCED_SINGLE_CAM_PARAMS, + CameraActionId::DefaultSingleFlatSat => DEFAULT_SINGLE_FLATSAT_CAM_PARAMS, + CameraActionId::BalancedSingleFlatSat => BALANCED_SINGLE_FLATSAT_CAM_PARAMS, + CameraActionId::CustomParameters => match &action_request.variant { ActionRequestVariant::NoData => return Err(Error::other("No Data sent!")), ActionRequestVariant::StoreData(_) => { // let param = serde_json::from_slice() @@ -223,11 +225,13 @@ impl IMS100BatchHandler { _ => return Err(Error::other("Invalid Action Request Variant!")), }, }; - self.take_picture(param) + self.take_picture(param)?; + self.action_reply_tx.send(GenericMessage::new(*requestor_info, ActionReplyPus::new(action_request.action_id, ActionReplyVariant::Completed))).unwrap(); + Ok(()) } pub fn take_picture(&mut self, param: CameraPictureParameters) -> std::io::Result<()> { - let mut cmd = Command::new("ims100_testapp"); + let mut cmd = Command::new(IMS_TESTAPP); cmd.arg("-R") .arg(¶m.R.to_string()) .arg("-G") @@ -314,7 +318,7 @@ impl IMS100BatchHandler { #[cfg(test)] mod tests { use crate::handlers::camera::{ - CameraActionIds, CameraPictureParameters, IMS100BatchHandler, + CameraActionId, CameraPictureParameters, IMS100BatchHandler, DEFAULT_SINGLE_FLATSAT_CAM_PARAMS, }; use crate::requests::CompositeRequest; @@ -359,7 +363,7 @@ mod tests { let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap(); let req = ActionRequest::new( - CameraActionIds::CustomParameters as u32, + CameraActionId::CustomParameters as u32, ActionRequestVariant::VecData(data.as_bytes().to_vec()), ); @@ -374,7 +378,7 @@ mod tests { let data = serde_json::to_string(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS).unwrap(); let req = ActionRequest::new( - CameraActionIds::CustomParameters as u32, + CameraActionId::CustomParameters as u32, ActionRequestVariant::VecData(data.as_bytes().to_vec()), ); let req = CompositeRequest::Action(req); diff --git a/src/logger.rs b/src/logger.rs index 4df2e97..c9bafc4 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use ops_sat_rs::config::LOG_FOLDER; @@ -6,6 +6,9 @@ pub fn setup_logger() -> Result<(), fern::InitError> { if !Path::new(LOG_FOLDER).exists() && std::fs::create_dir_all(LOG_FOLDER).is_err() { eprintln!("Failed to create log folder '{}'", LOG_FOLDER); } + let mut path_buf = PathBuf::from(LOG_FOLDER); + path_buf.push(format!("output.log")); + println!("{:?}", path_buf); fern::Dispatch::new() .format(move |out, message, record| { out.finish(format_args!( @@ -18,11 +21,7 @@ pub fn setup_logger() -> Result<(), fern::InitError> { }) .level(log::LevelFilter::Debug) .chain(std::io::stdout()) - .chain(fern::log_file(format!( - "{}/output_{}.log", - LOG_FOLDER, - humantime::format_rfc3339_seconds(std::time::SystemTime::now()) - ))?) + .chain(fern::log_file(path_buf.as_os_str())?) .apply()?; Ok(()) }