416 lines
14 KiB
Rust
416 lines
14 KiB
Rust
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<PathBuf> = OnceCell::new();
|
|
pub static TO_GROUND_LP_FOLDER_DIR: OnceCell<PathBuf> = OnceCell::new();
|
|
pub static HOME_PATH: OnceCell<PathBuf> = 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<SeverityInfo> =
|
|
EventU32TypedSev::<SeverityInfo>::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::<toml::Table>() {
|
|
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);
|
|
}
|