Compare commits
8 Commits
90a6b07575
...
satrs-v0.3
Author | SHA1 | Date | |
---|---|---|---|
767cf6b1a5 | |||
5bf5518591
|
|||
f3e0609910 | |||
05106354e3 | |||
719b70d834 | |||
d4082fa098 | |||
c9c9e4f735 | |||
2c92a95184
|
10
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
10
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
@@ -631,6 +631,7 @@ dependencies = [
|
||||
"satrs-shared",
|
||||
"smallvec",
|
||||
"spacepackets",
|
||||
"static_cell",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -720,6 +721,15 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_cell"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7"
|
||||
version = "0.15.1"
|
||||
|
@@ -3,7 +3,6 @@
|
||||
extern crate alloc;
|
||||
|
||||
use rtic::app;
|
||||
use rtic_monotonics::fugit::TimerInstantU32;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use satrs::pool::{PoolAddr, PoolProvider, StaticHeaplessMemoryPool};
|
||||
use satrs::static_subpool;
|
||||
@@ -74,9 +73,7 @@ impl Net {
|
||||
let mut iface = Interface::new(
|
||||
config,
|
||||
&mut ethdev,
|
||||
smoltcp::time::Instant::from_millis(
|
||||
(Mono::now() - TimerInstantU32::<1000>::from_ticks(0)).to_millis(),
|
||||
),
|
||||
smoltcp::time::Instant::from_millis(Mono::now().duration_since_epoch().to_millis()),
|
||||
);
|
||||
// Create sockets
|
||||
let dhcp_socket = dhcpv4::Socket::new();
|
||||
@@ -95,7 +92,7 @@ impl Net {
|
||||
/// Polls on the ethernet interface. You should refer to the smoltcp
|
||||
/// documentation for poll() to understand how to call poll efficiently
|
||||
pub fn poll<'a>(&mut self, sockets: &'a mut SocketSet) -> bool {
|
||||
let uptime = Mono::now() - TimerInstantU32::<1000>::from_ticks(0);
|
||||
let uptime = Mono::now().duration_since_epoch();
|
||||
let timestamp = smoltcp::time::Instant::from_millis(uptime.to_millis());
|
||||
|
||||
self.iface.poll(timestamp, &mut self.ethdev, sockets)
|
||||
|
@@ -39,7 +39,10 @@ class EventU32:
|
||||
|
||||
|
||||
class AcsId(enum.IntEnum):
|
||||
MGM_0 = 0
|
||||
SUBSYSTEM = 1
|
||||
MGM_ASSEMBLY = 2
|
||||
MGM_0 = 3
|
||||
MGM_1 = 4
|
||||
|
||||
|
||||
class AcsHkIds(enum.IntEnum):
|
||||
|
1
satrs-example/src/acs/assembly.rs
Normal file
1
satrs-example/src/acs/assembly.rs
Normal file
@@ -0,0 +1 @@
|
||||
// TODO: Write the assembly
|
1
satrs-example/src/acs/ctrl.rs
Normal file
1
satrs-example/src/acs/ctrl.rs
Normal file
@@ -0,0 +1 @@
|
||||
// TODO: Write dummy controller
|
@@ -2,6 +2,7 @@ use derive_new::new;
|
||||
use satrs::hk::{HkRequest, HkRequestVariant};
|
||||
use satrs::mode_tree::{ModeChild, ModeNode};
|
||||
use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender};
|
||||
use satrs_example::config::pus::PUS_MODE_SERVICE;
|
||||
use satrs_example::{DeviceMode, TimestampHelper};
|
||||
use satrs_minisim::acs::lis3mdl::{
|
||||
MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR,
|
||||
@@ -20,13 +21,13 @@ use satrs::mode::{
|
||||
};
|
||||
use satrs::pus::{EcssTmSender, PusTmVariant};
|
||||
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||
use satrs_example::config::components::{NO_SENDER, PUS_MODE_SERVICE};
|
||||
use satrs_example::config::components::NO_SENDER;
|
||||
|
||||
use crate::hk::PusHkHelper;
|
||||
use crate::pus::hk::{HkReply, HkReplyVariant};
|
||||
use crate::requests::CompositeRequest;
|
||||
use crate::spi::SpiInterface;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -165,7 +166,7 @@ pub struct MgmHandlerLis3Mdl<
|
||||
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||
hk_reply_tx: mpsc::SyncSender<GenericMessage<HkReply>>,
|
||||
switch_helper: SwitchHelper,
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
pub com_interface: ComInterface,
|
||||
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||
#[new(value = "PusHkHelper::new(id)")]
|
||||
@@ -491,7 +492,7 @@ mod tests {
|
||||
tmtc::PacketAsVec,
|
||||
ComponentId,
|
||||
};
|
||||
use satrs_example::config::components::{Apid, MGM_ASSEMBLY};
|
||||
use satrs_example::config::{acs::MGM_ASSEMBLY, components::Apid};
|
||||
use satrs_minisim::acs::lis3mdl::MgmLis3RawValues;
|
||||
|
||||
use crate::{eps::TestSwitchHelper, pus::hk::HkReply, requests::CompositeRequest};
|
||||
@@ -576,9 +577,9 @@ mod tests {
|
||||
let id = UniqueApidTargetId::new(Apid::Acs as u16, 1);
|
||||
let mode_node = ModeRequestHandlerMpscBounded::new(id.into(), request_rx);
|
||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||
let (hk_reply_tx, hk_reply_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel();
|
||||
let tm_sender = TmSender::new_heap(tm_tx);
|
||||
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
||||
let (tm_tx, tm_rx) = mpsc::sync_channel(10);
|
||||
let tm_sender = TmTcSender::Heap(tm_tx);
|
||||
let shared_mgm_set = Arc::default();
|
||||
let mut handler = MgmHandlerLis3Mdl::new(
|
||||
id,
|
||||
|
@@ -1 +1,4 @@
|
||||
pub mod assembly;
|
||||
pub mod ctrl;
|
||||
pub mod mgm;
|
||||
pub mod subsystem;
|
||||
|
1
satrs-example/src/acs/subsystem.rs
Normal file
1
satrs-example/src/acs/subsystem.rs
Normal file
@@ -0,0 +1 @@
|
||||
// TODO: Write subsystem
|
@@ -135,23 +135,6 @@ pub mod components {
|
||||
Eps = 6,
|
||||
}
|
||||
|
||||
// Component IDs for components with the PUS APID.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PusId {
|
||||
PusEventManagement = 0,
|
||||
PusRouting = 1,
|
||||
PusTest = 2,
|
||||
PusAction = 3,
|
||||
PusMode = 4,
|
||||
PusHk = 5,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum AcsId {
|
||||
Mgm0 = 0,
|
||||
Assembly = 1,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum EpsId {
|
||||
Pcdu = 0,
|
||||
@@ -164,6 +147,31 @@ pub mod components {
|
||||
TcpServer = 1,
|
||||
}
|
||||
|
||||
pub const EPS_SUBSYSTEM: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Subsystem 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 =
|
||||
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::TcpServer as u32);
|
||||
pub const NO_SENDER: ComponentId = ComponentId::MAX;
|
||||
}
|
||||
|
||||
pub mod pus {
|
||||
use super::components::Apid;
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
|
||||
// Component IDs for components with the PUS APID.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PusId {
|
||||
PusEventManagement = 0,
|
||||
PusRouting = 1,
|
||||
PusTest = 2,
|
||||
PusAction = 3,
|
||||
PusMode = 4,
|
||||
PusHk = 5,
|
||||
}
|
||||
pub const PUS_ACTION_SERVICE: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusAction as u32);
|
||||
pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId =
|
||||
@@ -178,19 +186,26 @@ pub mod components {
|
||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusHk as u32);
|
||||
pub const PUS_SCHED_SERVICE: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(Apid::Sched as u16, 0);
|
||||
}
|
||||
|
||||
pub mod acs {
|
||||
use super::components::Apid;
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum AcsId {
|
||||
Subsystem = 1,
|
||||
Assembly = 2,
|
||||
Mgm0 = 3,
|
||||
Mgm1 = 4,
|
||||
}
|
||||
|
||||
pub const MGM_ASSEMBLY: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Assembly as u32);
|
||||
pub const MGM_HANDLER_0: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
|
||||
pub const EPS_SUBSYSTEM: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Subsystem 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 =
|
||||
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::TcpServer as u32);
|
||||
pub const NO_SENDER: ComponentId = ComponentId::MAX;
|
||||
pub const MGM_HANDLER_1: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
|
||||
}
|
||||
|
||||
pub mod pool {
|
||||
|
@@ -20,7 +20,10 @@ use satrs::{
|
||||
spacepackets::ByteConversionError,
|
||||
};
|
||||
use satrs_example::{
|
||||
config::components::{NO_SENDER, PCDU_HANDLER, PUS_MODE_SERVICE},
|
||||
config::{
|
||||
components::{NO_SENDER, PCDU_HANDLER},
|
||||
pus::PUS_MODE_SERVICE,
|
||||
},
|
||||
DeviceMode, TimestampHelper,
|
||||
};
|
||||
use satrs_minisim::{
|
||||
@@ -35,6 +38,7 @@ use crate::{
|
||||
hk::PusHkHelper,
|
||||
pus::hk::{HkReply, HkReplyVariant},
|
||||
requests::CompositeRequest,
|
||||
tmtc::sender::TmTcSender,
|
||||
};
|
||||
|
||||
pub trait SerialInterface {
|
||||
@@ -203,14 +207,14 @@ pub type SharedSwitchSet = Arc<Mutex<SwitchSet>>;
|
||||
/// Example PCDU device handler.
|
||||
#[derive(new)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub struct PcduHandler<ComInterface: SerialInterface, TmSender: EcssTmSender> {
|
||||
pub struct PcduHandler<ComInterface: SerialInterface> {
|
||||
id: UniqueApidTargetId,
|
||||
dev_str: &'static str,
|
||||
mode_node: ModeRequestHandlerMpscBounded,
|
||||
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||
hk_reply_tx: mpsc::SyncSender<GenericMessage<HkReply>>,
|
||||
switch_request_rx: mpsc::Receiver<GenericMessage<SwitchRequest>>,
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
pub com_interface: ComInterface,
|
||||
shared_switch_map: Arc<Mutex<SwitchSet>>,
|
||||
#[new(value = "PusHkHelper::new(id)")]
|
||||
@@ -223,7 +227,7 @@ pub struct PcduHandler<ComInterface: SerialInterface, TmSender: EcssTmSender> {
|
||||
tm_buf: [u8; 256],
|
||||
}
|
||||
|
||||
impl<ComInterface: SerialInterface, TmSender: EcssTmSender> PcduHandler<ComInterface, TmSender> {
|
||||
impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
|
||||
pub fn periodic_operation(&mut self, op_code: OpCode) {
|
||||
match op_code {
|
||||
OpCode::RegularOp => {
|
||||
@@ -404,17 +408,13 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> PcduHandler<ComInter
|
||||
}
|
||||
}
|
||||
|
||||
impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeProvider
|
||||
for PcduHandler<ComInterface, TmSender>
|
||||
{
|
||||
impl<ComInterface: SerialInterface> ModeProvider for PcduHandler<ComInterface> {
|
||||
fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||
self.mode_and_submode
|
||||
}
|
||||
}
|
||||
|
||||
impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
|
||||
for PcduHandler<ComInterface, TmSender>
|
||||
{
|
||||
impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterface> {
|
||||
type Error = ModeError;
|
||||
fn start_transition(
|
||||
&mut self,
|
||||
@@ -490,17 +490,13 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
|
||||
}
|
||||
}
|
||||
|
||||
impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeNode
|
||||
for PcduHandler<ComInterface, TmSender>
|
||||
{
|
||||
impl<ComInterface: SerialInterface> ModeNode for PcduHandler<ComInterface> {
|
||||
fn id(&self) -> satrs::ComponentId {
|
||||
PCDU_HANDLER.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeChild
|
||||
for PcduHandler<ComInterface, TmSender>
|
||||
{
|
||||
impl<ComInterface: SerialInterface> ModeChild for PcduHandler<ComInterface> {
|
||||
type Sender = mpsc::SyncSender<GenericMessage<ModeReply>>;
|
||||
|
||||
fn add_mode_parent(&mut self, id: satrs::ComponentId, reply_sender: Self::Sender) {
|
||||
@@ -515,7 +511,10 @@ mod tests {
|
||||
use satrs::{
|
||||
mode::ModeRequest, power::SwitchStateBinary, request::GenericMessage, tmtc::PacketAsVec,
|
||||
};
|
||||
use satrs_example::config::components::{Apid, EPS_SUBSYSTEM, MGM_HANDLER_0, PCDU_HANDLER};
|
||||
use satrs_example::config::{
|
||||
acs::MGM_HANDLER_0,
|
||||
components::{Apid, EPS_SUBSYSTEM, PCDU_HANDLER},
|
||||
};
|
||||
use satrs_minisim::eps::SwitchMapBinary;
|
||||
|
||||
use super::*;
|
||||
@@ -563,7 +562,7 @@ mod tests {
|
||||
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
|
||||
pub tm_rx: mpsc::Receiver<PacketAsVec>,
|
||||
pub switch_request_tx: mpsc::Sender<GenericMessage<SwitchRequest>>,
|
||||
pub handler: PcduHandler<SerialInterfaceTest, mpsc::Sender<PacketAsVec>>,
|
||||
pub handler: PcduHandler<SerialInterfaceTest>,
|
||||
}
|
||||
|
||||
impl PcduTestbench {
|
||||
@@ -574,8 +573,8 @@ mod tests {
|
||||
let mode_node =
|
||||
ModeRequestHandlerMpscBounded::new(PCDU_HANDLER.into(), mode_request_rx);
|
||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||
let (hk_reply_tx, hk_reply_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel::<PacketAsVec>();
|
||||
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
||||
let (tm_tx, tm_rx) = mpsc::sync_channel::<PacketAsVec>(5);
|
||||
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
|
||||
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
|
||||
let mut handler = PcduHandler::new(
|
||||
@@ -585,7 +584,7 @@ mod tests {
|
||||
composite_request_rx,
|
||||
hk_reply_tx,
|
||||
switch_reqest_rx,
|
||||
tm_tx,
|
||||
TmTcSender::Heap(tm_tx.clone()),
|
||||
SerialInterfaceTest::default(),
|
||||
shared_switch_map,
|
||||
);
|
||||
|
@@ -16,7 +16,7 @@ use satrs::{
|
||||
},
|
||||
spacepackets::time::cds::CdsTime,
|
||||
};
|
||||
use satrs_example::config::components::PUS_EVENT_MANAGEMENT;
|
||||
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT;
|
||||
|
||||
use crate::update_time;
|
||||
|
||||
|
@@ -5,7 +5,7 @@ use std::{
|
||||
};
|
||||
|
||||
use log::{info, warn};
|
||||
use satrs::tmtc::{StoreAndSendError, TcSender};
|
||||
use satrs::tmtc::StoreAndSendError;
|
||||
use satrs::{
|
||||
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
||||
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
|
||||
@@ -13,6 +13,8 @@ use satrs::{
|
||||
tmtc::PacketSource,
|
||||
};
|
||||
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ConnectionFinishedHandler {}
|
||||
|
||||
@@ -110,13 +112,13 @@ pub type TcpServer<ReceivesTc, SendError> = TcpSpacepacketsServer<
|
||||
SendError,
|
||||
>;
|
||||
|
||||
pub struct TcpTask(pub TcpServer<TcSender, StoreAndSendError>);
|
||||
pub struct TcpTask(pub TcpServer<TmTcSender, StoreAndSendError>);
|
||||
|
||||
impl TcpTask {
|
||||
pub fn new(
|
||||
cfg: ServerConfig,
|
||||
tm_source: SyncTcpTmSource,
|
||||
tc_sender: TcSender,
|
||||
tc_sender: TmTcSender,
|
||||
valid_ids: HashSet<PacketId>,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
Ok(Self(TcpSpacepacketsServer::new(
|
||||
|
@@ -3,12 +3,14 @@ use std::sync::mpsc;
|
||||
|
||||
use log::{info, warn};
|
||||
use satrs::pus::HandlingStatus;
|
||||
use satrs::tmtc::{PacketAsVec, PacketInPool, StoreAndSendError, TcSender};
|
||||
use satrs::tmtc::{PacketAsVec, PacketInPool, StoreAndSendError};
|
||||
use satrs::{
|
||||
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
||||
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
|
||||
};
|
||||
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
|
||||
pub trait UdpTmHandler {
|
||||
fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
|
||||
}
|
||||
@@ -65,7 +67,7 @@ impl UdpTmHandler for DynamicUdpTmHandler {
|
||||
}
|
||||
|
||||
pub struct UdpTmtcServer<TmHandler: UdpTmHandler> {
|
||||
pub udp_tc_server: UdpTcServer<TcSender, StoreAndSendError>,
|
||||
pub udp_tc_server: UdpTcServer<TmTcSender, StoreAndSendError>,
|
||||
pub tm_handler: TmHandler,
|
||||
}
|
||||
|
||||
@@ -105,7 +107,6 @@ impl<TmHandler: UdpTmHandler> UdpTmtcServer<TmHandler> {
|
||||
mod tests {
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::VecDeque,
|
||||
net::IpAddr,
|
||||
sync::{Arc, Mutex},
|
||||
@@ -116,30 +117,16 @@ mod tests {
|
||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||
SpHeader,
|
||||
},
|
||||
tmtc::PacketSenderRaw,
|
||||
ComponentId,
|
||||
};
|
||||
use satrs_example::config::{components, OBSW_SERVER_ADDR};
|
||||
|
||||
use crate::tmtc::sender::{MockSender, TmTcSender};
|
||||
|
||||
use super::*;
|
||||
|
||||
const UDP_SERVER_ID: ComponentId = 0x05;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TestSender {
|
||||
tc_vec: RefCell<VecDeque<PacketAsVec>>,
|
||||
}
|
||||
|
||||
impl PacketSenderRaw for TestSender {
|
||||
type Error = ();
|
||||
|
||||
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||
let mut mut_queue = self.tc_vec.borrow_mut();
|
||||
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct TestTmHandler {
|
||||
addrs_to_send_to: Arc<Mutex<VecDeque<SocketAddr>>>,
|
||||
@@ -154,8 +141,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0);
|
||||
let test_receiver = TestSender::default();
|
||||
// let tc_queue = test_receiver.tc_vec.clone();
|
||||
let test_receiver = TmTcSender::Mock(MockSender::default());
|
||||
let udp_tc_server =
|
||||
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
||||
let tm_handler = TestTmHandler::default();
|
||||
@@ -165,7 +151,13 @@ mod tests {
|
||||
tm_handler,
|
||||
};
|
||||
udp_dyn_server.periodic_operation();
|
||||
let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
|
||||
let queue = udp_dyn_server
|
||||
.udp_tc_server
|
||||
.tc_sender
|
||||
.get_mock_sender()
|
||||
.unwrap()
|
||||
.0
|
||||
.borrow();
|
||||
assert!(queue.is_empty());
|
||||
assert!(tm_handler_calls.lock().unwrap().is_empty());
|
||||
}
|
||||
@@ -173,8 +165,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_transactions() {
|
||||
let sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
|
||||
let test_receiver = TestSender::default();
|
||||
// let tc_queue = test_receiver.tc_vec.clone();
|
||||
let test_receiver = TmTcSender::Mock(MockSender::default());
|
||||
let udp_tc_server =
|
||||
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
||||
let server_addr = udp_tc_server.socket.local_addr().unwrap();
|
||||
@@ -194,7 +185,13 @@ mod tests {
|
||||
client.send_to(&ping_tc, server_addr).unwrap();
|
||||
udp_dyn_server.periodic_operation();
|
||||
{
|
||||
let mut queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow_mut();
|
||||
let mut queue = udp_dyn_server
|
||||
.udp_tc_server
|
||||
.tc_sender
|
||||
.get_mock_sender()
|
||||
.unwrap()
|
||||
.0
|
||||
.borrow_mut();
|
||||
assert!(!queue.is_empty());
|
||||
let packet_with_sender = queue.pop_front().unwrap();
|
||||
assert_eq!(packet_with_sender.packet, ping_tc);
|
||||
@@ -209,7 +206,13 @@ mod tests {
|
||||
assert_eq!(received_addr, client_addr);
|
||||
}
|
||||
udp_dyn_server.periodic_operation();
|
||||
let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
|
||||
let queue = udp_dyn_server
|
||||
.udp_tc_server
|
||||
.tc_sender
|
||||
.get_mock_sender()
|
||||
.unwrap()
|
||||
.0
|
||||
.borrow();
|
||||
assert!(queue.is_empty());
|
||||
drop(queue);
|
||||
// Still tries to send to the same client.
|
||||
|
@@ -36,18 +36,18 @@ use satrs::{
|
||||
pus::{event_man::EventRequestWithToken, EcssTcInMemConverter, HandlingStatus},
|
||||
request::{GenericMessage, MessageMetadata},
|
||||
spacepackets::time::{cds::CdsTime, TimeWriter},
|
||||
tmtc::TcSender,
|
||||
};
|
||||
use satrs_example::{
|
||||
config::{
|
||||
components::{MGM_HANDLER_0, NO_SENDER, PCDU_HANDLER, TCP_SERVER, UDP_SERVER},
|
||||
acs::{MGM_HANDLER_0, MGM_HANDLER_1},
|
||||
components::{NO_SENDER, PCDU_HANDLER, TCP_SERVER, UDP_SERVER},
|
||||
pool::create_sched_tc_pool,
|
||||
tasks::{FREQ_MS_AOCS, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS},
|
||||
OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT,
|
||||
},
|
||||
DeviceMode,
|
||||
};
|
||||
use tm_sender::TmSender;
|
||||
use tmtc::sender::TmTcSender;
|
||||
use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink};
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
@@ -77,7 +77,6 @@ mod logger;
|
||||
mod pus;
|
||||
mod requests;
|
||||
mod spi;
|
||||
mod tm_sender;
|
||||
mod tmtc;
|
||||
|
||||
fn main() {
|
||||
@@ -100,29 +99,35 @@ fn main() {
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(feature = "heap_tmtc"))] {
|
||||
let tm_sender = TmSender::Static(
|
||||
let tm_sender = TmTcSender::Static(
|
||||
PacketSenderWithSharedPool::new(tm_sink_tx.clone(), shared_tm_pool_wrapper.clone())
|
||||
);
|
||||
} else if #[cfg(feature = "heap_tmtc")] {
|
||||
let tm_sender = TmSender::Heap(tm_sink_tx.clone());
|
||||
let tm_sender = TmTcSender::Heap(tm_sink_tx.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let (sim_request_tx, sim_request_rx) = mpsc::channel();
|
||||
let (mgm_sim_reply_tx, mgm_sim_reply_rx) = mpsc::channel();
|
||||
let (mgm_0_sim_reply_tx, mgm_0_sim_reply_rx) = mpsc::channel();
|
||||
let (mgm_1_sim_reply_tx, mgm_1_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::sync_channel(10);
|
||||
let (mgm_0_handler_composite_tx, mgm_0_handler_composite_rx) = mpsc::sync_channel(10);
|
||||
let (mgm_1_handler_composite_tx, mgm_1_handler_composite_rx) = mpsc::sync_channel(10);
|
||||
let (pcdu_handler_composite_tx, pcdu_handler_composite_rx) = mpsc::sync_channel(30);
|
||||
let (mgm_handler_mode_tx, mgm_handler_mode_rx) = mpsc::sync_channel(5);
|
||||
let (mgm_0_handler_mode_tx, mgm_0_handler_mode_rx) = mpsc::sync_channel(5);
|
||||
let (mgm_1_handler_mode_tx, mgm_1_handler_mode_rx) = mpsc::sync_channel(5);
|
||||
let (pcdu_handler_mode_tx, pcdu_handler_mode_rx) = mpsc::sync_channel(5);
|
||||
|
||||
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||
let mut request_map = GenericRequestRouter::default();
|
||||
request_map
|
||||
.composite_router_map
|
||||
.insert(MGM_HANDLER_0.id(), mgm_handler_composite_tx);
|
||||
.insert(MGM_HANDLER_0.id(), mgm_0_handler_composite_tx);
|
||||
request_map
|
||||
.composite_router_map
|
||||
.insert(MGM_HANDLER_0.id(), mgm_1_handler_composite_tx);
|
||||
request_map
|
||||
.composite_router_map
|
||||
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
|
||||
@@ -232,7 +237,7 @@ fn main() {
|
||||
tc_source_rx,
|
||||
PusTcDistributor::new(tm_sender.clone(), pus_router),
|
||||
));
|
||||
let tc_sender = TcSender::StaticStore(tc_sender_with_shared_pool);
|
||||
let tc_sender = TmTcSender::Static(tc_sender_with_shared_pool);
|
||||
let udp_tm_handler = StaticUdpTmHandler {
|
||||
tm_rx: tm_server_rx,
|
||||
tm_store: shared_tm_pool.clone(),
|
||||
@@ -242,7 +247,7 @@ fn main() {
|
||||
tc_source_rx,
|
||||
PusTcDistributor::new(tm_sender.clone(), pus_router),
|
||||
));
|
||||
let tc_sender = TcSender::HeapStore(tc_source_tx.clone());
|
||||
let tc_sender = TmTcSender::Heap(tc_source_tx.clone());
|
||||
let udp_tm_handler = DynamicUdpTmHandler {
|
||||
tm_rx: tm_server_rx,
|
||||
};
|
||||
@@ -294,34 +299,67 @@ fn main() {
|
||||
let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20);
|
||||
let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone());
|
||||
|
||||
let shared_mgm_set = Arc::default();
|
||||
let mgm_mode_node =
|
||||
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_0.into(), mgm_handler_mode_rx);
|
||||
let mgm_spi_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
|
||||
sim_client.add_reply_recipient(satrs_minisim::SimComponent::MgmLis3Mdl, mgm_sim_reply_tx);
|
||||
SpiSimInterfaceWrapper::Sim(SpiSimInterface {
|
||||
sim_request_tx: sim_request_tx.clone(),
|
||||
sim_reply_rx: mgm_sim_reply_rx,
|
||||
})
|
||||
} else {
|
||||
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default())
|
||||
};
|
||||
let mut mgm_handler = MgmHandlerLis3Mdl::new(
|
||||
let shared_mgm_0_set = Arc::default();
|
||||
let shared_mgm_1_set = Arc::default();
|
||||
let mgm_0_mode_node =
|
||||
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_0.into(), mgm_0_handler_mode_rx);
|
||||
let mgm_1_mode_node =
|
||||
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_1.into(), mgm_1_handler_mode_rx);
|
||||
let (mgm_0_spi_interface, mgm_1_spi_interface) =
|
||||
if let Some(sim_client) = opt_sim_client.as_mut() {
|
||||
sim_client
|
||||
.add_reply_recipient(satrs_minisim::SimComponent::Mgm0Lis3Mdl, mgm_0_sim_reply_tx);
|
||||
sim_client
|
||||
.add_reply_recipient(satrs_minisim::SimComponent::Mgm1Lis3Mdl, mgm_1_sim_reply_tx);
|
||||
(
|
||||
SpiSimInterfaceWrapper::Sim(SpiSimInterface {
|
||||
sim_request_tx: sim_request_tx.clone(),
|
||||
sim_reply_rx: mgm_0_sim_reply_rx,
|
||||
}),
|
||||
SpiSimInterfaceWrapper::Sim(SpiSimInterface {
|
||||
sim_request_tx: sim_request_tx.clone(),
|
||||
sim_reply_rx: mgm_1_sim_reply_rx,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()),
|
||||
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()),
|
||||
)
|
||||
};
|
||||
let mut mgm_0_handler = MgmHandlerLis3Mdl::new(
|
||||
MGM_HANDLER_0,
|
||||
"MGM_0",
|
||||
mgm_mode_node,
|
||||
mgm_handler_composite_rx,
|
||||
mgm_0_mode_node,
|
||||
mgm_0_handler_composite_rx,
|
||||
pus_hk_reply_tx.clone(),
|
||||
switch_helper.clone(),
|
||||
tm_sender,
|
||||
mgm_spi_interface,
|
||||
shared_mgm_set,
|
||||
tm_sender.clone(),
|
||||
mgm_0_spi_interface,
|
||||
shared_mgm_0_set,
|
||||
);
|
||||
// Connect PUS service to device handler.
|
||||
let mut mgm_1_handler = MgmHandlerLis3Mdl::new(
|
||||
MGM_HANDLER_1,
|
||||
"MGM_1",
|
||||
mgm_1_mode_node,
|
||||
mgm_1_handler_composite_rx,
|
||||
pus_hk_reply_tx.clone(),
|
||||
switch_helper.clone(),
|
||||
tm_sender.clone(),
|
||||
mgm_1_spi_interface,
|
||||
shared_mgm_1_set,
|
||||
);
|
||||
// Connect PUS service to device handlers.
|
||||
connect_mode_nodes(
|
||||
&mut pus_stack.mode_srv,
|
||||
mgm_handler_mode_tx,
|
||||
&mut mgm_handler,
|
||||
mgm_0_handler_mode_tx,
|
||||
&mut mgm_0_handler,
|
||||
pus_mode_reply_tx.clone(),
|
||||
);
|
||||
connect_mode_nodes(
|
||||
&mut pus_stack.mode_srv,
|
||||
mgm_1_handler_mode_tx,
|
||||
&mut mgm_1_handler,
|
||||
pus_mode_reply_tx.clone(),
|
||||
);
|
||||
|
||||
@@ -343,7 +381,7 @@ fn main() {
|
||||
pcdu_handler_composite_rx,
|
||||
pus_hk_reply_tx,
|
||||
switch_request_rx,
|
||||
tm_sink_tx,
|
||||
tm_sender.clone(),
|
||||
pcdu_serial_interface,
|
||||
shared_switch_set,
|
||||
);
|
||||
@@ -416,7 +454,8 @@ fn main() {
|
||||
let jh_aocs = thread::Builder::new()
|
||||
.name("sat-rs aocs".to_string())
|
||||
.spawn(move || loop {
|
||||
mgm_handler.periodic_operation();
|
||||
mgm_0_handler.periodic_operation();
|
||||
mgm_1_handler.periodic_operation();
|
||||
thread::sleep(Duration::from_millis(FREQ_MS_AOCS));
|
||||
})
|
||||
.unwrap();
|
||||
|
@@ -16,13 +16,13 @@ use satrs::pus::{
|
||||
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId};
|
||||
use satrs_example::config::components::PUS_ACTION_SERVICE;
|
||||
use satrs_example::config::pus::PUS_ACTION_SERVICE;
|
||||
use satrs_example::config::tmtc_err;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::requests::GenericRequestRouter;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
|
||||
use super::{
|
||||
create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus,
|
||||
@@ -206,7 +206,7 @@ impl PusTcToRequestConverter<ActivePusActionRequestStd, ActionRequest> for Actio
|
||||
}
|
||||
|
||||
pub fn create_action_service(
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
tc_in_mem_converter: EcssTcInMemConverter,
|
||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
action_router: GenericRequestRouter,
|
||||
@@ -236,8 +236,6 @@ pub fn create_action_service(
|
||||
pub struct ActionServiceWrapper {
|
||||
pub(crate) service: PusTargetedRequestService<
|
||||
MpscTcReceiver,
|
||||
TmSender,
|
||||
EcssTcInMemConverter,
|
||||
VerificationReporter,
|
||||
ActionRequestConverter,
|
||||
ActionReplyHandler,
|
||||
@@ -310,7 +308,7 @@ mod tests {
|
||||
{
|
||||
pub fn new_for_action(owner_id: ComponentId, target_id: ComponentId) -> Self {
|
||||
let _ = env_logger::builder().is_test(true).try_init();
|
||||
let (tm_funnel_tx, tm_funnel_rx) = mpsc::channel();
|
||||
let (tm_funnel_tx, tm_funnel_rx) = mpsc::sync_channel(5);
|
||||
let (pus_action_tx, pus_action_rx) = mpsc::channel();
|
||||
let (action_reply_tx, action_reply_rx) = mpsc::channel();
|
||||
let (action_req_tx, action_req_rx) = mpsc::sync_channel(10);
|
||||
@@ -324,9 +322,9 @@ mod tests {
|
||||
PusServiceHelper::new(
|
||||
owner_id,
|
||||
pus_action_rx,
|
||||
tm_funnel_tx.clone(),
|
||||
TmTcSender::Heap(tm_funnel_tx.clone()),
|
||||
verif_reporter,
|
||||
EcssTcInVecConverter::default(),
|
||||
EcssTcInMemConverter::Heap(EcssTcInVecConverter::default()),
|
||||
),
|
||||
ActionRequestConverter::default(),
|
||||
DefaultActiveActionRequestMap::default(),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use std::sync::mpsc;
|
||||
|
||||
use crate::pus::create_verification_reporter;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
use satrs::pus::event_man::EventRequestWithToken;
|
||||
use satrs::pus::event_srv::PusEventServiceHandler;
|
||||
use satrs::pus::verification::VerificationReporter;
|
||||
@@ -10,12 +10,12 @@ use satrs::pus::{
|
||||
PartialPusHandlingError, PusServiceHelper,
|
||||
};
|
||||
use satrs::spacepackets::ecss::PusServiceId;
|
||||
use satrs_example::config::components::PUS_EVENT_MANAGEMENT;
|
||||
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT;
|
||||
|
||||
use super::{DirectPusService, HandlingStatus};
|
||||
|
||||
pub fn create_event_service(
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
tm_in_pool_converter: EcssTcInMemConverter,
|
||||
pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
||||
@@ -38,7 +38,7 @@ pub fn create_event_service(
|
||||
pub struct EventServiceWrapper {
|
||||
pub handler: PusEventServiceHandler<
|
||||
MpscTcReceiver,
|
||||
TmSender,
|
||||
TmTcSender,
|
||||
EcssTcInMemConverter,
|
||||
VerificationReporter,
|
||||
>,
|
||||
|
@@ -13,14 +13,14 @@ use satrs::request::{GenericMessage, UniqueApidTargetId};
|
||||
use satrs::res_code::ResultU16;
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId};
|
||||
use satrs_example::config::components::PUS_HK_SERVICE;
|
||||
use satrs_example::config::pus::PUS_HK_SERVICE;
|
||||
use satrs_example::config::{hk_err, tmtc_err};
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler};
|
||||
use crate::requests::GenericRequestRouter;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
|
||||
use super::{HandlingStatus, PusTargetedRequestService, TargetedPusService};
|
||||
|
||||
@@ -241,7 +241,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
|
||||
}
|
||||
|
||||
pub fn create_hk_service(
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
tc_in_mem_converter: EcssTcInMemConverter,
|
||||
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
request_router: GenericRequestRouter,
|
||||
@@ -269,8 +269,6 @@ pub fn create_hk_service(
|
||||
pub struct HkServiceWrapper {
|
||||
pub(crate) service: PusTargetedRequestService<
|
||||
MpscTcReceiver,
|
||||
TmSender,
|
||||
EcssTcInMemConverter,
|
||||
VerificationReporter,
|
||||
HkRequestConverter,
|
||||
HkReplyHandler,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use crate::requests::GenericRequestRouter;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
use log::warn;
|
||||
use satrs::pool::PoolAddr;
|
||||
use satrs::pus::verification::{
|
||||
@@ -8,9 +8,9 @@ use satrs::pus::verification::{
|
||||
};
|
||||
use satrs::pus::{
|
||||
ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConversionProvider,
|
||||
EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, GenericRoutingError,
|
||||
HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, PusServiceHelper,
|
||||
PusTcToRequestConverter, TcInMemory,
|
||||
EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError,
|
||||
GenericRoutingError, HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter,
|
||||
PusServiceHelper, PusTcToRequestConverter, TcInMemory,
|
||||
};
|
||||
use satrs::queue::{GenericReceiveError, GenericSendError};
|
||||
use satrs::request::{Apid, GenericMessage, MessageMetadata};
|
||||
@@ -18,7 +18,7 @@ use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
||||
use satrs::tmtc::{PacketAsVec, PacketInPool};
|
||||
use satrs::ComponentId;
|
||||
use satrs_example::config::components::PUS_ROUTING_SERVICE;
|
||||
use satrs_example::config::pus::PUS_ROUTING_SERVICE;
|
||||
use satrs_example::config::{tmtc_err, CustomPusServiceId};
|
||||
use satrs_example::TimestampHelper;
|
||||
use std::fmt::Debug;
|
||||
@@ -53,14 +53,14 @@ pub struct PusTcMpscRouter {
|
||||
pub struct PusTcDistributor {
|
||||
#[allow(dead_code)]
|
||||
pub id: ComponentId,
|
||||
pub tm_sender: TmSender,
|
||||
pub tm_sender: TmTcSender,
|
||||
pub verif_reporter: VerificationReporter,
|
||||
pub pus_router: PusTcMpscRouter,
|
||||
stamp_helper: TimestampHelper,
|
||||
}
|
||||
|
||||
impl PusTcDistributor {
|
||||
pub fn new(tm_sender: TmSender, pus_router: PusTcMpscRouter) -> Self {
|
||||
pub fn new(tm_sender: TmTcSender, pus_router: PusTcMpscRouter) -> Self {
|
||||
Self {
|
||||
id: PUS_ROUTING_SERVICE.raw(),
|
||||
tm_sender,
|
||||
@@ -269,8 +269,6 @@ pub trait DirectPusService {
|
||||
/// 3. [Self::check_for_request_timeouts] which checks for request timeouts, covering step 7.
|
||||
pub struct PusTargetedRequestService<
|
||||
TcReceiver: EcssTcReceiver,
|
||||
TmSender: EcssTmSender,
|
||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
||||
@@ -280,7 +278,7 @@ pub struct PusTargetedRequestService<
|
||||
ReplyType,
|
||||
> {
|
||||
pub service_helper:
|
||||
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
|
||||
PusServiceHelper<TcReceiver, TmTcSender, EcssTcInMemConverter, VerificationReporter>,
|
||||
pub request_router: GenericRequestRouter,
|
||||
pub request_converter: RequestConverter,
|
||||
pub active_request_map: ActiveRequestMap,
|
||||
@@ -291,8 +289,6 @@ pub struct PusTargetedRequestService<
|
||||
|
||||
impl<
|
||||
TcReceiver: EcssTcReceiver,
|
||||
TmSender: EcssTmSender,
|
||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
||||
@@ -303,8 +299,6 @@ impl<
|
||||
>
|
||||
PusTargetedRequestService<
|
||||
TcReceiver,
|
||||
TmSender,
|
||||
TcInMemConverter,
|
||||
VerificationReporter,
|
||||
RequestConverter,
|
||||
ReplyHandler,
|
||||
@@ -319,8 +313,8 @@ where
|
||||
pub fn new(
|
||||
service_helper: PusServiceHelper<
|
||||
TcReceiver,
|
||||
TmSender,
|
||||
TcInMemConverter,
|
||||
TmTcSender,
|
||||
EcssTcInMemConverter,
|
||||
VerificationReporter,
|
||||
>,
|
||||
request_converter: RequestConverter,
|
||||
@@ -546,7 +540,7 @@ pub(crate) mod tests {
|
||||
use satrs::{
|
||||
pus::{
|
||||
verification::test_util::TestVerificationReporter, ActivePusRequestStd,
|
||||
ActiveRequestMapProvider, EcssTcInVecConverter, MpscTcReceiver,
|
||||
ActiveRequestMapProvider, MpscTcReceiver,
|
||||
},
|
||||
request::UniqueApidTargetId,
|
||||
spacepackets::{
|
||||
@@ -767,8 +761,6 @@ pub(crate) mod tests {
|
||||
> {
|
||||
pub service: PusTargetedRequestService<
|
||||
MpscTcReceiver,
|
||||
MpscTmAsVecSender,
|
||||
EcssTcInVecConverter,
|
||||
TestVerificationReporter,
|
||||
RequestConverter,
|
||||
ReplyHandler,
|
||||
|
@@ -1,10 +1,11 @@
|
||||
use derive_new::new;
|
||||
use satrs::mode_tree::{ModeNode, ModeParent};
|
||||
use satrs_example::config::pus::PUS_MODE_SERVICE;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::requests::GenericRequestRouter;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
use satrs::pus::verification::VerificationReporter;
|
||||
use satrs::pus::{
|
||||
DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver,
|
||||
@@ -33,7 +34,6 @@ use satrs::{
|
||||
},
|
||||
ComponentId,
|
||||
};
|
||||
use satrs_example::config::components::PUS_MODE_SERVICE;
|
||||
use satrs_example::config::{mode_err, tmtc_err, CustomPusServiceId};
|
||||
|
||||
use super::{
|
||||
@@ -209,7 +209,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
|
||||
}
|
||||
|
||||
pub fn create_mode_service(
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
tc_in_mem_converter: EcssTcInMemConverter,
|
||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
mode_router: GenericRequestRouter,
|
||||
@@ -237,8 +237,6 @@ pub fn create_mode_service(
|
||||
pub struct ModeServiceWrapper {
|
||||
pub(crate) service: PusTargetedRequestService<
|
||||
MpscTcReceiver,
|
||||
TmSender,
|
||||
EcssTcInMemConverter,
|
||||
VerificationReporter,
|
||||
ModeRequestConverter,
|
||||
ModeReplyHandler,
|
||||
|
@@ -2,7 +2,7 @@ use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::pus::create_verification_reporter;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
use log::info;
|
||||
use satrs::pool::{PoolProvider, StaticMemoryPool};
|
||||
use satrs::pus::scheduler::{PusScheduler, TcInfo};
|
||||
@@ -15,7 +15,7 @@ use satrs::pus::{
|
||||
use satrs::spacepackets::ecss::PusServiceId;
|
||||
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool};
|
||||
use satrs::ComponentId;
|
||||
use satrs_example::config::components::PUS_SCHED_SERVICE;
|
||||
use satrs_example::config::pus::PUS_SCHED_SERVICE;
|
||||
|
||||
use super::{DirectPusService, HandlingStatus};
|
||||
|
||||
@@ -83,7 +83,7 @@ impl TcReleaseProvider for TcReleaser {
|
||||
pub struct SchedulingServiceWrapper {
|
||||
pub pus_11_handler: PusSchedServiceHandler<
|
||||
MpscTcReceiver,
|
||||
TmSender,
|
||||
TmTcSender,
|
||||
EcssTcInMemConverter,
|
||||
VerificationReporter,
|
||||
PusScheduler,
|
||||
@@ -173,7 +173,7 @@ impl SchedulingServiceWrapper {
|
||||
}
|
||||
|
||||
pub fn create_scheduler_service(
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
tc_in_mem_converter: EcssTcInMemConverter,
|
||||
tc_releaser: TcReleaser,
|
||||
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use crate::pus::create_verification_reporter;
|
||||
use crate::tm_sender::TmSender;
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
use log::info;
|
||||
use satrs::event_man::{EventMessage, EventMessageU32};
|
||||
use satrs::pus::test::PusService17TestHandler;
|
||||
@@ -11,14 +11,14 @@ use satrs::pus::{
|
||||
};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
||||
use satrs_example::config::components::PUS_TEST_SERVICE;
|
||||
use satrs_example::config::pus::PUS_TEST_SERVICE;
|
||||
use satrs_example::config::{tmtc_err, TEST_EVENT};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use super::{DirectPusService, HandlingStatus};
|
||||
|
||||
pub fn create_test_service(
|
||||
tm_sender: TmSender,
|
||||
tm_sender: TmTcSender,
|
||||
tc_in_mem_converter: EcssTcInMemConverter,
|
||||
event_sender: mpsc::SyncSender<EventMessageU32>,
|
||||
pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||
@@ -39,7 +39,7 @@ pub fn create_test_service(
|
||||
pub struct TestCustomServiceWrapper {
|
||||
pub handler: PusService17TestHandler<
|
||||
MpscTcReceiver,
|
||||
TmSender,
|
||||
TmTcSender,
|
||||
EcssTcInMemConverter,
|
||||
VerificationReporter,
|
||||
>,
|
||||
|
@@ -14,7 +14,7 @@ use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs::spacepackets::ecss::PusPacket;
|
||||
use satrs::ComponentId;
|
||||
use satrs_example::config::components::PUS_ROUTING_SERVICE;
|
||||
use satrs_example::config::pus::PUS_ROUTING_SERVICE;
|
||||
use satrs_example::config::tmtc_err;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@@ -1,33 +0,0 @@
|
||||
use std::sync::mpsc;
|
||||
|
||||
use satrs::{
|
||||
pus::EcssTmSender,
|
||||
queue::GenericSendError,
|
||||
spacepackets::ecss::WritablePusPacket,
|
||||
tmtc::{PacketAsVec, PacketSenderWithSharedPool},
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TmSender {
|
||||
Static(PacketSenderWithSharedPool),
|
||||
Heap(mpsc::SyncSender<PacketAsVec>),
|
||||
}
|
||||
|
||||
impl EcssTmSender for TmSender {
|
||||
fn send_tm(
|
||||
&self,
|
||||
sender_id: satrs::ComponentId,
|
||||
tm: satrs::pus::PusTmVariant,
|
||||
) -> Result<(), satrs::pus::EcssTmtcError> {
|
||||
match self {
|
||||
TmSender::Static(sync_sender) => sync_sender.send_tm(sender_id, tm),
|
||||
TmSender::Heap(sync_sender) => match tm {
|
||||
satrs::pus::PusTmVariant::InStore(_) => panic!("can not send TM in store"),
|
||||
satrs::pus::PusTmVariant::Direct(pus_tm_creator) => sync_sender
|
||||
.send(PacketAsVec::new(sender_id, pus_tm_creator.to_vec()?))
|
||||
.map_err(|_| GenericSendError::RxDisconnected.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,2 +1,3 @@
|
||||
pub mod sender;
|
||||
pub mod tc_source;
|
||||
pub mod tm_sink;
|
||||
|
75
satrs-example/src/tmtc/sender.rs
Normal file
75
satrs-example/src/tmtc/sender.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use std::{cell::RefCell, collections::VecDeque, sync::mpsc};
|
||||
|
||||
use satrs::{
|
||||
pus::EcssTmSender,
|
||||
queue::GenericSendError,
|
||||
spacepackets::ecss::WritablePusPacket,
|
||||
tmtc::{PacketAsVec, PacketSenderRaw, PacketSenderWithSharedPool, StoreAndSendError},
|
||||
ComponentId,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MockSender(pub RefCell<VecDeque<PacketAsVec>>);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TmTcSender {
|
||||
Static(PacketSenderWithSharedPool),
|
||||
Heap(mpsc::SyncSender<PacketAsVec>),
|
||||
Mock(MockSender),
|
||||
}
|
||||
|
||||
impl TmTcSender {
|
||||
#[allow(dead_code)]
|
||||
pub fn get_mock_sender(&mut self) -> Option<&mut MockSender> {
|
||||
match self {
|
||||
TmTcSender::Mock(sender) => Some(sender),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssTmSender for TmTcSender {
|
||||
fn send_tm(
|
||||
&self,
|
||||
sender_id: satrs::ComponentId,
|
||||
tm: satrs::pus::PusTmVariant,
|
||||
) -> Result<(), satrs::pus::EcssTmtcError> {
|
||||
match self {
|
||||
TmTcSender::Static(sync_sender) => sync_sender.send_tm(sender_id, tm),
|
||||
TmTcSender::Heap(sync_sender) => match tm {
|
||||
satrs::pus::PusTmVariant::InStore(_) => panic!("can not send TM in store"),
|
||||
satrs::pus::PusTmVariant::Direct(pus_tm_creator) => sync_sender
|
||||
.send(PacketAsVec::new(sender_id, pus_tm_creator.to_vec()?))
|
||||
.map_err(|_| GenericSendError::RxDisconnected.into()),
|
||||
},
|
||||
TmTcSender::Mock(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketSenderRaw for TmTcSender {
|
||||
type Error = StoreAndSendError;
|
||||
|
||||
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
||||
match self {
|
||||
TmTcSender::Static(packet_sender_with_shared_pool) => {
|
||||
packet_sender_with_shared_pool.send_packet(sender_id, packet)
|
||||
}
|
||||
TmTcSender::Heap(sync_sender) => sync_sender
|
||||
.send_packet(sender_id, packet)
|
||||
.map_err(StoreAndSendError::Send),
|
||||
TmTcSender::Mock(sender) => sender.send_packet(sender_id, packet),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketSenderRaw for MockSender {
|
||||
type Error = StoreAndSendError;
|
||||
|
||||
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||
let mut mut_queue = self.0.borrow_mut();
|
||||
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -203,7 +203,7 @@ pub mod tests {
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
assert_eq!(sim_reply.component(), SimComponent::MgmLis3Mdl);
|
||||
assert_eq!(sim_reply.component(), SimComponent::Mgm0Lis3Mdl);
|
||||
let reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
|
||||
.expect("failed to deserialize MGM sensor values");
|
||||
assert_eq!(reply.common.switch_state, SwitchStateBinary::Off);
|
||||
@@ -226,7 +226,7 @@ pub mod tests {
|
||||
let mut sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_some());
|
||||
let mut sim_reply = sim_reply_res.unwrap();
|
||||
assert_eq!(sim_reply.component(), SimComponent::MgmLis3Mdl);
|
||||
assert_eq!(sim_reply.component(), SimComponent::Mgm0Lis3Mdl);
|
||||
let first_reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
|
||||
.expect("failed to deserialize MGM sensor values");
|
||||
sim_testbench.step_until(Duration::from_millis(50)).unwrap();
|
||||
|
@@ -24,7 +24,8 @@ const PCDU_REQ_WIRETAPPING: bool = false;
|
||||
const MGT_REQ_WIRETAPPING: bool = false;
|
||||
|
||||
pub struct ModelAddrWrapper {
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
mgm_0_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
mgm_1_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
}
|
||||
@@ -42,12 +43,14 @@ pub struct SimController {
|
||||
|
||||
impl ModelAddrWrapper {
|
||||
pub fn new(
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
mgm_0_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
mgm_1_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
) -> Self {
|
||||
Self {
|
||||
mgm_addr,
|
||||
mgm_0_addr,
|
||||
mgm_1_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
}
|
||||
@@ -96,7 +99,8 @@ impl SimController {
|
||||
}
|
||||
if let Err(e) = match request.component() {
|
||||
SimComponent::SimCtrl => self.handle_ctrl_request(&request),
|
||||
SimComponent::MgmLis3Mdl => self.handle_mgm_request(&request),
|
||||
SimComponent::Mgm0Lis3Mdl => self.handle_mgm_request(0, &request),
|
||||
SimComponent::Mgm1Lis3Mdl => self.handle_mgm_request(1, &request),
|
||||
SimComponent::Mgt => self.handle_mgt_request(&request),
|
||||
SimComponent::Pcdu => self.handle_pcdu_request(&request),
|
||||
} {
|
||||
@@ -128,19 +132,25 @@ impl SimController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_mgm_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
|
||||
fn handle_mgm_request(
|
||||
&mut self,
|
||||
mgm_idx: usize,
|
||||
request: &SimRequest,
|
||||
) -> Result<(), SimRequestError> {
|
||||
let mgm_request = MgmRequestLis3Mdl::from_sim_message(request)?;
|
||||
if MGM_REQ_WIRETAPPING {
|
||||
log::info!("received MGM request: {:?}", mgm_request);
|
||||
}
|
||||
match mgm_request {
|
||||
MgmRequestLis3Mdl::RequestSensorData => {
|
||||
let addr = match mgm_idx {
|
||||
0 => &self.addr_wrapper.mgm_0_addr,
|
||||
1 => &self.addr_wrapper.mgm_1_addr,
|
||||
|
||||
_ => panic!("invalid mgm index"),
|
||||
};
|
||||
self.simulation
|
||||
.process_event(
|
||||
MagnetometerModel::send_sensor_values,
|
||||
(),
|
||||
&self.addr_wrapper.mgm_addr,
|
||||
)
|
||||
.process_event(MagnetometerModel::send_sensor_values, (), addr)
|
||||
.expect("event execution error for mgm");
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,8 @@ pub const SWITCH_INFO_DELAY_MS: u64 = 10;
|
||||
|
||||
pub struct PcduModel {
|
||||
pub switcher_map: SwitchMapBinaryWrapper,
|
||||
pub mgm_switch: Output<SwitchStateBinary>,
|
||||
pub mgm_0_switch: Output<SwitchStateBinary>,
|
||||
pub mgm_1_switch: Output<SwitchStateBinary>,
|
||||
pub mgt_switch: Output<SwitchStateBinary>,
|
||||
pub reply_sender: mpsc::Sender<SimReply>,
|
||||
}
|
||||
@@ -23,7 +24,8 @@ impl PcduModel {
|
||||
pub fn new(reply_sender: mpsc::Sender<SimReply>) -> Self {
|
||||
Self {
|
||||
switcher_map: Default::default(),
|
||||
mgm_switch: Output::new(),
|
||||
mgm_0_switch: Output::new(),
|
||||
mgm_1_switch: Output::new(),
|
||||
mgt_switch: Output::new(),
|
||||
reply_sender,
|
||||
}
|
||||
@@ -55,7 +57,7 @@ impl PcduModel {
|
||||
*val = switch_and_target_state.1;
|
||||
match switch_and_target_state.0 {
|
||||
PcduSwitch::Mgm => {
|
||||
self.mgm_switch.send(switch_and_target_state.1).await;
|
||||
self.mgm_0_switch.send(switch_and_target_state.1).await;
|
||||
}
|
||||
PcduSwitch::Mgt => {
|
||||
self.mgt_switch.send(switch_and_target_state.1).await;
|
||||
|
@@ -5,7 +5,8 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
pub enum SimComponent {
|
||||
SimCtrl,
|
||||
MgmLis3Mdl,
|
||||
Mgm0Lis3Mdl,
|
||||
Mgm1Lis3Mdl,
|
||||
Mgt,
|
||||
Pcdu,
|
||||
}
|
||||
@@ -277,7 +278,7 @@ pub mod acs {
|
||||
}
|
||||
|
||||
impl SerializableSimMsgPayload<SimRequest> for MgmRequestLis3Mdl {
|
||||
const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
|
||||
const TARGET: SimComponent = SimComponent::Mgm0Lis3Mdl;
|
||||
}
|
||||
|
||||
// Normally, small magnetometers generate their output as a signed 16 bit raw format or something
|
||||
@@ -368,7 +369,7 @@ pub mod acs {
|
||||
}
|
||||
|
||||
impl SerializableSimMsgPayload<SimReply> for MgmLis3MdlReply {
|
||||
const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
|
||||
const TARGET: SimComponent = SimComponent::Mgm0Lis3Mdl;
|
||||
}
|
||||
|
||||
impl MgmReplyProvider for MgmLis3MdlReply {
|
||||
@@ -418,7 +419,7 @@ pub mod acs {
|
||||
}
|
||||
|
||||
impl SerializableSimMsgPayload<SimReply> for MgtReply {
|
||||
const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
|
||||
const TARGET: SimComponent = SimComponent::Mgm0Lis3Mdl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,11 +31,15 @@ fn create_sim_controller(
|
||||
request_receiver: mpsc::Receiver<SimRequest>,
|
||||
) -> SimController {
|
||||
// Instantiate models and their mailboxes.
|
||||
let mgm_model =
|
||||
let mgm_0_model =
|
||||
MagnetometerModel::new_for_lis3mdl(Duration::from_millis(50), reply_sender.clone());
|
||||
let mgm_1_model =
|
||||
MagnetometerModel::new_for_lis3mdl(Duration::from_millis(50), reply_sender.clone());
|
||||
|
||||
let mgm_mailbox = Mailbox::new();
|
||||
let mgm_addr = mgm_mailbox.address();
|
||||
let mgm_0_mailbox = Mailbox::new();
|
||||
let mgm_0_addr = mgm_0_mailbox.address();
|
||||
let mgm_1_mailbox = Mailbox::new();
|
||||
let mgm_1_addr = mgm_1_mailbox.address();
|
||||
let pcdu_mailbox = Mailbox::new();
|
||||
let pcdu_addr = pcdu_mailbox.address();
|
||||
let mgt_mailbox = Mailbox::new();
|
||||
@@ -43,8 +47,11 @@ fn create_sim_controller(
|
||||
|
||||
let mut pcdu_model = PcduModel::new(reply_sender.clone());
|
||||
pcdu_model
|
||||
.mgm_switch
|
||||
.connect(MagnetometerModel::switch_device, &mgm_addr);
|
||||
.mgm_0_switch
|
||||
.connect(MagnetometerModel::switch_device, &mgm_0_addr);
|
||||
pcdu_model
|
||||
.mgm_1_switch
|
||||
.connect(MagnetometerModel::switch_device, &mgm_1_addr);
|
||||
|
||||
let mut mgt_model = MagnetorquerModel::new(reply_sender.clone());
|
||||
// Input connections.
|
||||
@@ -52,9 +59,14 @@ fn create_sim_controller(
|
||||
.mgt_switch
|
||||
.connect(MagnetorquerModel::switch_device, &mgt_addr);
|
||||
// Output connections.
|
||||
mgt_model
|
||||
.gen_magnetic_field
|
||||
.connect(MagnetometerModel::apply_external_magnetic_field, &mgm_addr);
|
||||
mgt_model.gen_magnetic_field.connect(
|
||||
MagnetometerModel::apply_external_magnetic_field,
|
||||
&mgm_0_addr,
|
||||
);
|
||||
mgt_model.gen_magnetic_field.connect(
|
||||
MagnetometerModel::apply_external_magnetic_field,
|
||||
&mgm_1_addr,
|
||||
);
|
||||
|
||||
// Instantiate the simulator
|
||||
let sys_clock = SystemClock::from_system_time(start_time, SystemTime::now());
|
||||
@@ -63,9 +75,10 @@ fn create_sim_controller(
|
||||
} else {
|
||||
SimInit::new()
|
||||
};
|
||||
let addrs = ModelAddrWrapper::new(mgm_addr, pcdu_addr, mgt_addr);
|
||||
let addrs = ModelAddrWrapper::new(mgm_0_addr, mgm_1_addr, pcdu_addr, mgt_addr);
|
||||
let (simulation, scheduler) = sim_init
|
||||
.add_model(mgm_model, mgm_mailbox, "MGM model")
|
||||
.add_model(mgm_0_model, mgm_0_mailbox, "MGM 0 model")
|
||||
.add_model(mgm_1_model, mgm_1_mailbox, "MGM 1 model")
|
||||
.add_model(pcdu_model, pcdu_mailbox, "PCDU model")
|
||||
.add_model(mgt_model, mgt_mailbox, "MGT model")
|
||||
.init(start_time)
|
||||
|
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
# [v0.3.0-alpha.0] 2025-02-18
|
||||
|
||||
`spacepackets` v0.13
|
||||
|
||||
## Changed
|
||||
|
||||
- Renamed `StaticPoolConfig::new` to `StaticPoolConfig::new_from_subpool_cfg_tuples`. The new
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "satrs"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0-alpha.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.82.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
@@ -13,79 +13,29 @@ keywords = ["no-std", "space", "aerospace"]
|
||||
categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"]
|
||||
|
||||
[dependencies]
|
||||
satrs-shared = ">=0.1.3, <=0.2"
|
||||
delegate = ">0.7, <=0.13"
|
||||
paste = "1"
|
||||
derive-new = ">=0.6, <=0.7"
|
||||
smallvec = "1"
|
||||
crc = "3"
|
||||
num_enum = { version = ">0.5, <=0.7", default-features = false }
|
||||
spacepackets = { version = "0.13", default-features = false }
|
||||
cobs = { version = "0.3", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
thiserror = { version = "2", default-features = false }
|
||||
|
||||
[dependencies.satrs-shared]
|
||||
version = ">=0.1.3, <=0.2"
|
||||
|
||||
[dependencies.num_enum]
|
||||
version = ">0.5, <=0.7"
|
||||
default-features = false
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = "0.13"
|
||||
default-features = false
|
||||
|
||||
[dependencies.cobs]
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.dyn-clone]
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.hashbrown]
|
||||
version = ">=0.14, <=0.15"
|
||||
optional = true
|
||||
|
||||
[dependencies.heapless]
|
||||
version = "0.8"
|
||||
optional = true
|
||||
|
||||
[dependencies.downcast-rs]
|
||||
version = "2"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.bus]
|
||||
version = "2.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.crossbeam-channel]
|
||||
version= "0.5"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.socket2]
|
||||
version = "0.5.4"
|
||||
features = ["all"]
|
||||
optional = true
|
||||
|
||||
[dependencies.mio]
|
||||
version = "1"
|
||||
features = ["os-poll", "net"]
|
||||
optional = true
|
||||
|
||||
[dependencies.defmt]
|
||||
version = "0.3"
|
||||
optional = true
|
||||
hashbrown = { version = ">=0.14, <=0.15", optional = true }
|
||||
static_cell = { version = "2", optional = true }
|
||||
dyn-clone = { version = "1", optional = true }
|
||||
heapless = { version = "0.8", optional = true }
|
||||
downcast-rs = { version = "2", default-features = false, optional = true }
|
||||
bus = { version = "2.2", optional = true }
|
||||
crossbeam-channel = { version = "0.5", default-features = false, optional = true }
|
||||
serde = { version = "1", default-features = false, optional = true }
|
||||
socket2 = { version = "0.5", features = ["all"], optional = true }
|
||||
mio = { version = "1", features = ["os-poll", "net"], optional = true }
|
||||
defmt = { version = "0.3", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde = "1"
|
||||
@@ -121,7 +71,7 @@ alloc = [
|
||||
]
|
||||
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
|
||||
crossbeam = ["crossbeam-channel"]
|
||||
heapless = ["dep:heapless"]
|
||||
heapless = ["dep:heapless", "static_cell"]
|
||||
defmt = ["dep:defmt", "spacepackets/defmt"]
|
||||
test_util = []
|
||||
|
||||
|
@@ -378,74 +378,27 @@ pub struct SubpoolConfig {
|
||||
#[cfg(feature = "heapless")]
|
||||
pub mod heapless_mod {
|
||||
use super::*;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PoolIsFull;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnsafeCellBufWrapper<T> {
|
||||
val: UnsafeCell<T>,
|
||||
once: AtomicBool,
|
||||
}
|
||||
// `Sync` is required because `UnsafeCell` is not `Sync` by default.
|
||||
// This is safe as long as access is manually synchronized.
|
||||
unsafe impl<T> Sync for UnsafeCellBufWrapper<T> {}
|
||||
|
||||
impl<T: Sync> UnsafeCellBufWrapper<T> {
|
||||
/// Creates a new wrapper around an arbitrary value which should be [Sync].
|
||||
pub const fn new(v: T) -> Self {
|
||||
unsafe { Self::new_unchecked(v) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UnsafeCellBufWrapper<T> {
|
||||
/// Creates a new wrapper around a buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Currently, the [Sync] trait is implemented for all T and ignores the usual [Sync] bound
|
||||
/// on T. This API should only be called for declaring byte buffers statically or if T is
|
||||
/// known to be [Sync]. You can use [new] to let the compiler do the [Sync] check.
|
||||
pub const unsafe fn new_unchecked(v: T) -> Self {
|
||||
Self {
|
||||
val: UnsafeCell::new(v),
|
||||
once: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the internal value once.
|
||||
///
|
||||
/// All subsequent calls return None.
|
||||
pub fn get_mut(&self) -> Option<&mut T> {
|
||||
if self.once.load(Ordering::Relaxed) {
|
||||
return None;
|
||||
}
|
||||
// Safety: We ensure that this is only done once with an [AtomicBool].
|
||||
let mut_ref = unsafe { &mut *self.val.get() };
|
||||
self.once.store(true, Ordering::Relaxed);
|
||||
Some(mut_ref)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper macro to generate static buffers for the [crate::pool::StaticHeaplessMemoryPool].
|
||||
#[macro_export]
|
||||
macro_rules! static_subpool {
|
||||
($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr) => {
|
||||
static $pool_name: $crate::pool::UnsafeCellBufWrapper<[u8; $num_blocks * $block_size]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([0; $num_blocks * $block_size]);
|
||||
static $sizes_list_name: $crate::pool::UnsafeCellBufWrapper<[usize; $num_blocks]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
static $pool_name: static_cell::ConstStaticCell<[u8; $num_blocks * $block_size]> =
|
||||
static_cell::ConstStaticCell::new([0; $num_blocks * $block_size]);
|
||||
static $sizes_list_name: static_cell::ConstStaticCell<[usize; $num_blocks]> =
|
||||
static_cell::ConstStaticCell::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
};
|
||||
($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr, $meta_data: meta) => {
|
||||
#[$meta_data]
|
||||
static $pool_name: $crate::pool::UnsafeCellBufWrapper<[u8; $num_blocks * $block_size]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([0; $num_blocks * $block_size]);
|
||||
static $pool_name: static_cell::ConstStaticCell<[u8; $num_blocks * $block_size]> =
|
||||
static_cell::ConstStaticCell::new([0; $num_blocks * $block_size]);
|
||||
#[$meta_data]
|
||||
static $sizes_list_name: $crate::pool::UnsafeCellBufWrapper<[usize; $num_blocks]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
static $sizes_list_name: static_cell::ConstStaticCell<[usize; $num_blocks]> =
|
||||
static_cell::ConstStaticCell::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -482,14 +435,14 @@ pub mod heapless_mod {
|
||||
///
|
||||
/// let mut mem_pool: StaticHeaplessMemoryPool<2> = StaticHeaplessMemoryPool::new(true);
|
||||
/// mem_pool.grow(
|
||||
/// SUBPOOL_SMALL.get_mut().unwrap(),
|
||||
/// SUBPOOL_SMALL_SIZES.get_mut().unwrap(),
|
||||
/// SUBPOOL_SMALL.take(),
|
||||
/// SUBPOOL_SMALL_SIZES.take(),
|
||||
/// SUBPOOL_SMALL_NUM_BLOCKS,
|
||||
/// false
|
||||
/// ).unwrap();
|
||||
/// mem_pool.grow(
|
||||
/// SUBPOOL_LARGE.get_mut().unwrap(),
|
||||
/// SUBPOOL_LARGE_SIZES.get_mut().unwrap(),
|
||||
/// SUBPOOL_LARGE.take(),
|
||||
/// SUBPOOL_LARGE_SIZES.take(),
|
||||
/// SUBPOOL_LARGE_NUM_BLOCKS,
|
||||
/// false
|
||||
/// ).unwrap();
|
||||
@@ -1640,9 +1593,11 @@ mod tests {
|
||||
const SUBPOOL_1_BLOCK_SIZE: usize = 4;
|
||||
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
|
||||
|
||||
static SUBPOOL_1: UnsafeCellBufWrapper<
|
||||
static SUBPOOL_1: static_cell::ConstStaticCell<
|
||||
[u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
||||
> = UnsafeCellBufWrapper::new([0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE]);
|
||||
> = static_cell::ConstStaticCell::new(
|
||||
[0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
||||
);
|
||||
|
||||
static SUBPOOL_1_SIZES: Mutex<UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>> =
|
||||
Mutex::new(UnsafeCell::new(
|
||||
@@ -1651,11 +1606,14 @@ mod tests {
|
||||
|
||||
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
|
||||
const SUBPOOL_2_BLOCK_SIZE: usize = 8;
|
||||
static SUBPOOL_2: UnsafeCellBufWrapper<
|
||||
static SUBPOOL_2: static_cell::ConstStaticCell<
|
||||
[u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
||||
> = UnsafeCellBufWrapper::new([0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE]);
|
||||
static SUBPOOL_2_SIZES: UnsafeCellBufWrapper<[usize; SUBPOOL_2_NUM_ELEMENTS as usize]> =
|
||||
UnsafeCellBufWrapper::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
|
||||
> = static_cell::ConstStaticCell::new(
|
||||
[0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
||||
);
|
||||
static SUBPOOL_2_SIZES: static_cell::ConstStaticCell<
|
||||
[usize; SUBPOOL_2_NUM_ELEMENTS as usize],
|
||||
> = static_cell::ConstStaticCell::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
|
||||
|
||||
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
|
||||
const SUBPOOL_3_BLOCK_SIZE: usize = 16;
|
||||
@@ -1698,7 +1656,7 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(false);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_1.get_mut().unwrap(),
|
||||
SUBPOOL_1.take(),
|
||||
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
|
||||
SUBPOOL_1_NUM_ELEMENTS,
|
||||
true
|
||||
@@ -1706,16 +1664,16 @@ mod tests {
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_2.get_mut().unwrap(),
|
||||
SUBPOOL_2_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_2.take(),
|
||||
SUBPOOL_2_SIZES.take(),
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@@ -1837,16 +1795,16 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_2.get_mut().unwrap(),
|
||||
SUBPOOL_2_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_2.take(),
|
||||
SUBPOOL_2_SIZES.take(),
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_4.get_mut().unwrap(),
|
||||
SUBPOOL_4_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_4.take(),
|
||||
SUBPOOL_4_SIZES.take(),
|
||||
SUBPOOL_4_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@@ -1860,16 +1818,16 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_5.take(),
|
||||
SUBPOOL_5_SIZES.take(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@@ -1883,24 +1841,24 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_5.take(),
|
||||
SUBPOOL_5_SIZES.take(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_6.get_mut().unwrap(),
|
||||
SUBPOOL_6_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_6.take(),
|
||||
SUBPOOL_6_SIZES.take(),
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@@ -1914,24 +1872,24 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_5.take(),
|
||||
SUBPOOL_5_SIZES.take(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_6.get_mut().unwrap(),
|
||||
SUBPOOL_6_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_6.take(),
|
||||
SUBPOOL_6_SIZES.take(),
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
|
@@ -121,10 +121,10 @@ impl SequenceExecutionHelper {
|
||||
/// example by checking [mode replies][ModeReply] received by the children components, and
|
||||
/// then calling [Self::confirm_sequence_done] to advance to the sequence or complete the
|
||||
/// sequence.
|
||||
/// * [ModeCommandingResult::CommandingDone] - The sequence is done. The user can load a new
|
||||
/// * [ModeCommandingResult::Done] - The sequence is done. The user can load a new
|
||||
/// sequence now without overwriting the last one. The sequence executor is in
|
||||
/// [SequenceExecutionHelperState::Idle] again.
|
||||
/// * [ModeCommandingResult::CommandingStepDone] - The sequence has advanced one step. The user
|
||||
/// * [ModeCommandingResult::StepDone] - The sequence has advanced one step. The user
|
||||
/// can now call [Self::run] again to immediately execute the next step in the sequence.
|
||||
///
|
||||
/// Generally, periodic execution of the [Self::run] method should be performed while
|
||||
|
@@ -477,27 +477,6 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TcSender {
|
||||
StaticStore(PacketSenderWithSharedPool),
|
||||
HeapStore(mpsc::SyncSender<PacketAsVec>),
|
||||
}
|
||||
|
||||
impl PacketSenderRaw for TcSender {
|
||||
type Error = StoreAndSendError;
|
||||
|
||||
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
||||
match self {
|
||||
TcSender::StaticStore(packet_sender_with_shared_pool) => {
|
||||
packet_sender_with_shared_pool.send_packet(sender_id, packet)
|
||||
}
|
||||
TcSender::HeapStore(sync_sender) => sync_sender
|
||||
.send_packet(sender_id, packet)
|
||||
.map_err(StoreAndSendError::Send),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Reference in New Issue
Block a user