Compare commits
43 Commits
b5445d1d4e
...
f2648fb3b6
Author | SHA1 | Date | |
---|---|---|---|
f2648fb3b6 | |||
9325707fe8 | |||
04663cb5ae | |||
127d22d445 | |||
0870471886 | |||
365f8f9e7a | |||
9130e68bce | |||
32bc2826d9 | |||
d107ab5ed3 | |||
96e88659ab | |||
0d930b5832 | |||
c952d813d4 | |||
b3b1569b3d | |||
5d7423a19e | |||
975e1a5323 | |||
c8bed19e42 | |||
ce92ab9b2f | |||
7f674dd5bf | |||
a6aa20d09b | |||
3a02fcf77a | |||
95d232dc02 | |||
df24f50e8e | |||
a42aefff87 | |||
d4339f3ea3 | |||
b07b8d6347 | |||
0afcc35513 | |||
a2c2e35067 | |||
4fc7972bdd | |||
04b96579bd | |||
c973339ee5 | |||
656aafccff | |||
3569fce95e | |||
ed266a11f6 | |||
af972e174f | |||
3d12083c16 | |||
e8d2c020fa | |||
69e172b633 | |||
e1dda751bc | |||
b01628d8ef | |||
31844e4fe2 | |||
738872f421 | |||
309e39999f | |||
1c43c3adf9 |
85
3
Normal file
85
3
Normal file
@ -0,0 +1,85 @@
|
||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use spacepackets::time::cds::CdsTime;
|
||||
use spacepackets::time::TimeWriter;
|
||||
use spacepackets::SpHeader;
|
||||
|
||||
pub struct PusTmWithCdsShortHelper {
|
||||
apid: u16,
|
||||
cds_short_buf: [u8; 7],
|
||||
}
|
||||
|
||||
impl PusTmWithCdsShortHelper {
|
||||
pub fn new(apid: u16) -> Self {
|
||||
Self {
|
||||
apid,
|
||||
cds_short_buf: [0; 7],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn create_pus_tm_timestamp_now<'data>(
|
||||
&mut self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
let time_stamp = CdsTime::now_with_u16_days().unwrap();
|
||||
time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||
self.create_pus_tm_common(service, subservice, source_data, seq_count)
|
||||
}
|
||||
|
||||
pub fn create_pus_tm_with_stamper<'data>(
|
||||
&mut self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
stamper: &CdsTime,
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
stamper.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||
self.create_pus_tm_common(service, subservice, source_data, seq_count)
|
||||
}
|
||||
|
||||
fn create_pus_tm_common<'data>(
|
||||
&self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
|
||||
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
|
||||
PusTmCreator::new(reply_header, tc_header, source_data, true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use spacepackets::{ecss::PusPacket, time::cds::CdsTime, CcsdsPacket};
|
||||
|
||||
use super::PusTmWithCdsShortHelper;
|
||||
|
||||
#[test]
|
||||
fn test_helper_with_stamper() {
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
||||
let stamper = CdsTime::new_with_u16_days(0, 0);
|
||||
let tm = pus_tm_helper.create_pus_tm_with_stamper(17, 1, &[1, 2, 3, 4], &stamper, 25);
|
||||
assert_eq!(tm.service(), 17);
|
||||
assert_eq!(tm.subservice(), 1);
|
||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||
assert_eq!(tm.seq_count(), 25);
|
||||
assert_eq!(tm.timestamp(), [64, 0, 0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_helper_from_now() {
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
||||
let tm = pus_tm_helper.create_pus_tm_timestamp_now(17, 1, &[1, 2, 3, 4], 25);
|
||||
assert_eq!(tm.service(), 17);
|
||||
assert_eq!(tm.subservice(), 1);
|
||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||
assert_eq!(tm.seq_count(), 25);
|
||||
assert_eq!(tm.timestamp().len(), 7);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Example client for the sat-rs example application"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
@ -12,7 +12,7 @@ authors = [
|
||||
{name = "Robin Mueller", email = "robin.mueller.m@gmail.com"},
|
||||
]
|
||||
dependencies = [
|
||||
"tmtccmd~=8.0",
|
||||
"tmtccmd~=8.1",
|
||||
"pydantic~=2.7"
|
||||
]
|
||||
|
||||
|
11
satrs-example/pytmtc/pytmtc/acs/__init__.py
Normal file
11
satrs-example/pytmtc/pytmtc/acs/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
from tmtccmd.config import CmdTreeNode
|
||||
|
||||
|
||||
def create_acs_node(mode_node: CmdTreeNode, hk_node: CmdTreeNode) -> CmdTreeNode:
|
||||
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
||||
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
||||
mgm_node.add_child(mode_node)
|
||||
mgm_node.add_child(hk_node)
|
||||
|
||||
acs_node.add_child(mgm_node)
|
||||
return acs_node
|
@ -12,7 +12,7 @@ from pytmtc.pus_tc import create_cmd_definition_tree
|
||||
|
||||
class SatrsConfigHook(HookBase):
|
||||
def __init__(self, json_cfg_path: str):
|
||||
super().__init__(json_cfg_path=json_cfg_path)
|
||||
super().__init__(json_cfg_path)
|
||||
|
||||
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
||||
from tmtccmd.config.com import (
|
||||
|
0
satrs-example/pytmtc/pytmtc/eps/__init__.py
Normal file
0
satrs-example/pytmtc/pytmtc/eps/__init__.py
Normal file
0
satrs-example/pytmtc/pytmtc/eps/pcdu.py
Normal file
0
satrs-example/pytmtc/pytmtc/eps/pcdu.py
Normal file
@ -4,7 +4,7 @@ from spacepackets.ecss.pus_3_hk import Subservice
|
||||
from spacepackets.ecss import PusTm
|
||||
|
||||
from pytmtc.common import AcsId, Apid
|
||||
from pytmtc.mgms import handle_mgm_hk_report
|
||||
from pytmtc.acs.mgms import handle_mgm_hk_report
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -17,8 +17,9 @@ from tmtccmd.tmtc import (
|
||||
)
|
||||
from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd
|
||||
|
||||
from pytmtc.acs import create_acs_node
|
||||
from pytmtc.common import Apid
|
||||
from pytmtc.mgms import create_mgm_cmds
|
||||
from pytmtc.acs.mgms import create_mgm_cmds
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -67,7 +68,6 @@ class TcHandler(TcHandlerBase):
|
||||
|
||||
|
||||
def create_cmd_definition_tree() -> CmdTreeNode:
|
||||
|
||||
root_node = CmdTreeNode.root_node()
|
||||
|
||||
hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True)
|
||||
@ -101,15 +101,7 @@ def create_cmd_definition_tree() -> CmdTreeNode:
|
||||
)
|
||||
)
|
||||
root_node.add_child(scheduler_node)
|
||||
|
||||
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
||||
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
||||
mgm_node.add_child(mode_node)
|
||||
mgm_node.add_child(hk_node)
|
||||
|
||||
acs_node.add_child(mgm_node)
|
||||
root_node.add_child(acs_node)
|
||||
|
||||
root_node.add_child(create_acs_node(mode_node, hk_node))
|
||||
return root_node
|
||||
|
||||
|
||||
|
@ -14,12 +14,10 @@ fern = "0.7"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
num_enum = "0.7"
|
||||
humantime = "2"
|
||||
tai-time = { version = "0.3", features = ["serde"] }
|
||||
|
||||
[dependencies.asynchronix]
|
||||
version = "0.2.2"
|
||||
# git = "https://github.com/asynchronics/asynchronix.git"
|
||||
# branch = "main"
|
||||
features = ["serde"]
|
||||
[dependencies.nexosim]
|
||||
version = "0.3.1"
|
||||
|
||||
[dependencies.satrs]
|
||||
path = "../satrs"
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::{f32::consts::PI, sync::mpsc, time::Duration};
|
||||
|
||||
use asynchronix::{
|
||||
model::{Model, Output},
|
||||
time::Scheduler,
|
||||
use nexosim::{
|
||||
model::{Context, Model},
|
||||
ports::Output,
|
||||
};
|
||||
use satrs::power::SwitchStateBinary;
|
||||
use satrs_minisim::{
|
||||
@ -55,7 +55,7 @@ impl<ReplyProvider: MgmReplyProvider> MagnetometerModel<ReplyProvider> {
|
||||
self.switch_state = switch_state;
|
||||
}
|
||||
|
||||
pub async fn send_sensor_values(&mut self, _: (), scheduler: &Scheduler<Self>) {
|
||||
pub async fn send_sensor_values(&mut self, _: (), scheduler: &mut Context<Self>) {
|
||||
self.reply_sender
|
||||
.send(ReplyProvider::create_mgm_reply(MgmReplyCommon {
|
||||
switch_state: self.switch_state,
|
||||
@ -114,11 +114,11 @@ impl MagnetorquerModel {
|
||||
pub async fn apply_torque(
|
||||
&mut self,
|
||||
duration_and_dipole: (Duration, MgtDipole),
|
||||
scheduler: &Scheduler<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.torque_dipole = duration_and_dipole.1;
|
||||
self.torquing = true;
|
||||
if scheduler
|
||||
if cx
|
||||
.schedule_event(duration_and_dipole.0, Self::clear_torque, ())
|
||||
.is_err()
|
||||
{
|
||||
@ -138,12 +138,11 @@ impl MagnetorquerModel {
|
||||
self.generate_magnetic_field(()).await;
|
||||
}
|
||||
|
||||
pub async fn request_housekeeping_data(&mut self, _: (), scheduler: &Scheduler<Self>) {
|
||||
pub async fn request_housekeeping_data(&mut self, _: (), cx: &mut Context<Self>) {
|
||||
if self.switch_state != SwitchStateBinary::On {
|
||||
return;
|
||||
}
|
||||
scheduler
|
||||
.schedule_event(Duration::from_millis(15), Self::send_housekeeping_data, ())
|
||||
cx.schedule_event(Duration::from_millis(15), Self::send_housekeeping_data, ())
|
||||
.expect("requesting housekeeping data failed")
|
||||
}
|
||||
|
||||
@ -200,7 +199,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
@ -223,21 +222,21 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
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);
|
||||
let first_reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
|
||||
.expect("failed to deserialize MGM sensor values");
|
||||
sim_testbench.step_by(Duration::from_millis(50));
|
||||
sim_testbench.step_until(Duration::from_millis(50)).unwrap();
|
||||
|
||||
request = SimRequest::new_with_epoch_time(MgmRequestLis3Mdl::RequestSensorData);
|
||||
sim_testbench
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_some());
|
||||
sim_reply = sim_reply_res.unwrap();
|
||||
@ -272,7 +271,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
let sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_none());
|
||||
}
|
||||
@ -287,7 +286,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
let sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_some());
|
||||
let sim_reply = sim_reply_res.unwrap();
|
||||
@ -308,7 +307,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
let sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_some());
|
||||
let sim_reply = sim_reply_res.unwrap();
|
||||
@ -339,7 +338,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step_by(Duration::from_millis(5));
|
||||
sim_testbench.step_until(Duration::from_millis(5)).unwrap();
|
||||
|
||||
check_mgt_hk(
|
||||
&mut sim_testbench,
|
||||
@ -348,7 +347,9 @@ pub mod tests {
|
||||
torquing: true,
|
||||
},
|
||||
);
|
||||
sim_testbench.step_by(Duration::from_millis(100));
|
||||
sim_testbench
|
||||
.step_until(Duration::from_millis(100))
|
||||
.unwrap();
|
||||
check_mgt_hk(
|
||||
&mut sim_testbench,
|
||||
MgtHkSet {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
|
||||
use asynchronix::{
|
||||
simulation::{Address, Simulation},
|
||||
use nexosim::{
|
||||
simulation::{Address, Scheduler, Simulation},
|
||||
time::{Clock, MonotonicTime, SystemClock},
|
||||
};
|
||||
use satrs_minisim::{
|
||||
@ -23,35 +23,52 @@ const MGM_REQ_WIRETAPPING: bool = false;
|
||||
const PCDU_REQ_WIRETAPPING: bool = false;
|
||||
const MGT_REQ_WIRETAPPING: bool = false;
|
||||
|
||||
pub struct ModelAddrWrapper {
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
}
|
||||
|
||||
// The simulation controller processes requests and drives the simulation.
|
||||
#[allow(dead_code)]
|
||||
pub struct SimController {
|
||||
pub sys_clock: SystemClock,
|
||||
pub request_receiver: mpsc::Receiver<SimRequest>,
|
||||
pub reply_sender: mpsc::Sender<SimReply>,
|
||||
pub simulation: Simulation,
|
||||
pub mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pub pcdu_addr: Address<PcduModel>,
|
||||
pub mgt_addr: Address<MagnetorquerModel>,
|
||||
pub scheduler: Scheduler,
|
||||
pub addr_wrapper: ModelAddrWrapper,
|
||||
}
|
||||
|
||||
impl ModelAddrWrapper {
|
||||
pub fn new(
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
) -> Self {
|
||||
Self {
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SimController {
|
||||
pub fn new(
|
||||
sys_clock: SystemClock,
|
||||
request_receiver: mpsc::Receiver<SimRequest>,
|
||||
reply_sender: mpsc::Sender<SimReply>,
|
||||
simulation: Simulation,
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
scheduler: Scheduler,
|
||||
addr_wrapper: ModelAddrWrapper,
|
||||
) -> Self {
|
||||
Self {
|
||||
sys_clock,
|
||||
request_receiver,
|
||||
reply_sender,
|
||||
simulation,
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
scheduler,
|
||||
addr_wrapper,
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +79,7 @@ impl SimController {
|
||||
// Check for UDP requests every millisecond. Shift the simulator ahead here to prevent
|
||||
// replies lying in the past.
|
||||
t += Duration::from_millis(udp_polling_interval_ms);
|
||||
self.sys_clock.synchronize(t);
|
||||
let _synch_status = self.sys_clock.synchronize(t);
|
||||
self.handle_sim_requests(t_old);
|
||||
self.simulation
|
||||
.step_until(t)
|
||||
@ -118,11 +135,13 @@ impl SimController {
|
||||
}
|
||||
match mgm_request {
|
||||
MgmRequestLis3Mdl::RequestSensorData => {
|
||||
self.simulation.send_event(
|
||||
MagnetometerModel::send_sensor_values,
|
||||
(),
|
||||
&self.mgm_addr,
|
||||
);
|
||||
self.simulation
|
||||
.process_event(
|
||||
MagnetometerModel::send_sensor_values,
|
||||
(),
|
||||
&self.addr_wrapper.mgm_addr,
|
||||
)
|
||||
.expect("event execution error for mgm");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -136,14 +155,21 @@ impl SimController {
|
||||
match pcdu_request {
|
||||
PcduRequest::RequestSwitchInfo => {
|
||||
self.simulation
|
||||
.send_event(PcduModel::request_switch_info, (), &self.pcdu_addr);
|
||||
.process_event(
|
||||
PcduModel::request_switch_info,
|
||||
(),
|
||||
&self.addr_wrapper.pcdu_addr,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
PcduRequest::SwitchDevice { switch, state } => {
|
||||
self.simulation.send_event(
|
||||
PcduModel::switch_device,
|
||||
(switch, state),
|
||||
&self.pcdu_addr,
|
||||
);
|
||||
self.simulation
|
||||
.process_event(
|
||||
PcduModel::switch_device,
|
||||
(switch, state),
|
||||
&self.addr_wrapper.pcdu_addr,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -155,17 +181,23 @@ impl SimController {
|
||||
log::info!("received MGT request: {:?}", mgt_request);
|
||||
}
|
||||
match mgt_request {
|
||||
MgtRequest::ApplyTorque { duration, dipole } => self.simulation.send_event(
|
||||
MagnetorquerModel::apply_torque,
|
||||
(duration, dipole),
|
||||
&self.mgt_addr,
|
||||
),
|
||||
MgtRequest::RequestHk => self.simulation.send_event(
|
||||
MagnetorquerModel::request_housekeeping_data,
|
||||
(),
|
||||
&self.mgt_addr,
|
||||
),
|
||||
}
|
||||
MgtRequest::ApplyTorque { duration, dipole } => self
|
||||
.simulation
|
||||
.process_event(
|
||||
MagnetorquerModel::apply_torque,
|
||||
(duration, dipole),
|
||||
&self.addr_wrapper.mgt_addr,
|
||||
)
|
||||
.unwrap(),
|
||||
MgtRequest::RequestHk => self
|
||||
.simulation
|
||||
.process_event(
|
||||
MagnetorquerModel::request_housekeeping_data,
|
||||
(),
|
||||
&self.addr_wrapper.mgt_addr,
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -199,7 +231,7 @@ mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending sim ctrl request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
|
||||
use asynchronix::{
|
||||
model::{Model, Output},
|
||||
time::Scheduler,
|
||||
use nexosim::{
|
||||
model::{Context, Model},
|
||||
ports::Output,
|
||||
};
|
||||
use satrs::power::SwitchStateBinary;
|
||||
use satrs_minisim::{
|
||||
@ -29,14 +29,13 @@ impl PcduModel {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn request_switch_info(&mut self, _: (), scheduler: &Scheduler<Self>) {
|
||||
scheduler
|
||||
.schedule_event(
|
||||
Duration::from_millis(SWITCH_INFO_DELAY_MS),
|
||||
Self::send_switch_info,
|
||||
(),
|
||||
)
|
||||
.expect("requesting switch info failed");
|
||||
pub async fn request_switch_info(&mut self, _: (), cx: &mut Context<Self>) {
|
||||
cx.schedule_event(
|
||||
Duration::from_millis(SWITCH_INFO_DELAY_MS),
|
||||
Self::send_switch_info,
|
||||
(),
|
||||
)
|
||||
.expect("requesting switch info failed");
|
||||
}
|
||||
|
||||
pub fn send_switch_info(&mut self) {
|
||||
@ -92,7 +91,7 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM switch request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -113,7 +112,7 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
@ -143,12 +142,12 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step_by(Duration::from_millis(1));
|
||||
sim_testbench.step_until(Duration::from_millis(1)).unwrap();
|
||||
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_none());
|
||||
// Reply takes 20ms
|
||||
sim_testbench.step_by(Duration::from_millis(25));
|
||||
sim_testbench.step_until(Duration::from_millis(25)).unwrap();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
|
@ -1,4 +1,4 @@
|
||||
use asynchronix::time::MonotonicTime;
|
||||
use nexosim::time::MonotonicTime;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use acs::{MagnetometerModel, MagnetorquerModel};
|
||||
use asynchronix::simulation::{Mailbox, SimInit};
|
||||
use asynchronix::time::{MonotonicTime, SystemClock};
|
||||
use controller::SimController;
|
||||
use controller::{ModelAddrWrapper, SimController};
|
||||
use eps::PcduModel;
|
||||
use nexosim::simulation::{Mailbox, SimInit};
|
||||
use nexosim::time::{MonotonicTime, SystemClock};
|
||||
use satrs_minisim::udp::SIM_CTRL_PORT;
|
||||
use satrs_minisim::{SimReply, SimRequest};
|
||||
use std::sync::mpsc;
|
||||
@ -63,19 +63,20 @@ fn create_sim_controller(
|
||||
} else {
|
||||
SimInit::new()
|
||||
};
|
||||
let simulation = sim_init
|
||||
.add_model(mgm_model, mgm_mailbox)
|
||||
.add_model(pcdu_model, pcdu_mailbox)
|
||||
.add_model(mgt_model, mgt_mailbox)
|
||||
.init(start_time);
|
||||
let addrs = ModelAddrWrapper::new(mgm_addr, pcdu_addr, mgt_addr);
|
||||
let (simulation, scheduler) = sim_init
|
||||
.add_model(mgm_model, mgm_mailbox, "MGM model")
|
||||
.add_model(pcdu_model, pcdu_mailbox, "PCDU model")
|
||||
.add_model(mgt_model, mgt_mailbox, "MGT model")
|
||||
.init(start_time)
|
||||
.unwrap();
|
||||
SimController::new(
|
||||
sys_clock,
|
||||
request_receiver,
|
||||
reply_sender,
|
||||
simulation,
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
scheduler,
|
||||
addrs,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
use delegate::delegate;
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use asynchronix::time::MonotonicTime;
|
||||
use nexosim::{
|
||||
simulation::ExecutionError,
|
||||
time::{Deadline, MonotonicTime},
|
||||
};
|
||||
use satrs_minisim::{SimReply, SimRequest};
|
||||
|
||||
use crate::{controller::SimController, create_sim_controller, ThreadingModel};
|
||||
@ -35,8 +38,8 @@ impl SimTestbench {
|
||||
pub fn handle_sim_requests(&mut self, old_timestamp: MonotonicTime);
|
||||
}
|
||||
to self.sim_controller.simulation {
|
||||
pub fn step(&mut self);
|
||||
pub fn step_by(&mut self, duration: Duration);
|
||||
pub fn step(&mut self) -> Result<(), ExecutionError>;
|
||||
pub fn step_until(&mut self, duration: impl Deadline) -> Result<(), ExecutionError>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use asynchronix::time::MonotonicTime;
|
||||
use nexosim::time::MonotonicTime;
|
||||
|
||||
pub fn current_millis(time: MonotonicTime) -> u64 {
|
||||
(time.as_secs() as u64 * 1000) + (time.subsec_nanos() as u64 / 1_000_000)
|
||||
|
@ -31,9 +31,9 @@ version = "0.13"
|
||||
default-features = false
|
||||
|
||||
[dependencies.cobs]
|
||||
git = "https://github.com/robamu/cobs.rs.git"
|
||||
git = "https://github.com/jamesmunns/cobs.rs.git"
|
||||
version = "0.2.3"
|
||||
branch = "all_features"
|
||||
branch = "main"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num-traits]
|
||||
|
@ -378,27 +378,74 @@ 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 mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
|
||||
core::mem::MaybeUninit::new([0; $num_blocks * $block_size]);
|
||||
static mut $sizes_list_name: core::mem::MaybeUninit<[usize; $num_blocks]> =
|
||||
core::mem::MaybeUninit::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
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]);
|
||||
};
|
||||
($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr, $meta_data: meta) => {
|
||||
#[$meta_data]
|
||||
static mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
|
||||
core::mem::MaybeUninit::new([0; $num_blocks * $block_size]);
|
||||
static $pool_name: $crate::pool::UnsafeCellBufWrapper<[u8; $num_blocks * $block_size]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([0; $num_blocks * $block_size]);
|
||||
#[$meta_data]
|
||||
static mut $sizes_list_name: core::mem::MaybeUninit<[usize; $num_blocks]> =
|
||||
core::mem::MaybeUninit::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
static $sizes_list_name: $crate::pool::UnsafeCellBufWrapper<[usize; $num_blocks]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
};
|
||||
}
|
||||
|
||||
@ -435,17 +482,17 @@ pub mod heapless_mod {
|
||||
///
|
||||
/// let mut mem_pool: StaticHeaplessMemoryPool<2> = StaticHeaplessMemoryPool::new(true);
|
||||
/// mem_pool.grow(
|
||||
/// unsafe { SUBPOOL_SMALL.assume_init_mut() },
|
||||
/// unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
|
||||
/// SUBPOOL_SMALL.get_mut().unwrap(),
|
||||
/// SUBPOOL_SMALL_SIZES.get_mut().unwrap(),
|
||||
/// SUBPOOL_SMALL_NUM_BLOCKS,
|
||||
/// false
|
||||
/// );
|
||||
/// ).unwrap();
|
||||
/// mem_pool.grow(
|
||||
/// unsafe { SUBPOOL_LARGE.assume_init_mut() },
|
||||
/// unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
|
||||
/// SUBPOOL_LARGE.get_mut().unwrap(),
|
||||
/// SUBPOOL_LARGE_SIZES.get_mut().unwrap(),
|
||||
/// SUBPOOL_LARGE_NUM_BLOCKS,
|
||||
/// false
|
||||
/// );
|
||||
/// ).unwrap();
|
||||
///
|
||||
/// let mut read_buf: [u8; 16] = [0; 16];
|
||||
/// let mut addr;
|
||||
@ -522,12 +569,14 @@ pub mod heapless_mod {
|
||||
num_blocks: NumBlocks,
|
||||
set_sizes_list_to_all_free: bool,
|
||||
) -> Result<(), PoolIsFull> {
|
||||
assert!(
|
||||
(subpool_memory.len() % num_blocks as usize) == 0,
|
||||
assert_eq!(
|
||||
(subpool_memory.len() % num_blocks as usize),
|
||||
0,
|
||||
"pool slice length must be multiple of number of blocks"
|
||||
);
|
||||
assert!(
|
||||
num_blocks as usize == sizes_list.len(),
|
||||
assert_eq!(
|
||||
num_blocks as usize,
|
||||
sizes_list.len(),
|
||||
"used block size list slice must be of same length as number of blocks"
|
||||
);
|
||||
let subpool_config = SubpoolConfig {
|
||||
@ -1584,21 +1633,28 @@ mod tests {
|
||||
mod heapless_tests {
|
||||
use super::*;
|
||||
use crate::static_subpool;
|
||||
use core::ptr::addr_of_mut;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::Mutex;
|
||||
|
||||
const SUBPOOL_1_BLOCK_SIZE: usize = 4;
|
||||
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
|
||||
static mut SUBPOOL_1: [u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE] =
|
||||
[0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE];
|
||||
static mut SUBPOOL_1_SIZES: [usize; SUBPOOL_1_NUM_ELEMENTS as usize] =
|
||||
[STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize];
|
||||
|
||||
static SUBPOOL_1: UnsafeCellBufWrapper<
|
||||
[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 SUBPOOL_1_SIZES: Mutex<UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>> =
|
||||
Mutex::new(UnsafeCell::new(
|
||||
[STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize],
|
||||
));
|
||||
|
||||
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
|
||||
const SUBPOOL_2_BLOCK_SIZE: usize = 8;
|
||||
static mut SUBPOOL_2: [u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE] =
|
||||
[0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE];
|
||||
static mut SUBPOOL_2_SIZES: [usize; SUBPOOL_2_NUM_ELEMENTS as usize] =
|
||||
[STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize];
|
||||
static SUBPOOL_2: UnsafeCellBufWrapper<
|
||||
[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]);
|
||||
|
||||
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
|
||||
const SUBPOOL_3_BLOCK_SIZE: usize = 16;
|
||||
@ -1641,24 +1697,24 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(false);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { &mut *addr_of_mut!(SUBPOOL_1) },
|
||||
unsafe { &mut *addr_of_mut!(SUBPOOL_1_SIZES) },
|
||||
SUBPOOL_1.get_mut().unwrap(),
|
||||
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
|
||||
SUBPOOL_1_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { &mut *addr_of_mut!(SUBPOOL_2) },
|
||||
unsafe { &mut *addr_of_mut!(SUBPOOL_2_SIZES) },
|
||||
SUBPOOL_2.get_mut().unwrap(),
|
||||
SUBPOOL_2_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@ -1780,16 +1836,16 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { &mut *addr_of_mut!(SUBPOOL_2) },
|
||||
unsafe { &mut *addr_of_mut!(SUBPOOL_2_SIZES) },
|
||||
SUBPOOL_2.get_mut().unwrap(),
|
||||
SUBPOOL_2_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_4.assume_init_mut() },
|
||||
unsafe { SUBPOOL_4_SIZES.assume_init_mut() },
|
||||
SUBPOOL_4.get_mut().unwrap(),
|
||||
SUBPOOL_4_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_4_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@ -1803,16 +1859,16 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@ -1826,24 +1882,24 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_6.assume_init_mut() },
|
||||
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
|
||||
SUBPOOL_6.get_mut().unwrap(),
|
||||
SUBPOOL_6_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@ -1857,24 +1913,24 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_6.assume_init_mut() },
|
||||
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
|
||||
SUBPOOL_6.get_mut().unwrap(),
|
||||
SUBPOOL_6_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user