Merge pull request 'start adding ACS subsystem' (#266) from start-adding-acs-subsys into main
Reviewed-on: #266
This commit was merged in pull request #266.
This commit is contained in:
@@ -8,8 +8,7 @@ clap = { version = "4", features = ["derive"] }
|
||||
serialport = "4"
|
||||
toml = "0.9"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
satrs-stm32f3-disco-rtic = { path = "../stm32f3-disco-rtic" }
|
||||
spacepackets = { version = "0.17" }
|
||||
spacepackets = { version = "0.18" }
|
||||
embedded-models = { path = "../models" }
|
||||
tmtc-utils = { git = "https://egit.irs.uni-stuttgart.de/rust/tmtc-utils.git", version = "0.1", rev = "d8015379a45e77400dd9c71afbf22ca415ed67d1" }
|
||||
postcard = { version = "1", features = ["alloc"] }
|
||||
|
||||
@@ -66,6 +66,6 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
fn create_stm32f3_tc(request: &stm32f3::Request) -> CcsdsPacketCreatorOwned {
|
||||
let req_raw = postcard::to_allocvec(&request).unwrap();
|
||||
let sp_header = SpHeader::new_from_apid(satrs_stm32f3_disco_rtic::APID);
|
||||
let sp_header = SpHeader::new_from_apid(embedded_models::stm32f3::PUS_APID);
|
||||
CcsdsPacketCreatorOwned::new_tc_with_checksum(sp_header, &req_raw).unwrap()
|
||||
}
|
||||
|
||||
@@ -90,6 +90,6 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
fn create_stm32h7_tc(request: &stm32h7::Request) -> CcsdsPacketCreatorOwned {
|
||||
let req_raw = postcard::to_allocvec(&request).unwrap();
|
||||
let sp_header = SpHeader::new_from_apid(satrs_stm32f3_disco_rtic::APID);
|
||||
let sp_header = SpHeader::new_from_apid(embedded_models::stm32h7::PUS_APID);
|
||||
CcsdsPacketCreatorOwned::new_tc_with_checksum(sp_header, &req_raw).unwrap()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ edition = "2024"
|
||||
[dependencies]
|
||||
serde = { version = "1", default-features = false }
|
||||
defmt = { version = "1", optional = true }
|
||||
spacepackets = { version = "0.17", default-features = false, features = ["defmt", "serde"] }
|
||||
spacepackets = { version = "0.18", default-features = false, features = ["defmt", "serde"] }
|
||||
postcard = { version = "1", features = ["defmt"] }
|
||||
arbitrary-int = "2"
|
||||
|
||||
+7
-14
@@ -20,12 +20,6 @@ dependencies = [
|
||||
"as-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary-int"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary-int"
|
||||
version = "2.1.1"
|
||||
@@ -83,11 +77,10 @@ checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitbybit"
|
||||
version = "1.4.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec187a89ab07e209270175faf9e07ceb2755d984954e58a2296e325ddece2762"
|
||||
checksum = "71d2a3353d70ac1091a33cbf31fc7e77b19091538a7e306e3740712af19807ca"
|
||||
dependencies = [
|
||||
"arbitrary-int 1.3.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.118",
|
||||
@@ -644,7 +637,7 @@ dependencies = [
|
||||
name = "embedded-models"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.1.1",
|
||||
"arbitrary-int",
|
||||
"defmt 1.1.1",
|
||||
"postcard",
|
||||
"serde",
|
||||
@@ -1159,7 +1152,7 @@ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||
name = "satrs-stm32f3-disco-rtic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.1.1",
|
||||
"arbitrary-int",
|
||||
"cobs 0.5.1",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
@@ -1282,11 +1275,11 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||
|
||||
[[package]]
|
||||
name = "spacepackets"
|
||||
version = "0.17.0"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94979a990b4333f43667cba9fe9e72b9c4e9ada82e09fbe8fb250844358590cc"
|
||||
checksum = "93b9cf1b8372119e7c79383f1578f3e845b2ca2e6d07073e96f475ba354f44e3"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.1.1",
|
||||
"arbitrary-int",
|
||||
"bitbybit",
|
||||
"chrono",
|
||||
"crc",
|
||||
|
||||
@@ -20,7 +20,7 @@ embassy-time = { version = "0.5", features = ["defmt", "generic-queue-16", "defm
|
||||
enumset = "1"
|
||||
heapless = "0.9"
|
||||
embassy-sync = "0.8"
|
||||
spacepackets = { version = "0.17", default-features = false, features = ["defmt", "serde"] }
|
||||
spacepackets = { version = "0.18", default-features = false, features = ["defmt", "serde"] }
|
||||
static_cell = "2"
|
||||
cobs = { version = "0.5", default-features = false, features = ["defmt"] }
|
||||
postcard = { version = "1" }
|
||||
|
||||
+7
-14
@@ -20,12 +20,6 @@ dependencies = [
|
||||
"as-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary-int"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary-int"
|
||||
version = "2.1.1"
|
||||
@@ -89,11 +83,10 @@ checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitbybit"
|
||||
version = "1.4.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec187a89ab07e209270175faf9e07ceb2755d984954e58a2296e325ddece2762"
|
||||
checksum = "71d2a3353d70ac1091a33cbf31fc7e77b19091538a7e306e3740712af19807ca"
|
||||
dependencies = [
|
||||
"arbitrary-int 1.3.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
@@ -586,7 +579,7 @@ dependencies = [
|
||||
name = "embedded-models"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.1.1",
|
||||
"arbitrary-int",
|
||||
"defmt 1.1.0",
|
||||
"postcard",
|
||||
"serde",
|
||||
@@ -1028,7 +1021,7 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
name = "satrs-stm32h7-nucleo-rtic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.1.1",
|
||||
"arbitrary-int",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"defmt 1.1.0",
|
||||
@@ -1132,11 +1125,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacepackets"
|
||||
version = "0.17.0"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94979a990b4333f43667cba9fe9e72b9c4e9ada82e09fbe8fb250844358590cc"
|
||||
checksum = "93b9cf1b8372119e7c79383f1578f3e845b2ca2e6d07073e96f475ba354f44e3"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.1.1",
|
||||
"arbitrary-int",
|
||||
"bitbybit",
|
||||
"chrono",
|
||||
"crc",
|
||||
|
||||
@@ -25,7 +25,7 @@ panic-probe = { version = "1", features = ["print-defmt"] }
|
||||
embedded-alloc = "0.7"
|
||||
static_cell = "2"
|
||||
rtic = { version = "2", features = ["thumbv7-backend"] }
|
||||
spacepackets = { version = "0.17", default-features = false, features = ["defmt"] }
|
||||
spacepackets = { version = "0.18", default-features = false, features = ["defmt"] }
|
||||
postcard = "1"
|
||||
|
||||
embassy-stm32 = { git = "https://github.com/embassy-rs/embassy.git", rev = "dd8e4c14e53f088bae27c5d841ab7a4fa338a52c", version = "0.6", features = ["stm32h743zi", "memory-x", "defmt", "time-driver-any"]}
|
||||
|
||||
@@ -11,7 +11,7 @@ humantime = "2"
|
||||
serde = { version = "1" }
|
||||
satrs-example = { path = ".." }
|
||||
models = { path = "../models" }
|
||||
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
|
||||
spacepackets = { version = "0.18", default-features = false }
|
||||
bitbybit = "2"
|
||||
arbitrary-int = "2"
|
||||
ctrlc = { version = "3.5" }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::bail;
|
||||
use arbitrary_int::u11;
|
||||
use clap::Parser as _;
|
||||
use models::{Apid, MessageType, TcHeader, mgm::request::HkRequest};
|
||||
use models::{Apid, MessageType, TcHeader, acs::mgm::request::HkRequest};
|
||||
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||
use spacepackets::{CcsdsPacketIdAndPsc, SpacePacketHeader};
|
||||
use std::{
|
||||
@@ -136,7 +136,7 @@ fn main() -> anyhow::Result<()> {
|
||||
let request = models::ccsds::CcsdsTcPacketOwned::new_with_request(
|
||||
SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)),
|
||||
TcHeader::new(cmd.target_id(), models::MessageType::Ping),
|
||||
models::mgm::request::Request::Ping,
|
||||
models::acs::mgm::request::Request::Ping,
|
||||
);
|
||||
let sent_tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&request.sp_header);
|
||||
log::info!(
|
||||
@@ -151,8 +151,8 @@ fn main() -> anyhow::Result<()> {
|
||||
let request = models::ccsds::CcsdsTcPacketOwned::new_with_request(
|
||||
SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)),
|
||||
TcHeader::new(target_id, models::MessageType::Hk),
|
||||
models::mgm::request::Request::Hk(HkRequest {
|
||||
id: models::mgm::request::HkId::Sensor,
|
||||
models::acs::mgm::request::Request::Hk(HkRequest {
|
||||
id: models::acs::mgm::request::HkId::Sensor,
|
||||
req_type: models::HkRequestType::OneShot,
|
||||
}),
|
||||
);
|
||||
@@ -174,8 +174,8 @@ fn main() -> anyhow::Result<()> {
|
||||
let request = models::ccsds::CcsdsTcPacketOwned::new_with_request(
|
||||
SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)),
|
||||
TcHeader::new(target_id, models::MessageType::Mode),
|
||||
models::mgm::request::Request::Mode(
|
||||
models::mgm::request::ModeRequest::SetMode(dev_mode),
|
||||
models::acs::mgm::request::Request::Mode(
|
||||
models::acs::mgm::request::ModeRequest::SetMode(dev_mode),
|
||||
),
|
||||
);
|
||||
let sent_tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&request.sp_header);
|
||||
@@ -193,7 +193,7 @@ fn main() -> anyhow::Result<()> {
|
||||
let request = models::ccsds::CcsdsTcPacketOwned::new_with_request(
|
||||
SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)),
|
||||
TcHeader::new(cmd.target_id(), models::MessageType::Ping),
|
||||
models::mgm::request::Request::Ping,
|
||||
models::acs::mgm::request::Request::Ping,
|
||||
);
|
||||
let sent_tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&request.sp_header);
|
||||
log::info!(
|
||||
@@ -207,21 +207,21 @@ fn main() -> anyhow::Result<()> {
|
||||
if let Some(mode) = mgm_assembly_args.mode {
|
||||
let assembly_mode = match mode {
|
||||
AssemblyModeSelect::NoModeKeeping => {
|
||||
models::mgm_assembly::AssemblyMode::NoModeKeeping
|
||||
models::acs::mgm_assembly::Mode::NoModeKeeping
|
||||
}
|
||||
AssemblyModeSelect::Off => {
|
||||
models::mgm_assembly::AssemblyMode::Device(models::DeviceMode::Off)
|
||||
models::acs::mgm_assembly::Mode::Device(models::DeviceMode::Off)
|
||||
}
|
||||
AssemblyModeSelect::Normal => {
|
||||
models::mgm_assembly::AssemblyMode::Device(models::DeviceMode::Normal)
|
||||
models::acs::mgm_assembly::Mode::Device(models::DeviceMode::Normal)
|
||||
}
|
||||
};
|
||||
|
||||
let request = models::ccsds::CcsdsTcPacketOwned::new_with_request(
|
||||
SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)),
|
||||
TcHeader::new(target_id, models::MessageType::Mode),
|
||||
models::mgm_assembly::request::Request::Mode(
|
||||
models::mgm_assembly::request::ModeRequest::SetMode(assembly_mode),
|
||||
models::acs::mgm_assembly::request::Request::Mode(
|
||||
models::acs::mgm_assembly::request::ModeRequest::SetMode(assembly_mode),
|
||||
),
|
||||
);
|
||||
let sent_tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&request.sp_header);
|
||||
@@ -302,10 +302,10 @@ fn handle_raw_tm_packet(data: &[u8]) -> anyhow::Result<()> {
|
||||
postcard::from_bytes::<models::control::response::Response>(remainder);
|
||||
log::info!("Received response from controller: {:?}", response.unwrap());
|
||||
}
|
||||
models::ComponentId::AcsSubsystem => todo!(),
|
||||
models::ComponentId::AcsMgmAssembly => {
|
||||
let response =
|
||||
postcard::from_bytes::<models::mgm_assembly::response::Response>(remainder);
|
||||
let response = postcard::from_bytes::<
|
||||
models::acs::mgm_assembly::response::Response,
|
||||
>(remainder);
|
||||
log::info!(
|
||||
"Received response from MGM Assembly: {:?}",
|
||||
response.unwrap()
|
||||
@@ -313,19 +313,22 @@ fn handle_raw_tm_packet(data: &[u8]) -> anyhow::Result<()> {
|
||||
}
|
||||
models::ComponentId::AcsMgm0 => {
|
||||
let response =
|
||||
postcard::from_bytes::<models::mgm::response::Response>(remainder);
|
||||
postcard::from_bytes::<models::acs::mgm::response::Response>(remainder);
|
||||
log::info!("Received response from MGM0: {:?}", response.unwrap());
|
||||
}
|
||||
models::ComponentId::AcsMgm1 => {
|
||||
let response =
|
||||
postcard::from_bytes::<models::mgm::response::Response>(remainder);
|
||||
postcard::from_bytes::<models::acs::mgm::response::Response>(remainder);
|
||||
log::info!("Received response from MGM1: {:?}", response.unwrap());
|
||||
}
|
||||
models::ComponentId::AcsSubsystem => todo!(),
|
||||
models::ComponentId::EpsSubsystem => todo!(),
|
||||
models::ComponentId::UdpServer => todo!(),
|
||||
models::ComponentId::TcpServer => todo!(),
|
||||
models::ComponentId::Ground => todo!(),
|
||||
models::ComponentId::EventManager => {}
|
||||
models::ComponentId::AcsController => todo!(),
|
||||
models::ComponentId::AcsMgt => todo!(),
|
||||
}
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
|
||||
@@ -5,7 +5,7 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
|
||||
spacepackets = { version = "0.18", default-features = false }
|
||||
satrs = { path = "../../satrs" }
|
||||
num_enum = { version = "0.7" }
|
||||
strum = { version = "0.28", features = ["derive"] }
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#[derive(
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
num_enum::IntoPrimitive,
|
||||
num_enum::TryFromPrimitive,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum Mode {
|
||||
Passive,
|
||||
Safe,
|
||||
Idle,
|
||||
}
|
||||
|
||||
pub mod request {
|
||||
use super::Mode;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeRequest {
|
||||
SetMode(Mode),
|
||||
ReadMode,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
use super::Mode;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeReport {
|
||||
/// Mode of the assembly.
|
||||
Mode(super::Mode),
|
||||
/// Children are in wrong mode after commanding.
|
||||
WrongMode([Option<Mode>; 2]),
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ pub mod request {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MgmData {
|
||||
pub struct SensorData {
|
||||
pub valid: bool,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
@@ -50,11 +50,11 @@ pub struct MgmData {
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
use crate::{DeviceMode, Message, mgm::MgmData};
|
||||
use crate::{DeviceMode, Message, acs::mgm::SensorData};
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum HkResponse {
|
||||
MgmData(MgmData),
|
||||
MgmData(SensorData),
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)]
|
||||
+40
-11
@@ -1,9 +1,12 @@
|
||||
use core::str::FromStr;
|
||||
|
||||
use num_enum::TryFromPrimitive as _;
|
||||
use satrs::mode::ModeRaw;
|
||||
|
||||
use crate::DeviceMode;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AssemblyMode {
|
||||
pub enum Mode {
|
||||
/// The assembly mode ressembles the modes of the devices it controls. It also tries to keep
|
||||
/// the children in the correct mode by re-commanding them into the correct mode.
|
||||
Device(DeviceMode),
|
||||
@@ -11,22 +14,48 @@ pub enum AssemblyMode {
|
||||
NoModeKeeping,
|
||||
}
|
||||
|
||||
impl FromStr for AssemblyMode {
|
||||
impl From<Mode> for ModeRaw {
|
||||
fn from(value: Mode) -> Self {
|
||||
match value {
|
||||
Mode::Device(device_mode) => device_mode.into(),
|
||||
Mode::NoModeKeeping => 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ModeRaw> for Mode {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: ModeRaw) -> Result<Self, Self::Error> {
|
||||
match DeviceMode::try_from_primitive(value) {
|
||||
Ok(val) => Ok(Mode::Device(val)),
|
||||
Err(_) => {
|
||||
if value == 5 {
|
||||
Ok(Mode::NoModeKeeping)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Mode {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"off" => Ok(AssemblyMode::Device(DeviceMode::Off)),
|
||||
"on" => Ok(AssemblyMode::Device(DeviceMode::On)),
|
||||
"normal" => Ok(AssemblyMode::Device(DeviceMode::Normal)),
|
||||
"no_mode_keeping" => Ok(AssemblyMode::NoModeKeeping),
|
||||
"off" => Ok(Mode::Device(DeviceMode::Off)),
|
||||
"on" => Ok(Mode::Device(DeviceMode::On)),
|
||||
"normal" => Ok(Mode::Device(DeviceMode::Normal)),
|
||||
"no_mode_keeping" => Ok(Mode::NoModeKeeping),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod request {
|
||||
use crate::{HkRequestType, Message, mgm_assembly::AssemblyMode};
|
||||
use crate::{HkRequestType, Message, acs::mgm_assembly::Mode};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub enum HkId {
|
||||
@@ -35,7 +64,7 @@ pub mod request {
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeRequest {
|
||||
SetMode(AssemblyMode),
|
||||
SetMode(Mode),
|
||||
ReadMode,
|
||||
}
|
||||
|
||||
@@ -75,9 +104,9 @@ pub mod response {
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeReport {
|
||||
pub enum ModeResponse {
|
||||
/// Mode of the assembly.
|
||||
Mode(super::AssemblyMode),
|
||||
Mode(super::Mode),
|
||||
/// Timeout failure setting the children modes.
|
||||
SetModeTimeout([Option<DeviceMode>; 2]),
|
||||
/// Children are in wrong mode after commanding.
|
||||
@@ -89,7 +118,7 @@ pub mod response {
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Response {
|
||||
Ok,
|
||||
Mode(ModeReport),
|
||||
Mode(ModeResponse),
|
||||
}
|
||||
|
||||
impl Response {
|
||||
@@ -0,0 +1,38 @@
|
||||
#[derive(
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
num_enum::IntoPrimitive,
|
||||
num_enum::TryFromPrimitive,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum Mode {
|
||||
Off,
|
||||
Normal,
|
||||
}
|
||||
|
||||
pub mod request {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeRequest {
|
||||
SetMode(Mode),
|
||||
ReadMode,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeReport {
|
||||
/// Mode of the assembly.
|
||||
Mode(super::Mode),
|
||||
/// Children are in wrong mode after commanding.
|
||||
WrongMode([Option<Mode>; 2]),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod ctrl;
|
||||
pub mod mgm;
|
||||
pub mod mgm_assembly;
|
||||
pub mod mgt;
|
||||
pub mod subsystem;
|
||||
@@ -0,0 +1,88 @@
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Mode {
|
||||
Off,
|
||||
Safe,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ModeChild {
|
||||
MgmAssembly,
|
||||
Mgt,
|
||||
Controller,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
/// Each subsystem mode has a fallback mode to allow clean transitions.
|
||||
pub const fn fallback_mode(&self) -> Self {
|
||||
match self {
|
||||
Mode::Off => Mode::Safe,
|
||||
Mode::Safe => Mode::Safe,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod request {
|
||||
use crate::Message;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeRequest {
|
||||
SetMode(super::Mode),
|
||||
ReadMode,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Request {
|
||||
Ping,
|
||||
Mode(ModeRequest),
|
||||
}
|
||||
|
||||
impl Request {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
match self {
|
||||
Request::Ping => crate::MessageType::Verification,
|
||||
Request::Mode(_mode) => crate::MessageType::Mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Message for Request {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
self.message_type()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
use crate::Message;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ModeResponse {
|
||||
/// Mode of the subsystem.
|
||||
Mode(super::Mode),
|
||||
/// Command timeout when commanding a child.
|
||||
CommandTimeout(super::ModeChild),
|
||||
/// Can not keep mode because a child changed mode unexpectedly.
|
||||
CanNotKeepMode(super::ModeChild),
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
|
||||
pub enum Response {
|
||||
Ok,
|
||||
Mode(ModeResponse),
|
||||
}
|
||||
|
||||
impl Response {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
match self {
|
||||
Response::Ok => crate::MessageType::Verification,
|
||||
Response::Mode(_mode_report) => crate::MessageType::Mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Message for Response {
|
||||
fn message_type(&self) -> crate::MessageType {
|
||||
self.message_type()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,9 @@ use spacepackets::{
|
||||
time::cds::{CdsTime, MIN_CDS_FIELD_LEN},
|
||||
};
|
||||
|
||||
pub mod acs;
|
||||
pub mod ccsds;
|
||||
pub mod control;
|
||||
pub mod mgm;
|
||||
pub mod mgm_assembly;
|
||||
pub mod pcdu;
|
||||
|
||||
#[derive(
|
||||
@@ -30,8 +29,10 @@ pub enum ComponentId {
|
||||
|
||||
AcsSubsystem,
|
||||
AcsMgmAssembly,
|
||||
AcsController,
|
||||
AcsMgm0,
|
||||
AcsMgm1,
|
||||
AcsMgt,
|
||||
|
||||
EpsSubsystem,
|
||||
EpsPcdu,
|
||||
@@ -160,7 +161,18 @@ pub trait Message {
|
||||
/// The states are related both to the physical and the logical state of the device. Some
|
||||
/// device handlers control the power supply of their own device and an off state might also
|
||||
/// mean that the device is physically off.
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[derive(
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Copy,
|
||||
Clone,
|
||||
num_enum::IntoPrimitive,
|
||||
num_enum::TryFromPrimitive,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum DeviceMode {
|
||||
Off = 0,
|
||||
On = 1,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use models::mgm::MgmData;
|
||||
use models::mgm::request::ModeRequest;
|
||||
use models::mgm::response::ModeResponse;
|
||||
use models::acs::mgm::SensorData;
|
||||
use models::acs::mgm::request::ModeRequest;
|
||||
use models::acs::mgm::response::ModeResponse;
|
||||
use models::pcdu::SwitchId;
|
||||
use models::{ComponentId, DeviceMode, HkRequestType, mgm};
|
||||
use models::{ComponentId, DeviceMode, HkRequestType, acs::mgm};
|
||||
use satrs::spacepackets::CcsdsPacketIdAndPsc;
|
||||
use satrs_example::{HkHelperSingleSet, ModeHelper, TimestampHelper, TmtcQueues};
|
||||
use satrs_minisim::acs::MgmRequestLis3Mdl;
|
||||
@@ -153,7 +153,7 @@ pub struct MgmHandlerLis3Mdl {
|
||||
switch_helper: PowerSwitchHelper,
|
||||
tmtc_queues: TmtcQueues,
|
||||
pub spi_com: SpiCommunication,
|
||||
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||
shared_mgm_set: Arc<Mutex<SensorData>>,
|
||||
buffers: BufWrapper,
|
||||
stamp_helper: TimestampHelper,
|
||||
hk_helper: HkHelperSingleSet,
|
||||
@@ -167,7 +167,7 @@ impl MgmHandlerLis3Mdl {
|
||||
tmtc_queues: TmtcQueues,
|
||||
switch_helper: PowerSwitchHelper,
|
||||
spi_com: SpiCommunication,
|
||||
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||
shared_mgm_set: Arc<Mutex<SensorData>>,
|
||||
mode_leaf_helper: ModeLeafHelper,
|
||||
mode_timeout: Duration,
|
||||
) -> Self {
|
||||
@@ -308,7 +308,7 @@ impl MgmHandlerLis3Mdl {
|
||||
pub fn handle_hk_request(
|
||||
&mut self,
|
||||
tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
hk_request: &models::mgm::request::HkRequest,
|
||||
hk_request: &models::acs::mgm::request::HkRequest,
|
||||
) {
|
||||
match hk_request.req_type {
|
||||
HkRequestType::OneShot => {
|
||||
@@ -462,8 +462,8 @@ mod tests {
|
||||
use arbitrary_int::u11;
|
||||
use models::{
|
||||
Apid, ComponentId, TcHeader,
|
||||
acs::mgm::request::HkRequest,
|
||||
ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned},
|
||||
mgm::request::HkRequest,
|
||||
pcdu::{SwitchRequest, SwitchState, SwitchStateBinary},
|
||||
};
|
||||
use satrs::{request::GenericMessage, spacepackets::SpacePacketHeader};
|
||||
@@ -490,7 +490,7 @@ mod tests {
|
||||
|
||||
pub fn create_request_tc(
|
||||
select: MgmSelect,
|
||||
request: models::mgm::request::Request,
|
||||
request: models::acs::mgm::request::Request,
|
||||
) -> models::ccsds::CcsdsTcPacketOwned {
|
||||
models::ccsds::CcsdsTcPacketOwned::new_with_request(
|
||||
SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)),
|
||||
@@ -533,7 +533,7 @@ mod tests {
|
||||
SpiCommunication::Test(TestSpiInterface::default()),
|
||||
shared_mgm_set,
|
||||
mode_leaf_helper,
|
||||
Duration::from_millis(200),
|
||||
Duration::from_millis(100),
|
||||
);
|
||||
Self {
|
||||
assembly_mode_request_tx,
|
||||
@@ -601,9 +601,10 @@ mod tests {
|
||||
|
||||
assert_eq!(tm_packet.tm_header.sender_id, ComponentId::AcsMgm0);
|
||||
|
||||
let response = postcard::from_bytes::<models::mgm::response::Response>(&tm_packet.payload)
|
||||
.expect("failed to deserialize mode reply");
|
||||
matches!(response, models::mgm::response::Response::Ok);
|
||||
let response =
|
||||
postcard::from_bytes::<models::acs::mgm::response::Response>(&tm_packet.payload)
|
||||
.expect("failed to deserialize mode reply");
|
||||
matches!(response, models::acs::mgm::response::Response::Ok);
|
||||
// The device should have been polled once.
|
||||
assert_eq!(testbench.test_spi_interface().call_count, 1);
|
||||
let mgm_set = *testbench.handler.shared_mgm_set.lock().unwrap();
|
||||
@@ -689,9 +690,10 @@ mod tests {
|
||||
|
||||
assert_eq!(tm_packet.tm_header.sender_id, ComponentId::AcsMgm0);
|
||||
|
||||
let response = postcard::from_bytes::<models::mgm::response::Response>(&tm_packet.payload)
|
||||
.expect("failed to deserialize mode reply");
|
||||
if let models::mgm::response::Response::Hk(mgm::response::HkResponse::MgmData(data)) =
|
||||
let response =
|
||||
postcard::from_bytes::<models::acs::mgm::response::Response>(&tm_packet.payload)
|
||||
.expect("failed to deserialize mode reply");
|
||||
if let models::acs::mgm::response::Response::Hk(mgm::response::HkResponse::MgmData(data)) =
|
||||
response
|
||||
{
|
||||
assert_eq!(data.valid, false);
|
||||
@@ -740,17 +742,18 @@ mod tests {
|
||||
|
||||
assert_eq!(mode_tm.tm_header.sender_id, ComponentId::AcsMgm0);
|
||||
|
||||
let response = postcard::from_bytes::<models::mgm::response::Response>(&mode_tm.payload)
|
||||
.expect("failed to deserialize mode reply");
|
||||
matches!(response, models::mgm::response::Response::Ok);
|
||||
let response =
|
||||
postcard::from_bytes::<models::acs::mgm::response::Response>(&mode_tm.payload)
|
||||
.expect("failed to deserialize mode reply");
|
||||
matches!(response, models::acs::mgm::response::Response::Ok);
|
||||
|
||||
let hk_tm = testbench.tm_rx.try_recv().expect("no hk reply generated");
|
||||
|
||||
assert_eq!(hk_tm.tm_header.sender_id, ComponentId::AcsMgm0);
|
||||
|
||||
let response = postcard::from_bytes::<models::mgm::response::Response>(&hk_tm.payload)
|
||||
let response = postcard::from_bytes::<models::acs::mgm::response::Response>(&hk_tm.payload)
|
||||
.expect("failed to deserialize mode reply");
|
||||
if let models::mgm::response::Response::Hk(mgm::response::HkResponse::MgmData(data)) =
|
||||
if let models::acs::mgm::response::Response::Hk(mgm::response::HkResponse::MgmData(data)) =
|
||||
response
|
||||
{
|
||||
// Set is now valid.
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{sync::mpsc, time::Duration};
|
||||
|
||||
use models::{
|
||||
ComponentId, DeviceMode,
|
||||
mgm_assembly::{AssemblyMode, request, response},
|
||||
acs::mgm_assembly::{Mode, request, response},
|
||||
};
|
||||
use satrs::spacepackets::CcsdsPacketIdAndPsc;
|
||||
use satrs_example::{ModeHelper, TmtcQueues};
|
||||
@@ -11,13 +11,13 @@ use crate::ccsds::pack_ccsds_tm_packet_for_now;
|
||||
|
||||
pub struct ParentQueueHelper {
|
||||
pub request_rx: mpsc::Receiver<request::ModeRequest>,
|
||||
pub report_tx: mpsc::SyncSender<response::ModeReport>,
|
||||
pub report_tx: mpsc::SyncSender<response::ModeResponse>,
|
||||
}
|
||||
|
||||
/// Helper component for communication with a parent component, which is usually as assembly.
|
||||
pub struct ChildrenQueueHelper {
|
||||
pub request_tx_queues: [mpsc::SyncSender<models::mgm::request::ModeRequest>; 2],
|
||||
pub report_rx_queues: [mpsc::Receiver<models::mgm::response::ModeResponse>; 2],
|
||||
pub request_tx_queues: [mpsc::SyncSender<models::acs::mgm::request::ModeRequest>; 2],
|
||||
pub report_rx_queues: [mpsc::Receiver<models::acs::mgm::response::ModeResponse>; 2],
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -35,7 +35,7 @@ pub struct MgmInfo {
|
||||
|
||||
/// MGM assembly component.
|
||||
pub struct Assembly {
|
||||
mode_helper: ModeHelper<AssemblyMode, TransitionState>,
|
||||
mode_helper: ModeHelper<Mode, TransitionState>,
|
||||
/// This boolean is used for the distinction between transitions commanded by the parent
|
||||
/// or by ground, and transitions which were commanded autonomously as part of children
|
||||
/// mode keeping.
|
||||
@@ -56,7 +56,7 @@ impl Assembly {
|
||||
mode_timeout: Duration,
|
||||
) -> Self {
|
||||
Self {
|
||||
mode_helper: ModeHelper::new(AssemblyMode::NoModeKeeping, mode_timeout),
|
||||
mode_helper: ModeHelper::new(Mode::NoModeKeeping, mode_timeout),
|
||||
mode_keeping_transition: false,
|
||||
tmtc_queues,
|
||||
mgm_modes: [MgmInfo::default(); 2],
|
||||
@@ -80,21 +80,21 @@ impl Assembly {
|
||||
match self.tmtc_queues.tc_rx.try_recv() {
|
||||
Ok(packet) => {
|
||||
let tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&packet.sp_header);
|
||||
match postcard::from_bytes::<models::mgm_assembly::request::Request>(
|
||||
match postcard::from_bytes::<models::acs::mgm_assembly::request::Request>(
|
||||
&packet.payload,
|
||||
) {
|
||||
Ok(request) => match request {
|
||||
models::mgm_assembly::request::Request::Ping => {
|
||||
models::acs::mgm_assembly::request::Request::Ping => {
|
||||
self.send_telemetry(Some(tc_id), response::Response::Ok)
|
||||
}
|
||||
models::mgm_assembly::request::Request::Mode(request) => {
|
||||
models::acs::mgm_assembly::request::Request::Mode(request) => {
|
||||
match request {
|
||||
request::ModeRequest::SetMode(assembly_mode) => {
|
||||
self.start_transition(false, assembly_mode, Some(tc_id))
|
||||
}
|
||||
request::ModeRequest::ReadMode => self.send_telemetry(
|
||||
Some(tc_id),
|
||||
response::Response::Mode(response::ModeReport::Mode(
|
||||
response::Response::Mode(response::ModeResponse::Mode(
|
||||
self.mode(),
|
||||
)),
|
||||
),
|
||||
@@ -117,7 +117,7 @@ impl Assembly {
|
||||
pub fn send_telemetry(
|
||||
&self,
|
||||
tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
response: models::mgm_assembly::response::Response,
|
||||
response: models::acs::mgm_assembly::response::Response,
|
||||
) {
|
||||
match pack_ccsds_tm_packet_for_now(Self::ID, tc_id, &response) {
|
||||
Ok(packet) => {
|
||||
@@ -136,17 +136,17 @@ impl Assembly {
|
||||
match self.parent_queues.request_rx.try_recv() {
|
||||
Ok(request) => match request {
|
||||
request::ModeRequest::SetMode(assembly_mode) => match assembly_mode {
|
||||
AssemblyMode::Device(_device_mode) => {
|
||||
Mode::Device(_device_mode) => {
|
||||
self.start_transition(false, assembly_mode, None);
|
||||
}
|
||||
AssemblyMode::NoModeKeeping => {
|
||||
self.mode_helper.current = AssemblyMode::NoModeKeeping;
|
||||
Mode::NoModeKeeping => {
|
||||
self.mode_helper.current = Mode::NoModeKeeping;
|
||||
}
|
||||
},
|
||||
request::ModeRequest::ReadMode => self
|
||||
.parent_queues
|
||||
.report_tx
|
||||
.send(response::ModeReport::Mode(self.mode_helper.current))
|
||||
.send(response::ModeResponse::Mode(self.mode_helper.current))
|
||||
.unwrap(),
|
||||
},
|
||||
Err(e) => match e {
|
||||
@@ -165,12 +165,12 @@ impl Assembly {
|
||||
loop {
|
||||
match rx.try_recv() {
|
||||
Ok(report) => match report {
|
||||
models::mgm::response::ModeResponse::Mode(device_mode) => {
|
||||
models::acs::mgm::response::ModeResponse::Mode(device_mode) => {
|
||||
self.mgm_modes[idx].mode = Some(device_mode);
|
||||
self.mgm_modes[idx].reply_received = true;
|
||||
mode_report_received = true;
|
||||
}
|
||||
models::mgm::response::ModeResponse::SetModeTimeout => {
|
||||
models::acs::mgm::response::ModeResponse::SetModeTimeout => {
|
||||
// Ignore, handle this with our own timeout.
|
||||
log::warn!("MGM {} mode timeout", idx);
|
||||
}
|
||||
@@ -191,23 +191,23 @@ impl Assembly {
|
||||
// Transition is active, check for completion.
|
||||
if self.mode_helper.transition_active()
|
||||
&& self.mgm_modes.iter().all(|i| i.reply_received)
|
||||
&& let AssemblyMode::Device(device_mode) = self.mode_helper.target.unwrap()
|
||||
&& let Mode::Device(device_mode) = self.mode_helper.target.unwrap()
|
||||
{
|
||||
// If at least one child reached the correct mode, we are done.
|
||||
if self.mgm_modes.iter().any(|i| i.mode == Some(device_mode)) {
|
||||
self.handle_mode_reached(true);
|
||||
} else {
|
||||
let report = if self.mode_keeping_transition {
|
||||
response::ModeReport::CanNotKeepMode(self.mgm_modes.map(|info| info.mode))
|
||||
response::ModeResponse::CanNotKeepMode(self.mgm_modes.map(|info| info.mode))
|
||||
} else {
|
||||
response::ModeReport::WrongMode(self.mgm_modes.map(|info| info.mode))
|
||||
response::ModeResponse::WrongMode(self.mgm_modes.map(|info| info.mode))
|
||||
};
|
||||
self.handle_mode_transition_failure(report);
|
||||
}
|
||||
}
|
||||
|
||||
// Mode keeping active: Check children modes.
|
||||
if let AssemblyMode::Device(device_mode) = self.mode_helper.current
|
||||
if let Mode::Device(device_mode) = self.mode_helper.current
|
||||
&& self
|
||||
.mgm_modes
|
||||
.iter()
|
||||
@@ -226,8 +226,8 @@ impl Assembly {
|
||||
}
|
||||
let target = self.mode_helper.target.unwrap();
|
||||
let device_mode = match target {
|
||||
AssemblyMode::Device(device_mode) => device_mode,
|
||||
AssemblyMode::NoModeKeeping => {
|
||||
Mode::Device(device_mode) => device_mode,
|
||||
Mode::NoModeKeeping => {
|
||||
self.handle_mode_reached(true);
|
||||
return;
|
||||
}
|
||||
@@ -240,9 +240,9 @@ impl Assembly {
|
||||
&& self.mode_helper.timed_out()
|
||||
{
|
||||
let report = if self.mode_keeping_transition {
|
||||
response::ModeReport::CanNotKeepMode(self.mgm_modes.map(|info| info.mode))
|
||||
response::ModeResponse::CanNotKeepMode(self.mgm_modes.map(|info| info.mode))
|
||||
} else {
|
||||
response::ModeReport::SetModeTimeout(self.mgm_modes.map(|info| info.mode))
|
||||
response::ModeResponse::SetModeTimeout(self.mgm_modes.map(|info| info.mode))
|
||||
};
|
||||
self.handle_mode_transition_failure(report);
|
||||
}
|
||||
@@ -256,15 +256,15 @@ impl Assembly {
|
||||
}
|
||||
self.parent_queues
|
||||
.report_tx
|
||||
.send(response::ModeReport::Mode(self.mode_helper.current))
|
||||
.send(response::ModeResponse::Mode(self.mode_helper.current))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_mode_transition_failure(&mut self, report: response::ModeReport) {
|
||||
pub fn handle_mode_transition_failure(&mut self, report: response::ModeResponse) {
|
||||
if self.mode_helper.tc_commander.is_some() {
|
||||
self.send_telemetry(
|
||||
self.mode_helper.tc_commander,
|
||||
response::Response::Mode(response::ModeReport::SetModeTimeout(
|
||||
response::Response::Mode(response::ModeResponse::SetModeTimeout(
|
||||
self.mgm_modes.map(|info| info.mode),
|
||||
)),
|
||||
);
|
||||
@@ -275,7 +275,7 @@ impl Assembly {
|
||||
|
||||
pub fn command_children(&self, mode: DeviceMode) {
|
||||
for tx in &self.children_queues.request_tx_queues {
|
||||
tx.send(models::mgm::request::ModeRequest::SetMode(mode))
|
||||
tx.send(models::acs::mgm::request::ModeRequest::SetMode(mode))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ impl Assembly {
|
||||
pub fn start_transition(
|
||||
&mut self,
|
||||
mode_keeping: bool,
|
||||
target: AssemblyMode,
|
||||
target: Mode,
|
||||
tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
) {
|
||||
self.mode_keeping_transition = mode_keeping;
|
||||
@@ -304,7 +304,7 @@ impl Assembly {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mode(&self) -> AssemblyMode {
|
||||
pub fn mode(&self) -> Mode {
|
||||
self.mode_helper.current
|
||||
}
|
||||
|
||||
@@ -322,8 +322,8 @@ mod tests {
|
||||
use arbitrary_int::u11;
|
||||
use models::{
|
||||
Apid, Message, MessageType, TcHeader,
|
||||
acs::mgm_assembly,
|
||||
ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned},
|
||||
mgm_assembly,
|
||||
};
|
||||
use satrs::spacepackets::SpacePacketHeader;
|
||||
|
||||
@@ -331,9 +331,9 @@ mod tests {
|
||||
|
||||
pub struct Testbench {
|
||||
subsystem_req_tx: mpsc::SyncSender<request::ModeRequest>,
|
||||
subsystem_report_rx: mpsc::Receiver<response::ModeReport>,
|
||||
mgm_request_rx: [mpsc::Receiver<models::mgm::request::ModeRequest>; 2],
|
||||
mgm_report_tx: [mpsc::SyncSender<models::mgm::response::ModeResponse>; 2],
|
||||
subsystem_report_rx: mpsc::Receiver<response::ModeResponse>,
|
||||
mgm_request_rx: [mpsc::Receiver<models::acs::mgm::request::ModeRequest>; 2],
|
||||
mgm_report_tx: [mpsc::SyncSender<models::acs::mgm::response::ModeResponse>; 2],
|
||||
tc_tx: mpsc::SyncSender<CcsdsTcPacketOwned>,
|
||||
tm_rx: mpsc::Receiver<CcsdsTmPacketOwned>,
|
||||
assembly: Assembly,
|
||||
@@ -396,7 +396,7 @@ mod tests {
|
||||
}
|
||||
|
||||
pub fn create_request_tc(
|
||||
request: models::mgm_assembly::request::Request,
|
||||
request: models::acs::mgm_assembly::request::Request,
|
||||
) -> models::ccsds::CcsdsTcPacketOwned {
|
||||
models::ccsds::CcsdsTcPacketOwned::new_with_request(
|
||||
SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)),
|
||||
@@ -411,7 +411,7 @@ mod tests {
|
||||
tb.assert_all_queues_empty();
|
||||
tb.assembly.periodic_operation();
|
||||
tb.assert_all_queues_empty();
|
||||
assert_eq!(tb.assembly.mode(), AssemblyMode::NoModeKeeping);
|
||||
assert_eq!(tb.assembly.mode(), Mode::NoModeKeeping);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -419,7 +419,7 @@ mod tests {
|
||||
let mut tb = Testbench::new();
|
||||
tb.tc_tx
|
||||
.send(create_request_tc(mgm_assembly::request::Request::Mode(
|
||||
request::ModeRequest::SetMode(AssemblyMode::Device(DeviceMode::Normal)),
|
||||
request::ModeRequest::SetMode(Mode::Device(DeviceMode::Normal)),
|
||||
)))
|
||||
.unwrap();
|
||||
tb.assembly.periodic_operation();
|
||||
@@ -429,13 +429,13 @@ mod tests {
|
||||
let request = rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
request,
|
||||
models::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
models::acs::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
);
|
||||
}
|
||||
|
||||
// Confirm the mode is set.
|
||||
for tx in tb.mgm_report_tx.iter() {
|
||||
tx.send(models::mgm::response::ModeResponse::Mode(
|
||||
tx.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Normal,
|
||||
))
|
||||
.unwrap();
|
||||
@@ -443,7 +443,7 @@ mod tests {
|
||||
|
||||
tb.assembly.periodic_operation();
|
||||
assert!(!tb.assembly.mode_transition_active());
|
||||
assert_eq!(tb.assembly.mode(), AssemblyMode::Device(DeviceMode::Normal));
|
||||
assert_eq!(tb.assembly.mode(), Mode::Device(DeviceMode::Normal));
|
||||
|
||||
let response = tb.tm_rx.try_recv().unwrap();
|
||||
assert_eq!(response.tm_header.sender_id, Assembly::ID);
|
||||
@@ -456,7 +456,7 @@ mod tests {
|
||||
fn test_parent_commanded_transition() {
|
||||
let mut tb = Testbench::new();
|
||||
tb.subsystem_req_tx
|
||||
.send(request::ModeRequest::SetMode(AssemblyMode::Device(
|
||||
.send(request::ModeRequest::SetMode(Mode::Device(
|
||||
DeviceMode::Normal,
|
||||
)))
|
||||
.unwrap();
|
||||
@@ -467,13 +467,13 @@ mod tests {
|
||||
let request = rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
request,
|
||||
models::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
models::acs::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
);
|
||||
}
|
||||
|
||||
// Confirm the mode is set.
|
||||
for tx in tb.mgm_report_tx.iter() {
|
||||
tx.send(models::mgm::response::ModeResponse::Mode(
|
||||
tx.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Normal,
|
||||
))
|
||||
.unwrap();
|
||||
@@ -481,12 +481,12 @@ mod tests {
|
||||
|
||||
tb.assembly.periodic_operation();
|
||||
assert!(!tb.assembly.mode_transition_active());
|
||||
assert_eq!(tb.assembly.mode(), AssemblyMode::Device(DeviceMode::Normal));
|
||||
assert_eq!(tb.assembly.mode(), Mode::Device(DeviceMode::Normal));
|
||||
|
||||
let report = tb.subsystem_report_rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
report,
|
||||
response::ModeReport::Mode(AssemblyMode::Device(DeviceMode::Normal))
|
||||
response::ModeResponse::Mode(Mode::Device(DeviceMode::Normal))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ mod tests {
|
||||
fn test_one_mgm_is_sufficient() {
|
||||
let mut tb = Testbench::new();
|
||||
tb.subsystem_req_tx
|
||||
.send(request::ModeRequest::SetMode(AssemblyMode::Device(
|
||||
.send(request::ModeRequest::SetMode(Mode::Device(
|
||||
DeviceMode::Normal,
|
||||
)))
|
||||
.unwrap();
|
||||
@@ -505,28 +505,30 @@ mod tests {
|
||||
let request = rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
request,
|
||||
models::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
models::acs::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
);
|
||||
}
|
||||
|
||||
// One device is sufficient.
|
||||
tb.mgm_report_tx[0]
|
||||
.send(models::mgm::response::ModeResponse::Mode(
|
||||
.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Normal,
|
||||
))
|
||||
.unwrap();
|
||||
tb.mgm_report_tx[1]
|
||||
.send(models::mgm::response::ModeResponse::Mode(DeviceMode::Off))
|
||||
.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Off,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
tb.assembly.periodic_operation();
|
||||
assert!(!tb.assembly.mode_transition_active());
|
||||
assert_eq!(tb.assembly.mode(), AssemblyMode::Device(DeviceMode::Normal));
|
||||
assert_eq!(tb.assembly.mode(), Mode::Device(DeviceMode::Normal));
|
||||
|
||||
let report = tb.subsystem_report_rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
report,
|
||||
response::ModeReport::Mode(AssemblyMode::Device(DeviceMode::Normal))
|
||||
response::ModeResponse::Mode(Mode::Device(DeviceMode::Normal))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -534,7 +536,7 @@ mod tests {
|
||||
fn test_mode_commanding_fails() {
|
||||
let mut tb = Testbench::new();
|
||||
tb.subsystem_req_tx
|
||||
.send(request::ModeRequest::SetMode(AssemblyMode::Device(
|
||||
.send(request::ModeRequest::SetMode(Mode::Device(
|
||||
DeviceMode::Normal,
|
||||
)))
|
||||
.unwrap();
|
||||
@@ -545,24 +547,26 @@ mod tests {
|
||||
let request = rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
request,
|
||||
models::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
models::acs::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
);
|
||||
}
|
||||
|
||||
// Confirm the mode is set.
|
||||
for tx in tb.mgm_report_tx.iter() {
|
||||
tx.send(models::mgm::response::ModeResponse::Mode(DeviceMode::Off))
|
||||
.unwrap();
|
||||
tx.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Off,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
tb.assembly.periodic_operation();
|
||||
assert!(!tb.assembly.mode_transition_active());
|
||||
assert_eq!(tb.assembly.mode(), AssemblyMode::NoModeKeeping);
|
||||
assert_eq!(tb.assembly.mode(), Mode::NoModeKeeping);
|
||||
|
||||
let report = tb.subsystem_report_rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
report,
|
||||
response::ModeReport::WrongMode([Some(DeviceMode::Off), Some(DeviceMode::Off)])
|
||||
response::ModeResponse::WrongMode([Some(DeviceMode::Off), Some(DeviceMode::Off)])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -570,7 +574,7 @@ mod tests {
|
||||
fn test_mode_keeping_fails() {
|
||||
let mut tb = Testbench::new();
|
||||
tb.subsystem_req_tx
|
||||
.send(request::ModeRequest::SetMode(AssemblyMode::Device(
|
||||
.send(request::ModeRequest::SetMode(Mode::Device(
|
||||
DeviceMode::Normal,
|
||||
)))
|
||||
.unwrap();
|
||||
@@ -581,13 +585,13 @@ mod tests {
|
||||
let request = rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
request,
|
||||
models::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
models::acs::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
);
|
||||
}
|
||||
|
||||
// Confirm the mode is set.
|
||||
for tx in tb.mgm_report_tx.iter() {
|
||||
tx.send(models::mgm::response::ModeResponse::Mode(
|
||||
tx.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Normal,
|
||||
))
|
||||
.unwrap();
|
||||
@@ -595,17 +599,19 @@ mod tests {
|
||||
|
||||
tb.assembly.periodic_operation();
|
||||
assert!(!tb.assembly.mode_transition_active());
|
||||
assert_eq!(tb.assembly.mode(), AssemblyMode::Device(DeviceMode::Normal));
|
||||
assert_eq!(tb.assembly.mode(), Mode::Device(DeviceMode::Normal));
|
||||
|
||||
let report = tb.subsystem_report_rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
report,
|
||||
response::ModeReport::Mode(AssemblyMode::Device(DeviceMode::Normal))
|
||||
response::ModeResponse::Mode(Mode::Device(DeviceMode::Normal))
|
||||
);
|
||||
|
||||
for tx in tb.mgm_report_tx.iter() {
|
||||
tx.send(models::mgm::response::ModeResponse::Mode(DeviceMode::Off))
|
||||
.unwrap();
|
||||
tx.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Off,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
// This should start mode keeping.
|
||||
tb.assembly.periodic_operation();
|
||||
@@ -615,20 +621,22 @@ mod tests {
|
||||
let request = rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
request,
|
||||
models::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
models::acs::mgm::request::ModeRequest::SetMode(DeviceMode::Normal)
|
||||
);
|
||||
}
|
||||
|
||||
// Let the mode keeping fail.
|
||||
for tx in tb.mgm_report_tx.iter() {
|
||||
tx.send(models::mgm::response::ModeResponse::Mode(DeviceMode::Off))
|
||||
.unwrap();
|
||||
tx.send(models::acs::mgm::response::ModeResponse::Mode(
|
||||
DeviceMode::Off,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
tb.assembly.periodic_operation();
|
||||
let report = tb.subsystem_report_rx.try_recv().unwrap();
|
||||
assert_eq!(
|
||||
report,
|
||||
response::ModeReport::CanNotKeepMode([Some(DeviceMode::Off), Some(DeviceMode::Off)])
|
||||
response::ModeResponse::CanNotKeepMode([Some(DeviceMode::Off), Some(DeviceMode::Off)])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
pub mod ctrl;
|
||||
|
||||
pub mod mgm;
|
||||
pub mod mgm_assembly;
|
||||
|
||||
pub mod mgt;
|
||||
pub mod subsystem;
|
||||
|
||||
@@ -1 +1,282 @@
|
||||
// TODO: Write subsystem
|
||||
#![allow(dead_code)]
|
||||
use std::{
|
||||
sync::mpsc::{self, Receiver, SyncSender},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use models::{
|
||||
ComponentId,
|
||||
acs::subsystem::{Mode, response},
|
||||
};
|
||||
use satrs::{
|
||||
mode_tree::{ModeStoreProvider, ModeStoreVec, SequenceModeTables, TargetModeTables},
|
||||
spacepackets::CcsdsPacketIdAndPsc,
|
||||
subsystem::SubsystemCommandingHelper,
|
||||
};
|
||||
use satrs_example::{ModeHelper, TmtcQueues};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TransitionInfo {
|
||||
check_mode_reached: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TransitionState {
|
||||
#[default]
|
||||
Idle,
|
||||
AwaitingReplies,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChildModes {
|
||||
mgm_assembly_mode: models::acs::mgm_assembly::Mode,
|
||||
mgt_mode: models::acs::mgt::Mode,
|
||||
controller_mode: models::acs::ctrl::Mode,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TransitionCommands {
|
||||
mgm_assembly_mode: Option<(models::acs::mgm_assembly::Mode, TransitionInfo)>,
|
||||
mgt_mode: Option<(models::acs::mgt::Mode, TransitionInfo)>,
|
||||
controller_mode: Option<(models::acs::ctrl::Mode, TransitionInfo)>,
|
||||
}
|
||||
|
||||
const OFF_SEQUENCE: [TransitionCommands; 2] = [
|
||||
TransitionCommands {
|
||||
mgm_assembly_mode: None,
|
||||
mgt_mode: None,
|
||||
controller_mode: Some((
|
||||
models::acs::ctrl::Mode::Passive,
|
||||
TransitionInfo {
|
||||
check_mode_reached: true,
|
||||
},
|
||||
)),
|
||||
},
|
||||
TransitionCommands {
|
||||
mgm_assembly_mode: Some((
|
||||
models::acs::mgm_assembly::Mode::Device(models::DeviceMode::Off),
|
||||
TransitionInfo {
|
||||
check_mode_reached: false,
|
||||
},
|
||||
)),
|
||||
mgt_mode: Some((
|
||||
models::acs::mgt::Mode::Off,
|
||||
TransitionInfo {
|
||||
check_mode_reached: false,
|
||||
},
|
||||
)),
|
||||
controller_mode: None,
|
||||
},
|
||||
];
|
||||
|
||||
const SAFE_SEQUENCE: [TransitionCommands; 2] = [
|
||||
TransitionCommands {
|
||||
mgm_assembly_mode: Some((
|
||||
models::acs::mgm_assembly::Mode::Device(models::DeviceMode::Normal),
|
||||
TransitionInfo {
|
||||
check_mode_reached: false,
|
||||
},
|
||||
)),
|
||||
mgt_mode: Some((
|
||||
models::acs::mgt::Mode::Normal,
|
||||
TransitionInfo {
|
||||
check_mode_reached: false,
|
||||
},
|
||||
)),
|
||||
controller_mode: None,
|
||||
},
|
||||
TransitionCommands {
|
||||
mgm_assembly_mode: None,
|
||||
mgt_mode: None,
|
||||
controller_mode: Some((
|
||||
models::acs::ctrl::Mode::Safe,
|
||||
TransitionInfo {
|
||||
check_mode_reached: false,
|
||||
},
|
||||
)),
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ModeRequestSenders {
|
||||
pub mode_request_ctrl: SyncSender<models::acs::ctrl::request::ModeRequest>,
|
||||
pub mode_request_assy: SyncSender<models::acs::mgm_assembly::request::ModeRequest>,
|
||||
pub mode_request_mgt: SyncSender<models::acs::mgt::request::ModeRequest>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ModeReportReceivers {
|
||||
pub mode_response_ctrl: Receiver<models::acs::ctrl::response::ModeReport>,
|
||||
pub mode_response_assy: Receiver<models::acs::mgm_assembly::response::ModeResponse>,
|
||||
pub mode_response_mgt: Receiver<models::acs::mgt::response::ModeReport>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Subsystem {
|
||||
mode_helper: ModeHelper<models::acs::subsystem::Mode, TransitionState>,
|
||||
transition_step: usize,
|
||||
current_child_modes: Option<ChildModes>,
|
||||
mode_request_senders: ModeRequestSenders,
|
||||
mode_report_receivers: ModeReportReceivers,
|
||||
tmtc_queues: TmtcQueues,
|
||||
subsystem_helper: SubsystemCommandingHelper,
|
||||
}
|
||||
|
||||
impl Subsystem {
|
||||
pub const ID: ComponentId = ComponentId::AcsSubsystem;
|
||||
|
||||
pub fn new(
|
||||
mode_request_senders: ModeRequestSenders,
|
||||
mode_report_receivers: ModeReportReceivers,
|
||||
tmtc_queues: TmtcQueues,
|
||||
) -> Self {
|
||||
let mut mode_store_vec = ModeStoreVec::default();
|
||||
mode_store_vec
|
||||
.add_component(
|
||||
ComponentId::AcsMgmAssembly as satrs::ComponentId,
|
||||
models::acs::mgm_assembly::Mode::NoModeKeeping.into(),
|
||||
)
|
||||
.unwrap();
|
||||
mode_store_vec
|
||||
.add_component(
|
||||
ComponentId::AcsController as satrs::ComponentId,
|
||||
models::acs::ctrl::Mode::Passive.into(),
|
||||
)
|
||||
.unwrap();
|
||||
mode_store_vec
|
||||
.add_component(
|
||||
ComponentId::AcsMgt as satrs::ComponentId,
|
||||
models::acs::mgt::Mode::Off.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let target_tables = TargetModeTables::default();
|
||||
|
||||
let sequence_tables = SequenceModeTables::default();
|
||||
|
||||
Self {
|
||||
mode_helper: ModeHelper::new(
|
||||
models::acs::subsystem::Mode::Off,
|
||||
Duration::from_millis(2000),
|
||||
),
|
||||
current_child_modes: None,
|
||||
mode_request_senders,
|
||||
mode_report_receivers,
|
||||
tmtc_queues,
|
||||
transition_step: 0,
|
||||
subsystem_helper: SubsystemCommandingHelper::new(
|
||||
mode_store_vec,
|
||||
target_tables,
|
||||
sequence_tables,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn periodic_operation(&mut self) {
|
||||
self.handle_telecommands();
|
||||
}
|
||||
|
||||
pub fn handle_telecommands(&mut self) {
|
||||
loop {
|
||||
match self.tmtc_queues.tc_rx.try_recv() {
|
||||
Ok(packet) => {
|
||||
let tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&packet.sp_header);
|
||||
match postcard::from_bytes::<models::acs::subsystem::request::Request>(
|
||||
&packet.payload,
|
||||
) {
|
||||
Ok(request) => match request {
|
||||
models::acs::subsystem::request::Request::Ping => {
|
||||
self.send_telemetry(Some(tc_id), response::Response::Ok)
|
||||
}
|
||||
models::acs::subsystem::request::Request::Mode(mode_request) => {
|
||||
self.handle_mode_request(mode_request);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("failed to deserialize request: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => match e {
|
||||
mpsc::TryRecvError::Empty => break,
|
||||
mpsc::TryRecvError::Disconnected => log::warn!("packet sender disconnected"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transition_sequence_for_mode(mode: Mode) -> &'static [TransitionCommands] {
|
||||
match mode {
|
||||
Mode::Off => &OFF_SEQUENCE,
|
||||
Mode::Safe => &SAFE_SEQUENCE,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_transition_step(&mut self, step: &TransitionCommands) {
|
||||
if let Some((target_mode, _info)) = &step.mgm_assembly_mode {
|
||||
self.mode_request_senders
|
||||
.mode_request_assy
|
||||
.send(models::acs::mgm_assembly::request::ModeRequest::SetMode(
|
||||
*target_mode,
|
||||
))
|
||||
.expect("failed to send mode request to MGM assembly");
|
||||
}
|
||||
if let Some((target_mode, _info)) = &step.mgt_mode {
|
||||
self.mode_request_senders
|
||||
.mode_request_mgt
|
||||
.send(models::acs::mgt::request::ModeRequest::SetMode(
|
||||
*target_mode,
|
||||
))
|
||||
.expect("failed to send mode request to MGM assembly");
|
||||
}
|
||||
if let Some((target_mode, _info)) = &step.controller_mode {
|
||||
self.mode_request_senders
|
||||
.mode_request_ctrl
|
||||
.send(models::acs::ctrl::request::ModeRequest::SetMode(
|
||||
*target_mode,
|
||||
))
|
||||
.expect("failed to send mode request to MGM assembly");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mode_request(
|
||||
&mut self,
|
||||
mode_request: models::acs::subsystem::request::ModeRequest,
|
||||
) {
|
||||
match mode_request {
|
||||
models::acs::subsystem::request::ModeRequest::SetMode(target_mode) => {
|
||||
self.mode_helper.start(target_mode);
|
||||
self.transition_step = 0;
|
||||
let first_step = Self::transition_sequence_for_mode(target_mode)
|
||||
.get(self.transition_step)
|
||||
.expect("empty transition table");
|
||||
self.execute_transition_step(first_step);
|
||||
}
|
||||
models::acs::subsystem::request::ModeRequest::ReadMode => {
|
||||
self.send_telemetry(
|
||||
None,
|
||||
response::Response::Mode(response::ModeResponse::Mode(
|
||||
self.mode_helper.current,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_telemetry(
|
||||
&self,
|
||||
tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
response: models::acs::subsystem::response::Response,
|
||||
) {
|
||||
match crate::ccsds::pack_ccsds_tm_packet_for_now(Self::ID, tc_id, &response) {
|
||||
Ok(packet) => {
|
||||
if let Err(e) = self.tmtc_queues.tm_tx.send(packet) {
|
||||
log::warn!("failed to send TM packet: {}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("failed to pack TM packet: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,7 +536,6 @@ mod tests {
|
||||
pcdu::{SwitchMapBinary, SwitchStateBinary},
|
||||
};
|
||||
use satrs::{
|
||||
mode::{ModeReply, ModeRequest},
|
||||
request::{GenericMessage, MessageMetadata},
|
||||
spacepackets::SpacePacketHeader,
|
||||
};
|
||||
@@ -590,8 +589,8 @@ mod tests {
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct PcduTestbench {
|
||||
pub mode_request_tx: mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
pub mode_reply_rx_to_parent: mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
pub mode_request_tx: mpsc::SyncSender<models::pcdu::request::Request>,
|
||||
pub mode_reply_rx_to_parent: mpsc::Receiver<models::pcdu::response::Response>,
|
||||
pub tc_tx: mpsc::SyncSender<CcsdsTcPacketOwned>,
|
||||
pub tm_rx: mpsc::Receiver<CcsdsTmPacketOwned>,
|
||||
pub switch_request_tx: mpsc::Sender<GenericMessage<SwitchRequest>>,
|
||||
|
||||
@@ -95,6 +95,7 @@ impl HkHelperSingleSet {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TmtcQueues {
|
||||
pub tc_rx: mpsc::Receiver<CcsdsTcPacketOwned>,
|
||||
pub tm_tx: mpsc::SyncSender<CcsdsTmPacketOwned>,
|
||||
|
||||
+30
-13
@@ -23,16 +23,13 @@ use logger::setup_logger;
|
||||
use models::{ComponentId, DeviceMode};
|
||||
use satrs::{
|
||||
hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer},
|
||||
mode::{Mode, ModeAndSubmode, ModeRequest},
|
||||
pus::HandlingStatus,
|
||||
request::{GenericMessage, MessageMetadata},
|
||||
spacepackets::time::cds::CdsTime,
|
||||
};
|
||||
use satrs_example::{
|
||||
TmtcQueues,
|
||||
config::{
|
||||
OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT,
|
||||
components::NO_SENDER,
|
||||
tasks::{FREQ_MS_AOCS, FREQ_MS_CONTROLLER, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS},
|
||||
},
|
||||
};
|
||||
@@ -40,7 +37,7 @@ use tmtc::sender::TmTcSender;
|
||||
use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink};
|
||||
|
||||
use crate::{
|
||||
acs::{mgm, mgm_assembly},
|
||||
acs::{mgm, mgm_assembly, subsystem},
|
||||
control::Controller,
|
||||
eps::pcdu::SwitchSet,
|
||||
event_manager::EventManager,
|
||||
@@ -80,12 +77,19 @@ fn main() {
|
||||
let (mgm_0_handler_tc_tx, mgm_0_handler_tc_rx) = mpsc::sync_channel(10);
|
||||
let (mgm_1_handler_tc_tx, mgm_1_handler_tc_rx) = mpsc::sync_channel(10);
|
||||
let (mgm_assembly_tc_tx, mgm_assembly_tc_rx) = mpsc::sync_channel(10);
|
||||
let (acs_subsystem_tc_tx, acs_subsystem_tc_rx) = mpsc::sync_channel(10);
|
||||
let (pcdu_handler_tc_tx, pcdu_handler_tc_rx) = mpsc::sync_channel(30);
|
||||
let (controller_tc_tx, controller_tc_rx) = mpsc::sync_channel(10);
|
||||
|
||||
let (mgt_request_tx, _mgt_request_rx) = mpsc::sync_channel(5);
|
||||
let (_mgt_report_tx, mgt_report_rx) = mpsc::sync_channel(5);
|
||||
|
||||
let (acs_ctrl_request_tx, _acs_ctrl_request_rx) = mpsc::sync_channel(5);
|
||||
let (_acs_ctrl_response_tx, acs_ctrl_response_rx) = mpsc::sync_channel(5);
|
||||
|
||||
// These message handles need to go into the MGM assembly and ACS subsystem.
|
||||
let (_mgm_assembly_request_tx, mgm_assembly_request_rx) = mpsc::sync_channel(5);
|
||||
let (mgm_assembly_report_tx, _mgm_assembly_report_rx) = mpsc::sync_channel(5);
|
||||
let (mgm_assembly_request_tx, mgm_assembly_request_rx) = mpsc::sync_channel(5);
|
||||
let (mgm_assembly_report_tx, mgm_assembly_report_rx) = mpsc::sync_channel(5);
|
||||
|
||||
// These message handles need to go into the MGM assembly and MGM devices.
|
||||
let (mgm_0_mode_request_tx, mgm_0_mode_request_rx) = mpsc::sync_channel(5);
|
||||
@@ -110,6 +114,7 @@ fn main() {
|
||||
tc_source.add_target(ComponentId::AcsMgm0, mgm_0_handler_tc_tx);
|
||||
tc_source.add_target(ComponentId::AcsMgm1, mgm_1_handler_tc_tx);
|
||||
tc_source.add_target(ComponentId::AcsMgmAssembly, mgm_assembly_tc_tx);
|
||||
tc_source.add_target(ComponentId::AcsSubsystem, acs_subsystem_tc_tx);
|
||||
|
||||
let tc_sender = TmTcSender::Normal(tc_source_tx.clone());
|
||||
let udp_tm_handler = UdpTmHandlerWithChannel {
|
||||
@@ -221,6 +226,23 @@ fn main() {
|
||||
Duration::from_millis(2000),
|
||||
);
|
||||
|
||||
let mut acs_subsystem = subsystem::Subsystem::new(
|
||||
subsystem::ModeRequestSenders {
|
||||
mode_request_ctrl: acs_ctrl_request_tx,
|
||||
mode_request_assy: mgm_assembly_request_tx,
|
||||
mode_request_mgt: mgt_request_tx,
|
||||
},
|
||||
subsystem::ModeReportReceivers {
|
||||
mode_response_ctrl: acs_ctrl_response_rx,
|
||||
mode_response_assy: mgm_assembly_report_rx,
|
||||
mode_response_mgt: mgt_report_rx,
|
||||
},
|
||||
TmtcQueues {
|
||||
tc_rx: acs_subsystem_tc_rx,
|
||||
tm_tx: tm_sink_tx.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
|
||||
sim_client.add_reply_recipient(satrs_minisim::SimComponent::Pcdu, pcdu_sim_reply_tx);
|
||||
SerialSimInterfaceWrapper::Sim(SerialInterfaceToSim::new(
|
||||
@@ -241,13 +263,7 @@ fn main() {
|
||||
|
||||
// The PCDU is a critical component which should be in normal mode immediately.
|
||||
pcdu_handler_mode_tx
|
||||
.send(GenericMessage::new(
|
||||
MessageMetadata::new(0, NO_SENDER),
|
||||
ModeRequest::SetMode {
|
||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as Mode, 0),
|
||||
forced: false,
|
||||
},
|
||||
))
|
||||
.send(models::pcdu::request::Request::Mode(DeviceMode::Normal))
|
||||
.expect("sending initial mode request failed");
|
||||
|
||||
info!("Starting TMTC and UDP task");
|
||||
@@ -324,6 +340,7 @@ fn main() {
|
||||
mgm_0_handler.periodic_operation();
|
||||
mgm_1_handler.periodic_operation();
|
||||
mgm_assembly.periodic_operation();
|
||||
acs_subsystem.periodic_operation();
|
||||
thread::sleep(Duration::from_millis(FREQ_MS_AOCS));
|
||||
}
|
||||
})
|
||||
|
||||
@@ -11,8 +11,7 @@ license = "Apache-2.0"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# spacepackets = { version = "0.17", default-features = false }
|
||||
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
|
||||
spacepackets = { version = "0.18", default-features = false }
|
||||
serde = { version = "1", default-features = false, optional = true }
|
||||
defmt = {version = "1", optional = true }
|
||||
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
|
||||
|
||||
[dependencies]
|
||||
satrs-shared = { version = "0.2", path = "../satrs-shared" }
|
||||
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
|
||||
spacepackets = { version = "0.18", default-features = false }
|
||||
|
||||
delegate = "0.13"
|
||||
paste = "1"
|
||||
@@ -23,7 +23,7 @@ num_enum = { version = "0.7", default-features = false }
|
||||
cobs = { version = "0.5", default-features = false }
|
||||
thiserror = { version = "2", default-features = false }
|
||||
|
||||
hashbrown = { version = "0.16", optional = true }
|
||||
hashbrown = { version = "0.17", optional = true }
|
||||
static_cell = { version = "2" }
|
||||
heapless = { version = "0.9", optional = true }
|
||||
dyn-clone = { version = "1", optional = true }
|
||||
@@ -42,7 +42,7 @@ serde = "1"
|
||||
zerocopy = "0.8"
|
||||
once_cell = "1"
|
||||
serde_json = "1"
|
||||
rand = "0.9"
|
||||
rand = "0.10"
|
||||
tempfile = "3"
|
||||
|
||||
[dev-dependencies.postcard]
|
||||
|
||||
@@ -0,0 +1,763 @@
|
||||
use core::mem::size_of;
|
||||
use satrs_shared::res_code::ResultU16;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spacepackets::ByteConversionError;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std_mod::*;
|
||||
|
||||
use crate::{
|
||||
ComponentId,
|
||||
queue::{GenericReceiveError, GenericSendError},
|
||||
request::{
|
||||
GenericMessage, MessageMetadata, MessageReceiverProvider, MessageReceiverWithId, RequestId,
|
||||
},
|
||||
};
|
||||
|
||||
pub type ModeRaw = u32;
|
||||
|
||||
pub type Mode = u32;
|
||||
pub type Submode = u16;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ModeAndSubmode {
|
||||
mode: Mode,
|
||||
submode: Submode,
|
||||
}
|
||||
|
||||
pub const INVALID_MODE_VAL: Mode = Mode::MAX;
|
||||
pub const UNKNOWN_MODE_VAL: Mode = Mode::MAX - 1;
|
||||
pub const INVALID_MODE: ModeAndSubmode = ModeAndSubmode::new(INVALID_MODE_VAL, 0);
|
||||
pub const UNKNOWN_MODE: ModeAndSubmode = ModeAndSubmode::new(UNKNOWN_MODE_VAL, 0);
|
||||
|
||||
impl ModeAndSubmode {
|
||||
pub const RAW_LEN: usize = size_of::<Mode>() + size_of::<Submode>();
|
||||
|
||||
pub const fn new_mode_only(mode: Mode) -> Self {
|
||||
Self { mode, submode: 0 }
|
||||
}
|
||||
|
||||
pub const fn new(mode: Mode, submode: Submode) -> Self {
|
||||
Self { mode, submode }
|
||||
}
|
||||
|
||||
pub fn from_be_bytes(buf: &[u8]) -> Result<Self, ByteConversionError> {
|
||||
if buf.len() < 6 {
|
||||
return Err(ByteConversionError::FromSliceTooSmall {
|
||||
expected: Self::RAW_LEN,
|
||||
found: buf.len(),
|
||||
});
|
||||
}
|
||||
Ok(Self {
|
||||
mode: Mode::from_be_bytes(buf[0..size_of::<Mode>()].try_into().unwrap()),
|
||||
submode: Submode::from_be_bytes(
|
||||
buf[size_of::<Mode>()..size_of::<Mode>() + size_of::<Submode>()]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
if buf.len() < Self::RAW_LEN {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
expected: Self::RAW_LEN,
|
||||
found: buf.len(),
|
||||
});
|
||||
}
|
||||
buf[0..size_of::<Mode>()].copy_from_slice(&self.mode.to_be_bytes());
|
||||
buf[size_of::<Mode>()..Self::RAW_LEN].copy_from_slice(&self.submode.to_be_bytes());
|
||||
Ok(Self::RAW_LEN)
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> Mode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
pub fn submode(&self) -> Submode {
|
||||
self.submode
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct TargetedModeCommand {
|
||||
pub address: ComponentId,
|
||||
pub mode_submode: ModeAndSubmode,
|
||||
}
|
||||
|
||||
impl TargetedModeCommand {
|
||||
pub const fn new(address: ComponentId, mode_submode: ModeAndSubmode) -> Self {
|
||||
Self {
|
||||
address,
|
||||
mode_submode,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> ComponentId {
|
||||
self.address
|
||||
}
|
||||
|
||||
pub fn mode_submode(&self) -> ModeAndSubmode {
|
||||
self.mode_submode
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> u32 {
|
||||
self.mode_submode.mode
|
||||
}
|
||||
|
||||
pub fn submode(&self) -> u16 {
|
||||
self.mode_submode.submode
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ModeRequest {
|
||||
/// Mode information. Can be used to notify other components of changed modes.
|
||||
ModeInfo(ModeAndSubmode),
|
||||
SetMode {
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
forced: bool,
|
||||
},
|
||||
ReadMode,
|
||||
AnnounceMode,
|
||||
AnnounceModeRecursive,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct TargetedModeRequest {
|
||||
target_id: ComponentId,
|
||||
mode_request: ModeRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ModeReply {
|
||||
/// Mode information. Can be used to notify other components of changed modes.
|
||||
ModeInfo(ModeAndSubmode),
|
||||
/// Reply to a mode request to confirm the commanded mode was reached.
|
||||
ModeReply(ModeAndSubmode),
|
||||
// Can not reach the commanded mode. Contains a reason as a [ResultU16].
|
||||
CantReachMode(ResultU16),
|
||||
/// We are in the wrong mode for unknown reasons. Contains the expected and reached mode.
|
||||
WrongMode {
|
||||
expected: ModeAndSubmode,
|
||||
reached: ModeAndSubmode,
|
||||
},
|
||||
}
|
||||
|
||||
pub type GenericModeReply = GenericMessage<ModeReply>;
|
||||
|
||||
pub trait ModeRequestSender {
|
||||
fn local_channel_id(&self) -> ComponentId;
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError>;
|
||||
}
|
||||
|
||||
pub trait ModeRequestReceiver {
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError>;
|
||||
}
|
||||
|
||||
impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver
|
||||
for MessageReceiverWithId<ModeRequest, R>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
pub enum ModeError {
|
||||
#[error("Messaging send error: {0}")]
|
||||
Send(#[from] GenericSendError),
|
||||
#[error("Messaging receive error: {0}")]
|
||||
Receive(#[from] GenericReceiveError),
|
||||
#[error("busy with other mode request")]
|
||||
Busy,
|
||||
}
|
||||
|
||||
pub trait ModeProvider {
|
||||
fn mode_and_submode(&self) -> ModeAndSubmode;
|
||||
|
||||
fn mode(&self) -> Mode {
|
||||
self.mode_and_submode().mode()
|
||||
}
|
||||
|
||||
fn submode(&self) -> Submode {
|
||||
self.mode_and_submode().submode()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeRequestHandler: ModeProvider {
|
||||
type Error;
|
||||
|
||||
fn start_transition(
|
||||
&mut self,
|
||||
requestor: MessageMetadata,
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
forced: bool,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool);
|
||||
|
||||
fn handle_mode_reached(
|
||||
&mut self,
|
||||
requestor_info: Option<MessageMetadata>,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn handle_mode_info(
|
||||
&mut self,
|
||||
requestor_info: MessageMetadata,
|
||||
info: ModeAndSubmode,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn handle_mode_request(
|
||||
&mut self,
|
||||
request: GenericMessage<ModeRequest>,
|
||||
) -> Result<(), Self::Error> {
|
||||
match request.message {
|
||||
ModeRequest::SetMode {
|
||||
mode_and_submode,
|
||||
forced,
|
||||
} => self.start_transition(request.requestor_info, mode_and_submode, forced),
|
||||
ModeRequest::ReadMode => self.send_mode_reply(
|
||||
request.requestor_info,
|
||||
ModeReply::ModeReply(self.mode_and_submode()),
|
||||
),
|
||||
ModeRequest::AnnounceMode => {
|
||||
self.announce_mode(Some(request.requestor_info), false);
|
||||
Ok(())
|
||||
}
|
||||
ModeRequest::AnnounceModeRecursive => {
|
||||
self.announce_mode(Some(request.requestor_info), true);
|
||||
Ok(())
|
||||
}
|
||||
ModeRequest::ModeInfo(info) => self.handle_mode_info(request.requestor_info, info),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeReplyReceiver {
|
||||
fn try_recv_mode_reply(&self)
|
||||
-> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError>;
|
||||
}
|
||||
|
||||
impl<R: MessageReceiverProvider<ModeReply>> ModeReplyReceiver
|
||||
for MessageReceiverWithId<ModeReply, R>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeReplySender {
|
||||
fn local_channel_id(&self) -> ComponentId;
|
||||
|
||||
/// The requestor is assumed to be the target of the reply.
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), GenericSendError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use crate::{
|
||||
queue::{GenericReceiveError, GenericSendError},
|
||||
request::{
|
||||
MessageSenderAndReceiver, MessageSenderMap, MessageSenderProvider,
|
||||
MessageSenderStoreProvider, RequestAndReplySenderAndReceiver,
|
||||
},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<S: MessageSenderProvider<ModeReply>> MessageSenderMap<ModeReply, S> {
|
||||
pub fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
target_id: ComponentId,
|
||||
request: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(requestor_info, target_id, request)
|
||||
}
|
||||
|
||||
pub fn add_reply_target(&mut self, target_id: ComponentId, request_sender: S) {
|
||||
self.add_message_target(target_id, request_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
From,
|
||||
Sender: MessageSenderProvider<ModeReply>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||
> ModeReplySender for MessageSenderAndReceiver<ModeReply, From, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
request: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.message_sender_store.send_message(
|
||||
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
|
||||
requestor_info.sender_id(),
|
||||
request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
To,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<ModeReply>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> ModeReplyReceiver for MessageSenderAndReceiver<To, ModeReply, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.message_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
>
|
||||
RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: ReplySender) {
|
||||
self.reply_sender_store
|
||||
.add_message_target(target_id, reply_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
ReplySender: MessageSenderProvider<ModeReply>,
|
||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||
> ModeReplySender
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.reply_sender_store.send_message(
|
||||
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
|
||||
requestor_info.sender_id(),
|
||||
reply,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
ReplySender: MessageSenderProvider<ModeReply>,
|
||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||
> ModeReplyReceiver
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.reply_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type definition for a mode handler which can handle mode requests.
|
||||
pub type ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore> =
|
||||
MessageSenderAndReceiver<ModeReply, ModeRequest, Sender, Receiver, ReplySenderStore>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ModeReply>,
|
||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||
> ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore>
|
||||
{
|
||||
pub fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
|
||||
pub fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(
|
||||
requestor_info.request_id(),
|
||||
requestor_info.sender_id(),
|
||||
reply,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type defintion for a mode handler object which can send mode requests and receive
|
||||
/// mode replies.
|
||||
pub type ModeRequestorInterface<Sender, Receiver, RequestSenderStore> =
|
||||
MessageSenderAndReceiver<ModeRequest, ModeReply, Sender, Receiver, RequestSenderStore>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ModeRequest>,
|
||||
Receiver: MessageReceiverProvider<ModeReply>,
|
||||
RequestSenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||
> ModeRequestorInterface<Sender, Receiver, RequestSenderStore>
|
||||
{
|
||||
pub fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
|
||||
pub fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
reply: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(request_id, target_id, reply)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type defintion for a mode handler object which can both send mode requests and
|
||||
/// process mode requests.
|
||||
pub type ModeInterface<
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
> = RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>;
|
||||
|
||||
impl<S: MessageSenderProvider<ModeRequest>> MessageSenderMap<ModeRequest, S> {
|
||||
pub fn send_mode_request(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(requestor_info, target_id, request)
|
||||
}
|
||||
|
||||
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S) {
|
||||
self.add_message_target(target_id, request_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
To,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> ModeRequestReceiver
|
||||
for MessageSenderAndReceiver<To, ModeRequest, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.message_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
From,
|
||||
Sender: MessageSenderProvider<ModeRequest>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||
> ModeRequestSender
|
||||
for MessageSenderAndReceiver<ModeRequest, From, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.message_sender_store.send_message(
|
||||
MessageMetadata::new(request_id, self.local_channel_id()),
|
||||
target_id,
|
||||
request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
>
|
||||
RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: ReqSender) {
|
||||
self.request_sender_store
|
||||
.add_message_target(target_id, request_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
> ModeRequestSender
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.request_sender_store.send_message(
|
||||
MessageMetadata::new(request_id, self.local_channel_id()),
|
||||
target_id,
|
||||
request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
> ModeRequestReceiver
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.request_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod std_mod {
|
||||
use std::sync::mpsc;
|
||||
|
||||
use crate::request::{MessageSenderList, OneMessageSender};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface<
|
||||
mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
|
||||
pub type ModeRequestorOneChildMpsc = ModeRequestorInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
OneMessageSender<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorOneChildBoundedMpsc = ModeRequestorInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
OneMessageSender<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorChildListMpsc = ModeRequestorInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorChildListBoundedMpsc = ModeRequestorInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
|
||||
pub type ModeRequestorAndHandlerMpsc = ModeInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
pub type ModeRequestorAndHandlerMpscBounded = ModeInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use core::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::{ComponentId, request::RequestId};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct ModeReqWrapper {
|
||||
pub request_id: RequestId,
|
||||
pub target_id: ComponentId,
|
||||
pub request: ModeRequest,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModeReqSenderMock {
|
||||
pub requests: RefCell<VecDeque<ModeReqWrapper>>,
|
||||
}
|
||||
|
||||
impl ModeRequestSender for ModeReqSenderMock {
|
||||
fn local_channel_id(&self) -> crate::ComponentId {
|
||||
0
|
||||
}
|
||||
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.requests.borrow_mut().push_back(ModeReqWrapper {
|
||||
request_id,
|
||||
target_id,
|
||||
request,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,614 @@
|
||||
use alloc::vec::Vec;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::{
|
||||
ComponentId, mode::{Mode, ModeAndSubmode, ModeRaw, ModeReply, ModeRequest, Submode}, request::MessageSenderProvider,
|
||||
};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
|
||||
/// Common trait for node modes which can have mode parents or mode children.
|
||||
pub trait ModeNode {
|
||||
fn id(&self) -> ComponentId;
|
||||
}
|
||||
/// Trait which denotes that an object is a parent in a mode tree.
|
||||
///
|
||||
/// A mode parent is capable of sending mode requests to child objects and has a unique component
|
||||
/// ID.
|
||||
pub trait ModeParent: ModeNode {
|
||||
type Sender: MessageSenderProvider<ModeRequest>;
|
||||
|
||||
fn add_mode_child(&mut self, id: ComponentId, request_sender: Self::Sender);
|
||||
}
|
||||
|
||||
/// Trait which denotes that an object is a child in a mode tree.
|
||||
///
|
||||
/// A child is capable of sending mode replies to parent objects and has a unique component ID.
|
||||
pub trait ModeChild: ModeNode {
|
||||
type Sender: MessageSenderProvider<ModeReply>;
|
||||
|
||||
fn add_mode_parent(&mut self, id: ComponentId, reply_sender: Self::Sender);
|
||||
}
|
||||
|
||||
/// Utility method which connects a mode tree parent object to a child object by calling
|
||||
/// [ModeParent::add_mode_child] on the [parent][ModeParent] and calling
|
||||
/// [ModeChild::add_mode_parent] on the [child][ModeChild].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `parent` - The parent object which implements [ModeParent].
|
||||
/// * `request_sender` - Sender object to send mode requests to the child.
|
||||
/// * `child` - The child object which implements [ModeChild].
|
||||
/// * `reply_sender` - Sender object to send mode replies to the parent.
|
||||
pub fn connect_mode_nodes<ReqSender, ReplySender>(
|
||||
parent: &mut impl ModeParent<Sender = ReqSender>,
|
||||
request_sender: ReqSender,
|
||||
child: &mut impl ModeChild<Sender = ReplySender>,
|
||||
reply_sender: ReplySender,
|
||||
) {
|
||||
parent.add_mode_child(child.id(), request_sender);
|
||||
child.add_mode_parent(parent.id(), reply_sender);
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum TableEntryType {
|
||||
/// Target table containing information of the expected children modes for given mode.
|
||||
Target,
|
||||
/// Sequence table which contains information about how to reach a target table, including
|
||||
/// the order of the sequences.
|
||||
Sequence,
|
||||
}
|
||||
|
||||
/// Common fields required for both target and sequence table entries.
|
||||
///
|
||||
/// The most important parameters here are the target ID which this entry belongs to, and the mode
|
||||
/// and submode the entry either will be commanded to for sequence table entries or which will be
|
||||
/// monitored for target table entries.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ModeTableEntryCommon {
|
||||
/// Name of respective table entry.
|
||||
pub name: &'static str,
|
||||
/// Target component ID.
|
||||
pub target_id: ComponentId,
|
||||
/// Has a different meaning depending on whether this is a sequence table or a target table.
|
||||
///
|
||||
/// - For sequence tables, this denotes the mode which will be commanded
|
||||
/// - For target tables, this is the mode which the target children should have and which
|
||||
/// might be monitored depending on configuration.
|
||||
pub mode: ModeRaw,
|
||||
/// This mask allows to specify multiple allowed submodes for a given mode.
|
||||
pub allowed_submode_mask: Option<Submode>,
|
||||
}
|
||||
|
||||
impl ModeTableEntryCommon {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode) {
|
||||
self.allowed_submode_mask = Some(mask);
|
||||
}
|
||||
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode> {
|
||||
self.allowed_submode_mask
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry for the target tables.
|
||||
#[derive(Debug)]
|
||||
pub struct TargetTableEntry {
|
||||
pub common: ModeTableEntryCommon,
|
||||
pub monitor_state: bool,
|
||||
}
|
||||
|
||||
impl TargetTableEntry {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
allowed_submode_mask: Option<Submode>,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask,
|
||||
},
|
||||
monitor_state: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_precise_submode(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask: None,
|
||||
},
|
||||
monitor_state: true,
|
||||
}
|
||||
}
|
||||
|
||||
delegate::delegate! {
|
||||
to self.common {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry for the sequence tables.
|
||||
///
|
||||
/// The [Self::check_success] field specifies that a mode sequence executor should check that the
|
||||
/// target mode was actually reached before executing the next sequence.
|
||||
#[derive(Debug)]
|
||||
pub struct SequenceTableEntry {
|
||||
pub common: ModeTableEntryCommon,
|
||||
pub check_success: bool,
|
||||
}
|
||||
|
||||
impl SequenceTableEntry {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
check_success: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask: None,
|
||||
},
|
||||
check_success,
|
||||
}
|
||||
}
|
||||
|
||||
delegate::delegate! {
|
||||
to self.common {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("target {0} not in mode store")]
|
||||
pub struct TargetNotInModeStoreError(pub ComponentId);
|
||||
|
||||
/// Mode store value type.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ModeStoreValue {
|
||||
/// ID of the mode component.
|
||||
id: ComponentId,
|
||||
/// Current mode and submode of the component.
|
||||
pub mode_and_submode: ModeAndSubmode,
|
||||
/// State information to track whether a reply should be awaited for the mode component.
|
||||
pub awaiting_reply: bool,
|
||||
}
|
||||
|
||||
impl ModeStoreValue {
|
||||
pub fn new(id: ComponentId, mode_and_submode: ModeAndSubmode) -> Self {
|
||||
Self {
|
||||
id,
|
||||
mode_and_submode,
|
||||
awaiting_reply: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ComponentId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||
self.mode_and_submode
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeStoreProvider {
|
||||
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode);
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool;
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue>;
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue>;
|
||||
|
||||
/// Generic handler for mode replies received from child components.
|
||||
///
|
||||
/// Implementation should clear the awaition flag if the `handle_reply_awaition` argument is
|
||||
/// true and returns whether any children are still awaiting replies. If the flag is not set
|
||||
fn mode_reply_handler_with_reply_awaition(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
) -> bool {
|
||||
self.mode_reply_handler(sender_id, reported_mode_and_submode, true)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn mode_reply_handler_without_reply_awaition(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
) {
|
||||
self.mode_reply_handler(sender_id, reported_mode_and_submode, false);
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
with_reply_awaition: bool,
|
||||
) -> Option<bool>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use crate::subsystem::ModeRaw;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TargetTablesMapValue {
|
||||
/// Name for a given mode table entry.
|
||||
pub name: &'static str,
|
||||
/// Optional fallback mode if the target mode can not be kept.
|
||||
pub fallback_mode: Option<Mode>,
|
||||
/// These are the rows of the a target table.
|
||||
pub entries: Vec<TargetTableEntry>,
|
||||
}
|
||||
|
||||
impl TargetTablesMapValue {
|
||||
pub fn new(name: &'static str, fallback_mode: Option<Mode>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
fallback_mode,
|
||||
entries: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entry(&mut self, entry: TargetTableEntry) {
|
||||
self.entries.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// One sequence of a [SequenceTablesMapValue] in a [SequenceModeTables].
|
||||
///
|
||||
/// It contains all mode requests which need to be executed for a sequence step and it also
|
||||
/// associates a [Self::name] with the sequence.
|
||||
#[derive(Debug)]
|
||||
pub struct SequenceTableMapTable {
|
||||
/// Name for a given mode sequence.
|
||||
pub name: &'static str,
|
||||
/// These are the rows of the a sequence table.
|
||||
pub entries: Vec<SequenceTableEntry>,
|
||||
}
|
||||
|
||||
impl SequenceTableMapTable {
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
entries: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entry(&mut self, entry: SequenceTableEntry) {
|
||||
self.entries.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// A sequence table entry.
|
||||
///
|
||||
/// This is simply a list of [SequenceTableMapTable]s which also associates a [Self::name]
|
||||
/// with the sequence. The order of sub-tables in the list also specifies the execution order
|
||||
/// in the mode sequence.
|
||||
#[derive(Debug)]
|
||||
pub struct SequenceTablesMapValue {
|
||||
/// Name for a given mode sequence.
|
||||
pub name: &'static str,
|
||||
/// Each sequence can consists of multiple sequences that are executed consecutively.
|
||||
pub entries: Vec<SequenceTableMapTable>,
|
||||
}
|
||||
|
||||
impl SequenceTablesMapValue {
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
entries: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_sequence_table(&mut self, entry: SequenceTableMapTable) {
|
||||
self.entries.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TargetModeTables(pub HashMap<Mode, TargetTablesMapValue>);
|
||||
|
||||
impl TargetModeTables {
|
||||
pub fn name(&self, mode: Mode) -> Option<&'static str> {
|
||||
self.0.get(&mode).map(|value| value.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl SequenceModeTables {
|
||||
pub fn name(&self, mode: Mode) -> Option<&'static str> {
|
||||
self.0.get(&mode).map(|value| value.name)
|
||||
}
|
||||
|
||||
pub fn name_of_sequence(&self, mode: Mode, seq_idx: usize) -> Option<&'static str> {
|
||||
self.0
|
||||
.get(&mode)
|
||||
.map(|value| value.entries.get(seq_idx).map(|v| v.name))?
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the core data structure used to store mode sequence tables.
|
||||
///
|
||||
/// A mode sequence table specifies which commands have to be sent in which order
|
||||
/// to reach a certain [Mode]. Therefore, it simply maps a [Mode] to a [SequenceTablesMapValue].
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SequenceModeTables(pub HashMap<ModeRaw, SequenceTablesMapValue>);
|
||||
|
||||
/// Mode store which tracks the [mode information][ModeStoreValue] inside a [Vec]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ModeStoreVec(pub alloc::vec::Vec<ModeStoreValue>);
|
||||
|
||||
impl<'a> IntoIterator for &'a ModeStoreVec {
|
||||
type Item = &'a ModeStoreValue;
|
||||
type IntoIter = std::slice::Iter<'a, ModeStoreValue>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut ModeStoreVec {
|
||||
type Item = &'a mut ModeStoreValue;
|
||||
type IntoIter = std::slice::IterMut<'a, ModeStoreValue>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Mode store which tracks the mode information inside a [hashbrown::HashMap]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeStoreValue>);
|
||||
|
||||
impl<'a> IntoIterator for &'a ModeStoreMap {
|
||||
type Item = (&'a ComponentId, &'a ModeStoreValue);
|
||||
type IntoIter = hashbrown::hash_map::Iter<'a, ComponentId, ModeStoreValue>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl ModeStoreProvider for ModeStoreVec {
|
||||
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
|
||||
self.0.push(ModeStoreValue::new(target_id, mode));
|
||||
}
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool {
|
||||
self.0.iter().any(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
|
||||
self.0.iter().find(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
|
||||
self.0.iter_mut().find(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
handle_reply_awaition: bool,
|
||||
) -> Option<bool> {
|
||||
let mut still_awating_replies = None;
|
||||
if handle_reply_awaition {
|
||||
still_awating_replies = Some(false);
|
||||
}
|
||||
self.0.iter_mut().for_each(|val| {
|
||||
if val.id() == sender_id {
|
||||
if let Some(mode_and_submode) = reported_mode_and_submode {
|
||||
val.mode_and_submode = mode_and_submode;
|
||||
}
|
||||
if handle_reply_awaition {
|
||||
val.awaiting_reply = false;
|
||||
}
|
||||
}
|
||||
if handle_reply_awaition && val.awaiting_reply {
|
||||
still_awating_replies = Some(true);
|
||||
}
|
||||
});
|
||||
still_awating_replies
|
||||
}
|
||||
}
|
||||
|
||||
impl ModeStoreProvider for ModeStoreMap {
|
||||
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
|
||||
self.0
|
||||
.insert(target_id, ModeStoreValue::new(target_id, mode));
|
||||
}
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool {
|
||||
self.0.contains_key(&target_id)
|
||||
}
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
|
||||
self.0.get(&target_id)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
|
||||
self.0.get_mut(&target_id)
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
handle_reply_awaition: bool,
|
||||
) -> Option<bool> {
|
||||
let mut still_awating_replies = None;
|
||||
if handle_reply_awaition {
|
||||
still_awating_replies = Some(false);
|
||||
}
|
||||
for val in self.0.values_mut() {
|
||||
if val.id() == sender_id {
|
||||
if let Some(mode_and_submode) = reported_mode_and_submode {
|
||||
val.mode_and_submode = mode_and_submode;
|
||||
}
|
||||
if handle_reply_awaition {
|
||||
val.awaiting_reply = false;
|
||||
}
|
||||
}
|
||||
if handle_reply_awaition && val.awaiting_reply {
|
||||
still_awating_replies = Some(true);
|
||||
}
|
||||
}
|
||||
still_awating_replies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn generic_test(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
assert!(mode_store.has_component(1));
|
||||
assert!(mode_store.has_component(2));
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(0, 0)
|
||||
);
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(mode_store.get(1).unwrap().id, 1);
|
||||
assert_eq!(mode_store.get(2).unwrap().id, 2);
|
||||
assert!(mode_store.get(3).is_none());
|
||||
assert!(mode_store.get_mut(3).is_none());
|
||||
}
|
||||
|
||||
fn generic_reply_handling_with_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
let mut reply_awation_pending =
|
||||
mode_store.mode_reply_handler_with_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
|
||||
assert!(reply_awation_pending);
|
||||
reply_awation_pending = mode_store.mode_reply_handler_with_reply_awaition(2, None);
|
||||
assert!(!reply_awation_pending);
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
fn generic_reply_handling_test_no_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
mode_store.mode_reply_handler_without_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
|
||||
mode_store.mode_reply_handler_without_reply_awaition(2, None);
|
||||
assert!(mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
fn generic_reply_handling_with_reply_awaition_2(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
let mut reply_awation_pending =
|
||||
mode_store.mode_reply_handler(1, Some(ModeAndSubmode::new(2, 0)), true);
|
||||
assert!(reply_awation_pending.unwrap());
|
||||
reply_awation_pending = mode_store.mode_reply_handler(2, None, true);
|
||||
assert!(!reply_awation_pending.unwrap());
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_mode_store() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_test(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_mode_store() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_test(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_vec_with_reply_awaition() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_reply_handling_with_reply_awaition(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_vec_with_reply_awaition_2() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_map_with_reply_awaition() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_reply_handling_with_reply_awaition(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_map_with_reply_awaition_2() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_vec_no_reply_awaition() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
|
||||
}
|
||||
#[test]
|
||||
fn test_generic_reply_handler_map_no_reply_awaition() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+2
-759
@@ -1,761 +1,4 @@
|
||||
use core::mem::size_of;
|
||||
use satrs_shared::res_code::ResultU16;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spacepackets::ByteConversionError;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std_mod::*;
|
||||
|
||||
use crate::{
|
||||
ComponentId,
|
||||
queue::{GenericReceiveError, GenericSendError},
|
||||
request::{
|
||||
GenericMessage, MessageMetadata, MessageReceiverProvider, MessageReceiverWithId, RequestId,
|
||||
},
|
||||
};
|
||||
|
||||
pub type Mode = u32;
|
||||
pub type Submode = u16;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ModeAndSubmode {
|
||||
mode: Mode,
|
||||
submode: Submode,
|
||||
}
|
||||
|
||||
pub const INVALID_MODE_VAL: Mode = Mode::MAX;
|
||||
pub const UNKNOWN_MODE_VAL: Mode = Mode::MAX - 1;
|
||||
pub const INVALID_MODE: ModeAndSubmode = ModeAndSubmode::new(INVALID_MODE_VAL, 0);
|
||||
pub const UNKNOWN_MODE: ModeAndSubmode = ModeAndSubmode::new(UNKNOWN_MODE_VAL, 0);
|
||||
|
||||
impl ModeAndSubmode {
|
||||
pub const RAW_LEN: usize = size_of::<Mode>() + size_of::<Submode>();
|
||||
|
||||
pub const fn new_mode_only(mode: Mode) -> Self {
|
||||
Self { mode, submode: 0 }
|
||||
}
|
||||
|
||||
pub const fn new(mode: Mode, submode: Submode) -> Self {
|
||||
Self { mode, submode }
|
||||
}
|
||||
|
||||
pub fn from_be_bytes(buf: &[u8]) -> Result<Self, ByteConversionError> {
|
||||
if buf.len() < 6 {
|
||||
return Err(ByteConversionError::FromSliceTooSmall {
|
||||
expected: Self::RAW_LEN,
|
||||
found: buf.len(),
|
||||
});
|
||||
}
|
||||
Ok(Self {
|
||||
mode: Mode::from_be_bytes(buf[0..size_of::<Mode>()].try_into().unwrap()),
|
||||
submode: Submode::from_be_bytes(
|
||||
buf[size_of::<Mode>()..size_of::<Mode>() + size_of::<Submode>()]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
if buf.len() < Self::RAW_LEN {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
expected: Self::RAW_LEN,
|
||||
found: buf.len(),
|
||||
});
|
||||
}
|
||||
buf[0..size_of::<Mode>()].copy_from_slice(&self.mode.to_be_bytes());
|
||||
buf[size_of::<Mode>()..Self::RAW_LEN].copy_from_slice(&self.submode.to_be_bytes());
|
||||
Ok(Self::RAW_LEN)
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> Mode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
pub fn submode(&self) -> Submode {
|
||||
self.submode
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct TargetedModeCommand {
|
||||
pub address: ComponentId,
|
||||
pub mode_submode: ModeAndSubmode,
|
||||
}
|
||||
|
||||
impl TargetedModeCommand {
|
||||
pub const fn new(address: ComponentId, mode_submode: ModeAndSubmode) -> Self {
|
||||
Self {
|
||||
address,
|
||||
mode_submode,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> ComponentId {
|
||||
self.address
|
||||
}
|
||||
|
||||
pub fn mode_submode(&self) -> ModeAndSubmode {
|
||||
self.mode_submode
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> u32 {
|
||||
self.mode_submode.mode
|
||||
}
|
||||
|
||||
pub fn submode(&self) -> u16 {
|
||||
self.mode_submode.submode
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ModeRequest {
|
||||
/// Mode information. Can be used to notify other components of changed modes.
|
||||
ModeInfo(ModeAndSubmode),
|
||||
SetMode {
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
forced: bool,
|
||||
},
|
||||
ReadMode,
|
||||
AnnounceMode,
|
||||
AnnounceModeRecursive,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct TargetedModeRequest {
|
||||
target_id: ComponentId,
|
||||
mode_request: ModeRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ModeReply {
|
||||
/// Mode information. Can be used to notify other components of changed modes.
|
||||
ModeInfo(ModeAndSubmode),
|
||||
/// Reply to a mode request to confirm the commanded mode was reached.
|
||||
ModeReply(ModeAndSubmode),
|
||||
// Can not reach the commanded mode. Contains a reason as a [ResultU16].
|
||||
CantReachMode(ResultU16),
|
||||
/// We are in the wrong mode for unknown reasons. Contains the expected and reached mode.
|
||||
WrongMode {
|
||||
expected: ModeAndSubmode,
|
||||
reached: ModeAndSubmode,
|
||||
},
|
||||
}
|
||||
|
||||
pub type GenericModeReply = GenericMessage<ModeReply>;
|
||||
|
||||
pub trait ModeRequestSender {
|
||||
fn local_channel_id(&self) -> ComponentId;
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError>;
|
||||
}
|
||||
|
||||
pub trait ModeRequestReceiver {
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError>;
|
||||
}
|
||||
|
||||
impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver
|
||||
for MessageReceiverWithId<ModeRequest, R>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
pub enum ModeError {
|
||||
#[error("Messaging send error: {0}")]
|
||||
Send(#[from] GenericSendError),
|
||||
#[error("Messaging receive error: {0}")]
|
||||
Receive(#[from] GenericReceiveError),
|
||||
#[error("busy with other mode request")]
|
||||
Busy,
|
||||
}
|
||||
|
||||
pub trait ModeProvider {
|
||||
fn mode_and_submode(&self) -> ModeAndSubmode;
|
||||
|
||||
fn mode(&self) -> Mode {
|
||||
self.mode_and_submode().mode()
|
||||
}
|
||||
|
||||
fn submode(&self) -> Submode {
|
||||
self.mode_and_submode().submode()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeRequestHandler: ModeProvider {
|
||||
type Error;
|
||||
|
||||
fn start_transition(
|
||||
&mut self,
|
||||
requestor: MessageMetadata,
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
forced: bool,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool);
|
||||
|
||||
fn handle_mode_reached(
|
||||
&mut self,
|
||||
requestor_info: Option<MessageMetadata>,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn handle_mode_info(
|
||||
&mut self,
|
||||
requestor_info: MessageMetadata,
|
||||
info: ModeAndSubmode,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn handle_mode_request(
|
||||
&mut self,
|
||||
request: GenericMessage<ModeRequest>,
|
||||
) -> Result<(), Self::Error> {
|
||||
match request.message {
|
||||
ModeRequest::SetMode {
|
||||
mode_and_submode,
|
||||
forced,
|
||||
} => self.start_transition(request.requestor_info, mode_and_submode, forced),
|
||||
ModeRequest::ReadMode => self.send_mode_reply(
|
||||
request.requestor_info,
|
||||
ModeReply::ModeReply(self.mode_and_submode()),
|
||||
),
|
||||
ModeRequest::AnnounceMode => {
|
||||
self.announce_mode(Some(request.requestor_info), false);
|
||||
Ok(())
|
||||
}
|
||||
ModeRequest::AnnounceModeRecursive => {
|
||||
self.announce_mode(Some(request.requestor_info), true);
|
||||
Ok(())
|
||||
}
|
||||
ModeRequest::ModeInfo(info) => self.handle_mode_info(request.requestor_info, info),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeReplyReceiver {
|
||||
fn try_recv_mode_reply(&self)
|
||||
-> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError>;
|
||||
}
|
||||
|
||||
impl<R: MessageReceiverProvider<ModeReply>> ModeReplyReceiver
|
||||
for MessageReceiverWithId<ModeReply, R>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeReplySender {
|
||||
fn local_channel_id(&self) -> ComponentId;
|
||||
|
||||
/// The requestor is assumed to be the target of the reply.
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), GenericSendError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use crate::{
|
||||
queue::{GenericReceiveError, GenericSendError},
|
||||
request::{
|
||||
MessageSenderAndReceiver, MessageSenderMap, MessageSenderProvider,
|
||||
MessageSenderStoreProvider, RequestAndReplySenderAndReceiver,
|
||||
},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<S: MessageSenderProvider<ModeReply>> MessageSenderMap<ModeReply, S> {
|
||||
pub fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
target_id: ComponentId,
|
||||
request: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(requestor_info, target_id, request)
|
||||
}
|
||||
|
||||
pub fn add_reply_target(&mut self, target_id: ComponentId, request_sender: S) {
|
||||
self.add_message_target(target_id, request_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
From,
|
||||
Sender: MessageSenderProvider<ModeReply>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||
> ModeReplySender for MessageSenderAndReceiver<ModeReply, From, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
request: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.message_sender_store.send_message(
|
||||
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
|
||||
requestor_info.sender_id(),
|
||||
request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
To,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<ModeReply>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> ModeReplyReceiver for MessageSenderAndReceiver<To, ModeReply, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.message_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
>
|
||||
RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: ReplySender) {
|
||||
self.reply_sender_store
|
||||
.add_message_target(target_id, reply_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
ReplySender: MessageSenderProvider<ModeReply>,
|
||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||
> ModeReplySender
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.reply_sender_store.send_message(
|
||||
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
|
||||
requestor_info.sender_id(),
|
||||
reply,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
ReplySender: MessageSenderProvider<ModeReply>,
|
||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||
> ModeReplyReceiver
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.reply_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type definition for a mode handler which can handle mode requests.
|
||||
pub type ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore> =
|
||||
MessageSenderAndReceiver<ModeReply, ModeRequest, Sender, Receiver, ReplySenderStore>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ModeReply>,
|
||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||
> ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore>
|
||||
{
|
||||
pub fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
|
||||
pub fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(
|
||||
requestor_info.request_id(),
|
||||
requestor_info.sender_id(),
|
||||
reply,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type defintion for a mode handler object which can send mode requests and receive
|
||||
/// mode replies.
|
||||
pub type ModeRequestorInterface<Sender, Receiver, RequestSenderStore> =
|
||||
MessageSenderAndReceiver<ModeRequest, ModeReply, Sender, Receiver, RequestSenderStore>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ModeRequest>,
|
||||
Receiver: MessageReceiverProvider<ModeReply>,
|
||||
RequestSenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||
> ModeRequestorInterface<Sender, Receiver, RequestSenderStore>
|
||||
{
|
||||
pub fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> {
|
||||
self.try_recv_message()
|
||||
}
|
||||
|
||||
pub fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
reply: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(request_id, target_id, reply)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type defintion for a mode handler object which can both send mode requests and
|
||||
/// process mode requests.
|
||||
pub type ModeInterface<
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
> = RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>;
|
||||
|
||||
impl<S: MessageSenderProvider<ModeRequest>> MessageSenderMap<ModeRequest, S> {
|
||||
pub fn send_mode_request(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.send_message(requestor_info, target_id, request)
|
||||
}
|
||||
|
||||
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S) {
|
||||
self.add_message_target(target_id, request_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
To,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> ModeRequestReceiver
|
||||
for MessageSenderAndReceiver<To, ModeRequest, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.message_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
From,
|
||||
Sender: MessageSenderProvider<ModeRequest>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||
> ModeRequestSender
|
||||
for MessageSenderAndReceiver<ModeRequest, From, Sender, Receiver, SenderStore>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.message_sender_store.send_message(
|
||||
MessageMetadata::new(request_id, self.local_channel_id()),
|
||||
target_id,
|
||||
request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
>
|
||||
RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: ReqSender) {
|
||||
self.request_sender_store
|
||||
.add_message_target(target_id, request_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
> ModeRequestSender
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
}
|
||||
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.request_sender_store.send_message(
|
||||
MessageMetadata::new(request_id, self.local_channel_id()),
|
||||
target_id,
|
||||
request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
> ModeRequestReceiver
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> {
|
||||
self.request_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod std_mod {
|
||||
use std::sync::mpsc;
|
||||
|
||||
use crate::request::{MessageSenderList, OneMessageSender};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface<
|
||||
mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
|
||||
pub type ModeRequestorOneChildMpsc = ModeRequestorInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
OneMessageSender<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorOneChildBoundedMpsc = ModeRequestorInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
OneMessageSender<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorChildListMpsc = ModeRequestorInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorChildListBoundedMpsc = ModeRequestorInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
|
||||
pub type ModeRequestorAndHandlerMpsc = ModeInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
pub type ModeRequestorAndHandlerMpscBounded = ModeInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
}
|
||||
pub type ModeRaw = u32;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use core::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::{ComponentId, request::RequestId};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct ModeReqWrapper {
|
||||
pub request_id: RequestId,
|
||||
pub target_id: ComponentId,
|
||||
pub request: ModeRequest,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModeReqSenderMock {
|
||||
pub requests: RefCell<VecDeque<ModeReqWrapper>>,
|
||||
}
|
||||
|
||||
impl ModeRequestSender for ModeReqSenderMock {
|
||||
fn local_channel_id(&self) -> crate::ComponentId {
|
||||
0
|
||||
}
|
||||
|
||||
fn send_mode_request(
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericSendError> {
|
||||
self.requests.borrow_mut().push_back(ModeReqWrapper {
|
||||
request_id,
|
||||
target_id,
|
||||
request,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) mod tests {}
|
||||
|
||||
+265
-162
@@ -1,11 +1,7 @@
|
||||
use alloc::vec::Vec;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::{
|
||||
ComponentId,
|
||||
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, Submode},
|
||||
request::MessageSenderProvider,
|
||||
};
|
||||
use crate::{ComponentId, mode::ModeRaw};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
@@ -14,44 +10,6 @@ pub use alloc_mod::*;
|
||||
pub trait ModeNode {
|
||||
fn id(&self) -> ComponentId;
|
||||
}
|
||||
/// Trait which denotes that an object is a parent in a mode tree.
|
||||
///
|
||||
/// A mode parent is capable of sending mode requests to child objects and has a unique component
|
||||
/// ID.
|
||||
pub trait ModeParent: ModeNode {
|
||||
type Sender: MessageSenderProvider<ModeRequest>;
|
||||
|
||||
fn add_mode_child(&mut self, id: ComponentId, request_sender: Self::Sender);
|
||||
}
|
||||
|
||||
/// Trait which denotes that an object is a child in a mode tree.
|
||||
///
|
||||
/// A child is capable of sending mode replies to parent objects and has a unique component ID.
|
||||
pub trait ModeChild: ModeNode {
|
||||
type Sender: MessageSenderProvider<ModeReply>;
|
||||
|
||||
fn add_mode_parent(&mut self, id: ComponentId, reply_sender: Self::Sender);
|
||||
}
|
||||
|
||||
/// Utility method which connects a mode tree parent object to a child object by calling
|
||||
/// [ModeParent::add_mode_child] on the [parent][ModeParent] and calling
|
||||
/// [ModeChild::add_mode_parent] on the [child][ModeChild].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `parent` - The parent object which implements [ModeParent].
|
||||
/// * `request_sender` - Sender object to send mode requests to the child.
|
||||
/// * `child` - The child object which implements [ModeChild].
|
||||
/// * `reply_sender` - Sender object to send mode replies to the parent.
|
||||
pub fn connect_mode_nodes<ReqSender, ReplySender>(
|
||||
parent: &mut impl ModeParent<Sender = ReqSender>,
|
||||
request_sender: ReqSender,
|
||||
child: &mut impl ModeChild<Sender = ReplySender>,
|
||||
reply_sender: ReplySender,
|
||||
) {
|
||||
parent.add_mode_child(child.id(), request_sender);
|
||||
child.add_mode_parent(parent.id(), reply_sender);
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum TableEntryType {
|
||||
@@ -78,68 +36,48 @@ pub struct ModeTableEntryCommon {
|
||||
/// - For sequence tables, this denotes the mode which will be commanded
|
||||
/// - For target tables, this is the mode which the target children should have and which
|
||||
/// might be monitored depending on configuration.
|
||||
pub mode_submode: ModeAndSubmode,
|
||||
/// This mask allows to specify multiple allowed submodes for a given mode.
|
||||
pub allowed_submode_mask: Option<Submode>,
|
||||
}
|
||||
|
||||
impl ModeTableEntryCommon {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode) {
|
||||
self.allowed_submode_mask = Some(mask);
|
||||
}
|
||||
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode> {
|
||||
self.allowed_submode_mask
|
||||
}
|
||||
pub mode: ModeRaw,
|
||||
}
|
||||
|
||||
/// An entry for the target tables.
|
||||
#[derive(Debug)]
|
||||
pub struct TargetTableEntry {
|
||||
pub common: ModeTableEntryCommon,
|
||||
/// To allow multiple modes, you can specify mask bits which are ignored during mode
|
||||
/// checking.
|
||||
pub ignored_bits: Option<u32>,
|
||||
pub monitor_state: bool,
|
||||
}
|
||||
|
||||
impl TargetTableEntry {
|
||||
pub fn new(
|
||||
pub fn new(name: &'static str, target_id: ComponentId, mode: ModeRaw) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode,
|
||||
},
|
||||
ignored_bits: None,
|
||||
monitor_state: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_ignored_bits(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
allowed_submode_mask: Option<Submode>,
|
||||
mode: ModeRaw,
|
||||
ignored_bits: Option<u32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask,
|
||||
mode,
|
||||
},
|
||||
ignored_bits,
|
||||
monitor_state: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_precise_submode(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask: None,
|
||||
},
|
||||
monitor_state: true,
|
||||
}
|
||||
}
|
||||
|
||||
delegate::delegate! {
|
||||
to self.common {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry for the sequence tables.
|
||||
@@ -156,15 +94,14 @@ impl SequenceTableEntry {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
mode: ModeRaw,
|
||||
check_success: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask: None,
|
||||
mode,
|
||||
},
|
||||
check_success,
|
||||
}
|
||||
@@ -172,8 +109,6 @@ impl SequenceTableEntry {
|
||||
|
||||
delegate::delegate! {
|
||||
to self.common {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,16 +123,16 @@ pub struct ModeStoreValue {
|
||||
/// ID of the mode component.
|
||||
id: ComponentId,
|
||||
/// Current mode and submode of the component.
|
||||
pub mode_and_submode: ModeAndSubmode,
|
||||
pub mode: ModeRaw,
|
||||
/// State information to track whether a reply should be awaited for the mode component.
|
||||
pub awaiting_reply: bool,
|
||||
}
|
||||
|
||||
impl ModeStoreValue {
|
||||
pub fn new(id: ComponentId, mode_and_submode: ModeAndSubmode) -> Self {
|
||||
pub fn new(id: ComponentId, mode: ModeRaw) -> Self {
|
||||
Self {
|
||||
id,
|
||||
mode_and_submode,
|
||||
mode,
|
||||
awaiting_reply: false,
|
||||
}
|
||||
}
|
||||
@@ -206,13 +141,26 @@ impl ModeStoreValue {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||
self.mode_and_submode
|
||||
pub fn mode(&self) -> ModeRaw {
|
||||
self.mode
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SequenceTableProvider {
|
||||
fn sequence_at_index(&self, index: u8) -> Option<&[SequenceTableEntry]>;
|
||||
fn number_of_sequences(&self) -> usize;
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("mode store is full")]
|
||||
pub struct FullError;
|
||||
|
||||
pub trait ModeStoreProvider {
|
||||
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode);
|
||||
fn add_component(
|
||||
&mut self,
|
||||
target_id: ComponentId,
|
||||
initial_mode: ModeRaw,
|
||||
) -> Result<(), FullError>;
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool;
|
||||
|
||||
@@ -223,11 +171,11 @@ pub trait ModeStoreProvider {
|
||||
/// Generic handler for mode replies received from child components.
|
||||
///
|
||||
/// Implementation should clear the awaition flag if the `handle_reply_awaition` argument is
|
||||
/// true and returns whether any children are still awaiting replies. If the flag is not set
|
||||
/// true and returns whether any children are still awaiting replies.
|
||||
fn mode_reply_handler_with_reply_awaition(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
) -> bool {
|
||||
self.mode_reply_handler(sender_id, reported_mode_and_submode, true)
|
||||
.unwrap_or(false)
|
||||
@@ -236,21 +184,125 @@ pub trait ModeStoreProvider {
|
||||
fn mode_reply_handler_without_reply_awaition(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
) {
|
||||
self.mode_reply_handler(sender_id, reported_mode_and_submode, false);
|
||||
}
|
||||
|
||||
/// This function should return whether any children are still awaiting replies.
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
with_reply_awaition: bool,
|
||||
) -> Option<bool>;
|
||||
|
||||
fn set_reply_awaition_flag(&mut self, target_id: ComponentId);
|
||||
}
|
||||
|
||||
impl<const N: usize> ModeStoreProvider for heapless::Vec<ModeStoreValue, N> {
|
||||
fn add_component(
|
||||
&mut self,
|
||||
target_id: ComponentId,
|
||||
initial_mode: ModeRaw,
|
||||
) -> Result<(), FullError> {
|
||||
if self.is_full() {
|
||||
return Err(FullError);
|
||||
}
|
||||
self.push(ModeStoreValue::new(target_id, initial_mode))
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool {
|
||||
self.iter().any(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
|
||||
self.iter().find(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
|
||||
self.iter_mut().find(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
with_reply_awaition: bool,
|
||||
) -> Option<bool>;
|
||||
) -> Option<bool> {
|
||||
let mut still_awating_replies = None;
|
||||
if with_reply_awaition {
|
||||
still_awating_replies = Some(false);
|
||||
}
|
||||
self.iter_mut().for_each(|val| {
|
||||
if val.id() == sender_id {
|
||||
if let Some(mode) = reported_mode_and_submode {
|
||||
val.mode = mode;
|
||||
}
|
||||
if with_reply_awaition {
|
||||
val.awaiting_reply = false;
|
||||
}
|
||||
}
|
||||
if with_reply_awaition && val.awaiting_reply {
|
||||
still_awating_replies = Some(true);
|
||||
}
|
||||
});
|
||||
still_awating_replies
|
||||
}
|
||||
|
||||
fn set_reply_awaition_flag(&mut self, target_id: ComponentId) {
|
||||
self.iter_mut().for_each(|val| {
|
||||
if val.id() == target_id {
|
||||
val.awaiting_reply = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HeaplessModeStoreProvider<const N: usize>(pub heapless::Vec<ModeStoreValue, N>);
|
||||
|
||||
impl<const N: usize> ModeStoreProvider for HeaplessModeStoreProvider<N> {
|
||||
fn add_component(
|
||||
&mut self,
|
||||
target_id: ComponentId,
|
||||
initial_mode: ModeRaw,
|
||||
) -> Result<(), FullError> {
|
||||
self.0.add_component(target_id, initial_mode)
|
||||
}
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool {
|
||||
self.0.has_component(target_id)
|
||||
}
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
|
||||
self.0.get(target_id)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
|
||||
self.0.get_mut(target_id)
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
with_reply_awaition: bool,
|
||||
) -> Option<bool> {
|
||||
self.0
|
||||
.mode_reply_handler(sender_id, reported_mode_and_submode, with_reply_awaition)
|
||||
}
|
||||
|
||||
fn set_reply_awaition_flag(&mut self, target_id: ComponentId) {
|
||||
self.0.set_reply_awaition_flag(target_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use crate::subsystem::ModeRaw;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -258,13 +310,13 @@ pub mod alloc_mod {
|
||||
/// Name for a given mode table entry.
|
||||
pub name: &'static str,
|
||||
/// Optional fallback mode if the target mode can not be kept.
|
||||
pub fallback_mode: Option<Mode>,
|
||||
pub fallback_mode: Option<ModeRaw>,
|
||||
/// These are the rows of the a target table.
|
||||
pub entries: Vec<TargetTableEntry>,
|
||||
}
|
||||
|
||||
impl TargetTablesMapValue {
|
||||
pub fn new(name: &'static str, fallback_mode: Option<Mode>) -> Self {
|
||||
pub fn new(name: &'static str, fallback_mode: Option<ModeRaw>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
fallback_mode,
|
||||
@@ -328,21 +380,33 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl SequenceTableProvider for SequenceTablesMapValue {
|
||||
fn sequence_at_index(&self, index: u8) -> Option<&[SequenceTableEntry]> {
|
||||
self.entries
|
||||
.get(index as usize)
|
||||
.map(|table| table.entries.as_slice())
|
||||
}
|
||||
|
||||
fn number_of_sequences(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TargetModeTables(pub HashMap<Mode, TargetTablesMapValue>);
|
||||
pub struct TargetModeTables(pub HashMap<ModeRaw, TargetTablesMapValue>);
|
||||
|
||||
impl TargetModeTables {
|
||||
pub fn name(&self, mode: Mode) -> Option<&'static str> {
|
||||
pub fn name(&self, mode: ModeRaw) -> Option<&'static str> {
|
||||
self.0.get(&mode).map(|value| value.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl SequenceModeTables {
|
||||
pub fn name(&self, mode: Mode) -> Option<&'static str> {
|
||||
pub fn name(&self, mode: ModeRaw) -> Option<&'static str> {
|
||||
self.0.get(&mode).map(|value| value.name)
|
||||
}
|
||||
|
||||
pub fn name_of_sequence(&self, mode: Mode, seq_idx: usize) -> Option<&'static str> {
|
||||
pub fn name_of_sequence(&self, mode: ModeRaw, seq_idx: usize) -> Option<&'static str> {
|
||||
self.0
|
||||
.get(&mode)
|
||||
.map(|value| value.entries.get(seq_idx).map(|v| v.name))?
|
||||
@@ -352,9 +416,9 @@ pub mod alloc_mod {
|
||||
/// This is the core data structure used to store mode sequence tables.
|
||||
///
|
||||
/// A mode sequence table specifies which commands have to be sent in which order
|
||||
/// to reach a certain [Mode]. Therefore, it simply maps a [Mode] to a [SequenceTablesMapValue].
|
||||
/// to reach a certain [mode][ModeRaw]. Therefore, it simply maps a [mode][ModeRaw] to a [SequenceTablesMapValue].
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SequenceModeTables(pub HashMap<Mode, SequenceTablesMapValue>);
|
||||
pub struct SequenceModeTables(pub HashMap<ModeRaw, SequenceTablesMapValue>);
|
||||
|
||||
/// Mode store which tracks the [mode information][ModeStoreValue] inside a [Vec]
|
||||
#[derive(Debug, Default)]
|
||||
@@ -391,37 +455,42 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl ModeStoreProvider for ModeStoreVec {
|
||||
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
|
||||
self.0.push(ModeStoreValue::new(target_id, mode));
|
||||
impl ModeStoreProvider for alloc::vec::Vec<ModeStoreValue> {
|
||||
fn add_component(
|
||||
&mut self,
|
||||
target_id: ComponentId,
|
||||
mode: ModeRaw,
|
||||
) -> Result<(), FullError> {
|
||||
self.push(ModeStoreValue::new(target_id, mode));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool {
|
||||
self.0.iter().any(|val| val.id == target_id)
|
||||
self.iter().any(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
|
||||
self.0.iter().find(|val| val.id == target_id)
|
||||
self.iter().find(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
|
||||
self.0.iter_mut().find(|val| val.id == target_id)
|
||||
self.iter_mut().find(|val| val.id == target_id)
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
handle_reply_awaition: bool,
|
||||
) -> Option<bool> {
|
||||
let mut still_awating_replies = None;
|
||||
if handle_reply_awaition {
|
||||
still_awating_replies = Some(false);
|
||||
}
|
||||
self.0.iter_mut().for_each(|val| {
|
||||
self.iter_mut().for_each(|val| {
|
||||
if val.id() == sender_id {
|
||||
if let Some(mode_and_submode) = reported_mode_and_submode {
|
||||
val.mode_and_submode = mode_and_submode;
|
||||
if let Some(mode) = reported_mode_and_submode {
|
||||
val.mode = mode;
|
||||
}
|
||||
if handle_reply_awaition {
|
||||
val.awaiting_reply = false;
|
||||
@@ -433,12 +502,60 @@ pub mod alloc_mod {
|
||||
});
|
||||
still_awating_replies
|
||||
}
|
||||
|
||||
fn set_reply_awaition_flag(&mut self, target_id: ComponentId) {
|
||||
self.iter_mut().for_each(|val| {
|
||||
if val.id() == target_id {
|
||||
val.awaiting_reply = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
impl ModeStoreProvider for ModeStoreVec {
|
||||
fn add_component(
|
||||
&mut self,
|
||||
target_id: ComponentId,
|
||||
mode: ModeRaw,
|
||||
) -> Result<(), FullError> {
|
||||
self.0.add_component(target_id, mode)
|
||||
}
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool {
|
||||
self.0.has_component(target_id)
|
||||
}
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
|
||||
self.0.get(target_id)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
|
||||
self.0.get_mut(target_id)
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
handle_reply_awaition: bool,
|
||||
) -> Option<bool> {
|
||||
self.0
|
||||
.mode_reply_handler(sender_id, reported_mode_and_submode, handle_reply_awaition)
|
||||
}
|
||||
|
||||
fn set_reply_awaition_flag(&mut self, target_id: ComponentId) {
|
||||
self.0.set_reply_awaition_flag(target_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl ModeStoreProvider for ModeStoreMap {
|
||||
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
|
||||
fn add_component(
|
||||
&mut self,
|
||||
target_id: ComponentId,
|
||||
mode: ModeRaw,
|
||||
) -> Result<(), FullError> {
|
||||
self.0
|
||||
.insert(target_id, ModeStoreValue::new(target_id, mode));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool {
|
||||
@@ -456,7 +573,7 @@ pub mod alloc_mod {
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
reported_mode_and_submode: Option<ModeRaw>,
|
||||
handle_reply_awaition: bool,
|
||||
) -> Option<bool> {
|
||||
let mut still_awating_replies = None;
|
||||
@@ -465,8 +582,8 @@ pub mod alloc_mod {
|
||||
}
|
||||
for val in self.0.values_mut() {
|
||||
if val.id() == sender_id {
|
||||
if let Some(mode_and_submode) = reported_mode_and_submode {
|
||||
val.mode_and_submode = mode_and_submode;
|
||||
if let Some(mode) = reported_mode_and_submode {
|
||||
val.mode = mode;
|
||||
}
|
||||
if handle_reply_awaition {
|
||||
val.awaiting_reply = false;
|
||||
@@ -478,6 +595,14 @@ pub mod alloc_mod {
|
||||
}
|
||||
still_awating_replies
|
||||
}
|
||||
|
||||
fn set_reply_awaition_flag(&mut self, target_id: ComponentId) {
|
||||
self.0.iter_mut().for_each(|(_, val)| {
|
||||
if val.id() == target_id {
|
||||
val.awaiting_reply = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,14 +611,11 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
fn generic_test(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.add_component(1, 0).unwrap();
|
||||
mode_store.add_component(2, 1).unwrap();
|
||||
assert!(mode_store.has_component(1));
|
||||
assert!(mode_store.has_component(2));
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(0, 0)
|
||||
);
|
||||
assert_eq!(mode_store.get(1).unwrap().mode(), 0);
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(mode_store.get(1).unwrap().id, 1);
|
||||
@@ -503,66 +625,47 @@ mod tests {
|
||||
}
|
||||
|
||||
fn generic_reply_handling_with_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.add_component(1, 0).unwrap();
|
||||
mode_store.add_component(2, 1).unwrap();
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
let mut reply_awation_pending =
|
||||
mode_store.mode_reply_handler_with_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
|
||||
mode_store.mode_reply_handler_with_reply_awaition(1, Some(2));
|
||||
assert!(reply_awation_pending);
|
||||
reply_awation_pending = mode_store.mode_reply_handler_with_reply_awaition(2, None);
|
||||
assert!(!reply_awation_pending);
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
assert_eq!(mode_store.get(1).unwrap().mode(), 2);
|
||||
assert_eq!(mode_store.get(2).unwrap().mode(), 1);
|
||||
}
|
||||
|
||||
fn generic_reply_handling_test_no_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.add_component(1, 0).unwrap();
|
||||
mode_store.add_component(2, 1).unwrap();
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
mode_store.mode_reply_handler_without_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
|
||||
mode_store.mode_reply_handler_without_reply_awaition(1, Some(2));
|
||||
mode_store.mode_reply_handler_without_reply_awaition(2, None);
|
||||
assert!(mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
assert_eq!(mode_store.get(1).unwrap().mode(), 2);
|
||||
assert_eq!(mode_store.get(2).unwrap().mode(), 1);
|
||||
}
|
||||
|
||||
fn generic_reply_handling_with_reply_awaition_2(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.add_component(1, 0).unwrap();
|
||||
mode_store.add_component(2, 1).unwrap();
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
let mut reply_awation_pending =
|
||||
mode_store.mode_reply_handler(1, Some(ModeAndSubmode::new(2, 0)), true);
|
||||
let mut reply_awation_pending = mode_store.mode_reply_handler(1, Some(2), true);
|
||||
assert!(reply_awation_pending.unwrap());
|
||||
reply_awation_pending = mode_store.mode_reply_handler(2, None, true);
|
||||
assert!(!reply_awation_pending.unwrap());
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
assert_eq!(mode_store.get(1).unwrap().mode(), 2);
|
||||
assert_eq!(mode_store.get(2).unwrap().mode(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+1
-123
@@ -32,126 +32,4 @@ pub mod alloc_mod {}
|
||||
pub mod std_mod {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::sync::mpsc;
|
||||
|
||||
use crate::{
|
||||
ComponentId,
|
||||
mode::{
|
||||
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
|
||||
ModeRequestorAndHandlerMpsc, ModeRequestorOneChildMpsc,
|
||||
},
|
||||
request::{GenericMessage, MessageMetadata},
|
||||
};
|
||||
|
||||
const TEST_COMPONENT_ID_0: ComponentId = 5;
|
||||
const TEST_COMPONENT_ID_1: ComponentId = 6;
|
||||
const TEST_COMPONENT_ID_2: ComponentId = 7;
|
||||
|
||||
#[test]
|
||||
fn test_simple_mode_requestor() {
|
||||
let (reply_sender, reply_receiver) = mpsc::channel();
|
||||
let (request_sender, request_receiver) = mpsc::channel();
|
||||
let mut mode_requestor =
|
||||
ModeRequestorOneChildMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
|
||||
mode_requestor.add_message_target(TEST_COMPONENT_ID_1, request_sender);
|
||||
|
||||
// Send a request and verify it arrives at the receiver.
|
||||
let request_id = 2;
|
||||
let sent_request = ModeRequest::ReadMode;
|
||||
mode_requestor
|
||||
.send_mode_request(request_id, TEST_COMPONENT_ID_1, sent_request)
|
||||
.expect("send failed");
|
||||
let request = request_receiver.recv().expect("recv failed");
|
||||
assert_eq!(request.request_id(), 2);
|
||||
assert_eq!(request.sender_id(), TEST_COMPONENT_ID_0);
|
||||
assert_eq!(request.message, sent_request);
|
||||
|
||||
// Send a reply and verify it arrives at the requestor.
|
||||
let mode_reply = ModeReply::ModeReply(ModeAndSubmode::new(1, 5));
|
||||
reply_sender
|
||||
.send(GenericMessage::new(
|
||||
MessageMetadata::new(request_id, TEST_COMPONENT_ID_1),
|
||||
mode_reply,
|
||||
))
|
||||
.expect("send failed");
|
||||
let reply = mode_requestor.try_recv_mode_reply().expect("recv failed");
|
||||
assert!(reply.is_some());
|
||||
let reply = reply.unwrap();
|
||||
assert_eq!(reply.sender_id(), TEST_COMPONENT_ID_1);
|
||||
assert_eq!(reply.request_id(), 2);
|
||||
assert_eq!(reply.message, mode_reply);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mode_requestor_and_request_handler_request_sending() {
|
||||
let (_reply_sender_to_connector, reply_receiver_of_connector) = mpsc::channel();
|
||||
let (_request_sender_to_connector, request_receiver_of_connector) = mpsc::channel();
|
||||
|
||||
let (request_sender_to_channel_1, request_receiver_channel_1) = mpsc::channel();
|
||||
//let (reply_sender_to_channel_2, reply_receiver_channel_2) = mpsc::channel();
|
||||
let mut mode_connector = ModeRequestorAndHandlerMpsc::new(
|
||||
TEST_COMPONENT_ID_0,
|
||||
request_receiver_of_connector,
|
||||
reply_receiver_of_connector,
|
||||
);
|
||||
assert_eq!(
|
||||
ModeRequestSender::local_channel_id(&mode_connector),
|
||||
TEST_COMPONENT_ID_0
|
||||
);
|
||||
assert_eq!(
|
||||
ModeReplySender::local_channel_id(&mode_connector),
|
||||
TEST_COMPONENT_ID_0
|
||||
);
|
||||
assert_eq!(
|
||||
mode_connector.local_channel_id_generic(),
|
||||
TEST_COMPONENT_ID_0
|
||||
);
|
||||
|
||||
mode_connector.add_request_target(TEST_COMPONENT_ID_1, request_sender_to_channel_1);
|
||||
|
||||
// Send a request and verify it arrives at the receiver.
|
||||
let request_id = 2;
|
||||
let sent_request = ModeRequest::ReadMode;
|
||||
mode_connector
|
||||
.send_mode_request(request_id, TEST_COMPONENT_ID_1, sent_request)
|
||||
.expect("send failed");
|
||||
|
||||
let request = request_receiver_channel_1.recv().expect("recv failed");
|
||||
assert_eq!(request.request_id(), 2);
|
||||
assert_eq!(request.sender_id(), TEST_COMPONENT_ID_0);
|
||||
assert_eq!(request.message, ModeRequest::ReadMode);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mode_requestor_and_request_handler_reply_sending() {
|
||||
let (_reply_sender_to_connector, reply_receiver_of_connector) = mpsc::channel();
|
||||
let (_request_sender_to_connector, request_receiver_of_connector) = mpsc::channel();
|
||||
|
||||
let (reply_sender_to_channel_2, reply_receiver_channel_2) = mpsc::channel();
|
||||
let mut mode_connector = ModeRequestorAndHandlerMpsc::new(
|
||||
TEST_COMPONENT_ID_0,
|
||||
request_receiver_of_connector,
|
||||
reply_receiver_of_connector,
|
||||
);
|
||||
mode_connector.add_reply_target(TEST_COMPONENT_ID_2, reply_sender_to_channel_2);
|
||||
|
||||
// Send a reply and verify it arrives at the receiver.
|
||||
let request_id = 2;
|
||||
let sent_reply = ModeReply::ModeReply(ModeAndSubmode::new(3, 5));
|
||||
mode_connector
|
||||
.send_mode_reply(
|
||||
MessageMetadata::new(request_id, TEST_COMPONENT_ID_2),
|
||||
sent_reply,
|
||||
)
|
||||
.expect("send failed");
|
||||
let reply = reply_receiver_channel_2.recv().expect("recv failed");
|
||||
assert_eq!(reply.request_id(), 2);
|
||||
assert_eq!(reply.sender_id(), TEST_COMPONENT_ID_0);
|
||||
assert_eq!(reply.message, sent_reply);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mode_reply_handler() {}
|
||||
}
|
||||
mod tests {}
|
||||
|
||||
@@ -85,8 +85,9 @@ impl fmt::Display for UniqueApidTargetId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Target and APID ID with APID {:#03x} and target {}",
|
||||
self.apid, self.unique_id
|
||||
"Target and APID ID with APID {:#05x} and target {}",
|
||||
self.apid.value(),
|
||||
self.unique_id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+472
-649
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user