use num_enum::{IntoPrimitive, TryFromPrimitive}; use once_cell::sync::OnceCell; use satrs::events::{EventU32TypedSev, SeverityInfo}; use satrs::res_code::ResultU16; use satrs::spacepackets::PacketId; use satrs_mib::res_code::ResultU16Info; use satrs_mib::resultcode; use std::net::Ipv4Addr; use std::path::{Path, PathBuf}; pub const STOP_FILE_NAME: &str = "stop-experiment"; pub const CONFIG_FILE_NAME: &str = "exp278.toml"; pub const HOME_FOLDER_EXPERIMENT: &str = "/home/exp278"; // also where IMS-100 images are placed pub const TO_GROUND_FOLDER_NAME: &str = "toGround"; pub const TO_GROUND_LP_FOLDER_NAME: &str = "toGroundLP"; pub const LOG_FOLDER: &str = "logs"; pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED; pub const SERVER_PORT: u16 = 7301; pub const TCP_SPP_SERVER_PORT: u16 = 4096; pub const EXPERIMENT_ID: u32 = 278; pub const EXPERIMENT_APID: u16 = 1024 + EXPERIMENT_ID as u16; pub const EXPERIMENT_PACKET_ID: PacketId = PacketId::new_for_tc(true, EXPERIMENT_APID); pub const VALID_PACKET_ID_LIST: &[PacketId] = &[PacketId::new_for_tc(true, EXPERIMENT_APID)]; // TODO: Would be nice if this can be commanded as well.. /// Can be enabled to print all SPP packets received from the SPP server on port 4096. pub const SPP_CLIENT_WIRETAPPING_RX: bool = false; pub const SPP_CLIENT_WIRETAPPING_TX: bool = false; pub const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION"); pub static TO_GROUND_FOLDER_DIR: OnceCell = OnceCell::new(); pub static TO_GROUND_LP_FOLDER_DIR: OnceCell = OnceCell::new(); pub static HOME_PATH: OnceCell = OnceCell::new(); #[derive(Copy, Clone, PartialEq, Eq, Debug, TryFromPrimitive, IntoPrimitive)] #[repr(u8)] pub enum CustomPusServiceId { Mode = 200, Health = 201, } #[derive(Debug)] pub enum GroupId { Generic = 0, Tmtc = 1, Hk = 2, Mode = 3, Action = 4, Controller = 5, Camera = 6, } pub const TEST_EVENT: EventU32TypedSev = EventU32TypedSev::::new(GroupId::Tmtc as u16, 0); pub fn set_up_home_path() -> PathBuf { let mut home_path = PathBuf::new(); if cfg!(feature = "host") { home_path = std::env::current_dir() .expect("getting current dir failed") .to_path_buf(); } else { let home_path_default = homedir::get_my_home() .expect("Getting home dir from OS failed.") .expect("No home dir found."); if Path::new(HOME_FOLDER_EXPERIMENT).exists() { home_path.push(HOME_FOLDER_EXPERIMENT); } else { home_path = home_path_default; } } HOME_PATH .set(home_path.clone()) .expect("attempting to set once cell twice"); home_path } pub fn set_up_low_prio_ground_dir(home_path: PathBuf) { let mut to_ground_lp_dir = home_path.to_path_buf(); to_ground_lp_dir.push(TO_GROUND_LP_FOLDER_NAME); if !Path::new(&to_ground_lp_dir).exists() { log::info!( "creating low priority to ground directory at {:?}", to_ground_lp_dir ); if std::fs::create_dir_all(&to_ground_lp_dir).is_err() { log::error!( "Failed to create low priority to ground directory '{:?}'", to_ground_lp_dir ); } } TO_GROUND_LP_FOLDER_DIR .set(to_ground_lp_dir) .expect("attemting to set once cell twice"); } pub fn set_up_ground_dir(home_path: PathBuf) { let mut to_ground_dir = home_path.to_path_buf(); to_ground_dir.push(TO_GROUND_FOLDER_NAME); if !Path::new(&to_ground_dir).exists() { log::info!("creating to ground directory at {:?}", to_ground_dir); if std::fs::create_dir_all(&to_ground_dir).is_err() { log::error!( "Failed to create low priority to ground directory '{:?}'", to_ground_dir ); } } TO_GROUND_FOLDER_DIR .set(to_ground_dir) .expect("attemting to set once cell twice"); } pub mod cfg_file { use std::{ fs::File, io::Read, path::{Path, PathBuf}, }; use super::{CONFIG_FILE_NAME, TCP_SPP_SERVER_PORT}; pub const SPP_CLIENT_PORT_CFG_KEY: &str = "tcp_spp_server_port"; #[derive(Debug)] pub struct AppCfg { pub tcp_spp_server_port: u16, } impl Default for AppCfg { fn default() -> Self { Self { tcp_spp_server_port: TCP_SPP_SERVER_PORT, } } } pub fn create_app_config(base_path: PathBuf) -> AppCfg { let mut cfg_path = base_path; cfg_path.push(CONFIG_FILE_NAME); let cfg_path_home = cfg_path.as_path(); let relevant_path = if Path::new(CONFIG_FILE_NAME).exists() { Some(PathBuf::from(Path::new(CONFIG_FILE_NAME))) } else if cfg_path_home.exists() { Some(PathBuf::from(cfg_path_home)) } else { None }; let mut app_cfg = AppCfg::default(); if relevant_path.is_none() { log::warn!("No config file found, using default values"); return app_cfg; } let relevant_path = relevant_path.unwrap(); match File::open(relevant_path.as_path()) { Ok(mut file) => { let mut toml_str = String::new(); match file.read_to_string(&mut toml_str) { Ok(_size) => match toml_str.parse::() { Ok(table) => { handle_config_file_table(table, &mut app_cfg); } Err(e) => log::error!("error parsing TOML config file: {e}"), }, Err(e) => log::error!("error reading TOML config file: {e}"), } } Err(e) => log::error!("error opening TOML config file: {e}"), } app_cfg } #[allow(clippy::collapsible_match)] pub fn handle_config_file_table(table: toml::Table, app_cfg: &mut AppCfg) { if let Some(value) = table.get(SPP_CLIENT_PORT_CFG_KEY) { if let toml::Value::Integer(port) = value { if *port < 0 { log::warn!("invalid port value, is negative"); } else { app_cfg.tcp_spp_server_port = *port as u16 } } } } } #[resultcode] pub const GENERIC_FAILED: ResultU16 = ResultU16::new(GroupId::Generic as u8, 1); pub mod tmtc_err { use super::*; #[resultcode] pub const INVALID_PUS_SERVICE: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 0); #[resultcode] pub const INVALID_PUS_SUBSERVICE: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 1); #[resultcode] pub const PUS_SERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2); #[resultcode] pub const PUS_SUBSERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 3); #[resultcode] pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 4); #[resultcode] pub const ROUTING_ERROR: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 5); #[resultcode(info = "Request timeout for targeted PUS request. P1: Request ID. P2: Target ID")] pub const REQUEST_TIMEOUT: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 6); #[resultcode( info = "Not enough data inside the TC application data field. Optionally includes: \ 8 bytes of failure data containing 2 failure parameters, \ P1 (u32 big endian): Expected data length, P2: Found data length" )] pub const NOT_ENOUGH_APP_DATA: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2); pub const TMTC_RESULTS: &[ResultU16Info] = &[ INVALID_PUS_SERVICE_EXT, INVALID_PUS_SUBSERVICE_EXT, PUS_SERVICE_NOT_IMPLEMENTED_EXT, UNKNOWN_TARGET_ID_EXT, ROUTING_ERROR_EXT, NOT_ENOUGH_APP_DATA_EXT, REQUEST_TIMEOUT_EXT, ]; } pub mod action_err { use super::*; #[resultcode] pub const INVALID_ACTION_ID: ResultU16 = ResultU16::new(GroupId::Action as u8, 0); pub const ACTION_RESULTS: &[ResultU16Info] = &[INVALID_ACTION_ID_EXT]; } pub mod hk_err { use super::*; #[resultcode] pub const TARGET_ID_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 0); #[resultcode] pub const UNIQUE_ID_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 1); #[resultcode] pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Hk as u8, 2); #[resultcode] pub const COLLECTION_INTERVAL_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 3); pub const HK_ERR_RESULTS: &[ResultU16Info] = &[ TARGET_ID_MISSING_EXT, UNKNOWN_TARGET_ID_EXT, UNKNOWN_TARGET_ID_EXT, COLLECTION_INTERVAL_MISSING_EXT, ]; } pub mod mode_err { use super::*; #[resultcode] pub const WRONG_MODE: ResultU16 = ResultU16::new(GroupId::Mode as u8, 0); } pub mod ctrl_err { use super::*; #[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); // 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); #[resultcode] pub const IO_ERROR: ResultU16 = ResultU16::new(GroupId::Controller as u8, 7); 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, FILESYSTEM_COPY_ERROR_EXT, IMAGE_NOT_FOUND_FOR_COPY_EXT, INVALID_LOGFILE_PATH_EXT, IO_ERROR_EXT, ]; } pub mod cam_error { use super::*; use thiserror::Error; #[derive(Debug, Error)] pub enum CameraError { #[error("Error taking image: {0}")] TakeImageError(String), #[error("error listing image files: {0}")] ListFileError(String), #[error("IO error: {0}")] IoError(#[from] std::io::Error), } #[resultcode] pub const TAKE_IMAGE_ERROR: ResultU16 = ResultU16::new(GroupId::Camera as u8, 0); #[resultcode] pub const NO_DATA: ResultU16 = ResultU16::new(GroupId::Camera as u8, 1); #[resultcode] pub const ACTION_REQ_VARIANT_NOT_IMPL: ResultU16 = ResultU16::new(GroupId::Camera as u8, 2); #[resultcode] pub const DESERIALIZE_ERROR: ResultU16 = ResultU16::new(GroupId::Camera as u8, 3); // TODO: Probably could be in a dedicated modules for these returnvalues. #[resultcode] pub const LIST_FILE_ERROR: ResultU16 = ResultU16::new(GroupId::Camera as u8, 4); #[resultcode] pub const IO_ERROR: ResultU16 = ResultU16::new(GroupId::Camera as u8, 5); pub const CAM_ERR_RESULTS: &[ResultU16Info] = &[ TAKE_IMAGE_ERROR_EXT, NO_DATA_EXT, ACTION_REQ_VARIANT_NOT_IMPL_EXT, DESERIALIZE_ERROR_EXT, LIST_FILE_ERROR_EXT, IO_ERROR_EXT, ]; } pub mod pool { use satrs::pool::{StaticMemoryPool, StaticPoolConfig}; pub fn create_sched_tc_pool() -> StaticMemoryPool { StaticMemoryPool::new(StaticPoolConfig::new( vec![ (100, 32), (50, 64), (50, 128), (50, 256), (50, 1024), (100, 2048), ], true, )) } } pub mod components { use satrs::request::UniqueApidTargetId; use super::EXPERIMENT_APID; // Component IDs for components with the PUS APID. #[derive(Copy, Clone, PartialEq, Eq)] pub enum UniqueId { Controller = 0, PusEventManagement = 1, PusRouting = 2, PusTest = 3, PusAction = 4, PusMode = 5, PusHk = 6, UdpServer = 7, TcpServer = 8, TcpSppClient = 9, PusScheduler = 10, CameraHandler = 11, } pub const CONTROLLER_ID: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::Controller as u32); pub const PUS_ACTION_SERVICE: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::PusAction as u32); pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::PusEventManagement as u32); pub const PUS_ROUTING_SERVICE: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::PusRouting as u32); pub const PUS_TEST_SERVICE: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::PusTest as u32); pub const PUS_MODE_SERVICE: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::PusMode as u32); pub const PUS_SCHEDULER_SERVICE: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::PusScheduler as u32); pub const PUS_HK_SERVICE: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::PusHk as u32); pub const UDP_SERVER: UniqueApidTargetId = UniqueApidTargetId::new(EXPERIMENT_APID, UniqueId::UdpServer as u32); pub const TCP_SERVER: UniqueApidTargetId = 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 { use std::time::Duration; pub const FREQ_MS_UDP_TMTC: u64 = 200; pub const FREQ_MS_EVENT_HANDLING: u64 = 400; pub const FREQ_MS_AOCS: u64 = 500; pub const FREQ_MS_PUS_STACK: u64 = 200; pub const FREQ_MS_CTRL: u64 = 400; pub const FREQ_MS_CAMERA_HANDLING: u64 = 400; pub const STOP_CHECK_FREQUENCY_MS: u64 = 400; pub const STOP_CHECK_FREQUENCY: Duration = Duration::from_millis(STOP_CHECK_FREQUENCY_MS); }