Fixes and optimizations for camera #27
@ -2,6 +2,7 @@ use lazy_static::lazy_static;
|
|||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use satrs::events::{EventU32TypedSev, SeverityInfo};
|
use satrs::events::{EventU32TypedSev, SeverityInfo};
|
||||||
|
use satrs::res_code::ResultU16;
|
||||||
use satrs::spacepackets::PacketId;
|
use satrs::spacepackets::PacketId;
|
||||||
use satrs_mib::res_code::ResultU16Info;
|
use satrs_mib::res_code::ResultU16Info;
|
||||||
use satrs_mib::resultcode;
|
use satrs_mib::resultcode;
|
||||||
@ -40,11 +41,13 @@ pub enum CustomPusServiceId {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GroupId {
|
pub enum GroupId {
|
||||||
Tmtc = 0,
|
Generic = 0,
|
||||||
Hk = 1,
|
Tmtc = 1,
|
||||||
Mode = 2,
|
Hk = 2,
|
||||||
Action = 3,
|
Mode = 3,
|
||||||
Controller = 4,
|
Action = 4,
|
||||||
|
Controller = 5,
|
||||||
|
Camera = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const TEST_EVENT: EventU32TypedSev<SeverityInfo> =
|
pub const TEST_EVENT: EventU32TypedSev<SeverityInfo> =
|
||||||
@ -187,9 +190,11 @@ pub mod cfg_file {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[resultcode]
|
||||||
|
pub const GENERIC_FAILED: ResultU16 = ResultU16::new(GroupId::Generic as u8, 1);
|
||||||
|
|
||||||
pub mod tmtc_err {
|
pub mod tmtc_err {
|
||||||
use super::*;
|
use super::*;
|
||||||
use satrs::res_code::ResultU16;
|
|
||||||
|
|
||||||
#[resultcode]
|
#[resultcode]
|
||||||
pub const INVALID_PUS_SERVICE: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 0);
|
pub const INVALID_PUS_SERVICE: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 0);
|
||||||
@ -225,7 +230,6 @@ pub mod tmtc_err {
|
|||||||
|
|
||||||
pub mod action_err {
|
pub mod action_err {
|
||||||
use super::*;
|
use super::*;
|
||||||
use satrs::res_code::ResultU16;
|
|
||||||
|
|
||||||
#[resultcode]
|
#[resultcode]
|
||||||
pub const INVALID_ACTION_ID: ResultU16 = ResultU16::new(GroupId::Action as u8, 0);
|
pub const INVALID_ACTION_ID: ResultU16 = ResultU16::new(GroupId::Action as u8, 0);
|
||||||
@ -235,7 +239,6 @@ pub mod action_err {
|
|||||||
|
|
||||||
pub mod hk_err {
|
pub mod hk_err {
|
||||||
use super::*;
|
use super::*;
|
||||||
use satrs::res_code::ResultU16;
|
|
||||||
|
|
||||||
#[resultcode]
|
#[resultcode]
|
||||||
pub const TARGET_ID_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 0);
|
pub const TARGET_ID_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 0);
|
||||||
@ -256,7 +259,6 @@ pub mod hk_err {
|
|||||||
|
|
||||||
pub mod mode_err {
|
pub mod mode_err {
|
||||||
use super::*;
|
use super::*;
|
||||||
use satrs::res_code::ResultU16;
|
|
||||||
|
|
||||||
#[resultcode]
|
#[resultcode]
|
||||||
pub const WRONG_MODE: ResultU16 = ResultU16::new(GroupId::Mode as u8, 0);
|
pub const WRONG_MODE: ResultU16 = ResultU16::new(GroupId::Mode as u8, 0);
|
||||||
@ -264,7 +266,6 @@ pub mod mode_err {
|
|||||||
|
|
||||||
pub mod ctrl_err {
|
pub mod ctrl_err {
|
||||||
use super::*;
|
use super::*;
|
||||||
use satrs::res_code::ResultU16;
|
|
||||||
|
|
||||||
#[resultcode]
|
#[resultcode]
|
||||||
pub const INVALID_CMD_FORMAT: ResultU16 = ResultU16::new(GroupId::Controller as u8, 0);
|
pub const INVALID_CMD_FORMAT: ResultU16 = ResultU16::new(GroupId::Controller as u8, 0);
|
||||||
@ -290,6 +291,35 @@ pub mod ctrl_err {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 mod pool {
|
pub mod pool {
|
||||||
use satrs::pool::{StaticMemoryPool, StaticPoolConfig};
|
use satrs::pool::{StaticMemoryPool, StaticPoolConfig};
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ impl EventHandler {
|
|||||||
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
|
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
|
||||||
let pus_event_handler = PusEventHandler::new(
|
let pus_event_handler = PusEventHandler::new(
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
|
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid, 16),
|
||||||
&mut event_manager,
|
&mut event_manager,
|
||||||
event_request_rx,
|
event_request_rx,
|
||||||
);
|
);
|
||||||
|
@ -27,22 +27,22 @@
|
|||||||
use crate::pus::action::send_data_reply;
|
use crate::pus::action::send_data_reply;
|
||||||
use crate::requests::CompositeRequest;
|
use crate::requests::CompositeRequest;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use log::{debug, info};
|
use log::info;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
use ops_sat_rs::config::cam_error::{self, CameraError};
|
||||||
|
use ops_sat_rs::config::GENERIC_FAILED;
|
||||||
use ops_sat_rs::TimeStampHelper;
|
use ops_sat_rs::TimeStampHelper;
|
||||||
use satrs::action::{ActionRequest, ActionRequestVariant};
|
use satrs::action::{ActionRequest, ActionRequestVariant};
|
||||||
use satrs::hk::HkRequest;
|
use satrs::hk::HkRequest;
|
||||||
|
use satrs::params::Params;
|
||||||
use satrs::pus::action::{ActionReplyPus, ActionReplyVariant};
|
use satrs::pus::action::{ActionReplyPus, ActionReplyVariant};
|
||||||
use satrs::pus::EcssTmtcError;
|
|
||||||
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||||
|
use satrs::res_code::ResultU16;
|
||||||
use satrs::tmtc::PacketAsVec;
|
use satrs::tmtc::PacketAsVec;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
|
||||||
use std::fmt::Formatter;
|
|
||||||
use std::process::{Command, Output};
|
use std::process::{Command, Output};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
// const IMS_TESTAPP: &str = "scripts/ims100_testapp";
|
|
||||||
const IMS_TESTAPP: &str = "ims100_testapp";
|
const IMS_TESTAPP: &str = "ims100_testapp";
|
||||||
|
|
||||||
const DEFAULT_SINGLE_CAM_PARAMS: CameraPictureParameters = CameraPictureParameters {
|
const DEFAULT_SINGLE_CAM_PARAMS: CameraPictureParameters = CameraPictureParameters {
|
||||||
@ -112,58 +112,6 @@ pub struct CameraPictureParameters {
|
|||||||
pub W: u32, // wait time between pictures in ms, max: 40000
|
pub W: u32, // wait time between pictures in ms, max: 40000
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum CameraError {
|
|
||||||
TakeImageError,
|
|
||||||
NoDataSent,
|
|
||||||
VariantNotImplemented,
|
|
||||||
DeserializeError,
|
|
||||||
ListFileError,
|
|
||||||
IoError(std::io::Error),
|
|
||||||
EcssTmtcError(EcssTmtcError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for CameraError {
|
|
||||||
fn from(value: std::io::Error) -> Self {
|
|
||||||
Self::IoError(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EcssTmtcError> for CameraError {
|
|
||||||
fn from(value: EcssTmtcError) -> Self {
|
|
||||||
Self::EcssTmtcError(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for CameraError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
CameraError::TakeImageError => {
|
|
||||||
write!(f, "Error taking image.")
|
|
||||||
}
|
|
||||||
CameraError::NoDataSent => {
|
|
||||||
write!(f, "No data sent.")
|
|
||||||
}
|
|
||||||
CameraError::VariantNotImplemented => {
|
|
||||||
write!(f, "Request variant not implemented.")
|
|
||||||
}
|
|
||||||
CameraError::DeserializeError => {
|
|
||||||
write!(f, "Unable to deserialize parameters.")
|
|
||||||
}
|
|
||||||
CameraError::ListFileError => {
|
|
||||||
write!(f, "Error listing image files.")
|
|
||||||
}
|
|
||||||
CameraError::IoError(io_error) => {
|
|
||||||
write!(f, "{}", io_error)
|
|
||||||
}
|
|
||||||
CameraError::EcssTmtcError(ecss_tmtc_error) => {
|
|
||||||
write!(f, "{}", ecss_tmtc_error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Ims100BatchHandler {
|
pub struct Ims100BatchHandler {
|
||||||
id: UniqueApidTargetId,
|
id: UniqueApidTargetId,
|
||||||
@ -205,11 +153,7 @@ impl Ims100BatchHandler {
|
|||||||
self.handle_hk_request(&msg.requestor_info, hk_request);
|
self.handle_hk_request(&msg.requestor_info, hk_request);
|
||||||
}
|
}
|
||||||
CompositeRequest::Action(action_request) => {
|
CompositeRequest::Action(action_request) => {
|
||||||
if let Err(e) =
|
self.handle_action_request(&msg.requestor_info, action_request);
|
||||||
self.handle_action_request(&msg.requestor_info, action_request)
|
|
||||||
{
|
|
||||||
log::warn!("camera action request IO error: {e}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
@ -235,53 +179,124 @@ impl Ims100BatchHandler {
|
|||||||
&mut self,
|
&mut self,
|
||||||
requestor_info: &MessageMetadata,
|
requestor_info: &MessageMetadata,
|
||||||
action_request: &ActionRequest,
|
action_request: &ActionRequest,
|
||||||
) -> Result<(), CameraError> {
|
) {
|
||||||
let param = match ActionId::try_from(action_request.action_id).expect("Invalid action id") {
|
let param = match ActionId::try_from(action_request.action_id).expect("Invalid action id") {
|
||||||
ActionId::DefaultSingle => DEFAULT_SINGLE_CAM_PARAMS,
|
ActionId::DefaultSingle => DEFAULT_SINGLE_CAM_PARAMS,
|
||||||
ActionId::BalancedSingle => BALANCED_SINGLE_CAM_PARAMS,
|
ActionId::BalancedSingle => BALANCED_SINGLE_CAM_PARAMS,
|
||||||
ActionId::DefaultSingleFlatSat => DEFAULT_SINGLE_FLATSAT_CAM_PARAMS,
|
ActionId::DefaultSingleFlatSat => DEFAULT_SINGLE_FLATSAT_CAM_PARAMS,
|
||||||
ActionId::BalancedSingleFlatSat => BALANCED_SINGLE_FLATSAT_CAM_PARAMS,
|
ActionId::BalancedSingleFlatSat => BALANCED_SINGLE_FLATSAT_CAM_PARAMS,
|
||||||
ActionId::CustomParameters => match &action_request.variant {
|
ActionId::CustomParameters => match &action_request.variant {
|
||||||
ActionRequestVariant::NoData => return Err(CameraError::NoDataSent),
|
ActionRequestVariant::NoData => {
|
||||||
ActionRequestVariant::StoreData(_) => {
|
self.send_completion_failure(
|
||||||
// let param = serde_json::from_slice()
|
requestor_info,
|
||||||
// TODO implement non dynamic version
|
action_request,
|
||||||
return Err(CameraError::VariantNotImplemented);
|
cam_error::NO_DATA,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
ActionRequestVariant::VecData(data) => {
|
ActionRequestVariant::VecData(data) => {
|
||||||
let param: serde_json::Result<CameraPictureParameters> =
|
let param: serde_json::Result<CameraPictureParameters> =
|
||||||
serde_json::from_slice(data.as_slice());
|
serde_json::from_slice(data.as_slice());
|
||||||
match param {
|
match param {
|
||||||
Ok(param) => param,
|
Ok(param) => param,
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
return Err(CameraError::DeserializeError);
|
self.send_completion_failure(
|
||||||
|
requestor_info,
|
||||||
|
action_request,
|
||||||
|
cam_error::DESERIALIZE_ERROR,
|
||||||
|
Some(e.to_string().into()),
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => return Err(CameraError::VariantNotImplemented),
|
_ => {
|
||||||
|
self.send_completion_failure(
|
||||||
|
requestor_info,
|
||||||
|
action_request,
|
||||||
|
cam_error::ACTION_REQ_VARIANT_NOT_IMPL,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let output = self.take_picture(param)?;
|
match self.take_picture(¶m) {
|
||||||
info!("Sending action reply!");
|
Ok(ref output) => {
|
||||||
send_data_reply(self.id, output.stdout, &self.stamp_helper, &self.tm_tx)?;
|
self.send_completion_success(requestor_info, action_request);
|
||||||
self.action_reply_tx
|
if let Err(e) =
|
||||||
.send(GenericMessage::new(
|
send_data_reply(self.id, &output.stdout, &self.stamp_helper, &self.tm_tx)
|
||||||
*requestor_info,
|
{
|
||||||
ActionReplyPus::new(action_request.action_id, ActionReplyVariant::Completed),
|
log::error!("sending data reply unexpectedly failed: {e}");
|
||||||
))
|
}
|
||||||
.unwrap();
|
}
|
||||||
Ok(())
|
Err(e) => match e {
|
||||||
|
CameraError::TakeImageError(ref err_str) => {
|
||||||
|
self.send_completion_failure(
|
||||||
|
requestor_info,
|
||||||
|
action_request,
|
||||||
|
cam_error::TAKE_IMAGE_ERROR,
|
||||||
|
Some(err_str.to_string().into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
CameraError::IoError(ref e) => {
|
||||||
|
self.send_completion_failure(
|
||||||
|
requestor_info,
|
||||||
|
action_request,
|
||||||
|
cam_error::IO_ERROR,
|
||||||
|
Some(e.to_string().into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log::warn!("unexpected error: {:?}", e);
|
||||||
|
self.send_completion_failure(
|
||||||
|
requestor_info,
|
||||||
|
action_request,
|
||||||
|
GENERIC_FAILED,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_picture(&mut self, param: CameraPictureParameters) -> Result<Output, CameraError> {
|
pub fn send_completion_success(&self, requestor: &MessageMetadata, action_req: &ActionRequest) {
|
||||||
info!("Taking image!");
|
let result = self.action_reply_tx.send(GenericMessage::new_action_reply(
|
||||||
|
*requestor,
|
||||||
|
action_req.action_id,
|
||||||
|
ActionReplyVariant::Completed,
|
||||||
|
));
|
||||||
|
if result.is_err() {
|
||||||
|
log::error!("sending action reply failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_completion_failure(
|
||||||
|
&self,
|
||||||
|
requestor: &MessageMetadata,
|
||||||
|
action_req: &ActionRequest,
|
||||||
|
error_code: ResultU16,
|
||||||
|
params: Option<Params>,
|
||||||
|
) {
|
||||||
|
let result = self.action_reply_tx.send(GenericMessage::new_action_reply(
|
||||||
|
*requestor,
|
||||||
|
action_req.action_id,
|
||||||
|
ActionReplyVariant::CompletionFailed { error_code, params },
|
||||||
|
));
|
||||||
|
if result.is_err() {
|
||||||
|
log::error!("sending action reply failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_picture(&mut self, param: &CameraPictureParameters) -> Result<Output, CameraError> {
|
||||||
let mut cmd = Command::new(IMS_TESTAPP);
|
let mut cmd = Command::new(IMS_TESTAPP);
|
||||||
cmd.arg("-R")
|
cmd.arg("-R")
|
||||||
.arg(¶m.R.to_string())
|
.arg(param.R.to_string())
|
||||||
.arg("-G")
|
.arg("-G")
|
||||||
.arg(¶m.G.to_string())
|
.arg(param.G.to_string())
|
||||||
.arg("-B")
|
.arg("-B")
|
||||||
.arg(¶m.B.to_string())
|
.arg(param.B.to_string())
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg("/dev/cam_tty")
|
.arg("/dev/cam_tty")
|
||||||
.arg("-m")
|
.arg("-m")
|
||||||
@ -289,18 +304,27 @@ impl Ims100BatchHandler {
|
|||||||
.arg("-v")
|
.arg("-v")
|
||||||
.arg("0")
|
.arg("0")
|
||||||
.arg("-n")
|
.arg("-n")
|
||||||
.arg(¶m.N.to_string());
|
.arg(param.N.to_string());
|
||||||
if param.P {
|
if param.P {
|
||||||
cmd.arg("-p");
|
cmd.arg("-p");
|
||||||
}
|
}
|
||||||
cmd.arg("-e")
|
cmd.arg("-e")
|
||||||
.arg(¶m.E.to_string())
|
.arg(param.E.to_string())
|
||||||
.arg("-w")
|
.arg("-w")
|
||||||
.arg(¶m.W.to_string());
|
.arg(param.W.to_string());
|
||||||
|
info!("taking image with command: {cmd:?}");
|
||||||
let output = cmd.output()?;
|
let output = cmd.output()?;
|
||||||
|
|
||||||
debug!("Imager Output: {}", String::from_utf8_lossy(&output.stdout));
|
info!("imager cmd status: {}", &output.status);
|
||||||
|
info!("imager output: {}", String::from_utf8_lossy(&output.stdout));
|
||||||
|
let mut error_string = String::new();
|
||||||
|
if !output.stderr.is_empty() {
|
||||||
|
error_string = String::from_utf8_lossy(&output.stderr).to_string();
|
||||||
|
log::warn!("imager error: {}", error_string);
|
||||||
|
}
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(CameraError::TakeImageError(error_string.to_string()));
|
||||||
|
}
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,49 +337,11 @@ impl Ims100BatchHandler {
|
|||||||
let files: Vec<String> = output_str.lines().map(|s| s.to_string()).collect();
|
let files: Vec<String> = output_str.lines().map(|s| s.to_string()).collect();
|
||||||
Ok(files)
|
Ok(files)
|
||||||
} else {
|
} else {
|
||||||
Err(CameraError::ListFileError)
|
Err(CameraError::ListFileError(
|
||||||
|
String::from_utf8_lossy(&output.stderr).to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn take_picture_from_str(
|
|
||||||
&mut self,
|
|
||||||
R: &str,
|
|
||||||
G: &str,
|
|
||||||
B: &str,
|
|
||||||
N: &str,
|
|
||||||
P: &str,
|
|
||||||
E: &str,
|
|
||||||
W: &str,
|
|
||||||
) -> Result<(), CameraError> {
|
|
||||||
let mut cmd = Command::new("ims100_testapp");
|
|
||||||
cmd.arg("-R")
|
|
||||||
.arg(R)
|
|
||||||
.arg("-G")
|
|
||||||
.arg(G)
|
|
||||||
.arg("-B")
|
|
||||||
.arg(B)
|
|
||||||
.arg("-c")
|
|
||||||
.arg("/dev/cam_tty")
|
|
||||||
.arg("-m")
|
|
||||||
.arg("/dev/cam_sd")
|
|
||||||
.arg("-v")
|
|
||||||
.arg("0")
|
|
||||||
.arg("-n")
|
|
||||||
.arg(N)
|
|
||||||
.arg(P)
|
|
||||||
.arg("-e")
|
|
||||||
.arg(E)
|
|
||||||
.arg("-w")
|
|
||||||
.arg(W);
|
|
||||||
|
|
||||||
let output = cmd.output()?;
|
|
||||||
|
|
||||||
debug!("{}", String::from_utf8_lossy(&output.stdout));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -372,6 +358,7 @@ mod tests {
|
|||||||
use satrs::tmtc::PacketAsVec;
|
use satrs::tmtc::PacketAsVec;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
struct Ims1000Testbench {
|
struct Ims1000Testbench {
|
||||||
pub handler: Ims100BatchHandler,
|
pub handler: Ims100BatchHandler,
|
||||||
pub composite_req_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
|
pub composite_req_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
|
||||||
@ -393,13 +380,7 @@ mod tests {
|
|||||||
time_helper,
|
time_helper,
|
||||||
);
|
);
|
||||||
Ims1000Testbench {
|
Ims1000Testbench {
|
||||||
handler: Ims100BatchHandler::new(
|
handler: cam_handler,
|
||||||
CAMERA_HANDLER,
|
|
||||||
composite_request_rx,
|
|
||||||
tm_tx,
|
|
||||||
action_reply_tx,
|
|
||||||
time_helper,
|
|
||||||
),
|
|
||||||
composite_req_tx: composite_request_tx,
|
composite_req_tx: composite_request_tx,
|
||||||
tm_receiver: tm_rx,
|
tm_receiver: tm_rx,
|
||||||
action_reply_rx,
|
action_reply_rx,
|
||||||
@ -412,7 +393,7 @@ mod tests {
|
|||||||
let mut testbench = Ims1000Testbench::default();
|
let mut testbench = Ims1000Testbench::default();
|
||||||
testbench
|
testbench
|
||||||
.handler
|
.handler
|
||||||
.take_picture(DEFAULT_SINGLE_FLATSAT_CAM_PARAMS)
|
.take_picture(&DEFAULT_SINGLE_FLATSAT_CAM_PARAMS)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +417,6 @@ mod tests {
|
|||||||
testbench
|
testbench
|
||||||
.handler
|
.handler
|
||||||
.handle_action_request(&MessageMetadata::new(1, 1), &req)
|
.handle_action_request(&MessageMetadata::new(1, 1), &req)
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use log::{debug, error, warn};
|
use log::{error, warn};
|
||||||
use ops_sat_rs::config::components::PUS_ACTION_SERVICE;
|
use ops_sat_rs::config::components::PUS_ACTION_SERVICE;
|
||||||
use ops_sat_rs::config::tmtc_err;
|
use ops_sat_rs::config::tmtc_err;
|
||||||
use ops_sat_rs::TimeStampHelper;
|
use ops_sat_rs::TimeStampHelper;
|
||||||
@ -35,13 +35,13 @@ use super::{
|
|||||||
pub const DATA_REPLY: u8 = 130;
|
pub const DATA_REPLY: u8 = 130;
|
||||||
|
|
||||||
pub struct ActionReplyHandler {
|
pub struct ActionReplyHandler {
|
||||||
fail_data_buf: [u8; 128],
|
fail_data_buf: [u8; 2048],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ActionReplyHandler {
|
impl Default for ActionReplyHandler {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fail_data_buf: [0; 128],
|
fail_data_buf: [0; 2048],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,8 +74,57 @@ impl PusReplyHandler<ActivePusActionRequestStd, ActionReplyPus> for ActionReplyH
|
|||||||
ActionReplyVariant::CompletionFailed { error_code, params } => {
|
ActionReplyVariant::CompletionFailed { error_code, params } => {
|
||||||
let mut fail_data_len = 0;
|
let mut fail_data_len = 0;
|
||||||
if let Some(params) = params {
|
if let Some(params) = params {
|
||||||
fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?;
|
match params {
|
||||||
|
satrs::params::Params::Heapless(heapless_param) => {
|
||||||
|
// TODO: This should be part of the framework.
|
||||||
|
match heapless_param {
|
||||||
|
satrs::params::ParamsHeapless::Raw(raw) => {
|
||||||
|
// TODO: size check.
|
||||||
|
let _ = raw.write_to_be_bytes(
|
||||||
|
&mut self.fail_data_buf[0..raw.written_len()],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
satrs::params::ParamsHeapless::EcssEnum(ecss_enum) => {
|
||||||
|
// TODO: size check.
|
||||||
|
let _ = ecss_enum.write_to_be_bytes(
|
||||||
|
&mut self.fail_data_buf[0..ecss_enum.written_len()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
satrs::params::Params::Store(_) => {
|
||||||
|
log::warn!("can not process store parameters")
|
||||||
|
}
|
||||||
|
satrs::params::Params::Vec(vec) => {
|
||||||
|
// Truncate the string for now.
|
||||||
|
fail_data_len = vec.len();
|
||||||
|
if vec.len() > self.fail_data_buf.len() {
|
||||||
|
log::warn!(
|
||||||
|
"action reply vec too large, truncating to {} bytes",
|
||||||
|
self.fail_data_buf.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.fail_data_buf[0..fail_data_len]
|
||||||
|
.copy_from_slice(&vec[0..fail_data_len]);
|
||||||
|
}
|
||||||
|
satrs::params::Params::String(str) => {
|
||||||
|
fail_data_len = str.len();
|
||||||
|
// Truncate the string for now.
|
||||||
|
if str.len() > self.fail_data_buf.len() {
|
||||||
|
fail_data_len = self.fail_data_buf.len();
|
||||||
|
log::warn!(
|
||||||
|
"action reply string too large, truncating to {} bytes",
|
||||||
|
self.fail_data_buf.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.fail_data_buf[0..fail_data_len]
|
||||||
|
.copy_from_slice(&str.as_bytes()[0..fail_data_len]);
|
||||||
|
log::warn!("received string param with len {}", str.len());
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::warn!("completion failure with fail data len: {}", fail_data_len);
|
||||||
verification_handler.completion_failure(
|
verification_handler.completion_failure(
|
||||||
tm_sender,
|
tm_sender,
|
||||||
verif_token,
|
verif_token,
|
||||||
@ -209,7 +258,7 @@ pub fn create_action_service(
|
|||||||
PUS_ACTION_SERVICE.id(),
|
PUS_ACTION_SERVICE.id(),
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
tm_funnel_tx,
|
tm_funnel_tx,
|
||||||
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid),
|
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid, 2048),
|
||||||
EcssTcInVecConverter::default(),
|
EcssTcInVecConverter::default(),
|
||||||
),
|
),
|
||||||
ActionRequestConverter::default(),
|
ActionRequestConverter::default(),
|
||||||
@ -277,7 +326,7 @@ impl TargetedPusService for ActionServiceWrapper {
|
|||||||
|
|
||||||
pub fn send_data_reply<TmSender: EcssTmSender>(
|
pub fn send_data_reply<TmSender: EcssTmSender>(
|
||||||
apid_target: UniqueApidTargetId,
|
apid_target: UniqueApidTargetId,
|
||||||
reply_data: Vec<u8>,
|
reply_data: &Vec<u8>,
|
||||||
stamp_helper: &TimeStampHelper,
|
stamp_helper: &TimeStampHelper,
|
||||||
tm_sender: &TmSender,
|
tm_sender: &TmSender,
|
||||||
) -> Result<(), EcssTmtcError> {
|
) -> Result<(), EcssTmtcError> {
|
||||||
@ -287,8 +336,8 @@ pub fn send_data_reply<TmSender: EcssTmSender>(
|
|||||||
data.extend(apid_target.apid.to_be_bytes());
|
data.extend(apid_target.apid.to_be_bytes());
|
||||||
data.extend(apid_target.unique_id.to_be_bytes());
|
data.extend(apid_target.unique_id.to_be_bytes());
|
||||||
data.extend(reply_data);
|
data.extend(reply_data);
|
||||||
debug!(
|
log::trace!(
|
||||||
"{}",
|
"PUS action reply: {}",
|
||||||
String::from_utf8(data.clone()[6..].to_vec()).expect("Error decoding data reply.")
|
String::from_utf8(data.clone()[6..].to_vec()).expect("Error decoding data reply.")
|
||||||
);
|
);
|
||||||
let data_reply_tm = PusTmCreator::new(sp_header, sec_header, &data, true);
|
let data_reply_tm = PusTmCreator::new(sp_header, sec_header, &data, true);
|
||||||
|
@ -22,7 +22,7 @@ pub fn create_event_service(
|
|||||||
PUS_EVENT_MANAGEMENT.id(),
|
PUS_EVENT_MANAGEMENT.id(),
|
||||||
pus_event_rx,
|
pus_event_rx,
|
||||||
tm_funnel_tx,
|
tm_funnel_tx,
|
||||||
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
|
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid, 16),
|
||||||
EcssTcInVecConverter::default(),
|
EcssTcInVecConverter::default(),
|
||||||
),
|
),
|
||||||
event_request_tx,
|
event_request_tx,
|
||||||
|
@ -241,7 +241,7 @@ pub fn create_hk_service(
|
|||||||
PUS_HK_SERVICE.id(),
|
PUS_HK_SERVICE.id(),
|
||||||
pus_hk_rx,
|
pus_hk_rx,
|
||||||
tm_funnel_tx,
|
tm_funnel_tx,
|
||||||
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid),
|
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid, 16),
|
||||||
EcssTcInVecConverter::default(),
|
EcssTcInVecConverter::default(),
|
||||||
),
|
),
|
||||||
HkRequestConverter::default(),
|
HkRequestConverter::default(),
|
||||||
|
@ -37,8 +37,12 @@ pub enum HandlingStatus {
|
|||||||
HandledOne,
|
HandledOne,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter {
|
pub fn create_verification_reporter(
|
||||||
let verif_cfg = VerificationReporterCfg::new(apid, 1, 2, 8).unwrap();
|
owner_id: ComponentId,
|
||||||
|
apid: Apid,
|
||||||
|
max_fail_data_len: usize,
|
||||||
|
) -> VerificationReporter {
|
||||||
|
let verif_cfg = VerificationReporterCfg::new(apid, 1, 2, max_fail_data_len).unwrap();
|
||||||
// Every software component which needs to generate verification telemetry, gets a cloned
|
// Every software component which needs to generate verification telemetry, gets a cloned
|
||||||
// verification reporter.
|
// verification reporter.
|
||||||
VerificationReporter::new(owner_id, &verif_cfg)
|
VerificationReporter::new(owner_id, &verif_cfg)
|
||||||
@ -70,6 +74,7 @@ impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
|
|||||||
verif_reporter: create_verification_reporter(
|
verif_reporter: create_verification_reporter(
|
||||||
PUS_ROUTING_SERVICE.id(),
|
PUS_ROUTING_SERVICE.id(),
|
||||||
PUS_ROUTING_SERVICE.apid,
|
PUS_ROUTING_SERVICE.apid,
|
||||||
|
16,
|
||||||
),
|
),
|
||||||
pus_router,
|
pus_router,
|
||||||
stamp_helper: TimeStampHelper::default(),
|
stamp_helper: TimeStampHelper::default(),
|
||||||
@ -406,21 +411,25 @@ where
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let active_request = active_req_opt.unwrap();
|
let active_request = active_req_opt.unwrap();
|
||||||
let request_finished = self
|
match self.reply_handler.handle_reply(
|
||||||
.reply_handler
|
|
||||||
.handle_reply(
|
|
||||||
reply,
|
reply,
|
||||||
active_request,
|
active_request,
|
||||||
&self.service_helper.common.tm_sender,
|
&self.service_helper.common.tm_sender,
|
||||||
&self.service_helper.common.verif_reporter,
|
&self.service_helper.common.verif_reporter,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
)
|
) {
|
||||||
.unwrap_or(false);
|
Ok(finished) => {
|
||||||
if request_finished {
|
if finished {
|
||||||
self.active_request_map.remove(reply.request_id());
|
self.active_request_map.remove(reply.request_id());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.active_request_map.remove(reply.request_id());
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_for_request_timeouts(&mut self) {
|
pub fn check_for_request_timeouts(&mut self) {
|
||||||
let mut requests_to_delete = Vec::new();
|
let mut requests_to_delete = Vec::new();
|
||||||
|
@ -212,7 +212,7 @@ pub fn create_mode_service(
|
|||||||
PUS_MODE_SERVICE.id(),
|
PUS_MODE_SERVICE.id(),
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
tm_funnel_tx,
|
tm_funnel_tx,
|
||||||
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid),
|
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid, 16),
|
||||||
EcssTcInVecConverter::default(),
|
EcssTcInVecConverter::default(),
|
||||||
),
|
),
|
||||||
ModeRequestConverter::default(),
|
ModeRequestConverter::default(),
|
||||||
|
@ -139,7 +139,11 @@ pub fn create_scheduler_service(
|
|||||||
PUS_SCHEDULER_SERVICE.id(),
|
PUS_SCHEDULER_SERVICE.id(),
|
||||||
pus_sched_rx,
|
pus_sched_rx,
|
||||||
tm_funnel_tx,
|
tm_funnel_tx,
|
||||||
create_verification_reporter(PUS_SCHEDULER_SERVICE.id(), PUS_SCHEDULER_SERVICE.apid),
|
create_verification_reporter(
|
||||||
|
PUS_SCHEDULER_SERVICE.id(),
|
||||||
|
PUS_SCHEDULER_SERVICE.apid,
|
||||||
|
16,
|
||||||
|
),
|
||||||
EcssTcInVecConverter::default(),
|
EcssTcInVecConverter::default(),
|
||||||
),
|
),
|
||||||
scheduler,
|
scheduler,
|
||||||
|
@ -27,7 +27,7 @@ pub fn create_test_service(
|
|||||||
PUS_TEST_SERVICE.id(),
|
PUS_TEST_SERVICE.id(),
|
||||||
pus_test_rx,
|
pus_test_rx,
|
||||||
tm_funnel_tx,
|
tm_funnel_tx,
|
||||||
create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid),
|
create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid, 16),
|
||||||
EcssTcInVecConverter::default(),
|
EcssTcInVecConverter::default(),
|
||||||
));
|
));
|
||||||
TestCustomServiceWrapper {
|
TestCustomServiceWrapper {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user