continue PCDU
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2024-05-18 14:08:42 +02:00
parent 3db54da3df
commit cb0a65c4d4
6 changed files with 240 additions and 59 deletions

View File

@ -132,6 +132,7 @@ pub mod components {
Acs = 3,
Cfdp = 4,
Tmtc = 5,
Eps = 6,
}
// Component IDs for components with the PUS APID.
@ -150,6 +151,11 @@ pub mod components {
Mgm0 = 0,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum EpsId {
Pcdu = 0,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum TmtcId {
UdpServer = 0,
@ -172,6 +178,8 @@ pub mod components {
UniqueApidTargetId::new(Apid::Sched as u16, 0);
pub const MGM_HANDLER_0: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
pub const PCDU_HANDLER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Pcdu as u32);
pub const UDP_SERVER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::UdpServer as u32);
pub const TCP_SERVER: UniqueApidTargetId =

View File

@ -1,5 +1,6 @@
use std::{
collections::HashMap,
cell::RefCell,
collections::VecDeque,
sync::{mpsc, Arc, Mutex},
};
@ -7,13 +8,16 @@ use derive_new::new;
use satrs::{
hk::{HkRequest, HkRequestVariant},
mode::{ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler},
power::SwitchState,
power::{SwitchState, SwitchStateBinary},
pus::EcssTmSender,
queue::{GenericSendError, GenericTargetedMessagingError},
request::{GenericMessage, MessageMetadata, UniqueApidTargetId},
};
use satrs_example::{config::components::PUS_MODE_SERVICE, DeviceMode, TimestampHelper};
use satrs_minisim::{SimReply, SimRequest};
use satrs_minisim::{
eps::{PcduReply, PcduRequest, SwitchMap, SwitchMapWrapper},
SerializableSimMsgPayload, SimReply, SimRequest,
};
use crate::{acs::mgm::MpscModeLeafInterface, pus::hk::HkReply, requests::CompositeRequest};
@ -29,12 +33,13 @@ pub trait SerialInterface {
) -> Result<(), Self::Error>;
}
pub struct SimSerialInterface {
#[derive(new)]
pub struct SerialInterfaceToSim {
pub sim_request_tx: mpsc::Sender<SimRequest>,
pub sim_reply_rx: mpsc::Receiver<SimReply>,
}
impl SerialInterface for SimSerialInterface {
impl SerialInterface for SerialInterfaceToSim {
type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
@ -68,27 +73,112 @@ impl SerialInterface for SimSerialInterface {
}
}
#[derive(Default)]
pub struct SerialInterfaceDummy {
// Need interior mutability here for both fields.
pub switch_map: RefCell<SwitchMapWrapper>,
pub reply_deque: RefCell<VecDeque<SimReply>>,
}
impl SerialInterface for SerialInterfaceDummy {
type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
let sim_req: SimRequest = serde_json::from_slice(data).unwrap();
let pcdu_req =
PcduRequest::from_sim_message(&sim_req).expect("PCDU request creation failed");
let switch_map_mut = &mut self.switch_map.borrow_mut().0;
match pcdu_req {
PcduRequest::SwitchDevice { switch, state } => {
match switch_map_mut.entry(switch) {
std::collections::hash_map::Entry::Occupied(mut val) => {
match state {
SwitchStateBinary::Off => {
*val.get_mut() = SwitchState::Off;
}
SwitchStateBinary::On => {
*val.get_mut() = SwitchState::On;
}
};
}
std::collections::hash_map::Entry::Vacant(vacant) => {
match state {
SwitchStateBinary::Off => vacant.insert(SwitchState::Off),
SwitchStateBinary::On => vacant.insert(SwitchState::On),
};
}
};
}
PcduRequest::RequestSwitchInfo => {
let mut reply_deque_mut = self.reply_deque.borrow_mut();
reply_deque_mut.push_back(SimReply::new(&PcduReply::SwitchInfo(
self.switch_map.borrow().0.clone(),
)));
}
};
Ok(())
}
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
&self,
mut f: ReplyHandler,
) -> Result<(), Self::Error> {
if self.reply_deque.borrow().is_empty() {
return Ok(());
}
loop {
let mut reply_deque_mut = self.reply_deque.borrow_mut();
let next_reply = reply_deque_mut.pop_front().unwrap();
let reply = serde_json::to_string(&next_reply).unwrap();
f(reply.as_bytes());
if reply_deque_mut.is_empty() {
break;
}
}
Ok(())
}
}
pub enum SerialSimInterfaceWrapper {
Dummy(SerialInterfaceDummy),
Sim(SerialInterfaceToSim),
}
impl SerialInterface for SerialSimInterfaceWrapper {
type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
match self {
SerialSimInterfaceWrapper::Dummy(dummy) => dummy.send(data),
SerialSimInterfaceWrapper::Sim(sim) => sim.send(data),
}
}
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
&self,
f: ReplyHandler,
) -> Result<(), Self::Error> {
match self {
SerialSimInterfaceWrapper::Dummy(dummy) => dummy.try_recv_replies(f),
SerialSimInterfaceWrapper::Sim(sim) => sim.try_recv_replies(f),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpCode {
RegularOp = 0,
PollAndRecvReplies = 1,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum SwitchId {
Mgm0 = 0,
Mgt = 1,
}
pub type SwitchMap = HashMap<SwitchId, SwitchState>;
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Default)]
pub struct SwitchSet {
pub valid: bool,
pub switch_map: SwitchMap,
}
pub type SharedSwitchSet = Arc<Mutex<SwitchSet>>;
/// Example PCDU device handler.
#[derive(new)]
#[allow(clippy::too_many_arguments)]

View File

@ -8,6 +8,9 @@ mod pus;
mod requests;
mod tmtc;
use crate::eps::pcdu::{
PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper,
};
use crate::events::EventHandler;
use crate::interface::udp::DynamicUdpTmHandler;
use crate::pus::stack::PusStack;
@ -45,7 +48,7 @@ use crate::requests::{CompositeRequest, GenericRequestRouter};
use satrs::mode::ModeRequest;
use satrs::pus::event_man::EventRequestWithToken;
use satrs::spacepackets::{time::cds::CdsTime, time::TimeWriter};
use satrs_example::config::components::{MGM_HANDLER_0, TCP_SERVER, UDP_SERVER};
use satrs_example::config::components::{MGM_HANDLER_0, PCDU_HANDLER, TCP_SERVER, UDP_SERVER};
use std::net::{IpAddr, SocketAddr};
use std::sync::mpsc;
use std::sync::{Arc, RwLock};
@ -68,11 +71,17 @@ fn static_tmtc_pool_main() {
let (sim_request_tx, sim_request_rx) = mpsc::channel();
let (mgm_sim_reply_tx, mgm_sim_reply_rx) = mpsc::channel();
let (pcdu_sim_reply_tx, pcdu_sim_reply_rx) = mpsc::channel();
let mut opt_sim_client = create_sim_client(sim_request_rx);
let (mgm_handler_composite_tx, mgm_handler_composite_rx) =
mpsc::channel::<GenericMessage<CompositeRequest>>();
let (pcdu_handler_composite_tx, pcdu_handler_composite_rx) =
mpsc::channel::<GenericMessage<CompositeRequest>>();
let (mgm_handler_mode_tx, mgm_handler_mode_rx) = mpsc::channel::<GenericMessage<ModeRequest>>();
let (pcdu_handler_mode_tx, pcdu_handler_mode_rx) =
mpsc::channel::<GenericMessage<ModeRequest>>();
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
let mut request_map = GenericRequestRouter::default();
@ -82,6 +91,12 @@ fn static_tmtc_pool_main() {
request_map
.mode_router_map
.insert(MGM_HANDLER_0.id(), mgm_handler_mode_tx);
request_map
.composite_router_map
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
request_map
.mode_router_map
.insert(PCDU_HANDLER.id(), pcdu_handler_mode_tx);
// This helper structure is used by all telecommand providers which need to send telecommands
// to the TC source.
@ -207,10 +222,11 @@ fn static_tmtc_pool_main() {
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
mpsc::channel();
let shared_switch_set = Arc::default();
let shared_mgm_set = Arc::default();
let mode_leaf_interface = MpscModeLeafInterface {
let mgm_mode_leaf_interface = MpscModeLeafInterface {
request_rx: mgm_handler_mode_rx,
reply_to_pus_tx: pus_mode_reply_tx,
reply_to_pus_tx: pus_mode_reply_tx.clone(),
reply_to_parent_tx: mgm_handler_mode_reply_to_parent_tx,
};
@ -226,14 +242,41 @@ fn static_tmtc_pool_main() {
let mut mgm_handler = MgmHandlerLis3Mdl::new(
MGM_HANDLER_0,
"MGM_0",
mode_leaf_interface,
mgm_mode_leaf_interface,
mgm_handler_composite_rx,
pus_hk_reply_tx,
tm_sink_tx,
pus_hk_reply_tx.clone(),
tm_sink_tx.clone(),
mgm_spi_interface,
shared_mgm_set,
);
let (pcdu_handler_mode_reply_to_parent_tx, _pcdu_handler_mode_reply_to_parent_rx) =
mpsc::channel();
let pcdu_mode_leaf_interface = MpscModeLeafInterface {
request_rx: pcdu_handler_mode_rx,
reply_to_pus_tx: pus_mode_reply_tx,
reply_to_parent_tx: pcdu_handler_mode_reply_to_parent_tx,
};
let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
sim_client.add_reply_recipient(satrs_minisim::SimComponent::Pcdu, pcdu_sim_reply_tx);
SerialSimInterfaceWrapper::Sim(SerialInterfaceToSim::new(
sim_request_tx.clone(),
pcdu_sim_reply_rx,
))
} else {
SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default())
};
let mut pcdu_handler = PcduHandler::new(
PCDU_HANDLER,
"PCDU",
pcdu_mode_leaf_interface,
pcdu_handler_composite_rx,
pus_hk_reply_tx,
tm_sink_tx,
pcdu_serial_interface,
shared_switch_set,
);
info!("Starting TMTC and UDP task");
let jh_udp_tmtc = thread::Builder::new()
.name("SATRS tmtc-udp".to_string())
@ -290,6 +333,21 @@ fn static_tmtc_pool_main() {
})
.unwrap();
info!("Starting EPS thread");
let jh_eps = thread::Builder::new()
.name("sat-rs eps".to_string())
.spawn(move || loop {
// TODO: We should introduce something like a fixed timeslot helper to allow a more
// declarative API. It would also be very useful for the AOCS task.
pcdu_handler.periodic_operation(eps::pcdu::OpCode::RegularOp);
thread::sleep(Duration::from_millis(50));
pcdu_handler.periodic_operation(eps::pcdu::OpCode::PollAndRecvReplies);
thread::sleep(Duration::from_millis(50));
pcdu_handler.periodic_operation(eps::pcdu::OpCode::PollAndRecvReplies);
thread::sleep(Duration::from_millis(300));
})
.unwrap();
info!("Starting PUS handler thread");
let jh_pus_handler = thread::Builder::new()
.name("sat-rs pus".to_string())
@ -315,6 +373,7 @@ fn static_tmtc_pool_main() {
.expect("Joining SIM client thread failed");
}
jh_aocs.join().expect("Joining AOCS thread failed");
jh_eps.join().expect("Joining EPS thread failed");
jh_pus_handler
.join()
.expect("Joining PUS handler thread failed");
@ -328,6 +387,7 @@ fn dyn_tmtc_pool_main() {
let (sim_request_tx, sim_request_rx) = mpsc::channel();
let (mgm_sim_reply_tx, mgm_sim_reply_rx) = mpsc::channel();
// let (pcdu_sim_reply_tx, pcdu_sim_reply_rx) = mpsc::channel();
let mut opt_sim_client = create_sim_client(sim_request_rx);
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.

View File

@ -11,6 +11,7 @@ serde_json = "1"
log = "0.4"
thiserror = "1"
fern = "0.5"
strum = { version = "0.26", features = ["derive"] }
humantime = "2"
[dependencies.asynchronix]

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, sync::mpsc, time::Duration};
use std::{sync::mpsc, time::Duration};
use asynchronix::{
model::{Model, Output},
@ -6,14 +6,14 @@ use asynchronix::{
};
use satrs::power::SwitchStateBinary;
use satrs_minisim::{
eps::{PcduReply, PcduSwitch, SwitchMap},
eps::{PcduReply, PcduSwitch, SwitchMapBinaryWrapper},
SimReply,
};
pub const SWITCH_INFO_DELAY_MS: u64 = 10;
pub struct PcduModel {
pub switcher_map: SwitchMap,
pub switcher_map: SwitchMapBinaryWrapper,
pub mgm_switch: Output<SwitchStateBinary>,
pub mgt_switch: Output<SwitchStateBinary>,
pub reply_sender: mpsc::Sender<SimReply>,
@ -21,12 +21,8 @@ pub struct PcduModel {
impl PcduModel {
pub fn new(reply_sender: mpsc::Sender<SimReply>) -> Self {
let mut switcher_map = HashMap::new();
switcher_map.insert(PcduSwitch::Mgm, SwitchStateBinary::Off);
switcher_map.insert(PcduSwitch::Mgt, SwitchStateBinary::Off);
Self {
switcher_map,
switcher_map: Default::default(),
mgm_switch: Output::new(),
mgt_switch: Output::new(),
reply_sender,
@ -44,7 +40,7 @@ impl PcduModel {
}
pub fn send_switch_info(&mut self) {
let reply = SimReply::new(&PcduReply::SwitchInfo(self.switcher_map.clone()));
let reply = SimReply::new(&PcduReply::SwitchInfo(self.switcher_map.0.clone()));
self.reply_sender.send(reply).unwrap();
}
@ -54,6 +50,7 @@ impl PcduModel {
) {
let val = self
.switcher_map
.0
.get_mut(&switch_and_target_state.0)
.unwrap_or_else(|| panic!("switch {:?} not found", switch_and_target_state.0));
*val = switch_and_target_state.1;
@ -76,7 +73,8 @@ pub(crate) mod tests {
use std::time::Duration;
use satrs_minisim::{
eps::PcduRequest, SerializableSimMsgPayload, SimComponent, SimMessageProvider, SimRequest,
eps::{PcduRequest, SwitchMapBinary},
SerializableSimMsgPayload, SimComponent, SimMessageProvider, SimRequest,
};
use crate::test_helpers::SimTestbench;
@ -105,14 +103,11 @@ pub(crate) mod tests {
switch_device(sim_testbench, switch, SwitchStateBinary::On);
}
pub(crate) fn get_all_off_switch_map() -> SwitchMap {
let mut switcher_map = SwitchMap::new();
switcher_map.insert(super::PcduSwitch::Mgm, super::SwitchStateBinary::Off);
switcher_map.insert(super::PcduSwitch::Mgt, super::SwitchStateBinary::Off);
switcher_map
pub(crate) fn get_all_off_switch_map() -> SwitchMapBinary {
SwitchMapBinary::default()
}
fn check_switch_state(sim_testbench: &mut SimTestbench, expected_switch_map: &SwitchMap) {
fn check_switch_state(sim_testbench: &mut SimTestbench, expected_switch_map: &SwitchMapBinary) {
let request = SimRequest::new_with_epoch_time(PcduRequest::RequestSwitchInfo);
sim_testbench
.send_request(request)

View File

@ -160,18 +160,62 @@ impl From<SimRequestError> for SimCtrlReply {
pub mod eps {
use super::*;
use satrs::power::{SwitchState, SwitchStateBinary};
use std::collections::HashMap;
use strum::{EnumIter, IntoEnumIterator};
use satrs::power::SwitchStateBinary;
pub type SwitchMap = HashMap<PcduSwitch, SwitchState>;
pub type SwitchMapBinary = HashMap<PcduSwitch, SwitchStateBinary>;
pub type SwitchMap = HashMap<PcduSwitch, SwitchStateBinary>;
pub struct SwitchMapWrapper(pub SwitchMap);
pub struct SwitchMapBinaryWrapper(pub SwitchMapBinary);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, EnumIter)]
pub enum PcduSwitch {
Mgm = 0,
Mgt = 1,
}
impl Default for SwitchMapBinaryWrapper {
fn default() -> Self {
let mut switch_map = SwitchMapBinary::default();
for entry in PcduSwitch::iter() {
switch_map.insert(entry, SwitchStateBinary::Off);
}
Self(switch_map)
}
}
impl Default for SwitchMapWrapper {
fn default() -> Self {
let mut switch_map = SwitchMap::default();
for entry in PcduSwitch::iter() {
switch_map.insert(entry, SwitchState::Unknown);
}
Self(switch_map)
}
}
impl SwitchMapWrapper {
pub fn new_with_init_switches_off() -> Self {
let mut switch_map = SwitchMap::default();
for entry in PcduSwitch::iter() {
switch_map.insert(entry, SwitchState::Off);
}
Self(switch_map)
}
}
impl From<SwitchMapBinaryWrapper> for SwitchMapWrapper {
fn from(value: SwitchMapBinaryWrapper) -> Self {
value
.0
.iter()
.map(|(key, value)| (*key, SwitchState::from(value.into())))
.collect()
}
}
#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum PcduRequestId {
@ -188,31 +232,14 @@ pub mod eps {
RequestSwitchInfo,
}
/*
impl PcduRequest {
/// The sole purpose of this method
pub fn write_to_be_bytes(&self, buf: &mut [u8]) {
match self {
PcduRequest::SwitchDevice { switch, state } => {
buf[0] = PcduRequestId::SwitchDevice as u8;
buf[1..3].copy_from_slice(&(*switch as u16).to_be_bytes());
buf[4] = *state as u8;
}
PcduRequest::RequestSwitchInfo => {
buf[0] = PcduRequestId::RequestSwitchInfo as u8;
}
}
}
}
*/
impl SerializableSimMsgPayload<SimRequest> for PcduRequest {
const TARGET: SimComponent = SimComponent::Pcdu;
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum PcduReply {
SwitchInfo(SwitchMap),
// Ack,
SwitchInfo(SwitchMapBinary),
}
impl SerializableSimMsgPayload<SimReply> for PcduReply {