Compare commits

..

33 Commits

Author SHA1 Message Date
b5445d1d4e
add health module 2025-01-30 15:57:02 +01:00
5d2993196f add first tests for assy helper 2025-01-30 10:55:18 +01:00
211e6063cb
all tests work again 2025-01-27 15:42:29 +01:00
674705535f
continue testing of mode tables 2025-01-27 12:35:08 +01:00
a19eb187c6
continue tests 2025-01-24 16:14:13 +01:00
418f68577f
Merge remote-tracking branch 'origin/main' into mode-tables-sequence-tables 2025-01-23 17:25:59 +01:00
68d20b222a fix no_std build 2025-01-23 17:05:19 +01:00
f202ba59d0 basic test suite for exec helper done 2025-01-23 17:05:19 +01:00
2b01537cfd add more tests 2025-01-23 17:05:19 +01:00
bda47b067d continue seq exec helper tests 2025-01-23 17:05:19 +01:00
4d1dd0aa19 continue tests 2025-01-23 17:05:19 +01:00
245cd6a2c2 continue mode tree 2025-01-23 17:05:19 +01:00
7b1efeb150 more generic components to library 2025-01-23 17:05:19 +01:00
b46c06f1af more tests 2025-01-23 17:05:19 +01:00
b948f9fe2d add more tests 2025-01-23 17:05:19 +01:00
4a2262dbe2 extend tests 2025-01-23 17:05:19 +01:00
fabe52ee0d some docs and helper methods 2025-01-23 17:05:19 +01:00
803f626d51 device manager works 2025-01-23 17:05:19 +01:00
559e040ea9 command propagation works 2025-01-23 17:05:19 +01:00
43f67876c1 continue mode tree 2025-01-23 17:05:19 +01:00
b7662636f5 got an assembly helper now 2025-01-23 17:05:19 +01:00
a326da6ed6 continue mode tree 2025-01-23 17:05:19 +01:00
da6ed5233d introduce forced flag for set mode cmd 2025-01-23 17:05:19 +01:00
7532dd78de continue mode tree execution helper 2025-01-23 17:05:19 +01:00
d5d3577af8 continue mode tree helper 2025-01-23 17:05:19 +01:00
1a211b073f save 2025-01-23 17:05:19 +01:00
3ec5c76733 continue subsystem helper 2025-01-23 17:05:19 +01:00
51e723a45f continue mode tree feature 2025-01-23 17:05:19 +01:00
a27e1e308d generic shananigans 2025-01-23 17:05:19 +01:00
31074dc1aa move generic components to library 2025-01-23 17:05:19 +01:00
d97ae401e5 add testbench 2025-01-23 17:05:19 +01:00
f4624afd41 continue mode tree 2025-01-23 17:05:19 +01:00
d9a20e2d7d continue 2025-01-23 17:05:19 +01:00
20 changed files with 159 additions and 338 deletions

85
3
View File

@ -1,85 +0,0 @@
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);
}
}

View File

@ -1,6 +1,5 @@
#!/usr/bin/env python3
"""Example client for the sat-rs example application"""
import logging
import sys
import time

View File

@ -12,7 +12,7 @@ authors = [
{name = "Robin Mueller", email = "robin.mueller.m@gmail.com"},
]
dependencies = [
"tmtccmd~=8.1",
"tmtccmd~=8.0",
"pydantic~=2.7"
]

View File

@ -1,11 +0,0 @@
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

View File

@ -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)
super().__init__(json_cfg_path=json_cfg_path)
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
from tmtccmd.config.com import (

View 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.acs.mgms import handle_mgm_hk_report
from pytmtc.mgms import handle_mgm_hk_report
_LOGGER = logging.getLogger(__name__)

View File

@ -17,9 +17,8 @@ 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.acs.mgms import create_mgm_cmds
from pytmtc.mgms import create_mgm_cmds
_LOGGER = logging.getLogger(__name__)
@ -68,6 +67,7 @@ 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,7 +101,15 @@ def create_cmd_definition_tree() -> CmdTreeNode:
)
)
root_node.add_child(scheduler_node)
root_node.add_child(create_acs_node(mode_node, hk_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)
return root_node

View File

@ -14,10 +14,12 @@ fern = "0.7"
strum = { version = "0.26", features = ["derive"] }
num_enum = "0.7"
humantime = "2"
tai-time = { version = "0.3", features = ["serde"] }
[dependencies.nexosim]
version = "0.3.1"
[dependencies.asynchronix]
version = "0.2.2"
# git = "https://github.com/asynchronics/asynchronix.git"
# branch = "main"
features = ["serde"]
[dependencies.satrs]
path = "../satrs"

View File

@ -1,8 +1,8 @@
use std::{f32::consts::PI, sync::mpsc, time::Duration};
use nexosim::{
model::{Context, Model},
ports::Output,
use asynchronix::{
model::{Model, Output},
time::Scheduler,
};
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: &mut Context<Self>) {
pub async fn send_sensor_values(&mut self, _: (), scheduler: &Scheduler<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),
cx: &mut Context<Self>,
scheduler: &Scheduler<Self>,
) {
self.torque_dipole = duration_and_dipole.1;
self.torquing = true;
if cx
if scheduler
.schedule_event(duration_and_dipole.0, Self::clear_torque, ())
.is_err()
{
@ -138,11 +138,12 @@ impl MagnetorquerModel {
self.generate_magnetic_field(()).await;
}
pub async fn request_housekeeping_data(&mut self, _: (), cx: &mut Context<Self>) {
pub async fn request_housekeeping_data(&mut self, _: (), scheduler: &Scheduler<Self>) {
if self.switch_state != SwitchStateBinary::On {
return;
}
cx.schedule_event(Duration::from_millis(15), Self::send_housekeeping_data, ())
scheduler
.schedule_event(Duration::from_millis(15), Self::send_housekeeping_data, ())
.expect("requesting housekeeping data failed")
}
@ -199,7 +200,7 @@ pub mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();
@ -222,21 +223,21 @@ pub mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
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_until(Duration::from_millis(50)).unwrap();
sim_testbench.step_by(Duration::from_millis(50));
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().unwrap();
sim_testbench.step();
sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some());
sim_reply = sim_reply_res.unwrap();
@ -271,7 +272,7 @@ pub mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_none());
}
@ -286,7 +287,7 @@ pub mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some());
let sim_reply = sim_reply_res.unwrap();
@ -307,7 +308,7 @@ pub mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some());
let sim_reply = sim_reply_res.unwrap();
@ -338,7 +339,7 @@ pub mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step_until(Duration::from_millis(5)).unwrap();
sim_testbench.step_by(Duration::from_millis(5));
check_mgt_hk(
&mut sim_testbench,
@ -347,9 +348,7 @@ pub mod tests {
torquing: true,
},
);
sim_testbench
.step_until(Duration::from_millis(100))
.unwrap();
sim_testbench.step_by(Duration::from_millis(100));
check_mgt_hk(
&mut sim_testbench,
MgtHkSet {

View File

@ -1,7 +1,7 @@
use std::{sync::mpsc, time::Duration};
use nexosim::{
simulation::{Address, Scheduler, Simulation},
use asynchronix::{
simulation::{Address, Simulation},
time::{Clock, MonotonicTime, SystemClock},
};
use satrs_minisim::{
@ -23,52 +23,35 @@ 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 scheduler: Scheduler,
pub addr_wrapper: ModelAddrWrapper,
pub mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
pub pcdu_addr: Address<PcduModel>,
pub mgt_addr: Address<MagnetorquerModel>,
}
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,
scheduler: Scheduler,
addr_wrapper: ModelAddrWrapper,
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
pcdu_addr: Address<PcduModel>,
mgt_addr: Address<MagnetorquerModel>,
) -> Self {
Self {
sys_clock,
request_receiver,
reply_sender,
simulation,
scheduler,
addr_wrapper,
mgm_addr,
pcdu_addr,
mgt_addr,
}
}
@ -79,7 +62,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);
let _synch_status = self.sys_clock.synchronize(t);
self.sys_clock.synchronize(t);
self.handle_sim_requests(t_old);
self.simulation
.step_until(t)
@ -135,13 +118,11 @@ impl SimController {
}
match mgm_request {
MgmRequestLis3Mdl::RequestSensorData => {
self.simulation
.process_event(
MagnetometerModel::send_sensor_values,
(),
&self.addr_wrapper.mgm_addr,
)
.expect("event execution error for mgm");
self.simulation.send_event(
MagnetometerModel::send_sensor_values,
(),
&self.mgm_addr,
);
}
}
Ok(())
@ -155,21 +136,14 @@ impl SimController {
match pcdu_request {
PcduRequest::RequestSwitchInfo => {
self.simulation
.process_event(
PcduModel::request_switch_info,
(),
&self.addr_wrapper.pcdu_addr,
)
.unwrap();
.send_event(PcduModel::request_switch_info, (), &self.pcdu_addr);
}
PcduRequest::SwitchDevice { switch, state } => {
self.simulation
.process_event(
PcduModel::switch_device,
(switch, state),
&self.addr_wrapper.pcdu_addr,
)
.unwrap();
self.simulation.send_event(
PcduModel::switch_device,
(switch, state),
&self.pcdu_addr,
);
}
}
Ok(())
@ -181,23 +155,17 @@ impl SimController {
log::info!("received MGT request: {:?}", mgt_request);
}
match mgt_request {
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(),
};
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,
),
}
Ok(())
}
@ -231,7 +199,7 @@ mod tests {
.send_request(request)
.expect("sending sim ctrl request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();

View File

@ -1,8 +1,8 @@
use std::{sync::mpsc, time::Duration};
use nexosim::{
model::{Context, Model},
ports::Output,
use asynchronix::{
model::{Model, Output},
time::Scheduler,
};
use satrs::power::SwitchStateBinary;
use satrs_minisim::{
@ -29,13 +29,14 @@ impl PcduModel {
}
}
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 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 fn send_switch_info(&mut self) {
@ -91,7 +92,7 @@ pub(crate) mod tests {
.send_request(request)
.expect("sending MGM switch request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
}
#[allow(dead_code)]
@ -112,7 +113,7 @@ pub(crate) mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap();
sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();
@ -142,12 +143,12 @@ pub(crate) mod tests {
.send_request(request)
.expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step_until(Duration::from_millis(1)).unwrap();
sim_testbench.step_by(Duration::from_millis(1));
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_none());
// Reply takes 20ms
sim_testbench.step_until(Duration::from_millis(25)).unwrap();
sim_testbench.step_by(Duration::from_millis(25));
let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap();

View File

@ -1,4 +1,4 @@
use nexosim::time::MonotonicTime;
use asynchronix::time::MonotonicTime;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use serde::{de::DeserializeOwned, Deserialize, Serialize};

View File

@ -1,8 +1,8 @@
use acs::{MagnetometerModel, MagnetorquerModel};
use controller::{ModelAddrWrapper, SimController};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, SystemClock};
use controller::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,20 +63,19 @@ fn create_sim_controller(
} else {
SimInit::new()
};
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();
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);
SimController::new(
sys_clock,
request_receiver,
reply_sender,
simulation,
scheduler,
addrs,
mgm_addr,
pcdu_addr,
mgt_addr,
)
}

View File

@ -1,10 +1,7 @@
use delegate::delegate;
use std::sync::mpsc;
use std::{sync::mpsc, time::Duration};
use nexosim::{
simulation::ExecutionError,
time::{Deadline, MonotonicTime},
};
use asynchronix::time::MonotonicTime;
use satrs_minisim::{SimReply, SimRequest};
use crate::{controller::SimController, create_sim_controller, ThreadingModel};
@ -38,8 +35,8 @@ impl SimTestbench {
pub fn handle_sim_requests(&mut self, old_timestamp: MonotonicTime);
}
to self.sim_controller.simulation {
pub fn step(&mut self) -> Result<(), ExecutionError>;
pub fn step_until(&mut self, duration: impl Deadline) -> Result<(), ExecutionError>;
pub fn step(&mut self);
pub fn step_by(&mut self, duration: Duration);
}
}

View File

@ -1,4 +1,4 @@
use nexosim::time::MonotonicTime;
use asynchronix::time::MonotonicTime;
pub fn current_millis(time: MonotonicTime) -> u64 {
(time.as_secs() as u64 * 1000) + (time.subsec_nanos() as u64 / 1_000_000)

View File

@ -31,9 +31,9 @@ version = "0.13"
default-features = false
[dependencies.cobs]
git = "https://github.com/jamesmunns/cobs.rs.git"
git = "https://github.com/robamu/cobs.rs.git"
version = "0.2.3"
branch = "main"
branch = "all_features"
default-features = false
[dependencies.num-traits]

View File

@ -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 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]);
};
($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 mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
core::mem::MaybeUninit::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 mut $sizes_list_name: core::mem::MaybeUninit<[usize; $num_blocks]> =
core::mem::MaybeUninit::new([$crate::pool::STORE_FREE; $num_blocks]);
};
}
@ -482,17 +435,17 @@ 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(),
/// unsafe { SUBPOOL_SMALL.assume_init_mut() },
/// unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
/// SUBPOOL_SMALL_NUM_BLOCKS,
/// false
/// ).unwrap();
/// );
/// mem_pool.grow(
/// SUBPOOL_LARGE.get_mut().unwrap(),
/// SUBPOOL_LARGE_SIZES.get_mut().unwrap(),
/// unsafe { SUBPOOL_LARGE.assume_init_mut() },
/// unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
/// SUBPOOL_LARGE_NUM_BLOCKS,
/// false
/// ).unwrap();
/// );
///
/// let mut read_buf: [u8; 16] = [0; 16];
/// let mut addr;
@ -569,14 +522,12 @@ pub mod heapless_mod {
num_blocks: NumBlocks,
set_sizes_list_to_all_free: bool,
) -> Result<(), PoolIsFull> {
assert_eq!(
(subpool_memory.len() % num_blocks as usize),
0,
assert!(
(subpool_memory.len() % num_blocks as usize) == 0,
"pool slice length must be multiple of number of blocks"
);
assert_eq!(
num_blocks as usize,
sizes_list.len(),
assert!(
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 {
@ -1633,28 +1584,21 @@ mod tests {
mod heapless_tests {
use super::*;
use crate::static_subpool;
use std::cell::UnsafeCell;
use std::sync::Mutex;
use core::ptr::addr_of_mut;
const SUBPOOL_1_BLOCK_SIZE: usize = 4;
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
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],
));
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];
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
const SUBPOOL_2_BLOCK_SIZE: usize = 8;
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]);
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];
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
const SUBPOOL_3_BLOCK_SIZE: usize = 16;
@ -1697,24 +1641,24 @@ mod tests {
StaticHeaplessMemoryPool::new(false);
assert!(heapless_pool
.grow(
SUBPOOL_1.get_mut().unwrap(),
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
unsafe { &mut *addr_of_mut!(SUBPOOL_1) },
unsafe { &mut *addr_of_mut!(SUBPOOL_1_SIZES) },
SUBPOOL_1_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_2.get_mut().unwrap(),
SUBPOOL_2_SIZES.get_mut().unwrap(),
unsafe { &mut *addr_of_mut!(SUBPOOL_2) },
unsafe { &mut *addr_of_mut!(SUBPOOL_2_SIZES) },
SUBPOOL_2_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_3.get_mut().unwrap(),
SUBPOOL_3_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_3.assume_init_mut() },
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS,
true
)
@ -1836,16 +1780,16 @@ mod tests {
StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool
.grow(
SUBPOOL_2.get_mut().unwrap(),
SUBPOOL_2_SIZES.get_mut().unwrap(),
unsafe { &mut *addr_of_mut!(SUBPOOL_2) },
unsafe { &mut *addr_of_mut!(SUBPOOL_2_SIZES) },
SUBPOOL_2_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_4.get_mut().unwrap(),
SUBPOOL_4_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_4.assume_init_mut() },
unsafe { SUBPOOL_4_SIZES.assume_init_mut() },
SUBPOOL_4_NUM_ELEMENTS,
true
)
@ -1859,16 +1803,16 @@ mod tests {
StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool
.grow(
SUBPOOL_5.get_mut().unwrap(),
SUBPOOL_5_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_5.assume_init_mut() },
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
SUBPOOL_5_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_3.get_mut().unwrap(),
SUBPOOL_3_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_3.assume_init_mut() },
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS,
true
)
@ -1882,24 +1826,24 @@ mod tests {
StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool
.grow(
SUBPOOL_5.get_mut().unwrap(),
SUBPOOL_5_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_5.assume_init_mut() },
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
SUBPOOL_5_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_6.get_mut().unwrap(),
SUBPOOL_6_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_6.assume_init_mut() },
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
SUBPOOL_6_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_3.get_mut().unwrap(),
SUBPOOL_3_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_3.assume_init_mut() },
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS,
true
)
@ -1913,24 +1857,24 @@ mod tests {
StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool
.grow(
SUBPOOL_5.get_mut().unwrap(),
SUBPOOL_5_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_5.assume_init_mut() },
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
SUBPOOL_5_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_6.get_mut().unwrap(),
SUBPOOL_6_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_6.assume_init_mut() },
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
SUBPOOL_6_NUM_ELEMENTS,
true
)
.is_ok());
assert!(heapless_pool
.grow(
SUBPOOL_3.get_mut().unwrap(),
SUBPOOL_3_SIZES.get_mut().unwrap(),
unsafe { SUBPOOL_3.assume_init_mut() },
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS,
true
)