Compare commits
59 Commits
satrs-shar
...
satrs-shar
| Author | SHA1 | Date | |
|---|---|---|---|
| 56d9cafa59 | |||
|
|
7de057aaf2
|
||
| 2f4a11a0bc | |||
|
|
0cd929a19c | ||
| d54167837e | |||
|
|
ecce07a471 | ||
| 526254851a | |||
|
|
440e757141
|
||
| 02aa825783 | |||
|
|
bc9e0e4a94 | ||
| 06e713a557 | |||
|
|
778512d50e | ||
| 5d40638964 | |||
|
|
1b07b845f2 | ||
| 2e58a311c8 | |||
|
|
62d64e692a
|
||
| 3784f47b66 | |||
|
|
e1911f1b6e | ||
| 2e53ce1871 | |||
|
|
a6c460129b
|
||
| d5ecefd683 | |||
| 2f9f3b8183 | |||
| b28cff85de | |||
| da3de9f22c | |||
| aefdf0987d | |||
|
|
7ae9e62d66 | ||
| fea1a9028b | |||
|
|
abc145fb2d
|
||
| 490635dfcf | |||
| de3d22a022 | |||
| cbe211fe8b | |||
|
|
e379bc3fd7 | ||
| a61ee85796 | |||
|
|
81473b30f9
|
||
| 22675a73f9 | |||
|
|
c68e3d4f75
|
||
| 3deedfba17 | |||
|
|
533caea0fe
|
||
| 848fe6f207 | |||
|
|
cf64fea7d9 | ||
| 4948db3fa5 | |||
|
|
1920e4878c
|
||
| f46bc94b04 | |||
|
fb45da1890
|
|||
| f600ee499a | |||
| 3f28f60c59 | |||
| 44b1f2b037 | |||
| 4b22958b34 | |||
| a711c6acd9 | |||
|
99a954a1f5
|
|||
|
4a4fd7ac2c
|
|||
|
9bf08849a2
|
|||
| b8f7fefe26 | |||
|
a501832698
|
|||
| c7284d3f1c | |||
| 18263d4568 | |||
|
95519c1363
|
|||
| 2ec32717d0 | |||
|
bfdd777685
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -47,6 +47,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
- run: cargo fmt --all -- --check
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
@@ -55,7 +57,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
|
- run: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc -p satrs --all-features
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
name: Clippy
|
name: Clippy
|
||||||
@@ -63,4 +65,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: clippy
|
||||||
- run: cargo clippy -- -D warnings
|
- run: cargo clippy -- -D warnings
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ members = [
|
|||||||
"satrs-example",
|
"satrs-example",
|
||||||
"satrs-minisim",
|
"satrs-minisim",
|
||||||
"satrs-shared",
|
"satrs-shared",
|
||||||
|
"embedded-examples/embedded-client",
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
[](https://robamu.github.io/sat-rs/book/)
|
[](https://robamu.github.io/sat-rs/book/)
|
||||||
[](https://crates.io/crates/satrs)
|
[](https://crates.io/crates/satrs)
|
||||||
[](https://docs.rs/satrs)
|
[](https://docs.rs/satrs)
|
||||||
|
[](https://matrix.to/#/#sat-rs:matrix.org)
|
||||||
|
|
||||||
sat-rs
|
sat-rs
|
||||||
=========
|
=========
|
||||||
@@ -61,6 +62,8 @@ Each project has its own `CHANGELOG.md`.
|
|||||||
packet protocol implementations. This repository is re-exported in the
|
packet protocol implementations. This repository is re-exported in the
|
||||||
[`satrs`](https://egit.irs.uni-stuttgart.de/rust/satrs/src/branch/main/satrs)
|
[`satrs`](https://egit.irs.uni-stuttgart.de/rust/satrs/src/branch/main/satrs)
|
||||||
crate.
|
crate.
|
||||||
|
* [`cfdp`](https://egit.irs.uni-stuttgart.de/rust/cfdp): CCSDS File Delivery Protocol
|
||||||
|
(CFDP) high-level library components.
|
||||||
|
|
||||||
# Flight Heritage
|
# Flight Heritage
|
||||||
|
|
||||||
|
|||||||
18
embedded-examples/embedded-client/Cargo.toml
Normal file
18
embedded-examples/embedded-client/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "embedded-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
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" }
|
||||||
|
tmtc-utils = { git = "https://egit.irs.uni-stuttgart.de/rust/tmtc-utils.git", version = "0.1" }
|
||||||
|
postcard = { version = "1", features = ["alloc"] }
|
||||||
|
cobs = "0.5"
|
||||||
|
fern = "0.7"
|
||||||
|
humantime = "2"
|
||||||
|
log = "0.4"
|
||||||
2
embedded-examples/embedded-client/config.toml
Normal file
2
embedded-examples/embedded-client/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[interface]
|
||||||
|
serial_port = "/dev/ttyUSB0"
|
||||||
107
embedded-examples/embedded-client/src/main.rs
Normal file
107
embedded-examples/embedded-client/src/main.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::Read,
|
||||||
|
path::Path,
|
||||||
|
time::{Duration, SystemTime},
|
||||||
|
};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use cobs::CobsDecoderOwned;
|
||||||
|
use satrs_stm32f3_disco_rtic::Request;
|
||||||
|
use spacepackets::{CcsdsPacketCreatorOwned, CcsdsPacketReader, SpHeader};
|
||||||
|
use tmtc_utils::transport::serial::PacketTransportSerialCobs;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
struct Cli {
|
||||||
|
#[arg(short, long)]
|
||||||
|
ping: bool,
|
||||||
|
|
||||||
|
/// Set frequency in milliseconds.
|
||||||
|
#[arg(short, long)]
|
||||||
|
set_led_frequency: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct Config {
|
||||||
|
interface: Interface,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct Interface {
|
||||||
|
serial_port: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_logger() -> Result<(), fern::InitError> {
|
||||||
|
fern::Dispatch::new()
|
||||||
|
.format(|out, message, record| {
|
||||||
|
out.finish(format_args!(
|
||||||
|
"[{} {} {}] {}",
|
||||||
|
humantime::format_rfc3339_seconds(SystemTime::now()),
|
||||||
|
record.level(),
|
||||||
|
record.target(),
|
||||||
|
message
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.level(log::LevelFilter::Debug)
|
||||||
|
.chain(std::io::stdout())
|
||||||
|
.chain(fern::log_file("output.log")?)
|
||||||
|
.apply()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
setup_logger().expect("failed to initialize logger");
|
||||||
|
println!("sat-rs embedded examples TMTC client");
|
||||||
|
|
||||||
|
let cli = Cli::parse();
|
||||||
|
let mut config_file =
|
||||||
|
File::open(Path::new("config.toml")).expect("opening config.toml file failed");
|
||||||
|
let mut toml_str = String::new();
|
||||||
|
config_file
|
||||||
|
.read_to_string(&mut toml_str)
|
||||||
|
.expect("reading config.toml file failed");
|
||||||
|
let config: Config = toml::from_str(&toml_str).expect("parsing config.toml file failed");
|
||||||
|
println!("Connecting to serial port {}", config.interface.serial_port);
|
||||||
|
|
||||||
|
let serial = serialport::new(config.interface.serial_port, 115200)
|
||||||
|
.open()
|
||||||
|
.expect("opening serial port failed");
|
||||||
|
let mut transport = PacketTransportSerialCobs::new(serial, CobsDecoderOwned::new(1024));
|
||||||
|
|
||||||
|
if cli.ping {
|
||||||
|
let request = Request::Ping;
|
||||||
|
let tc = create_stm32f3_tc(&request);
|
||||||
|
log::info!(
|
||||||
|
"Sending ping request with TC ID: {:#010x}",
|
||||||
|
tc.ccsds_packet_id_and_psc().raw()
|
||||||
|
);
|
||||||
|
transport.send(&tc.to_vec()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(freq_ms) = cli.set_led_frequency {
|
||||||
|
let request = Request::ChangeBlinkFrequency(Duration::from_millis(freq_ms as u64));
|
||||||
|
let tc = create_stm32f3_tc(&request);
|
||||||
|
log::info!(
|
||||||
|
"Sending change blink frequency request {:?} with TC ID: {:#010x}",
|
||||||
|
request,
|
||||||
|
tc.ccsds_packet_id_and_psc().raw()
|
||||||
|
);
|
||||||
|
transport.send(&tc.to_vec()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Waiting for response...");
|
||||||
|
loop {
|
||||||
|
transport
|
||||||
|
.receive(|packet: &[u8]| {
|
||||||
|
let reader = CcsdsPacketReader::new_with_checksum(packet);
|
||||||
|
log::info!("Received packet: {:?}", reader);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_stm32f3_tc(request: &Request) -> CcsdsPacketCreatorOwned {
|
||||||
|
let req_raw = postcard::to_allocvec(&request).unwrap();
|
||||||
|
let sp_header = SpHeader::new_from_apid(satrs_stm32f3_disco_rtic::APID);
|
||||||
|
CcsdsPacketCreatorOwned::new_tc_with_checksum(sp_header, &req_raw).unwrap()
|
||||||
|
}
|
||||||
@@ -34,4 +34,4 @@ rustflags = [
|
|||||||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
DEFMT_LOG = "info"
|
DEFMT_LOG = "info"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
/itm.txt
|
/itm.txt
|
||||||
/.cargo/config*
|
/.cargo/config.toml
|
||||||
/.vscode
|
/.vscode
|
||||||
|
|||||||
1212
embedded-examples/stm32f3-disco-rtic/Cargo.lock
generated
1212
embedded-examples/stm32f3-disco-rtic/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -9,49 +9,33 @@ default-run = "satrs-stm32f3-disco-rtic"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
defmt = "0.3"
|
defmt = "1"
|
||||||
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
defmt-rtt = { version = "1" }
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
panic-probe = { version = "1", features = ["print-defmt"] }
|
||||||
embedded-hal = "0.2.7"
|
embedded-hal = "1"
|
||||||
cortex-m-semihosting = "0.5.0"
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
embassy-stm32 = { version = "0.4", features = ["defmt", "stm32f303vc", "unstable-pac"] }
|
||||||
enumset = "1"
|
enumset = "1"
|
||||||
heapless = "0.8"
|
heapless = "0.9"
|
||||||
|
spacepackets = { version = "0.17", default-features = false, features = ["defmt", "serde"] }
|
||||||
|
static_cell = "2"
|
||||||
|
cobs = { version = "0.5", default-features = false, features = ["defmt"] }
|
||||||
|
postcard = { version = "1" }
|
||||||
|
arbitrary-int = "2"
|
||||||
|
thiserror = { version = "2", default-features = false }
|
||||||
|
serde = { version = "1", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
[dependencies.rtic]
|
rtic = { version = "2", features = ["thumbv7-backend"] }
|
||||||
version = "2"
|
rtic-sync = { version = "1" }
|
||||||
features = ["thumbv7-backend"]
|
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||||
|
|
||||||
[dependencies.rtic-monotonics]
|
#[dependencies.satrs]
|
||||||
version = "2"
|
# path = "../../satrs"
|
||||||
features = ["cortex-m-systick"]
|
#default-features = false
|
||||||
|
# features = ["defmt"]
|
||||||
[dependencies.cobs]
|
|
||||||
version = "0.3"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.stm32f3xx-hal]
|
|
||||||
git = "https://github.com/robamu/stm32f3xx-hal"
|
|
||||||
version = "0.11.0-alpha.0"
|
|
||||||
features = ["stm32f303xc", "rt", "enumset"]
|
|
||||||
branch = "complete-dma-update"
|
|
||||||
# Can be used in workspace to develop and update HAL
|
|
||||||
# path = "../stm32f3xx-hal"
|
|
||||||
|
|
||||||
[dependencies.stm32f3-discovery]
|
|
||||||
git = "https://github.com/robamu/stm32f3-discovery"
|
|
||||||
version = "0.8.0-alpha.0"
|
|
||||||
branch = "complete-dma-update-hal"
|
|
||||||
# Can be used in workspace to develop and update BSP
|
|
||||||
# path = "../stm32f3-discovery"
|
|
||||||
|
|
||||||
[dependencies.satrs]
|
|
||||||
# path = "satrs"
|
|
||||||
version = "0.2"
|
|
||||||
default-features = false
|
|
||||||
features = ["defmt"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
defmt-test = "0.3"
|
defmt-test = "0.4"
|
||||||
|
|
||||||
# cargo test
|
# cargo test
|
||||||
[profile.test]
|
[profile.test]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
|||||||
target extended-remote localhost:2331
|
|
||||||
|
|
||||||
monitor reset
|
|
||||||
|
|
||||||
# *try* to stop at the user entry point (it might be gone due to inlining)
|
|
||||||
break main
|
|
||||||
|
|
||||||
load
|
|
||||||
|
|
||||||
continue
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Sample OpenOCD configuration for the STM32F3DISCOVERY development board
|
|
||||||
|
|
||||||
# Depending on the hardware revision you got you'll have to pick ONE of these
|
|
||||||
# interfaces. At any time only one interface should be commented out.
|
|
||||||
|
|
||||||
# Revision C (newer revision)
|
|
||||||
source [find interface/stlink.cfg]
|
|
||||||
|
|
||||||
# Revision A and B (older revisions)
|
|
||||||
# source [find interface/stlink-v2.cfg]
|
|
||||||
|
|
||||||
source [find target/stm32f3x.cfg]
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
target extended-remote :3333
|
|
||||||
|
|
||||||
# print demangled symbols
|
|
||||||
set print asm-demangle on
|
|
||||||
|
|
||||||
# set backtrace limit to not have infinite backtrace loops
|
|
||||||
set backtrace limit 32
|
|
||||||
|
|
||||||
# detect unhandled exceptions, hard faults and panics
|
|
||||||
break DefaultHandler
|
|
||||||
break HardFault
|
|
||||||
break rust_begin_unwind
|
|
||||||
# # run the next few lines so the panic message is printed immediately
|
|
||||||
# # the number needs to be adjusted for your panic handler
|
|
||||||
# commands $bpnum
|
|
||||||
# next 4
|
|
||||||
# end
|
|
||||||
|
|
||||||
# *try* to stop at the user entry point (it might be gone due to inlining)
|
|
||||||
break main
|
|
||||||
|
|
||||||
# monitor arm semihosting enable
|
|
||||||
|
|
||||||
# # send captured ITM to the file itm.fifo
|
|
||||||
# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
|
|
||||||
# # 8000000 must match the core clock frequency
|
|
||||||
# # 2000000 is the frequency of the SWO pin. This was added for newer
|
|
||||||
# openocd versions like v0.12.0.
|
|
||||||
# monitor tpiu config internal itm.txt uart off 8000000 2000000
|
|
||||||
|
|
||||||
# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
|
|
||||||
# # 8000000 must match the core clock frequency
|
|
||||||
# # 2000000 is the frequency of the SWO pin
|
|
||||||
# monitor tpiu config external uart off 8000000 2000000
|
|
||||||
|
|
||||||
# # enable ITM port 0
|
|
||||||
# monitor itm port 0 on
|
|
||||||
|
|
||||||
load
|
|
||||||
|
|
||||||
# start the process but immediately halt the processor
|
|
||||||
stepi
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/venv
|
|
||||||
/.tmtc-history.txt
|
|
||||||
/log
|
|
||||||
/.idea/*
|
|
||||||
!/.idea/runConfigurations
|
|
||||||
|
|
||||||
/seqcnt.txt
|
|
||||||
/tmtc_conf.json
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"com_if": "serial_cobs",
|
|
||||||
"serial_baudrate": 115200
|
|
||||||
}
|
|
||||||
@@ -1,305 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Example client for the sat-rs example application"""
|
|
||||||
import struct
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from typing import Any, Optional, cast
|
|
||||||
from prompt_toolkit.history import FileHistory, History
|
|
||||||
from spacepackets.ecss.tm import CdsShortTimestamp
|
|
||||||
|
|
||||||
import tmtccmd
|
|
||||||
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusTm, PusVerificator
|
|
||||||
from spacepackets.ecss.pus_17_test import Service17Tm
|
|
||||||
from spacepackets.ecss.pus_1_verification import UnpackParams, Service1Tm
|
|
||||||
|
|
||||||
from tmtccmd import TcHandlerBase, ProcedureParamsWrapper
|
|
||||||
from tmtccmd.core.base import BackendRequest
|
|
||||||
from tmtccmd.core.ccsds_backend import QueueWrapper
|
|
||||||
from tmtccmd.logging import add_colorlog_console_logger
|
|
||||||
from tmtccmd.pus import VerificationWrapper
|
|
||||||
from tmtccmd.tmtc import CcsdsTmHandler, SpecificApidHandlerBase
|
|
||||||
from tmtccmd.com import ComInterface
|
|
||||||
from tmtccmd.config import (
|
|
||||||
CmdTreeNode,
|
|
||||||
default_json_path,
|
|
||||||
SetupParams,
|
|
||||||
HookBase,
|
|
||||||
params_to_procedure_conversion,
|
|
||||||
)
|
|
||||||
from tmtccmd.config.com import SerialCfgWrapper
|
|
||||||
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
|
||||||
from tmtccmd.logging.pus import (
|
|
||||||
RegularTmtcLogWrapper,
|
|
||||||
RawTmtcTimedLogWrapper,
|
|
||||||
TimedLogWhen,
|
|
||||||
)
|
|
||||||
from tmtccmd.tmtc import (
|
|
||||||
TcQueueEntryType,
|
|
||||||
ProcedureWrapper,
|
|
||||||
TcProcedureType,
|
|
||||||
FeedWrapper,
|
|
||||||
SendCbParams,
|
|
||||||
DefaultPusQueueHelper,
|
|
||||||
)
|
|
||||||
from tmtccmd.pus.s5_fsfw_event import Service5Tm
|
|
||||||
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
|
|
||||||
from tmtccmd.util.obj_id import ObjectIdDictT
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger()
|
|
||||||
|
|
||||||
EXAMPLE_PUS_APID = 0x02
|
|
||||||
|
|
||||||
|
|
||||||
class SatRsConfigHook(HookBase):
|
|
||||||
def __init__(self, json_cfg_path: str):
|
|
||||||
super().__init__(json_cfg_path)
|
|
||||||
|
|
||||||
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
|
||||||
from tmtccmd.config.com import (
|
|
||||||
create_com_interface_default,
|
|
||||||
create_com_interface_cfg_default,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert self.cfg_path is not None
|
|
||||||
cfg = create_com_interface_cfg_default(
|
|
||||||
com_if_key=com_if_key,
|
|
||||||
json_cfg_path=self.cfg_path,
|
|
||||||
space_packet_ids=None,
|
|
||||||
)
|
|
||||||
if cfg is None:
|
|
||||||
raise ValueError(
|
|
||||||
f"No valid configuration could be retrieved for the COM IF with key {com_if_key}"
|
|
||||||
)
|
|
||||||
if cfg.com_if_key == "serial_cobs":
|
|
||||||
cfg = cast(SerialCfgWrapper, cfg)
|
|
||||||
cfg.serial_cfg.serial_timeout = 0.5
|
|
||||||
return create_com_interface_default(cfg)
|
|
||||||
|
|
||||||
def get_command_definitions(self) -> CmdTreeNode:
|
|
||||||
"""This function should return the root node of the command definition tree."""
|
|
||||||
return create_cmd_definition_tree()
|
|
||||||
|
|
||||||
def get_cmd_history(self) -> Optional[History]:
|
|
||||||
"""Optionlly return a history class for the past command paths which will be used
|
|
||||||
when prompting a command path from the user in CLI mode."""
|
|
||||||
return FileHistory(".tmtc-history.txt")
|
|
||||||
|
|
||||||
def get_object_ids(self) -> ObjectIdDictT:
|
|
||||||
from tmtccmd.config.objects import get_core_object_ids
|
|
||||||
|
|
||||||
return get_core_object_ids()
|
|
||||||
|
|
||||||
|
|
||||||
def create_cmd_definition_tree() -> CmdTreeNode:
|
|
||||||
root_node = CmdTreeNode.root_node()
|
|
||||||
root_node.add_child(CmdTreeNode("ping", "Send PUS ping TC"))
|
|
||||||
root_node.add_child(CmdTreeNode("change_blink_freq", "Change blink frequency"))
|
|
||||||
return root_node
|
|
||||||
|
|
||||||
|
|
||||||
class PusHandler(SpecificApidHandlerBase):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
file_logger: logging.Logger,
|
|
||||||
verif_wrapper: VerificationWrapper,
|
|
||||||
raw_logger: RawTmtcTimedLogWrapper,
|
|
||||||
):
|
|
||||||
super().__init__(EXAMPLE_PUS_APID, None)
|
|
||||||
self.file_logger = file_logger
|
|
||||||
self.raw_logger = raw_logger
|
|
||||||
self.verif_wrapper = verif_wrapper
|
|
||||||
|
|
||||||
def handle_tm(self, packet: bytes, _user_args: Any):
|
|
||||||
try:
|
|
||||||
pus_tm = PusTm.unpack(
|
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
|
||||||
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
|
||||||
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
|
||||||
raise e
|
|
||||||
service = pus_tm.service
|
|
||||||
tm_packet = None
|
|
||||||
if service == 1:
|
|
||||||
tm_packet = Service1Tm.unpack(
|
|
||||||
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 1, 2)
|
|
||||||
)
|
|
||||||
res = self.verif_wrapper.add_tm(tm_packet)
|
|
||||||
if res is None:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Received Verification TM[{tm_packet.service}, {tm_packet.subservice}] "
|
|
||||||
f"with Request ID {tm_packet.tc_req_id.as_u32():#08x}"
|
|
||||||
)
|
|
||||||
_LOGGER.warning(
|
|
||||||
f"No matching telecommand found for {tm_packet.tc_req_id}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.verif_wrapper.log_to_console(tm_packet, res)
|
|
||||||
self.verif_wrapper.log_to_file(tm_packet, res)
|
|
||||||
if service == 3:
|
|
||||||
_LOGGER.info("No handling for HK packets implemented")
|
|
||||||
_LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
|
|
||||||
pus_tm = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
if pus_tm.subservice == 25:
|
|
||||||
if len(pus_tm.source_data) < 8:
|
|
||||||
raise ValueError("No addressable ID in HK packet")
|
|
||||||
json_str = pus_tm.source_data[8:]
|
|
||||||
_LOGGER.info("received JSON string: " + json_str.decode("utf-8"))
|
|
||||||
if service == 5:
|
|
||||||
tm_packet = Service5Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
if service == 17:
|
|
||||||
tm_packet = Service17Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
if tm_packet.subservice == 2:
|
|
||||||
_LOGGER.info("Received Ping Reply TM[17,2]")
|
|
||||||
else:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
|
||||||
)
|
|
||||||
if tm_packet is None:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"The service {service} is not implemented in Telemetry Factory"
|
|
||||||
)
|
|
||||||
tm_packet = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
self.raw_logger.log_tm(pus_tm)
|
|
||||||
|
|
||||||
|
|
||||||
def make_addressable_id(target_id: int, unique_id: int) -> bytes:
|
|
||||||
byte_string = bytearray(struct.pack("!I", target_id))
|
|
||||||
byte_string.extend(struct.pack("!I", unique_id))
|
|
||||||
return byte_string
|
|
||||||
|
|
||||||
|
|
||||||
class TcHandler(TcHandlerBase):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
seq_count_provider: FileSeqCountProvider,
|
|
||||||
verif_wrapper: VerificationWrapper,
|
|
||||||
):
|
|
||||||
super(TcHandler, self).__init__()
|
|
||||||
self.seq_count_provider = seq_count_provider
|
|
||||||
self.verif_wrapper = verif_wrapper
|
|
||||||
self.queue_helper = DefaultPusQueueHelper(
|
|
||||||
queue_wrapper=QueueWrapper.empty(),
|
|
||||||
tc_sched_timestamp_len=7,
|
|
||||||
seq_cnt_provider=seq_count_provider,
|
|
||||||
pus_verificator=verif_wrapper.pus_verificator,
|
|
||||||
default_pus_apid=EXAMPLE_PUS_APID,
|
|
||||||
)
|
|
||||||
|
|
||||||
def send_cb(self, send_params: SendCbParams):
|
|
||||||
entry_helper = send_params.entry
|
|
||||||
if entry_helper.is_tc:
|
|
||||||
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
|
||||||
pus_tc_wrapper = entry_helper.to_pus_tc_entry()
|
|
||||||
pus_tc_wrapper.pus_tc.seq_count = (
|
|
||||||
self.seq_count_provider.get_and_increment()
|
|
||||||
)
|
|
||||||
self.verif_wrapper.add_tc(pus_tc_wrapper.pus_tc)
|
|
||||||
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
|
||||||
_LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
|
||||||
send_params.com_if.send(raw_tc)
|
|
||||||
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
|
||||||
log_entry = entry_helper.to_log_entry()
|
|
||||||
_LOGGER.info(log_entry.log_str)
|
|
||||||
|
|
||||||
def queue_finished_cb(self, info: ProcedureWrapper):
|
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
|
||||||
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
|
||||||
|
|
||||||
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
|
||||||
q = self.queue_helper
|
|
||||||
q.queue_wrapper = wrapper.queue_wrapper
|
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
|
||||||
cmd_path = def_proc.cmd_path
|
|
||||||
if cmd_path == "/ping":
|
|
||||||
q.add_log_cmd("Sending PUS ping telecommand")
|
|
||||||
q.add_pus_tc(PusTelecommand(service=17, subservice=1))
|
|
||||||
if cmd_path == "/change_blink_freq":
|
|
||||||
self.create_change_blink_freq_command(q)
|
|
||||||
|
|
||||||
def create_change_blink_freq_command(self, q: DefaultPusQueueHelper):
|
|
||||||
q.add_log_cmd("Changing blink frequency")
|
|
||||||
while True:
|
|
||||||
blink_freq = int(
|
|
||||||
input(
|
|
||||||
"Please specify new blink frequency in ms. Valid Range [2..10000]: "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if blink_freq < 2 or blink_freq > 10000:
|
|
||||||
print(
|
|
||||||
"Invalid blink frequency. Please specify a value between 2 and 10000."
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
app_data = struct.pack("!I", blink_freq)
|
|
||||||
q.add_pus_tc(PusTelecommand(service=8, subservice=1, app_data=app_data))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
add_colorlog_console_logger(_LOGGER)
|
|
||||||
tmtccmd.init_printout(False)
|
|
||||||
hook_obj = SatRsConfigHook(json_cfg_path=default_json_path())
|
|
||||||
parser_wrapper = PreArgsParsingWrapper()
|
|
||||||
parser_wrapper.create_default_parent_parser()
|
|
||||||
parser_wrapper.create_default_parser()
|
|
||||||
parser_wrapper.add_def_proc_args()
|
|
||||||
params = SetupParams()
|
|
||||||
post_args_wrapper = parser_wrapper.parse(hook_obj, params)
|
|
||||||
proc_wrapper = ProcedureParamsWrapper()
|
|
||||||
if post_args_wrapper.use_gui:
|
|
||||||
post_args_wrapper.set_params_without_prompts(proc_wrapper)
|
|
||||||
else:
|
|
||||||
post_args_wrapper.set_params_with_prompts(proc_wrapper)
|
|
||||||
params.apid = EXAMPLE_PUS_APID
|
|
||||||
setup_args = SetupWrapper(
|
|
||||||
hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_wrapper
|
|
||||||
)
|
|
||||||
# Create console logger helper and file loggers
|
|
||||||
tmtc_logger = RegularTmtcLogWrapper()
|
|
||||||
file_logger = tmtc_logger.logger
|
|
||||||
raw_logger = RawTmtcTimedLogWrapper(when=TimedLogWhen.PER_HOUR, interval=1)
|
|
||||||
verificator = PusVerificator()
|
|
||||||
verification_wrapper = VerificationWrapper(verificator, _LOGGER, file_logger)
|
|
||||||
# Create primary TM handler and add it to the CCSDS Packet Handler
|
|
||||||
tm_handler = PusHandler(file_logger, verification_wrapper, raw_logger)
|
|
||||||
ccsds_handler = CcsdsTmHandler(generic_handler=None)
|
|
||||||
ccsds_handler.add_apid_handler(tm_handler)
|
|
||||||
|
|
||||||
# Create TC handler
|
|
||||||
seq_count_provider = PusFileSeqCountProvider()
|
|
||||||
tc_handler = TcHandler(seq_count_provider, verification_wrapper)
|
|
||||||
tmtccmd.setup(setup_args=setup_args)
|
|
||||||
init_proc = params_to_procedure_conversion(setup_args.proc_param_wrapper)
|
|
||||||
tmtc_backend = tmtccmd.create_default_tmtc_backend(
|
|
||||||
setup_wrapper=setup_args,
|
|
||||||
tm_handler=ccsds_handler,
|
|
||||||
tc_handler=tc_handler,
|
|
||||||
init_procedure=init_proc,
|
|
||||||
)
|
|
||||||
tmtccmd.start(tmtc_backend=tmtc_backend, hook_obj=hook_obj)
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
state = tmtc_backend.periodic_op(None)
|
|
||||||
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
|
||||||
sys.exit(0)
|
|
||||||
elif state.request == BackendRequest.DELAY_IDLE:
|
|
||||||
_LOGGER.info("TMTC Client in IDLE mode")
|
|
||||||
time.sleep(3.0)
|
|
||||||
elif state.request == BackendRequest.DELAY_LISTENER:
|
|
||||||
time.sleep(0.8)
|
|
||||||
elif state.request == BackendRequest.DELAY_CUSTOM:
|
|
||||||
if state.next_delay.total_seconds() <= 0.4:
|
|
||||||
time.sleep(state.next_delay.total_seconds())
|
|
||||||
else:
|
|
||||||
time.sleep(0.4)
|
|
||||||
elif state.request == BackendRequest.CALL_NEXT:
|
|
||||||
pass
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
tmtccmd == 8.0.1
|
|
||||||
# -e git+https://github.com/robamu-org/tmtccmd.git@main#egg=tmtccmd
|
|
||||||
@@ -1,76 +1,61 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use satrs_stm32f3_disco_rtic as _;
|
#![no_std]
|
||||||
|
|
||||||
use stm32f3_discovery::leds::Leds;
|
use panic_probe as _;
|
||||||
use stm32f3_discovery::stm32f3xx_hal::delay::Delay;
|
use rtic::app;
|
||||||
use stm32f3_discovery::stm32f3xx_hal::{pac, prelude::*};
|
|
||||||
use stm32f3_discovery::switch_hal::{OutputSwitch, ToggleableOutputSwitch};
|
|
||||||
|
|
||||||
#[cortex_m_rt::entry]
|
#[app(device = embassy_stm32)]
|
||||||
fn main() -> ! {
|
mod app {
|
||||||
defmt::println!("STM32F3 Discovery Blinky");
|
use rtic_monotonics::fugit::ExtU32;
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
use rtic_monotonics::Monotonic as _;
|
||||||
let mut rcc = dp.RCC.constrain();
|
use satrs_stm32f3_disco_rtic::{Direction, LedPinSet, Leds};
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
|
||||||
let mut flash = dp.FLASH.constrain();
|
|
||||||
let clocks = rcc.cfgr.freeze(&mut flash.acr);
|
|
||||||
let mut delay = Delay::new(cp.SYST, clocks);
|
|
||||||
|
|
||||||
let mut gpioe = dp.GPIOE.split(&mut rcc.ahb);
|
rtic_monotonics::systick_monotonic!(Mono, 1000);
|
||||||
let mut leds = Leds::new(
|
|
||||||
gpioe.pe8,
|
|
||||||
gpioe.pe9,
|
|
||||||
gpioe.pe10,
|
|
||||||
gpioe.pe11,
|
|
||||||
gpioe.pe12,
|
|
||||||
gpioe.pe13,
|
|
||||||
gpioe.pe14,
|
|
||||||
gpioe.pe15,
|
|
||||||
&mut gpioe.moder,
|
|
||||||
&mut gpioe.otyper,
|
|
||||||
);
|
|
||||||
let delay_ms = 200u16;
|
|
||||||
loop {
|
|
||||||
leds.ld3_n.toggle().ok();
|
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
leds.ld3_n.toggle().ok();
|
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
|
|
||||||
//explicit on/off
|
#[shared]
|
||||||
leds.ld4_nw.on().ok();
|
struct Shared {}
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
leds.ld4_nw.off().ok();
|
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
|
|
||||||
leds.ld5_ne.on().ok();
|
#[local]
|
||||||
delay.delay_ms(delay_ms);
|
struct Local {
|
||||||
leds.ld5_ne.off().ok();
|
leds: Leds,
|
||||||
delay.delay_ms(delay_ms);
|
current_dir: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
leds.ld6_w.on().ok();
|
#[init]
|
||||||
delay.delay_ms(delay_ms);
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
leds.ld6_w.off().ok();
|
let p = embassy_stm32::init(Default::default());
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
|
|
||||||
leds.ld7_e.on().ok();
|
defmt::info!("Starting sat-rs demo application for the STM32F3-Discovery using RTICv2");
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
leds.ld7_e.off().ok();
|
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
|
|
||||||
leds.ld8_sw.on().ok();
|
let led_pin_set = LedPinSet {
|
||||||
delay.delay_ms(delay_ms);
|
pin_n: p.PE8,
|
||||||
leds.ld8_sw.off().ok();
|
pin_ne: p.PE9,
|
||||||
delay.delay_ms(delay_ms);
|
pin_e: p.PE10,
|
||||||
|
pin_se: p.PE11,
|
||||||
|
pin_s: p.PE12,
|
||||||
|
pin_sw: p.PE13,
|
||||||
|
pin_w: p.PE14,
|
||||||
|
pin_nw: p.PE15,
|
||||||
|
};
|
||||||
|
let leds = Leds::new(led_pin_set);
|
||||||
|
|
||||||
leds.ld9_se.on().ok();
|
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||||
delay.delay_ms(delay_ms);
|
Mono::start(cx.core.SYST, 8_000_000);
|
||||||
leds.ld9_se.off().ok();
|
blinky::spawn().expect("failed to spawn blinky task");
|
||||||
delay.delay_ms(delay_ms);
|
(
|
||||||
|
Shared {},
|
||||||
|
Local {
|
||||||
|
leds,
|
||||||
|
current_dir: Direction::North,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
leds.ld10_s.on().ok();
|
#[task(local = [leds, current_dir])]
|
||||||
delay.delay_ms(delay_ms);
|
async fn blinky(cx: blinky::Context) {
|
||||||
leds.ld10_s.off().ok();
|
loop {
|
||||||
delay.delay_ms(delay_ms);
|
cx.local.leds.blink_next(cx.local.current_dir);
|
||||||
|
Mono::delay(200.millis()).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,190 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_semihosting::debug;
|
use arbitrary_int::u11;
|
||||||
|
use core::time::Duration;
|
||||||
|
use embassy_stm32::gpio::Output;
|
||||||
|
use spacepackets::{
|
||||||
|
ccsds_packet_len_for_user_data_len_with_checksum, CcsdsPacketCreationError,
|
||||||
|
CcsdsPacketCreatorWithReservedData, CcsdsPacketIdAndPsc, SpacePacketHeader,
|
||||||
|
};
|
||||||
|
|
||||||
use defmt_brtt as _; // global logger
|
pub const APID: u11 = u11::new(0x02);
|
||||||
|
|
||||||
use stm32f3xx_hal as _; // memory layout
|
#[derive(defmt::Format, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum Direction {
|
||||||
use panic_probe as _;
|
North,
|
||||||
|
NorthEast,
|
||||||
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
East,
|
||||||
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
SouthEast,
|
||||||
#[defmt::panic_handler]
|
South,
|
||||||
fn panic() -> ! {
|
SouthWest,
|
||||||
cortex_m::asm::udf()
|
West,
|
||||||
|
NorthWest,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Terminates the application and makes a semihosting-capable debug tool exit
|
impl Direction {
|
||||||
/// with status code 0.
|
pub fn switch_to_next(&mut self) -> (Self, Self) {
|
||||||
pub fn exit() -> ! {
|
let curr = *self;
|
||||||
loop {
|
*self = match self {
|
||||||
debug::exit(debug::EXIT_SUCCESS);
|
Direction::North => Direction::NorthEast,
|
||||||
|
Direction::NorthEast => Direction::East,
|
||||||
|
Direction::East => Direction::SouthEast,
|
||||||
|
Direction::SouthEast => Direction::South,
|
||||||
|
Direction::South => Direction::SouthWest,
|
||||||
|
Direction::SouthWest => Direction::West,
|
||||||
|
Direction::West => Direction::NorthWest,
|
||||||
|
Direction::NorthWest => Direction::North,
|
||||||
|
};
|
||||||
|
(curr, *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hardfault handler.
|
#[derive(Copy, Clone, Debug, defmt::Format, serde::Serialize, serde::Deserialize)]
|
||||||
///
|
pub enum Request {
|
||||||
/// Terminates the application and makes a semihosting-capable debug tool exit
|
Ping,
|
||||||
/// with an error. This seems better than the default, which is to spin in a
|
ChangeBlinkFrequency(Duration),
|
||||||
/// loop.
|
}
|
||||||
#[cortex_m_rt::exception]
|
|
||||||
unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
|
#[derive(Debug, defmt::Format, serde::Serialize, serde::Deserialize)]
|
||||||
loop {
|
pub struct TmHeader {
|
||||||
debug::exit(debug::EXIT_FAILURE);
|
pub tc_packet_id: Option<CcsdsPacketIdAndPsc>,
|
||||||
|
pub uptime_millis: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, defmt::Format, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum Response {
|
||||||
|
CommandDone,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tm_size(tm_header: &TmHeader, response: &Response) -> usize {
|
||||||
|
ccsds_packet_len_for_user_data_len_with_checksum(
|
||||||
|
postcard::experimental::serialized_size(tm_header).unwrap()
|
||||||
|
+ postcard::experimental::serialized_size(response).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_tm_packet(
|
||||||
|
buf: &mut [u8],
|
||||||
|
sp_header: SpacePacketHeader,
|
||||||
|
tm_header: TmHeader,
|
||||||
|
response: Response,
|
||||||
|
) -> Result<usize, CcsdsPacketCreationError> {
|
||||||
|
let packet_data_size = postcard::experimental::serialized_size(&tm_header).unwrap()
|
||||||
|
+ postcard::experimental::serialized_size(&response).unwrap();
|
||||||
|
let mut creator =
|
||||||
|
CcsdsPacketCreatorWithReservedData::new_tm_with_checksum(sp_header, packet_data_size, buf)?;
|
||||||
|
|
||||||
|
let current_index = postcard::to_slice(&tm_header, creator.packet_data_mut())
|
||||||
|
.unwrap()
|
||||||
|
.len();
|
||||||
|
postcard::to_slice(&response, &mut creator.packet_data_mut()[current_index..]).unwrap();
|
||||||
|
Ok(creator.finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Leds {
|
||||||
|
pub north: Output<'static>,
|
||||||
|
pub north_east: Output<'static>,
|
||||||
|
pub east: Output<'static>,
|
||||||
|
pub south_east: Output<'static>,
|
||||||
|
pub south: Output<'static>,
|
||||||
|
pub south_west: Output<'static>,
|
||||||
|
pub west: Output<'static>,
|
||||||
|
pub north_west: Output<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Leds {
|
||||||
|
pub fn blink_next(&mut self, current_dir: &mut Direction) {
|
||||||
|
let (prev, curr) = current_dir.switch_to_next();
|
||||||
|
self.set_dir_low(prev);
|
||||||
|
self.set_dir_high(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_dir(&mut self, dir: Direction, level: embassy_stm32::gpio::Level) {
|
||||||
|
match dir {
|
||||||
|
Direction::North => self.north.set_level(level),
|
||||||
|
Direction::NorthEast => self.north_east.set_level(level),
|
||||||
|
Direction::East => self.east.set_level(level),
|
||||||
|
Direction::SouthEast => self.south_east.set_level(level),
|
||||||
|
Direction::South => self.south.set_level(level),
|
||||||
|
Direction::SouthWest => self.south_west.set_level(level),
|
||||||
|
Direction::West => self.west.set_level(level),
|
||||||
|
Direction::NorthWest => self.north_west.set_level(level),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_dir_low(&mut self, dir: Direction) {
|
||||||
|
self.set_dir(dir, embassy_stm32::gpio::Level::Low);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_dir_high(&mut self, dir: Direction) {
|
||||||
|
self.set_dir(dir, embassy_stm32::gpio::Level::High);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used
|
pub struct LedPinSet {
|
||||||
// once within a crate. the module can be in any file but there can only be at most
|
pub pin_n: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE8>,
|
||||||
// one `#[tests]` module in this library crate
|
pub pin_ne: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE9>,
|
||||||
#[cfg(test)]
|
pub pin_e: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE10>,
|
||||||
#[defmt_test::tests]
|
pub pin_se: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE11>,
|
||||||
mod unit_tests {
|
pub pin_s: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE12>,
|
||||||
use defmt::assert;
|
pub pin_sw: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE13>,
|
||||||
|
pub pin_w: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE14>,
|
||||||
|
pub pin_nw: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE15>,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
impl Leds {
|
||||||
fn it_works() {
|
pub fn new(pin_set: LedPinSet) -> Self {
|
||||||
assert!(true)
|
let led_n = Output::new(
|
||||||
|
pin_set.pin_n,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
let led_ne = Output::new(
|
||||||
|
pin_set.pin_ne,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
let led_e = Output::new(
|
||||||
|
pin_set.pin_e,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
let led_se = Output::new(
|
||||||
|
pin_set.pin_se,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
let led_s = Output::new(
|
||||||
|
pin_set.pin_s,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
let led_sw = Output::new(
|
||||||
|
pin_set.pin_sw,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
let led_w = Output::new(
|
||||||
|
pin_set.pin_w,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
let led_nw = Output::new(
|
||||||
|
pin_set.pin_nw,
|
||||||
|
embassy_stm32::gpio::Level::Low,
|
||||||
|
embassy_stm32::gpio::Speed::Medium,
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
north: led_n,
|
||||||
|
north_east: led_ne,
|
||||||
|
east: led_e,
|
||||||
|
south_east: led_se,
|
||||||
|
south: led_s,
|
||||||
|
south_west: led_sw,
|
||||||
|
west: led_w,
|
||||||
|
north_west: led_nw,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,682 +1,349 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use satrs::pus::verification::{
|
use arbitrary_int::{u11, u14};
|
||||||
FailParams, TcStateAccepted, VerificationReportCreator, VerificationToken,
|
use cortex_m_semihosting::debug::{self, EXIT_FAILURE, EXIT_SUCCESS};
|
||||||
};
|
use satrs_stm32f3_disco_rtic::{create_tm_packet, tm_size, CcsdsPacketId, Request, Response};
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use spacepackets::{CcsdsPacketCreationError, SpHeader};
|
||||||
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
|
||||||
use satrs::spacepackets::ecss::EcssEnumU16;
|
use defmt_rtt as _; // global logger
|
||||||
use satrs::spacepackets::CcsdsPacket;
|
|
||||||
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
use panic_probe as _;
|
||||||
// global logger + panicking-behavior + memory layout
|
|
||||||
use satrs_stm32f3_disco_rtic as _;
|
|
||||||
|
|
||||||
use rtic::app;
|
use rtic::app;
|
||||||
|
|
||||||
use heapless::{mpmc::Q8, Vec};
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32};
|
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32};
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
use satrs::seq_count::SequenceCountProviderCore;
|
|
||||||
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
|
use crate::app::Mono;
|
||||||
use stm32f3xx_hal::dma::dma1;
|
|
||||||
use stm32f3xx_hal::gpio::{PushPull, AF7, PA2, PA3};
|
|
||||||
use stm32f3xx_hal::pac::USART2;
|
|
||||||
use stm32f3xx_hal::serial::{Rx, RxEvent, Serial, SerialDmaRx, SerialDmaTx, Tx, TxEvent};
|
|
||||||
|
|
||||||
const UART_BAUD: u32 = 115200;
|
const UART_BAUD: u32 = 115200;
|
||||||
const DEFAULT_BLINK_FREQ_MS: u32 = 1000;
|
const DEFAULT_BLINK_FREQ_MS: u32 = 1000;
|
||||||
const TX_HANDLER_FREQ_MS: u32 = 20;
|
const TX_HANDLER_FREQ_MS: u32 = 20;
|
||||||
const MIN_DELAY_BETWEEN_TX_PACKETS_MS: u32 = 5;
|
|
||||||
const MAX_TC_LEN: usize = 128;
|
const MAX_TC_LEN: usize = 128;
|
||||||
const MAX_TM_LEN: usize = 128;
|
const MAX_TM_LEN: usize = 128;
|
||||||
pub const PUS_APID: u16 = 0x02;
|
|
||||||
|
|
||||||
type TxType = Tx<USART2, PA2<AF7<PushPull>>>;
|
pub const PUS_APID: u11 = u11::new(0x02);
|
||||||
type RxType = Rx<USART2, PA3<AF7<PushPull>>>;
|
|
||||||
type InstantFugit = TimerInstantU32<1000>;
|
|
||||||
type TxDmaTransferType = SerialDmaTx<&'static [u8], dma1::C7, TxType>;
|
|
||||||
type RxDmaTransferType = SerialDmaRx<&'static mut [u8], dma1::C6, RxType>;
|
|
||||||
|
|
||||||
// This is the predictable maximum overhead of the COBS encoding scheme.
|
// This is the predictable maximum overhead of the COBS encoding scheme.
|
||||||
// It is simply the maximum packet lenght dividied by 254 rounded up.
|
// It is simply the maximum packet lenght dividied by 254 rounded up.
|
||||||
const COBS_TC_OVERHEAD: usize = (MAX_TC_LEN + 254 - 1) / 254;
|
const COBS_TM_OVERHEAD: usize = cobs::max_encoding_overhead(MAX_TM_LEN);
|
||||||
const COBS_TM_OVERHEAD: usize = (MAX_TM_LEN + 254 - 1) / 254;
|
|
||||||
|
|
||||||
const TC_BUF_LEN: usize = MAX_TC_LEN + COBS_TC_OVERHEAD;
|
|
||||||
const TM_BUF_LEN: usize = MAX_TC_LEN + COBS_TM_OVERHEAD;
|
const TM_BUF_LEN: usize = MAX_TC_LEN + COBS_TM_OVERHEAD;
|
||||||
|
|
||||||
// This is a static buffer which should ONLY (!) be used as the TX DMA
|
const TC_DMA_BUF_LEN: usize = 512;
|
||||||
// transfer buffer.
|
|
||||||
static mut DMA_TX_BUF: [u8; TM_BUF_LEN] = [0; TM_BUF_LEN];
|
|
||||||
// This is a static buffer which should ONLY (!) be used as the RX DMA
|
|
||||||
// transfer buffer.
|
|
||||||
static mut DMA_RX_BUF: [u8; TC_BUF_LEN] = [0; TC_BUF_LEN];
|
|
||||||
|
|
||||||
type TmPacket = Vec<u8, MAX_TM_LEN>;
|
type TmPacket = heapless::Vec<u8, MAX_TM_LEN>;
|
||||||
type TcPacket = Vec<u8, MAX_TC_LEN>;
|
|
||||||
|
|
||||||
static TM_REQUESTS: Q8<TmPacket> = Q8::new();
|
static TM_QUEUE: heapless::mpmc::Queue<TmPacket, 16> = heapless::mpmc::Queue::new();
|
||||||
|
|
||||||
use core::sync::atomic::{AtomicU16, Ordering};
|
#[derive(Debug, defmt::Format, thiserror::Error)]
|
||||||
|
|
||||||
pub struct SeqCountProviderAtomicRef {
|
|
||||||
atomic: AtomicU16,
|
|
||||||
ordering: Ordering,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SeqCountProviderAtomicRef {
|
|
||||||
pub const fn new(ordering: Ordering) -> Self {
|
|
||||||
Self {
|
|
||||||
atomic: AtomicU16::new(0),
|
|
||||||
ordering,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SequenceCountProviderCore<u16> for SeqCountProviderAtomicRef {
|
|
||||||
fn get(&self) -> u16 {
|
|
||||||
self.atomic.load(self.ordering)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn increment(&self) {
|
|
||||||
self.atomic.fetch_add(1, self.ordering);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_and_increment(&self) -> u16 {
|
|
||||||
self.atomic.fetch_add(1, self.ordering)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static SEQ_COUNT_PROVIDER: SeqCountProviderAtomicRef =
|
|
||||||
SeqCountProviderAtomicRef::new(Ordering::Relaxed);
|
|
||||||
|
|
||||||
pub struct TxIdle {
|
|
||||||
tx: TxType,
|
|
||||||
dma_channel: dma1::C7,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, defmt::Format)]
|
|
||||||
pub enum TmSendError {
|
pub enum TmSendError {
|
||||||
ByteConversion(ByteConversionError),
|
#[error("packet creation error: {0}")]
|
||||||
|
PacketCreation(#[from] CcsdsPacketCreationError),
|
||||||
|
#[error("queue error")]
|
||||||
Queue,
|
Queue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ByteConversionError> for TmSendError {
|
|
||||||
fn from(value: ByteConversionError) -> Self {
|
|
||||||
Self::ByteConversion(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_tm(tm_creator: PusTmCreator) -> Result<(), TmSendError> {
|
|
||||||
if tm_creator.len_written() > MAX_TM_LEN {
|
|
||||||
return Err(ByteConversionError::ToSliceTooSmall {
|
|
||||||
expected: tm_creator.len_written(),
|
|
||||||
found: MAX_TM_LEN,
|
|
||||||
}
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
let mut tm_vec = TmPacket::new();
|
|
||||||
tm_vec
|
|
||||||
.resize(tm_creator.len_written(), 0)
|
|
||||||
.expect("vec resize failed");
|
|
||||||
tm_creator.write_to_bytes(tm_vec.as_mut_slice())?;
|
|
||||||
defmt::info!(
|
|
||||||
"Sending TM[{},{}] with size {}",
|
|
||||||
tm_creator.service(),
|
|
||||||
tm_creator.subservice(),
|
|
||||||
tm_creator.len_written()
|
|
||||||
);
|
|
||||||
TM_REQUESTS
|
|
||||||
.enqueue(tm_vec)
|
|
||||||
.map_err(|_| TmSendError::Queue)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_tm_send_error(error: TmSendError) {
|
|
||||||
defmt::warn!("sending tm failed with error {}", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum UartTxState {
|
|
||||||
// Wrapped in an option because we need an owned type later.
|
|
||||||
Idle(Option<TxIdle>),
|
|
||||||
// Same as above
|
|
||||||
Transmitting(Option<TxDmaTransferType>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UartTxShared {
|
|
||||||
last_completed: Option<InstantFugit>,
|
|
||||||
state: UartTxState,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RequestWithToken {
|
|
||||||
token: VerificationToken<TcStateAccepted>,
|
|
||||||
request: Request,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, defmt::Format)]
|
#[derive(Debug, defmt::Format)]
|
||||||
pub enum Request {
|
pub struct RequestWithTcId {
|
||||||
Ping,
|
pub request: Request,
|
||||||
ChangeBlinkFrequency(u32),
|
pub tc_id: CcsdsPacketId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, defmt::Format)]
|
#[app(device = embassy_stm32)]
|
||||||
pub enum RequestError {
|
|
||||||
InvalidApid = 1,
|
|
||||||
InvalidService = 2,
|
|
||||||
InvalidSubservice = 3,
|
|
||||||
NotEnoughAppData = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn convert_pus_tc_to_request(
|
|
||||||
tc: &PusTcReader,
|
|
||||||
verif_reporter: &mut VerificationReportCreator,
|
|
||||||
src_data_buf: &mut [u8],
|
|
||||||
timestamp: &[u8],
|
|
||||||
) -> Result<RequestWithToken, RequestError> {
|
|
||||||
defmt::info!(
|
|
||||||
"Found PUS TC [{},{}] with length {}",
|
|
||||||
tc.service(),
|
|
||||||
tc.subservice(),
|
|
||||||
tc.len_packed()
|
|
||||||
);
|
|
||||||
|
|
||||||
let token = verif_reporter.add_tc(tc);
|
|
||||||
if tc.apid() != PUS_APID {
|
|
||||||
defmt::warn!("Received tc with unknown APID {}", tc.apid());
|
|
||||||
let result = send_tm(
|
|
||||||
verif_reporter
|
|
||||||
.acceptance_failure(
|
|
||||||
src_data_buf,
|
|
||||||
token,
|
|
||||||
SEQ_COUNT_PROVIDER.get_and_increment(),
|
|
||||||
0,
|
|
||||||
FailParams::new(timestamp, &EcssEnumU16::new(0), &[]),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
if let Err(e) = result {
|
|
||||||
handle_tm_send_error(e);
|
|
||||||
}
|
|
||||||
return Err(RequestError::InvalidApid);
|
|
||||||
}
|
|
||||||
let (tm_creator, accepted_token) = verif_reporter
|
|
||||||
.acceptance_success(
|
|
||||||
src_data_buf,
|
|
||||||
token,
|
|
||||||
SEQ_COUNT_PROVIDER.get_and_increment(),
|
|
||||||
0,
|
|
||||||
timestamp,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Err(e) = send_tm(tm_creator) {
|
|
||||||
handle_tm_send_error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if tc.service() == 17 && tc.subservice() == 1 {
|
|
||||||
if tc.subservice() == 1 {
|
|
||||||
return Ok(RequestWithToken {
|
|
||||||
request: Request::Ping,
|
|
||||||
token: accepted_token,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Err(RequestError::InvalidSubservice);
|
|
||||||
}
|
|
||||||
} else if tc.service() == 8 {
|
|
||||||
if tc.subservice() == 1 {
|
|
||||||
if tc.user_data().len() < 4 {
|
|
||||||
return Err(RequestError::NotEnoughAppData);
|
|
||||||
}
|
|
||||||
let new_freq_ms = u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap());
|
|
||||||
return Ok(RequestWithToken {
|
|
||||||
request: Request::ChangeBlinkFrequency(new_freq_ms),
|
|
||||||
token: accepted_token,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Err(RequestError::InvalidSubservice);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(RequestError::InvalidService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[app(device = stm32f3xx_hal::pac, peripherals = true)]
|
|
||||||
mod app {
|
mod app {
|
||||||
use super::*;
|
use core::time::Duration;
|
||||||
use core::slice::Iter;
|
|
||||||
use satrs::pus::verification::{TcStateStarted, VerificationReportCreator};
|
|
||||||
use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE};
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use stm32f3_discovery::leds::Direction;
|
|
||||||
use stm32f3_discovery::leds::Leds;
|
|
||||||
use stm32f3xx_hal::prelude::*;
|
|
||||||
|
|
||||||
use stm32f3_discovery::switch_hal::OutputSwitch;
|
use super::*;
|
||||||
use stm32f3xx_hal::Switch;
|
use arbitrary_int::u14;
|
||||||
#[allow(dead_code)]
|
use rtic::Mutex;
|
||||||
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
|
use rtic_sync::{
|
||||||
|
channel::{Receiver, Sender},
|
||||||
|
make_channel,
|
||||||
|
};
|
||||||
|
use satrs_stm32f3_disco_rtic::{CcsdsPacketId, LedPinSet, Request, Response};
|
||||||
|
use spacepackets::CcsdsPacketReader;
|
||||||
|
|
||||||
systick_monotonic!(Mono, 1000);
|
systick_monotonic!(Mono, 1000);
|
||||||
|
|
||||||
|
embassy_stm32::bind_interrupts!(struct Irqs {
|
||||||
|
USART2 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART2>;
|
||||||
|
});
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {
|
struct Shared {
|
||||||
blink_freq: MillisDurationU32,
|
blink_freq: Duration,
|
||||||
tx_shared: UartTxShared,
|
|
||||||
rx_transfer: Option<RxDmaTransferType>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
verif_reporter: VerificationReportCreator,
|
leds: satrs_stm32f3_disco_rtic::Leds,
|
||||||
leds: Leds,
|
current_dir: satrs_stm32f3_disco_rtic::Direction,
|
||||||
last_dir: Direction,
|
seq_count: u14,
|
||||||
curr_dir: Iter<'static, Direction>,
|
tx: embassy_stm32::usart::UartTx<'static, embassy_stm32::mode::Async>,
|
||||||
|
rx: embassy_stm32::usart::RingBufferedUartRx<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
let mut rcc = cx.device.RCC.constrain();
|
static DMA_BUF: static_cell::ConstStaticCell<[u8; TC_DMA_BUF_LEN]> =
|
||||||
|
static_cell::ConstStaticCell::new([0; TC_DMA_BUF_LEN]);
|
||||||
|
|
||||||
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
|
let (req_sender, req_receiver) = make_channel!(RequestWithTcId, 16);
|
||||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||||
Mono::start(cx.core.SYST, 8_000_000);
|
Mono::start(cx.core.SYST, 8_000_000);
|
||||||
|
|
||||||
let mut flash = cx.device.FLASH.constrain();
|
defmt::info!("sat-rs demo application for the STM32F3-Discovery with RTICv2");
|
||||||
let clocks = rcc
|
let led_pin_set = LedPinSet {
|
||||||
.cfgr
|
pin_n: p.PE8,
|
||||||
.use_hse(8.MHz())
|
pin_ne: p.PE9,
|
||||||
.sysclk(8.MHz())
|
pin_e: p.PE10,
|
||||||
.pclk1(8.MHz())
|
pin_se: p.PE11,
|
||||||
.freeze(&mut flash.acr);
|
pin_s: p.PE12,
|
||||||
|
pin_sw: p.PE13,
|
||||||
|
pin_w: p.PE14,
|
||||||
|
pin_nw: p.PE15,
|
||||||
|
};
|
||||||
|
let leds = satrs_stm32f3_disco_rtic::Leds::new(led_pin_set);
|
||||||
|
|
||||||
// Set up monotonic timer.
|
let mut config = embassy_stm32::usart::Config::default();
|
||||||
//let mono_timer = MonoTimer::new(cx.core.DWT, clocks, &mut cx.core.DCB);
|
config.baudrate = UART_BAUD;
|
||||||
|
let uart = embassy_stm32::usart::Uart::new(
|
||||||
|
p.USART2, p.PA3, p.PA2, Irqs, p.DMA1_CH7, p.DMA1_CH6, config,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
defmt::info!("Starting sat-rs demo application for the STM32F3-Discovery");
|
let (tx, rx) = uart.split();
|
||||||
let mut gpioe = cx.device.GPIOE.split(&mut rcc.ahb);
|
|
||||||
|
|
||||||
let leds = Leds::new(
|
|
||||||
gpioe.pe8,
|
|
||||||
gpioe.pe9,
|
|
||||||
gpioe.pe10,
|
|
||||||
gpioe.pe11,
|
|
||||||
gpioe.pe12,
|
|
||||||
gpioe.pe13,
|
|
||||||
gpioe.pe14,
|
|
||||||
gpioe.pe15,
|
|
||||||
&mut gpioe.moder,
|
|
||||||
&mut gpioe.otyper,
|
|
||||||
);
|
|
||||||
let mut gpioa = cx.device.GPIOA.split(&mut rcc.ahb);
|
|
||||||
// USART2 pins
|
|
||||||
let mut pins = (
|
|
||||||
// TX pin: PA2
|
|
||||||
gpioa
|
|
||||||
.pa2
|
|
||||||
.into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl),
|
|
||||||
// RX pin: PA3
|
|
||||||
gpioa
|
|
||||||
.pa3
|
|
||||||
.into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl),
|
|
||||||
);
|
|
||||||
pins.1.internal_pull_up(&mut gpioa.pupdr, true);
|
|
||||||
let mut usart2 = Serial::new(
|
|
||||||
cx.device.USART2,
|
|
||||||
pins,
|
|
||||||
UART_BAUD.Bd(),
|
|
||||||
clocks,
|
|
||||||
&mut rcc.apb1,
|
|
||||||
);
|
|
||||||
usart2.configure_rx_interrupt(RxEvent::Idle, Switch::On);
|
|
||||||
// This interrupt is enabled to re-schedule new transfers in the interrupt handler immediately.
|
|
||||||
usart2.configure_tx_interrupt(TxEvent::TransmissionComplete, Switch::On);
|
|
||||||
|
|
||||||
let dma1 = cx.device.DMA1.split(&mut rcc.ahb);
|
|
||||||
let (mut tx_serial, mut rx_serial) = usart2.split();
|
|
||||||
|
|
||||||
// This interrupt is immediately triggered, clear it. It will only be reset
|
|
||||||
// by the hardware when data is received on RX (RXNE event)
|
|
||||||
rx_serial.clear_event(RxEvent::Idle);
|
|
||||||
// For some reason, this is also immediately triggered..
|
|
||||||
tx_serial.clear_event(TxEvent::TransmissionComplete);
|
|
||||||
let rx_transfer = rx_serial.read_exact(unsafe { DMA_RX_BUF.as_mut_slice() }, dma1.ch6);
|
|
||||||
defmt::info!("Spawning tasks");
|
defmt::info!("Spawning tasks");
|
||||||
blink::spawn().unwrap();
|
blinky::spawn().unwrap();
|
||||||
serial_tx_handler::spawn().unwrap();
|
serial_tx_handler::spawn().unwrap();
|
||||||
|
serial_rx_handler::spawn(req_sender).unwrap();
|
||||||
let verif_reporter = VerificationReportCreator::new(PUS_APID).unwrap();
|
req_handler::spawn(req_receiver).unwrap();
|
||||||
|
|
||||||
(
|
(
|
||||||
Shared {
|
Shared {
|
||||||
blink_freq: MillisDurationU32::from_ticks(DEFAULT_BLINK_FREQ_MS),
|
blink_freq: Duration::from_millis(DEFAULT_BLINK_FREQ_MS as u64),
|
||||||
tx_shared: UartTxShared {
|
|
||||||
last_completed: None,
|
|
||||||
state: UartTxState::Idle(Some(TxIdle {
|
|
||||||
tx: tx_serial,
|
|
||||||
dma_channel: dma1.ch7,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
rx_transfer: Some(rx_transfer),
|
|
||||||
},
|
},
|
||||||
Local {
|
Local {
|
||||||
verif_reporter,
|
|
||||||
leds,
|
leds,
|
||||||
last_dir: Direction::North,
|
tx,
|
||||||
curr_dir: Direction::iter(),
|
seq_count: u14::new(0),
|
||||||
|
rx: rx.into_ring_buffered(DMA_BUF.take()),
|
||||||
|
current_dir: satrs_stm32f3_disco_rtic::Direction::North,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(local = [leds, curr_dir, last_dir], shared=[blink_freq])]
|
#[task(local = [leds, current_dir], shared=[blink_freq])]
|
||||||
async fn blink(mut cx: blink::Context) {
|
async fn blinky(mut cx: blinky::Context) {
|
||||||
let blink::LocalResources {
|
|
||||||
leds,
|
|
||||||
curr_dir,
|
|
||||||
last_dir,
|
|
||||||
..
|
|
||||||
} = cx.local;
|
|
||||||
let mut toggle_leds = |dir: &Direction| {
|
|
||||||
let last_led = leds.for_direction(*last_dir);
|
|
||||||
last_led.off().ok();
|
|
||||||
let led = leds.for_direction(*dir);
|
|
||||||
led.on().ok();
|
|
||||||
*last_dir = *dir;
|
|
||||||
};
|
|
||||||
loop {
|
loop {
|
||||||
match curr_dir.next() {
|
cx.local.leds.blink_next(cx.local.current_dir);
|
||||||
Some(dir) => {
|
|
||||||
toggle_leds(dir);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
*curr_dir = Direction::iter();
|
|
||||||
toggle_leds(curr_dir.next().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
||||||
Mono::delay(current_blink_freq).await;
|
Mono::delay(MillisDurationU32::from_ticks(
|
||||||
|
current_blink_freq.as_millis() as u32,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(
|
#[task(
|
||||||
shared = [tx_shared],
|
local = [
|
||||||
|
tx,
|
||||||
|
encoded_buf: [u8; TM_BUF_LEN] = [0; TM_BUF_LEN]
|
||||||
|
],
|
||||||
|
shared = [],
|
||||||
)]
|
)]
|
||||||
async fn serial_tx_handler(mut cx: serial_tx_handler::Context) {
|
async fn serial_tx_handler(cx: serial_tx_handler::Context) {
|
||||||
loop {
|
loop {
|
||||||
let is_idle = cx.shared.tx_shared.lock(|tx_shared| {
|
while let Some(vec) = TM_QUEUE.dequeue() {
|
||||||
if let UartTxState::Idle(_) = tx_shared.state {
|
let encoded_len =
|
||||||
return true;
|
cobs::encode_including_sentinels(&vec[0..vec.len()], cx.local.encoded_buf);
|
||||||
}
|
defmt::debug!("sending {} bytes over UART", encoded_len);
|
||||||
false
|
cx.local
|
||||||
});
|
.tx
|
||||||
if is_idle {
|
.write(&cx.local.encoded_buf[0..encoded_len])
|
||||||
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
|
.await
|
||||||
if let Some(last_completed) = last_completed {
|
.unwrap();
|
||||||
let elapsed_ms = (Mono::now() - last_completed).to_millis();
|
|
||||||
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
|
|
||||||
Mono::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Check for completion after 1 ms
|
|
||||||
Mono::delay(1.millis()).await;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(vec) = TM_REQUESTS.dequeue() {
|
|
||||||
cx.shared
|
|
||||||
.tx_shared
|
|
||||||
.lock(|tx_shared| match &mut tx_shared.state {
|
|
||||||
UartTxState::Idle(tx) => {
|
|
||||||
let encoded_len;
|
|
||||||
//debug!(target: "serial_tx_handler", "bytes: {:x?}", &buf[0..len]);
|
|
||||||
// Safety: We only copy the data into the TX DMA buffer in this task.
|
|
||||||
// If the DMA is active, another branch will be taken.
|
|
||||||
unsafe {
|
|
||||||
// 0 sentinel value as start marker
|
|
||||||
DMA_TX_BUF[0] = 0;
|
|
||||||
encoded_len =
|
|
||||||
cobs::encode(&vec[0..vec.len()], &mut DMA_TX_BUF[1..]);
|
|
||||||
// Should never panic, we accounted for the overhead.
|
|
||||||
// Write into transfer buffer directly, no need for intermediate
|
|
||||||
// encoding buffer.
|
|
||||||
// 0 end marker
|
|
||||||
DMA_TX_BUF[encoded_len + 1] = 0;
|
|
||||||
}
|
|
||||||
//debug!(target: "serial_tx_handler", "Sending {} bytes", encoded_len + 2);
|
|
||||||
//debug!("sent: {:x?}", &mut_tx_dma_buf[0..encoded_len + 2]);
|
|
||||||
let tx_idle = tx.take().unwrap();
|
|
||||||
// Transfer completion and re-scheduling of new TX transfers will be done
|
|
||||||
// by the IRQ handler.
|
|
||||||
// SAFETY: The DMA is the exclusive writer to the DMA buffer now.
|
|
||||||
let transfer = tx_idle.tx.write_all(
|
|
||||||
unsafe { &DMA_TX_BUF[0..encoded_len + 2] },
|
|
||||||
tx_idle.dma_channel,
|
|
||||||
);
|
|
||||||
tx_shared.state = UartTxState::Transmitting(Some(transfer));
|
|
||||||
// The memory block is automatically returned to the pool when it is dropped.
|
|
||||||
}
|
|
||||||
UartTxState::Transmitting(_) => (),
|
|
||||||
});
|
|
||||||
// Check for completion after 1 ms
|
|
||||||
Mono::delay(1.millis()).await;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Nothing to do, and we are idle.
|
|
||||||
Mono::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
Mono::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(
|
#[task(
|
||||||
local = [
|
local = [
|
||||||
verif_reporter,
|
rx,
|
||||||
|
read_buf: [u8; 128] = [0; 128],
|
||||||
decode_buf: [u8; MAX_TC_LEN] = [0; MAX_TC_LEN],
|
decode_buf: [u8; MAX_TC_LEN] = [0; MAX_TC_LEN],
|
||||||
src_data_buf: [u8; MAX_TM_LEN] = [0; MAX_TM_LEN],
|
|
||||||
timestamp: [u8; 7] = [0; 7],
|
|
||||||
],
|
],
|
||||||
shared = [blink_freq]
|
shared = [blink_freq]
|
||||||
)]
|
)]
|
||||||
async fn serial_rx_handler(
|
async fn serial_rx_handler(
|
||||||
mut cx: serial_rx_handler::Context,
|
cx: serial_rx_handler::Context,
|
||||||
received_packet: Vec<u8, MAX_TC_LEN>,
|
mut sender: Sender<'static, RequestWithTcId, 16>,
|
||||||
) {
|
) {
|
||||||
cx.local.timestamp[0] = P_FIELD_BASE;
|
let mut decoder = cobs::CobsDecoder::new(cx.local.decode_buf);
|
||||||
defmt::info!("Received packet with {} bytes", received_packet.len());
|
loop {
|
||||||
let decode_buf = cx.local.decode_buf;
|
match cx.local.rx.read(cx.local.read_buf).await {
|
||||||
let packet = received_packet.as_slice();
|
Ok(bytes) => {
|
||||||
let mut start_idx = None;
|
defmt::debug!("received {} bytes over UART", bytes);
|
||||||
for (idx, byte) in packet.iter().enumerate() {
|
for byte in cx.local.read_buf[0..bytes].iter() {
|
||||||
if *byte != 0 {
|
match decoder.feed(*byte) {
|
||||||
start_idx = Some(idx);
|
Ok(None) => (),
|
||||||
break;
|
Ok(Some(packet_size)) => {
|
||||||
}
|
match CcsdsPacketReader::new_with_checksum(
|
||||||
}
|
&decoder.dest()[0..packet_size],
|
||||||
if start_idx.is_none() {
|
) {
|
||||||
defmt::warn!("decoding error, can only process cobs encoded frames, data is all 0");
|
Ok(packet) => {
|
||||||
return;
|
let packet_id = packet.packet_id();
|
||||||
}
|
let psc = packet.psc();
|
||||||
let start_idx = start_idx.unwrap();
|
let tc_packet_id = CcsdsPacketId { packet_id, psc };
|
||||||
match cobs::decode(&received_packet.as_slice()[start_idx..], decode_buf) {
|
if let Ok(request) =
|
||||||
Ok(len) => {
|
postcard::from_bytes::<Request>(packet.packet_data())
|
||||||
defmt::info!("Decoded packet length: {}", len);
|
{
|
||||||
let pus_tc = PusTcReader::new(decode_buf);
|
sender
|
||||||
match pus_tc {
|
.send(RequestWithTcId {
|
||||||
Ok((tc, _tc_len)) => {
|
request,
|
||||||
match convert_pus_tc_to_request(
|
tc_id: tc_packet_id,
|
||||||
&tc,
|
})
|
||||||
cx.local.verif_reporter,
|
.await
|
||||||
cx.local.src_data_buf,
|
.unwrap();
|
||||||
cx.local.timestamp,
|
}
|
||||||
) {
|
|
||||||
Ok(request_with_token) => {
|
|
||||||
let started_token = handle_start_verification(
|
|
||||||
request_with_token.token,
|
|
||||||
cx.local.verif_reporter,
|
|
||||||
cx.local.src_data_buf,
|
|
||||||
cx.local.timestamp,
|
|
||||||
);
|
|
||||||
|
|
||||||
match request_with_token.request {
|
|
||||||
Request::Ping => {
|
|
||||||
handle_ping_request(cx.local.timestamp);
|
|
||||||
}
|
}
|
||||||
Request::ChangeBlinkFrequency(new_freq_ms) => {
|
Err(e) => {
|
||||||
defmt::info!("Received blink frequency change request with new frequncy {}", new_freq_ms);
|
defmt::error!("error unpacking ccsds packet: {}", e);
|
||||||
cx.shared.blink_freq.lock(|blink_freq| {
|
|
||||||
*blink_freq =
|
|
||||||
MillisDurationU32::from_ticks(new_freq_ms);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle_completion_verification(
|
|
||||||
started_token,
|
|
||||||
cx.local.verif_reporter,
|
|
||||||
cx.local.src_data_buf,
|
|
||||||
cx.local.timestamp,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// TODO: Error handling: Send verification failure based on request error.
|
defmt::error!("cobs decoding error: {}", e);
|
||||||
defmt::warn!("request error {}", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
}
|
||||||
defmt::warn!("Error unpacking PUS TC: {}", e);
|
Err(e) => {
|
||||||
}
|
defmt::error!("uart read error: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
|
||||||
defmt::warn!("decoding error, can only process cobs encoded frames")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ping_request(timestamp: &[u8]) {
|
#[task(shared = [blink_freq], local = [seq_count])]
|
||||||
defmt::info!("Received PUS ping telecommand, sending ping reply TM[17,2]");
|
async fn req_handler(
|
||||||
let sp_header =
|
mut cx: req_handler::Context,
|
||||||
SpHeader::new_for_unseg_tc(PUS_APID, SEQ_COUNT_PROVIDER.get_and_increment(), 0);
|
mut receiver: Receiver<'static, RequestWithTcId, 16>,
|
||||||
let sec_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
|
|
||||||
let ping_reply = PusTmCreator::new(sp_header, sec_header, &[], true);
|
|
||||||
let mut tm_packet = TmPacket::new();
|
|
||||||
tm_packet
|
|
||||||
.resize(ping_reply.len_written(), 0)
|
|
||||||
.expect("vec resize failed");
|
|
||||||
ping_reply.write_to_bytes(&mut tm_packet).unwrap();
|
|
||||||
if TM_REQUESTS.enqueue(tm_packet).is_err() {
|
|
||||||
defmt::warn!("TC queue full");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_start_verification(
|
|
||||||
accepted_token: VerificationToken<TcStateAccepted>,
|
|
||||||
verif_reporter: &mut VerificationReportCreator,
|
|
||||||
src_data_buf: &mut [u8],
|
|
||||||
timestamp: &[u8],
|
|
||||||
) -> VerificationToken<TcStateStarted> {
|
|
||||||
let (tm_creator, started_token) = verif_reporter
|
|
||||||
.start_success(
|
|
||||||
src_data_buf,
|
|
||||||
accepted_token,
|
|
||||||
SEQ_COUNT_PROVIDER.get(),
|
|
||||||
0,
|
|
||||||
×tamp,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let result = send_tm(tm_creator);
|
|
||||||
if let Err(e) = result {
|
|
||||||
handle_tm_send_error(e);
|
|
||||||
}
|
|
||||||
started_token
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_completion_verification(
|
|
||||||
started_token: VerificationToken<TcStateStarted>,
|
|
||||||
verif_reporter: &mut VerificationReportCreator,
|
|
||||||
src_data_buf: &mut [u8],
|
|
||||||
timestamp: &[u8],
|
|
||||||
) {
|
) {
|
||||||
let result = send_tm(
|
loop {
|
||||||
verif_reporter
|
match receiver.recv().await {
|
||||||
.completion_success(
|
Ok(request_with_tc_id) => {
|
||||||
src_data_buf,
|
let tm_send_result = match request_with_tc_id.request {
|
||||||
started_token,
|
Request::Ping => handle_ping_request(&mut cx, request_with_tc_id.tc_id),
|
||||||
SEQ_COUNT_PROVIDER.get(),
|
Request::ChangeBlinkFrequency(duration) => {
|
||||||
0,
|
handle_change_blink_frequency_request(
|
||||||
timestamp,
|
&mut cx,
|
||||||
)
|
request_with_tc_id.tc_id,
|
||||||
.unwrap(),
|
duration,
|
||||||
);
|
)
|
||||||
if let Err(e) = result {
|
}
|
||||||
handle_tm_send_error(e);
|
};
|
||||||
|
if let Err(e) = tm_send_result {
|
||||||
|
defmt::error!("error sending TM response: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_e) => defmt::error!("request receive error"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = DMA1_CH6, shared = [rx_transfer])]
|
fn handle_ping_request(
|
||||||
fn rx_dma_isr(mut cx: rx_dma_isr::Context) {
|
cx: &mut req_handler::Context,
|
||||||
let mut tc_packet = TcPacket::new();
|
tc_packet_id: CcsdsPacketId,
|
||||||
cx.shared.rx_transfer.lock(|rx_transfer| {
|
) -> Result<(), TmSendError> {
|
||||||
let rx_ref = rx_transfer.as_ref().unwrap();
|
defmt::info!("Received PUS ping telecommand, sending ping reply");
|
||||||
if rx_ref.is_complete() {
|
send_tm(tc_packet_id, Response::CommandDone, *cx.local.seq_count)?;
|
||||||
let uart_rx_owned = rx_transfer.take().unwrap();
|
*cx.local.seq_count = cx.local.seq_count.wrapping_add(u14::new(1));
|
||||||
let (buf, c, rx) = uart_rx_owned.stop();
|
Ok(())
|
||||||
// The received data is transferred to another task now to avoid any processing overhead
|
|
||||||
// during the interrupt. There are multiple ways to do this, we use a stack allocaed vector here
|
|
||||||
// to do this.
|
|
||||||
tc_packet.resize(buf.len(), 0).expect("vec resize failed");
|
|
||||||
tc_packet.copy_from_slice(buf);
|
|
||||||
|
|
||||||
// Start the next transfer as soon as possible.
|
|
||||||
*rx_transfer = Some(rx.read_exact(buf, c));
|
|
||||||
|
|
||||||
// Send the vector to a regular task.
|
|
||||||
serial_rx_handler::spawn(tc_packet).expect("spawning rx handler task failed");
|
|
||||||
// If this happens, there is a high chance that the maximum packet length was
|
|
||||||
// exceeded. Circular mode is not used here, so data might be missed.
|
|
||||||
defmt::warn!(
|
|
||||||
"rx transfer with maximum length {}, might miss data",
|
|
||||||
TC_BUF_LEN
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = USART2_EXTI26, shared = [rx_transfer, tx_shared])]
|
fn handle_change_blink_frequency_request(
|
||||||
fn serial_isr(mut cx: serial_isr::Context) {
|
cx: &mut req_handler::Context,
|
||||||
|
tc_packet_id: CcsdsPacketId,
|
||||||
|
duration: Duration,
|
||||||
|
) -> Result<(), TmSendError> {
|
||||||
|
defmt::info!(
|
||||||
|
"Received ChangeBlinkFrequency request, new frequency: {} ms",
|
||||||
|
duration.as_millis()
|
||||||
|
);
|
||||||
cx.shared
|
cx.shared
|
||||||
.tx_shared
|
.blink_freq
|
||||||
.lock(|tx_shared| match &mut tx_shared.state {
|
.lock(|blink_freq| *blink_freq = duration);
|
||||||
UartTxState::Idle(_) => (),
|
send_tm(tc_packet_id, Response::CommandDone, *cx.local.seq_count)?;
|
||||||
UartTxState::Transmitting(transfer) => {
|
*cx.local.seq_count = cx.local.seq_count.wrapping_add(u14::new(1));
|
||||||
let transfer_ref = transfer.as_ref().unwrap();
|
Ok(())
|
||||||
if transfer_ref.is_complete() {
|
}
|
||||||
let transfer = transfer.take().unwrap();
|
}
|
||||||
let (_, dma_channel, mut tx) = transfer.stop();
|
|
||||||
tx.clear_event(TxEvent::TransmissionComplete);
|
fn send_tm(
|
||||||
tx_shared.state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
|
tc_packet_id: CcsdsPacketId,
|
||||||
// We cache the last completed time to ensure that there is a minimum delay between consecutive
|
response: Response,
|
||||||
// transferred packets.
|
current_seq_count: u14,
|
||||||
tx_shared.last_completed = Some(Mono::now());
|
) -> Result<(), TmSendError> {
|
||||||
}
|
let sp_header = SpHeader::new_for_unseg_tc(PUS_APID, current_seq_count, 0);
|
||||||
}
|
let tm_header = satrs_stm32f3_disco_rtic::TmHeader {
|
||||||
});
|
tc_packet_id: Some(tc_packet_id),
|
||||||
let mut tc_packet = TcPacket::new();
|
uptime_millis: Mono::now().duration_since_epoch().to_millis(),
|
||||||
cx.shared.rx_transfer.lock(|rx_transfer| {
|
};
|
||||||
let rx_transfer_ref = rx_transfer.as_ref().unwrap();
|
let mut tm_packet = TmPacket::new();
|
||||||
// Received a partial packet.
|
let tm_size = tm_size(&tm_header, &response);
|
||||||
if rx_transfer_ref.is_event_triggered(RxEvent::Idle) {
|
tm_packet.resize(tm_size, 0).expect("vec resize failed");
|
||||||
let rx_transfer_owned = rx_transfer.take().unwrap();
|
create_tm_packet(&mut tm_packet, sp_header, tm_header, response)?;
|
||||||
let (buf, ch, mut rx, rx_len) = rx_transfer_owned.stop_and_return_received_bytes();
|
if TM_QUEUE.enqueue(tm_packet).is_err() {
|
||||||
// The received data is transferred to another task now to avoid any processing overhead
|
defmt::warn!("TC queue full");
|
||||||
// during the interrupt. There are multiple ways to do this, we use a stack
|
return Err(TmSendError::Queue);
|
||||||
// allocated vector to do this.
|
}
|
||||||
tc_packet
|
Ok(())
|
||||||
.resize(rx_len as usize, 0)
|
}
|
||||||
.expect("vec resize failed");
|
|
||||||
tc_packet[0..rx_len as usize].copy_from_slice(&buf[0..rx_len as usize]);
|
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||||
rx.clear_event(RxEvent::Idle);
|
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||||
serial_rx_handler::spawn(tc_packet).expect("spawning rx handler failed");
|
#[defmt::panic_handler]
|
||||||
*rx_transfer = Some(rx.read_exact(buf, ch));
|
fn panic() -> ! {
|
||||||
}
|
cortex_m::asm::udf()
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/// Terminates the application and makes a semihosting-capable debug tool exit
|
||||||
|
/// with status code 0.
|
||||||
|
pub fn exit() -> ! {
|
||||||
|
loop {
|
||||||
|
debug::exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardfault handler.
|
||||||
|
///
|
||||||
|
/// Terminates the application and makes a semihosting-capable debug tool exit
|
||||||
|
/// with an error. This seems better than the default, which is to spin in a
|
||||||
|
/// loop.
|
||||||
|
#[cortex_m_rt::exception]
|
||||||
|
unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||||
|
loop {
|
||||||
|
debug::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used
|
||||||
|
// once within a crate. the module can be in any file but there can only be at most
|
||||||
|
// one `#[tests]` module in this library crate
|
||||||
|
#[cfg(test)]
|
||||||
|
#[defmt_test::tests]
|
||||||
|
mod unit_tests {
|
||||||
|
use defmt::assert;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert!(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
641
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
641
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -16,16 +16,17 @@ harness = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
defmt = "0.3"
|
defmt = "1"
|
||||||
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
panic-probe = { version = "1", features = ["print-defmt"] }
|
||||||
cortex-m-semihosting = "0.5.0"
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
# TODO: Replace with embassy-hal.
|
||||||
stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] }
|
stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] }
|
||||||
embedded-alloc = "0.6"
|
embedded-alloc = "0.6"
|
||||||
rtic-sync = { version = "1", features = ["defmt-03"] }
|
rtic-sync = { version = "1", features = ["defmt-03"] }
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.11"
|
version = "0.12"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"]
|
features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"]
|
||||||
|
|
||||||
@@ -39,12 +40,12 @@ features = ["cortex-m-systick"]
|
|||||||
|
|
||||||
[dependencies.satrs]
|
[dependencies.satrs]
|
||||||
path = "../../satrs"
|
path = "../../satrs"
|
||||||
version = "0.2"
|
# version = "0.2"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["defmt", "heapless"]
|
features = ["defmt", "heapless"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
defmt-test = "0.3"
|
defmt-test = "0.4"
|
||||||
|
|
||||||
# cargo build/run
|
# cargo build/run
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
8
experiments/satrs-gen/Cargo.toml
Normal file
8
experiments/satrs-gen/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "satrs-gen"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
toml = "0.8"
|
||||||
|
heck = "0.5"
|
||||||
34
experiments/satrs-gen/components.toml
Normal file
34
experiments/satrs-gen/components.toml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[apid]
|
||||||
|
Sched = 1
|
||||||
|
GenericPus = 2
|
||||||
|
Acs = 3
|
||||||
|
Cfdp = 4
|
||||||
|
Tmtc = 5
|
||||||
|
Eps = 6
|
||||||
|
|
||||||
|
|
||||||
|
[ids]
|
||||||
|
[ids.Eps]
|
||||||
|
Pcdu = 0
|
||||||
|
Subsystem = 1
|
||||||
|
|
||||||
|
[ids.Tmtc]
|
||||||
|
UdpServer = 0
|
||||||
|
TcpServer = 1
|
||||||
|
|
||||||
|
[ids.GenericPus]
|
||||||
|
PusEventManagement = 0
|
||||||
|
PusRouting = 1
|
||||||
|
PusTest = 2
|
||||||
|
PusAction = 3
|
||||||
|
PusMode = 4
|
||||||
|
PusHk = 5
|
||||||
|
|
||||||
|
[ids.Sched]
|
||||||
|
PusSched = 0
|
||||||
|
|
||||||
|
[ids.Acs]
|
||||||
|
Subsystem = 1
|
||||||
|
Assembly = 2
|
||||||
|
Mgm0 = 3
|
||||||
|
Mgm1 = 4
|
||||||
91
experiments/satrs-gen/src/main.rs
Normal file
91
experiments/satrs-gen/src/main.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use heck::{ToShoutySnakeCase, ToSnakeCase};
|
||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
fs::{self, File},
|
||||||
|
io::{self, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
use toml::{Value, map::Map};
|
||||||
|
|
||||||
|
fn main() -> io::Result<()> {
|
||||||
|
// Read the configuration file
|
||||||
|
let config_str = fs::read_to_string("components.toml").expect("Unable to read file");
|
||||||
|
let config: Value = toml::from_str(&config_str).expect("Unable to parse TOML");
|
||||||
|
|
||||||
|
let mut output = File::create("../satrs-example/src/ids.rs")?;
|
||||||
|
|
||||||
|
generate_rust_code(&config, &mut output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_enum_table(table_map: &Map<String, Value>) -> BTreeMap<u64, &str> {
|
||||||
|
// Collect entries into a BTreeMap to sort them by key
|
||||||
|
let mut sorted_entries: BTreeMap<u64, &str> = BTreeMap::new();
|
||||||
|
|
||||||
|
for (key, value) in table_map {
|
||||||
|
if let Some(value) = value.as_integer() {
|
||||||
|
if !(0..=0x7FF).contains(&value) {
|
||||||
|
panic!("Invalid APID value: {}", value);
|
||||||
|
}
|
||||||
|
sorted_entries.insert(value as u64, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sorted_entries
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_rust_code(config: &Value, writer: &mut impl Write) {
|
||||||
|
writeln!(
|
||||||
|
writer,
|
||||||
|
"//! This is an auto-generated configuration module."
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(writer, "use satrs::request::UniqueApidTargetId;").unwrap();
|
||||||
|
writeln!(writer).unwrap();
|
||||||
|
|
||||||
|
// Generate the main module
|
||||||
|
writeln!(
|
||||||
|
writer,
|
||||||
|
"#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::EnumIter)]"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(writer, "pub enum Apid {{").unwrap();
|
||||||
|
|
||||||
|
// Generate constants for the main module
|
||||||
|
if let Some(apid_table) = config.get("apid").and_then(Value::as_table) {
|
||||||
|
let sorted_entries = sort_enum_table(apid_table);
|
||||||
|
// Write the sorted entries to the writer
|
||||||
|
for (value, key) in sorted_entries {
|
||||||
|
writeln!(writer, " {} = {},", key, value).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(writer, "}}").unwrap();
|
||||||
|
|
||||||
|
// Generate ID tables.
|
||||||
|
if let Some(id_tables) = config.get("ids").and_then(Value::as_table) {
|
||||||
|
for (mod_name, table) in id_tables {
|
||||||
|
let mod_name_as_snake = mod_name.to_snake_case();
|
||||||
|
writeln!(writer).unwrap();
|
||||||
|
writeln!(writer, "pub mod {} {{", mod_name_as_snake).unwrap();
|
||||||
|
let sorted_entries = sort_enum_table(table.as_table().unwrap());
|
||||||
|
writeln!(writer, " #[derive(Debug, Copy, Clone, PartialEq, Eq)]").unwrap();
|
||||||
|
writeln!(writer, " pub enum Id {{").unwrap();
|
||||||
|
// Write the sorted entries to the writer
|
||||||
|
for (value, key) in &sorted_entries {
|
||||||
|
writeln!(writer, " {} = {},", key, value).unwrap();
|
||||||
|
}
|
||||||
|
writeln!(writer, " }}").unwrap();
|
||||||
|
writeln!(writer).unwrap();
|
||||||
|
|
||||||
|
for (_value, key) in sorted_entries {
|
||||||
|
let key_shouting = key.to_shouty_snake_case();
|
||||||
|
writeln!(
|
||||||
|
writer,
|
||||||
|
" pub const {}: super::UniqueApidTargetId = super::UniqueApidTargetId::new(super::Apid::{} as u16, Id::{} as u32);",
|
||||||
|
key_shouting, mod_name, key
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(writer, "}}").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
justfile
Normal file
29
justfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
all: check build embedded test check-fmt clippy docs
|
||||||
|
|
||||||
|
check:
|
||||||
|
cargo check
|
||||||
|
cargo check -p satrs-example --no-default-features
|
||||||
|
|
||||||
|
build:
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
test:
|
||||||
|
cargo nextest run --all-features
|
||||||
|
cargo test --doc --all-features
|
||||||
|
|
||||||
|
embedded:
|
||||||
|
cargo check -p satrs --target=thumbv7em-none-eabihf --no-default-features
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
cargo fmt --all
|
||||||
|
|
||||||
|
check-fmt:
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
cargo clippy -- -D warnings
|
||||||
|
|
||||||
|
docs-satrs:
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p satrs --all-features
|
||||||
|
|
||||||
|
docs: docs-satrs
|
||||||
@@ -18,9 +18,11 @@ csv = "1"
|
|||||||
num_enum = "0.7"
|
num_enum = "0.7"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.27", features = ["derive"] }
|
||||||
derive-new = "0.7"
|
derive-new = "0.7"
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
|
arbitrary-int = "2"
|
||||||
|
bitbybit = "1.4"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
@@ -36,8 +38,8 @@ version = "0.1.1"
|
|||||||
path = "../satrs-mib"
|
path = "../satrs-mib"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
heap_tmtc = []
|
|
||||||
default = ["heap_tmtc"]
|
default = ["heap_tmtc"]
|
||||||
|
heap_tmtc = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use derive_new::new;
|
|||||||
use satrs::hk::{HkRequest, HkRequestVariant};
|
use satrs::hk::{HkRequest, HkRequestVariant};
|
||||||
use satrs::mode_tree::{ModeChild, ModeNode};
|
use satrs::mode_tree::{ModeChild, ModeNode};
|
||||||
use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender};
|
use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender};
|
||||||
use satrs_example::config::pus::PUS_MODE_SERVICE;
|
use satrs_example::ids::generic_pus::PUS_MODE;
|
||||||
use satrs_example::{DeviceMode, TimestampHelper};
|
use satrs_example::{DeviceMode, TimestampHelper};
|
||||||
use satrs_minisim::acs::lis3mdl::{
|
use satrs_minisim::acs::lis3mdl::{
|
||||||
MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR,
|
MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR,
|
||||||
@@ -83,7 +83,7 @@ impl SpiInterface for SpiSimInterface {
|
|||||||
.sim_request_tx
|
.sim_request_tx
|
||||||
.send(SimRequest::new_with_epoch_time(mgm_sensor_request))
|
.send(SimRequest::new_with_epoch_time(mgm_sensor_request))
|
||||||
{
|
{
|
||||||
log::error!("failed to send MGM LIS3 request: {}", e);
|
log::error!("failed to send MGM LIS3 request: {e}");
|
||||||
}
|
}
|
||||||
match self.sim_reply_rx.recv_timeout(Duration::from_millis(50)) {
|
match self.sim_reply_rx.recv_timeout(Duration::from_millis(50)) {
|
||||||
Ok(sim_reply) => {
|
Ok(sim_reply) => {
|
||||||
@@ -97,7 +97,7 @@ impl SpiInterface for SpiSimInterface {
|
|||||||
.copy_from_slice(&sim_reply_lis3.raw.z.to_le_bytes());
|
.copy_from_slice(&sim_reply_lis3.raw.z.to_le_bytes());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("MGM LIS3 SIM reply timeout: {}", e);
|
log::warn!("MGM LIS3 SIM reply timeout: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -417,7 +417,7 @@ impl<
|
|||||||
if requestor.sender_id() == NO_SENDER {
|
if requestor.sender_id() == NO_SENDER {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
if requestor.sender_id() != PUS_MODE.id() {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"can not send back mode reply to sender {:x}",
|
"can not send back mode reply to sender {:x}",
|
||||||
requestor.sender_id()
|
requestor.sender_id()
|
||||||
@@ -434,7 +434,7 @@ impl<
|
|||||||
requestor: MessageMetadata,
|
requestor: MessageMetadata,
|
||||||
reply: ModeReply,
|
reply: ModeReply,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
if requestor.sender_id() != PUS_MODE.id() {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"can not send back mode reply to sender {}",
|
"can not send back mode reply to sender {}",
|
||||||
requestor.sender_id()
|
requestor.sender_id()
|
||||||
@@ -484,6 +484,7 @@ mod tests {
|
|||||||
sync::{mpsc, Arc},
|
sync::{mpsc, Arc},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use arbitrary_int::u21;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
mode::{ModeReply, ModeRequest},
|
mode::{ModeReply, ModeRequest},
|
||||||
mode_tree::ModeParent,
|
mode_tree::ModeParent,
|
||||||
@@ -492,7 +493,7 @@ mod tests {
|
|||||||
tmtc::PacketAsVec,
|
tmtc::PacketAsVec,
|
||||||
ComponentId,
|
ComponentId,
|
||||||
};
|
};
|
||||||
use satrs_example::config::{acs::MGM_ASSEMBLY, components::Apid};
|
use satrs_example::ids::{acs::ASSEMBLY, Apid};
|
||||||
use satrs_minisim::acs::lis3mdl::MgmLis3RawValues;
|
use satrs_minisim::acs::lis3mdl::MgmLis3RawValues;
|
||||||
|
|
||||||
use crate::{eps::TestSwitchHelper, pus::hk::HkReply, requests::CompositeRequest};
|
use crate::{eps::TestSwitchHelper, pus::hk::HkReply, requests::CompositeRequest};
|
||||||
@@ -538,7 +539,7 @@ mod tests {
|
|||||||
|
|
||||||
impl ModeNode for MgmAssemblyMock {
|
impl ModeNode for MgmAssemblyMock {
|
||||||
fn id(&self) -> satrs::ComponentId {
|
fn id(&self) -> satrs::ComponentId {
|
||||||
PUS_MODE_SERVICE.into()
|
PUS_MODE.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +558,7 @@ mod tests {
|
|||||||
|
|
||||||
impl ModeNode for PusMock {
|
impl ModeNode for PusMock {
|
||||||
fn id(&self) -> satrs::ComponentId {
|
fn id(&self) -> satrs::ComponentId {
|
||||||
PUS_MODE_SERVICE.into()
|
PUS_MODE.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,7 +575,7 @@ mod tests {
|
|||||||
let (request_tx, request_rx) = mpsc::sync_channel(5);
|
let (request_tx, request_rx) = mpsc::sync_channel(5);
|
||||||
let (reply_tx_to_pus, reply_rx_to_pus) = mpsc::sync_channel(5);
|
let (reply_tx_to_pus, reply_rx_to_pus) = mpsc::sync_channel(5);
|
||||||
let (reply_tx_to_parent, reply_rx_to_parent) = mpsc::sync_channel(5);
|
let (reply_tx_to_parent, reply_rx_to_parent) = mpsc::sync_channel(5);
|
||||||
let id = UniqueApidTargetId::new(Apid::Acs as u16, 1);
|
let id = UniqueApidTargetId::new(Apid::Acs.raw_value(), u21::new(1));
|
||||||
let mode_node = ModeRequestHandlerMpscBounded::new(id.into(), request_rx);
|
let mode_node = ModeRequestHandlerMpscBounded::new(id.into(), request_rx);
|
||||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||||
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
||||||
@@ -592,8 +593,8 @@ mod tests {
|
|||||||
TestSpiInterface::default(),
|
TestSpiInterface::default(),
|
||||||
shared_mgm_set,
|
shared_mgm_set,
|
||||||
);
|
);
|
||||||
handler.add_mode_parent(PUS_MODE_SERVICE.into(), reply_tx_to_pus);
|
handler.add_mode_parent(PUS_MODE.into(), reply_tx_to_pus);
|
||||||
handler.add_mode_parent(MGM_ASSEMBLY.into(), reply_tx_to_parent);
|
handler.add_mode_parent(ASSEMBLY.into(), reply_tx_to_parent);
|
||||||
Self {
|
Self {
|
||||||
mode_request_tx: request_tx,
|
mode_request_tx: request_tx,
|
||||||
mode_reply_rx_to_pus: reply_rx_to_pus,
|
mode_reply_rx_to_pus: reply_rx_to_pus,
|
||||||
@@ -631,7 +632,7 @@ mod tests {
|
|||||||
testbench
|
testbench
|
||||||
.mode_request_tx
|
.mode_request_tx
|
||||||
.send(GenericMessage::new(
|
.send(GenericMessage::new(
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
MessageMetadata::new(0, PUS_MODE.id()),
|
||||||
ModeRequest::SetMode {
|
ModeRequest::SetMode {
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
||||||
forced: false,
|
forced: false,
|
||||||
@@ -692,7 +693,7 @@ mod tests {
|
|||||||
testbench
|
testbench
|
||||||
.mode_request_tx
|
.mode_request_tx
|
||||||
.send(GenericMessage::new(
|
.send(GenericMessage::new(
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
MessageMetadata::new(0, PUS_MODE.id()),
|
||||||
ModeRequest::SetMode {
|
ModeRequest::SetMode {
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
||||||
forced: false,
|
forced: false,
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
|
use arbitrary_int::u11;
|
||||||
use satrs::pus::verification::RequestId;
|
use satrs::pus::verification::RequestId;
|
||||||
use satrs::spacepackets::ecss::tc::PusTcCreator;
|
use satrs::spacepackets::ecss::tc::PusTcCreator;
|
||||||
use satrs::spacepackets::ecss::tm::PusTmReader;
|
use satrs::spacepackets::ecss::tm::PusTmReader;
|
||||||
use satrs::{
|
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
spacepackets::ecss::{PusPacket, WritablePusPacket},
|
use satrs::spacepackets::SpHeader;
|
||||||
spacepackets::SpHeader,
|
|
||||||
};
|
|
||||||
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
|
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||||
use std::net::{IpAddr, SocketAddr, UdpSocket};
|
use std::net::{IpAddr, SocketAddr, UdpSocket};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -12,7 +11,12 @@ use std::time::Duration;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut buf = [0; 32];
|
let mut buf = [0; 32];
|
||||||
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||||
let pus_tc = PusTcCreator::new_simple(SpHeader::new_from_apid(0x02), 17, 1, &[], true);
|
let pus_tc = PusTcCreator::new_simple(
|
||||||
|
SpHeader::new_from_apid(u11::new(0x02)),
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed");
|
let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed");
|
||||||
let tc_req_id = RequestId::new(&pus_tc);
|
let tc_req_id = RequestId::new(&pus_tc);
|
||||||
println!("Packing and sending PUS ping command TC[17,1] with request ID {tc_req_id}");
|
println!("Packing and sending PUS ping command TC[17,1] with request ID {tc_req_id}");
|
||||||
@@ -30,9 +34,9 @@ fn main() {
|
|||||||
match res {
|
match res {
|
||||||
Ok(_len) => {
|
Ok(_len) => {
|
||||||
let pus_tm = PusTmReader::new(&buf, 7).expect("Parsing PUS TM failed");
|
let pus_tm = PusTmReader::new(&buf, 7).expect("Parsing PUS TM failed");
|
||||||
if pus_tm.service() == 17 && pus_tm.subservice() == 2 {
|
if pus_tm.service_type_id() == 17 && pus_tm.message_subtype_id() == 2 {
|
||||||
println!("Received PUS Ping Reply TM[17,2]")
|
println!("Received PUS Ping Reply TM[17,2]")
|
||||||
} else if pus_tm.service() == 1 {
|
} else if pus_tm.service_type_id() == 1 {
|
||||||
if pus_tm.source_data().is_empty() {
|
if pus_tm.source_data().is_empty() {
|
||||||
println!("Invalid verification TM, no source data");
|
println!("Invalid verification TM, no source data");
|
||||||
}
|
}
|
||||||
@@ -41,28 +45,29 @@ fn main() {
|
|||||||
println!("Invalid verification TM source data, less than 4 bytes")
|
println!("Invalid verification TM source data, less than 4 bytes")
|
||||||
}
|
}
|
||||||
let req_id = RequestId::from_bytes(src_data).unwrap();
|
let req_id = RequestId::from_bytes(src_data).unwrap();
|
||||||
if pus_tm.subservice() == 1 {
|
let subtype_id = pus_tm.message_subtype_id();
|
||||||
|
if subtype_id == 1 {
|
||||||
println!("Received TM[1,1] acceptance success for request ID {req_id}")
|
println!("Received TM[1,1] acceptance success for request ID {req_id}")
|
||||||
} else if pus_tm.subservice() == 2 {
|
} else if subtype_id == 2 {
|
||||||
println!("Received TM[1,2] acceptance failure for request ID {req_id}")
|
println!("Received TM[1,2] acceptance failure for request ID {req_id}")
|
||||||
} else if pus_tm.subservice() == 3 {
|
} else if subtype_id == 3 {
|
||||||
println!("Received TM[1,3] start success for request ID {req_id}")
|
println!("Received TM[1,3] start success for request ID {req_id}")
|
||||||
} else if pus_tm.subservice() == 4 {
|
} else if subtype_id == 4 {
|
||||||
println!("Received TM[1,2] start failure for request ID {req_id}")
|
println!("Received TM[1,2] start failure for request ID {req_id}")
|
||||||
} else if pus_tm.subservice() == 5 {
|
} else if subtype_id == 5 {
|
||||||
println!("Received TM[1,5] step success for request ID {req_id}")
|
println!("Received TM[1,5] step success for request ID {req_id}")
|
||||||
} else if pus_tm.subservice() == 6 {
|
} else if subtype_id == 6 {
|
||||||
println!("Received TM[1,6] step failure for request ID {req_id}")
|
println!("Received TM[1,6] step failure for request ID {req_id}")
|
||||||
} else if pus_tm.subservice() == 7 {
|
} else if subtype_id == 7 {
|
||||||
println!("Received TM[1,7] completion success for request ID {req_id}")
|
println!("Received TM[1,7] completion success for request ID {req_id}")
|
||||||
} else if pus_tm.subservice() == 8 {
|
} else if subtype_id == 8 {
|
||||||
println!("Received TM[1,8] completion failure for request ID {req_id}");
|
println!("Received TM[1,8] completion failure for request ID {req_id}");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"Received TM[{}, {}] with {} bytes",
|
"Received TM[{}, {}] with {} bytes",
|
||||||
pus_tm.service(),
|
pus_tm.service_type_id(),
|
||||||
pus_tm.subservice(),
|
pus_tm.message_subtype_id(),
|
||||||
size
|
size
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use arbitrary_int::u11;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
res_code::ResultU16,
|
res_code::ResultU16,
|
||||||
@@ -10,7 +11,7 @@ use strum::IntoEnumIterator;
|
|||||||
|
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
use satrs::{
|
use satrs::{
|
||||||
events::{EventU32TypedSev, SeverityInfo},
|
events_legacy::{EventU32TypedSev, SeverityInfo},
|
||||||
pool::{StaticMemoryPool, StaticPoolConfig},
|
pool::{StaticMemoryPool, StaticPoolConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,14 +44,14 @@ pub const TEST_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<Severi
|
|||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref PACKET_ID_VALIDATOR: HashSet<PacketId> = {
|
pub static ref PACKET_ID_VALIDATOR: HashSet<PacketId> = {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
for id in components::Apid::iter() {
|
for id in crate::ids::Apid::iter() {
|
||||||
set.insert(PacketId::new(PacketType::Tc, true, id as u16));
|
set.insert(PacketId::new(PacketType::Tc, true, u11::new(id as u16)));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
};
|
};
|
||||||
pub static ref APID_VALIDATOR: HashSet<u16> = {
|
pub static ref APID_VALIDATOR: HashSet<u16> = {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
for id in components::Apid::iter() {
|
for id in crate::ids::Apid::iter() {
|
||||||
set.insert(id as u16);
|
set.insert(id as u16);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
@@ -122,92 +123,11 @@ pub mod mode_err {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod components {
|
pub mod components {
|
||||||
use satrs::{request::UniqueApidTargetId, ComponentId};
|
use satrs::ComponentId;
|
||||||
use strum::EnumIter;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, EnumIter)]
|
|
||||||
pub enum Apid {
|
|
||||||
Sched = 1,
|
|
||||||
GenericPus = 2,
|
|
||||||
Acs = 3,
|
|
||||||
Cfdp = 4,
|
|
||||||
Tmtc = 5,
|
|
||||||
Eps = 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum EpsId {
|
|
||||||
Pcdu = 0,
|
|
||||||
Subsystem = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum TmtcId {
|
|
||||||
UdpServer = 0,
|
|
||||||
TcpServer = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const EPS_SUBSYSTEM: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Subsystem as u32);
|
|
||||||
pub const PCDU_HANDLER: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Pcdu as u32);
|
|
||||||
pub const UDP_SERVER: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::UdpServer as u32);
|
|
||||||
pub const TCP_SERVER: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::TcpServer as u32);
|
|
||||||
pub const NO_SENDER: ComponentId = ComponentId::MAX;
|
pub const NO_SENDER: ComponentId = ComponentId::MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod pus {
|
|
||||||
use super::components::Apid;
|
|
||||||
use satrs::request::UniqueApidTargetId;
|
|
||||||
|
|
||||||
// Component IDs for components with the PUS APID.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum PusId {
|
|
||||||
PusEventManagement = 0,
|
|
||||||
PusRouting = 1,
|
|
||||||
PusTest = 2,
|
|
||||||
PusAction = 3,
|
|
||||||
PusMode = 4,
|
|
||||||
PusHk = 5,
|
|
||||||
}
|
|
||||||
pub const PUS_ACTION_SERVICE: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusAction as u32);
|
|
||||||
pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, 0);
|
|
||||||
pub const PUS_ROUTING_SERVICE: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusRouting as u32);
|
|
||||||
pub const PUS_TEST_SERVICE: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusTest as u32);
|
|
||||||
pub const PUS_MODE_SERVICE: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusMode as u32);
|
|
||||||
pub const PUS_HK_SERVICE: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusHk as u32);
|
|
||||||
pub const PUS_SCHED_SERVICE: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Sched as u16, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod acs {
|
|
||||||
use super::components::Apid;
|
|
||||||
use satrs::request::UniqueApidTargetId;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum AcsId {
|
|
||||||
Subsystem = 1,
|
|
||||||
Assembly = 2,
|
|
||||||
Mgm0 = 3,
|
|
||||||
Mgm1 = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MGM_ASSEMBLY: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Assembly as u32);
|
|
||||||
pub const MGM_HANDLER_0: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
|
|
||||||
pub const MGM_HANDLER_1: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod pool {
|
pub mod pool {
|
||||||
use super::*;
|
use super::*;
|
||||||
pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) {
|
pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) {
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ use satrs::{
|
|||||||
spacepackets::ByteConversionError,
|
spacepackets::ByteConversionError,
|
||||||
};
|
};
|
||||||
use satrs_example::{
|
use satrs_example::{
|
||||||
config::{
|
config::components::NO_SENDER,
|
||||||
components::{NO_SENDER, PCDU_HANDLER},
|
ids::{eps::PCDU, generic_pus::PUS_MODE},
|
||||||
pus::PUS_MODE_SERVICE,
|
|
||||||
},
|
|
||||||
DeviceMode, TimestampHelper,
|
DeviceMode, TimestampHelper,
|
||||||
};
|
};
|
||||||
use satrs_minisim::{
|
use satrs_minisim::{
|
||||||
@@ -403,7 +401,7 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
log::warn!("receiving PCDU replies failed: {:?}", e);
|
log::warn!("receiving PCDU replies failed: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,7 +450,7 @@ impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterf
|
|||||||
if requestor.sender_id() == NO_SENDER {
|
if requestor.sender_id() == NO_SENDER {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
if requestor.sender_id() != PUS_MODE.id() {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"can not send back mode reply to sender {}",
|
"can not send back mode reply to sender {}",
|
||||||
requestor.sender_id()
|
requestor.sender_id()
|
||||||
@@ -469,7 +467,7 @@ impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterf
|
|||||||
requestor: MessageMetadata,
|
requestor: MessageMetadata,
|
||||||
reply: ModeReply,
|
reply: ModeReply,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
if requestor.sender_id() != PUS_MODE.id() {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"can not send back mode reply to sender {}",
|
"can not send back mode reply to sender {}",
|
||||||
requestor.sender_id()
|
requestor.sender_id()
|
||||||
@@ -492,7 +490,7 @@ impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterf
|
|||||||
|
|
||||||
impl<ComInterface: SerialInterface> ModeNode for PcduHandler<ComInterface> {
|
impl<ComInterface: SerialInterface> ModeNode for PcduHandler<ComInterface> {
|
||||||
fn id(&self) -> satrs::ComponentId {
|
fn id(&self) -> satrs::ComponentId {
|
||||||
PCDU_HANDLER.into()
|
PCDU.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,13 +506,11 @@ impl<ComInterface: SerialInterface> ModeChild for PcduHandler<ComInterface> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use arbitrary_int::u21;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
mode::ModeRequest, power::SwitchStateBinary, request::GenericMessage, tmtc::PacketAsVec,
|
mode::ModeRequest, power::SwitchStateBinary, request::GenericMessage, tmtc::PacketAsVec,
|
||||||
};
|
};
|
||||||
use satrs_example::config::{
|
use satrs_example::ids::{self, Apid};
|
||||||
acs::MGM_HANDLER_0,
|
|
||||||
components::{Apid, EPS_SUBSYSTEM, PCDU_HANDLER},
|
|
||||||
};
|
|
||||||
use satrs_minisim::eps::SwitchMapBinary;
|
use satrs_minisim::eps::SwitchMapBinary;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -570,15 +566,14 @@ mod tests {
|
|||||||
let (mode_request_tx, mode_request_rx) = mpsc::sync_channel(5);
|
let (mode_request_tx, mode_request_rx) = mpsc::sync_channel(5);
|
||||||
let (mode_reply_tx_to_pus, mode_reply_rx_to_pus) = mpsc::sync_channel(5);
|
let (mode_reply_tx_to_pus, mode_reply_rx_to_pus) = mpsc::sync_channel(5);
|
||||||
let (mode_reply_tx_to_parent, mode_reply_rx_to_parent) = mpsc::sync_channel(5);
|
let (mode_reply_tx_to_parent, mode_reply_rx_to_parent) = mpsc::sync_channel(5);
|
||||||
let mode_node =
|
let mode_node = ModeRequestHandlerMpscBounded::new(PCDU.into(), mode_request_rx);
|
||||||
ModeRequestHandlerMpscBounded::new(PCDU_HANDLER.into(), mode_request_rx);
|
|
||||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||||
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
||||||
let (tm_tx, tm_rx) = mpsc::sync_channel::<PacketAsVec>(5);
|
let (tm_tx, tm_rx) = mpsc::sync_channel::<PacketAsVec>(5);
|
||||||
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
|
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
|
||||||
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
|
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
|
||||||
let mut handler = PcduHandler::new(
|
let mut handler = PcduHandler::new(
|
||||||
UniqueApidTargetId::new(Apid::Eps as u16, 0),
|
UniqueApidTargetId::new(Apid::Eps.raw_value(), u21::new(0)),
|
||||||
"TEST_PCDU",
|
"TEST_PCDU",
|
||||||
mode_node,
|
mode_node,
|
||||||
composite_request_rx,
|
composite_request_rx,
|
||||||
@@ -588,8 +583,8 @@ mod tests {
|
|||||||
SerialInterfaceTest::default(),
|
SerialInterfaceTest::default(),
|
||||||
shared_switch_map,
|
shared_switch_map,
|
||||||
);
|
);
|
||||||
handler.add_mode_parent(EPS_SUBSYSTEM.into(), mode_reply_tx_to_parent);
|
handler.add_mode_parent(ids::eps::SUBSYSTEM.into(), mode_reply_tx_to_parent);
|
||||||
handler.add_mode_parent(PUS_MODE_SERVICE.into(), mode_reply_tx_to_pus);
|
handler.add_mode_parent(PUS_MODE.into(), mode_reply_tx_to_pus);
|
||||||
Self {
|
Self {
|
||||||
mode_request_tx,
|
mode_request_tx,
|
||||||
mode_reply_rx_to_pus,
|
mode_reply_rx_to_pus,
|
||||||
@@ -684,7 +679,7 @@ mod tests {
|
|||||||
testbench
|
testbench
|
||||||
.mode_request_tx
|
.mode_request_tx
|
||||||
.send(GenericMessage::new(
|
.send(GenericMessage::new(
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
MessageMetadata::new(0, PUS_MODE.id()),
|
||||||
ModeRequest::SetMode {
|
ModeRequest::SetMode {
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
||||||
forced: false,
|
forced: false,
|
||||||
@@ -719,7 +714,7 @@ mod tests {
|
|||||||
testbench
|
testbench
|
||||||
.mode_request_tx
|
.mode_request_tx
|
||||||
.send(GenericMessage::new(
|
.send(GenericMessage::new(
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
MessageMetadata::new(0, PUS_MODE.id()),
|
||||||
ModeRequest::SetMode {
|
ModeRequest::SetMode {
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
||||||
forced: false,
|
forced: false,
|
||||||
@@ -729,7 +724,7 @@ mod tests {
|
|||||||
testbench
|
testbench
|
||||||
.switch_request_tx
|
.switch_request_tx
|
||||||
.send(GenericMessage::new(
|
.send(GenericMessage::new(
|
||||||
MessageMetadata::new(0, MGM_HANDLER_0.id()),
|
MessageMetadata::new(0, ids::acs::MGM0.id()),
|
||||||
SwitchRequest::new(0, SwitchStateBinary::On),
|
SwitchRequest::new(0, SwitchStateBinary::On),
|
||||||
))
|
))
|
||||||
.expect("failed to send switch request");
|
.expect("failed to send switch request");
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
use std::sync::mpsc::{self};
|
use std::sync::mpsc::{self};
|
||||||
|
|
||||||
use crate::pus::create_verification_reporter;
|
use crate::pus::create_verification_reporter;
|
||||||
use satrs::event_man::{EventMessageU32, EventRoutingError};
|
use arbitrary_int::traits::Integer as _;
|
||||||
use satrs::pus::event::EventTmHookProvider;
|
use arbitrary_int::u11;
|
||||||
|
use satrs::event_man_legacy::{EventMessageU32, EventRoutingError};
|
||||||
|
use satrs::pus::event::EventTmHook;
|
||||||
use satrs::pus::verification::VerificationReporter;
|
use satrs::pus::verification::VerificationReporter;
|
||||||
use satrs::pus::EcssTmSender;
|
use satrs::pus::EcssTmSender;
|
||||||
use satrs::request::UniqueApidTargetId;
|
use satrs::request::UniqueApidTargetId;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
event_man::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
|
event_man_legacy::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
|
||||||
pus::{
|
pus::{
|
||||||
event_man::{
|
event_man::{
|
||||||
DefaultPusEventU32TmCreator, EventReporter, EventRequest, EventRequestWithToken,
|
DefaultPusEventU32TmCreator, EventReporter, EventRequest, EventRequestWithToken,
|
||||||
@@ -16,17 +18,17 @@ use satrs::{
|
|||||||
},
|
},
|
||||||
spacepackets::time::cds::CdsTime,
|
spacepackets::time::cds::CdsTime,
|
||||||
};
|
};
|
||||||
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT;
|
use satrs_example::ids::generic_pus::PUS_EVENT_MANAGEMENT;
|
||||||
|
|
||||||
use crate::update_time;
|
use crate::update_time;
|
||||||
|
|
||||||
// This helper sets the APID of the event sender for the PUS telemetry.
|
// This helper sets the APID of the event sender for the PUS telemetry.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EventApidSetter {
|
pub struct EventApidSetter {
|
||||||
pub next_apid: u16,
|
pub next_apid: u11,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventTmHookProvider for EventApidSetter {
|
impl EventTmHook for EventApidSetter {
|
||||||
fn modify_tm(&self, tm: &mut satrs::spacepackets::ecss::tm::PusTmCreator) {
|
fn modify_tm(&self, tm: &mut satrs::spacepackets::ecss::tm::PusTmCreator) {
|
||||||
tm.set_apid(self.next_apid);
|
tm.set_apid(self.next_apid);
|
||||||
}
|
}
|
||||||
@@ -59,12 +61,11 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
|
|||||||
// telemetry for each event.
|
// telemetry for each event.
|
||||||
let event_reporter = EventReporter::new_with_hook(
|
let event_reporter = EventReporter::new_with_hook(
|
||||||
PUS_EVENT_MANAGEMENT.raw(),
|
PUS_EVENT_MANAGEMENT.raw(),
|
||||||
0,
|
u11::ZERO,
|
||||||
0,
|
0,
|
||||||
128,
|
128,
|
||||||
EventApidSetter::default(),
|
EventApidSetter::default(),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
let pus_event_dispatcher =
|
let pus_event_dispatcher =
|
||||||
DefaultPusEventU32TmCreator::new_with_default_backend(event_reporter);
|
DefaultPusEventU32TmCreator::new_with_default_backend(event_reporter);
|
||||||
let pus_event_man_send_provider = EventU32SenderMpscBounded::new(
|
let pus_event_man_send_provider = EventU32SenderMpscBounded::new(
|
||||||
@@ -217,20 +218,18 @@ impl<TmSender: EcssTmSender> EventHandler<TmSender> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use arbitrary_int::u21;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
events::EventU32,
|
events_legacy::EventU32,
|
||||||
pus::verification::VerificationReporterCfg,
|
pus::verification::VerificationReporterConfig,
|
||||||
spacepackets::{
|
spacepackets::ecss::{tm::PusTmReader, PusPacket},
|
||||||
ecss::{tm::PusTmReader, PusPacket},
|
|
||||||
CcsdsPacket,
|
|
||||||
},
|
|
||||||
tmtc::PacketAsVec,
|
tmtc::PacketAsVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(1, 2);
|
const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(u11::new(1), u21::new(2));
|
||||||
const TEST_EVENT: EventU32 = EventU32::new(satrs::events::Severity::Info, 1, 1);
|
const TEST_EVENT: EventU32 = EventU32::new(satrs::events_legacy::Severity::Info, 1, 1);
|
||||||
|
|
||||||
pub struct EventManagementTestbench {
|
pub struct EventManagementTestbench {
|
||||||
pub event_tx: mpsc::SyncSender<EventMessageU32>,
|
pub event_tx: mpsc::SyncSender<EventMessageU32>,
|
||||||
@@ -244,7 +243,7 @@ mod tests {
|
|||||||
let (event_tx, event_rx) = mpsc::sync_channel(10);
|
let (event_tx, event_rx) = mpsc::sync_channel(10);
|
||||||
let (_event_req_tx, event_req_rx) = mpsc::sync_channel(10);
|
let (_event_req_tx, event_req_rx) = mpsc::sync_channel(10);
|
||||||
let (tm_sender, tm_receiver) = mpsc::channel();
|
let (tm_sender, tm_receiver) = mpsc::channel();
|
||||||
let verif_reporter_cfg = VerificationReporterCfg::new(0x05, 2, 2, 128).unwrap();
|
let verif_reporter_cfg = VerificationReporterConfig::new(u11::new(0x05), 2, 2, 128);
|
||||||
let verif_reporter =
|
let verif_reporter =
|
||||||
VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg);
|
VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg);
|
||||||
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
|
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
|
||||||
@@ -270,7 +269,7 @@ mod tests {
|
|||||||
.event_tx
|
.event_tx
|
||||||
.send(EventMessageU32::new(
|
.send(EventMessageU32::new(
|
||||||
TEST_CREATOR_ID.id(),
|
TEST_CREATOR_ID.id(),
|
||||||
EventU32::new(satrs::events::Severity::Info, 1, 1),
|
EventU32::new(satrs::events_legacy::Severity::Info, 1, 1),
|
||||||
))
|
))
|
||||||
.expect("failed to send event");
|
.expect("failed to send event");
|
||||||
testbench.pus_event_handler.handle_event_requests();
|
testbench.pus_event_handler.handle_event_requests();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::hk::UniqueId;
|
use satrs::hk::UniqueId;
|
||||||
use satrs::request::UniqueApidTargetId;
|
use satrs::request::UniqueApidTargetId;
|
||||||
use satrs::spacepackets::ecss::hk;
|
|
||||||
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
|
use satrs::spacepackets::ecss::{hk, CreatorConfig, MessageTypeId};
|
||||||
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
||||||
|
|
||||||
#[derive(Debug, new, Copy, Clone)]
|
#[derive(Debug, new, Copy, Clone)]
|
||||||
@@ -29,7 +30,7 @@ impl HkUniqueId {
|
|||||||
expected: 8,
|
expected: 8,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
buf[0..4].copy_from_slice(&self.target_id.unique_id.to_be_bytes());
|
buf[0..4].copy_from_slice(&self.target_id.unique_id.as_u32().to_be_bytes());
|
||||||
buf[4..8].copy_from_slice(&self.set_id.to_be_bytes());
|
buf[4..8].copy_from_slice(&self.set_id.to_be_bytes());
|
||||||
|
|
||||||
Ok(8)
|
Ok(8)
|
||||||
@@ -53,9 +54,13 @@ impl PusHkHelper {
|
|||||||
hk_data_writer: &mut HkWriter,
|
hk_data_writer: &mut HkWriter,
|
||||||
buf: &'b mut [u8],
|
buf: &'b mut [u8],
|
||||||
) -> Result<PusTmCreator<'a, 'b>, ByteConversionError> {
|
) -> Result<PusTmCreator<'a, 'b>, ByteConversionError> {
|
||||||
let sec_header =
|
let sec_header = PusTmSecondaryHeader::new(
|
||||||
PusTmSecondaryHeader::new(3, hk::Subservice::TmHkPacket as u8, 0, 0, timestamp);
|
MessageTypeId::new(3, hk::MessageSubtypeId::TmHkPacket as u8),
|
||||||
buf[0..4].copy_from_slice(&self.component_id.unique_id.to_be_bytes());
|
0,
|
||||||
|
0,
|
||||||
|
timestamp,
|
||||||
|
);
|
||||||
|
buf[0..4].copy_from_slice(&self.component_id.unique_id.as_u32().to_be_bytes());
|
||||||
buf[4..8].copy_from_slice(&set_id.to_be_bytes());
|
buf[4..8].copy_from_slice(&set_id.to_be_bytes());
|
||||||
let (_, second_half) = buf.split_at_mut(8);
|
let (_, second_half) = buf.split_at_mut(8);
|
||||||
let hk_data_len = hk_data_writer(second_half)?;
|
let hk_data_len = hk_data_writer(second_half)?;
|
||||||
@@ -63,7 +68,7 @@ impl PusHkHelper {
|
|||||||
SpHeader::new_from_apid(self.component_id.apid),
|
SpHeader::new_from_apid(self.component_id.apid),
|
||||||
sec_header,
|
sec_header,
|
||||||
&buf[0..8 + hk_data_len],
|
&buf[0..8 + hk_data_len],
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
109
satrs-example/src/ids.rs
Normal file
109
satrs-example/src/ids.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//! This is an auto-generated configuration module.
|
||||||
|
use satrs::request::UniqueApidTargetId;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, strum::EnumIter)]
|
||||||
|
#[bitbybit::bitenum(u11)]
|
||||||
|
pub enum Apid {
|
||||||
|
Sched = 1,
|
||||||
|
GenericPus = 2,
|
||||||
|
Acs = 3,
|
||||||
|
Cfdp = 4,
|
||||||
|
Tmtc = 5,
|
||||||
|
Eps = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod acs {
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[bitbybit::bitenum(u21, exhaustive = false)]
|
||||||
|
pub enum Id {
|
||||||
|
Subsystem = 1,
|
||||||
|
Assembly = 2,
|
||||||
|
Mgm0 = 3,
|
||||||
|
Mgm1 = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SUBSYSTEM: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Subsystem.raw_value());
|
||||||
|
pub const ASSEMBLY: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Assembly.raw_value());
|
||||||
|
pub const MGM0: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm0.raw_value());
|
||||||
|
pub const MGM1: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm1.raw_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod eps {
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[bitbybit::bitenum(u21, exhaustive = false)]
|
||||||
|
pub enum Id {
|
||||||
|
Pcdu = 0,
|
||||||
|
Subsystem = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PCDU: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Pcdu.raw_value());
|
||||||
|
pub const SUBSYSTEM: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Subsystem.raw_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod generic_pus {
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[bitbybit::bitenum(u21, exhaustive = false)]
|
||||||
|
pub enum Id {
|
||||||
|
PusEventManagement = 0,
|
||||||
|
PusRouting = 1,
|
||||||
|
PusTest = 2,
|
||||||
|
PusAction = 3,
|
||||||
|
PusMode = 4,
|
||||||
|
PusHk = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PUS_EVENT_MANAGEMENT: super::UniqueApidTargetId = super::UniqueApidTargetId::new(
|
||||||
|
super::Apid::GenericPus.raw_value(),
|
||||||
|
Id::PusEventManagement.raw_value(),
|
||||||
|
);
|
||||||
|
pub const PUS_ROUTING: super::UniqueApidTargetId = super::UniqueApidTargetId::new(
|
||||||
|
super::Apid::GenericPus.raw_value(),
|
||||||
|
Id::PusRouting.raw_value(),
|
||||||
|
);
|
||||||
|
pub const PUS_TEST: super::UniqueApidTargetId = super::UniqueApidTargetId::new(
|
||||||
|
super::Apid::GenericPus.raw_value(),
|
||||||
|
Id::PusTest.raw_value(),
|
||||||
|
);
|
||||||
|
pub const PUS_ACTION: super::UniqueApidTargetId = super::UniqueApidTargetId::new(
|
||||||
|
super::Apid::GenericPus.raw_value(),
|
||||||
|
Id::PusAction.raw_value(),
|
||||||
|
);
|
||||||
|
pub const PUS_MODE: super::UniqueApidTargetId = super::UniqueApidTargetId::new(
|
||||||
|
super::Apid::GenericPus.raw_value(),
|
||||||
|
Id::PusMode.raw_value(),
|
||||||
|
);
|
||||||
|
pub const PUS_HK: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusHk.raw_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod sched {
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[bitbybit::bitenum(u21, exhaustive = false)]
|
||||||
|
pub enum Id {
|
||||||
|
PusSched = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PUS_SCHED: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Sched.raw_value(), Id::PusSched.raw_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod tmtc {
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[bitbybit::bitenum(u21, exhaustive = false)]
|
||||||
|
pub enum Id {
|
||||||
|
UdpServer = 0,
|
||||||
|
TcpServer = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const UDP_SERVER: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Tmtc.raw_value(), Id::UdpServer.raw_value());
|
||||||
|
pub const TCP_SERVER: super::UniqueApidTargetId =
|
||||||
|
super::UniqueApidTargetId::new(super::Apid::Tmtc.raw_value(), Id::TcpServer.raw_value());
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ pub fn create_sim_client(sim_request_rx: mpsc::Receiver<SimRequest>) -> Option<S
|
|||||||
return Some(sim_client);
|
return Some(sim_client);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("sim client creation error: {}", e);
|
log::warn!("sim client creation error: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -116,7 +116,7 @@ impl SimClientUdp {
|
|||||||
.udp_client
|
.udp_client
|
||||||
.send_to(request_json.as_bytes(), self.simulator_addr)
|
.send_to(request_json.as_bytes(), self.simulator_addr)
|
||||||
{
|
{
|
||||||
log::error!("error sending data to UDP SIM server: {}", e);
|
log::error!("error sending data to UDP SIM server: {e}");
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
no_sim_requests_handled = false;
|
no_sim_requests_handled = false;
|
||||||
@@ -151,7 +151,7 @@ impl SimClientUdp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("failed to deserialize SIM reply: {}", e);
|
log::warn!("failed to deserialize SIM reply: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ impl SimClientUdp {
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
log::error!("error receiving data from UDP SIM server: {}", e);
|
log::error!("error receiving data from UDP SIM server: {e}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use satrs::tmtc::StoreAndSendError;
|
use satrs::hal::std::tcp_spacepackets_server::CcsdsPacketParser;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
||||||
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
|
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
|
||||||
@@ -31,7 +31,7 @@ impl SpacePacketValidator for SimplePacketValidator {
|
|||||||
if self.valid_ids.contains(&sp_header.packet_id()) {
|
if self.valid_ids.contains(&sp_header.packet_id()) {
|
||||||
return SpValidity::Valid;
|
return SpValidity::Valid;
|
||||||
}
|
}
|
||||||
log::warn!("ignoring space packet with header {:?}", sp_header);
|
log::warn!("ignoring space packet with header {sp_header:?}");
|
||||||
// We could perform a CRC check.. but lets keep this simple and assume that TCP ensures
|
// We could perform a CRC check.. but lets keep this simple and assume that TCP ensures
|
||||||
// data integrity.
|
// data integrity.
|
||||||
SpValidity::Skip
|
SpValidity::Skip
|
||||||
@@ -103,16 +103,14 @@ impl PacketSource for SyncTcpTmSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TcpServer<ReceivesTc, SendError> = TcpSpacepacketsServer<
|
pub type TcpServer<ReceivesTc> = TcpSpacepacketsServer<
|
||||||
SyncTcpTmSource,
|
SyncTcpTmSource,
|
||||||
ReceivesTc,
|
ReceivesTc,
|
||||||
SimplePacketValidator,
|
SimplePacketValidator,
|
||||||
ConnectionFinishedHandler,
|
ConnectionFinishedHandler,
|
||||||
(),
|
|
||||||
SendError,
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub struct TcpTask(pub TcpServer<TmTcSender, StoreAndSendError>);
|
pub struct TcpTask(pub TcpServer<TmTcSender>);
|
||||||
|
|
||||||
impl TcpTask {
|
impl TcpTask {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@@ -124,8 +122,7 @@ impl TcpTask {
|
|||||||
Ok(Self(TcpSpacepacketsServer::new(
|
Ok(Self(TcpSpacepacketsServer::new(
|
||||||
cfg,
|
cfg,
|
||||||
tm_source,
|
tm_source,
|
||||||
tc_sender,
|
CcsdsPacketParser::new(cfg.id, 2048, tc_sender, SimplePacketValidator { valid_ids }),
|
||||||
SimplePacketValidator { valid_ids },
|
|
||||||
ConnectionFinishedHandler::default(),
|
ConnectionFinishedHandler::default(),
|
||||||
None,
|
None,
|
||||||
)?))
|
)?))
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
use std::net::{SocketAddr, UdpSocket};
|
use std::net::{SocketAddr, UdpSocket};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use satrs::hal::std::udp_server::{ReceiveResult, UdpTcServer};
|
||||||
use satrs::pus::HandlingStatus;
|
use satrs::pus::HandlingStatus;
|
||||||
use satrs::tmtc::{PacketAsVec, PacketInPool, StoreAndSendError};
|
use satrs::queue::GenericSendError;
|
||||||
use satrs::{
|
use satrs::tmtc::PacketAsVec;
|
||||||
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
|
||||||
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
|
use satrs::pool::{PoolProviderWithGuards, SharedStaticMemoryPool};
|
||||||
};
|
use satrs::tmtc::PacketInPool;
|
||||||
|
|
||||||
use crate::tmtc::sender::TmTcSender;
|
use crate::tmtc::sender::TmTcSender;
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ impl UdpTmHandler for DynamicUdpTmHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct UdpTmtcServer<TmHandler: UdpTmHandler> {
|
pub struct UdpTmtcServer<TmHandler: UdpTmHandler> {
|
||||||
pub udp_tc_server: UdpTcServer<TmTcSender, StoreAndSendError>,
|
pub udp_tc_server: UdpTcServer<TmTcSender, GenericSendError>,
|
||||||
pub tm_handler: TmHandler,
|
pub tm_handler: TmHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +114,9 @@ mod tests {
|
|||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
|
use arbitrary_int::u14;
|
||||||
|
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
use satrs::{
|
use satrs::{
|
||||||
spacepackets::{
|
spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||||
@@ -119,7 +124,8 @@ mod tests {
|
|||||||
},
|
},
|
||||||
ComponentId,
|
ComponentId,
|
||||||
};
|
};
|
||||||
use satrs_example::config::{components, OBSW_SERVER_ADDR};
|
use satrs_example::config::OBSW_SERVER_ADDR;
|
||||||
|
use satrs_example::ids;
|
||||||
|
|
||||||
use crate::tmtc::sender::{MockSender, TmTcSender};
|
use crate::tmtc::sender::{MockSender, TmTcSender};
|
||||||
|
|
||||||
@@ -175,10 +181,15 @@ mod tests {
|
|||||||
udp_tc_server,
|
udp_tc_server,
|
||||||
tm_handler,
|
tm_handler,
|
||||||
};
|
};
|
||||||
let sph = SpHeader::new_for_unseg_tc(components::Apid::GenericPus as u16, 0, 0);
|
let sph = SpHeader::new_for_unseg_tc(ids::Apid::GenericPus.raw_value(), u14::ZERO, 0);
|
||||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true)
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
.to_vec()
|
sph,
|
||||||
.unwrap();
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
|
.to_vec()
|
||||||
|
.unwrap();
|
||||||
let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed");
|
let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed");
|
||||||
let client_addr = client.local_addr().unwrap();
|
let client_addr = client.local_addr().unwrap();
|
||||||
println!("{}", server_addr);
|
println!("{}", server_addr);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use satrs::spacepackets::time::{cds::CdsTime, TimeWriter};
|
use satrs::spacepackets::time::cds::CdsTime;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod ids;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum DeviceMode {
|
pub enum DeviceMode {
|
||||||
|
|||||||
@@ -33,18 +33,22 @@ use satrs::{
|
|||||||
hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer},
|
hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer},
|
||||||
mode::{Mode, ModeAndSubmode, ModeRequest, ModeRequestHandlerMpscBounded},
|
mode::{Mode, ModeAndSubmode, ModeRequest, ModeRequestHandlerMpscBounded},
|
||||||
mode_tree::connect_mode_nodes,
|
mode_tree::connect_mode_nodes,
|
||||||
pus::{event_man::EventRequestWithToken, EcssTcInMemConverter, HandlingStatus},
|
pus::{event_man::EventRequestWithToken, EcssTcCacher, HandlingStatus},
|
||||||
request::{GenericMessage, MessageMetadata},
|
request::{GenericMessage, MessageMetadata},
|
||||||
spacepackets::time::{cds::CdsTime, TimeWriter},
|
spacepackets::time::cds::CdsTime,
|
||||||
};
|
};
|
||||||
use satrs_example::{
|
use satrs_example::{
|
||||||
config::{
|
config::{
|
||||||
acs::{MGM_HANDLER_0, MGM_HANDLER_1},
|
components::NO_SENDER,
|
||||||
components::{NO_SENDER, PCDU_HANDLER, TCP_SERVER, UDP_SERVER},
|
|
||||||
pool::create_sched_tc_pool,
|
pool::create_sched_tc_pool,
|
||||||
tasks::{FREQ_MS_AOCS, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS},
|
tasks::{FREQ_MS_AOCS, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS},
|
||||||
OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT,
|
OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT,
|
||||||
},
|
},
|
||||||
|
ids::{
|
||||||
|
acs::*,
|
||||||
|
eps::*,
|
||||||
|
tmtc::{TCP_SERVER, UDP_SERVER},
|
||||||
|
},
|
||||||
DeviceMode,
|
DeviceMode,
|
||||||
};
|
};
|
||||||
use tmtc::sender::TmTcSender;
|
use tmtc::sender::TmTcSender;
|
||||||
@@ -53,12 +57,12 @@ use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink};
|
|||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "heap_tmtc")] {
|
if #[cfg(feature = "heap_tmtc")] {
|
||||||
use interface::udp::DynamicUdpTmHandler;
|
use interface::udp::DynamicUdpTmHandler;
|
||||||
use satrs::pus::EcssTcInVecConverter;
|
use satrs::pus::EcssTcVecCacher;
|
||||||
use tmtc::{tc_source::TcSourceTaskDynamic, tm_sink::TmSinkDynamic};
|
use tmtc::{tc_source::TcSourceTaskDynamic, tm_sink::TmSinkDynamic};
|
||||||
} else {
|
} else {
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use interface::udp::StaticUdpTmHandler;
|
use interface::udp::StaticUdpTmHandler;
|
||||||
use satrs::pus::EcssTcInSharedPoolConverter;
|
use satrs::pus::EcssTcInSharedPoolCacher;
|
||||||
use satrs::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
use satrs::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
||||||
use satrs_example::config::pool::create_static_pools;
|
use satrs_example::config::pool::create_static_pools;
|
||||||
use tmtc::{
|
use tmtc::{
|
||||||
@@ -124,13 +128,13 @@ fn main() {
|
|||||||
let mut request_map = GenericRequestRouter::default();
|
let mut request_map = GenericRequestRouter::default();
|
||||||
request_map
|
request_map
|
||||||
.composite_router_map
|
.composite_router_map
|
||||||
.insert(MGM_HANDLER_0.id(), mgm_0_handler_composite_tx);
|
.insert(MGM0.id(), mgm_0_handler_composite_tx);
|
||||||
request_map
|
request_map
|
||||||
.composite_router_map
|
.composite_router_map
|
||||||
.insert(MGM_HANDLER_0.id(), mgm_1_handler_composite_tx);
|
.insert(MGM1.id(), mgm_1_handler_composite_tx);
|
||||||
request_map
|
request_map
|
||||||
.composite_router_map
|
.composite_router_map
|
||||||
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
|
.insert(PCDU.id(), pcdu_handler_composite_tx);
|
||||||
|
|
||||||
// This helper structure is used by all telecommand providers which need to send telecommands
|
// This helper structure is used by all telecommand providers which need to send telecommands
|
||||||
// to the TC source.
|
// to the TC source.
|
||||||
@@ -139,9 +143,9 @@ fn main() {
|
|||||||
let tc_sender_with_shared_pool =
|
let tc_sender_with_shared_pool =
|
||||||
PacketSenderWithSharedPool::new(tc_source_tx, shared_tc_pool_wrapper.clone());
|
PacketSenderWithSharedPool::new(tc_source_tx, shared_tc_pool_wrapper.clone());
|
||||||
let tc_in_mem_converter =
|
let tc_in_mem_converter =
|
||||||
EcssTcInMemConverter::Static(EcssTcInSharedPoolConverter::new(shared_tc_pool, 4096));
|
EcssTcCacher::Static(EcssTcInSharedPoolCacher::new(shared_tc_pool, 4096));
|
||||||
} else if #[cfg(feature = "heap_tmtc")] {
|
} else if #[cfg(feature = "heap_tmtc")] {
|
||||||
let tc_in_mem_converter = EcssTcInMemConverter::Heap(EcssTcInVecConverter::default());
|
let tc_in_mem_converter = EcssTcCacher::Heap(EcssTcVecCacher::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,10 +305,8 @@ fn main() {
|
|||||||
|
|
||||||
let shared_mgm_0_set = Arc::default();
|
let shared_mgm_0_set = Arc::default();
|
||||||
let shared_mgm_1_set = Arc::default();
|
let shared_mgm_1_set = Arc::default();
|
||||||
let mgm_0_mode_node =
|
let mgm_0_mode_node = ModeRequestHandlerMpscBounded::new(MGM0.into(), mgm_0_handler_mode_rx);
|
||||||
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_0.into(), mgm_0_handler_mode_rx);
|
let mgm_1_mode_node = ModeRequestHandlerMpscBounded::new(MGM1.into(), mgm_1_handler_mode_rx);
|
||||||
let mgm_1_mode_node =
|
|
||||||
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_1.into(), mgm_1_handler_mode_rx);
|
|
||||||
let (mgm_0_spi_interface, mgm_1_spi_interface) =
|
let (mgm_0_spi_interface, mgm_1_spi_interface) =
|
||||||
if let Some(sim_client) = opt_sim_client.as_mut() {
|
if let Some(sim_client) = opt_sim_client.as_mut() {
|
||||||
sim_client
|
sim_client
|
||||||
@@ -328,7 +330,7 @@ fn main() {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
let mut mgm_0_handler = MgmHandlerLis3Mdl::new(
|
let mut mgm_0_handler = MgmHandlerLis3Mdl::new(
|
||||||
MGM_HANDLER_0,
|
MGM0,
|
||||||
"MGM_0",
|
"MGM_0",
|
||||||
mgm_0_mode_node,
|
mgm_0_mode_node,
|
||||||
mgm_0_handler_composite_rx,
|
mgm_0_handler_composite_rx,
|
||||||
@@ -339,7 +341,7 @@ fn main() {
|
|||||||
shared_mgm_0_set,
|
shared_mgm_0_set,
|
||||||
);
|
);
|
||||||
let mut mgm_1_handler = MgmHandlerLis3Mdl::new(
|
let mut mgm_1_handler = MgmHandlerLis3Mdl::new(
|
||||||
MGM_HANDLER_1,
|
MGM1,
|
||||||
"MGM_1",
|
"MGM_1",
|
||||||
mgm_1_mode_node,
|
mgm_1_mode_node,
|
||||||
mgm_1_handler_composite_rx,
|
mgm_1_handler_composite_rx,
|
||||||
@@ -372,10 +374,9 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default())
|
SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default())
|
||||||
};
|
};
|
||||||
let pcdu_mode_node =
|
let pcdu_mode_node = ModeRequestHandlerMpscBounded::new(PCDU.into(), pcdu_handler_mode_rx);
|
||||||
ModeRequestHandlerMpscBounded::new(PCDU_HANDLER.into(), pcdu_handler_mode_rx);
|
|
||||||
let mut pcdu_handler = PcduHandler::new(
|
let mut pcdu_handler = PcduHandler::new(
|
||||||
PCDU_HANDLER,
|
PCDU,
|
||||||
"PCDU",
|
"PCDU",
|
||||||
pcdu_mode_node,
|
pcdu_mode_node,
|
||||||
pcdu_handler_composite_rx,
|
pcdu_handler_composite_rx,
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ use satrs::pus::verification::{
|
|||||||
VerificationReportingProvider, VerificationToken,
|
VerificationReportingProvider, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTmSender, EcssTmtcError,
|
ActiveRequest, EcssTcAndToken, EcssTcCacher, EcssTmSender, EcssTmtcError,
|
||||||
GenericConversionError, MpscTcReceiver, PusPacketHandlingError, PusReplyHandler,
|
GenericConversionError, MpscTcReceiver, PusPacketHandlingError, PusReplyHandler,
|
||||||
PusServiceHelper, PusTcToRequestConverter,
|
PusServiceHelper, PusTcToRequestConverter,
|
||||||
};
|
};
|
||||||
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId};
|
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId};
|
||||||
use satrs_example::config::pus::PUS_ACTION_SERVICE;
|
|
||||||
use satrs_example::config::tmtc_err;
|
use satrs_example::config::tmtc_err;
|
||||||
|
use satrs_example::ids;
|
||||||
|
use satrs_example::ids::generic_pus::PUS_ACTION;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ impl PusTcToRequestConverter<ActivePusActionRequestStd, ActionRequest> for Actio
|
|||||||
verif_reporter: &impl VerificationReportingProvider,
|
verif_reporter: &impl VerificationReportingProvider,
|
||||||
time_stamp: &[u8],
|
time_stamp: &[u8],
|
||||||
) -> Result<(ActivePusActionRequestStd, ActionRequest), Self::Error> {
|
) -> Result<(ActivePusActionRequestStd, ActionRequest), Self::Error> {
|
||||||
let subservice = tc.subservice();
|
let subservice = tc.message_subtype_id();
|
||||||
let user_data = tc.user_data();
|
let user_data = tc.user_data();
|
||||||
if user_data.len() < 8 {
|
if user_data.len() < 8 {
|
||||||
verif_reporter
|
verif_reporter
|
||||||
@@ -207,17 +208,17 @@ impl PusTcToRequestConverter<ActivePusActionRequestStd, ActionRequest> for Actio
|
|||||||
|
|
||||||
pub fn create_action_service(
|
pub fn create_action_service(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: TmTcSender,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_in_mem_converter: EcssTcCacher,
|
||||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
action_router: GenericRequestRouter,
|
action_router: GenericRequestRouter,
|
||||||
reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
|
reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
|
||||||
) -> ActionServiceWrapper {
|
) -> ActionServiceWrapper {
|
||||||
let action_request_handler = PusTargetedRequestService::new(
|
let action_request_handler = PusTargetedRequestService::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_ACTION_SERVICE.id(),
|
ids::generic_pus::PUS_ACTION.id(),
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid),
|
create_verification_reporter(PUS_ACTION.id(), PUS_ACTION.apid),
|
||||||
tc_in_mem_converter,
|
tc_in_mem_converter,
|
||||||
),
|
),
|
||||||
ActionRequestConverter::default(),
|
ActionRequestConverter::default(),
|
||||||
@@ -269,12 +270,14 @@ impl TargetedPusService for ActionServiceWrapper {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
use satrs::pus::test_util::{
|
use satrs::pus::test_util::{
|
||||||
TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
|
TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
|
||||||
};
|
};
|
||||||
use satrs::pus::verification::test_util::TestVerificationReporter;
|
use satrs::pus::verification::test_util::TestVerificationReporter;
|
||||||
use satrs::pus::{verification, EcssTcInVecConverter};
|
use satrs::pus::{verification, EcssTcVecCacher};
|
||||||
use satrs::request::MessageMetadata;
|
use satrs::request::MessageMetadata;
|
||||||
|
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
use satrs::tmtc::PacketAsVec;
|
use satrs::tmtc::PacketAsVec;
|
||||||
use satrs::ComponentId;
|
use satrs::ComponentId;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
@@ -324,7 +327,7 @@ mod tests {
|
|||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
TmTcSender::Heap(tm_funnel_tx.clone()),
|
TmTcSender::Heap(tm_funnel_tx.clone()),
|
||||||
verif_reporter,
|
verif_reporter,
|
||||||
EcssTcInMemConverter::Heap(EcssTcInVecConverter::default()),
|
EcssTcCacher::Heap(EcssTcVecCacher::default()),
|
||||||
),
|
),
|
||||||
ActionRequestConverter::default(),
|
ActionRequestConverter::default(),
|
||||||
DefaultActiveActionRequestMap::default(),
|
DefaultActiveActionRequestMap::default(),
|
||||||
@@ -447,12 +450,13 @@ mod tests {
|
|||||||
);
|
);
|
||||||
// Create a basic action request and verify forwarding.
|
// Create a basic action request and verify forwarding.
|
||||||
let sp_header = SpHeader::new_from_apid(TEST_APID);
|
let sp_header = SpHeader::new_from_apid(TEST_APID);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
|
||||||
let action_id = 5_u32;
|
let action_id = 5_u32;
|
||||||
let mut app_data: [u8; 8] = [0; 8];
|
let mut app_data: [u8; 8] = [0; 8];
|
||||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_1.to_be_bytes());
|
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_1.as_u32().to_be_bytes());
|
||||||
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
|
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
|
||||||
let pus8_packet = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
let pus8_packet =
|
||||||
|
PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||||
testbench.add_tc(&pus8_packet);
|
testbench.add_tc(&pus8_packet);
|
||||||
let time_stamp: [u8; 7] = [0; 7];
|
let time_stamp: [u8; 7] = [0; 7];
|
||||||
testbench.verify_next_tc_is_handled_properly(&time_stamp);
|
testbench.verify_next_tc_is_handled_properly(&time_stamp);
|
||||||
@@ -488,7 +492,7 @@ mod tests {
|
|||||||
TEST_COMPONENT_ID_1.id(),
|
TEST_COMPONENT_ID_1.id(),
|
||||||
);
|
);
|
||||||
// Create a basic action request and verify forwarding.
|
// Create a basic action request and verify forwarding.
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
|
||||||
let action_id = 5_u32;
|
let action_id = 5_u32;
|
||||||
let mut app_data: [u8; 8] = [0; 8];
|
let mut app_data: [u8; 8] = [0; 8];
|
||||||
// Invalid ID, routing should fail.
|
// Invalid ID, routing should fail.
|
||||||
@@ -498,7 +502,7 @@ mod tests {
|
|||||||
SpHeader::new_from_apid(TEST_APID),
|
SpHeader::new_from_apid(TEST_APID),
|
||||||
sec_header,
|
sec_header,
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
testbench.add_tc(&pus8_packet);
|
testbench.add_tc(&pus8_packet);
|
||||||
let time_stamp: [u8; 7] = [0; 7];
|
let time_stamp: [u8; 7] = [0; 7];
|
||||||
@@ -514,17 +518,17 @@ mod tests {
|
|||||||
TEST_COMPONENT_ID_0.raw(),
|
TEST_COMPONENT_ID_0.raw(),
|
||||||
ActionRequestConverter::default(),
|
ActionRequestConverter::default(),
|
||||||
);
|
);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
|
||||||
let action_id = 5_u32;
|
let action_id = 5_u32;
|
||||||
let mut app_data: [u8; 8] = [0; 8];
|
let mut app_data: [u8; 8] = [0; 8];
|
||||||
// Invalid ID, routing should fail.
|
// Invalid ID, routing should fail.
|
||||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
|
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
|
||||||
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
|
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
|
||||||
let pus8_packet = PusTcCreator::new(
|
let pus8_packet = PusTcCreator::new(
|
||||||
SpHeader::new_from_apid(TEST_APID),
|
SpHeader::new_from_apid(TEST_APID),
|
||||||
sec_header,
|
sec_header,
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
let token = testbench.add_tc(&pus8_packet);
|
let token = testbench.add_tc(&pus8_packet);
|
||||||
let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0);
|
let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0);
|
||||||
@@ -550,11 +554,11 @@ mod tests {
|
|||||||
fn converter_action_req_with_data() {
|
fn converter_action_req_with_data() {
|
||||||
let mut testbench =
|
let mut testbench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ActionRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ActionRequestConverter::default());
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
|
||||||
let action_id = 5_u32;
|
let action_id = 5_u32;
|
||||||
let mut app_data: [u8; 16] = [0; 16];
|
let mut app_data: [u8; 16] = [0; 16];
|
||||||
// Invalid ID, routing should fail.
|
// Invalid ID, routing should fail.
|
||||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
|
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
|
||||||
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
|
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
app_data[i + 8] = i as u8;
|
app_data[i + 8] = i as u8;
|
||||||
@@ -563,7 +567,7 @@ mod tests {
|
|||||||
SpHeader::new_from_apid(TEST_APID),
|
SpHeader::new_from_apid(TEST_APID),
|
||||||
sec_header,
|
sec_header,
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
let token = testbench.add_tc(&pus8_packet);
|
let token = testbench.add_tc(&pus8_packet);
|
||||||
let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0);
|
let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0);
|
||||||
@@ -693,7 +697,7 @@ mod tests {
|
|||||||
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default());
|
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default());
|
||||||
let action_reply = ActionReplyPus::new(5_u32, ActionReplyVariant::Completed);
|
let action_reply = ActionReplyPus::new(5_u32, ActionReplyVariant::Completed);
|
||||||
let unrequested_reply =
|
let unrequested_reply =
|
||||||
GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), action_reply);
|
GenericMessage::new(MessageMetadata::new(10_u32, 15_u32), action_reply);
|
||||||
// Right now this function does not do a lot. We simply check that it does not panic or do
|
// Right now this function does not do a lot. We simply check that it does not panic or do
|
||||||
// weird stuff.
|
// weird stuff.
|
||||||
let result = testbench.handle_unrequested_reply(&unrequested_reply);
|
let result = testbench.handle_unrequested_reply(&unrequested_reply);
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ use satrs::pus::event_man::EventRequestWithToken;
|
|||||||
use satrs::pus::event_srv::PusEventServiceHandler;
|
use satrs::pus::event_srv::PusEventServiceHandler;
|
||||||
use satrs::pus::verification::VerificationReporter;
|
use satrs::pus::verification::VerificationReporter;
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver,
|
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcCacher, MpscTcReceiver,
|
||||||
PartialPusHandlingError, PusServiceHelper,
|
PartialPusHandlingError, PusServiceHelper,
|
||||||
};
|
};
|
||||||
use satrs::spacepackets::ecss::PusServiceId;
|
use satrs::spacepackets::ecss::PusServiceId;
|
||||||
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT;
|
use satrs_example::ids::generic_pus::PUS_EVENT_MANAGEMENT;
|
||||||
|
|
||||||
use super::{DirectPusService, HandlingStatus};
|
use super::{DirectPusService, HandlingStatus};
|
||||||
|
|
||||||
pub fn create_event_service(
|
pub fn create_event_service(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: TmTcSender,
|
||||||
tm_in_pool_converter: EcssTcInMemConverter,
|
tm_in_pool_converter: EcssTcCacher,
|
||||||
pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
||||||
) -> EventServiceWrapper {
|
) -> EventServiceWrapper {
|
||||||
@@ -36,12 +36,8 @@ pub fn create_event_service(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventServiceWrapper {
|
pub struct EventServiceWrapper {
|
||||||
pub handler: PusEventServiceHandler<
|
pub handler:
|
||||||
MpscTcReceiver,
|
PusEventServiceHandler<MpscTcReceiver, TmTcSender, EcssTcCacher, VerificationReporter>,
|
||||||
TmTcSender,
|
|
||||||
EcssTcInMemConverter,
|
|
||||||
VerificationReporter,
|
|
||||||
>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectPusService for EventServiceWrapper {
|
impl DirectPusService for EventServiceWrapper {
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ use satrs::pus::verification::{
|
|||||||
VerificationReportingProvider, VerificationToken,
|
VerificationReportingProvider, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken,
|
ActivePusRequestStd, ActiveRequest, DefaultActiveRequestMap, EcssTcAndToken, EcssTcCacher,
|
||||||
EcssTcInMemConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver,
|
EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver, PusPacketHandlingError,
|
||||||
PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter,
|
PusReplyHandler, PusServiceHelper, PusTcToRequestConverter,
|
||||||
};
|
};
|
||||||
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
||||||
use satrs::res_code::ResultU16;
|
use satrs::res_code::ResultU16;
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId};
|
use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId};
|
||||||
use satrs_example::config::pus::PUS_HK_SERVICE;
|
|
||||||
use satrs_example::config::{hk_err, tmtc_err};
|
use satrs_example::config::{hk_err, tmtc_err};
|
||||||
|
use satrs_example::ids::generic_pus::PUS_HK;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -164,11 +164,11 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
|
|||||||
found: 4,
|
found: 4,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let subservice = tc.subservice();
|
let subservice = tc.message_subtype_id();
|
||||||
let target_id_and_apid = UniqueApidTargetId::from_pus_tc(tc).expect("invalid tc format");
|
let target_id_and_apid = UniqueApidTargetId::from_pus_tc(tc).expect("invalid tc format");
|
||||||
let unique_id = u32::from_be_bytes(tc.user_data()[4..8].try_into().unwrap());
|
let unique_id = u32::from_be_bytes(tc.user_data()[4..8].try_into().unwrap());
|
||||||
|
|
||||||
let standard_subservice = hk::Subservice::try_from(subservice);
|
let standard_subservice = hk::MessageSubtypeId::try_from(subservice);
|
||||||
if standard_subservice.is_err() {
|
if standard_subservice.is_err() {
|
||||||
verif_reporter
|
verif_reporter
|
||||||
.start_failure(
|
.start_failure(
|
||||||
@@ -180,19 +180,22 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
|
|||||||
return Err(GenericConversionError::InvalidSubservice(subservice));
|
return Err(GenericConversionError::InvalidSubservice(subservice));
|
||||||
}
|
}
|
||||||
let request = match standard_subservice.unwrap() {
|
let request = match standard_subservice.unwrap() {
|
||||||
hk::Subservice::TcEnableHkGeneration | hk::Subservice::TcEnableDiagGeneration => {
|
hk::MessageSubtypeId::TcEnableHkGeneration
|
||||||
|
| hk::MessageSubtypeId::TcEnableDiagGeneration => {
|
||||||
HkRequest::new(unique_id, HkRequestVariant::EnablePeriodic)
|
HkRequest::new(unique_id, HkRequestVariant::EnablePeriodic)
|
||||||
}
|
}
|
||||||
hk::Subservice::TcDisableHkGeneration | hk::Subservice::TcDisableDiagGeneration => {
|
hk::MessageSubtypeId::TcDisableHkGeneration
|
||||||
|
| hk::MessageSubtypeId::TcDisableDiagGeneration => {
|
||||||
HkRequest::new(unique_id, HkRequestVariant::DisablePeriodic)
|
HkRequest::new(unique_id, HkRequestVariant::DisablePeriodic)
|
||||||
}
|
}
|
||||||
hk::Subservice::TcReportHkReportStructures => todo!(),
|
hk::MessageSubtypeId::TcReportHkReportStructures => todo!(),
|
||||||
hk::Subservice::TmHkPacket => todo!(),
|
hk::MessageSubtypeId::TmHkPacket => todo!(),
|
||||||
hk::Subservice::TcGenerateOneShotHk | hk::Subservice::TcGenerateOneShotDiag => {
|
hk::MessageSubtypeId::TcGenerateOneShotHk
|
||||||
|
| hk::MessageSubtypeId::TcGenerateOneShotDiag => {
|
||||||
HkRequest::new(unique_id, HkRequestVariant::OneShot)
|
HkRequest::new(unique_id, HkRequestVariant::OneShot)
|
||||||
}
|
}
|
||||||
hk::Subservice::TcModifyDiagCollectionInterval
|
hk::MessageSubtypeId::TcModifyDiagCollectionInterval
|
||||||
| hk::Subservice::TcModifyHkCollectionInterval => {
|
| hk::MessageSubtypeId::TcModifyHkCollectionInterval => {
|
||||||
if user_data.len() < 12 {
|
if user_data.len() < 12 {
|
||||||
verif_reporter
|
verif_reporter
|
||||||
.start_failure(
|
.start_failure(
|
||||||
@@ -242,17 +245,17 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
|
|||||||
|
|
||||||
pub fn create_hk_service(
|
pub fn create_hk_service(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: TmTcSender,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_in_mem_converter: EcssTcCacher,
|
||||||
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
request_router: GenericRequestRouter,
|
request_router: GenericRequestRouter,
|
||||||
reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>,
|
reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>,
|
||||||
) -> HkServiceWrapper {
|
) -> HkServiceWrapper {
|
||||||
let pus_3_handler = PusTargetedRequestService::new(
|
let pus_3_handler = PusTargetedRequestService::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_HK_SERVICE.id(),
|
PUS_HK.id(),
|
||||||
pus_hk_rx,
|
pus_hk_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid),
|
create_verification_reporter(PUS_HK.id(), PUS_HK.apid),
|
||||||
tc_in_mem_converter,
|
tc_in_mem_converter,
|
||||||
),
|
),
|
||||||
HkRequestConverter::default(),
|
HkRequestConverter::default(),
|
||||||
@@ -302,16 +305,19 @@ impl TargetedPusService for HkServiceWrapper {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
|
use arbitrary_int::{u14, u21};
|
||||||
use satrs::pus::test_util::{
|
use satrs::pus::test_util::{
|
||||||
TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
|
TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
|
||||||
};
|
};
|
||||||
use satrs::request::MessageMetadata;
|
use satrs::request::MessageMetadata;
|
||||||
|
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
use satrs::{
|
use satrs::{
|
||||||
hk::HkRequestVariant,
|
hk::HkRequestVariant,
|
||||||
pus::test_util::TEST_APID,
|
pus::test_util::TEST_APID,
|
||||||
request::GenericMessage,
|
request::GenericMessage,
|
||||||
spacepackets::{
|
spacepackets::{
|
||||||
ecss::{hk::Subservice, tc::PusTcCreator},
|
ecss::{hk::MessageSubtypeId, tc::PusTcCreator},
|
||||||
SpHeader,
|
SpHeader,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -328,19 +334,18 @@ mod tests {
|
|||||||
fn hk_converter_one_shot_req() {
|
fn hk_converter_one_shot_req() {
|
||||||
let mut hk_bench =
|
let mut hk_bench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let target_id = TEST_UNIQUE_ID_0;
|
let target_id = TEST_UNIQUE_ID_0;
|
||||||
let unique_id = 5_u32;
|
let unique_id = 5_u32;
|
||||||
let mut app_data: [u8; 8] = [0; 8];
|
let mut app_data: [u8; 8] = [0; 8];
|
||||||
app_data[0..4].copy_from_slice(&target_id.to_be_bytes());
|
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
|
||||||
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
||||||
|
|
||||||
let hk_req = PusTcCreator::new_simple(
|
let hk_req = PusTcCreator::new_simple(
|
||||||
sp_header,
|
sp_header,
|
||||||
3,
|
MessageTypeId::new(3, MessageSubtypeId::TcGenerateOneShotHk as u8),
|
||||||
Subservice::TcGenerateOneShotHk as u8,
|
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
let accepted_token = hk_bench.add_tc(&hk_req);
|
let accepted_token = hk_bench.add_tc(&hk_req);
|
||||||
let (_active_req, req) = hk_bench
|
let (_active_req, req) = hk_bench
|
||||||
@@ -358,11 +363,11 @@ mod tests {
|
|||||||
fn hk_converter_enable_periodic_generation() {
|
fn hk_converter_enable_periodic_generation() {
|
||||||
let mut hk_bench =
|
let mut hk_bench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let target_id = TEST_UNIQUE_ID_0;
|
let target_id = TEST_UNIQUE_ID_0;
|
||||||
let unique_id = 5_u32;
|
let unique_id = 5_u32;
|
||||||
let mut app_data: [u8; 8] = [0; 8];
|
let mut app_data: [u8; 8] = [0; 8];
|
||||||
app_data[0..4].copy_from_slice(&target_id.to_be_bytes());
|
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
|
||||||
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
||||||
let mut generic_check = |tc: &PusTcCreator| {
|
let mut generic_check = |tc: &PusTcCreator| {
|
||||||
let accepted_token = hk_bench.add_tc(tc);
|
let accepted_token = hk_bench.add_tc(tc);
|
||||||
@@ -377,18 +382,16 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let tc0 = PusTcCreator::new_simple(
|
let tc0 = PusTcCreator::new_simple(
|
||||||
sp_header,
|
sp_header,
|
||||||
3,
|
MessageTypeId::new(3, MessageSubtypeId::TcEnableHkGeneration as u8),
|
||||||
Subservice::TcEnableHkGeneration as u8,
|
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
generic_check(&tc0);
|
generic_check(&tc0);
|
||||||
let tc1 = PusTcCreator::new_simple(
|
let tc1 = PusTcCreator::new_simple(
|
||||||
sp_header,
|
sp_header,
|
||||||
3,
|
MessageTypeId::new(3, MessageSubtypeId::TcEnableDiagGeneration as u8),
|
||||||
Subservice::TcEnableDiagGeneration as u8,
|
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
generic_check(&tc1);
|
generic_check(&tc1);
|
||||||
}
|
}
|
||||||
@@ -397,11 +400,11 @@ mod tests {
|
|||||||
fn hk_conversion_disable_periodic_generation() {
|
fn hk_conversion_disable_periodic_generation() {
|
||||||
let mut hk_bench =
|
let mut hk_bench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let target_id = TEST_UNIQUE_ID_0;
|
let target_id = TEST_UNIQUE_ID_0;
|
||||||
let unique_id = 5_u32;
|
let unique_id = 5_u32;
|
||||||
let mut app_data: [u8; 8] = [0; 8];
|
let mut app_data: [u8; 8] = [0; 8];
|
||||||
app_data[0..4].copy_from_slice(&target_id.to_be_bytes());
|
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
|
||||||
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
||||||
let mut generic_check = |tc: &PusTcCreator| {
|
let mut generic_check = |tc: &PusTcCreator| {
|
||||||
let accepted_token = hk_bench.add_tc(tc);
|
let accepted_token = hk_bench.add_tc(tc);
|
||||||
@@ -416,18 +419,16 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let tc0 = PusTcCreator::new_simple(
|
let tc0 = PusTcCreator::new_simple(
|
||||||
sp_header,
|
sp_header,
|
||||||
3,
|
MessageTypeId::new(3, MessageSubtypeId::TcDisableHkGeneration as u8),
|
||||||
Subservice::TcDisableHkGeneration as u8,
|
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
generic_check(&tc0);
|
generic_check(&tc0);
|
||||||
let tc1 = PusTcCreator::new_simple(
|
let tc1 = PusTcCreator::new_simple(
|
||||||
sp_header,
|
sp_header,
|
||||||
3,
|
MessageTypeId::new(3, MessageSubtypeId::TcDisableDiagGeneration as u8),
|
||||||
Subservice::TcDisableDiagGeneration as u8,
|
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
generic_check(&tc1);
|
generic_check(&tc1);
|
||||||
}
|
}
|
||||||
@@ -436,12 +437,12 @@ mod tests {
|
|||||||
fn hk_conversion_modify_interval() {
|
fn hk_conversion_modify_interval() {
|
||||||
let mut hk_bench =
|
let mut hk_bench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let target_id = TEST_UNIQUE_ID_0;
|
let target_id = TEST_UNIQUE_ID_0;
|
||||||
let unique_id = 5_u32;
|
let unique_id = 5_u32;
|
||||||
let mut app_data: [u8; 12] = [0; 12];
|
let mut app_data: [u8; 12] = [0; 12];
|
||||||
let collection_interval_factor = 5_u32;
|
let collection_interval_factor = 5_u32;
|
||||||
app_data[0..4].copy_from_slice(&target_id.to_be_bytes());
|
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
|
||||||
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
|
||||||
app_data[8..12].copy_from_slice(&collection_interval_factor.to_be_bytes());
|
app_data[8..12].copy_from_slice(&collection_interval_factor.to_be_bytes());
|
||||||
|
|
||||||
@@ -459,18 +460,16 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let tc0 = PusTcCreator::new_simple(
|
let tc0 = PusTcCreator::new_simple(
|
||||||
sp_header,
|
sp_header,
|
||||||
3,
|
MessageTypeId::new(3, MessageSubtypeId::TcModifyHkCollectionInterval as u8),
|
||||||
Subservice::TcModifyHkCollectionInterval as u8,
|
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
generic_check(&tc0);
|
generic_check(&tc0);
|
||||||
let tc1 = PusTcCreator::new_simple(
|
let tc1 = PusTcCreator::new_simple(
|
||||||
sp_header,
|
sp_header,
|
||||||
3,
|
MessageTypeId::new(3, MessageSubtypeId::TcModifyDiagCollectionInterval as u8),
|
||||||
Subservice::TcModifyDiagCollectionInterval as u8,
|
|
||||||
&app_data,
|
&app_data,
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
generic_check(&tc1);
|
generic_check(&tc1);
|
||||||
}
|
}
|
||||||
@@ -479,8 +478,8 @@ mod tests {
|
|||||||
fn hk_reply_handler() {
|
fn hk_reply_handler() {
|
||||||
let mut reply_testbench =
|
let mut reply_testbench =
|
||||||
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), HkReplyHandler::default());
|
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), HkReplyHandler::default());
|
||||||
let sender_id = 2_u64;
|
let sender_id = 2_u32;
|
||||||
let apid_target_id = 3_u32;
|
let apid_target_id = u21::new(3);
|
||||||
let unique_id = 5_u32;
|
let unique_id = 5_u32;
|
||||||
let (req_id, active_req) = reply_testbench.add_tc(TEST_APID, apid_target_id, &[]);
|
let (req_id, active_req) = reply_testbench.add_tc(TEST_APID, apid_target_id, &[]);
|
||||||
let reply = GenericMessage::new(
|
let reply = GenericMessage::new(
|
||||||
@@ -501,7 +500,7 @@ mod tests {
|
|||||||
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_1.id(), HkReplyHandler::default());
|
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_1.id(), HkReplyHandler::default());
|
||||||
let action_reply = HkReply::new(5_u32, HkReplyVariant::Ack);
|
let action_reply = HkReply::new(5_u32, HkReplyVariant::Ack);
|
||||||
let unrequested_reply =
|
let unrequested_reply =
|
||||||
GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), action_reply);
|
GenericMessage::new(MessageMetadata::new(10_u32, 15_u32), action_reply);
|
||||||
// Right now this function does not do a lot. We simply check that it does not panic or do
|
// Right now this function does not do a lot. We simply check that it does not panic or do
|
||||||
// weird stuff.
|
// weird stuff.
|
||||||
let result = testbench.handle_unrequested_reply(&unrequested_reply);
|
let result = testbench.handle_unrequested_reply(&unrequested_reply);
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ use log::warn;
|
|||||||
use satrs::pool::PoolAddr;
|
use satrs::pool::PoolAddr;
|
||||||
use satrs::pus::verification::{
|
use satrs::pus::verification::{
|
||||||
self, FailParams, TcStateAccepted, TcStateStarted, VerificationReporter,
|
self, FailParams, TcStateAccepted, TcStateStarted, VerificationReporter,
|
||||||
VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
|
VerificationReporterConfig, VerificationReportingProvider, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConversionProvider,
|
ActiveRequest, ActiveRequestStore, CacheAndReadRawEcssTc, EcssTcAndToken, EcssTcCacher,
|
||||||
EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError,
|
EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, GenericRoutingError,
|
||||||
GenericRoutingError, HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter,
|
HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, PusServiceHelper,
|
||||||
PusServiceHelper, PusTcToRequestConverter, TcInMemory,
|
PusTcToRequestConverter, TcInMemory,
|
||||||
};
|
};
|
||||||
use satrs::queue::{GenericReceiveError, GenericSendError};
|
use satrs::queue::{GenericReceiveError, GenericSendError};
|
||||||
use satrs::request::{Apid, GenericMessage, MessageMetadata};
|
use satrs::request::{Apid, GenericMessage, MessageMetadata};
|
||||||
@@ -18,8 +18,8 @@ use satrs::spacepackets::ecss::tc::PusTcReader;
|
|||||||
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
||||||
use satrs::tmtc::{PacketAsVec, PacketInPool};
|
use satrs::tmtc::{PacketAsVec, PacketInPool};
|
||||||
use satrs::ComponentId;
|
use satrs::ComponentId;
|
||||||
use satrs_example::config::pus::PUS_ROUTING_SERVICE;
|
|
||||||
use satrs_example::config::{tmtc_err, CustomPusServiceId};
|
use satrs_example::config::{tmtc_err, CustomPusServiceId};
|
||||||
|
use satrs_example::ids::generic_pus::PUS_ROUTING;
|
||||||
use satrs_example::TimestampHelper;
|
use satrs_example::TimestampHelper;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
@@ -33,7 +33,7 @@ pub mod stack;
|
|||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter {
|
pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter {
|
||||||
let verif_cfg = VerificationReporterCfg::new(apid, 1, 2, 8).unwrap();
|
let verif_cfg = VerificationReporterConfig::new(apid, 1, 2, 8);
|
||||||
// Every software component which needs to generate verification telemetry, gets a cloned
|
// Every software component which needs to generate verification telemetry, gets a cloned
|
||||||
// verification reporter.
|
// verification reporter.
|
||||||
VerificationReporter::new(owner_id, &verif_cfg)
|
VerificationReporter::new(owner_id, &verif_cfg)
|
||||||
@@ -62,12 +62,9 @@ pub struct PusTcDistributor {
|
|||||||
impl PusTcDistributor {
|
impl PusTcDistributor {
|
||||||
pub fn new(tm_sender: TmTcSender, pus_router: PusTcMpscRouter) -> Self {
|
pub fn new(tm_sender: TmTcSender, pus_router: PusTcMpscRouter) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: PUS_ROUTING_SERVICE.raw(),
|
id: PUS_ROUTING.raw(),
|
||||||
tm_sender,
|
tm_sender,
|
||||||
verif_reporter: create_verification_reporter(
|
verif_reporter: create_verification_reporter(PUS_ROUTING.id(), PUS_ROUTING.apid),
|
||||||
PUS_ROUTING_SERVICE.id(),
|
|
||||||
PUS_ROUTING_SERVICE.apid,
|
|
||||||
),
|
|
||||||
pus_router,
|
pus_router,
|
||||||
stamp_helper: TimestampHelper::default(),
|
stamp_helper: TimestampHelper::default(),
|
||||||
}
|
}
|
||||||
@@ -105,7 +102,7 @@ impl PusTcDistributor {
|
|||||||
sender_id,
|
sender_id,
|
||||||
pus_tc_result.unwrap_err()
|
pus_tc_result.unwrap_err()
|
||||||
);
|
);
|
||||||
log::warn!("raw data: {:x?}", raw_tc);
|
log::warn!("raw data: {raw_tc:x?}");
|
||||||
// TODO: Shouldn't this be an error?
|
// TODO: Shouldn't this be an error?
|
||||||
return Ok(HandlingStatus::HandledOne);
|
return Ok(HandlingStatus::HandledOne);
|
||||||
}
|
}
|
||||||
@@ -116,7 +113,7 @@ impl PusTcDistributor {
|
|||||||
.verif_reporter
|
.verif_reporter
|
||||||
.acceptance_success(&self.tm_sender, init_token, self.stamp_helper.stamp())
|
.acceptance_success(&self.tm_sender, init_token, self.stamp_helper.stamp())
|
||||||
.expect("Acceptance success failure");
|
.expect("Acceptance success failure");
|
||||||
let service = PusServiceId::try_from(pus_tc.service());
|
let service = PusServiceId::try_from(pus_tc.service_type_id());
|
||||||
let tc_in_memory: TcInMemory = if let Some(store_addr) = addr_opt {
|
let tc_in_memory: TcInMemory = if let Some(store_addr) = addr_opt {
|
||||||
PacketInPool::new(sender_id, store_addr).into()
|
PacketInPool::new(sender_id, store_addr).into()
|
||||||
} else {
|
} else {
|
||||||
@@ -272,16 +269,16 @@ pub struct PusTargetedRequestService<
|
|||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
||||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
||||||
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestInfo>,
|
ActiveRequestMapInstance: ActiveRequestStore<ActiveRequestInfo>,
|
||||||
ActiveRequestInfo: ActiveRequestProvider,
|
ActiveRequestInfo: ActiveRequest,
|
||||||
RequestType,
|
RequestType,
|
||||||
ReplyType,
|
ReplyType,
|
||||||
> {
|
> {
|
||||||
pub service_helper:
|
pub service_helper:
|
||||||
PusServiceHelper<TcReceiver, TmTcSender, EcssTcInMemConverter, VerificationReporter>,
|
PusServiceHelper<TcReceiver, TmTcSender, EcssTcCacher, VerificationReporter>,
|
||||||
pub request_router: GenericRequestRouter,
|
pub request_router: GenericRequestRouter,
|
||||||
pub request_converter: RequestConverter,
|
pub request_converter: RequestConverter,
|
||||||
pub active_request_map: ActiveRequestMap,
|
pub active_request_map: ActiveRequestMapInstance,
|
||||||
pub reply_handler: ReplyHandler,
|
pub reply_handler: ReplyHandler,
|
||||||
pub reply_receiver: mpsc::Receiver<GenericMessage<ReplyType>>,
|
pub reply_receiver: mpsc::Receiver<GenericMessage<ReplyType>>,
|
||||||
phantom: std::marker::PhantomData<(RequestType, ActiveRequestInfo, ReplyType)>,
|
phantom: std::marker::PhantomData<(RequestType, ActiveRequestInfo, ReplyType)>,
|
||||||
@@ -292,8 +289,8 @@ impl<
|
|||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
||||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
||||||
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestInfo>,
|
ActiveRequestMapInstance: ActiveRequestStore<ActiveRequestInfo>,
|
||||||
ActiveRequestInfo: ActiveRequestProvider,
|
ActiveRequestInfo: ActiveRequest,
|
||||||
RequestType,
|
RequestType,
|
||||||
ReplyType,
|
ReplyType,
|
||||||
>
|
>
|
||||||
@@ -302,7 +299,7 @@ impl<
|
|||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
RequestConverter,
|
RequestConverter,
|
||||||
ReplyHandler,
|
ReplyHandler,
|
||||||
ActiveRequestMap,
|
ActiveRequestMapInstance,
|
||||||
ActiveRequestInfo,
|
ActiveRequestInfo,
|
||||||
RequestType,
|
RequestType,
|
||||||
ReplyType,
|
ReplyType,
|
||||||
@@ -314,11 +311,11 @@ where
|
|||||||
service_helper: PusServiceHelper<
|
service_helper: PusServiceHelper<
|
||||||
TcReceiver,
|
TcReceiver,
|
||||||
TmTcSender,
|
TmTcSender,
|
||||||
EcssTcInMemConverter,
|
EcssTcCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>,
|
>,
|
||||||
request_converter: RequestConverter,
|
request_converter: RequestConverter,
|
||||||
active_request_map: ActiveRequestMap,
|
active_request_map: ActiveRequestMapInstance,
|
||||||
reply_hook: ReplyHandler,
|
reply_hook: ReplyHandler,
|
||||||
request_router: GenericRequestRouter,
|
request_router: GenericRequestRouter,
|
||||||
reply_receiver: mpsc::Receiver<GenericMessage<ReplyType>>,
|
reply_receiver: mpsc::Receiver<GenericMessage<ReplyType>>,
|
||||||
@@ -512,7 +509,7 @@ where
|
|||||||
/// and also log the error.
|
/// and also log the error.
|
||||||
pub fn generic_pus_request_timeout_handler(
|
pub fn generic_pus_request_timeout_handler(
|
||||||
sender: &(impl EcssTmSender + ?Sized),
|
sender: &(impl EcssTmSender + ?Sized),
|
||||||
active_request: &(impl ActiveRequestProvider + Debug),
|
active_request: &(impl ActiveRequest + Debug),
|
||||||
verification_handler: &impl VerificationReportingProvider,
|
verification_handler: &impl VerificationReportingProvider,
|
||||||
time_stamp: &[u8],
|
time_stamp: &[u8],
|
||||||
service_str: &'static str,
|
service_str: &'static str,
|
||||||
@@ -534,13 +531,15 @@ pub fn generic_pus_request_timeout_handler(
|
|||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use arbitrary_int::{u11, u21};
|
||||||
use satrs::pus::test_util::TEST_COMPONENT_ID_0;
|
use satrs::pus::test_util::TEST_COMPONENT_ID_0;
|
||||||
use satrs::pus::{MpscTmAsVecSender, PusTmVariant};
|
use satrs::pus::{MpscTmAsVecSender, PusTmVariant};
|
||||||
use satrs::request::RequestId;
|
use satrs::request::RequestId;
|
||||||
|
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
use satrs::{
|
use satrs::{
|
||||||
pus::{
|
pus::{
|
||||||
verification::test_util::TestVerificationReporter, ActivePusRequestStd,
|
verification::test_util::TestVerificationReporter, ActivePusRequestStd,
|
||||||
ActiveRequestMapProvider, MpscTcReceiver,
|
ActiveRequestStore, MpscTcReceiver,
|
||||||
},
|
},
|
||||||
request::UniqueApidTargetId,
|
request::UniqueApidTargetId,
|
||||||
spacepackets::{
|
spacepackets::{
|
||||||
@@ -559,7 +558,7 @@ pub(crate) mod tests {
|
|||||||
// Testbench dedicated to the testing of [PusReplyHandler]s
|
// Testbench dedicated to the testing of [PusReplyHandler]s
|
||||||
pub struct ReplyHandlerTestbench<
|
pub struct ReplyHandlerTestbench<
|
||||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, Reply, Error = EcssTmtcError>,
|
ReplyHandler: PusReplyHandler<ActiveRequestInfo, Reply, Error = EcssTmtcError>,
|
||||||
ActiveRequestInfo: ActiveRequestProvider,
|
ActiveRequestInfo: ActiveRequest,
|
||||||
Reply,
|
Reply,
|
||||||
> {
|
> {
|
||||||
pub id: ComponentId,
|
pub id: ComponentId,
|
||||||
@@ -573,7 +572,7 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
impl<
|
impl<
|
||||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, Reply, Error = EcssTmtcError>,
|
ReplyHandler: PusReplyHandler<ActiveRequestInfo, Reply, Error = EcssTmtcError>,
|
||||||
ActiveRequestInfo: ActiveRequestProvider,
|
ActiveRequestInfo: ActiveRequest,
|
||||||
Reply,
|
Reply,
|
||||||
> ReplyHandlerTestbench<ReplyHandler, ActiveRequestInfo, Reply>
|
> ReplyHandlerTestbench<ReplyHandler, ActiveRequestInfo, Reply>
|
||||||
{
|
{
|
||||||
@@ -593,17 +592,17 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
pub fn add_tc(
|
pub fn add_tc(
|
||||||
&mut self,
|
&mut self,
|
||||||
apid: u16,
|
apid: u11,
|
||||||
apid_target: u32,
|
apid_target: u21,
|
||||||
time_stamp: &[u8],
|
time_stamp: &[u8],
|
||||||
) -> (verification::RequestId, ActivePusRequestStd) {
|
) -> (verification::RequestId, ActivePusRequestStd) {
|
||||||
let sp_header = SpHeader::new_from_apid(apid);
|
let sp_header = SpHeader::new_from_apid(apid);
|
||||||
let sec_header_dummy = PusTcSecondaryHeader::new_simple(0, 0);
|
let sec_header_dummy = PusTcSecondaryHeader::new_simple(MessageTypeId::new(0, 0));
|
||||||
let init = self.verif_reporter.start_verification(&PusTcCreator::new(
|
let init = self.verif_reporter.start_verification(&PusTcCreator::new(
|
||||||
sp_header,
|
sp_header,
|
||||||
sec_header_dummy,
|
sec_header_dummy,
|
||||||
&[],
|
&[],
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
));
|
));
|
||||||
let accepted = self
|
let accepted = self
|
||||||
.verif_reporter
|
.verif_reporter
|
||||||
@@ -674,7 +673,7 @@ pub(crate) mod tests {
|
|||||||
// Testbench dedicated to the testing of [PusTcToRequestConverter]s
|
// Testbench dedicated to the testing of [PusTcToRequestConverter]s
|
||||||
pub struct PusConverterTestbench<
|
pub struct PusConverterTestbench<
|
||||||
Converter: PusTcToRequestConverter<ActiveRequestInfo, Request, Error = GenericConversionError>,
|
Converter: PusTcToRequestConverter<ActiveRequestInfo, Request, Error = GenericConversionError>,
|
||||||
ActiveRequestInfo: ActiveRequestProvider,
|
ActiveRequestInfo: ActiveRequest,
|
||||||
Request,
|
Request,
|
||||||
> {
|
> {
|
||||||
pub id: ComponentId,
|
pub id: ComponentId,
|
||||||
@@ -688,7 +687,7 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
impl<
|
impl<
|
||||||
Converter: PusTcToRequestConverter<ActiveRequestInfo, Request, Error = GenericConversionError>,
|
Converter: PusTcToRequestConverter<ActiveRequestInfo, Request, Error = GenericConversionError>,
|
||||||
ActiveRequestInfo: ActiveRequestProvider,
|
ActiveRequestInfo: ActiveRequest,
|
||||||
Request,
|
Request,
|
||||||
> PusConverterTestbench<Converter, ActiveRequestInfo, Request>
|
> PusConverterTestbench<Converter, ActiveRequestInfo, Request>
|
||||||
{
|
{
|
||||||
@@ -722,8 +721,8 @@ pub(crate) mod tests {
|
|||||||
&mut self,
|
&mut self,
|
||||||
token: VerificationToken<TcStateAccepted>,
|
token: VerificationToken<TcStateAccepted>,
|
||||||
time_stamp: &[u8],
|
time_stamp: &[u8],
|
||||||
expected_apid: u16,
|
expected_apid: u11,
|
||||||
expected_apid_target: u32,
|
expected_apid_target: u21,
|
||||||
) -> Result<(ActiveRequestInfo, Request), Converter::Error> {
|
) -> Result<(ActiveRequestInfo, Request), Converter::Error> {
|
||||||
if self.current_packet.is_none() {
|
if self.current_packet.is_none() {
|
||||||
return Err(GenericConversionError::InvalidAppData(
|
return Err(GenericConversionError::InvalidAppData(
|
||||||
@@ -754,8 +753,8 @@ pub(crate) mod tests {
|
|||||||
pub struct TargetedPusRequestTestbench<
|
pub struct TargetedPusRequestTestbench<
|
||||||
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
||||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
||||||
ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestInfo>,
|
ActiveRequestMapInstance: ActiveRequestStore<ActiveRequestInfo>,
|
||||||
ActiveRequestInfo: ActiveRequestProvider,
|
ActiveRequestInfo: ActiveRequest,
|
||||||
RequestType,
|
RequestType,
|
||||||
ReplyType,
|
ReplyType,
|
||||||
> {
|
> {
|
||||||
@@ -764,7 +763,7 @@ pub(crate) mod tests {
|
|||||||
TestVerificationReporter,
|
TestVerificationReporter,
|
||||||
RequestConverter,
|
RequestConverter,
|
||||||
ReplyHandler,
|
ReplyHandler,
|
||||||
ActiveRequestMap,
|
ActiveRequestMapInstance,
|
||||||
ActiveRequestInfo,
|
ActiveRequestInfo,
|
||||||
RequestType,
|
RequestType,
|
||||||
ReplyType,
|
ReplyType,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
|
use arbitrary_int::u14;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::mode_tree::{ModeNode, ModeParent};
|
use satrs::mode_tree::{ModeNode, ModeParent};
|
||||||
use satrs_example::config::pus::PUS_MODE_SERVICE;
|
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
|
use satrs_example::ids;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -8,8 +11,8 @@ use crate::requests::GenericRequestRouter;
|
|||||||
use crate::tmtc::sender::TmTcSender;
|
use crate::tmtc::sender::TmTcSender;
|
||||||
use satrs::pus::verification::VerificationReporter;
|
use satrs::pus::verification::VerificationReporter;
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver,
|
DefaultActiveRequestMap, EcssTcAndToken, EcssTcCacher, MpscTcReceiver, PusPacketHandlingError,
|
||||||
PusPacketHandlingError, PusServiceHelper,
|
PusServiceHelper,
|
||||||
};
|
};
|
||||||
use satrs::request::GenericMessage;
|
use satrs::request::GenericMessage;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
@@ -20,8 +23,8 @@ use satrs::{
|
|||||||
self, FailParams, TcStateAccepted, TcStateStarted, VerificationReportingProvider,
|
self, FailParams, TcStateAccepted, TcStateStarted, VerificationReportingProvider,
|
||||||
VerificationToken,
|
VerificationToken,
|
||||||
},
|
},
|
||||||
ActivePusRequestStd, ActiveRequestProvider, EcssTmSender, EcssTmtcError,
|
ActivePusRequestStd, ActiveRequest, EcssTmSender, EcssTmtcError, GenericConversionError,
|
||||||
GenericConversionError, PusReplyHandler, PusTcToRequestConverter, PusTmVariant,
|
PusReplyHandler, PusTcToRequestConverter, PusTmVariant,
|
||||||
},
|
},
|
||||||
request::UniqueApidTargetId,
|
request::UniqueApidTargetId,
|
||||||
spacepackets::{
|
spacepackets::{
|
||||||
@@ -77,10 +80,19 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
|
|||||||
.write_to_be_bytes(&mut source_data)
|
.write_to_be_bytes(&mut source_data)
|
||||||
.expect("writing mode reply failed");
|
.expect("writing mode reply failed");
|
||||||
let req_id = verification::RequestId::from(reply.request_id());
|
let req_id = verification::RequestId::from(reply.request_id());
|
||||||
let sp_header = SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), u14::ZERO, 0);
|
||||||
let sec_header =
|
let sec_header = PusTmSecondaryHeader::new(
|
||||||
PusTmSecondaryHeader::new(200, Subservice::TmModeReply as u8, 0, 0, time_stamp);
|
MessageTypeId::new(200, Subservice::TmModeReply as u8),
|
||||||
let pus_tm = PusTmCreator::new(sp_header, sec_header, &source_data, true);
|
0,
|
||||||
|
0,
|
||||||
|
time_stamp,
|
||||||
|
);
|
||||||
|
let pus_tm = PusTmCreator::new(
|
||||||
|
sp_header,
|
||||||
|
sec_header,
|
||||||
|
&source_data,
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
tm_sender.send_tm(self.owner_id, PusTmVariant::Direct(pus_tm))?;
|
tm_sender.send_tm(self.owner_id, PusTmVariant::Direct(pus_tm))?;
|
||||||
verification_handler.completion_success(tm_sender, started_token, time_stamp)?;
|
verification_handler.completion_success(tm_sender, started_token, time_stamp)?;
|
||||||
}
|
}
|
||||||
@@ -146,7 +158,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
|
|||||||
verif_reporter: &impl VerificationReportingProvider,
|
verif_reporter: &impl VerificationReportingProvider,
|
||||||
time_stamp: &[u8],
|
time_stamp: &[u8],
|
||||||
) -> Result<(ActivePusRequestStd, ModeRequest), Self::Error> {
|
) -> Result<(ActivePusRequestStd, ModeRequest), Self::Error> {
|
||||||
let subservice = tc.subservice();
|
let subservice = tc.message_subtype_id();
|
||||||
let user_data = tc.user_data();
|
let user_data = tc.user_data();
|
||||||
let not_enough_app_data = |expected: usize| {
|
let not_enough_app_data = |expected: usize| {
|
||||||
verif_reporter
|
verif_reporter
|
||||||
@@ -210,22 +222,25 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
|
|||||||
|
|
||||||
pub fn create_mode_service(
|
pub fn create_mode_service(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: TmTcSender,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_in_mem_converter: EcssTcCacher,
|
||||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
mode_router: GenericRequestRouter,
|
mode_router: GenericRequestRouter,
|
||||||
reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>,
|
reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||||
) -> ModeServiceWrapper {
|
) -> ModeServiceWrapper {
|
||||||
let mode_request_handler = PusTargetedRequestService::new(
|
let mode_request_handler = PusTargetedRequestService::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_MODE_SERVICE.id(),
|
ids::generic_pus::PUS_MODE.id(),
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid),
|
create_verification_reporter(
|
||||||
|
ids::generic_pus::PUS_MODE.id(),
|
||||||
|
ids::generic_pus::PUS_MODE.apid,
|
||||||
|
),
|
||||||
tc_in_mem_converter,
|
tc_in_mem_converter,
|
||||||
),
|
),
|
||||||
ModeRequestConverter::default(),
|
ModeRequestConverter::default(),
|
||||||
DefaultActiveRequestMap::default(),
|
DefaultActiveRequestMap::default(),
|
||||||
ModeReplyHandler::new(PUS_MODE_SERVICE.id()),
|
ModeReplyHandler::new(ids::generic_pus::PUS_MODE.id()),
|
||||||
mode_router,
|
mode_router,
|
||||||
reply_receiver,
|
reply_receiver,
|
||||||
);
|
);
|
||||||
@@ -287,8 +302,11 @@ impl TargetedPusService for ModeServiceWrapper {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use arbitrary_int::traits::Integer;
|
||||||
|
use arbitrary_int::u14;
|
||||||
use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0, TEST_UNIQUE_ID_0};
|
use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0, TEST_UNIQUE_ID_0};
|
||||||
use satrs::request::MessageMetadata;
|
use satrs::request::MessageMetadata;
|
||||||
|
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
use satrs::{
|
use satrs::{
|
||||||
mode::{ModeAndSubmode, ModeReply, ModeRequest},
|
mode::{ModeAndSubmode, ModeReply, ModeRequest},
|
||||||
pus::mode::Subservice,
|
pus::mode::Subservice,
|
||||||
@@ -311,11 +329,12 @@ mod tests {
|
|||||||
fn mode_converter_read_mode_request() {
|
fn mode_converter_read_mode_request() {
|
||||||
let mut testbench =
|
let mut testbench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcReadMode as u8);
|
let sec_header =
|
||||||
|
PusTcSecondaryHeader::new_simple(MessageTypeId::new(200, Subservice::TcReadMode as u8));
|
||||||
let mut app_data: [u8; 4] = [0; 4];
|
let mut app_data: [u8; 4] = [0; 4];
|
||||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
|
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
|
||||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||||
let token = testbench.add_tc(&tc);
|
let token = testbench.add_tc(&tc);
|
||||||
let (_active_req, req) = testbench
|
let (_active_req, req) = testbench
|
||||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||||
@@ -327,15 +346,16 @@ mod tests {
|
|||||||
fn mode_converter_set_mode_request() {
|
fn mode_converter_set_mode_request() {
|
||||||
let mut testbench =
|
let mut testbench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcSetMode as u8);
|
let sec_header =
|
||||||
|
PusTcSecondaryHeader::new_simple(MessageTypeId::new(200, Subservice::TcSetMode as u8));
|
||||||
let mut app_data: [u8; 4 + ModeAndSubmode::RAW_LEN] = [0; 4 + ModeAndSubmode::RAW_LEN];
|
let mut app_data: [u8; 4 + ModeAndSubmode::RAW_LEN] = [0; 4 + ModeAndSubmode::RAW_LEN];
|
||||||
let mode_and_submode = ModeAndSubmode::new(2, 1);
|
let mode_and_submode = ModeAndSubmode::new(2, 1);
|
||||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
|
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
|
||||||
mode_and_submode
|
mode_and_submode
|
||||||
.write_to_be_bytes(&mut app_data[4..])
|
.write_to_be_bytes(&mut app_data[4..])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||||
let token = testbench.add_tc(&tc);
|
let token = testbench.add_tc(&tc);
|
||||||
let (_active_req, req) = testbench
|
let (_active_req, req) = testbench
|
||||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||||
@@ -353,11 +373,14 @@ mod tests {
|
|||||||
fn mode_converter_announce_mode() {
|
fn mode_converter_announce_mode() {
|
||||||
let mut testbench =
|
let mut testbench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceMode as u8);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
|
||||||
|
200,
|
||||||
|
Subservice::TcAnnounceMode as u8,
|
||||||
|
));
|
||||||
let mut app_data: [u8; 4] = [0; 4];
|
let mut app_data: [u8; 4] = [0; 4];
|
||||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
|
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
|
||||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||||
let token = testbench.add_tc(&tc);
|
let token = testbench.add_tc(&tc);
|
||||||
let (_active_req, req) = testbench
|
let (_active_req, req) = testbench
|
||||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||||
@@ -369,12 +392,14 @@ mod tests {
|
|||||||
fn mode_converter_announce_mode_recursively() {
|
fn mode_converter_announce_mode_recursively() {
|
||||||
let mut testbench =
|
let mut testbench =
|
||||||
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header =
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
|
||||||
PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceModeRecursive as u8);
|
200,
|
||||||
|
Subservice::TcAnnounceModeRecursive as u8,
|
||||||
|
));
|
||||||
let mut app_data: [u8; 4] = [0; 4];
|
let mut app_data: [u8; 4] = [0; 4];
|
||||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
|
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
|
||||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||||
let token = testbench.add_tc(&tc);
|
let token = testbench.add_tc(&tc);
|
||||||
let (_active_req, req) = testbench
|
let (_active_req, req) = testbench
|
||||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||||
@@ -390,7 +415,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let mode_reply = ModeReply::ModeReply(ModeAndSubmode::new(5, 1));
|
let mode_reply = ModeReply::ModeReply(ModeAndSubmode::new(5, 1));
|
||||||
let unrequested_reply =
|
let unrequested_reply =
|
||||||
GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), mode_reply);
|
GenericMessage::new(MessageMetadata::new(10_u32, 15_u32), mode_reply);
|
||||||
// Right now this function does not do a lot. We simply check that it does not panic or do
|
// Right now this function does not do a lot. We simply check that it does not panic or do
|
||||||
// weird stuff.
|
// weird stuff.
|
||||||
let result = testbench.handle_unrequested_reply(&unrequested_reply);
|
let result = testbench.handle_unrequested_reply(&unrequested_reply);
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ use satrs::pus::scheduler::{PusScheduler, TcInfo};
|
|||||||
use satrs::pus::scheduler_srv::PusSchedServiceHandler;
|
use satrs::pus::scheduler_srv::PusSchedServiceHandler;
|
||||||
use satrs::pus::verification::VerificationReporter;
|
use satrs::pus::verification::VerificationReporter;
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver,
|
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcCacher, MpscTcReceiver,
|
||||||
PartialPusHandlingError, PusServiceHelper,
|
PartialPusHandlingError, PusServiceHelper,
|
||||||
};
|
};
|
||||||
use satrs::spacepackets::ecss::PusServiceId;
|
use satrs::spacepackets::ecss::PusServiceId;
|
||||||
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool};
|
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool};
|
||||||
use satrs::ComponentId;
|
use satrs::ComponentId;
|
||||||
use satrs_example::config::pus::PUS_SCHED_SERVICE;
|
use satrs_example::ids::sched::PUS_SCHED;
|
||||||
|
|
||||||
use super::{DirectPusService, HandlingStatus};
|
use super::{DirectPusService, HandlingStatus};
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ pub struct SchedulingServiceWrapper {
|
|||||||
pub pus_11_handler: PusSchedServiceHandler<
|
pub pus_11_handler: PusSchedServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
TmTcSender,
|
TmTcSender,
|
||||||
EcssTcInMemConverter,
|
EcssTcCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
PusScheduler,
|
PusScheduler,
|
||||||
>,
|
>,
|
||||||
@@ -174,7 +174,7 @@ impl SchedulingServiceWrapper {
|
|||||||
|
|
||||||
pub fn create_scheduler_service(
|
pub fn create_scheduler_service(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: TmTcSender,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_in_mem_converter: EcssTcCacher,
|
||||||
tc_releaser: TcReleaser,
|
tc_releaser: TcReleaser,
|
||||||
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
sched_tc_pool: StaticMemoryPool,
|
sched_tc_pool: StaticMemoryPool,
|
||||||
@@ -183,10 +183,10 @@ pub fn create_scheduler_service(
|
|||||||
.expect("Creating PUS Scheduler failed");
|
.expect("Creating PUS Scheduler failed");
|
||||||
let pus_11_handler = PusSchedServiceHandler::new(
|
let pus_11_handler = PusSchedServiceHandler::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_SCHED_SERVICE.id(),
|
PUS_SCHED.id(),
|
||||||
pus_sched_rx,
|
pus_sched_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid),
|
create_verification_reporter(PUS_SCHED.id(), PUS_SCHED.apid),
|
||||||
tc_in_mem_converter,
|
tc_in_mem_converter,
|
||||||
),
|
),
|
||||||
scheduler,
|
scheduler,
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
use crate::pus::create_verification_reporter;
|
use crate::pus::create_verification_reporter;
|
||||||
use crate::tmtc::sender::TmTcSender;
|
use crate::tmtc::sender::TmTcSender;
|
||||||
use log::info;
|
use log::info;
|
||||||
use satrs::event_man::{EventMessage, EventMessageU32};
|
use satrs::event_man_legacy::{EventMessage, EventMessageU32};
|
||||||
use satrs::pus::test::PusService17TestHandler;
|
use satrs::pus::test::PusService17TestHandler;
|
||||||
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
|
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
|
||||||
use satrs::pus::PartialPusHandlingError;
|
use satrs::pus::PartialPusHandlingError;
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConversionProvider,
|
CacheAndReadRawEcssTc, DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcCacher,
|
||||||
EcssTcInMemConverter, MpscTcReceiver, PusServiceHelper,
|
MpscTcReceiver, PusServiceHelper,
|
||||||
};
|
};
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
||||||
use satrs_example::config::pus::PUS_TEST_SERVICE;
|
|
||||||
use satrs_example::config::{tmtc_err, TEST_EVENT};
|
use satrs_example::config::{tmtc_err, TEST_EVENT};
|
||||||
|
use satrs_example::ids::generic_pus::PUS_TEST;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use super::{DirectPusService, HandlingStatus};
|
use super::{DirectPusService, HandlingStatus};
|
||||||
|
|
||||||
pub fn create_test_service(
|
pub fn create_test_service(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: TmTcSender,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_in_mem_converter: EcssTcCacher,
|
||||||
event_sender: mpsc::SyncSender<EventMessageU32>,
|
event_sender: mpsc::SyncSender<EventMessageU32>,
|
||||||
pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
) -> TestCustomServiceWrapper {
|
) -> TestCustomServiceWrapper {
|
||||||
let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
|
let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
|
||||||
PUS_TEST_SERVICE.id(),
|
PUS_TEST.id(),
|
||||||
pus_test_rx,
|
pus_test_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid),
|
create_verification_reporter(PUS_TEST.id(), PUS_TEST.apid),
|
||||||
tc_in_mem_converter,
|
tc_in_mem_converter,
|
||||||
));
|
));
|
||||||
TestCustomServiceWrapper {
|
TestCustomServiceWrapper {
|
||||||
@@ -37,12 +37,8 @@ pub fn create_test_service(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestCustomServiceWrapper {
|
pub struct TestCustomServiceWrapper {
|
||||||
pub handler: PusService17TestHandler<
|
pub handler:
|
||||||
MpscTcReceiver,
|
PusService17TestHandler<MpscTcReceiver, TmTcSender, EcssTcCacher, VerificationReporter>,
|
||||||
TmTcSender,
|
|
||||||
EcssTcInMemConverter,
|
|
||||||
VerificationReporter,
|
|
||||||
>,
|
|
||||||
pub event_tx: mpsc::SyncSender<EventMessageU32>,
|
pub event_tx: mpsc::SyncSender<EventMessageU32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +96,7 @@ impl DirectPusService for TestCustomServiceWrapper {
|
|||||||
if subservice == 128 {
|
if subservice == 128 {
|
||||||
info!("generating test event");
|
info!("generating test event");
|
||||||
self.event_tx
|
self.event_tx
|
||||||
.send(EventMessage::new(PUS_TEST_SERVICE.id(), TEST_EVENT.into()))
|
.send(EventMessage::new(PUS_TEST.id(), TEST_EVENT.into()))
|
||||||
.expect("Sending test event failed");
|
.expect("Sending test event failed");
|
||||||
match self.handler.service_helper.verif_reporter().start_success(
|
match self.handler.service_helper.verif_reporter().start_success(
|
||||||
self.handler.service_helper.tm_sender(),
|
self.handler.service_helper.tm_sender(),
|
||||||
@@ -126,7 +122,7 @@ impl DirectPusService for TestCustomServiceWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let fail_data = [tc.subservice()];
|
let fail_data = [tc.message_subtype_id()];
|
||||||
self.handler
|
self.handler
|
||||||
.service_helper
|
.service_helper
|
||||||
.verif_reporter()
|
.verif_reporter()
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ use satrs::mode::ModeRequest;
|
|||||||
use satrs::pus::verification::{
|
use satrs::pus::verification::{
|
||||||
FailParams, TcStateAccepted, VerificationReportingProvider, VerificationToken,
|
FailParams, TcStateAccepted, VerificationReportingProvider, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{ActiveRequestProvider, EcssTmSender, GenericRoutingError, PusRequestRouter};
|
use satrs::pus::{ActiveRequest, EcssTmSender, GenericRoutingError, PusRequestRouter};
|
||||||
use satrs::queue::GenericSendError;
|
use satrs::queue::GenericSendError;
|
||||||
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::PusPacket;
|
use satrs::spacepackets::ecss::PusPacket;
|
||||||
use satrs::ComponentId;
|
use satrs::ComponentId;
|
||||||
use satrs_example::config::pus::PUS_ROUTING_SERVICE;
|
|
||||||
use satrs_example::config::tmtc_err;
|
use satrs_example::config::tmtc_err;
|
||||||
|
use satrs_example::ids;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@@ -37,7 +37,7 @@ pub struct GenericRequestRouter {
|
|||||||
impl Default for GenericRequestRouter {
|
impl Default for GenericRequestRouter {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: PUS_ROUTING_SERVICE.raw(),
|
id: ids::generic_pus::PUS_ROUTING.raw(),
|
||||||
composite_router_map: Default::default(),
|
composite_router_map: Default::default(),
|
||||||
mode_router_map: Default::default(),
|
mode_router_map: Default::default(),
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ impl Default for GenericRequestRouter {
|
|||||||
impl GenericRequestRouter {
|
impl GenericRequestRouter {
|
||||||
pub(crate) fn handle_error_generic(
|
pub(crate) fn handle_error_generic(
|
||||||
&self,
|
&self,
|
||||||
active_request: &impl ActiveRequestProvider,
|
active_request: &impl ActiveRequest,
|
||||||
tc: &PusTcReader,
|
tc: &PusTcReader,
|
||||||
error: GenericRoutingError,
|
error: GenericRoutingError,
|
||||||
tm_sender: &(impl EcssTmSender + ?Sized),
|
tm_sender: &(impl EcssTmSender + ?Sized),
|
||||||
@@ -55,7 +55,7 @@ impl GenericRequestRouter {
|
|||||||
) {
|
) {
|
||||||
warn!(
|
warn!(
|
||||||
"Routing request for service {} failed: {error:?}",
|
"Routing request for service {} failed: {error:?}",
|
||||||
tc.service()
|
tc.service_type_id()
|
||||||
);
|
);
|
||||||
let accepted_token: VerificationToken<TcStateAccepted> = active_request
|
let accepted_token: VerificationToken<TcStateAccepted> = active_request
|
||||||
.token()
|
.token()
|
||||||
@@ -66,7 +66,8 @@ impl GenericRequestRouter {
|
|||||||
let apid_target_id = UniqueApidTargetId::from(id);
|
let apid_target_id = UniqueApidTargetId::from(id);
|
||||||
warn!("Target APID for request: {}", apid_target_id.apid);
|
warn!("Target APID for request: {}", apid_target_id.apid);
|
||||||
warn!("Target Unique ID for request: {}", apid_target_id.unique_id);
|
warn!("Target Unique ID for request: {}", apid_target_id.unique_id);
|
||||||
let mut fail_data: [u8; 8] = [0; 8];
|
let mut fail_data: [u8; core::mem::size_of::<ComponentId>()] =
|
||||||
|
[0; core::mem::size_of::<ComponentId>()];
|
||||||
fail_data.copy_from_slice(&id.to_be_bytes());
|
fail_data.copy_from_slice(&id.to_be_bytes());
|
||||||
verif_reporter
|
verif_reporter
|
||||||
.completion_failure(
|
.completion_failure(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use satrs::{
|
|||||||
pus::EcssTmSender,
|
pus::EcssTmSender,
|
||||||
queue::GenericSendError,
|
queue::GenericSendError,
|
||||||
spacepackets::ecss::WritablePusPacket,
|
spacepackets::ecss::WritablePusPacket,
|
||||||
tmtc::{PacketAsVec, PacketSenderRaw, PacketSenderWithSharedPool, StoreAndSendError},
|
tmtc::{PacketAsVec, PacketHandler, PacketSenderWithSharedPool},
|
||||||
ComponentId,
|
ComponentId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,26 +48,33 @@ impl EcssTmSender for TmTcSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSenderRaw for TmTcSender {
|
impl PacketHandler for TmTcSender {
|
||||||
type Error = StoreAndSendError;
|
type Error = GenericSendError;
|
||||||
|
|
||||||
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
||||||
match self {
|
match self {
|
||||||
TmTcSender::Static(packet_sender_with_shared_pool) => {
|
TmTcSender::Static(packet_sender_with_shared_pool) => {
|
||||||
packet_sender_with_shared_pool.send_packet(sender_id, packet)
|
if let Err(e) = packet_sender_with_shared_pool.handle_packet(sender_id, packet) {
|
||||||
|
log::error!("Error sending packet via Static TM/TC sender: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TmTcSender::Heap(sync_sender) => {
|
||||||
|
if let Err(e) = sync_sender.handle_packet(sender_id, packet) {
|
||||||
|
log::error!("Error sending packet via Heap TM/TC sender: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TmTcSender::Mock(sender) => {
|
||||||
|
sender.handle_packet(sender_id, packet).unwrap();
|
||||||
}
|
}
|
||||||
TmTcSender::Heap(sync_sender) => sync_sender
|
|
||||||
.send_packet(sender_id, packet)
|
|
||||||
.map_err(StoreAndSendError::Send),
|
|
||||||
TmTcSender::Mock(sender) => sender.send_packet(sender_id, packet),
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSenderRaw for MockSender {
|
impl PacketHandler for MockSender {
|
||||||
type Error = StoreAndSendError;
|
type Error = GenericSendError;
|
||||||
|
|
||||||
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
let mut mut_queue = self.0.borrow_mut();
|
let mut mut_queue = self.0.borrow_mut();
|
||||||
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ use crate::pus::PusTcDistributor;
|
|||||||
pub struct TcSourceTaskStatic {
|
pub struct TcSourceTaskStatic {
|
||||||
shared_tc_pool: SharedPacketPool,
|
shared_tc_pool: SharedPacketPool,
|
||||||
tc_receiver: mpsc::Receiver<PacketInPool>,
|
tc_receiver: mpsc::Receiver<PacketInPool>,
|
||||||
tc_buf: [u8; 4096],
|
/// We allocate this buffer from the heap to avoid a clippy warning on large enum variant
|
||||||
|
/// differences.
|
||||||
|
tc_buf: Box<[u8; 4096]>,
|
||||||
pus_distributor: PusTcDistributor,
|
pus_distributor: PusTcDistributor,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +27,7 @@ impl TcSourceTaskStatic {
|
|||||||
Self {
|
Self {
|
||||||
shared_tc_pool,
|
shared_tc_pool,
|
||||||
tc_receiver,
|
tc_receiver,
|
||||||
tc_buf: [0; 4096],
|
tc_buf: Box::new([0; 4096]),
|
||||||
pus_distributor: pus_receiver,
|
pus_distributor: pus_receiver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,11 +46,11 @@ impl TcSourceTaskStatic {
|
|||||||
.0
|
.0
|
||||||
.read()
|
.read()
|
||||||
.expect("locking tc pool failed");
|
.expect("locking tc pool failed");
|
||||||
pool.read(&packet_in_pool.store_addr, &mut self.tc_buf)
|
pool.read(&packet_in_pool.store_addr, self.tc_buf.as_mut_slice())
|
||||||
.expect("reading pool failed");
|
.expect("reading pool failed");
|
||||||
drop(pool);
|
drop(pool);
|
||||||
self.pus_distributor
|
self.pus_distributor
|
||||||
.handle_tc_packet_in_store(packet_in_pool, &self.tc_buf)
|
.handle_tc_packet_in_store(packet_in_pool, self.tc_buf.as_slice())
|
||||||
.ok();
|
.ok();
|
||||||
HandlingStatus::HandledOne
|
HandlingStatus::HandledOne
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,17 @@ use std::{
|
|||||||
sync::mpsc::{self},
|
sync::mpsc::{self},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use arbitrary_int::{u11, u14};
|
||||||
use log::info;
|
use log::info;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
pool::PoolProvider,
|
pool::PoolProvider,
|
||||||
spacepackets::{
|
spacepackets::{
|
||||||
ecss::{tm::PusTmZeroCopyWriter, PusPacket},
|
ecss::{tm::PusTmZeroCopyWriter, PusPacket},
|
||||||
seq_count::CcsdsSimpleSeqCountProvider,
|
seq_count::SequenceCounter,
|
||||||
|
seq_count::SequenceCounterCcsdsSimple,
|
||||||
time::cds::MIN_CDS_FIELD_LEN,
|
time::cds::MIN_CDS_FIELD_LEN,
|
||||||
CcsdsPacket,
|
CcsdsPacket,
|
||||||
},
|
},
|
||||||
};
|
|
||||||
use satrs::{
|
|
||||||
spacepackets::seq_count::SequenceCountProvider,
|
|
||||||
tmtc::{PacketAsVec, PacketInPool, SharedPacketPool},
|
tmtc::{PacketAsVec, PacketInPool, SharedPacketPool},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,14 +21,16 @@ use crate::interface::tcp::SyncTcpTmSource;
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CcsdsSeqCounterMap {
|
pub struct CcsdsSeqCounterMap {
|
||||||
apid_seq_counter_map: HashMap<u16, CcsdsSimpleSeqCountProvider>,
|
apid_seq_counter_map: HashMap<u11, SequenceCounterCcsdsSimple>,
|
||||||
}
|
}
|
||||||
impl CcsdsSeqCounterMap {
|
impl CcsdsSeqCounterMap {
|
||||||
pub fn get_and_increment(&mut self, apid: u16) -> u16 {
|
pub fn get_and_increment(&mut self, apid: u11) -> u14 {
|
||||||
self.apid_seq_counter_map
|
u14::new(
|
||||||
.entry(apid)
|
self.apid_seq_counter_map
|
||||||
.or_default()
|
.entry(apid)
|
||||||
.get_and_increment()
|
.or_default()
|
||||||
|
.get_and_increment(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ impl TmFunnelCommon {
|
|||||||
);
|
);
|
||||||
let entry = self
|
let entry = self
|
||||||
.msg_counter_map
|
.msg_counter_map
|
||||||
.entry(zero_copy_writer.service())
|
.entry(zero_copy_writer.service_type_id())
|
||||||
.or_insert(0);
|
.or_insert(0);
|
||||||
zero_copy_writer.set_msg_count(*entry);
|
zero_copy_writer.set_msg_count(*entry);
|
||||||
if *entry == u16::MAX {
|
if *entry == u16::MAX {
|
||||||
@@ -75,8 +76,8 @@ impl TmFunnelCommon {
|
|||||||
fn packet_printout(tm: &PusTmZeroCopyWriter) {
|
fn packet_printout(tm: &PusTmZeroCopyWriter) {
|
||||||
info!(
|
info!(
|
||||||
"Sending PUS TM[{},{}] with APID {}",
|
"Sending PUS TM[{},{}] with APID {}",
|
||||||
tm.service(),
|
tm.service_type_id(),
|
||||||
tm.subservice(),
|
tm.message_subtype_id(),
|
||||||
tm.apid()
|
tm.apid()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -114,7 +115,7 @@ impl TmSinkStatic {
|
|||||||
let mut tm_copy = Vec::new();
|
let mut tm_copy = Vec::new();
|
||||||
pool_guard
|
pool_guard
|
||||||
.modify(&pus_tm_in_pool.store_addr, |buf| {
|
.modify(&pus_tm_in_pool.store_addr, |buf| {
|
||||||
let zero_copy_writer = PusTmZeroCopyWriter::new(buf, MIN_CDS_FIELD_LEN)
|
let zero_copy_writer = PusTmZeroCopyWriter::new(buf, MIN_CDS_FIELD_LEN, true)
|
||||||
.expect("Creating TM zero copy writer failed");
|
.expect("Creating TM zero copy writer failed");
|
||||||
self.common.apply_packet_processing(zero_copy_writer);
|
self.common.apply_packet_processing(zero_copy_writer);
|
||||||
tm_copy = buf.to_vec()
|
tm_copy = buf.to_vec()
|
||||||
@@ -154,8 +155,9 @@ impl TmSinkDynamic {
|
|||||||
if let Ok(mut tm) = self.tm_funnel_rx.recv() {
|
if let Ok(mut tm) = self.tm_funnel_rx.recv() {
|
||||||
// Read the TM, set sequence counter and message counter, and finally update
|
// Read the TM, set sequence counter and message counter, and finally update
|
||||||
// the CRC.
|
// the CRC.
|
||||||
let zero_copy_writer = PusTmZeroCopyWriter::new(&mut tm.packet, MIN_CDS_FIELD_LEN)
|
let zero_copy_writer =
|
||||||
.expect("Creating TM zero copy writer failed");
|
PusTmZeroCopyWriter::new(&mut tm.packet, MIN_CDS_FIELD_LEN, true)
|
||||||
|
.expect("Creating TM zero copy writer failed");
|
||||||
self.common.apply_packet_processing(zero_copy_writer);
|
self.common.apply_packet_processing(zero_copy_writer);
|
||||||
self.common.sync_tm_tcp_source.add_tm(&tm.packet);
|
self.common.sync_tm_tcp_source.add_tm(&tm.packet);
|
||||||
self.tm_server_tx
|
self.tm_server_tx
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
# [v0.1.3] 2024-08-26
|
||||||
|
|
||||||
|
Bump `satrs-shared`.
|
||||||
|
|
||||||
# [v0.1.2] 2024-04-17
|
# [v0.1.2] 2024-04-17
|
||||||
|
|
||||||
Allow `satrs-shared` from `v0.1.3` to `<v0.2`.
|
Allow `satrs-shared` from `v0.1.3` to `<v0.2`.
|
||||||
@@ -19,3 +23,6 @@ Allow `satrs-shared` from `v0.1.3` to `<v0.2`.
|
|||||||
# [v0.1.0] 2024-02-12
|
# [v0.1.0] 2024-02-12
|
||||||
|
|
||||||
Initial release containing the `resultcode` macro.
|
Initial release containing the `resultcode` macro.
|
||||||
|
|
||||||
|
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-mib-v0.1.3...HEAD
|
||||||
|
[v0.1.3]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-mib-v0.1.2...satrs-mib-v0.1.3
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "satrs-mib"
|
name = "satrs-mib"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.61"
|
rust-version = "1.61"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
@@ -23,7 +23,7 @@ version = "1"
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.satrs-shared]
|
[dependencies.satrs-shared]
|
||||||
version = ">=0.1.3, <=0.2"
|
version = "0.2"
|
||||||
path = "../satrs-shared"
|
path = "../satrs-shared"
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ features = ["full"]
|
|||||||
trybuild = { version = "1", features = ["diff"] }
|
trybuild = { version = "1", features = ["diff"] }
|
||||||
|
|
||||||
[dev-dependencies.satrs-shared]
|
[dev-dependencies.satrs-shared]
|
||||||
|
version = "0.2"
|
||||||
path = "../../satrs-shared"
|
path = "../../satrs-shared"
|
||||||
version = ">=0.1.3, <=0.2"
|
|
||||||
|
|
||||||
[dev-dependencies.satrs-mib]
|
[dev-dependencies.satrs-mib]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ serde_json = "1"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
fern = "0.7"
|
fern = "0.7"
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.27", features = ["derive"] }
|
||||||
num_enum = "0.7"
|
num_enum = "0.7"
|
||||||
humantime = "2"
|
humantime = "2"
|
||||||
tai-time = { version = "0.3", features = ["serde"] }
|
tai-time = { version = "0.3", features = ["serde"] }
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ impl SimController {
|
|||||||
fn handle_ctrl_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
|
fn handle_ctrl_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
|
||||||
let sim_ctrl_request = SimCtrlRequest::from_sim_message(request)?;
|
let sim_ctrl_request = SimCtrlRequest::from_sim_message(request)?;
|
||||||
if SIM_CTRL_REQ_WIRETAPPING {
|
if SIM_CTRL_REQ_WIRETAPPING {
|
||||||
log::info!("received sim ctrl request: {:?}", sim_ctrl_request);
|
log::info!("received sim ctrl request: {sim_ctrl_request:?}");
|
||||||
}
|
}
|
||||||
match sim_ctrl_request {
|
match sim_ctrl_request {
|
||||||
SimCtrlRequest::Ping => {
|
SimCtrlRequest::Ping => {
|
||||||
@@ -139,7 +139,7 @@ impl SimController {
|
|||||||
) -> Result<(), SimRequestError> {
|
) -> Result<(), SimRequestError> {
|
||||||
let mgm_request = MgmRequestLis3Mdl::from_sim_message(request)?;
|
let mgm_request = MgmRequestLis3Mdl::from_sim_message(request)?;
|
||||||
if MGM_REQ_WIRETAPPING {
|
if MGM_REQ_WIRETAPPING {
|
||||||
log::info!("received MGM request: {:?}", mgm_request);
|
log::info!("received MGM request: {mgm_request:?}");
|
||||||
}
|
}
|
||||||
match mgm_request {
|
match mgm_request {
|
||||||
MgmRequestLis3Mdl::RequestSensorData => {
|
MgmRequestLis3Mdl::RequestSensorData => {
|
||||||
@@ -160,7 +160,7 @@ impl SimController {
|
|||||||
fn handle_pcdu_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
|
fn handle_pcdu_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
|
||||||
let pcdu_request = PcduRequest::from_sim_message(request)?;
|
let pcdu_request = PcduRequest::from_sim_message(request)?;
|
||||||
if PCDU_REQ_WIRETAPPING {
|
if PCDU_REQ_WIRETAPPING {
|
||||||
log::info!("received PCDU request: {:?}", pcdu_request);
|
log::info!("received PCDU request: {pcdu_request:?}");
|
||||||
}
|
}
|
||||||
match pcdu_request {
|
match pcdu_request {
|
||||||
PcduRequest::RequestSwitchInfo => {
|
PcduRequest::RequestSwitchInfo => {
|
||||||
@@ -188,7 +188,7 @@ impl SimController {
|
|||||||
fn handle_mgt_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
|
fn handle_mgt_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
|
||||||
let mgt_request = MgtRequest::from_sim_message(request)?;
|
let mgt_request = MgtRequest::from_sim_message(request)?;
|
||||||
if MGT_REQ_WIRETAPPING {
|
if MGT_REQ_WIRETAPPING {
|
||||||
log::info!("received MGT request: {:?}", mgt_request);
|
log::info!("received MGT request: {mgt_request:?}");
|
||||||
}
|
}
|
||||||
match mgt_request {
|
match mgt_request {
|
||||||
MgtRequest::ApplyTorque { duration, dipole } => self
|
MgtRequest::ApplyTorque { duration, dipole } => self
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ fn main() {
|
|||||||
let mut udp_server =
|
let mut udp_server =
|
||||||
SimUdpServer::new(SIM_CTRL_PORT, request_sender, reply_receiver, 200, None)
|
SimUdpServer::new(SIM_CTRL_PORT, request_sender, reply_receiver, 200, None)
|
||||||
.expect("could not create UDP request server");
|
.expect("could not create UDP request server");
|
||||||
log::info!("starting UDP server on port {}", SIM_CTRL_PORT);
|
log::info!("starting UDP server on port {SIM_CTRL_PORT}");
|
||||||
// This thread manages the simulator UDP server.
|
// This thread manages the simulator UDP server.
|
||||||
let udp_tc_thread = thread::spawn(move || {
|
let udp_tc_thread = thread::spawn(move || {
|
||||||
udp_server.run();
|
udp_server.run();
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
# [v0.2.4] 2025-11-06
|
||||||
|
|
||||||
|
`spacepackets` v0.17.0
|
||||||
|
|
||||||
|
# [v0.2.3] 2025-07-22
|
||||||
|
|
||||||
|
`spacepackets` range v0.14 to v0.15
|
||||||
|
|
||||||
# [v0.2.2] 2025-05-10
|
# [v0.2.2] 2025-05-10
|
||||||
|
|
||||||
- Bump to `spacepackests` v0.14
|
- Bump to `spacepackests` v0.14
|
||||||
@@ -46,5 +54,7 @@ Allow `spacepackets` range starting with v0.10 and v0.11.
|
|||||||
|
|
||||||
Initial release.
|
Initial release.
|
||||||
|
|
||||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-shared-v0.2.2...HEAD
|
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-shared-v0.2.4...HEAD
|
||||||
|
[v0.2.3]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-shared-v0.2.3...satrs-shared-v0.2.4
|
||||||
|
[v0.2.3]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-shared-v0.2.1...satrs-shared-v0.2.3
|
||||||
[v0.2.2]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-shared-v0.2.1...satrs-shared-v0.2.2
|
[v0.2.2]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-shared-v0.2.1...satrs-shared-v0.2.2
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "satrs-shared"
|
name = "satrs-shared"
|
||||||
description = "Components shared by multiple sat-rs crates"
|
description = "Components shared by multiple sat-rs crates"
|
||||||
version = "0.2.2"
|
version = "0.2.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
||||||
@@ -11,19 +11,9 @@ license = "Apache-2.0"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
spacepackets = { version = "0.17", default-features = false }
|
||||||
[dependencies.serde]
|
serde = { version = "1", default-features = false, optional = true }
|
||||||
version = "1"
|
defmt = {version = "1", optional = true }
|
||||||
default-features = false
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.defmt]
|
|
||||||
version = "1"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.spacepackets]
|
|
||||||
version = "0.14"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
serde = ["dep:serde", "spacepackets/serde"]
|
serde = ["dep:serde", "spacepackets/serde"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! This crates contains modules shared among other sat-rs framework crates.
|
//! This crates contains modules shared among other sat-rs framework crates.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
pub mod res_code;
|
pub mod res_code;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub struct ResultU16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResultU16 {
|
impl ResultU16 {
|
||||||
|
#[inline]
|
||||||
pub const fn new(group_id: u8, unique_id: u8) -> Self {
|
pub const fn new(group_id: u8, unique_id: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
group_id,
|
group_id,
|
||||||
@@ -21,18 +22,22 @@ impl ResultU16 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw(&self) -> u16 {
|
#[inline]
|
||||||
|
pub const fn raw(&self) -> u16 {
|
||||||
((self.group_id as u16) << 8) | self.unique_id as u16
|
((self.group_id as u16) << 8) | self.unique_id as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn group_id(&self) -> u8 {
|
#[inline]
|
||||||
|
pub const fn group_id(&self) -> u8 {
|
||||||
self.group_id
|
self.group_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unique_id(&self) -> u8 {
|
#[inline]
|
||||||
|
pub const fn unique_id(&self) -> u8 {
|
||||||
self.unique_id
|
self.unique_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
|
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
|
||||||
Self::from(u16::from_be_bytes(bytes))
|
Self::from(u16::from_be_bytes(bytes))
|
||||||
}
|
}
|
||||||
@@ -51,6 +56,7 @@ impl From<ResultU16> for EcssEnumU16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UnsignedEnum for ResultU16 {
|
impl UnsignedEnum for ResultU16 {
|
||||||
|
#[inline]
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
core::mem::size_of::<u16>()
|
core::mem::size_of::<u16>()
|
||||||
}
|
}
|
||||||
@@ -67,12 +73,14 @@ impl UnsignedEnum for ResultU16 {
|
|||||||
Ok(self.size())
|
Ok(self.size())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value(&self) -> u64 {
|
#[inline]
|
||||||
|
fn value_raw(&self) -> u64 {
|
||||||
self.raw() as u64
|
self.raw() as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EcssEnumeration for ResultU16 {
|
impl EcssEnumeration for ResultU16 {
|
||||||
|
#[inline]
|
||||||
fn pfc(&self) -> u8 {
|
fn pfc(&self) -> u8 {
|
||||||
16
|
16
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
# [v0.3.0-alpha.3] 2025-11-06
|
||||||
|
|
||||||
|
- Bump `sat-rs` edition to 2024.
|
||||||
|
- Bumped `spacepackets` to v0.17
|
||||||
|
- `ComponentId` is u32 now
|
||||||
|
- Simplified TCP servers
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
Some trait renaming to be more in-line with Rust naming conventions.
|
||||||
|
|
||||||
|
- `EventTmHookProvider` -> `EventTmHook`
|
||||||
|
- `ActiveRequestProvider` -> `ActiveRequest`
|
||||||
|
- `EcssTcInMemConversionProvider` -> `CacheAndReadRawEcssTc`
|
||||||
|
- `ActiveRequestMapProvider` -> `ActiveRequestStore`
|
||||||
|
- `CountdownProvider` -> `Countdown`
|
||||||
|
|
||||||
|
# [v0.3.0-alpha.2] 2025-07-22
|
||||||
|
|
||||||
|
`satrs-shared` update
|
||||||
|
|
||||||
|
# [v0.3.0-alpha.1] 2025-07-22
|
||||||
|
|
||||||
|
`spacepackets` range v0.14 to v0.15
|
||||||
|
|
||||||
# [v0.3.0-alpha.0] 2025-02-18
|
# [v0.3.0-alpha.0] 2025-02-18
|
||||||
|
|
||||||
`spacepackets` v0.13
|
`spacepackets` v0.13
|
||||||
@@ -198,3 +223,9 @@ docs-rs hotfix
|
|||||||
# [v0.1.0] 2024-02-12
|
# [v0.1.0] 2024-02-12
|
||||||
|
|
||||||
Initial release.
|
Initial release.
|
||||||
|
|
||||||
|
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.3...HEAD
|
||||||
|
[v0.3.0-alpha.3]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.2...satrs-v0.3.0-alpha.3
|
||||||
|
[v0.3.0-alpha.2]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.1...satrs-v0.3.0-alpha.2
|
||||||
|
[v0.3.0-alpha.1]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.0...satrs-v0.3.0-alpha.1
|
||||||
|
[v0.3.0-alpha.0]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.2.1...satrs-v0.3.0-alpha.0
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "satrs"
|
name = "satrs"
|
||||||
version = "0.3.0-alpha.0"
|
version = "0.3.0-alpha.2"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
rust-version = "1.82.0"
|
rust-version = "1.85.0"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
description = "A framework to build software for remote systems"
|
description = "A library collection to build software for remote systems"
|
||||||
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
homepage = "https://github.com/us-irs/sat-rs"
|
||||||
repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
keywords = ["no-std", "space", "aerospace"]
|
keywords = ["no-std", "space", "aerospace"]
|
||||||
@@ -13,26 +13,29 @@ keywords = ["no-std", "space", "aerospace"]
|
|||||||
categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"]
|
categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
satrs-shared = { version = ">=0.1.3, <=0.2", path = "../satrs-shared" }
|
satrs-shared = { version = "0.2", path = "../satrs-shared" }
|
||||||
delegate = ">0.7, <=0.13"
|
spacepackets = { version = "0.17", default-features = false }
|
||||||
|
|
||||||
|
delegate = "0.13"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
derive-new = ">=0.6, <=0.7"
|
derive-new = "0.7"
|
||||||
num_enum = { version = ">0.5, <=0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
spacepackets = { version = "0.14", default-features = false }
|
cobs = { version = "0.5", default-features = false }
|
||||||
cobs = { version = "0.4", default-features = false, git = "https://github.com/jamesmunns/cobs.rs.git", branch = "main" }
|
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
|
||||||
hashbrown = { version = ">=0.14, <=0.15", optional = true }
|
hashbrown = { version = "0.16", optional = true }
|
||||||
static_cell = { version = "2", optional = true }
|
static_cell = { version = "2" }
|
||||||
|
heapless = { version = "0.9", optional = true }
|
||||||
dyn-clone = { version = "1", optional = true }
|
dyn-clone = { version = "1", optional = true }
|
||||||
heapless = { version = "0.8", optional = true }
|
|
||||||
downcast-rs = { version = "2", default-features = false, optional = true }
|
downcast-rs = { version = "2", default-features = false, optional = true }
|
||||||
bus = { version = "2.2", optional = true }
|
bus = { version = "2.2", optional = true }
|
||||||
crossbeam-channel = { version = "0.5", default-features = false, optional = true }
|
crossbeam-channel = { version = "0.5", default-features = false, optional = true }
|
||||||
|
postcard = { version = "1", features = ["alloc"] }
|
||||||
serde = { version = "1", default-features = false, optional = true }
|
serde = { version = "1", default-features = false, optional = true }
|
||||||
socket2 = { version = "0.5", features = ["all"], optional = true }
|
socket2 = { version = "0.6", features = ["all"], optional = true }
|
||||||
|
arbitrary-int = "2"
|
||||||
mio = { version = "1", features = ["os-poll", "net"], optional = true }
|
mio = { version = "1", features = ["os-poll", "net"], optional = true }
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde = "1"
|
serde = "1"
|
||||||
@@ -46,7 +49,7 @@ tempfile = "3"
|
|||||||
version = "1"
|
version = "1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std", "heapless"]
|
||||||
std = [
|
std = [
|
||||||
"downcast-rs/std",
|
"downcast-rs/std",
|
||||||
"alloc",
|
"alloc",
|
||||||
@@ -61,6 +64,7 @@ std = [
|
|||||||
]
|
]
|
||||||
alloc = [
|
alloc = [
|
||||||
"serde/alloc",
|
"serde/alloc",
|
||||||
|
"cobs/alloc",
|
||||||
"spacepackets/alloc",
|
"spacepackets/alloc",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
@@ -68,7 +72,6 @@ alloc = [
|
|||||||
]
|
]
|
||||||
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
|
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
|
||||||
crossbeam = ["crossbeam-channel"]
|
crossbeam = ["crossbeam-channel"]
|
||||||
heapless = ["dep:heapless", "static_cell"]
|
|
||||||
defmt = ["dep:defmt", "spacepackets/defmt"]
|
defmt = ["dep:defmt", "spacepackets/defmt"]
|
||||||
test_util = []
|
test_util = []
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
sat-rs
|
sat-rs
|
||||||
======
|
======
|
||||||
|
|
||||||
This crate contains the primary components of the sat-rs framework.
|
This crate contains the primary components of the sat-rs library collection.
|
||||||
You can find more information on the [homepage](https://egit.irs.uni-stuttgart.de/rust/sat-rs).
|
You can find more information on the [homepage](https://egit.irs.uni-stuttgart.de/rust/sat-rs).
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
mode::{ModeAndSubmode, ModeReply, ModeRequest, ModeRequestSender},
|
mode::{ModeAndSubmode, ModeReply, ModeRequest, ModeRequestSender},
|
||||||
mode_tree::{ModeStoreProvider, ModeStoreVec},
|
mode_tree::{ModeStoreProvider, ModeStoreVec},
|
||||||
queue::{GenericSendError, GenericTargetedMessagingError},
|
queue::{GenericSendError, GenericTargetedMessagingError},
|
||||||
request::{GenericMessage, RequestId},
|
request::{GenericMessage, RequestId},
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
@@ -270,7 +270,7 @@ impl<UserHook: DevManagerUserHook> DevManagerCommandingHelper<UserHook> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
mode::{tests::ModeReqSenderMock, UNKNOWN_MODE},
|
mode::{UNKNOWN_MODE, tests::ModeReqSenderMock},
|
||||||
request::MessageMetadata,
|
request::MessageMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -296,18 +296,18 @@ mod tests {
|
|||||||
fn test_mode_announce() {
|
fn test_mode_announce() {
|
||||||
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
||||||
let mode_req_sender = ModeReqSenderMock::default();
|
let mode_req_sender = ModeReqSenderMock::default();
|
||||||
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
|
assy_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
|
||||||
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
|
assy_helper.add_mode_child(ExampleId::Id2 as ComponentId, UNKNOWN_MODE);
|
||||||
assy_helper
|
assy_helper
|
||||||
.send_announce_mode_cmd_to_children(1, &mode_req_sender, false)
|
.send_announce_mode_cmd_to_children(1, &mode_req_sender, false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
|
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
|
||||||
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req.target_id, ExampleId::Id1 as u64);
|
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
|
||||||
assert_eq!(req.request_id, 1);
|
assert_eq!(req.request_id, 1);
|
||||||
assert_eq!(req.request, ModeRequest::AnnounceMode);
|
assert_eq!(req.request, ModeRequest::AnnounceMode);
|
||||||
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req.target_id, ExampleId::Id2 as u64);
|
assert_eq!(req.target_id, ExampleId::Id2 as ComponentId);
|
||||||
assert_eq!(req.request_id, 1);
|
assert_eq!(req.request_id, 1);
|
||||||
assert_eq!(req.request, ModeRequest::AnnounceMode);
|
assert_eq!(req.request, ModeRequest::AnnounceMode);
|
||||||
}
|
}
|
||||||
@@ -316,18 +316,18 @@ mod tests {
|
|||||||
fn test_mode_announce_recursive() {
|
fn test_mode_announce_recursive() {
|
||||||
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
||||||
let mode_req_sender = ModeReqSenderMock::default();
|
let mode_req_sender = ModeReqSenderMock::default();
|
||||||
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
|
assy_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
|
||||||
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
|
assy_helper.add_mode_child(ExampleId::Id2 as ComponentId, UNKNOWN_MODE);
|
||||||
assy_helper
|
assy_helper
|
||||||
.send_announce_mode_cmd_to_children(1, &mode_req_sender, true)
|
.send_announce_mode_cmd_to_children(1, &mode_req_sender, true)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
|
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
|
||||||
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req.target_id, ExampleId::Id1 as u64);
|
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
|
||||||
assert_eq!(req.request_id, 1);
|
assert_eq!(req.request_id, 1);
|
||||||
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
|
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
|
||||||
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req.target_id, ExampleId::Id2 as u64);
|
assert_eq!(req.target_id, ExampleId::Id2 as ComponentId);
|
||||||
assert_eq!(req.request_id, 1);
|
assert_eq!(req.request_id, 1);
|
||||||
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
|
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
|
||||||
}
|
}
|
||||||
@@ -337,12 +337,12 @@ mod tests {
|
|||||||
let mut dev_mgmt_helper =
|
let mut dev_mgmt_helper =
|
||||||
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
||||||
let mode_req_sender = ModeReqSenderMock::default();
|
let mode_req_sender = ModeReqSenderMock::default();
|
||||||
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
|
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
|
||||||
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode1 as u32, 0);
|
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode1 as u32, 0);
|
||||||
dev_mgmt_helper
|
dev_mgmt_helper
|
||||||
.send_mode_cmd_to_one_child(
|
.send_mode_cmd_to_one_child(
|
||||||
1,
|
1,
|
||||||
ExampleId::Id1 as u64,
|
ExampleId::Id1 as ComponentId,
|
||||||
expected_mode,
|
expected_mode,
|
||||||
false,
|
false,
|
||||||
&mode_req_sender,
|
&mode_req_sender,
|
||||||
@@ -350,7 +350,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(mode_req_sender.requests.borrow().len(), 1);
|
assert_eq!(mode_req_sender.requests.borrow().len(), 1);
|
||||||
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req.target_id, ExampleId::Id1 as u64);
|
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
|
||||||
assert_eq!(req.request_id, 1);
|
assert_eq!(req.request_id, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req.request,
|
req.request,
|
||||||
@@ -368,7 +368,7 @@ mod tests {
|
|||||||
assert_eq!(ctx.active_request_id, 1);
|
assert_eq!(ctx.active_request_id, 1);
|
||||||
}
|
}
|
||||||
let reply = GenericMessage::new(
|
let reply = GenericMessage::new(
|
||||||
MessageMetadata::new(1, ExampleId::Id1 as u64),
|
MessageMetadata::new(1, ExampleId::Id1 as ComponentId),
|
||||||
ModeReply::ModeReply(expected_mode),
|
ModeReply::ModeReply(expected_mode),
|
||||||
);
|
);
|
||||||
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {
|
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {
|
||||||
@@ -387,15 +387,15 @@ mod tests {
|
|||||||
let mut dev_mgmt_helper =
|
let mut dev_mgmt_helper =
|
||||||
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
|
||||||
let mode_req_sender = ModeReqSenderMock::default();
|
let mode_req_sender = ModeReqSenderMock::default();
|
||||||
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
|
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
|
||||||
dev_mgmt_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
|
dev_mgmt_helper.add_mode_child(ExampleId::Id2 as ComponentId, UNKNOWN_MODE);
|
||||||
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode2 as u32, 0);
|
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode2 as u32, 0);
|
||||||
dev_mgmt_helper
|
dev_mgmt_helper
|
||||||
.send_mode_cmd_to_all_children(1, expected_mode, false, &mode_req_sender)
|
.send_mode_cmd_to_all_children(1, expected_mode, false, &mode_req_sender)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
|
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
|
||||||
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req.target_id, ExampleId::Id1 as u64);
|
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
|
||||||
assert_eq!(req.request_id, 1);
|
assert_eq!(req.request_id, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req.request,
|
req.request,
|
||||||
@@ -405,7 +405,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req.target_id, ExampleId::Id2 as u64);
|
assert_eq!(req.target_id, ExampleId::Id2 as ComponentId);
|
||||||
assert_eq!(req.request_id, 1);
|
assert_eq!(req.request_id, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req.request,
|
req.request,
|
||||||
@@ -424,7 +424,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let reply = GenericMessage::new(
|
let reply = GenericMessage::new(
|
||||||
MessageMetadata::new(1, ExampleId::Id1 as u64),
|
MessageMetadata::new(1, ExampleId::Id1 as ComponentId),
|
||||||
ModeReply::ModeReply(expected_mode),
|
ModeReply::ModeReply(expected_mode),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -432,7 +432,7 @@ mod tests {
|
|||||||
DevManagerHelperResult::Busy
|
DevManagerHelperResult::Busy
|
||||||
);
|
);
|
||||||
let reply = GenericMessage::new(
|
let reply = GenericMessage::new(
|
||||||
MessageMetadata::new(1, ExampleId::Id2 as u64),
|
MessageMetadata::new(1, ExampleId::Id2 as ComponentId),
|
||||||
ModeReply::ModeReply(expected_mode),
|
ModeReply::ModeReply(expected_mode),
|
||||||
);
|
);
|
||||||
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {
|
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use spacepackets::{CcsdsPacket, SpHeader};
|
use spacepackets::SpHeader;
|
||||||
|
|
||||||
use crate::{tmtc::PacketSenderRaw, ComponentId};
|
use crate::{ComponentId, tmtc::PacketHandler};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum SpValidity {
|
pub enum SpValidity {
|
||||||
@@ -24,13 +24,17 @@ pub trait SpacePacketValidator {
|
|||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
pub struct ParseResult {
|
pub struct ParseResult {
|
||||||
pub packets_found: u32,
|
pub packets_found: u32,
|
||||||
/// If an incomplete space packet was found, its start index is indicated by this value.
|
pub parsed_bytes: usize,
|
||||||
pub incomplete_tail_start: Option<usize>,
|
// If an incomplete space packet was found, its start index is indicated by this value.
|
||||||
|
//pub incomplete_packet_start: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function parses a given buffer for tightly packed CCSDS space packets. It uses the
|
/// This function parses a given buffer for tightly packed CCSDS space packets.
|
||||||
/// [spacepackets::SpHeader] of the CCSDS packets and a user provided [SpacePacketValidator]
|
///
|
||||||
/// to check whether a received space packet is relevant for processing.
|
/// Please note that it is recommended to use a proper data link layer instead to have proper
|
||||||
|
/// packet framing and to allow more reliable recovery from packet loss.
|
||||||
|
/// It uses the [spacepackets::SpHeader] of the CCSDS packets and a user provided
|
||||||
|
/// [SpacePacketValidator] to check whether a received space packet is relevant for processing.
|
||||||
///
|
///
|
||||||
/// This function is also able to deal with broken tail packets at the end as long a the parser
|
/// This function is also able to deal with broken tail packets at the end as long a the parser
|
||||||
/// can read the full 7 bytes which constitue a space packet header plus one byte minimal size.
|
/// can read the full 7 bytes which constitue a space packet header plus one byte minimal size.
|
||||||
@@ -41,17 +45,18 @@ pub struct ParseResult {
|
|||||||
/// [SpacePacketValidator]:
|
/// [SpacePacketValidator]:
|
||||||
///
|
///
|
||||||
/// 1. [SpValidity::Valid]: The parser will forward all packets to the given `packet_sender` and
|
/// 1. [SpValidity::Valid]: The parser will forward all packets to the given `packet_sender` and
|
||||||
/// return the number of packets found.If the [PacketSenderRaw::send_packet] calls fails, the
|
/// return the number of packets found.If the [PacketHandler::handle_packet] calls fails, the
|
||||||
/// error will be returned.
|
/// error will be returned.
|
||||||
/// 2. [SpValidity::Invalid]: The parser assumes that the synchronization is lost and tries to
|
/// 2. [SpValidity::Invalid]: The parser assumes that the synchronization is lost and tries to
|
||||||
/// find the start of a new space packet header by scanning all the following bytes.
|
/// find the start of a new space packet header by scanning all the following bytes.
|
||||||
/// 3. [SpValidity::Skip]: The parser skips the packet using the packet length determined from the
|
/// 3. [SpValidity::Skip]: The parser skips the packet using the packet length determined from the
|
||||||
/// space packet header.
|
/// space packet header.
|
||||||
|
///
|
||||||
pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
packet_validator: &(impl SpacePacketValidator + ?Sized),
|
packet_validator: &(impl SpacePacketValidator + ?Sized),
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
|
packet_sender: &(impl PacketHandler<Error = SendError> + ?Sized),
|
||||||
) -> Result<ParseResult, SendError> {
|
) -> Result<ParseResult, SendError> {
|
||||||
let mut parse_result = ParseResult::default();
|
let mut parse_result = ParseResult::default();
|
||||||
let mut current_idx = 0;
|
let mut current_idx = 0;
|
||||||
@@ -63,20 +68,21 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
|||||||
let sp_header = SpHeader::from_be_bytes(&buf[current_idx..]).unwrap().0;
|
let sp_header = SpHeader::from_be_bytes(&buf[current_idx..]).unwrap().0;
|
||||||
match packet_validator.validate(&sp_header, &buf[current_idx..]) {
|
match packet_validator.validate(&sp_header, &buf[current_idx..]) {
|
||||||
SpValidity::Valid => {
|
SpValidity::Valid => {
|
||||||
let packet_size = sp_header.total_len();
|
let packet_size = sp_header.packet_len();
|
||||||
if (current_idx + packet_size) <= buf_len {
|
if (current_idx + packet_size) <= buf_len {
|
||||||
packet_sender
|
packet_sender
|
||||||
.send_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
|
.handle_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
|
||||||
parse_result.packets_found += 1;
|
parse_result.packets_found += 1;
|
||||||
} else {
|
} else {
|
||||||
// Move packet to start of buffer if applicable.
|
// Move packet to start of buffer if applicable.
|
||||||
parse_result.incomplete_tail_start = Some(current_idx);
|
//parse_result.incomplete_packet_start = Some(current_idx);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
current_idx += packet_size;
|
current_idx += packet_size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpValidity::Skip => {
|
SpValidity::Skip => {
|
||||||
current_idx += sp_header.total_len();
|
current_idx += sp_header.packet_len();
|
||||||
}
|
}
|
||||||
// We might have lost sync. Try to find the start of a new space packet header.
|
// We might have lost sync. Try to find the start of a new space packet header.
|
||||||
SpValidity::Invalid => {
|
SpValidity::Invalid => {
|
||||||
@@ -84,23 +90,25 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
parse_result.parsed_bytes = current_idx;
|
||||||
Ok(parse_result)
|
Ok(parse_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use arbitrary_int::{u11, u14};
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
CcsdsPacket, PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader,
|
||||||
CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader,
|
ecss::{CreatorConfig, MessageTypeId, tc::PusTcCreator},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{encoding::tests::TcCacher, ComponentId};
|
use crate::{ComponentId, encoding::tests::TcCacher};
|
||||||
|
|
||||||
use super::{parse_buffer_for_ccsds_space_packets, SpValidity, SpacePacketValidator};
|
use super::{SpValidity, SpacePacketValidator, parse_buffer_for_ccsds_space_packets};
|
||||||
|
|
||||||
const PARSER_ID: ComponentId = 0x05;
|
const PARSER_ID: ComponentId = 0x05;
|
||||||
const TEST_APID_0: u16 = 0x02;
|
const TEST_APID_0: u11 = u11::new(0x02);
|
||||||
const TEST_APID_1: u16 = 0x10;
|
const TEST_APID_1: u11 = u11::new(0x10);
|
||||||
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
|
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
|
||||||
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
|
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
|
||||||
|
|
||||||
@@ -131,7 +139,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_basic() {
|
fn test_basic() {
|
||||||
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
||||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let mut buffer: [u8; 32] = [0; 32];
|
let mut buffer: [u8; 32] = [0; 32];
|
||||||
let packet_len = ping_tc
|
let packet_len = ping_tc
|
||||||
.write_to_bytes(&mut buffer)
|
.write_to_bytes(&mut buffer)
|
||||||
@@ -156,8 +169,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_multi_packet() {
|
fn test_multi_packet() {
|
||||||
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
||||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
|
sph,
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
|
let action_tc =
|
||||||
|
PusTcCreator::new_simple(sph, MessageTypeId::new(8, 0), &[], CreatorConfig::default());
|
||||||
let mut buffer: [u8; 32] = [0; 32];
|
let mut buffer: [u8; 32] = [0; 32];
|
||||||
let packet_len_ping = ping_tc
|
let packet_len_ping = ping_tc
|
||||||
.write_to_bytes(&mut buffer)
|
.write_to_bytes(&mut buffer)
|
||||||
@@ -191,9 +210,15 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_multi_apid() {
|
fn test_multi_apid() {
|
||||||
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
||||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let sph = SpHeader::new_from_apid(TEST_APID_1);
|
let sph = SpHeader::new_from_apid(TEST_APID_1);
|
||||||
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
|
let action_tc =
|
||||||
|
PusTcCreator::new_simple(sph, MessageTypeId::new(8, 0), &[], CreatorConfig::default());
|
||||||
let mut buffer: [u8; 32] = [0; 32];
|
let mut buffer: [u8; 32] = [0; 32];
|
||||||
let packet_len_ping = ping_tc
|
let packet_len_ping = ping_tc
|
||||||
.write_to_bytes(&mut buffer)
|
.write_to_bytes(&mut buffer)
|
||||||
@@ -221,10 +246,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_split_packet_multi() {
|
fn test_split_packet_multi() {
|
||||||
let ping_tc =
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
SpHeader::new_from_apid(TEST_APID_0),
|
||||||
let action_tc =
|
MessageTypeId::new(17, 1),
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true);
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
|
let action_tc = PusTcCreator::new_simple(
|
||||||
|
SpHeader::new_from_apid(TEST_APID_1),
|
||||||
|
MessageTypeId::new(8, 0),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let mut buffer: [u8; 32] = [0; 32];
|
let mut buffer: [u8; 32] = [0; 32];
|
||||||
let packet_len_ping = ping_tc
|
let packet_len_ping = ping_tc
|
||||||
.write_to_bytes(&mut buffer)
|
.write_to_bytes(&mut buffer)
|
||||||
@@ -243,8 +276,7 @@ mod tests {
|
|||||||
assert!(parse_result.is_ok());
|
assert!(parse_result.is_ok());
|
||||||
let parse_result = parse_result.unwrap();
|
let parse_result = parse_result.unwrap();
|
||||||
assert_eq!(parse_result.packets_found, 1);
|
assert_eq!(parse_result.packets_found, 1);
|
||||||
assert!(parse_result.incomplete_tail_start.is_some());
|
let incomplete_tail_idx = parse_result.parsed_bytes;
|
||||||
let incomplete_tail_idx = parse_result.incomplete_tail_start.unwrap();
|
|
||||||
assert_eq!(incomplete_tail_idx, packet_len_ping);
|
assert_eq!(incomplete_tail_idx, packet_len_ping);
|
||||||
|
|
||||||
let queue = tc_cacher.tc_queue.borrow();
|
let queue = tc_cacher.tc_queue.borrow();
|
||||||
@@ -255,8 +287,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_one_split_packet() {
|
fn test_one_split_packet() {
|
||||||
let ping_tc =
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
SpHeader::new_from_apid(TEST_APID_0),
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let mut buffer: [u8; 32] = [0; 32];
|
let mut buffer: [u8; 32] = [0; 32];
|
||||||
let packet_len_ping = ping_tc
|
let packet_len_ping = ping_tc
|
||||||
.write_to_bytes(&mut buffer)
|
.write_to_bytes(&mut buffer)
|
||||||
@@ -281,7 +317,7 @@ mod tests {
|
|||||||
fn test_smallest_packet() {
|
fn test_smallest_packet() {
|
||||||
let ccsds_header_only = SpHeader::new(
|
let ccsds_header_only = SpHeader::new(
|
||||||
PacketId::new(PacketType::Tc, true, TEST_APID_0),
|
PacketId::new(PacketType::Tc, true, TEST_APID_0),
|
||||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 0),
|
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0)),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
let mut buf: [u8; 7] = [0; 7];
|
let mut buf: [u8; 7] = [0; 7];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{tmtc::PacketSenderRaw, ComponentId};
|
use crate::{ComponentId, tmtc::PacketHandler};
|
||||||
use cobs::{decode_in_place, encode, max_encoding_length};
|
use cobs::{decode_in_place, encode_including_sentinels, max_encoding_length};
|
||||||
|
|
||||||
/// This function encodes the given packet with COBS and also wraps the encoded packet with
|
/// This function encodes the given packet with COBS and also wraps the encoded packet with
|
||||||
/// the sentinel value 0. It can be used repeatedly on the same encoded buffer by expecting
|
/// the sentinel value 0. It can be used repeatedly on the same encoded buffer by expecting
|
||||||
@@ -37,15 +37,16 @@ pub fn encode_packet_with_cobs(
|
|||||||
if *current_idx + max_encoding_len + 2 > encoded_buf.len() {
|
if *current_idx + max_encoding_len + 2 > encoded_buf.len() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
encoded_buf[*current_idx] = 0;
|
*current_idx += encode_including_sentinels(packet, &mut encoded_buf[*current_idx..]);
|
||||||
*current_idx += 1;
|
|
||||||
*current_idx += encode(packet, &mut encoded_buf[*current_idx..]);
|
|
||||||
encoded_buf[*current_idx] = 0;
|
|
||||||
*current_idx += 1;
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function parses a given buffer for COBS encoded packets. The packet structure is
|
/// This function parses a given buffer for COBS encoded packets.
|
||||||
|
///
|
||||||
|
/// Please note that, it is recommended to use [cobs::CobsDecoderOwned] or [cobs::CobsDecoder]
|
||||||
|
/// instead.
|
||||||
|
///
|
||||||
|
/// The packet structure is
|
||||||
/// expected to be like this, assuming a sentinel value of 0 as the packet delimiter:
|
/// expected to be like this, assuming a sentinel value of 0 as the packet delimiter:
|
||||||
///
|
///
|
||||||
/// 0 | ... Encoded Packet Data ... | 0 | 0 | ... Encoded Packet Data ... | 0
|
/// 0 | ... Encoded Packet Data ... | 0 | 0 | ... Encoded Packet Data ... | 0
|
||||||
@@ -58,7 +59,7 @@ pub fn encode_packet_with_cobs(
|
|||||||
pub fn parse_buffer_for_cobs_encoded_packets<SendError>(
|
pub fn parse_buffer_for_cobs_encoded_packets<SendError>(
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
|
packet_sender: &(impl PacketHandler<Error = SendError> + ?Sized),
|
||||||
next_write_idx: &mut usize,
|
next_write_idx: &mut usize,
|
||||||
) -> Result<u32, SendError> {
|
) -> Result<u32, SendError> {
|
||||||
let mut start_index_packet = 0;
|
let mut start_index_packet = 0;
|
||||||
@@ -79,7 +80,7 @@ pub fn parse_buffer_for_cobs_encoded_packets<SendError>(
|
|||||||
let decode_result = decode_in_place(&mut buf[start_index_packet..i]);
|
let decode_result = decode_in_place(&mut buf[start_index_packet..i]);
|
||||||
if let Ok(packet_len) = decode_result {
|
if let Ok(packet_len) = decode_result {
|
||||||
packets_found += 1;
|
packets_found += 1;
|
||||||
packet_sender.send_packet(
|
packet_sender.handle_packet(
|
||||||
sender_id,
|
sender_id,
|
||||||
&buf[start_index_packet..start_index_packet + packet_len],
|
&buf[start_index_packet..start_index_packet + packet_len],
|
||||||
)?;
|
)?;
|
||||||
@@ -104,8 +105,8 @@ pub(crate) mod tests {
|
|||||||
use cobs::encode;
|
use cobs::encode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
encoding::tests::{encode_simple_packet, TcCacher, INVERTED_PACKET, SIMPLE_PACKET},
|
|
||||||
ComponentId,
|
ComponentId,
|
||||||
|
encoding::tests::{INVERTED_PACKET, SIMPLE_PACKET, TcCacher, encode_simple_packet},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::parse_buffer_for_cobs_encoded_packets;
|
use super::parse_buffer_for_cobs_encoded_packets;
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ pub(crate) mod tests {
|
|||||||
use alloc::collections::VecDeque;
|
use alloc::collections::VecDeque;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
tmtc::{PacketAsVec, PacketSenderRaw},
|
|
||||||
ComponentId,
|
ComponentId,
|
||||||
|
tmtc::{PacketAsVec, PacketHandler},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::cobs::encode_packet_with_cobs;
|
use super::cobs::encode_packet_with_cobs;
|
||||||
@@ -25,10 +25,10 @@ pub(crate) mod tests {
|
|||||||
pub(crate) tc_queue: RefCell<VecDeque<PacketAsVec>>,
|
pub(crate) tc_queue: RefCell<VecDeque<PacketAsVec>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSenderRaw for TcCacher {
|
impl PacketHandler for TcCacher {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
let mut mut_queue = self.tc_queue.borrow_mut();
|
let mut mut_queue = self.tc_queue.borrow_mut();
|
||||||
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
850
satrs/src/event_man_legacy.rs
Normal file
850
satrs/src/event_man_legacy.rs
Normal file
@@ -0,0 +1,850 @@
|
|||||||
|
//! # Event management and forwarding
|
||||||
|
//!
|
||||||
|
//! This is a legacy module. It is recommended to use [super::event_man] instead.
|
||||||
|
//!
|
||||||
|
//! It is recommended to read the
|
||||||
|
//! [sat-rs book chapter](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/events.html)
|
||||||
|
//! about events first.
|
||||||
|
//!
|
||||||
|
//! This module provides components to perform event routing. The most important component for this
|
||||||
|
//! task is the [EventManager]. It receives all events and then routes them to event subscribers
|
||||||
|
//! where appropriate.
|
||||||
|
//!
|
||||||
|
//! The event manager has a listener table abstracted by the [ListenerMapProvider], which maps
|
||||||
|
//! listener groups identified by [ListenerKey]s to a [listener ID][ComponentId].
|
||||||
|
//! It also contains a sender table abstracted by the [SenderMapProvider] which maps these sender
|
||||||
|
//! IDs to concrete [EventSendProvider]s. A simple approach would be to use one send event provider
|
||||||
|
//! for each OBSW thread and then subscribe for all interesting events for a particular thread
|
||||||
|
//! using the send event provider ID.
|
||||||
|
//!
|
||||||
|
//! This can be done with the [EventManager] like this:
|
||||||
|
//!
|
||||||
|
//! 1. Provide a concrete [EventReceiveProvider] implementation. This abstraction allow to use different
|
||||||
|
//! message queue backends. A straightforward implementation where dynamic memory allocation is
|
||||||
|
//! not a big concern would be to use the [std::sync::mpsc::Receiver] handle. The trait is
|
||||||
|
//! already implemented for this type.
|
||||||
|
//! 2. To set up event creators, create channel pairs using some message queue implementation.
|
||||||
|
//! Each event creator gets a (cloned) sender component which allows it to send events to the
|
||||||
|
//! manager.
|
||||||
|
//! 3. The event manager receives the receiver component as part of a [EventReceiveProvider]
|
||||||
|
//! implementation so all events are routed to the manager.
|
||||||
|
//! 4. Create the [event sender map][SenderMapProvider]s which allow routing events to
|
||||||
|
//! subscribers. You can now use the subscriber component IDs to subscribe
|
||||||
|
//! for event groups, for example by using the [EventManager::subscribe_single] method.
|
||||||
|
//! 5. Add the send provider as well using the [EventManager::add_sender] call so the event
|
||||||
|
//! manager can route listener groups to a the send provider.
|
||||||
|
//!
|
||||||
|
//! Some components like a PUS Event Service or PUS Event Action Service might require all
|
||||||
|
//! events to package them as telemetry or start actions where applicable.
|
||||||
|
//! Other components might only be interested in certain events. For example, a thermal system
|
||||||
|
//! handler might only be interested in temperature events generated by a thermal sensor component.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! You can check [integration test](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/pus_events.rs)
|
||||||
|
//! for a concrete example using multi-threading where events are routed to
|
||||||
|
//! different threads.
|
||||||
|
//!
|
||||||
|
//! The [satrs-example](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
|
||||||
|
//! also contains a full event manager instance and exposes a test event via the PUS test service.
|
||||||
|
//! The [PUS event](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/pus/event.rs)
|
||||||
|
//! module and the generic [events module](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/events.rs)
|
||||||
|
//! show how the event management modules can be integrated into a more complex software.
|
||||||
|
use crate::events_legacy::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw};
|
||||||
|
use crate::params::Params;
|
||||||
|
use crate::queue::GenericSendError;
|
||||||
|
use core::fmt::Debug;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::slice::Iter;
|
||||||
|
|
||||||
|
use crate::ComponentId;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub use alloc_mod::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std_mod::*;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||||
|
pub enum ListenerKey {
|
||||||
|
Single(LargestEventRaw),
|
||||||
|
Group(LargestGroupIdRaw),
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EventMessage<Event: GenericEvent, Parameters: Debug = Params> {
|
||||||
|
sender_id: ComponentId,
|
||||||
|
event: Event,
|
||||||
|
params: Option<Parameters>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Event: GenericEvent, Parameters: Debug + Clone> EventMessage<Event, Parameters> {
|
||||||
|
pub fn new_generic(sender_id: ComponentId, event: Event, params: Option<&Parameters>) -> Self {
|
||||||
|
Self {
|
||||||
|
sender_id,
|
||||||
|
event,
|
||||||
|
params: params.cloned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sender_id(&self) -> ComponentId {
|
||||||
|
self.sender_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event(&self) -> Event {
|
||||||
|
self.event
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn params(&self) -> Option<&Parameters> {
|
||||||
|
self.params.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(sender_id: ComponentId, event: Event) -> Self {
|
||||||
|
Self::new_generic(sender_id, event, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_params(sender_id: ComponentId, event: Event, params: &Parameters) -> Self {
|
||||||
|
Self::new_generic(sender_id, event, Some(params))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type EventMessageU32 = EventMessage<EventU32, Params>;
|
||||||
|
pub type EventMessageU16 = EventMessage<EventU16, Params>;
|
||||||
|
|
||||||
|
/// Generic abstraction
|
||||||
|
pub trait EventSendProvider<Event: GenericEvent, ParamProvider: Debug = Params> {
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
fn target_id(&self) -> ComponentId;
|
||||||
|
|
||||||
|
fn send(&self, message: EventMessage<Event, ParamProvider>) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic abstraction for an event receiver.
|
||||||
|
pub trait EventReceiveProvider<Event: GenericEvent, ParamsProvider: Debug = Params> {
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
/// This function has to be provided by any event receiver. A call may or may not return
|
||||||
|
/// an event and optional auxiliary data.
|
||||||
|
fn try_recv_event(&self) -> Result<Option<EventMessage<Event, ParamsProvider>>, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ListenerMapProvider {
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn get_listeners(&self) -> alloc::vec::Vec<ListenerKey>;
|
||||||
|
fn contains_listener(&self, key: &ListenerKey) -> bool;
|
||||||
|
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<'_, ComponentId>>;
|
||||||
|
fn add_listener(&mut self, key: ListenerKey, listener_id: ComponentId) -> bool;
|
||||||
|
fn remove_duplicates(&mut self, key: &ListenerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SenderMapProvider<
|
||||||
|
EventSender: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent = EventU32,
|
||||||
|
ParamProvider: Debug = Params,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
fn contains_send_event_provider(&self, target_id: &ComponentId) -> bool;
|
||||||
|
|
||||||
|
fn get_send_event_provider(&self, target_id: &ComponentId) -> Option<&EventSender>;
|
||||||
|
fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic event manager implementation.
|
||||||
|
///
|
||||||
|
/// # Generics
|
||||||
|
///
|
||||||
|
/// * `EventReceiver`: [EventReceiveProvider] used to receive all events.
|
||||||
|
/// * `SenderMap`: [SenderMapProvider] which maps channel IDs to send providers.
|
||||||
|
/// * `ListenerMap`: [ListenerMapProvider] which maps listener keys to channel IDs.
|
||||||
|
/// * `EventSender`: [EventSendProvider] contained within the sender map which sends the events.
|
||||||
|
/// * `Event`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32]
|
||||||
|
/// and [EventU16] are supported.
|
||||||
|
/// * `ParamProvider`: Auxiliary data which is sent with the event to provide optional context
|
||||||
|
/// information
|
||||||
|
pub struct EventManager<
|
||||||
|
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
|
||||||
|
SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>,
|
||||||
|
ListenerMap: ListenerMapProvider,
|
||||||
|
EventSender: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent = EventU32,
|
||||||
|
ParamProvider: Debug = Params,
|
||||||
|
> {
|
||||||
|
event_receiver: EventReceiver,
|
||||||
|
sender_map: SenderMap,
|
||||||
|
listener_map: ListenerMap,
|
||||||
|
phantom: core::marker::PhantomData<(EventSender, Event, ParamProvider)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EventRoutingResult<Event: GenericEvent, ParamProvider: Debug> {
|
||||||
|
/// No event was received
|
||||||
|
Empty,
|
||||||
|
/// An event was received and routed to listeners.
|
||||||
|
Handled {
|
||||||
|
num_recipients: u32,
|
||||||
|
event_msg: EventMessage<Event, ParamProvider>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EventRoutingError {
|
||||||
|
Send(GenericSendError),
|
||||||
|
NoSendersForKey(ListenerKey),
|
||||||
|
NoSenderForId(ComponentId),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
|
||||||
|
SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>,
|
||||||
|
ListenerMap: ListenerMapProvider,
|
||||||
|
EventSender: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent + Copy,
|
||||||
|
ParamProvider: Debug,
|
||||||
|
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSender, Event, ParamProvider>
|
||||||
|
{
|
||||||
|
pub fn remove_duplicates(&mut self, key: &ListenerKey) {
|
||||||
|
self.listener_map.remove_duplicates(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subscribe for a unique event.
|
||||||
|
pub fn subscribe_single(&mut self, event: &Event, sender_id: ComponentId) {
|
||||||
|
self.update_listeners(ListenerKey::Single(event.raw_as_largest_type()), sender_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subscribe for an event group.
|
||||||
|
pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: ComponentId) {
|
||||||
|
self.update_listeners(ListenerKey::Group(group_id), sender_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subscribe for all events received by the manager.
|
||||||
|
///
|
||||||
|
/// For example, this can be useful for a handler component which sends every event as
|
||||||
|
/// a telemetry packet.
|
||||||
|
pub fn subscribe_all(&mut self, sender_id: ComponentId) {
|
||||||
|
self.update_listeners(ListenerKey::All, sender_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<
|
||||||
|
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
|
||||||
|
SenderMap: SenderMapProvider<EventSenderMap, Event, ParamProvider>,
|
||||||
|
ListenerMap: ListenerMapProvider,
|
||||||
|
EventSenderMap: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent + Copy,
|
||||||
|
ParamProvider: Debug,
|
||||||
|
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSenderMap, Event, ParamProvider>
|
||||||
|
{
|
||||||
|
pub fn new_with_custom_maps(
|
||||||
|
event_receiver: EventReceiver,
|
||||||
|
sender_map: SenderMap,
|
||||||
|
listener_map: ListenerMap,
|
||||||
|
) -> Self {
|
||||||
|
EventManager {
|
||||||
|
listener_map,
|
||||||
|
sender_map,
|
||||||
|
event_receiver,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new sender component which can be used to send events to subscribers.
|
||||||
|
pub fn add_sender(&mut self, send_provider: EventSenderMap) {
|
||||||
|
if !self
|
||||||
|
.sender_map
|
||||||
|
.contains_send_event_provider(&send_provider.target_id())
|
||||||
|
{
|
||||||
|
self.sender_map.add_send_event_provider(send_provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic function to update the event subscribers.
|
||||||
|
fn update_listeners(&mut self, key: ListenerKey, sender_id: ComponentId) {
|
||||||
|
self.listener_map.add_listener(key, sender_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
|
||||||
|
SenderMap: SenderMapProvider<EventSenderMap, Event, ParamProvider>,
|
||||||
|
ListenerMap: ListenerMapProvider,
|
||||||
|
EventSenderMap: EventSendProvider<Event, ParamProvider, Error = GenericSendError>,
|
||||||
|
Event: GenericEvent + Copy,
|
||||||
|
ParamProvider: Clone + Debug,
|
||||||
|
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSenderMap, Event, ParamProvider>
|
||||||
|
{
|
||||||
|
/// This function will use the cached event receiver and try to receive one event.
|
||||||
|
/// If an event was received, it will try to route that event to all subscribed event listeners.
|
||||||
|
/// If this works without any issues, the [EventRoutingResult] will contain context information
|
||||||
|
/// about the routed event.
|
||||||
|
///
|
||||||
|
/// If an error occurs during the routing, the error handler will be called. The error handler
|
||||||
|
/// should take a reference to the event message as the first argument, and the routing error
|
||||||
|
/// as the second argument.
|
||||||
|
pub fn try_event_handling<E: FnMut(&EventMessage<Event, ParamProvider>, EventRoutingError)>(
|
||||||
|
&self,
|
||||||
|
mut error_handler: E,
|
||||||
|
) -> EventRoutingResult<Event, ParamProvider> {
|
||||||
|
let mut num_recipients = 0;
|
||||||
|
let mut send_handler =
|
||||||
|
|key: &ListenerKey, event_msg: &EventMessage<Event, ParamProvider>| {
|
||||||
|
if self.listener_map.contains_listener(key) {
|
||||||
|
if let Some(ids) = self.listener_map.get_listener_ids(key) {
|
||||||
|
for id in ids {
|
||||||
|
if let Some(sender) = self.sender_map.get_send_event_provider(id) {
|
||||||
|
if let Err(e) = sender.send(EventMessage::new_generic(
|
||||||
|
event_msg.sender_id,
|
||||||
|
event_msg.event,
|
||||||
|
event_msg.params.as_ref(),
|
||||||
|
)) {
|
||||||
|
error_handler(event_msg, EventRoutingError::Send(e));
|
||||||
|
} else {
|
||||||
|
num_recipients += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_handler(event_msg, EventRoutingError::NoSenderForId(*id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_handler(event_msg, EventRoutingError::NoSendersForKey(*key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Ok(Some(event_msg)) = self.event_receiver.try_recv_event() {
|
||||||
|
let single_key = ListenerKey::Single(event_msg.event.raw_as_largest_type());
|
||||||
|
send_handler(&single_key, &event_msg);
|
||||||
|
let group_key = ListenerKey::Group(event_msg.event.group_id_as_largest_type());
|
||||||
|
send_handler(&group_key, &event_msg);
|
||||||
|
send_handler(&ListenerKey::All, &event_msg);
|
||||||
|
return EventRoutingResult::Handled {
|
||||||
|
num_recipients,
|
||||||
|
event_msg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EventRoutingResult::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub mod alloc_mod {
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap]
|
||||||
|
/// and the [DefaultListenerMap]. It uses regular mpsc channels as the message queue backend.
|
||||||
|
pub type EventManagerWithMpsc<Event = EventU32, ParamProvider = Params> = EventManager<
|
||||||
|
EventU32ReceiverMpsc<ParamProvider>,
|
||||||
|
DefaultSenderMap<EventSenderMpsc<Event>, Event, ParamProvider>,
|
||||||
|
DefaultListenerMap,
|
||||||
|
EventSenderMpsc<Event>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap]
|
||||||
|
/// and the [DefaultListenerMap]. It uses
|
||||||
|
/// [bounded mpsc senders](https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html) as the
|
||||||
|
/// message queue backend.
|
||||||
|
pub type EventManagerWithBoundedMpsc<Event = EventU32, ParamProvider = Params> = EventManager<
|
||||||
|
EventU32ReceiverMpsc<ParamProvider>,
|
||||||
|
DefaultSenderMap<EventSenderMpscBounded<Event>, Event, ParamProvider>,
|
||||||
|
DefaultListenerMap,
|
||||||
|
EventSenderMpscBounded<Event>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<
|
||||||
|
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
|
||||||
|
EventSender: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent + Copy,
|
||||||
|
ParamProvider: 'static + Debug,
|
||||||
|
>
|
||||||
|
EventManager<
|
||||||
|
EventReceiver,
|
||||||
|
DefaultSenderMap<EventSender, Event, ParamProvider>,
|
||||||
|
DefaultListenerMap,
|
||||||
|
EventSender,
|
||||||
|
Event,
|
||||||
|
ParamProvider,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
/// Create an event manager where the sender table will be the [DefaultSenderMap]
|
||||||
|
/// and the listener table will be the [DefaultListenerMap].
|
||||||
|
pub fn new(event_receiver: EventReceiver) -> Self {
|
||||||
|
Self {
|
||||||
|
listener_map: DefaultListenerMap::default(),
|
||||||
|
sender_map: DefaultSenderMap::default(),
|
||||||
|
event_receiver,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default listener map.
|
||||||
|
///
|
||||||
|
/// Simple implementation which uses a [HashMap] and a [Vec] internally.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct DefaultListenerMap {
|
||||||
|
listeners: HashMap<ListenerKey, Vec<ComponentId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListenerMapProvider for DefaultListenerMap {
|
||||||
|
fn get_listeners(&self) -> Vec<ListenerKey> {
|
||||||
|
let mut key_list = Vec::new();
|
||||||
|
for key in self.listeners.keys() {
|
||||||
|
key_list.push(*key);
|
||||||
|
}
|
||||||
|
key_list
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_listener(&self, key: &ListenerKey) -> bool {
|
||||||
|
self.listeners.contains_key(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<'_, ComponentId>> {
|
||||||
|
self.listeners.get(key).map(|vec| vec.iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_listener(&mut self, key: ListenerKey, sender_id: ComponentId) -> bool {
|
||||||
|
if let Some(existing_list) = self.listeners.get_mut(&key) {
|
||||||
|
existing_list.push(sender_id);
|
||||||
|
} else {
|
||||||
|
let new_list = alloc::vec![sender_id];
|
||||||
|
self.listeners.insert(key, new_list);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_duplicates(&mut self, key: &ListenerKey) {
|
||||||
|
if let Some(list) = self.listeners.get_mut(key) {
|
||||||
|
list.sort_unstable();
|
||||||
|
list.dedup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default sender map.
|
||||||
|
///
|
||||||
|
/// Simple implementation which uses a [HashMap] internally.
|
||||||
|
pub struct DefaultSenderMap<
|
||||||
|
EventSender: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent = EventU32,
|
||||||
|
ParamProvider: Debug = Params,
|
||||||
|
> {
|
||||||
|
senders: HashMap<ComponentId, EventSender>,
|
||||||
|
phantom: PhantomData<(Event, ParamProvider)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
EventSender: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent,
|
||||||
|
ParamProvider: Debug,
|
||||||
|
> Default for DefaultSenderMap<EventSender, Event, ParamProvider>
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
senders: Default::default(),
|
||||||
|
phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
EventSender: EventSendProvider<Event, ParamProvider>,
|
||||||
|
Event: GenericEvent,
|
||||||
|
ParamProvider: Debug,
|
||||||
|
> SenderMapProvider<EventSender, Event, ParamProvider>
|
||||||
|
for DefaultSenderMap<EventSender, Event, ParamProvider>
|
||||||
|
{
|
||||||
|
fn contains_send_event_provider(&self, id: &ComponentId) -> bool {
|
||||||
|
self.senders.contains_key(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_send_event_provider(&self, id: &ComponentId) -> Option<&EventSender> {
|
||||||
|
self.senders
|
||||||
|
.get(id)
|
||||||
|
.filter(|sender| sender.target_id() == *id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool {
|
||||||
|
let id = send_provider.target_id();
|
||||||
|
if self.senders.contains_key(&id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.senders.insert(id, send_provider).is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub mod std_mod {
|
||||||
|
use crate::queue::GenericReceiveError;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
impl<Event: GenericEvent + Send, ParamProvider: Debug>
|
||||||
|
EventReceiveProvider<Event, ParamProvider>
|
||||||
|
for mpsc::Receiver<EventMessage<Event, ParamProvider>>
|
||||||
|
{
|
||||||
|
type Error = GenericReceiveError;
|
||||||
|
|
||||||
|
fn try_recv_event(
|
||||||
|
&self,
|
||||||
|
) -> Result<Option<EventMessage<Event, ParamProvider>>, Self::Error> {
|
||||||
|
match self.try_recv() {
|
||||||
|
Ok(msg) => Ok(Some(msg)),
|
||||||
|
Err(e) => match e {
|
||||||
|
mpsc::TryRecvError::Empty => Ok(None),
|
||||||
|
mpsc::TryRecvError::Disconnected => {
|
||||||
|
Err(GenericReceiveError::TxDisconnected(None))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type EventU32ReceiverMpsc<ParamProvider = Params> =
|
||||||
|
mpsc::Receiver<EventMessage<EventU32, ParamProvider>>;
|
||||||
|
pub type EventU16ReceiverMpsc<ParamProvider = Params> =
|
||||||
|
mpsc::Receiver<EventMessage<EventU16, ParamProvider>>;
|
||||||
|
|
||||||
|
/// Generic event sender which uses a regular [mpsc::Sender] as the messaging backend to
|
||||||
|
/// send events.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EventSenderMpsc<Event: GenericEvent + Send> {
|
||||||
|
target_id: ComponentId,
|
||||||
|
sender: mpsc::Sender<EventMessage<Event>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Event: GenericEvent + Send> EventSenderMpsc<Event> {
|
||||||
|
pub fn new(target_id: ComponentId, sender: mpsc::Sender<EventMessage<Event>>) -> Self {
|
||||||
|
Self { target_id, sender }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Event: GenericEvent + Send> EventSendProvider<Event> for EventSenderMpsc<Event> {
|
||||||
|
type Error = GenericSendError;
|
||||||
|
|
||||||
|
fn target_id(&self) -> ComponentId {
|
||||||
|
self.target_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(&self, event_msg: EventMessage<Event>) -> Result<(), GenericSendError> {
|
||||||
|
self.sender
|
||||||
|
.send(event_msg)
|
||||||
|
.map_err(|_| GenericSendError::RxDisconnected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic event sender which uses the [mpsc::SyncSender] as the messaging backend to send
|
||||||
|
/// events. This has the advantage that the channel is bounded and thus more deterministic.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EventSenderMpscBounded<Event: GenericEvent + Send> {
|
||||||
|
target_id: ComponentId,
|
||||||
|
sender: mpsc::SyncSender<EventMessage<Event>>,
|
||||||
|
capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Event: GenericEvent + Send> EventSenderMpscBounded<Event> {
|
||||||
|
pub fn new(
|
||||||
|
target_id: ComponentId,
|
||||||
|
sender: mpsc::SyncSender<EventMessage<Event>>,
|
||||||
|
capacity: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
target_id,
|
||||||
|
sender,
|
||||||
|
capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Event: GenericEvent + Send> EventSendProvider<Event> for EventSenderMpscBounded<Event> {
|
||||||
|
type Error = GenericSendError;
|
||||||
|
|
||||||
|
fn target_id(&self) -> ComponentId {
|
||||||
|
self.target_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(&self, event_msg: EventMessage<Event>) -> Result<(), Self::Error> {
|
||||||
|
if let Err(e) = self.sender.try_send(event_msg) {
|
||||||
|
return match e {
|
||||||
|
mpsc::TrySendError::Full(_) => {
|
||||||
|
Err(GenericSendError::QueueFull(Some(self.capacity as u32)))
|
||||||
|
}
|
||||||
|
mpsc::TrySendError::Disconnected(_) => Err(GenericSendError::RxDisconnected),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type EventU32SenderMpsc = EventSenderMpsc<EventU32>;
|
||||||
|
pub type EventU16SenderMpsc = EventSenderMpsc<EventU16>;
|
||||||
|
pub type EventU32SenderMpscBounded = EventSenderMpscBounded<EventU32>;
|
||||||
|
pub type EventU16SenderMpscBounded = EventSenderMpscBounded<EventU16>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::event_man_legacy::EventManager;
|
||||||
|
use crate::events_legacy::{EventU32, GenericEvent, Severity};
|
||||||
|
use crate::params::{ParamsHeapless, ParamsRaw};
|
||||||
|
use crate::pus::test_util::{TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1};
|
||||||
|
use std::format;
|
||||||
|
use std::sync::mpsc::{self};
|
||||||
|
|
||||||
|
const TEST_EVENT: EventU32 = EventU32::new(Severity::Info, 0, 5);
|
||||||
|
|
||||||
|
fn check_next_event(
|
||||||
|
expected: EventU32,
|
||||||
|
receiver: &mpsc::Receiver<EventMessageU32>,
|
||||||
|
) -> Option<Params> {
|
||||||
|
if let Ok(event_msg) = receiver.try_recv() {
|
||||||
|
assert_eq!(event_msg.event, expected);
|
||||||
|
return event_msg.params;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_handled_event(
|
||||||
|
res: EventRoutingResult<EventU32, Params>,
|
||||||
|
expected: EventU32,
|
||||||
|
expected_num_sent: u32,
|
||||||
|
expected_sender_id: ComponentId,
|
||||||
|
) {
|
||||||
|
assert!(matches!(res, EventRoutingResult::Handled { .. }));
|
||||||
|
if let EventRoutingResult::Handled {
|
||||||
|
num_recipients,
|
||||||
|
event_msg,
|
||||||
|
} = res
|
||||||
|
{
|
||||||
|
assert_eq!(event_msg.event, expected);
|
||||||
|
assert_eq!(event_msg.sender_id, expected_sender_id);
|
||||||
|
assert_eq!(num_recipients, expected_num_sent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_event_man() -> (mpsc::Sender<EventMessageU32>, EventManagerWithMpsc) {
|
||||||
|
let (event_sender, event_receiver) = mpsc::channel();
|
||||||
|
(event_sender, EventManager::new(event_receiver))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic() {
|
||||||
|
let (event_sender, mut event_man) = generic_event_man();
|
||||||
|
let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
|
||||||
|
let event_grp_1_0 = EventU32::new(Severity::High, 1, 0);
|
||||||
|
let (single_event_sender, single_event_receiver) = mpsc::channel();
|
||||||
|
let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
|
||||||
|
event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
|
||||||
|
event_man.add_sender(single_event_listener);
|
||||||
|
let (group_event_sender_0, group_event_receiver_0) = mpsc::channel();
|
||||||
|
let group_event_listener = EventU32SenderMpsc::new(1, group_event_sender_0);
|
||||||
|
event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.target_id());
|
||||||
|
event_man.add_sender(group_event_listener);
|
||||||
|
|
||||||
|
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
|
||||||
|
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
|
||||||
|
};
|
||||||
|
// Test event with one listener
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0))
|
||||||
|
.expect("Sending single error failed");
|
||||||
|
let res = event_man.try_event_handling(&error_handler);
|
||||||
|
check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
|
||||||
|
check_next_event(event_grp_0, &single_event_receiver);
|
||||||
|
|
||||||
|
// Test event which is sent to all group listeners
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
|
||||||
|
.expect("Sending group error failed");
|
||||||
|
let res = event_man.try_event_handling(&error_handler);
|
||||||
|
check_handled_event(res, event_grp_1_0, 1, TEST_COMPONENT_ID_1.id());
|
||||||
|
check_next_event(event_grp_1_0, &group_event_receiver_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_with_basic_params() {
|
||||||
|
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
|
||||||
|
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
|
||||||
|
};
|
||||||
|
let (event_sender, mut event_man) = generic_event_man();
|
||||||
|
let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
|
||||||
|
let (single_event_sender, single_event_receiver) = mpsc::channel();
|
||||||
|
let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
|
||||||
|
event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
|
||||||
|
event_man.add_sender(single_event_listener);
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new_with_params(
|
||||||
|
TEST_COMPONENT_ID_0.id(),
|
||||||
|
event_grp_0,
|
||||||
|
&Params::Heapless((2_u32, 3_u32).into()),
|
||||||
|
))
|
||||||
|
.expect("Sending group error failed");
|
||||||
|
let res = event_man.try_event_handling(&error_handler);
|
||||||
|
check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
|
||||||
|
let aux = check_next_event(event_grp_0, &single_event_receiver);
|
||||||
|
assert!(aux.is_some());
|
||||||
|
let aux = aux.unwrap();
|
||||||
|
if let Params::Heapless(ParamsHeapless::Raw(ParamsRaw::U32Pair(pair))) = aux {
|
||||||
|
assert_eq!(pair.0, 2);
|
||||||
|
assert_eq!(pair.1, 3);
|
||||||
|
} else {
|
||||||
|
panic!("{}", format!("Unexpected auxiliary value type {:?}", aux));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test listening for multiple groups
|
||||||
|
#[test]
|
||||||
|
fn test_multi_group() {
|
||||||
|
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
|
||||||
|
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
|
||||||
|
};
|
||||||
|
let (event_sender, mut event_man) = generic_event_man();
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
assert!(matches!(res, EventRoutingResult::Empty));
|
||||||
|
|
||||||
|
let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
|
||||||
|
let event_grp_1_0 = EventU32::new(Severity::High, 1, 0);
|
||||||
|
let (event_grp_0_sender, event_grp_0_receiver) = mpsc::channel();
|
||||||
|
let event_grp_0_and_1_listener = EventU32SenderMpsc::new(0, event_grp_0_sender);
|
||||||
|
event_man.subscribe_group(
|
||||||
|
event_grp_0.group_id(),
|
||||||
|
event_grp_0_and_1_listener.target_id(),
|
||||||
|
);
|
||||||
|
event_man.subscribe_group(
|
||||||
|
event_grp_1_0.group_id(),
|
||||||
|
event_grp_0_and_1_listener.target_id(),
|
||||||
|
);
|
||||||
|
event_man.add_sender(event_grp_0_and_1_listener);
|
||||||
|
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0))
|
||||||
|
.expect("Sending Event Group 0 failed");
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
|
||||||
|
.expect("Sendign Event Group 1 failed");
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_grp_1_0, 1, TEST_COMPONENT_ID_1.id());
|
||||||
|
|
||||||
|
check_next_event(event_grp_0, &event_grp_0_receiver);
|
||||||
|
check_next_event(event_grp_1_0, &event_grp_0_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test listening to the same event from multiple listeners. Also test listening
|
||||||
|
/// to both group and single events from one listener
|
||||||
|
#[test]
|
||||||
|
fn test_listening_to_same_event_and_multi_type() {
|
||||||
|
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
|
||||||
|
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
|
||||||
|
};
|
||||||
|
let (event_sender, mut event_man) = generic_event_man();
|
||||||
|
let event_0 = EventU32::new(Severity::Info, 0, 5);
|
||||||
|
let event_1 = EventU32::new(Severity::High, 1, 0);
|
||||||
|
let (event_0_tx_0, event_0_rx_0) = mpsc::channel();
|
||||||
|
let (event_0_tx_1, event_0_rx_1) = mpsc::channel();
|
||||||
|
let event_listener_0 = EventU32SenderMpsc::new(0, event_0_tx_0);
|
||||||
|
let event_listener_1 = EventU32SenderMpsc::new(1, event_0_tx_1);
|
||||||
|
let event_listener_0_sender_id = event_listener_0.target_id();
|
||||||
|
event_man.subscribe_single(&event_0, event_listener_0_sender_id);
|
||||||
|
event_man.add_sender(event_listener_0);
|
||||||
|
let event_listener_1_sender_id = event_listener_1.target_id();
|
||||||
|
event_man.subscribe_single(&event_0, event_listener_1_sender_id);
|
||||||
|
event_man.add_sender(event_listener_1);
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
|
||||||
|
.expect("Triggering Event 0 failed");
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_0, 2, TEST_COMPONENT_ID_0.id());
|
||||||
|
check_next_event(event_0, &event_0_rx_0);
|
||||||
|
check_next_event(event_0, &event_0_rx_1);
|
||||||
|
event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
|
||||||
|
.expect("Triggering Event 0 failed");
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1))
|
||||||
|
.expect("Triggering Event 1 failed");
|
||||||
|
|
||||||
|
// 3 Events messages will be sent now
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_0, 2, TEST_COMPONENT_ID_0.id());
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id());
|
||||||
|
// Both the single event and the group event should arrive now
|
||||||
|
check_next_event(event_0, &event_0_rx_0);
|
||||||
|
check_next_event(event_1, &event_0_rx_0);
|
||||||
|
|
||||||
|
// Do double insertion and then remove duplicates
|
||||||
|
event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
|
||||||
|
event_man.remove_duplicates(&ListenerKey::Group(event_1.group_id()));
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_1))
|
||||||
|
.expect("Triggering Event 1 failed");
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_0.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_all_events_listener() {
|
||||||
|
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
|
||||||
|
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
|
||||||
|
};
|
||||||
|
let (event_sender, event_receiver) = mpsc::channel();
|
||||||
|
let mut event_man = EventManagerWithMpsc::new(event_receiver);
|
||||||
|
let event_0 = EventU32::new(Severity::Info, 0, 5);
|
||||||
|
let event_1 = EventU32::new(Severity::High, 1, 0);
|
||||||
|
let (event_0_tx_0, all_events_rx) = mpsc::channel();
|
||||||
|
let all_events_listener = EventU32SenderMpsc::new(0, event_0_tx_0);
|
||||||
|
event_man.subscribe_all(all_events_listener.target_id());
|
||||||
|
event_man.add_sender(all_events_listener);
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
|
||||||
|
.expect("Triggering event 0 failed");
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1))
|
||||||
|
.expect("Triggering event 1 failed");
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_0, 1, TEST_COMPONENT_ID_0.id());
|
||||||
|
let res = event_man.try_event_handling(error_handler);
|
||||||
|
check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id());
|
||||||
|
check_next_event(event_0, &all_events_rx);
|
||||||
|
check_next_event(event_1, &all_events_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bounded_event_sender_queue_full() {
|
||||||
|
let (event_sender, _event_receiver) = mpsc::sync_channel(3);
|
||||||
|
let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3);
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
|
||||||
|
.expect("sending test event failed");
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
|
||||||
|
.expect("sending test event failed");
|
||||||
|
event_sender
|
||||||
|
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
|
||||||
|
.expect("sending test event failed");
|
||||||
|
let error = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT));
|
||||||
|
if let Err(e) = error {
|
||||||
|
assert!(matches!(e, GenericSendError::QueueFull(Some(3))));
|
||||||
|
} else {
|
||||||
|
panic!("unexpected error {error:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_bounded_event_sender_rx_dropped() {
|
||||||
|
let (event_sender, event_receiver) = mpsc::sync_channel(3);
|
||||||
|
let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3);
|
||||||
|
drop(event_receiver);
|
||||||
|
if let Err(e) = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)) {
|
||||||
|
assert!(matches!(e, GenericSendError::RxDisconnected));
|
||||||
|
} else {
|
||||||
|
panic!("Expected error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
//! Event support module
|
//! # Event support module
|
||||||
//!
|
//!
|
||||||
//! This module includes the basic event structs [EventU32] and [EventU16] and versions with the
|
//! The user can define events as custom structs or enumerations.
|
||||||
//! ECSS severity levels as a type parameter. These structs are simple abstractions on top of the
|
//! The event structures defined here are type erased and rely on some properties which
|
||||||
//! [u32] and [u16] types where the raw value is the unique identifier for a particular event.
|
//! should be provided by the user through the [Event] and [serde::Serialize] trait.
|
||||||
|
//!
|
||||||
|
//! This in turn allows to use higher-level abstractions like the event manger.
|
||||||
|
//!
|
||||||
|
//! This module includes the basic type erased event structs [EventErasedAlloc] and
|
||||||
|
//! [EventErasedHeapless].
|
||||||
//! The abstraction also allows to group related events using a group ID, and the severity
|
//! The abstraction also allows to group related events using a group ID, and the severity
|
||||||
//! of an event is encoded inside the raw value itself with four possible [Severity] levels:
|
//! of an event is encoded inside the raw value itself with four possible [Severity] levels:
|
||||||
//!
|
//!
|
||||||
@@ -10,29 +15,11 @@
|
|||||||
//! - LOW
|
//! - LOW
|
||||||
//! - MEDIUM
|
//! - MEDIUM
|
||||||
//! - HIGH
|
//! - HIGH
|
||||||
//!
|
|
||||||
//! All event structs implement the [EcssEnumeration] trait and can be created as constants.
|
|
||||||
//! This allows to easily create a static list of constant events which can then be used to generate
|
|
||||||
//! event telemetry using the PUS event manager modules.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo};
|
|
||||||
//!
|
|
||||||
//! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(1, 0);
|
|
||||||
//! const MSG_FAILED: EventU32 = EventU32::new(Severity::Low, 1, 1);
|
|
||||||
//!
|
|
||||||
//! const TEMPERATURE_HIGH: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(2, 0);
|
|
||||||
//!
|
|
||||||
//! let small_event = EventU16::new(Severity::Info, 3, 0);
|
|
||||||
//! ```
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
use core::marker::PhantomData;
|
|
||||||
use delegate::delegate;
|
use arbitrary_int::{prelude::*, u14};
|
||||||
use spacepackets::ecss::EcssEnumeration;
|
#[cfg(feature = "heapless")]
|
||||||
use spacepackets::util::{ToBeBytes, UnsignedEnum};
|
|
||||||
use spacepackets::ByteConversionError;
|
use spacepackets::ByteConversionError;
|
||||||
|
|
||||||
/// Using a type definition allows to change this to u64 in the future more easily
|
/// Using a type definition allows to change this to u64 in the future more easily
|
||||||
@@ -40,12 +27,14 @@ pub type LargestEventRaw = u32;
|
|||||||
/// Using a type definition allows to change this to u32 in the future more easily
|
/// Using a type definition allows to change this to u32 in the future more easily
|
||||||
pub type LargestGroupIdRaw = u16;
|
pub type LargestGroupIdRaw = u16;
|
||||||
|
|
||||||
pub const MAX_GROUP_ID_U32_EVENT: u16 = 2_u16.pow(14) - 1;
|
pub const MAX_GROUP_ID_U32_EVENT: u16 = u14::MAX.value();
|
||||||
pub const MAX_GROUP_ID_U16_EVENT: u16 = 2_u16.pow(6) - 1;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(
|
||||||
|
Copy, Clone, PartialEq, Eq, Debug, Hash, num_enum::TryFromPrimitive, num_enum::IntoPrimitive,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
Info = 0,
|
Info = 0,
|
||||||
Low = 1,
|
Low = 1,
|
||||||
@@ -57,801 +46,203 @@ pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone {
|
|||||||
const SEVERITY: Severity;
|
const SEVERITY: Severity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type level support struct
|
pub trait Event: Clone {
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
fn id(&self) -> EventId;
|
||||||
pub struct SeverityInfo {}
|
|
||||||
impl HasSeverity for SeverityInfo {
|
|
||||||
const SEVERITY: Severity = Severity::Info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type level support struct
|
pub type GroupId = u14;
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
||||||
pub struct SeverityLow {}
|
|
||||||
impl HasSeverity for SeverityLow {
|
|
||||||
const SEVERITY: Severity = Severity::Low;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type level support struct
|
/// Unique event identifier.
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
///
|
||||||
pub struct SeverityMedium {}
|
/// Consists of a group ID, a unique ID and the severity.
|
||||||
impl HasSeverity for SeverityMedium {
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
const SEVERITY: Severity = Severity::Medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type level support struct
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
||||||
pub struct SeverityHigh {}
|
|
||||||
impl HasSeverity for SeverityHigh {
|
|
||||||
const SEVERITY: Severity = Severity::High;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait GenericEvent: EcssEnumeration + Copy + Clone {
|
|
||||||
type Raw;
|
|
||||||
type GroupId;
|
|
||||||
type UniqueId;
|
|
||||||
|
|
||||||
fn raw(&self) -> Self::Raw;
|
|
||||||
fn severity(&self) -> Severity;
|
|
||||||
fn group_id(&self) -> Self::GroupId;
|
|
||||||
fn unique_id(&self) -> Self::UniqueId;
|
|
||||||
|
|
||||||
fn raw_as_largest_type(&self) -> LargestEventRaw;
|
|
||||||
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u8> for Severity {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
x if x == Severity::Info as u8 => Ok(Severity::Info),
|
|
||||||
x if x == Severity::Low as u8 => Ok(Severity::Low),
|
|
||||||
x if x == Severity::Medium as u8 => Ok(Severity::Medium),
|
|
||||||
x if x == Severity::High as u8 => Ok(Severity::High),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
struct EventBase<Raw, GroupId, UniqueId> {
|
pub struct EventId {
|
||||||
severity: Severity,
|
|
||||||
group_id: GroupId,
|
group_id: GroupId,
|
||||||
unique_id: UniqueId,
|
unique_id: u16,
|
||||||
phantom: PhantomData<Raw>,
|
severity: Severity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Raw: ToBeBytes, GroupId, UniqueId> EventBase<Raw, GroupId, UniqueId> {
|
impl EventId {
|
||||||
fn write_to_bytes(
|
pub fn new(severity: Severity, group_id: u14, unique_id: u16) -> Self {
|
||||||
&self,
|
Self {
|
||||||
raw: Raw,
|
severity,
|
||||||
buf: &mut [u8],
|
group_id,
|
||||||
width: usize,
|
unique_id,
|
||||||
) -> Result<usize, ByteConversionError> {
|
|
||||||
if buf.len() < width {
|
|
||||||
return Err(ByteConversionError::ToSliceTooSmall {
|
|
||||||
found: buf.len(),
|
|
||||||
expected: width,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
buf.copy_from_slice(raw.to_be_bytes().as_ref());
|
|
||||||
Ok(raw.written_len())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl EventBase<u32, u16, u16> {
|
|
||||||
#[inline]
|
|
||||||
fn raw(&self) -> u32 {
|
|
||||||
((self.severity as u32) << 30) | ((self.group_id as u32) << 16) | self.unique_id as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventBase<u16, u8, u8> {
|
|
||||||
#[inline]
|
|
||||||
fn raw(&self) -> u16 {
|
|
||||||
((self.severity as u16) << 14) | ((self.group_id as u16) << 8) | self.unique_id as u16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<RAW, GID, UID> EventBase<RAW, GID, UID> {
|
|
||||||
#[inline]
|
|
||||||
pub fn severity(&self) -> Severity {
|
|
||||||
self.severity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<RAW, GID> EventBase<RAW, GID, u16> {
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unique_id(&self) -> u16 {
|
pub fn unique_id(&self) -> u16 {
|
||||||
self.unique_id
|
self.unique_id
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<RAW, GID> EventBase<RAW, GID, u8> {
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unique_id(&self) -> u8 {
|
pub fn severity(&self) -> Severity {
|
||||||
self.unique_id
|
self.severity
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<RAW, UID> EventBase<RAW, u16, UID> {
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn group_id(&self) -> u16 {
|
pub fn group_id(&self) -> u14 {
|
||||||
self.group_id
|
self.group_id
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<RAW, UID> EventBase<RAW, u8, UID> {
|
pub fn raw(&self) -> u32 {
|
||||||
#[inline]
|
((self.severity as u32) << 30)
|
||||||
pub fn group_id(&self) -> u8 {
|
| ((self.group_id.as_u16() as u32) << 16)
|
||||||
self.group_id
|
| (self.unique_id as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! event_provider_impl {
|
impl From<u32> for EventId {
|
||||||
() => {
|
|
||||||
#[inline]
|
|
||||||
fn raw(&self) -> Self::Raw {
|
|
||||||
self.base.raw()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the severity of an event. Returns None if that severity bit field of the raw event
|
|
||||||
/// ID is invalid
|
|
||||||
#[inline]
|
|
||||||
fn severity(&self) -> Severity {
|
|
||||||
self.base.severity()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn group_id(&self) -> Self::GroupId {
|
|
||||||
self.base.group_id()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn unique_id(&self) -> Self::UniqueId {
|
|
||||||
self.base.unique_id()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_event_provider {
|
|
||||||
($BaseIdent: ident, $TypedIdent: ident, $raw: ty, $gid: ty, $uid: ty) => {
|
|
||||||
impl GenericEvent for $BaseIdent {
|
|
||||||
type Raw = $raw;
|
|
||||||
type GroupId = $gid;
|
|
||||||
type UniqueId = $uid;
|
|
||||||
|
|
||||||
event_provider_impl!();
|
|
||||||
|
|
||||||
fn raw_as_largest_type(&self) -> LargestEventRaw {
|
|
||||||
self.raw().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw {
|
|
||||||
self.group_id().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SEVERITY: HasSeverity> GenericEvent for $TypedIdent<SEVERITY> {
|
|
||||||
type Raw = $raw;
|
|
||||||
type GroupId = $gid;
|
|
||||||
type UniqueId = $uid;
|
|
||||||
|
|
||||||
delegate!(to self.event {
|
|
||||||
fn raw(&self) -> Self::Raw;
|
|
||||||
fn severity(&self) -> Severity;
|
|
||||||
fn group_id(&self) -> Self::GroupId;
|
|
||||||
fn unique_id(&self) -> Self::UniqueId;
|
|
||||||
fn raw_as_largest_type(&self) -> LargestEventRaw;
|
|
||||||
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! try_from_impls {
|
|
||||||
($SevIdent: ident, $severity: path, $raw: ty, $TypedSevIdent: ident) => {
|
|
||||||
impl TryFrom<$raw> for $TypedSevIdent<$SevIdent> {
|
|
||||||
type Error = Severity;
|
|
||||||
|
|
||||||
fn try_from(raw: $raw) -> Result<Self, Self::Error> {
|
|
||||||
Self::try_from_generic($severity, raw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! const_from_fn {
|
|
||||||
($from_fn_name: ident, $TypedIdent: ident, $SevIdent: ident) => {
|
|
||||||
pub const fn $from_fn_name(event: $TypedIdent<$SevIdent>) -> Self {
|
|
||||||
Self {
|
|
||||||
base: event.event.base,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct EventU32 {
|
|
||||||
base: EventBase<u32, u16, u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct EventU32TypedSev<SEVERITY> {
|
|
||||||
event: EventU32,
|
|
||||||
phantom: PhantomData<SEVERITY>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SEVERITY: HasSeverity> From<EventU32TypedSev<SEVERITY>> for EventU32 {
|
|
||||||
fn from(e: EventU32TypedSev<SEVERITY>) -> Self {
|
|
||||||
Self { base: e.event.base }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> AsRef<EventU32> for EventU32TypedSev<Severity> {
|
|
||||||
fn as_ref(&self) -> &EventU32 {
|
|
||||||
&self.event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> AsMut<EventU32> for EventU32TypedSev<Severity> {
|
|
||||||
fn as_mut(&mut self) -> &mut EventU32 {
|
|
||||||
&mut self.event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_event_provider!(EventU32, EventU32TypedSev, u32, u16, u16);
|
|
||||||
|
|
||||||
impl EventU32 {
|
|
||||||
/// Generate an event. The raw representation of an event has 32 bits.
|
|
||||||
/// If the passed group ID is invalid (too large), None wil be returned
|
|
||||||
///
|
|
||||||
/// # Parameter
|
|
||||||
///
|
|
||||||
/// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will
|
|
||||||
/// be stored inside the uppermost 2 bits of the raw event ID
|
|
||||||
/// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the
|
|
||||||
/// next 14 bits after the severity. Therefore, the size is limited by dec 16383 hex 0x3FFF.
|
|
||||||
/// * `unique_id`: Each event has a unique 16 bit ID occupying the last 16 bits of the
|
|
||||||
/// raw event ID
|
|
||||||
pub fn new_checked(
|
|
||||||
severity: Severity,
|
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
|
||||||
) -> Option<Self> {
|
|
||||||
if group_id > MAX_GROUP_ID_U32_EVENT {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Self {
|
|
||||||
base: EventBase {
|
|
||||||
severity,
|
|
||||||
group_id,
|
|
||||||
unique_id,
|
|
||||||
phantom: PhantomData,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This constructor will panic if the passed group is is larger than [MAX_GROUP_ID_U32_EVENT].
|
|
||||||
pub const fn new(
|
|
||||||
severity: Severity,
|
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
|
||||||
) -> Self {
|
|
||||||
if group_id > MAX_GROUP_ID_U32_EVENT {
|
|
||||||
panic!("Group ID too large");
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
base: EventBase {
|
|
||||||
severity,
|
|
||||||
group_id,
|
|
||||||
unique_id,
|
|
||||||
phantom: PhantomData,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_be_bytes(bytes: [u8; 4]) -> Self {
|
|
||||||
Self::from(u32::from_be_bytes(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
const_from_fn!(const_from_info, EventU32TypedSev, SeverityInfo);
|
|
||||||
const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow);
|
|
||||||
const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium);
|
|
||||||
const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for EventU32 {
|
|
||||||
fn from(raw: u32) -> Self {
|
fn from(raw: u32) -> Self {
|
||||||
// Severity conversion from u8 should never fail
|
// Severity conversion from u8 should never fail
|
||||||
let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
|
let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
|
||||||
let group_id = ((raw >> 16) & 0x3FFF) as u16;
|
let group_id = u14::new(((raw >> 16) & 0x3FFF) as u16);
|
||||||
let unique_id = (raw & 0xFFFF) as u16;
|
let unique_id = (raw & 0xFFFF) as u16;
|
||||||
// Sanitized input, should never fail
|
// Sanitized input, should never fail
|
||||||
Self::new(severity, group_id, unique_id)
|
Self::new(severity, group_id, unique_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnsignedEnum for EventU32 {
|
/// Event which was type erased and serialized into a [alloc::vec::Vec].
|
||||||
fn size(&self) -> usize {
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
core::mem::size_of::<u32>()
|
#[cfg(feature = "alloc")]
|
||||||
|
pub struct EventErasedAlloc {
|
||||||
|
id: EventId,
|
||||||
|
event_raw: alloc::vec::Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl EventErasedAlloc {
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
/// Creates a new event by serializing the given event using [postcard].
|
||||||
|
pub fn new(event: &(impl serde::Serialize + Event)) -> Self {
|
||||||
|
Self {
|
||||||
|
id: event.id(),
|
||||||
|
event_raw: postcard::to_allocvec(event).unwrap(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
pub fn new_with_raw_event(id: EventId, event_raw: &[u8]) -> Self {
|
||||||
self.base.write_to_bytes(self.raw(), buf, self.size())
|
Self {
|
||||||
|
id,
|
||||||
|
event_raw: event_raw.to_vec(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value(&self) -> u64 {
|
#[inline]
|
||||||
self.raw().into()
|
pub fn raw(&self) -> &[u8] {
|
||||||
|
&self.event_raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EcssEnumeration for EventU32 {
|
#[cfg(feature = "serde")]
|
||||||
fn pfc(&self) -> u8 {
|
#[cfg(feature = "alloc")]
|
||||||
u32::BITS as u8
|
impl<T: serde::Serialize + Event> From<T> for EventErasedAlloc {
|
||||||
|
fn from(event: T) -> Self {
|
||||||
|
Self::new(&event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SEVERITY: HasSeverity> EventU32TypedSev<SEVERITY> {
|
#[cfg(feature = "alloc")]
|
||||||
/// This is similar to [EventU32::new] but the severity is a type generic, which allows to
|
impl Event for EventErasedAlloc {
|
||||||
/// have distinct types for events with different severities
|
fn id(&self) -> EventId {
|
||||||
pub fn new_checked(
|
self.id
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
}
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
}
|
||||||
) -> Option<Self> {
|
|
||||||
let event = EventU32::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?;
|
/// Event which was type erased and serialized into a [heapless::vec::Vec].
|
||||||
Some(Self {
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
event,
|
#[cfg(feature = "heapless")]
|
||||||
phantom: PhantomData,
|
pub struct EventErasedHeapless<const N: usize> {
|
||||||
|
id: EventId,
|
||||||
|
event_raw: heapless::vec::Vec<u8, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "heapless")]
|
||||||
|
impl<const N: usize> Event for EventErasedHeapless<N> {
|
||||||
|
fn id(&self) -> EventId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "heapless")]
|
||||||
|
impl<const N: usize> EventErasedHeapless<N> {
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
/// Creates a new event by serializing the given event using [postcard].
|
||||||
|
pub fn new(event: &(impl serde::Serialize + Event)) -> Result<Self, ByteConversionError> {
|
||||||
|
let ser_size = postcard::experimental::serialized_size(event).unwrap();
|
||||||
|
if ser_size > N {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall {
|
||||||
|
found: N,
|
||||||
|
expected: ser_size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut vec = heapless::Vec::<u8, N>::new();
|
||||||
|
vec.resize(N, 0).unwrap();
|
||||||
|
postcard::to_slice(event, vec.as_mut_slice()).unwrap();
|
||||||
|
Ok(Self {
|
||||||
|
id: event.id(),
|
||||||
|
event_raw: vec,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U32_EVENT].
|
pub fn new_with_raw_event(id: EventId, event_raw: heapless::Vec<u8, N>) -> Self {
|
||||||
pub const fn new(
|
Self { id, event_raw }
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
|
||||||
) -> Self {
|
|
||||||
let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id);
|
|
||||||
Self {
|
|
||||||
event,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_from_generic(expected: Severity, raw: u32) -> Result<Self, Severity> {
|
|
||||||
let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
|
|
||||||
if severity != expected {
|
|
||||||
return Err(severity);
|
|
||||||
}
|
|
||||||
Ok(Self::new(
|
|
||||||
((raw >> 16) & 0x3FFF) as u16,
|
|
||||||
(raw & 0xFFFF) as u16,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try_from_impls!(SeverityInfo, Severity::Info, u32, EventU32TypedSev);
|
|
||||||
try_from_impls!(SeverityLow, Severity::Low, u32, EventU32TypedSev);
|
|
||||||
try_from_impls!(SeverityMedium, Severity::Medium, u32, EventU32TypedSev);
|
|
||||||
try_from_impls!(SeverityHigh, Severity::High, u32, EventU32TypedSev);
|
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
|
||||||
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> {
|
|
||||||
delegate!(to self.event {
|
|
||||||
fn size(&self) -> usize;
|
|
||||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
|
|
||||||
fn value(&self) -> u64;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
|
||||||
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU32TypedSev<SEVERITY> {
|
|
||||||
delegate!(to self.event {
|
|
||||||
fn pfc(&self) -> u8;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct EventU16 {
|
|
||||||
base: EventBase<u16, u8, u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct EventU16TypedSev<SEVERITY> {
|
|
||||||
event: EventU16,
|
|
||||||
phantom: PhantomData<SEVERITY>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> AsRef<EventU16> for EventU16TypedSev<Severity> {
|
|
||||||
fn as_ref(&self) -> &EventU16 {
|
|
||||||
&self.event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> AsMut<EventU16> for EventU16TypedSev<Severity> {
|
|
||||||
fn as_mut(&mut self) -> &mut EventU16 {
|
|
||||||
&mut self.event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventU16 {
|
|
||||||
/// Generate a small event. The raw representation of a small event has 16 bits.
|
|
||||||
/// If the passed group ID is invalid (too large), [None] wil be returned
|
|
||||||
///
|
|
||||||
/// # Parameter
|
|
||||||
///
|
|
||||||
/// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will
|
|
||||||
/// be stored inside the uppermost 2 bits of the raw event ID
|
|
||||||
/// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the
|
|
||||||
/// next 6 bits after the severity. Therefore, the size is limited by dec 63 hex 0x3F.
|
|
||||||
/// * `unique_id`: Each event has a unique 8 bit ID occupying the last 8 bits of the
|
|
||||||
/// raw event ID
|
|
||||||
pub fn new_checked(
|
|
||||||
severity: Severity,
|
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
|
||||||
) -> Option<Self> {
|
|
||||||
if group_id > (2u8.pow(6) - 1) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Self {
|
|
||||||
base: EventBase {
|
|
||||||
severity,
|
|
||||||
group_id,
|
|
||||||
unique_id,
|
|
||||||
phantom: Default::default(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT].
|
|
||||||
pub const fn new(
|
|
||||||
severity: Severity,
|
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
|
||||||
) -> Self {
|
|
||||||
if group_id > (2u8.pow(6) - 1) {
|
|
||||||
panic!("Group ID too large");
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
base: EventBase {
|
|
||||||
severity,
|
|
||||||
group_id,
|
|
||||||
unique_id,
|
|
||||||
phantom: PhantomData,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
|
|
||||||
Self::from(u16::from_be_bytes(bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
const_from_fn!(const_from_info, EventU16TypedSev, SeverityInfo);
|
|
||||||
const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow);
|
|
||||||
const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium);
|
|
||||||
const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u16> for EventU16 {
|
|
||||||
fn from(raw: <Self as GenericEvent>::Raw) -> Self {
|
|
||||||
let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
|
|
||||||
let group_id = ((raw >> 8) & 0x3F) as u8;
|
|
||||||
let unique_id = (raw & 0xFF) as u8;
|
|
||||||
// Sanitized input, new call should never fail
|
|
||||||
Self::new(severity, group_id, unique_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnsignedEnum for EventU16 {
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
core::mem::size_of::<u16>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
|
||||||
self.base.write_to_bytes(self.raw(), buf, self.size())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn value(&self) -> u64 {
|
|
||||||
self.raw().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl EcssEnumeration for EventU16 {
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pfc(&self) -> u8 {
|
pub fn raw(&self) -> &[u8] {
|
||||||
u16::BITS as u8
|
&self.event_raw
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SEVERITY: HasSeverity> EventU16TypedSev<SEVERITY> {
|
|
||||||
/// This is similar to [EventU16::new] but the severity is a type generic, which allows to
|
|
||||||
/// have distinct types for events with different severities
|
|
||||||
pub fn new_checked(
|
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
|
||||||
) -> Option<Self> {
|
|
||||||
let event = EventU16::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?;
|
|
||||||
Some(Self {
|
|
||||||
event,
|
|
||||||
phantom: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT].
|
|
||||||
pub const fn new(
|
|
||||||
group_id: <Self as GenericEvent>::GroupId,
|
|
||||||
unique_id: <Self as GenericEvent>::UniqueId,
|
|
||||||
) -> Self {
|
|
||||||
let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id);
|
|
||||||
Self {
|
|
||||||
event,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_from_generic(expected: Severity, raw: u16) -> Result<Self, Severity> {
|
|
||||||
let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
|
|
||||||
if severity != expected {
|
|
||||||
return Err(severity);
|
|
||||||
}
|
|
||||||
Ok(Self::new(((raw >> 8) & 0x3F) as u8, (raw & 0xFF) as u8))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8);
|
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
|
||||||
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> {
|
|
||||||
delegate!(to self.event {
|
|
||||||
fn size(&self) -> usize;
|
|
||||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
|
|
||||||
fn value(&self) -> u64;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
|
||||||
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU16TypedSev<SEVERITY> {
|
|
||||||
delegate!(to self.event {
|
|
||||||
fn pfc(&self) -> u8;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try_from_impls!(SeverityInfo, Severity::Info, u16, EventU16TypedSev);
|
|
||||||
try_from_impls!(SeverityLow, Severity::Low, u16, EventU16TypedSev);
|
|
||||||
try_from_impls!(SeverityMedium, Severity::Medium, u16, EventU16TypedSev);
|
|
||||||
try_from_impls!(SeverityHigh, Severity::High, u16, EventU16TypedSev);
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> PartialEq<EventU32> for EventU32TypedSev<Severity> {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &EventU32) -> bool {
|
|
||||||
self.raw() == other.raw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> PartialEq<EventU32TypedSev<Severity>> for EventU32 {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &EventU32TypedSev<Severity>) -> bool {
|
|
||||||
self.raw() == other.raw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> PartialEq<EventU16> for EventU16TypedSev<Severity> {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &EventU16) -> bool {
|
|
||||||
self.raw() == other.raw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Severity: HasSeverity> PartialEq<EventU16TypedSev<Severity>> for EventU16 {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &EventU16TypedSev<Severity>) -> bool {
|
|
||||||
self.raw() == other.raw()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::EventU32TypedSev;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use spacepackets::ByteConversionError;
|
|
||||||
use std::mem::size_of;
|
|
||||||
|
|
||||||
fn assert_size<T>(_: T, val: usize) {
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
assert_eq!(size_of::<T>(), val);
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
pub enum TestEvent {
|
||||||
|
Info,
|
||||||
|
ErrorOtherGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(0, 0);
|
impl Event for TestEvent {
|
||||||
const INFO_EVENT_SMALL: EventU16TypedSev<SeverityInfo> = EventU16TypedSev::new(0, 0);
|
fn id(&self) -> EventId {
|
||||||
const HIGH_SEV_EVENT: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(0x3FFF, 0xFFFF);
|
match self {
|
||||||
const HIGH_SEV_EVENT_SMALL: EventU16TypedSev<SeverityHigh> = EventU16TypedSev::new(0x3F, 0xff);
|
TestEvent::Info => EventId::new(Severity::Info, u14::new(0), 0),
|
||||||
|
TestEvent::ErrorOtherGroup => EventId::new(Severity::High, u14::new(1), 1),
|
||||||
/// This working is a test in itself.
|
}
|
||||||
const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT);
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_normal_from_raw_conversion() {
|
|
||||||
let conv_from_raw = EventU32TypedSev::<SeverityInfo>::try_from(INFO_EVENT.raw())
|
|
||||||
.expect("Creating typed EventU32 failed");
|
|
||||||
assert_eq!(conv_from_raw, INFO_EVENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_small_from_raw_conversion() {
|
|
||||||
let conv_from_raw = EventU16TypedSev::<SeverityInfo>::try_from(INFO_EVENT_SMALL.raw())
|
|
||||||
.expect("Creating typed EventU16 failed");
|
|
||||||
assert_eq!(conv_from_raw, INFO_EVENT_SMALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_normal_size() {
|
|
||||||
assert_size(INFO_EVENT.raw(), 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_small_size() {
|
|
||||||
assert_size(INFO_EVENT_SMALL.raw(), 2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normal_event_getters() {
|
fn test_normal_event_getters() {
|
||||||
assert_eq!(INFO_EVENT.severity(), Severity::Info);
|
assert_eq!(TestEvent::Info.id().severity(), Severity::Info);
|
||||||
assert_eq!(INFO_EVENT.unique_id(), 0);
|
assert_eq!(TestEvent::Info.id().unique_id(), 0);
|
||||||
assert_eq!(INFO_EVENT.group_id(), 0);
|
assert_eq!(TestEvent::Info.id().group_id().value(), 0);
|
||||||
let raw_event = INFO_EVENT.raw();
|
assert_eq!(TestEvent::ErrorOtherGroup.id().group_id().value(), 1);
|
||||||
|
assert_eq!(TestEvent::ErrorOtherGroup.id().unique_id(), 1);
|
||||||
|
let raw_event = TestEvent::Info.id().raw();
|
||||||
assert_eq!(raw_event, 0x00000000);
|
assert_eq!(raw_event, 0x00000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_small_event_getters() {
|
#[cfg(feature = "serde")]
|
||||||
assert_eq!(INFO_EVENT_SMALL.severity(), Severity::Info);
|
fn test_basic_erased_alloc_event() {
|
||||||
assert_eq!(INFO_EVENT_SMALL.unique_id(), 0);
|
let event = EventErasedAlloc::new(&TestEvent::Info);
|
||||||
assert_eq!(INFO_EVENT_SMALL.group_id(), 0);
|
let test_event: TestEvent = postcard::from_bytes(event.raw()).unwrap();
|
||||||
let raw_event = INFO_EVENT_SMALL.raw();
|
assert_eq!(test_event, TestEvent::Info);
|
||||||
assert_eq!(raw_event, 0x00000000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn all_ones_event_regular() {
|
#[cfg(all(feature = "serde", feature = "alloc"))]
|
||||||
assert_eq!(HIGH_SEV_EVENT.severity(), Severity::High);
|
fn test_basic_erased_heapless_event() {
|
||||||
assert_eq!(HIGH_SEV_EVENT.group_id(), 0x3FFF);
|
let event = EventErasedHeapless::<8>::new(&TestEvent::Info).unwrap();
|
||||||
assert_eq!(HIGH_SEV_EVENT.unique_id(), 0xFFFF);
|
let test_event: TestEvent = postcard::from_bytes(event.raw()).unwrap();
|
||||||
let raw_event = HIGH_SEV_EVENT.raw();
|
assert_eq!(test_event, TestEvent::Info);
|
||||||
assert_eq!(raw_event, 0xFFFFFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn all_ones_event_small() {
|
|
||||||
assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::High);
|
|
||||||
assert_eq!(HIGH_SEV_EVENT_SMALL.group_id(), 0x3F);
|
|
||||||
assert_eq!(HIGH_SEV_EVENT_SMALL.unique_id(), 0xFF);
|
|
||||||
let raw_event = HIGH_SEV_EVENT_SMALL.raw();
|
|
||||||
assert_eq!(raw_event, 0xFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_group_id_normal() {
|
|
||||||
assert!(EventU32TypedSev::<SeverityMedium>::new_checked(2_u16.pow(14), 0).is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_group_id_small() {
|
|
||||||
assert!(EventU16TypedSev::<SeverityMedium>::new_checked(2_u8.pow(6), 0).is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn regular_new() {
|
|
||||||
assert_eq!(
|
|
||||||
EventU32TypedSev::<SeverityInfo>::new_checked(0, 0)
|
|
||||||
.expect("Creating regular event failed"),
|
|
||||||
INFO_EVENT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn small_new() {
|
|
||||||
assert_eq!(
|
|
||||||
EventU16TypedSev::<SeverityInfo>::new_checked(0, 0)
|
|
||||||
.expect("Creating regular event failed"),
|
|
||||||
INFO_EVENT_SMALL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn as_largest_type() {
|
|
||||||
let event_raw = HIGH_SEV_EVENT.raw_as_largest_type();
|
|
||||||
assert_size(event_raw, 4);
|
|
||||||
assert_eq!(event_raw, 0xFFFFFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn as_largest_type_for_small_event() {
|
|
||||||
let event_raw = HIGH_SEV_EVENT_SMALL.raw_as_largest_type();
|
|
||||||
assert_size(event_raw, 4);
|
|
||||||
assert_eq!(event_raw, 0xFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn as_largest_group_id() {
|
|
||||||
let group_id = HIGH_SEV_EVENT.group_id_as_largest_type();
|
|
||||||
assert_size(group_id, 2);
|
|
||||||
assert_eq!(group_id, 0x3FFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn as_largest_group_id_small_event() {
|
|
||||||
let group_id = HIGH_SEV_EVENT_SMALL.group_id_as_largest_type();
|
|
||||||
assert_size(group_id, 2);
|
|
||||||
assert_eq!(group_id, 0x3F);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_to_buf() {
|
|
||||||
let mut buf: [u8; 4] = [0; 4];
|
|
||||||
assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok());
|
|
||||||
let val_from_raw = u32::from_be_bytes(buf);
|
|
||||||
assert_eq!(val_from_raw, 0xFFFFFFFF);
|
|
||||||
let event_read_back = EventU32::from_be_bytes(buf);
|
|
||||||
assert_eq!(event_read_back, HIGH_SEV_EVENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_to_buf_small() {
|
|
||||||
let mut buf: [u8; 2] = [0; 2];
|
|
||||||
assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok());
|
|
||||||
let val_from_raw = u16::from_be_bytes(buf);
|
|
||||||
assert_eq!(val_from_raw, 0xFFFF);
|
|
||||||
let event_read_back = EventU16::from_be_bytes(buf);
|
|
||||||
assert_eq!(event_read_back, HIGH_SEV_EVENT_SMALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_to_buf_insufficient_buf() {
|
|
||||||
let mut buf: [u8; 3] = [0; 3];
|
|
||||||
let err = HIGH_SEV_EVENT.write_to_be_bytes(&mut buf);
|
|
||||||
assert!(err.is_err());
|
|
||||||
let err = err.unwrap_err();
|
|
||||||
if let ByteConversionError::ToSliceTooSmall { found, expected } = err {
|
|
||||||
assert_eq!(expected, 4);
|
|
||||||
assert_eq!(found, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_to_buf_small_insufficient_buf() {
|
|
||||||
let mut buf: [u8; 1] = [0; 1];
|
|
||||||
let err = HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf);
|
|
||||||
assert!(err.is_err());
|
|
||||||
let err = err.unwrap_err();
|
|
||||||
if let ByteConversionError::ToSliceTooSmall { found, expected } = err {
|
|
||||||
assert_eq!(expected, 2);
|
|
||||||
assert_eq!(found, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn severity_from_invalid_raw_val() {
|
|
||||||
let invalid = 0xFF;
|
|
||||||
assert!(Severity::try_from(invalid).is_err());
|
|
||||||
let invalid = Severity::High as u8 + 1;
|
|
||||||
assert!(Severity::try_from(invalid).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reduction() {
|
|
||||||
let event = EventU32TypedSev::<SeverityInfo>::new(1, 1);
|
|
||||||
let raw = event.raw();
|
|
||||||
let reduced: EventU32 = event.into();
|
|
||||||
assert_eq!(reduced.group_id(), 1);
|
|
||||||
assert_eq!(reduced.unique_id(), 1);
|
|
||||||
assert_eq!(raw, reduced.raw());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn const_reducation() {
|
|
||||||
assert_eq!(INFO_REDUCED.raw(), INFO_EVENT.raw());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
859
satrs/src/events_legacy.rs
Normal file
859
satrs/src/events_legacy.rs
Normal file
@@ -0,0 +1,859 @@
|
|||||||
|
//! # Event support module
|
||||||
|
//!
|
||||||
|
//! This is a legacy module. It is recommended to use [super::events] instead.
|
||||||
|
//!
|
||||||
|
//! This module includes the basic event structs [EventU32] and [EventU16] and versions with the
|
||||||
|
//! ECSS severity levels as a type parameter. These structs are simple abstractions on top of the
|
||||||
|
//! [u32] and [u16] types where the raw value is the unique identifier for a particular event.
|
||||||
|
//! The abstraction also allows to group related events using a group ID, and the severity
|
||||||
|
//! of an event is encoded inside the raw value itself with four possible [Severity] levels:
|
||||||
|
//!
|
||||||
|
//! - INFO
|
||||||
|
//! - LOW
|
||||||
|
//! - MEDIUM
|
||||||
|
//! - HIGH
|
||||||
|
//!
|
||||||
|
//! All event structs implement the [EcssEnumeration] trait and can be created as constants.
|
||||||
|
//! This allows to easily create a static list of constant events which can then be used to generate
|
||||||
|
//! event telemetry using the PUS event manager modules.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use satrs::events_legacy::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo};
|
||||||
|
//!
|
||||||
|
//! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(1, 0);
|
||||||
|
//! const MSG_FAILED: EventU32 = EventU32::new(Severity::Low, 1, 1);
|
||||||
|
//!
|
||||||
|
//! const TEMPERATURE_HIGH: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(2, 0);
|
||||||
|
//!
|
||||||
|
//! let small_event = EventU16::new(Severity::Info, 3, 0);
|
||||||
|
//! ```
|
||||||
|
use core::fmt::Debug;
|
||||||
|
use core::hash::Hash;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use delegate::delegate;
|
||||||
|
use spacepackets::ByteConversionError;
|
||||||
|
use spacepackets::ecss::EcssEnumeration;
|
||||||
|
use spacepackets::util::{ToBeBytes, UnsignedEnum};
|
||||||
|
|
||||||
|
/// Using a type definition allows to change this to u64 in the future more easily
|
||||||
|
pub type LargestEventRaw = u32;
|
||||||
|
/// Using a type definition allows to change this to u32 in the future more easily
|
||||||
|
pub type LargestGroupIdRaw = u16;
|
||||||
|
|
||||||
|
pub const MAX_GROUP_ID_U32_EVENT: u16 = 2_u16.pow(14) - 1;
|
||||||
|
pub const MAX_GROUP_ID_U16_EVENT: u16 = 2_u16.pow(6) - 1;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Severity {
|
||||||
|
Info = 0,
|
||||||
|
Low = 1,
|
||||||
|
Medium = 2,
|
||||||
|
High = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone {
|
||||||
|
const SEVERITY: Severity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type level support struct
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct SeverityInfo {}
|
||||||
|
impl HasSeverity for SeverityInfo {
|
||||||
|
const SEVERITY: Severity = Severity::Info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type level support struct
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct SeverityLow {}
|
||||||
|
impl HasSeverity for SeverityLow {
|
||||||
|
const SEVERITY: Severity = Severity::Low;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type level support struct
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct SeverityMedium {}
|
||||||
|
impl HasSeverity for SeverityMedium {
|
||||||
|
const SEVERITY: Severity = Severity::Medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type level support struct
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct SeverityHigh {}
|
||||||
|
impl HasSeverity for SeverityHigh {
|
||||||
|
const SEVERITY: Severity = Severity::High;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GenericEvent: EcssEnumeration + Copy + Clone {
|
||||||
|
type Raw;
|
||||||
|
type GroupId;
|
||||||
|
type UniqueId;
|
||||||
|
|
||||||
|
fn raw(&self) -> Self::Raw;
|
||||||
|
fn severity(&self) -> Severity;
|
||||||
|
fn group_id(&self) -> Self::GroupId;
|
||||||
|
fn unique_id(&self) -> Self::UniqueId;
|
||||||
|
|
||||||
|
fn raw_as_largest_type(&self) -> LargestEventRaw;
|
||||||
|
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for Severity {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
x if x == Severity::Info as u8 => Ok(Severity::Info),
|
||||||
|
x if x == Severity::Low as u8 => Ok(Severity::Low),
|
||||||
|
x if x == Severity::Medium as u8 => Ok(Severity::Medium),
|
||||||
|
x if x == Severity::High as u8 => Ok(Severity::High),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
struct EventBase<Raw, GroupId, UniqueId> {
|
||||||
|
severity: Severity,
|
||||||
|
group_id: GroupId,
|
||||||
|
unique_id: UniqueId,
|
||||||
|
phantom: PhantomData<Raw>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Raw: ToBeBytes, GroupId, UniqueId> EventBase<Raw, GroupId, UniqueId> {
|
||||||
|
fn write_to_bytes(
|
||||||
|
&self,
|
||||||
|
raw: Raw,
|
||||||
|
buf: &mut [u8],
|
||||||
|
width: usize,
|
||||||
|
) -> Result<usize, ByteConversionError> {
|
||||||
|
if buf.len() < width {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: width,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
buf.copy_from_slice(raw.to_be_bytes().as_ref());
|
||||||
|
Ok(raw.written_len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventBase<u32, u16, u16> {
|
||||||
|
#[inline]
|
||||||
|
fn raw(&self) -> u32 {
|
||||||
|
((self.severity as u32) << 30) | ((self.group_id as u32) << 16) | self.unique_id as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventBase<u16, u8, u8> {
|
||||||
|
#[inline]
|
||||||
|
fn raw(&self) -> u16 {
|
||||||
|
((self.severity as u16) << 14) | ((self.group_id as u16) << 8) | self.unique_id as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RAW, GID, UID> EventBase<RAW, GID, UID> {
|
||||||
|
#[inline]
|
||||||
|
pub fn severity(&self) -> Severity {
|
||||||
|
self.severity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RAW, GID> EventBase<RAW, GID, u16> {
|
||||||
|
#[inline]
|
||||||
|
pub fn unique_id(&self) -> u16 {
|
||||||
|
self.unique_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RAW, GID> EventBase<RAW, GID, u8> {
|
||||||
|
#[inline]
|
||||||
|
pub fn unique_id(&self) -> u8 {
|
||||||
|
self.unique_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RAW, UID> EventBase<RAW, u16, UID> {
|
||||||
|
#[inline]
|
||||||
|
pub fn group_id(&self) -> u16 {
|
||||||
|
self.group_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RAW, UID> EventBase<RAW, u8, UID> {
|
||||||
|
#[inline]
|
||||||
|
pub fn group_id(&self) -> u8 {
|
||||||
|
self.group_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! event_provider_impl {
|
||||||
|
() => {
|
||||||
|
#[inline]
|
||||||
|
fn raw(&self) -> Self::Raw {
|
||||||
|
self.base.raw()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the severity of an event. Returns None if that severity bit field of the raw event
|
||||||
|
/// ID is invalid
|
||||||
|
#[inline]
|
||||||
|
fn severity(&self) -> Severity {
|
||||||
|
self.base.severity()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn group_id(&self) -> Self::GroupId {
|
||||||
|
self.base.group_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unique_id(&self) -> Self::UniqueId {
|
||||||
|
self.base.unique_id()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_event_provider {
|
||||||
|
($BaseIdent: ident, $TypedIdent: ident, $raw: ty, $gid: ty, $uid: ty) => {
|
||||||
|
impl GenericEvent for $BaseIdent {
|
||||||
|
type Raw = $raw;
|
||||||
|
type GroupId = $gid;
|
||||||
|
type UniqueId = $uid;
|
||||||
|
|
||||||
|
event_provider_impl!();
|
||||||
|
|
||||||
|
fn raw_as_largest_type(&self) -> LargestEventRaw {
|
||||||
|
self.raw().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw {
|
||||||
|
self.group_id().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SEVERITY: HasSeverity> GenericEvent for $TypedIdent<SEVERITY> {
|
||||||
|
type Raw = $raw;
|
||||||
|
type GroupId = $gid;
|
||||||
|
type UniqueId = $uid;
|
||||||
|
|
||||||
|
delegate!(to self.event {
|
||||||
|
fn raw(&self) -> Self::Raw;
|
||||||
|
fn severity(&self) -> Severity;
|
||||||
|
fn group_id(&self) -> Self::GroupId;
|
||||||
|
fn unique_id(&self) -> Self::UniqueId;
|
||||||
|
fn raw_as_largest_type(&self) -> LargestEventRaw;
|
||||||
|
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! try_from_impls {
|
||||||
|
($SevIdent: ident, $severity: path, $raw: ty, $TypedSevIdent: ident) => {
|
||||||
|
impl TryFrom<$raw> for $TypedSevIdent<$SevIdent> {
|
||||||
|
type Error = Severity;
|
||||||
|
|
||||||
|
fn try_from(raw: $raw) -> Result<Self, Self::Error> {
|
||||||
|
Self::try_from_generic($severity, raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! const_from_fn {
|
||||||
|
($from_fn_name: ident, $TypedIdent: ident, $SevIdent: ident) => {
|
||||||
|
pub const fn $from_fn_name(event: $TypedIdent<$SevIdent>) -> Self {
|
||||||
|
Self {
|
||||||
|
base: event.event.base,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
pub struct EventU32 {
|
||||||
|
base: EventBase<u32, u16, u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EventU32TypedSev<SEVERITY> {
|
||||||
|
event: EventU32,
|
||||||
|
phantom: PhantomData<SEVERITY>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SEVERITY: HasSeverity> From<EventU32TypedSev<SEVERITY>> for EventU32 {
|
||||||
|
fn from(e: EventU32TypedSev<SEVERITY>) -> Self {
|
||||||
|
Self { base: e.event.base }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> AsRef<EventU32> for EventU32TypedSev<Severity> {
|
||||||
|
fn as_ref(&self) -> &EventU32 {
|
||||||
|
&self.event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> AsMut<EventU32> for EventU32TypedSev<Severity> {
|
||||||
|
fn as_mut(&mut self) -> &mut EventU32 {
|
||||||
|
&mut self.event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_event_provider!(EventU32, EventU32TypedSev, u32, u16, u16);
|
||||||
|
|
||||||
|
impl EventU32 {
|
||||||
|
/// Generate an event. The raw representation of an event has 32 bits.
|
||||||
|
/// If the passed group ID is invalid (too large), None wil be returned
|
||||||
|
///
|
||||||
|
/// # Parameter
|
||||||
|
///
|
||||||
|
/// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will
|
||||||
|
/// be stored inside the uppermost 2 bits of the raw event ID
|
||||||
|
/// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the
|
||||||
|
/// next 14 bits after the severity. Therefore, the size is limited by dec 16383 hex 0x3FFF.
|
||||||
|
/// * `unique_id`: Each event has a unique 16 bit ID occupying the last 16 bits of the
|
||||||
|
/// raw event ID
|
||||||
|
pub fn new_checked(
|
||||||
|
severity: Severity,
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Option<Self> {
|
||||||
|
if group_id > MAX_GROUP_ID_U32_EVENT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self {
|
||||||
|
base: EventBase {
|
||||||
|
severity,
|
||||||
|
group_id,
|
||||||
|
unique_id,
|
||||||
|
phantom: PhantomData,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This constructor will panic if the passed group is is larger than [MAX_GROUP_ID_U32_EVENT].
|
||||||
|
pub const fn new(
|
||||||
|
severity: Severity,
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Self {
|
||||||
|
if group_id > MAX_GROUP_ID_U32_EVENT {
|
||||||
|
panic!("Group ID too large");
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
base: EventBase {
|
||||||
|
severity,
|
||||||
|
group_id,
|
||||||
|
unique_id,
|
||||||
|
phantom: PhantomData,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_be_bytes(bytes: [u8; 4]) -> Self {
|
||||||
|
Self::from(u32::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
const_from_fn!(const_from_info, EventU32TypedSev, SeverityInfo);
|
||||||
|
const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow);
|
||||||
|
const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium);
|
||||||
|
const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for EventU32 {
|
||||||
|
fn from(raw: u32) -> Self {
|
||||||
|
// Severity conversion from u8 should never fail
|
||||||
|
let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
|
||||||
|
let group_id = ((raw >> 16) & 0x3FFF) as u16;
|
||||||
|
let unique_id = (raw & 0xFFFF) as u16;
|
||||||
|
// Sanitized input, should never fail
|
||||||
|
Self::new(severity, group_id, unique_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnsignedEnum for EventU32 {
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
core::mem::size_of::<u32>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||||
|
self.base.write_to_bytes(self.raw(), buf, self.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_raw(&self) -> u64 {
|
||||||
|
self.raw().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EcssEnumeration for EventU32 {
|
||||||
|
fn pfc(&self) -> u8 {
|
||||||
|
u32::BITS as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SEVERITY: HasSeverity> EventU32TypedSev<SEVERITY> {
|
||||||
|
/// This is similar to [EventU32::new] but the severity is a type generic, which allows to
|
||||||
|
/// have distinct types for events with different severities
|
||||||
|
pub fn new_checked(
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Option<Self> {
|
||||||
|
let event = EventU32::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?;
|
||||||
|
Some(Self {
|
||||||
|
event,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U32_EVENT].
|
||||||
|
pub const fn new(
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Self {
|
||||||
|
let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id);
|
||||||
|
Self {
|
||||||
|
event,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_generic(expected: Severity, raw: u32) -> Result<Self, Severity> {
|
||||||
|
let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
|
||||||
|
if severity != expected {
|
||||||
|
return Err(severity);
|
||||||
|
}
|
||||||
|
Ok(Self::new(
|
||||||
|
((raw >> 16) & 0x3FFF) as u16,
|
||||||
|
(raw & 0xFFFF) as u16,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try_from_impls!(SeverityInfo, Severity::Info, u32, EventU32TypedSev);
|
||||||
|
try_from_impls!(SeverityLow, Severity::Low, u32, EventU32TypedSev);
|
||||||
|
try_from_impls!(SeverityMedium, Severity::Medium, u32, EventU32TypedSev);
|
||||||
|
try_from_impls!(SeverityHigh, Severity::High, u32, EventU32TypedSev);
|
||||||
|
|
||||||
|
//noinspection RsTraitImplementation
|
||||||
|
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> {
|
||||||
|
delegate!(to self.event {
|
||||||
|
fn size(&self) -> usize;
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
|
||||||
|
fn value_raw(&self) -> u64;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection RsTraitImplementation
|
||||||
|
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU32TypedSev<SEVERITY> {
|
||||||
|
delegate!(to self.event {
|
||||||
|
fn pfc(&self) -> u8;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct EventU16 {
|
||||||
|
base: EventBase<u16, u8, u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EventU16TypedSev<SEVERITY> {
|
||||||
|
event: EventU16,
|
||||||
|
phantom: PhantomData<SEVERITY>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> AsRef<EventU16> for EventU16TypedSev<Severity> {
|
||||||
|
fn as_ref(&self) -> &EventU16 {
|
||||||
|
&self.event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> AsMut<EventU16> for EventU16TypedSev<Severity> {
|
||||||
|
fn as_mut(&mut self) -> &mut EventU16 {
|
||||||
|
&mut self.event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventU16 {
|
||||||
|
/// Generate a small event. The raw representation of a small event has 16 bits.
|
||||||
|
/// If the passed group ID is invalid (too large), [None] wil be returned
|
||||||
|
///
|
||||||
|
/// # Parameter
|
||||||
|
///
|
||||||
|
/// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will
|
||||||
|
/// be stored inside the uppermost 2 bits of the raw event ID
|
||||||
|
/// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the
|
||||||
|
/// next 6 bits after the severity. Therefore, the size is limited by dec 63 hex 0x3F.
|
||||||
|
/// * `unique_id`: Each event has a unique 8 bit ID occupying the last 8 bits of the
|
||||||
|
/// raw event ID
|
||||||
|
pub fn new_checked(
|
||||||
|
severity: Severity,
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Option<Self> {
|
||||||
|
if group_id > (2u8.pow(6) - 1) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self {
|
||||||
|
base: EventBase {
|
||||||
|
severity,
|
||||||
|
group_id,
|
||||||
|
unique_id,
|
||||||
|
phantom: Default::default(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT].
|
||||||
|
pub const fn new(
|
||||||
|
severity: Severity,
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Self {
|
||||||
|
if group_id > (2u8.pow(6) - 1) {
|
||||||
|
panic!("Group ID too large");
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
base: EventBase {
|
||||||
|
severity,
|
||||||
|
group_id,
|
||||||
|
unique_id,
|
||||||
|
phantom: PhantomData,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
|
||||||
|
Self::from(u16::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
const_from_fn!(const_from_info, EventU16TypedSev, SeverityInfo);
|
||||||
|
const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow);
|
||||||
|
const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium);
|
||||||
|
const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for EventU16 {
|
||||||
|
fn from(raw: <Self as GenericEvent>::Raw) -> Self {
|
||||||
|
let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
|
||||||
|
let group_id = ((raw >> 8) & 0x3F) as u8;
|
||||||
|
let unique_id = (raw & 0xFF) as u8;
|
||||||
|
// Sanitized input, new call should never fail
|
||||||
|
Self::new(severity, group_id, unique_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnsignedEnum for EventU16 {
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
core::mem::size_of::<u16>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||||
|
self.base.write_to_bytes(self.raw(), buf, self.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_raw(&self) -> u64 {
|
||||||
|
self.raw().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl EcssEnumeration for EventU16 {
|
||||||
|
#[inline]
|
||||||
|
fn pfc(&self) -> u8 {
|
||||||
|
u16::BITS as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SEVERITY: HasSeverity> EventU16TypedSev<SEVERITY> {
|
||||||
|
/// This is similar to [EventU16::new] but the severity is a type generic, which allows to
|
||||||
|
/// have distinct types for events with different severities
|
||||||
|
pub fn new_checked(
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Option<Self> {
|
||||||
|
let event = EventU16::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?;
|
||||||
|
Some(Self {
|
||||||
|
event,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT].
|
||||||
|
pub const fn new(
|
||||||
|
group_id: <Self as GenericEvent>::GroupId,
|
||||||
|
unique_id: <Self as GenericEvent>::UniqueId,
|
||||||
|
) -> Self {
|
||||||
|
let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id);
|
||||||
|
Self {
|
||||||
|
event,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_generic(expected: Severity, raw: u16) -> Result<Self, Severity> {
|
||||||
|
let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
|
||||||
|
if severity != expected {
|
||||||
|
return Err(severity);
|
||||||
|
}
|
||||||
|
Ok(Self::new(((raw >> 8) & 0x3F) as u8, (raw & 0xFF) as u8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8);
|
||||||
|
|
||||||
|
//noinspection RsTraitImplementation
|
||||||
|
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> {
|
||||||
|
delegate!(to self.event {
|
||||||
|
fn size(&self) -> usize;
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
|
||||||
|
fn value_raw(&self) -> u64;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection RsTraitImplementation
|
||||||
|
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU16TypedSev<SEVERITY> {
|
||||||
|
delegate!(to self.event {
|
||||||
|
fn pfc(&self) -> u8;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try_from_impls!(SeverityInfo, Severity::Info, u16, EventU16TypedSev);
|
||||||
|
try_from_impls!(SeverityLow, Severity::Low, u16, EventU16TypedSev);
|
||||||
|
try_from_impls!(SeverityMedium, Severity::Medium, u16, EventU16TypedSev);
|
||||||
|
try_from_impls!(SeverityHigh, Severity::High, u16, EventU16TypedSev);
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> PartialEq<EventU32> for EventU32TypedSev<Severity> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &EventU32) -> bool {
|
||||||
|
self.raw() == other.raw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> PartialEq<EventU32TypedSev<Severity>> for EventU32 {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &EventU32TypedSev<Severity>) -> bool {
|
||||||
|
self.raw() == other.raw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> PartialEq<EventU16> for EventU16TypedSev<Severity> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &EventU16) -> bool {
|
||||||
|
self.raw() == other.raw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Severity: HasSeverity> PartialEq<EventU16TypedSev<Severity>> for EventU16 {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &EventU16TypedSev<Severity>) -> bool {
|
||||||
|
self.raw() == other.raw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::EventU32TypedSev;
|
||||||
|
use super::*;
|
||||||
|
use spacepackets::ByteConversionError;
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
fn assert_size<T>(_: T, val: usize) {
|
||||||
|
assert_eq!(size_of::<T>(), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(0, 0);
|
||||||
|
const INFO_EVENT_SMALL: EventU16TypedSev<SeverityInfo> = EventU16TypedSev::new(0, 0);
|
||||||
|
const HIGH_SEV_EVENT: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(0x3FFF, 0xFFFF);
|
||||||
|
const HIGH_SEV_EVENT_SMALL: EventU16TypedSev<SeverityHigh> = EventU16TypedSev::new(0x3F, 0xff);
|
||||||
|
|
||||||
|
/// This working is a test in itself.
|
||||||
|
const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_normal_from_raw_conversion() {
|
||||||
|
let conv_from_raw = EventU32TypedSev::<SeverityInfo>::try_from(INFO_EVENT.raw())
|
||||||
|
.expect("Creating typed EventU32 failed");
|
||||||
|
assert_eq!(conv_from_raw, INFO_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_small_from_raw_conversion() {
|
||||||
|
let conv_from_raw = EventU16TypedSev::<SeverityInfo>::try_from(INFO_EVENT_SMALL.raw())
|
||||||
|
.expect("Creating typed EventU16 failed");
|
||||||
|
assert_eq!(conv_from_raw, INFO_EVENT_SMALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_normal_size() {
|
||||||
|
assert_size(INFO_EVENT.raw(), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_small_size() {
|
||||||
|
assert_size(INFO_EVENT_SMALL.raw(), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_normal_event_getters() {
|
||||||
|
assert_eq!(INFO_EVENT.severity(), Severity::Info);
|
||||||
|
assert_eq!(INFO_EVENT.unique_id(), 0);
|
||||||
|
assert_eq!(INFO_EVENT.group_id(), 0);
|
||||||
|
let raw_event = INFO_EVENT.raw();
|
||||||
|
assert_eq!(raw_event, 0x00000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_small_event_getters() {
|
||||||
|
assert_eq!(INFO_EVENT_SMALL.severity(), Severity::Info);
|
||||||
|
assert_eq!(INFO_EVENT_SMALL.unique_id(), 0);
|
||||||
|
assert_eq!(INFO_EVENT_SMALL.group_id(), 0);
|
||||||
|
let raw_event = INFO_EVENT_SMALL.raw();
|
||||||
|
assert_eq!(raw_event, 0x00000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_ones_event_regular() {
|
||||||
|
assert_eq!(HIGH_SEV_EVENT.severity(), Severity::High);
|
||||||
|
assert_eq!(HIGH_SEV_EVENT.group_id(), 0x3FFF);
|
||||||
|
assert_eq!(HIGH_SEV_EVENT.unique_id(), 0xFFFF);
|
||||||
|
let raw_event = HIGH_SEV_EVENT.raw();
|
||||||
|
assert_eq!(raw_event, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_ones_event_small() {
|
||||||
|
assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::High);
|
||||||
|
assert_eq!(HIGH_SEV_EVENT_SMALL.group_id(), 0x3F);
|
||||||
|
assert_eq!(HIGH_SEV_EVENT_SMALL.unique_id(), 0xFF);
|
||||||
|
let raw_event = HIGH_SEV_EVENT_SMALL.raw();
|
||||||
|
assert_eq!(raw_event, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_group_id_normal() {
|
||||||
|
assert!(EventU32TypedSev::<SeverityMedium>::new_checked(2_u16.pow(14), 0).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_group_id_small() {
|
||||||
|
assert!(EventU16TypedSev::<SeverityMedium>::new_checked(2_u8.pow(6), 0).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regular_new() {
|
||||||
|
assert_eq!(
|
||||||
|
EventU32TypedSev::<SeverityInfo>::new_checked(0, 0)
|
||||||
|
.expect("Creating regular event failed"),
|
||||||
|
INFO_EVENT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn small_new() {
|
||||||
|
assert_eq!(
|
||||||
|
EventU16TypedSev::<SeverityInfo>::new_checked(0, 0)
|
||||||
|
.expect("Creating regular event failed"),
|
||||||
|
INFO_EVENT_SMALL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn as_largest_type() {
|
||||||
|
let event_raw = HIGH_SEV_EVENT.raw_as_largest_type();
|
||||||
|
assert_size(event_raw, 4);
|
||||||
|
assert_eq!(event_raw, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn as_largest_type_for_small_event() {
|
||||||
|
let event_raw = HIGH_SEV_EVENT_SMALL.raw_as_largest_type();
|
||||||
|
assert_size(event_raw, 4);
|
||||||
|
assert_eq!(event_raw, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn as_largest_group_id() {
|
||||||
|
let group_id = HIGH_SEV_EVENT.group_id_as_largest_type();
|
||||||
|
assert_size(group_id, 2);
|
||||||
|
assert_eq!(group_id, 0x3FFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn as_largest_group_id_small_event() {
|
||||||
|
let group_id = HIGH_SEV_EVENT_SMALL.group_id_as_largest_type();
|
||||||
|
assert_size(group_id, 2);
|
||||||
|
assert_eq!(group_id, 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_to_buf() {
|
||||||
|
let mut buf: [u8; 4] = [0; 4];
|
||||||
|
assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok());
|
||||||
|
let val_from_raw = u32::from_be_bytes(buf);
|
||||||
|
assert_eq!(val_from_raw, 0xFFFFFFFF);
|
||||||
|
let event_read_back = EventU32::from_be_bytes(buf);
|
||||||
|
assert_eq!(event_read_back, HIGH_SEV_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_to_buf_small() {
|
||||||
|
let mut buf: [u8; 2] = [0; 2];
|
||||||
|
assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok());
|
||||||
|
let val_from_raw = u16::from_be_bytes(buf);
|
||||||
|
assert_eq!(val_from_raw, 0xFFFF);
|
||||||
|
let event_read_back = EventU16::from_be_bytes(buf);
|
||||||
|
assert_eq!(event_read_back, HIGH_SEV_EVENT_SMALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_to_buf_insufficient_buf() {
|
||||||
|
let mut buf: [u8; 3] = [0; 3];
|
||||||
|
let err = HIGH_SEV_EVENT.write_to_be_bytes(&mut buf);
|
||||||
|
assert!(err.is_err());
|
||||||
|
let err = err.unwrap_err();
|
||||||
|
if let ByteConversionError::ToSliceTooSmall { found, expected } = err {
|
||||||
|
assert_eq!(expected, 4);
|
||||||
|
assert_eq!(found, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_to_buf_small_insufficient_buf() {
|
||||||
|
let mut buf: [u8; 1] = [0; 1];
|
||||||
|
let err = HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf);
|
||||||
|
assert!(err.is_err());
|
||||||
|
let err = err.unwrap_err();
|
||||||
|
if let ByteConversionError::ToSliceTooSmall { found, expected } = err {
|
||||||
|
assert_eq!(expected, 2);
|
||||||
|
assert_eq!(found, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn severity_from_invalid_raw_val() {
|
||||||
|
let invalid = 0xFF;
|
||||||
|
assert!(Severity::try_from(invalid).is_err());
|
||||||
|
let invalid = Severity::High as u8 + 1;
|
||||||
|
assert!(Severity::try_from(invalid).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reduction() {
|
||||||
|
let event = EventU32TypedSev::<SeverityInfo>::new(1, 1);
|
||||||
|
let raw = event.raw();
|
||||||
|
let reduced: EventU32 = event.into();
|
||||||
|
assert_eq!(reduced.group_id(), 1);
|
||||||
|
assert_eq!(reduced.unique_id(), 1);
|
||||||
|
assert_eq!(raw, reduced.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_reducation() {
|
||||||
|
assert_eq!(INFO_REDUCED.raw(), INFO_EVENT.raw());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,33 +55,35 @@ pub fn exec_sched_single<
|
|||||||
let mut cycle_count = 0;
|
let mut cycle_count = 0;
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(String::from(executable.task_name()))
|
.name(String::from(executable.task_name()))
|
||||||
.spawn(move || loop {
|
.spawn(move || {
|
||||||
if let Some(ref mut terminator) = termination {
|
loop {
|
||||||
match terminator.try_recv() {
|
if let Some(ref mut terminator) = termination {
|
||||||
Ok(_) | Err(TryRecvError::Disconnected) => {
|
match terminator.try_recv() {
|
||||||
return Ok(OpResult::Ok);
|
Ok(_) | Err(TryRecvError::Disconnected) => {
|
||||||
}
|
return Ok(OpResult::Ok);
|
||||||
Err(TryRecvError::Empty) => (),
|
}
|
||||||
}
|
Err(TryRecvError::Empty) => (),
|
||||||
}
|
|
||||||
match executable.exec_type() {
|
|
||||||
ExecutionType::OneShot => {
|
|
||||||
executable.periodic_op(op_code)?;
|
|
||||||
return Ok(OpResult::Ok);
|
|
||||||
}
|
|
||||||
ExecutionType::Infinite => {
|
|
||||||
executable.periodic_op(op_code)?;
|
|
||||||
}
|
|
||||||
ExecutionType::Cycles(cycles) => {
|
|
||||||
executable.periodic_op(op_code)?;
|
|
||||||
cycle_count += 1;
|
|
||||||
if cycle_count == cycles {
|
|
||||||
return Ok(OpResult::Ok);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
match executable.exec_type() {
|
||||||
if let Some(freq) = task_freq {
|
ExecutionType::OneShot => {
|
||||||
thread::sleep(freq);
|
executable.periodic_op(op_code)?;
|
||||||
|
return Ok(OpResult::Ok);
|
||||||
|
}
|
||||||
|
ExecutionType::Infinite => {
|
||||||
|
executable.periodic_op(op_code)?;
|
||||||
|
}
|
||||||
|
ExecutionType::Cycles(cycles) => {
|
||||||
|
executable.periodic_op(op_code)?;
|
||||||
|
cycle_count += 1;
|
||||||
|
if cycle_count == cycles {
|
||||||
|
return Ok(OpResult::Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(freq) = task_freq {
|
||||||
|
thread::sleep(freq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -110,51 +112,53 @@ pub fn exec_sched_multi<
|
|||||||
|
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(String::from(task_name))
|
.name(String::from(task_name))
|
||||||
.spawn(move || loop {
|
.spawn(move || {
|
||||||
if let Some(ref mut terminator) = termination {
|
loop {
|
||||||
match terminator.try_recv() {
|
if let Some(ref mut terminator) = termination {
|
||||||
Ok(_) | Err(TryRecvError::Disconnected) => {
|
match terminator.try_recv() {
|
||||||
removal_flags.iter_mut().for_each(|x| *x = true);
|
Ok(_) | Err(TryRecvError::Disconnected) => {
|
||||||
|
removal_flags.iter_mut().for_each(|x| *x = true);
|
||||||
|
}
|
||||||
|
Err(TryRecvError::Empty) => (),
|
||||||
}
|
}
|
||||||
Err(TryRecvError::Empty) => (),
|
|
||||||
}
|
}
|
||||||
}
|
for (idx, executable) in executable_vec.iter_mut().enumerate() {
|
||||||
for (idx, executable) in executable_vec.iter_mut().enumerate() {
|
match executable.exec_type() {
|
||||||
match executable.exec_type() {
|
ExecutionType::OneShot => {
|
||||||
ExecutionType::OneShot => {
|
executable.periodic_op(op_code)?;
|
||||||
executable.periodic_op(op_code)?;
|
|
||||||
removal_flags[idx] = true;
|
|
||||||
}
|
|
||||||
ExecutionType::Infinite => {
|
|
||||||
executable.periodic_op(op_code)?;
|
|
||||||
}
|
|
||||||
ExecutionType::Cycles(cycles) => {
|
|
||||||
executable.periodic_op(op_code)?;
|
|
||||||
cycle_counts[idx] += 1;
|
|
||||||
if cycle_counts[idx] == cycles {
|
|
||||||
removal_flags[idx] = true;
|
removal_flags[idx] = true;
|
||||||
}
|
}
|
||||||
|
ExecutionType::Infinite => {
|
||||||
|
executable.periodic_op(op_code)?;
|
||||||
|
}
|
||||||
|
ExecutionType::Cycles(cycles) => {
|
||||||
|
executable.periodic_op(op_code)?;
|
||||||
|
cycle_counts[idx] += 1;
|
||||||
|
if cycle_counts[idx] == cycles {
|
||||||
|
removal_flags[idx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut removal_iter = removal_flags.iter();
|
||||||
|
executable_vec.retain(|_| !*removal_iter.next().unwrap());
|
||||||
|
removal_iter = removal_flags.iter();
|
||||||
|
cycle_counts.retain(|_| !*removal_iter.next().unwrap());
|
||||||
|
removal_flags.retain(|&i| !i);
|
||||||
|
if executable_vec.is_empty() {
|
||||||
|
return Ok(OpResult::Ok);
|
||||||
|
}
|
||||||
|
let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified"));
|
||||||
|
thread::sleep(freq);
|
||||||
}
|
}
|
||||||
let mut removal_iter = removal_flags.iter();
|
|
||||||
executable_vec.retain(|_| !*removal_iter.next().unwrap());
|
|
||||||
removal_iter = removal_flags.iter();
|
|
||||||
cycle_counts.retain(|_| !*removal_iter.next().unwrap());
|
|
||||||
removal_flags.retain(|&i| !i);
|
|
||||||
if executable_vec.is_empty() {
|
|
||||||
return Ok(OpResult::Ok);
|
|
||||||
}
|
|
||||||
let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified"));
|
|
||||||
thread::sleep(freq);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
exec_sched_multi, exec_sched_single, Executable, ExecutableWithType, ExecutionType,
|
Executable, ExecutableWithType, ExecutionType, OpResult, exec_sched_multi,
|
||||||
OpResult,
|
exec_sched_single,
|
||||||
};
|
};
|
||||||
use bus::Bus;
|
use bus::Bus;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
pub mod tcp_server;
|
pub mod tcp_server;
|
||||||
pub mod udp_server;
|
pub mod udp_server;
|
||||||
|
|
||||||
mod tcp_cobs_server;
|
pub mod tcp_cobs_server;
|
||||||
mod tcp_spacepackets_server;
|
pub mod tcp_spacepackets_server;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
|
use cobs::CobsDecoderOwned;
|
||||||
|
use cobs::DecodeError;
|
||||||
use cobs::encode;
|
use cobs::encode;
|
||||||
use core::sync::atomic::AtomicBool;
|
use core::sync::atomic::AtomicBool;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
@@ -9,40 +11,58 @@ use std::io::Write;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use crate::encoding::parse_buffer_for_cobs_encoded_packets;
|
use crate::queue::GenericSendError;
|
||||||
use crate::tmtc::PacketSenderRaw;
|
use crate::tmtc::PacketHandler;
|
||||||
use crate::tmtc::PacketSource;
|
use crate::tmtc::PacketSource;
|
||||||
|
|
||||||
use crate::hal::std::tcp_server::{
|
|
||||||
ConnectionResult, ServerConfig, TcpTcParser, TcpTmSender, TcpTmtcError, TcpTmtcGenericServer,
|
|
||||||
};
|
|
||||||
use crate::ComponentId;
|
use crate::ComponentId;
|
||||||
|
use crate::hal::std::tcp_server::{
|
||||||
|
ConnectionResult, ServerConfig, TcpTcParser, TcpTmSender, TcpTmtcGenericServer,
|
||||||
|
};
|
||||||
|
|
||||||
use super::tcp_server::HandledConnectionHandler;
|
use super::tcp_server::HandledConnectionHandler;
|
||||||
use super::tcp_server::HandledConnectionInfo;
|
use super::tcp_server::HandledConnectionInfo;
|
||||||
|
|
||||||
/// Concrete [TcpTcParser] implementation for the [TcpTmtcInCobsServer].
|
/// Concrete [TcpTcParser] implementation for the [TcpTmtcInCobsServer].
|
||||||
#[derive(Default)]
|
pub struct CobsTcParser<PacketHandlerInstance: PacketHandler> {
|
||||||
pub struct CobsTcParser {}
|
sender_id: ComponentId,
|
||||||
|
owned_decoder: CobsDecoderOwned,
|
||||||
|
packet_handler: PacketHandlerInstance,
|
||||||
|
last_decode_error: Option<DecodeError>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<TmError, TcError: 'static> TcpTcParser<TmError, TcError> for CobsTcParser {
|
impl<PacketHandlerInstance: PacketHandler> CobsTcParser<PacketHandlerInstance> {
|
||||||
fn handle_tc_parsing(
|
pub fn new(
|
||||||
&mut self,
|
|
||||||
tc_buffer: &mut [u8],
|
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
tc_sender: &(impl PacketSenderRaw<Error = TcError> + ?Sized),
|
decoder_buf_size: usize,
|
||||||
conn_result: &mut HandledConnectionInfo,
|
packet_handler: PacketHandlerInstance,
|
||||||
current_write_idx: usize,
|
) -> Self {
|
||||||
next_write_idx: &mut usize,
|
Self {
|
||||||
) -> Result<(), TcpTmtcError<TmError, TcError>> {
|
|
||||||
conn_result.num_received_tcs += parse_buffer_for_cobs_encoded_packets(
|
|
||||||
&mut tc_buffer[..current_write_idx],
|
|
||||||
sender_id,
|
sender_id,
|
||||||
tc_sender,
|
owned_decoder: CobsDecoderOwned::new(decoder_buf_size),
|
||||||
next_write_idx,
|
packet_handler,
|
||||||
)
|
last_decode_error: None,
|
||||||
.map_err(|e| TcpTmtcError::TcError(e))?;
|
}
|
||||||
Ok(())
|
}
|
||||||
|
}
|
||||||
|
impl<PacketHandlerInstance: PacketHandler> TcpTcParser for CobsTcParser<PacketHandlerInstance> {
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.owned_decoder.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, data: &[u8], conn_result: &mut HandledConnectionInfo) {
|
||||||
|
for byte in data {
|
||||||
|
match self.owned_decoder.feed(*byte) {
|
||||||
|
Ok(Some(packet_len)) => {
|
||||||
|
self.packet_handler
|
||||||
|
.handle_packet(self.sender_id, &self.owned_decoder.dest()[..packet_len])
|
||||||
|
.ok();
|
||||||
|
conn_result.num_received_tcs += 1;
|
||||||
|
}
|
||||||
|
Ok(None) => (),
|
||||||
|
Err(e) => self.last_decode_error = Some(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,22 +81,18 @@ impl CobsTmSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TmError, TcError> TcpTmSender<TmError, TcError> for CobsTmSender {
|
impl TcpTmSender for CobsTmSender {
|
||||||
fn handle_tm_sending(
|
fn handle_tm_sending(
|
||||||
&mut self,
|
&mut self,
|
||||||
tm_buffer: &mut [u8],
|
tm_buffer: &mut [u8],
|
||||||
tm_source: &mut (impl PacketSource<Error = TmError> + ?Sized),
|
tm_source: &mut (impl PacketSource<Error = ()> + ?Sized),
|
||||||
conn_result: &mut HandledConnectionInfo,
|
conn_result: &mut HandledConnectionInfo,
|
||||||
stream: &mut TcpStream,
|
stream: &mut TcpStream,
|
||||||
) -> Result<bool, TcpTmtcError<TmError, TcError>> {
|
) -> Result<bool, std::io::Error> {
|
||||||
let mut tm_was_sent = false;
|
let mut tm_was_sent = false;
|
||||||
loop {
|
// Write TM until TM source is exhausted or there is an unexpected error. For now, there
|
||||||
// Write TM until TM source is exhausted. For now, there is no limit for the amount
|
// is no limit for the amount of TM written this way.
|
||||||
// of TM written this way.
|
while let Ok(read_tm_len) = tm_source.retrieve_packet(tm_buffer) {
|
||||||
let read_tm_len = tm_source
|
|
||||||
.retrieve_packet(tm_buffer)
|
|
||||||
.map_err(|e| TcpTmtcError::TmError(e))?;
|
|
||||||
|
|
||||||
if read_tm_len == 0 {
|
if read_tm_len == 0 {
|
||||||
return Ok(tm_was_sent);
|
return Ok(tm_was_sent);
|
||||||
}
|
}
|
||||||
@@ -95,6 +111,7 @@ impl<TmError, TcError> TcpTmSender<TmError, TcError> for CobsTmSender {
|
|||||||
current_idx += 1;
|
current_idx += 1;
|
||||||
stream.write_all(&self.tm_encoding_buffer[..current_idx])?;
|
stream.write_all(&self.tm_encoding_buffer[..current_idx])?;
|
||||||
}
|
}
|
||||||
|
Ok(tm_was_sent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +125,8 @@ impl<TmError, TcError> TcpTmSender<TmError, TcError> for CobsTmSender {
|
|||||||
///
|
///
|
||||||
/// Using a framing protocol like COBS imposes minimal restrictions on the type of TMTC data
|
/// Using a framing protocol like COBS imposes minimal restrictions on the type of TMTC data
|
||||||
/// exchanged while also allowing packets with flexible size and a reliable way to reconstruct full
|
/// exchanged while also allowing packets with flexible size and a reliable way to reconstruct full
|
||||||
/// packets even from a data stream which is split up. The server wil use the
|
/// packets even from a data stream which is split up. The server wil use the streaming
|
||||||
/// [parse_buffer_for_cobs_encoded_packets] function to parse for packets and pass them to a
|
/// [CobsDecoderOwned] decoder to parse for packets and pass them to a
|
||||||
/// generic TC receiver. The user can use [crate::encoding::encode_packet_with_cobs] to encode
|
/// generic TC receiver. The user can use [crate::encoding::encode_packet_with_cobs] to encode
|
||||||
/// telecommands sent to the server.
|
/// telecommands sent to the server.
|
||||||
///
|
///
|
||||||
@@ -118,30 +135,19 @@ impl<TmError, TcError> TcpTmSender<TmError, TcError> for CobsTmSender {
|
|||||||
/// The [TCP integration tests](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/tcp_servers.rs)
|
/// The [TCP integration tests](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/tcp_servers.rs)
|
||||||
/// test also serves as the example application for this module.
|
/// test also serves as the example application for this module.
|
||||||
pub struct TcpTmtcInCobsServer<
|
pub struct TcpTmtcInCobsServer<
|
||||||
TmSource: PacketSource<Error = TmError>,
|
TmSource: PacketSource<Error = ()>,
|
||||||
TcSender: PacketSenderRaw<Error = SendError>,
|
TcHandler: PacketHandler<Error = GenericSendError>,
|
||||||
HandledConnection: HandledConnectionHandler,
|
HandledConnection: HandledConnectionHandler,
|
||||||
TmError,
|
|
||||||
SendError: 'static,
|
|
||||||
> {
|
> {
|
||||||
pub generic_server: TcpTmtcGenericServer<
|
pub generic_server:
|
||||||
TmSource,
|
TcpTmtcGenericServer<TmSource, CobsTmSender, CobsTcParser<TcHandler>, HandledConnection>,
|
||||||
TcSender,
|
|
||||||
CobsTmSender,
|
|
||||||
CobsTcParser,
|
|
||||||
HandledConnection,
|
|
||||||
TmError,
|
|
||||||
SendError,
|
|
||||||
>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
TmSource: PacketSource<Error = TmError>,
|
TmSource: PacketSource<Error = ()>,
|
||||||
TcReceiver: PacketSenderRaw<Error = TcError>,
|
TcHandler: PacketHandler<Error = GenericSendError>,
|
||||||
HandledConnection: HandledConnectionHandler,
|
HandledConnection: HandledConnectionHandler,
|
||||||
TmError: 'static,
|
> TcpTmtcInCobsServer<TmSource, TcHandler, HandledConnection>
|
||||||
TcError: 'static,
|
|
||||||
> TcpTmtcInCobsServer<TmSource, TcReceiver, HandledConnection, TmError, TcError>
|
|
||||||
{
|
{
|
||||||
/// Create a new TCP TMTC server which exchanges TMTC packets encoded with
|
/// Create a new TCP TMTC server which exchanges TMTC packets encoded with
|
||||||
/// [COBS protocol](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing).
|
/// [COBS protocol](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing).
|
||||||
@@ -156,17 +162,16 @@ impl<
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
cfg: ServerConfig,
|
cfg: ServerConfig,
|
||||||
tm_source: TmSource,
|
tm_source: TmSource,
|
||||||
tc_receiver: TcReceiver,
|
cobs_tc_parser: CobsTcParser<TcHandler>,
|
||||||
handled_connection: HandledConnection,
|
handled_connection: HandledConnection,
|
||||||
stop_signal: Option<Arc<AtomicBool>>,
|
stop_signal: Option<Arc<AtomicBool>>,
|
||||||
) -> Result<Self, std::io::Error> {
|
) -> Result<Self, std::io::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
generic_server: TcpTmtcGenericServer::new(
|
generic_server: TcpTmtcGenericServer::new(
|
||||||
cfg,
|
cfg,
|
||||||
CobsTcParser::default(),
|
cobs_tc_parser,
|
||||||
CobsTmSender::new(cfg.tm_buffer_size),
|
CobsTmSender::new(cfg.tm_buffer_size),
|
||||||
tm_source,
|
tm_source,
|
||||||
tc_receiver,
|
|
||||||
handled_connection,
|
handled_connection,
|
||||||
stop_signal,
|
stop_signal,
|
||||||
)?,
|
)?,
|
||||||
@@ -185,7 +190,7 @@ impl<
|
|||||||
pub fn handle_all_connections(
|
pub fn handle_all_connections(
|
||||||
&mut self,
|
&mut self,
|
||||||
poll_duration: Option<Duration>,
|
poll_duration: Option<Duration>,
|
||||||
) -> Result<ConnectionResult, TcpTmtcError<TmError, TcError>>;
|
) -> Result<ConnectionResult, std::io::Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,14 +211,13 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
encoding::tests::{INVERTED_PACKET, SIMPLE_PACKET},
|
encoding::tests::{INVERTED_PACKET, SIMPLE_PACKET},
|
||||||
hal::std::tcp_server::{
|
hal::std::tcp_server::{
|
||||||
tests::{ConnectionFinishedHandler, SyncTmSource},
|
|
||||||
ConnectionResult, ServerConfig,
|
ConnectionResult, ServerConfig,
|
||||||
|
tests::{ConnectionFinishedHandler, SyncTmSource},
|
||||||
},
|
},
|
||||||
queue::GenericSendError,
|
|
||||||
tmtc::PacketAsVec,
|
tmtc::PacketAsVec,
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use cobs::encode;
|
use cobs::encode;
|
||||||
@@ -243,17 +247,12 @@ mod tests {
|
|||||||
tc_sender: mpsc::Sender<PacketAsVec>,
|
tc_sender: mpsc::Sender<PacketAsVec>,
|
||||||
tm_source: SyncTmSource,
|
tm_source: SyncTmSource,
|
||||||
stop_signal: Option<Arc<AtomicBool>>,
|
stop_signal: Option<Arc<AtomicBool>>,
|
||||||
) -> TcpTmtcInCobsServer<
|
) -> TcpTmtcInCobsServer<SyncTmSource, mpsc::Sender<PacketAsVec>, ConnectionFinishedHandler>
|
||||||
SyncTmSource,
|
{
|
||||||
mpsc::Sender<PacketAsVec>,
|
|
||||||
ConnectionFinishedHandler,
|
|
||||||
(),
|
|
||||||
GenericSendError,
|
|
||||||
> {
|
|
||||||
TcpTmtcInCobsServer::new(
|
TcpTmtcInCobsServer::new(
|
||||||
ServerConfig::new(TCP_SERVER_ID, *addr, Duration::from_millis(2), 1024, 1024),
|
ServerConfig::new(TCP_SERVER_ID, *addr, Duration::from_millis(2), 1024, 1024),
|
||||||
tm_source,
|
tm_source,
|
||||||
tc_sender,
|
super::CobsTcParser::new(TCP_SERVER_ID, 1024, tc_sender),
|
||||||
ConnectionFinishedHandler::default(),
|
ConnectionFinishedHandler::default(),
|
||||||
stop_signal,
|
stop_signal,
|
||||||
)
|
)
|
||||||
@@ -435,17 +434,19 @@ mod tests {
|
|||||||
generic_tmtc_server(&auto_port_addr, tc_sender.clone(), tm_source, None);
|
generic_tmtc_server(&auto_port_addr, tc_sender.clone(), tm_source, None);
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
// Call the connection handler in separate thread, does block.
|
// Call the connection handler in separate thread, does block.
|
||||||
let thread_jh = thread::spawn(move || loop {
|
let thread_jh = thread::spawn(move || {
|
||||||
let result = tcp_server.handle_all_connections(Some(Duration::from_millis(20)));
|
loop {
|
||||||
if result.is_err() {
|
let result = tcp_server.handle_all_connections(Some(Duration::from_millis(20)));
|
||||||
panic!("handling connection failed: {:?}", result.unwrap_err());
|
if result.is_err() {
|
||||||
}
|
panic!("handling connection failed: {:?}", result.unwrap_err());
|
||||||
let result = result.unwrap();
|
}
|
||||||
if result == ConnectionResult::AcceptTimeout {
|
let result = result.unwrap();
|
||||||
break;
|
if result == ConnectionResult::AcceptTimeout {
|
||||||
}
|
break;
|
||||||
if Instant::now() - start > Duration::from_millis(100) {
|
}
|
||||||
panic!("regular stop signal handling failed");
|
if Instant::now() - start > Duration::from_millis(100) {
|
||||||
|
panic!("regular stop signal handling failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
thread_jh.join().expect("thread join failed");
|
thread_jh.join().expect("thread join failed");
|
||||||
@@ -469,20 +470,22 @@ mod tests {
|
|||||||
let stop_signal_copy = stop_signal.clone();
|
let stop_signal_copy = stop_signal.clone();
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
// Call the connection handler in separate thread, does block.
|
// Call the connection handler in separate thread, does block.
|
||||||
let thread_jh = thread::spawn(move || loop {
|
let thread_jh = thread::spawn(move || {
|
||||||
let result = tcp_server.handle_all_connections(Some(Duration::from_millis(20)));
|
loop {
|
||||||
if result.is_err() {
|
let result = tcp_server.handle_all_connections(Some(Duration::from_millis(20)));
|
||||||
panic!("handling connection failed: {:?}", result.unwrap_err());
|
if result.is_err() {
|
||||||
}
|
panic!("handling connection failed: {:?}", result.unwrap_err());
|
||||||
let result = result.unwrap();
|
}
|
||||||
if result == ConnectionResult::AcceptTimeout {
|
let result = result.unwrap();
|
||||||
panic!("unexpected accept timeout");
|
if result == ConnectionResult::AcceptTimeout {
|
||||||
}
|
panic!("unexpected accept timeout");
|
||||||
if stop_signal_copy.load(Ordering::Relaxed) {
|
}
|
||||||
break;
|
if stop_signal_copy.load(Ordering::Relaxed) {
|
||||||
}
|
break;
|
||||||
if Instant::now() - start > Duration::from_millis(100) {
|
}
|
||||||
panic!("regular stop signal handling failed");
|
if Instant::now() - start > Duration::from_millis(100) {
|
||||||
|
panic!("regular stop signal handling failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// We connect but do not do anything.
|
// We connect but do not do anything.
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ use std::io::{self, Read};
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use crate::tmtc::{PacketSenderRaw, PacketSource};
|
|
||||||
use crate::ComponentId;
|
use crate::ComponentId;
|
||||||
|
use crate::tmtc::PacketSource;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
// Re-export the TMTC in COBS server.
|
// Re-export the TMTC in COBS server.
|
||||||
@@ -73,11 +73,9 @@ impl ServerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum TcpTmtcError<TmError, TcError> {
|
pub enum TcpTmError<TmError> {
|
||||||
#[error("TM retrieval error: {0}")]
|
#[error("TM retrieval error: {0}")]
|
||||||
TmError(TmError),
|
TmError(TmError),
|
||||||
#[error("TC retrieval error: {0}")]
|
|
||||||
TcError(TcError),
|
|
||||||
#[error("io error: {0}")]
|
#[error("io error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
@@ -116,32 +114,29 @@ pub trait HandledConnectionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generic parser abstraction for an object which can parse for telecommands given a raw
|
/// Generic parser abstraction for an object which can parse for telecommands given a raw
|
||||||
/// bytestream received from a TCP socket and send them using a generic [PacketSenderRaw]
|
/// bytestream received from a TCP socket and extract packets from them. This allows different
|
||||||
/// implementation. This allows different encoding schemes for telecommands.
|
/// encoding schemes for telecommands.
|
||||||
pub trait TcpTcParser<TmError, SendError> {
|
pub trait TcpTcParser {
|
||||||
fn handle_tc_parsing(
|
/// Reset the state of the parser.
|
||||||
&mut self,
|
fn reset(&mut self);
|
||||||
tc_buffer: &mut [u8],
|
|
||||||
sender_id: ComponentId,
|
/// Pushes received data into the parser.
|
||||||
tc_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
|
fn push(&mut self, tc_data: &[u8], conn_result: &mut HandledConnectionInfo);
|
||||||
conn_result: &mut HandledConnectionInfo,
|
|
||||||
current_write_idx: usize,
|
|
||||||
next_write_idx: &mut usize,
|
|
||||||
) -> Result<(), TcpTmtcError<TmError, SendError>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic sender abstraction for an object which can pull telemetry from a given TM source
|
/// Generic sender abstraction for an object which can pull telemetry from a given TM source
|
||||||
/// using a [PacketSource] and then send them back to a client using a given [TcpStream].
|
/// using a [PacketSource] and then send them back to a client using a given [TcpStream].
|
||||||
/// The concrete implementation can also perform any encoding steps which are necessary before
|
/// The concrete implementation can also perform any encoding steps which are necessary before
|
||||||
/// sending back the data to a client.
|
/// sending back the data to a client.
|
||||||
pub trait TcpTmSender<TmError, TcError> {
|
pub trait TcpTmSender {
|
||||||
|
/// Returns whether any packets were sent back to the client.
|
||||||
fn handle_tm_sending(
|
fn handle_tm_sending(
|
||||||
&mut self,
|
&mut self,
|
||||||
tm_buffer: &mut [u8],
|
tm_buffer: &mut [u8],
|
||||||
tm_source: &mut (impl PacketSource<Error = TmError> + ?Sized),
|
tm_source: &mut (impl PacketSource<Error = ()> + ?Sized),
|
||||||
conn_result: &mut HandledConnectionInfo,
|
conn_result: &mut HandledConnectionInfo,
|
||||||
stream: &mut TcpStream,
|
stream: &mut TcpStream,
|
||||||
) -> Result<bool, TcpTmtcError<TmError, TcError>>;
|
) -> Result<bool, std::io::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TCP TMTC server implementation for exchange of generic TMTC packets in a generic way which
|
/// TCP TMTC server implementation for exchange of generic TMTC packets in a generic way which
|
||||||
@@ -151,7 +146,8 @@ pub trait TcpTmSender<TmError, TcError> {
|
|||||||
/// through the following 4 core abstractions:
|
/// through the following 4 core abstractions:
|
||||||
///
|
///
|
||||||
/// 1. [TcpTcParser] to parse for telecommands from the raw bytestream received from a client.
|
/// 1. [TcpTcParser] to parse for telecommands from the raw bytestream received from a client.
|
||||||
/// 2. Parsed telecommands will be sent using the [PacketSenderRaw] object.
|
/// 2. Parsed telecommands will be handled by the [TcpTcParser] object as well. For example, this
|
||||||
|
/// parser can contain a message queue handle to send the packets somewhere.
|
||||||
/// 3. [TcpTmSender] to send telemetry pulled from a TM source back to the client.
|
/// 3. [TcpTmSender] to send telemetry pulled from a TM source back to the client.
|
||||||
/// 4. [PacketSource] as a generic TM source used by the [TcpTmSender].
|
/// 4. [PacketSource] as a generic TM source used by the [TcpTmSender].
|
||||||
///
|
///
|
||||||
@@ -163,13 +159,10 @@ pub trait TcpTmSender<TmError, TcError> {
|
|||||||
/// 1. [TcpTmtcInCobsServer] to exchange TMTC wrapped inside the COBS framing protocol.
|
/// 1. [TcpTmtcInCobsServer] to exchange TMTC wrapped inside the COBS framing protocol.
|
||||||
/// 2. [TcpSpacepacketsServer] to exchange space packets via TCP.
|
/// 2. [TcpSpacepacketsServer] to exchange space packets via TCP.
|
||||||
pub struct TcpTmtcGenericServer<
|
pub struct TcpTmtcGenericServer<
|
||||||
TmSource: PacketSource<Error = TmError>,
|
TmSource: PacketSource<Error = ()>,
|
||||||
TcSender: PacketSenderRaw<Error = TcSendError>,
|
TmSender: TcpTmSender,
|
||||||
TmSender: TcpTmSender<TmError, TcSendError>,
|
TcParser: TcpTcParser,
|
||||||
TcParser: TcpTcParser<TmError, TcSendError>,
|
|
||||||
HandledConnection: HandledConnectionHandler,
|
HandledConnection: HandledConnectionHandler,
|
||||||
TmError,
|
|
||||||
TcSendError,
|
|
||||||
> {
|
> {
|
||||||
pub id: ComponentId,
|
pub id: ComponentId,
|
||||||
pub finished_handler: HandledConnection,
|
pub finished_handler: HandledConnection,
|
||||||
@@ -177,7 +170,6 @@ pub struct TcpTmtcGenericServer<
|
|||||||
pub(crate) inner_loop_delay: Duration,
|
pub(crate) inner_loop_delay: Duration,
|
||||||
pub(crate) tm_source: TmSource,
|
pub(crate) tm_source: TmSource,
|
||||||
pub(crate) tm_buffer: Vec<u8>,
|
pub(crate) tm_buffer: Vec<u8>,
|
||||||
pub(crate) tc_sender: TcSender,
|
|
||||||
pub(crate) tc_buffer: Vec<u8>,
|
pub(crate) tc_buffer: Vec<u8>,
|
||||||
poll: Poll,
|
poll: Poll,
|
||||||
events: Events,
|
events: Events,
|
||||||
@@ -187,23 +179,11 @@ pub struct TcpTmtcGenericServer<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
TmSource: PacketSource<Error = TmError>,
|
TmSource: PacketSource<Error = ()>,
|
||||||
TcSender: PacketSenderRaw<Error = TcSendError>,
|
TmSender: TcpTmSender,
|
||||||
TmSender: TcpTmSender<TmError, TcSendError>,
|
TcParser: TcpTcParser,
|
||||||
TcParser: TcpTcParser<TmError, TcSendError>,
|
HandledConnection: HandledConnectionHandler,
|
||||||
HandledConnection: HandledConnectionHandler,
|
> TcpTmtcGenericServer<TmSource, TmSender, TcParser, HandledConnection>
|
||||||
TmError: 'static,
|
|
||||||
TcSendError: 'static,
|
|
||||||
>
|
|
||||||
TcpTmtcGenericServer<
|
|
||||||
TmSource,
|
|
||||||
TcSender,
|
|
||||||
TmSender,
|
|
||||||
TcParser,
|
|
||||||
HandledConnection,
|
|
||||||
TmError,
|
|
||||||
TcSendError,
|
|
||||||
>
|
|
||||||
{
|
{
|
||||||
/// Create a new generic TMTC server instance.
|
/// Create a new generic TMTC server instance.
|
||||||
///
|
///
|
||||||
@@ -223,7 +203,6 @@ impl<
|
|||||||
tc_parser: TcParser,
|
tc_parser: TcParser,
|
||||||
tm_sender: TmSender,
|
tm_sender: TmSender,
|
||||||
tm_source: TmSource,
|
tm_source: TmSource,
|
||||||
tc_receiver: TcSender,
|
|
||||||
finished_handler: HandledConnection,
|
finished_handler: HandledConnection,
|
||||||
stop_signal: Option<Arc<AtomicBool>>,
|
stop_signal: Option<Arc<AtomicBool>>,
|
||||||
) -> Result<Self, std::io::Error> {
|
) -> Result<Self, std::io::Error> {
|
||||||
@@ -263,7 +242,6 @@ impl<
|
|||||||
inner_loop_delay: cfg.inner_loop_delay,
|
inner_loop_delay: cfg.inner_loop_delay,
|
||||||
tm_source,
|
tm_source,
|
||||||
tm_buffer: vec![0; cfg.tm_buffer_size],
|
tm_buffer: vec![0; cfg.tm_buffer_size],
|
||||||
tc_sender: tc_receiver,
|
|
||||||
tc_buffer: vec![0; cfg.tc_buffer_size],
|
tc_buffer: vec![0; cfg.tc_buffer_size],
|
||||||
stop_signal,
|
stop_signal,
|
||||||
finished_handler,
|
finished_handler,
|
||||||
@@ -297,7 +275,7 @@ impl<
|
|||||||
pub fn handle_all_connections(
|
pub fn handle_all_connections(
|
||||||
&mut self,
|
&mut self,
|
||||||
poll_timeout: Option<Duration>,
|
poll_timeout: Option<Duration>,
|
||||||
) -> Result<ConnectionResult, TcpTmtcError<TmError, TcSendError>> {
|
) -> Result<ConnectionResult, std::io::Error> {
|
||||||
let mut handled_connections = 0;
|
let mut handled_connections = 0;
|
||||||
// Poll Mio for events.
|
// Poll Mio for events.
|
||||||
self.poll.poll(&mut self.events, poll_timeout)?;
|
self.poll.poll(&mut self.events, poll_timeout)?;
|
||||||
@@ -327,7 +305,7 @@ impl<
|
|||||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.reregister_poll_interest()?;
|
self.reregister_poll_interest()?;
|
||||||
return Err(TcpTmtcError::Io(err));
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -350,58 +328,24 @@ impl<
|
|||||||
&mut self,
|
&mut self,
|
||||||
mut stream: TcpStream,
|
mut stream: TcpStream,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
) -> Result<(), TcpTmtcError<TmError, TcSendError>> {
|
) -> Result<(), std::io::Error> {
|
||||||
let mut current_write_idx;
|
self.tc_handler.reset();
|
||||||
let mut next_write_idx = 0;
|
|
||||||
let mut connection_result = HandledConnectionInfo::new(addr);
|
let mut connection_result = HandledConnectionInfo::new(addr);
|
||||||
current_write_idx = next_write_idx;
|
|
||||||
loop {
|
loop {
|
||||||
let read_result = stream.read(&mut self.tc_buffer[current_write_idx..]);
|
let read_result = stream.read(&mut self.tc_buffer);
|
||||||
match read_result {
|
match read_result {
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
// Connection closed by client. If any TC was read, parse for complete packets.
|
// Connection closed by client.
|
||||||
// After that, break the outer loop.
|
|
||||||
if current_write_idx > 0 {
|
|
||||||
self.tc_handler.handle_tc_parsing(
|
|
||||||
&mut self.tc_buffer,
|
|
||||||
self.id,
|
|
||||||
&self.tc_sender,
|
|
||||||
&mut connection_result,
|
|
||||||
current_write_idx,
|
|
||||||
&mut next_write_idx,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(read_len) => {
|
Ok(read_len) => {
|
||||||
current_write_idx += read_len;
|
self.tc_handler
|
||||||
// TC buffer is full, we must parse for complete packets now.
|
.push(&self.tc_buffer[0..read_len], &mut connection_result);
|
||||||
if current_write_idx == self.tc_buffer.capacity() {
|
|
||||||
self.tc_handler.handle_tc_parsing(
|
|
||||||
&mut self.tc_buffer,
|
|
||||||
self.id,
|
|
||||||
&self.tc_sender,
|
|
||||||
&mut connection_result,
|
|
||||||
current_write_idx,
|
|
||||||
&mut next_write_idx,
|
|
||||||
)?;
|
|
||||||
current_write_idx = next_write_idx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => match e.kind() {
|
Err(e) => match e.kind() {
|
||||||
// As per [TcpStream::set_read_timeout] documentation, this should work for
|
// As per [TcpStream::set_read_timeout] documentation, this should work for
|
||||||
// both UNIX and Windows.
|
// both UNIX and Windows.
|
||||||
std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut => {
|
std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut => {
|
||||||
self.tc_handler.handle_tc_parsing(
|
|
||||||
&mut self.tc_buffer,
|
|
||||||
self.id,
|
|
||||||
&self.tc_sender,
|
|
||||||
&mut connection_result,
|
|
||||||
current_write_idx,
|
|
||||||
&mut next_write_idx,
|
|
||||||
)?;
|
|
||||||
current_write_idx = next_write_idx;
|
|
||||||
|
|
||||||
if !self.tm_handler.handle_tm_sending(
|
if !self.tm_handler.handle_tm_sending(
|
||||||
&mut self.tm_buffer,
|
&mut self.tm_buffer,
|
||||||
&mut self.tm_source,
|
&mut self.tm_source,
|
||||||
@@ -426,7 +370,7 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(TcpTmtcError::Io(e));
|
return Err(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -502,8 +446,11 @@ pub(crate) mod tests {
|
|||||||
.connection_info
|
.connection_info
|
||||||
.pop_back()
|
.pop_back()
|
||||||
.expect("no connection info available");
|
.expect("no connection info available");
|
||||||
assert_eq!(last_conn_result.num_received_tcs, num_tcs);
|
assert_eq!(
|
||||||
assert_eq!(last_conn_result.num_sent_tms, num_tms);
|
last_conn_result.num_received_tcs, num_tcs,
|
||||||
|
"received tcs mismatch"
|
||||||
|
);
|
||||||
|
assert_eq!(last_conn_result.num_sent_tms, num_tms, "sent tms missmatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_no_connections_left(&self) {
|
pub fn check_no_connections_left(&self) {
|
||||||
|
|||||||
@@ -5,41 +5,106 @@ use mio::net::{TcpListener, TcpStream};
|
|||||||
use std::{io::Write, net::SocketAddr};
|
use std::{io::Write, net::SocketAddr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
encoding::{ccsds::SpacePacketValidator, parse_buffer_for_ccsds_space_packets},
|
|
||||||
tmtc::{PacketSenderRaw, PacketSource},
|
|
||||||
ComponentId,
|
ComponentId,
|
||||||
|
encoding::{ccsds::SpacePacketValidator, parse_buffer_for_ccsds_space_packets},
|
||||||
|
queue::GenericSendError,
|
||||||
|
tmtc::{PacketHandler, PacketSource},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::tcp_server::{
|
use super::tcp_server::{
|
||||||
ConnectionResult, HandledConnectionHandler, HandledConnectionInfo, ServerConfig, TcpTcParser,
|
ConnectionResult, HandledConnectionHandler, HandledConnectionInfo, ServerConfig, TcpTcParser,
|
||||||
TcpTmSender, TcpTmtcError, TcpTmtcGenericServer,
|
TcpTmSender, TcpTmtcGenericServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<T: SpacePacketValidator, TmError, TcError: 'static> TcpTcParser<TmError, TcError> for T {
|
pub struct CcsdsPacketParser<
|
||||||
fn handle_tc_parsing(
|
PacketValidator: SpacePacketValidator,
|
||||||
&mut self,
|
PacketHandlerInstance: PacketHandler,
|
||||||
tc_buffer: &mut [u8],
|
> {
|
||||||
|
sender_id: ComponentId,
|
||||||
|
parsing_buffer: alloc::vec::Vec<u8>,
|
||||||
|
validator: PacketValidator,
|
||||||
|
packet_handler: PacketHandlerInstance,
|
||||||
|
current_write_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PacketValidator: SpacePacketValidator, PacketHandlerInstance: PacketHandler>
|
||||||
|
CcsdsPacketParser<PacketValidator, PacketHandlerInstance>
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
tc_sender: &(impl PacketSenderRaw<Error = TcError> + ?Sized),
|
parsing_buf_size: usize,
|
||||||
conn_result: &mut HandledConnectionInfo,
|
packet_handler: PacketHandlerInstance,
|
||||||
current_write_idx: usize,
|
validator: PacketValidator,
|
||||||
next_write_idx: &mut usize,
|
) -> Self {
|
||||||
) -> Result<(), TcpTmtcError<TmError, TcError>> {
|
Self {
|
||||||
// Reader vec full, need to parse for packets.
|
|
||||||
let parse_result = parse_buffer_for_ccsds_space_packets(
|
|
||||||
&tc_buffer[..current_write_idx],
|
|
||||||
self,
|
|
||||||
sender_id,
|
sender_id,
|
||||||
tc_sender,
|
parsing_buffer: alloc::vec![0; parsing_buf_size],
|
||||||
)
|
validator,
|
||||||
.map_err(|e| TcpTmtcError::TcError(e))?;
|
packet_handler,
|
||||||
if let Some(broken_tail_start) = parse_result.incomplete_tail_start {
|
current_write_index: 0,
|
||||||
// Copy broken tail to front of buffer.
|
}
|
||||||
tc_buffer.copy_within(broken_tail_start..current_write_idx, 0);
|
}
|
||||||
*next_write_idx = current_write_idx - broken_tail_start;
|
|
||||||
|
fn write_to_buffer(&mut self, data: &[u8]) -> usize {
|
||||||
|
let available = self.parsing_buffer.len() - self.current_write_index;
|
||||||
|
let to_write = core::cmp::min(data.len(), available);
|
||||||
|
|
||||||
|
self.parsing_buffer[self.current_write_index..self.current_write_index + to_write]
|
||||||
|
.copy_from_slice(&data[..to_write]);
|
||||||
|
self.current_write_index += to_write;
|
||||||
|
|
||||||
|
to_write
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_and_handle_packets(&mut self) -> u32 {
|
||||||
|
match parse_buffer_for_ccsds_space_packets(
|
||||||
|
&self.parsing_buffer[..self.current_write_index],
|
||||||
|
&self.validator,
|
||||||
|
self.sender_id,
|
||||||
|
&self.packet_handler,
|
||||||
|
) {
|
||||||
|
Ok(parse_result) => {
|
||||||
|
self.parsing_buffer
|
||||||
|
.copy_within(parse_result.parsed_bytes..self.current_write_index, 0);
|
||||||
|
self.current_write_index -= parse_result.parsed_bytes;
|
||||||
|
parse_result.packets_found
|
||||||
|
}
|
||||||
|
Err(_) => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_first_half_of_buffer(&mut self) {
|
||||||
|
let mid = self.parsing_buffer.len() / 2;
|
||||||
|
self.parsing_buffer.copy_within(mid.., 0);
|
||||||
|
self.current_write_index -= mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<PacketValidator: SpacePacketValidator, PacketHandlerInstance: PacketHandler> TcpTcParser
|
||||||
|
for CcsdsPacketParser<PacketValidator, PacketHandlerInstance>
|
||||||
|
{
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.current_write_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, mut tc_buffer: &[u8], conn_result: &mut HandledConnectionInfo) {
|
||||||
|
while !tc_buffer.is_empty() {
|
||||||
|
// Write as much as possible to buffer
|
||||||
|
let written = self.write_to_buffer(tc_buffer);
|
||||||
|
tc_buffer = &tc_buffer[written..];
|
||||||
|
|
||||||
|
// Parse for complete packets
|
||||||
|
let packets_found = self.parse_and_handle_packets();
|
||||||
|
conn_result.num_received_tcs += packets_found;
|
||||||
|
|
||||||
|
if tc_buffer.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle buffer overflow
|
||||||
|
if self.current_write_index == self.parsing_buffer.len() {
|
||||||
|
self.drop_first_half_of_buffer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
conn_result.num_received_tcs += parse_result.packets_found;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,21 +112,18 @@ impl<T: SpacePacketValidator, TmError, TcError: 'static> TcpTcParser<TmError, Tc
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SpacepacketsTmSender {}
|
pub struct SpacepacketsTmSender {}
|
||||||
|
|
||||||
impl<TmError, TcError> TcpTmSender<TmError, TcError> for SpacepacketsTmSender {
|
impl TcpTmSender for SpacepacketsTmSender {
|
||||||
fn handle_tm_sending(
|
fn handle_tm_sending(
|
||||||
&mut self,
|
&mut self,
|
||||||
tm_buffer: &mut [u8],
|
tm_buffer: &mut [u8],
|
||||||
tm_source: &mut (impl PacketSource<Error = TmError> + ?Sized),
|
tm_source: &mut (impl PacketSource<Error = ()> + ?Sized),
|
||||||
conn_result: &mut HandledConnectionInfo,
|
conn_result: &mut HandledConnectionInfo,
|
||||||
stream: &mut TcpStream,
|
stream: &mut TcpStream,
|
||||||
) -> Result<bool, TcpTmtcError<TmError, TcError>> {
|
) -> Result<bool, std::io::Error> {
|
||||||
let mut tm_was_sent = false;
|
let mut tm_was_sent = false;
|
||||||
loop {
|
while let Ok(read_tm_len) = tm_source.retrieve_packet(tm_buffer) {
|
||||||
// Write TM until TM source is exhausted. For now, there is no limit for the amount
|
// Write TM until TM source is exhausted. For now, there is no limit for the amount
|
||||||
// of TM written this way.
|
// of TM written this way.
|
||||||
let read_tm_len = tm_source
|
|
||||||
.retrieve_packet(tm_buffer)
|
|
||||||
.map_err(|e| TcpTmtcError::TmError(e))?;
|
|
||||||
|
|
||||||
if read_tm_len == 0 {
|
if read_tm_len == 0 {
|
||||||
return Ok(tm_was_sent);
|
return Ok(tm_was_sent);
|
||||||
@@ -71,6 +133,7 @@ impl<TmError, TcError> TcpTmSender<TmError, TcError> for SpacepacketsTmSender {
|
|||||||
|
|
||||||
stream.write_all(&tm_buffer[..read_tm_len])?;
|
stream.write_all(&tm_buffer[..read_tm_len])?;
|
||||||
}
|
}
|
||||||
|
Ok(tm_was_sent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,32 +151,25 @@ impl<TmError, TcError> TcpTmSender<TmError, TcError> for SpacepacketsTmSender {
|
|||||||
/// The [TCP server integration tests](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/tcp_servers.rs)
|
/// The [TCP server integration tests](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/tcp_servers.rs)
|
||||||
/// also serves as the example application for this module.
|
/// also serves as the example application for this module.
|
||||||
pub struct TcpSpacepacketsServer<
|
pub struct TcpSpacepacketsServer<
|
||||||
TmSource: PacketSource<Error = TmError>,
|
TmSource: PacketSource<Error = ()>,
|
||||||
TcSender: PacketSenderRaw<Error = SendError>,
|
TcSender: PacketHandler<Error = GenericSendError>,
|
||||||
Validator: SpacePacketValidator,
|
Validator: SpacePacketValidator,
|
||||||
HandledConnection: HandledConnectionHandler,
|
HandledConnection: HandledConnectionHandler,
|
||||||
TmError,
|
|
||||||
SendError: 'static,
|
|
||||||
> {
|
> {
|
||||||
pub generic_server: TcpTmtcGenericServer<
|
pub generic_server: TcpTmtcGenericServer<
|
||||||
TmSource,
|
TmSource,
|
||||||
TcSender,
|
|
||||||
SpacepacketsTmSender,
|
SpacepacketsTmSender,
|
||||||
Validator,
|
CcsdsPacketParser<Validator, TcSender>,
|
||||||
HandledConnection,
|
HandledConnection,
|
||||||
TmError,
|
|
||||||
SendError,
|
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
TmSource: PacketSource<Error = TmError>,
|
TmSource: PacketSource<Error = ()>,
|
||||||
TcSender: PacketSenderRaw<Error = TcError>,
|
TcSender: PacketHandler<Error = GenericSendError>,
|
||||||
Validator: SpacePacketValidator,
|
Validator: SpacePacketValidator,
|
||||||
HandledConnection: HandledConnectionHandler,
|
HandledConnection: HandledConnectionHandler,
|
||||||
TmError: 'static,
|
> TcpSpacepacketsServer<TmSource, TcSender, Validator, HandledConnection>
|
||||||
TcError: 'static,
|
|
||||||
> TcpSpacepacketsServer<TmSource, TcSender, Validator, HandledConnection, TmError, TcError>
|
|
||||||
{
|
{
|
||||||
///
|
///
|
||||||
/// ## Parameter
|
/// ## Parameter
|
||||||
@@ -122,7 +178,7 @@ impl<
|
|||||||
/// * `tm_source` - Generic TM source used by the server to pull telemetry packets which are
|
/// * `tm_source` - Generic TM source used by the server to pull telemetry packets which are
|
||||||
/// then sent back to the client.
|
/// then sent back to the client.
|
||||||
/// * `tc_sender` - Any received telecommands which were decoded successfully will be
|
/// * `tc_sender` - Any received telecommands which were decoded successfully will be
|
||||||
/// forwarded using this [PacketSenderRaw].
|
/// forwarded using this [PacketHandler].
|
||||||
/// * `validator` - Used to determine the space packets relevant for further processing and
|
/// * `validator` - Used to determine the space packets relevant for further processing and
|
||||||
/// to detect broken space packets.
|
/// to detect broken space packets.
|
||||||
/// * `handled_connection_hook` - Called to notify the user about a succesfully handled
|
/// * `handled_connection_hook` - Called to notify the user about a succesfully handled
|
||||||
@@ -132,18 +188,16 @@ impl<
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
cfg: ServerConfig,
|
cfg: ServerConfig,
|
||||||
tm_source: TmSource,
|
tm_source: TmSource,
|
||||||
tc_sender: TcSender,
|
tc_parser: CcsdsPacketParser<Validator, TcSender>,
|
||||||
validator: Validator,
|
|
||||||
handled_connection_hook: HandledConnection,
|
handled_connection_hook: HandledConnection,
|
||||||
stop_signal: Option<Arc<AtomicBool>>,
|
stop_signal: Option<Arc<AtomicBool>>,
|
||||||
) -> Result<Self, std::io::Error> {
|
) -> Result<Self, std::io::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
generic_server: TcpTmtcGenericServer::new(
|
generic_server: TcpTmtcGenericServer::new(
|
||||||
cfg,
|
cfg,
|
||||||
validator,
|
tc_parser,
|
||||||
SpacepacketsTmSender::default(),
|
SpacepacketsTmSender::default(),
|
||||||
tm_source,
|
tm_source,
|
||||||
tc_sender,
|
|
||||||
handled_connection_hook,
|
handled_connection_hook,
|
||||||
stop_signal,
|
stop_signal,
|
||||||
)?,
|
)?,
|
||||||
@@ -162,7 +216,7 @@ impl<
|
|||||||
pub fn handle_all_connections(
|
pub fn handle_all_connections(
|
||||||
&mut self,
|
&mut self,
|
||||||
poll_timeout: Option<Duration>
|
poll_timeout: Option<Duration>
|
||||||
) -> Result<ConnectionResult, TcpTmtcError<TmError, TcError>>;
|
) -> Result<ConnectionResult, std::io::Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,29 +237,29 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
|
use arbitrary_int::u11;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
|
||||||
CcsdsPacket, PacketId, SpHeader,
|
CcsdsPacket, PacketId, SpHeader,
|
||||||
|
ecss::{CreatorConfig, MessageTypeId, WritablePusPacket, tc::PusTcCreator},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
||||||
hal::std::tcp_server::{
|
hal::std::tcp_server::{
|
||||||
tests::{ConnectionFinishedHandler, SyncTmSource},
|
|
||||||
ConnectionResult, ServerConfig,
|
ConnectionResult, ServerConfig,
|
||||||
|
tests::{ConnectionFinishedHandler, SyncTmSource},
|
||||||
},
|
},
|
||||||
queue::GenericSendError,
|
|
||||||
tmtc::PacketAsVec,
|
tmtc::PacketAsVec,
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::TcpSpacepacketsServer;
|
use super::TcpSpacepacketsServer;
|
||||||
|
|
||||||
const TCP_SERVER_ID: ComponentId = 0x05;
|
const TCP_SERVER_ID: ComponentId = 0x05;
|
||||||
const TEST_APID_0: u16 = 0x02;
|
const TEST_APID_0: u11 = u11::new(0x02);
|
||||||
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
|
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
|
||||||
const TEST_APID_1: u16 = 0x10;
|
const TEST_APID_1: u11 = u11::new(0x10);
|
||||||
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
|
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -223,23 +277,19 @@ mod tests {
|
|||||||
|
|
||||||
fn generic_tmtc_server(
|
fn generic_tmtc_server(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
tc_sender: mpsc::Sender<PacketAsVec>,
|
tc_parser: super::CcsdsPacketParser<SimpleValidator, mpsc::Sender<PacketAsVec>>,
|
||||||
tm_source: SyncTmSource,
|
tm_source: SyncTmSource,
|
||||||
validator: SimpleValidator,
|
|
||||||
stop_signal: Option<Arc<AtomicBool>>,
|
stop_signal: Option<Arc<AtomicBool>>,
|
||||||
) -> TcpSpacepacketsServer<
|
) -> TcpSpacepacketsServer<
|
||||||
SyncTmSource,
|
SyncTmSource,
|
||||||
mpsc::Sender<PacketAsVec>,
|
mpsc::Sender<PacketAsVec>,
|
||||||
SimpleValidator,
|
SimpleValidator,
|
||||||
ConnectionFinishedHandler,
|
ConnectionFinishedHandler,
|
||||||
(),
|
|
||||||
GenericSendError,
|
|
||||||
> {
|
> {
|
||||||
TcpSpacepacketsServer::new(
|
TcpSpacepacketsServer::new(
|
||||||
ServerConfig::new(TCP_SERVER_ID, *addr, Duration::from_millis(2), 1024, 1024),
|
ServerConfig::new(TCP_SERVER_ID, *addr, Duration::from_millis(2), 1024, 1024),
|
||||||
tm_source,
|
tm_source,
|
||||||
tc_sender,
|
tc_parser,
|
||||||
validator,
|
|
||||||
ConnectionFinishedHandler::default(),
|
ConnectionFinishedHandler::default(),
|
||||||
stop_signal,
|
stop_signal,
|
||||||
)
|
)
|
||||||
@@ -255,9 +305,8 @@ mod tests {
|
|||||||
validator.0.insert(TEST_PACKET_ID_0);
|
validator.0.insert(TEST_PACKET_ID_0);
|
||||||
let mut tcp_server = generic_tmtc_server(
|
let mut tcp_server = generic_tmtc_server(
|
||||||
&auto_port_addr,
|
&auto_port_addr,
|
||||||
tc_sender.clone(),
|
super::CcsdsPacketParser::new(TCP_SERVER_ID, 1024, tc_sender.clone(), validator),
|
||||||
tm_source,
|
tm_source,
|
||||||
validator,
|
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let dest_addr = tcp_server
|
let dest_addr = tcp_server
|
||||||
@@ -283,8 +332,12 @@ mod tests {
|
|||||||
.check_no_connections_left();
|
.check_no_connections_left();
|
||||||
set_if_done.store(true, Ordering::Relaxed);
|
set_if_done.store(true, Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
let ping_tc =
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
SpHeader::new_from_apid(TEST_APID_0),
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let tc_0 = ping_tc.to_vec().expect("packet generation failed");
|
let tc_0 = ping_tc.to_vec().expect("packet generation failed");
|
||||||
let mut stream = TcpStream::connect(dest_addr).expect("connecting to TCP server failed");
|
let mut stream = TcpStream::connect(dest_addr).expect("connecting to TCP server failed");
|
||||||
stream
|
stream
|
||||||
@@ -314,13 +367,21 @@ mod tests {
|
|||||||
|
|
||||||
// Add telemetry
|
// Add telemetry
|
||||||
let mut total_tm_len = 0;
|
let mut total_tm_len = 0;
|
||||||
let verif_tm =
|
let verif_tm = PusTcCreator::new_simple(
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 1, 1, &[], true);
|
SpHeader::new_from_apid(TEST_APID_0),
|
||||||
|
MessageTypeId::new(1, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let tm_0 = verif_tm.to_vec().expect("writing packet failed");
|
let tm_0 = verif_tm.to_vec().expect("writing packet failed");
|
||||||
total_tm_len += tm_0.len();
|
total_tm_len += tm_0.len();
|
||||||
tm_source.add_tm(&tm_0);
|
tm_source.add_tm(&tm_0);
|
||||||
let verif_tm =
|
let verif_tm = PusTcCreator::new_simple(
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 1, 3, &[], true);
|
SpHeader::new_from_apid(TEST_APID_1),
|
||||||
|
MessageTypeId::new(1, 3),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let tm_1 = verif_tm.to_vec().expect("writing packet failed");
|
let tm_1 = verif_tm.to_vec().expect("writing packet failed");
|
||||||
total_tm_len += tm_1.len();
|
total_tm_len += tm_1.len();
|
||||||
tm_source.add_tm(&tm_1);
|
tm_source.add_tm(&tm_1);
|
||||||
@@ -331,9 +392,8 @@ mod tests {
|
|||||||
validator.0.insert(TEST_PACKET_ID_1);
|
validator.0.insert(TEST_PACKET_ID_1);
|
||||||
let mut tcp_server = generic_tmtc_server(
|
let mut tcp_server = generic_tmtc_server(
|
||||||
&auto_port_addr,
|
&auto_port_addr,
|
||||||
tc_sender.clone(),
|
super::CcsdsPacketParser::new(TCP_SERVER_ID, 1024, tc_sender.clone(), validator),
|
||||||
tm_source,
|
tm_source,
|
||||||
validator,
|
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let dest_addr = tcp_server
|
let dest_addr = tcp_server
|
||||||
@@ -366,14 +426,22 @@ mod tests {
|
|||||||
.expect("setting reas timeout failed");
|
.expect("setting reas timeout failed");
|
||||||
|
|
||||||
// Send telecommands
|
// Send telecommands
|
||||||
let ping_tc =
|
let ping_tc = PusTcCreator::new_simple(
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
SpHeader::new_from_apid(TEST_APID_0),
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let tc_0 = ping_tc.to_vec().expect("ping tc creation failed");
|
let tc_0 = ping_tc.to_vec().expect("ping tc creation failed");
|
||||||
stream
|
stream
|
||||||
.write_all(&tc_0)
|
.write_all(&tc_0)
|
||||||
.expect("writing to TCP server failed");
|
.expect("writing to TCP server failed");
|
||||||
let action_tc =
|
let action_tc = PusTcCreator::new_simple(
|
||||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true);
|
SpHeader::new_from_apid(TEST_APID_1),
|
||||||
|
MessageTypeId::new(8, 0),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let tc_1 = action_tc.to_vec().expect("action tc creation failed");
|
let tc_1 = action_tc.to_vec().expect("action tc creation failed");
|
||||||
stream
|
stream
|
||||||
.write_all(&tc_1)
|
.write_all(&tc_1)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Generic UDP TC server.
|
//! Generic UDP TC server.
|
||||||
use crate::tmtc::PacketSenderRaw;
|
|
||||||
use crate::ComponentId;
|
use crate::ComponentId;
|
||||||
|
use crate::tmtc::PacketHandler;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use std::io::{self, ErrorKind};
|
use std::io::{self, ErrorKind};
|
||||||
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||||
@@ -12,7 +12,7 @@ use std::vec::Vec;
|
|||||||
///
|
///
|
||||||
/// It caches all received telecomands into a vector. The maximum expected telecommand size should
|
/// It caches all received telecomands into a vector. The maximum expected telecommand size should
|
||||||
/// be declared upfront. This avoids dynamic allocation during run-time. The user can specify a TC
|
/// be declared upfront. This avoids dynamic allocation during run-time. The user can specify a TC
|
||||||
/// sender in form of a special trait object which implements [PacketSenderRaw]. For example, this
|
/// sender in form of a special trait object which implements [PacketHandler]. For example, this
|
||||||
/// can be used to send the telecommands to a centralized TC source component for further
|
/// can be used to send the telecommands to a centralized TC source component for further
|
||||||
/// processing and routing.
|
/// processing and routing.
|
||||||
///
|
///
|
||||||
@@ -24,9 +24,10 @@ use std::vec::Vec;
|
|||||||
/// use spacepackets::ecss::WritablePusPacket;
|
/// use spacepackets::ecss::WritablePusPacket;
|
||||||
/// use satrs::hal::std::udp_server::UdpTcServer;
|
/// use satrs::hal::std::udp_server::UdpTcServer;
|
||||||
/// use satrs::ComponentId;
|
/// use satrs::ComponentId;
|
||||||
/// use satrs::tmtc::PacketSenderRaw;
|
/// use satrs::tmtc::PacketHandler;
|
||||||
/// use spacepackets::SpHeader;
|
/// use spacepackets::SpHeader;
|
||||||
/// use spacepackets::ecss::tc::PusTcCreator;
|
/// use spacepackets::ecss::tc::{MessageTypeId, PusTcCreator, CreatorConfig};
|
||||||
|
/// use arbitrary_int::u11;
|
||||||
///
|
///
|
||||||
/// const UDP_SERVER_ID: ComponentId = 0x05;
|
/// const UDP_SERVER_ID: ComponentId = 0x05;
|
||||||
///
|
///
|
||||||
@@ -34,8 +35,8 @@ use std::vec::Vec;
|
|||||||
/// let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777);
|
/// let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777);
|
||||||
/// let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, packet_sender)
|
/// let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, packet_sender)
|
||||||
/// .expect("Creating UDP TMTC server failed");
|
/// .expect("Creating UDP TMTC server failed");
|
||||||
/// let sph = SpHeader::new_from_apid(0x02);
|
/// let sph = SpHeader::new_from_apid(u11::new(0x02));
|
||||||
/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
/// let pus_tc = PusTcCreator::new_simple(sph, MessageTypeId::new(17, 1), &[], CreatorConfig::default());
|
||||||
/// // Can not fail.
|
/// // Can not fail.
|
||||||
/// let ping_tc_raw = pus_tc.to_vec().unwrap();
|
/// let ping_tc_raw = pus_tc.to_vec().unwrap();
|
||||||
///
|
///
|
||||||
@@ -59,7 +60,7 @@ use std::vec::Vec;
|
|||||||
/// [example code](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/tmtc.rs#L67)
|
/// [example code](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/tmtc.rs#L67)
|
||||||
/// on how to use this TC server. It uses the server to receive PUS telecommands on a specific port
|
/// on how to use this TC server. It uses the server to receive PUS telecommands on a specific port
|
||||||
/// and then forwards them to a generic CCSDS packet receiver.
|
/// and then forwards them to a generic CCSDS packet receiver.
|
||||||
pub struct UdpTcServer<TcSender: PacketSenderRaw<Error = SendError>, SendError> {
|
pub struct UdpTcServer<TcSender: PacketHandler<Error = SendError>, SendError> {
|
||||||
pub id: ComponentId,
|
pub id: ComponentId,
|
||||||
pub socket: UdpSocket,
|
pub socket: UdpSocket,
|
||||||
recv_buf: Vec<u8>,
|
recv_buf: Vec<u8>,
|
||||||
@@ -77,7 +78,7 @@ pub enum ReceiveResult<SendError: Debug + 'static> {
|
|||||||
Send(SendError),
|
Send(SendError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
|
impl<TcSender: PacketHandler<Error = SendError>, SendError: Debug + 'static>
|
||||||
UdpTcServer<TcSender, SendError>
|
UdpTcServer<TcSender, SendError>
|
||||||
{
|
{
|
||||||
pub fn new<A: ToSocketAddrs>(
|
pub fn new<A: ToSocketAddrs>(
|
||||||
@@ -105,13 +106,13 @@ impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
|
|||||||
Err(ReceiveResult::NothingReceived)
|
Err(ReceiveResult::NothingReceived)
|
||||||
} else {
|
} else {
|
||||||
Err(e.into())
|
Err(e.into())
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (num_bytes, from) = res;
|
let (num_bytes, from) = res;
|
||||||
self.sender_addr = Some(from);
|
self.sender_addr = Some(from);
|
||||||
self.tc_sender
|
self.tc_sender
|
||||||
.send_packet(self.id, &self.recv_buf[0..num_bytes])
|
.handle_packet(self.id, &self.recv_buf[0..num_bytes])
|
||||||
.map_err(ReceiveResult::Send)?;
|
.map_err(ReceiveResult::Send)?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@@ -123,14 +124,15 @@ impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::ComponentId;
|
||||||
use crate::hal::std::udp_server::{ReceiveResult, UdpTcServer};
|
use crate::hal::std::udp_server::{ReceiveResult, UdpTcServer};
|
||||||
use crate::queue::GenericSendError;
|
use crate::queue::GenericSendError;
|
||||||
use crate::tmtc::PacketSenderRaw;
|
use crate::tmtc::PacketHandler;
|
||||||
use crate::ComponentId;
|
use arbitrary_int::u11;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use spacepackets::ecss::tc::PusTcCreator;
|
|
||||||
use spacepackets::ecss::WritablePusPacket;
|
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
|
use spacepackets::ecss::tc::PusTcCreator;
|
||||||
|
use spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
@@ -144,10 +146,10 @@ mod tests {
|
|||||||
pub sent_cmds: RefCell<VecDeque<Vec<u8>>>,
|
pub sent_cmds: RefCell<VecDeque<Vec<u8>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSenderRaw for PingReceiver {
|
impl PacketHandler for PingReceiver {
|
||||||
type Error = GenericSendError;
|
type Error = GenericSendError;
|
||||||
|
|
||||||
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
assert_eq!(sender_id, UDP_SERVER_ID);
|
assert_eq!(sender_id, UDP_SERVER_ID);
|
||||||
let mut sent_data = Vec::new();
|
let mut sent_data = Vec::new();
|
||||||
sent_data.extend_from_slice(tc_raw);
|
sent_data.extend_from_slice(tc_raw);
|
||||||
@@ -166,8 +168,13 @@ mod tests {
|
|||||||
let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, ping_receiver)
|
let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, ping_receiver)
|
||||||
.expect("Creating UDP TMTC server failed");
|
.expect("Creating UDP TMTC server failed");
|
||||||
is_send(&udp_tc_server);
|
is_send(&udp_tc_server);
|
||||||
let sph = SpHeader::new_from_apid(0x02);
|
let sph = SpHeader::new_from_apid(u11::new(0x02));
|
||||||
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
let pus_tc = PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let len = pus_tc
|
let len = pus_tc
|
||||||
.write_to_bytes(&mut buf)
|
.write_to_bytes(&mut buf)
|
||||||
.expect("Error writing PUS TC packet");
|
.expect("Error writing PUS TC packet");
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
//! - The [pus] module which provides special support for projects using
|
//! - The [pus] module which provides special support for projects using
|
||||||
//! the [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
//! the [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(any(feature = "alloc", test))]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
extern crate downcast_rs;
|
extern crate downcast_rs;
|
||||||
@@ -27,7 +27,9 @@ pub mod action;
|
|||||||
pub mod dev_mgmt;
|
pub mod dev_mgmt;
|
||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
pub mod event_man;
|
pub mod event_man;
|
||||||
|
pub mod event_man_legacy;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod events_legacy;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod executable;
|
pub mod executable;
|
||||||
pub mod hal;
|
pub mod hal;
|
||||||
@@ -55,7 +57,7 @@ pub use spacepackets;
|
|||||||
use spacepackets::PacketId;
|
use spacepackets::PacketId;
|
||||||
|
|
||||||
/// Generic component ID type.
|
/// Generic component ID type.
|
||||||
pub type ComponentId = u64;
|
pub type ComponentId = u32;
|
||||||
|
|
||||||
pub trait ValidatorU16Id {
|
pub trait ValidatorU16Id {
|
||||||
fn validate(&self, id: u16) -> bool;
|
fn validate(&self, id: u16) -> bool;
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ pub use alloc_mod::*;
|
|||||||
pub use std_mod::*;
|
pub use std_mod::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
queue::{GenericReceiveError, GenericSendError},
|
queue::{GenericReceiveError, GenericSendError},
|
||||||
request::{
|
request::{
|
||||||
GenericMessage, MessageMetadata, MessageReceiverProvider, MessageReceiverWithId, RequestId,
|
GenericMessage, MessageMetadata, MessageReceiverProvider, MessageReceiverWithId, RequestId,
|
||||||
},
|
},
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Mode = u32;
|
pub type Mode = u32;
|
||||||
@@ -257,7 +257,7 @@ pub trait ModeRequestHandler: ModeProvider {
|
|||||||
|
|
||||||
pub trait ModeReplyReceiver {
|
pub trait ModeReplyReceiver {
|
||||||
fn try_recv_mode_reply(&self)
|
fn try_recv_mode_reply(&self)
|
||||||
-> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError>;
|
-> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: MessageReceiverProvider<ModeReply>> ModeReplyReceiver
|
impl<R: MessageReceiverProvider<ModeReply>> ModeReplyReceiver
|
||||||
@@ -309,12 +309,11 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
From,
|
From,
|
||||||
Sender: MessageSenderProvider<ModeReply>,
|
Sender: MessageSenderProvider<ModeReply>,
|
||||||
Receiver: MessageReceiverProvider<From>,
|
Receiver: MessageReceiverProvider<From>,
|
||||||
SenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
SenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||||
> ModeReplySender
|
> ModeReplySender for MessageSenderAndReceiver<ModeReply, From, Sender, Receiver, SenderStore>
|
||||||
for MessageSenderAndReceiver<ModeReply, From, Sender, Receiver, SenderStore>
|
|
||||||
{
|
{
|
||||||
fn local_channel_id(&self) -> ComponentId {
|
fn local_channel_id(&self) -> ComponentId {
|
||||||
self.local_channel_id_generic()
|
self.local_channel_id_generic()
|
||||||
@@ -334,12 +333,11 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
To,
|
To,
|
||||||
Sender: MessageSenderProvider<To>,
|
Sender: MessageSenderProvider<To>,
|
||||||
Receiver: MessageReceiverProvider<ModeReply>,
|
Receiver: MessageReceiverProvider<ModeReply>,
|
||||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||||
> ModeReplyReceiver
|
> ModeReplyReceiver for MessageSenderAndReceiver<To, ModeReply, Sender, Receiver, SenderStore>
|
||||||
for MessageSenderAndReceiver<To, ModeReply, Sender, Receiver, SenderStore>
|
|
||||||
{
|
{
|
||||||
fn try_recv_mode_reply(
|
fn try_recv_mode_reply(
|
||||||
&self,
|
&self,
|
||||||
@@ -349,15 +347,15 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Request,
|
Request,
|
||||||
ReqSender: MessageSenderProvider<Request>,
|
ReqSender: MessageSenderProvider<Request>,
|
||||||
ReqReceiver: MessageReceiverProvider<Request>,
|
ReqReceiver: MessageReceiverProvider<Request>,
|
||||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||||
Reply,
|
Reply,
|
||||||
ReplySender: MessageSenderProvider<Reply>,
|
ReplySender: MessageSenderProvider<Reply>,
|
||||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||||
>
|
>
|
||||||
RequestAndReplySenderAndReceiver<
|
RequestAndReplySenderAndReceiver<
|
||||||
Request,
|
Request,
|
||||||
ReqSender,
|
ReqSender,
|
||||||
@@ -376,14 +374,14 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Request,
|
Request,
|
||||||
ReqSender: MessageSenderProvider<Request>,
|
ReqSender: MessageSenderProvider<Request>,
|
||||||
ReqReceiver: MessageReceiverProvider<Request>,
|
ReqReceiver: MessageReceiverProvider<Request>,
|
||||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||||
ReplySender: MessageSenderProvider<ModeReply>,
|
ReplySender: MessageSenderProvider<ModeReply>,
|
||||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||||
> ModeReplySender
|
> ModeReplySender
|
||||||
for RequestAndReplySenderAndReceiver<
|
for RequestAndReplySenderAndReceiver<
|
||||||
Request,
|
Request,
|
||||||
ReqSender,
|
ReqSender,
|
||||||
@@ -413,14 +411,14 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Request,
|
Request,
|
||||||
ReqSender: MessageSenderProvider<Request>,
|
ReqSender: MessageSenderProvider<Request>,
|
||||||
ReqReceiver: MessageReceiverProvider<Request>,
|
ReqReceiver: MessageReceiverProvider<Request>,
|
||||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||||
ReplySender: MessageSenderProvider<ModeReply>,
|
ReplySender: MessageSenderProvider<ModeReply>,
|
||||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||||
> ModeReplyReceiver
|
> ModeReplyReceiver
|
||||||
for RequestAndReplySenderAndReceiver<
|
for RequestAndReplySenderAndReceiver<
|
||||||
Request,
|
Request,
|
||||||
ReqSender,
|
ReqSender,
|
||||||
@@ -444,10 +442,10 @@ pub mod alloc_mod {
|
|||||||
MessageSenderAndReceiver<ModeReply, ModeRequest, Sender, Receiver, ReplySenderStore>;
|
MessageSenderAndReceiver<ModeReply, ModeRequest, Sender, Receiver, ReplySenderStore>;
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: MessageSenderProvider<ModeReply>,
|
Sender: MessageSenderProvider<ModeReply>,
|
||||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
ReplySenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||||
> ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore>
|
> ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore>
|
||||||
{
|
{
|
||||||
pub fn try_recv_mode_request(
|
pub fn try_recv_mode_request(
|
||||||
&self,
|
&self,
|
||||||
@@ -474,10 +472,10 @@ pub mod alloc_mod {
|
|||||||
MessageSenderAndReceiver<ModeRequest, ModeReply, Sender, Receiver, RequestSenderStore>;
|
MessageSenderAndReceiver<ModeRequest, ModeReply, Sender, Receiver, RequestSenderStore>;
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: MessageSenderProvider<ModeRequest>,
|
Sender: MessageSenderProvider<ModeRequest>,
|
||||||
Receiver: MessageReceiverProvider<ModeReply>,
|
Receiver: MessageReceiverProvider<ModeReply>,
|
||||||
RequestSenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
RequestSenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||||
> ModeRequestorInterface<Sender, Receiver, RequestSenderStore>
|
> ModeRequestorInterface<Sender, Receiver, RequestSenderStore>
|
||||||
{
|
{
|
||||||
pub fn try_recv_mode_reply(
|
pub fn try_recv_mode_reply(
|
||||||
&self,
|
&self,
|
||||||
@@ -531,11 +529,11 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
To,
|
To,
|
||||||
Sender: MessageSenderProvider<To>,
|
Sender: MessageSenderProvider<To>,
|
||||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||||
> ModeRequestReceiver
|
> ModeRequestReceiver
|
||||||
for MessageSenderAndReceiver<To, ModeRequest, Sender, Receiver, SenderStore>
|
for MessageSenderAndReceiver<To, ModeRequest, Sender, Receiver, SenderStore>
|
||||||
{
|
{
|
||||||
fn try_recv_mode_request(
|
fn try_recv_mode_request(
|
||||||
@@ -546,11 +544,11 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
From,
|
From,
|
||||||
Sender: MessageSenderProvider<ModeRequest>,
|
Sender: MessageSenderProvider<ModeRequest>,
|
||||||
Receiver: MessageReceiverProvider<From>,
|
Receiver: MessageReceiverProvider<From>,
|
||||||
SenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
SenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||||
> ModeRequestSender
|
> ModeRequestSender
|
||||||
for MessageSenderAndReceiver<ModeRequest, From, Sender, Receiver, SenderStore>
|
for MessageSenderAndReceiver<ModeRequest, From, Sender, Receiver, SenderStore>
|
||||||
{
|
{
|
||||||
fn local_channel_id(&self) -> ComponentId {
|
fn local_channel_id(&self) -> ComponentId {
|
||||||
@@ -572,14 +570,14 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||||
Reply,
|
Reply,
|
||||||
ReplySender: MessageSenderProvider<Reply>,
|
ReplySender: MessageSenderProvider<Reply>,
|
||||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||||
>
|
>
|
||||||
RequestAndReplySenderAndReceiver<
|
RequestAndReplySenderAndReceiver<
|
||||||
ModeRequest,
|
ModeRequest,
|
||||||
ReqSender,
|
ReqSender,
|
||||||
@@ -598,14 +596,14 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||||
Reply,
|
Reply,
|
||||||
ReplySender: MessageSenderProvider<Reply>,
|
ReplySender: MessageSenderProvider<Reply>,
|
||||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||||
> ModeRequestSender
|
> ModeRequestSender
|
||||||
for RequestAndReplySenderAndReceiver<
|
for RequestAndReplySenderAndReceiver<
|
||||||
ModeRequest,
|
ModeRequest,
|
||||||
ReqSender,
|
ReqSender,
|
||||||
@@ -636,14 +634,14 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||||
Reply,
|
Reply,
|
||||||
ReplySender: MessageSenderProvider<Reply>,
|
ReplySender: MessageSenderProvider<Reply>,
|
||||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||||
> ModeRequestReceiver
|
> ModeRequestReceiver
|
||||||
for RequestAndReplySenderAndReceiver<
|
for RequestAndReplySenderAndReceiver<
|
||||||
ModeRequest,
|
ModeRequest,
|
||||||
ReqSender,
|
ReqSender,
|
||||||
@@ -726,7 +724,7 @@ pub(crate) mod tests {
|
|||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use crate::{request::RequestId, ComponentId};
|
use crate::{ComponentId, request::RequestId};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ use alloc::vec::Vec;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, Submode},
|
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, Submode},
|
||||||
request::MessageSenderProvider,
|
request::MessageSenderProvider,
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ use crate::pool::PoolAddr;
|
|||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU64, EcssEnumU8};
|
use spacepackets::ByteConversionError;
|
||||||
|
use spacepackets::ecss::{EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumU64};
|
||||||
pub use spacepackets::util::ToBeBytes;
|
pub use spacepackets::util::ToBeBytes;
|
||||||
use spacepackets::util::UnsignedEnum;
|
use spacepackets::util::UnsignedEnum;
|
||||||
use spacepackets::ByteConversionError;
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ impl Error for PoolError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic trait for pool providers which provide memory pools for variable sized data.
|
/// Generic trait for pool providers which provide memory pools for variable sized packet data.
|
||||||
///
|
///
|
||||||
/// It specifies a basic API to [Self::add], [Self::modify], [Self::read] and [Self::delete] data
|
/// It specifies a basic API to [Self::add], [Self::modify], [Self::read] and [Self::delete] data
|
||||||
/// in the store at its core. The API was designed so internal optimizations can be performed
|
/// in the store at its core. The API was designed so internal optimizations can be performed
|
||||||
@@ -250,7 +250,7 @@ pub trait PoolProvider {
|
|||||||
/// call the user-provided closure and pass a mutable reference to the memory block
|
/// call the user-provided closure and pass a mutable reference to the memory block
|
||||||
/// to the closure. This allows the user to modify the memory block.
|
/// to the closure. This allows the user to modify the memory block.
|
||||||
fn modify<U: FnMut(&mut [u8])>(&mut self, addr: &PoolAddr, updater: U)
|
fn modify<U: FnMut(&mut [u8])>(&mut self, addr: &PoolAddr, updater: U)
|
||||||
-> Result<(), PoolError>;
|
-> Result<(), PoolError>;
|
||||||
|
|
||||||
/// The provider should copy the data from the memory block to the user-provided buffer if
|
/// The provider should copy the data from the memory block to the user-provided buffer if
|
||||||
/// it exists.
|
/// it exists.
|
||||||
@@ -258,6 +258,9 @@ pub trait PoolProvider {
|
|||||||
|
|
||||||
/// Delete data inside the pool given a [PoolAddr].
|
/// Delete data inside the pool given a [PoolAddr].
|
||||||
fn delete(&mut self, addr: PoolAddr) -> Result<(), PoolError>;
|
fn delete(&mut self, addr: PoolAddr) -> Result<(), PoolError>;
|
||||||
|
|
||||||
|
fn clear(&mut self) -> Result<(), PoolError>;
|
||||||
|
|
||||||
fn has_element_at(&self, addr: &PoolAddr) -> Result<bool, PoolError>;
|
fn has_element_at(&self, addr: &PoolAddr) -> Result<bool, PoolError>;
|
||||||
|
|
||||||
/// Retrieve the length of the data at the given store address.
|
/// Retrieve the length of the data at the given store address.
|
||||||
@@ -281,7 +284,7 @@ pub trait PoolProviderWithGuards: PoolProvider {
|
|||||||
/// This can prevent memory leaks. Users can read the data and release the guard
|
/// This can prevent memory leaks. Users can read the data and release the guard
|
||||||
/// if the data in the store is valid for further processing. If the data is faulty, no
|
/// if the data in the store is valid for further processing. If the data is faulty, no
|
||||||
/// manual deletion is necessary when returning from a processing function prematurely.
|
/// manual deletion is necessary when returning from a processing function prematurely.
|
||||||
fn read_with_guard(&mut self, addr: PoolAddr) -> PoolGuard<Self>;
|
fn read_with_guard(&mut self, addr: PoolAddr) -> PoolGuard<'_, Self>;
|
||||||
|
|
||||||
/// This function behaves like [PoolProvider::modify], but consumes the provided
|
/// This function behaves like [PoolProvider::modify], but consumes the provided
|
||||||
/// address and returns a RAII conformant guard object.
|
/// address and returns a RAII conformant guard object.
|
||||||
@@ -291,7 +294,7 @@ pub trait PoolProviderWithGuards: PoolProvider {
|
|||||||
/// This can prevent memory leaks. Users can read (and modify) the data and release the guard
|
/// This can prevent memory leaks. Users can read (and modify) the data and release the guard
|
||||||
/// if the data in the store is valid for further processing. If the data is faulty, no
|
/// if the data in the store is valid for further processing. If the data is faulty, no
|
||||||
/// manual deletion is necessary when returning from a processing function prematurely.
|
/// manual deletion is necessary when returning from a processing function prematurely.
|
||||||
fn modify_with_guard(&mut self, addr: PoolAddr) -> PoolRwGuard<Self>;
|
fn modify_with_guard(&mut self, addr: PoolAddr) -> PoolRwGuard<'_, Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PoolGuard<'a, MemProvider: PoolProvider + ?Sized> {
|
pub struct PoolGuard<'a, MemProvider: PoolProvider + ?Sized> {
|
||||||
@@ -510,11 +513,11 @@ pub mod heapless_mod {
|
|||||||
///
|
///
|
||||||
/// * `subpool_memory` - Static memory for a particular subpool to store the actual data.
|
/// * `subpool_memory` - Static memory for a particular subpool to store the actual data.
|
||||||
/// * `sizes_list` - Static sizes list structure to store the size of the data which is
|
/// * `sizes_list` - Static sizes list structure to store the size of the data which is
|
||||||
/// actually stored.
|
/// actually stored.
|
||||||
/// * `num_blocks ` - The number of memory blocks inside the subpool.
|
/// * `num_blocks ` - The number of memory blocks inside the subpool.
|
||||||
/// * `set_sizes_list_to_all_free` - If this is set to true, the method will take care
|
/// * `set_sizes_list_to_all_free` - If this is set to true, the method will take care
|
||||||
/// of setting all values in the sizes list to [super::STORE_FREE]. This does not have
|
/// of setting all values in the sizes list to [super::STORE_FREE]. This does not have
|
||||||
/// to be done if the user initializes the sizes list to that value themselves.
|
/// to be done if the user initializes the sizes list to that value themselves.
|
||||||
pub fn grow(
|
pub fn grow(
|
||||||
&mut self,
|
&mut self,
|
||||||
subpool_memory: &'static mut [u8],
|
subpool_memory: &'static mut [u8],
|
||||||
@@ -602,7 +605,7 @@ pub mod heapless_mod {
|
|||||||
if i < start_at_subpool as usize {
|
if i < start_at_subpool as usize {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if pool_cfg.block_size as usize >= req_size {
|
if pool_cfg.block_size >= req_size {
|
||||||
return Ok(i as u16);
|
return Ok(i as u16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -638,7 +641,7 @@ pub mod heapless_mod {
|
|||||||
|
|
||||||
fn raw_pos(&self, addr: &StaticPoolAddr) -> Option<usize> {
|
fn raw_pos(&self, addr: &StaticPoolAddr) -> Option<usize> {
|
||||||
let (pool_cfg, _) = self.pool.get(addr.pool_idx as usize)?;
|
let (pool_cfg, _) = self.pool.get(addr.pool_idx as usize)?;
|
||||||
Some(addr.packet_idx as usize * pool_cfg.block_size as usize)
|
Some(addr.packet_idx as usize * pool_cfg.block_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,13 +710,20 @@ pub mod heapless_mod {
|
|||||||
let subpool_cfg = self.pool.get(addr.pool_idx as usize).unwrap().0;
|
let subpool_cfg = self.pool.get(addr.pool_idx as usize).unwrap().0;
|
||||||
let raw_pos = self.raw_pos(&addr).unwrap();
|
let raw_pos = self.raw_pos(&addr).unwrap();
|
||||||
let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap().1
|
let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap().1
|
||||||
[raw_pos..raw_pos + subpool_cfg.block_size as usize];
|
[raw_pos..raw_pos + subpool_cfg.block_size];
|
||||||
let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap();
|
let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap();
|
||||||
size_list[addr.packet_idx as usize] = STORE_FREE;
|
size_list[addr.packet_idx as usize] = STORE_FREE;
|
||||||
block.fill(0);
|
block.fill(0);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) -> Result<(), PoolError> {
|
||||||
|
for size in self.sizes_lists.iter_mut() {
|
||||||
|
size.fill(STORE_FREE);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn has_element_at(&self, addr: &PoolAddr) -> Result<bool, PoolError> {
|
fn has_element_at(&self, addr: &PoolAddr) -> Result<bool, PoolError> {
|
||||||
let addr = StaticPoolAddr::from(*addr);
|
let addr = StaticPoolAddr::from(*addr);
|
||||||
self.validate_addr(&addr)?;
|
self.validate_addr(&addr)?;
|
||||||
@@ -742,11 +752,11 @@ pub mod heapless_mod {
|
|||||||
impl<const MAX_NUM_SUBPOOLS: usize> PoolProviderWithGuards
|
impl<const MAX_NUM_SUBPOOLS: usize> PoolProviderWithGuards
|
||||||
for StaticHeaplessMemoryPool<MAX_NUM_SUBPOOLS>
|
for StaticHeaplessMemoryPool<MAX_NUM_SUBPOOLS>
|
||||||
{
|
{
|
||||||
fn modify_with_guard(&mut self, addr: PoolAddr) -> PoolRwGuard<Self> {
|
fn modify_with_guard(&mut self, addr: PoolAddr) -> PoolRwGuard<'_, Self> {
|
||||||
PoolRwGuard::new(self, addr)
|
PoolRwGuard::new(self, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_with_guard(&mut self, addr: PoolAddr) -> PoolGuard<Self> {
|
fn read_with_guard(&mut self, addr: PoolAddr) -> PoolGuard<'_, Self> {
|
||||||
PoolGuard::new(self, addr)
|
PoolGuard::new(self, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1055,14 +1065,21 @@ mod alloc_mod {
|
|||||||
_ => size,
|
_ => size,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) -> Result<(), PoolError> {
|
||||||
|
for size in self.sizes_lists.iter_mut() {
|
||||||
|
size.fill(STORE_FREE);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PoolProviderWithGuards for StaticMemoryPool {
|
impl PoolProviderWithGuards for StaticMemoryPool {
|
||||||
fn modify_with_guard(&mut self, addr: PoolAddr) -> PoolRwGuard<Self> {
|
fn modify_with_guard(&mut self, addr: PoolAddr) -> PoolRwGuard<'_, Self> {
|
||||||
PoolRwGuard::new(self, addr)
|
PoolRwGuard::new(self, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_with_guard(&mut self, addr: PoolAddr) -> PoolGuard<Self> {
|
fn read_with_guard(&mut self, addr: PoolAddr) -> PoolGuard<'_, Self> {
|
||||||
PoolGuard::new(self, addr)
|
PoolGuard::new(self, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1307,9 +1324,11 @@ mod tests {
|
|||||||
let addr = pool_provider.add(&test_buf).expect("Adding data failed");
|
let addr = pool_provider.add(&test_buf).expect("Adding data failed");
|
||||||
let read_guard = PoolGuard::new(pool_provider, addr);
|
let read_guard = PoolGuard::new(pool_provider, addr);
|
||||||
drop(read_guard);
|
drop(read_guard);
|
||||||
assert!(!pool_provider
|
assert!(
|
||||||
.has_element_at(&addr)
|
!pool_provider
|
||||||
.expect("Invalid address"));
|
.has_element_at(&addr)
|
||||||
|
.expect("Invalid address")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_test_pool_guard_deletion(pool_provider: &mut impl PoolProviderWithGuards) {
|
fn generic_test_pool_guard_deletion(pool_provider: &mut impl PoolProviderWithGuards) {
|
||||||
@@ -1317,9 +1336,11 @@ mod tests {
|
|||||||
let addr = pool_provider.add(&test_buf).expect("Adding data failed");
|
let addr = pool_provider.add(&test_buf).expect("Adding data failed");
|
||||||
let read_guard = pool_provider.read_with_guard(addr);
|
let read_guard = pool_provider.read_with_guard(addr);
|
||||||
drop(read_guard);
|
drop(read_guard);
|
||||||
assert!(!pool_provider
|
assert!(
|
||||||
.has_element_at(&addr)
|
!pool_provider
|
||||||
.expect("Invalid address"));
|
.has_element_at(&addr)
|
||||||
|
.expect("Invalid address")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_test_pool_guard_with_release(pool_provider: &mut impl PoolProviderWithGuards) {
|
fn generic_test_pool_guard_with_release(pool_provider: &mut impl PoolProviderWithGuards) {
|
||||||
@@ -1328,9 +1349,11 @@ mod tests {
|
|||||||
let mut read_guard = PoolGuard::new(pool_provider, addr);
|
let mut read_guard = PoolGuard::new(pool_provider, addr);
|
||||||
read_guard.release();
|
read_guard.release();
|
||||||
drop(read_guard);
|
drop(read_guard);
|
||||||
assert!(pool_provider
|
assert!(
|
||||||
.has_element_at(&addr)
|
pool_provider
|
||||||
.expect("Invalid address"));
|
.has_element_at(&addr)
|
||||||
|
.expect("Invalid address")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_test_pool_modify_guard_man_creation(
|
fn generic_test_pool_modify_guard_man_creation(
|
||||||
@@ -1341,9 +1364,11 @@ mod tests {
|
|||||||
let mut rw_guard = PoolRwGuard::new(pool_provider, addr);
|
let mut rw_guard = PoolRwGuard::new(pool_provider, addr);
|
||||||
rw_guard.update(&mut |_| {}).expect("modify failed");
|
rw_guard.update(&mut |_| {}).expect("modify failed");
|
||||||
drop(rw_guard);
|
drop(rw_guard);
|
||||||
assert!(!pool_provider
|
assert!(
|
||||||
.has_element_at(&addr)
|
!pool_provider
|
||||||
.expect("Invalid address"));
|
.has_element_at(&addr)
|
||||||
|
.expect("Invalid address")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_test_pool_modify_guard(pool_provider: &mut impl PoolProviderWithGuards) {
|
fn generic_test_pool_modify_guard(pool_provider: &mut impl PoolProviderWithGuards) {
|
||||||
@@ -1352,9 +1377,11 @@ mod tests {
|
|||||||
let mut rw_guard = pool_provider.modify_with_guard(addr);
|
let mut rw_guard = pool_provider.modify_with_guard(addr);
|
||||||
rw_guard.update(&mut |_| {}).expect("modify failed");
|
rw_guard.update(&mut |_| {}).expect("modify failed");
|
||||||
drop(rw_guard);
|
drop(rw_guard);
|
||||||
assert!(!pool_provider
|
assert!(
|
||||||
.has_element_at(&addr)
|
!pool_provider
|
||||||
.expect("Invalid address"));
|
.has_element_at(&addr)
|
||||||
|
.expect("Invalid address")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_modify_pool_index_above_0(pool_provider: &mut impl PoolProvider) {
|
fn generic_modify_pool_index_above_0(pool_provider: &mut impl PoolProvider) {
|
||||||
@@ -1587,228 +1614,243 @@ mod tests {
|
|||||||
mod heapless_tests {
|
mod heapless_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::static_subpool;
|
use crate::static_subpool;
|
||||||
use std::cell::UnsafeCell;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
const SUBPOOL_1_BLOCK_SIZE: usize = 4;
|
const SUBPOOL_1_BLOCK_SIZE: usize = 4;
|
||||||
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
|
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
|
||||||
|
|
||||||
static SUBPOOL_1: static_cell::ConstStaticCell<
|
|
||||||
[u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
|
||||||
> = static_cell::ConstStaticCell::new(
|
|
||||||
[0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
|
||||||
);
|
|
||||||
|
|
||||||
static SUBPOOL_1_SIZES: Mutex<UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>> =
|
|
||||||
Mutex::new(UnsafeCell::new(
|
|
||||||
[STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize],
|
|
||||||
));
|
|
||||||
|
|
||||||
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
|
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
|
||||||
const SUBPOOL_2_BLOCK_SIZE: usize = 8;
|
const SUBPOOL_2_BLOCK_SIZE: usize = 8;
|
||||||
static SUBPOOL_2: static_cell::ConstStaticCell<
|
|
||||||
[u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
|
||||||
> = static_cell::ConstStaticCell::new(
|
|
||||||
[0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
|
||||||
);
|
|
||||||
static SUBPOOL_2_SIZES: static_cell::ConstStaticCell<
|
|
||||||
[usize; SUBPOOL_2_NUM_ELEMENTS as usize],
|
|
||||||
> = static_cell::ConstStaticCell::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
|
|
||||||
|
|
||||||
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
|
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
|
||||||
const SUBPOOL_3_BLOCK_SIZE: usize = 16;
|
const SUBPOOL_3_BLOCK_SIZE: usize = 16;
|
||||||
static_subpool!(
|
|
||||||
SUBPOOL_3,
|
|
||||||
SUBPOOL_3_SIZES,
|
|
||||||
SUBPOOL_3_NUM_ELEMENTS as usize,
|
|
||||||
SUBPOOL_3_BLOCK_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
const SUBPOOL_4_NUM_ELEMENTS: u16 = 2;
|
const SUBPOOL_4_NUM_ELEMENTS: u16 = 2;
|
||||||
const SUBPOOL_4_BLOCK_SIZE: usize = 16;
|
const SUBPOOL_4_BLOCK_SIZE: usize = 16;
|
||||||
static_subpool!(
|
|
||||||
SUBPOOL_4,
|
|
||||||
SUBPOOL_4_SIZES,
|
|
||||||
SUBPOOL_4_NUM_ELEMENTS as usize,
|
|
||||||
SUBPOOL_4_BLOCK_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
const SUBPOOL_5_NUM_ELEMENTS: u16 = 1;
|
const SUBPOOL_5_NUM_ELEMENTS: u16 = 1;
|
||||||
const SUBPOOL_5_BLOCK_SIZE: usize = 8;
|
const SUBPOOL_5_BLOCK_SIZE: usize = 8;
|
||||||
static_subpool!(
|
|
||||||
SUBPOOL_5,
|
|
||||||
SUBPOOL_5_SIZES,
|
|
||||||
SUBPOOL_5_NUM_ELEMENTS as usize,
|
|
||||||
SUBPOOL_5_BLOCK_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
const SUBPOOL_6_NUM_ELEMENTS: u16 = 1;
|
const SUBPOOL_6_NUM_ELEMENTS: u16 = 1;
|
||||||
const SUBPOOL_6_BLOCK_SIZE: usize = 12;
|
const SUBPOOL_6_BLOCK_SIZE: usize = 12;
|
||||||
static_subpool!(
|
|
||||||
SUBPOOL_6,
|
|
||||||
SUBPOOL_6_SIZES,
|
|
||||||
SUBPOOL_6_NUM_ELEMENTS as usize,
|
|
||||||
SUBPOOL_6_BLOCK_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
fn small_heapless_pool() -> StaticHeaplessMemoryPool<3> {
|
macro_rules! make_heapless_pool {
|
||||||
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
|
($prefix:ident) => {{
|
||||||
StaticHeaplessMemoryPool::new(false);
|
paste::paste! {
|
||||||
assert!(heapless_pool
|
static [<$prefix _SUBPOOL_1>]: static_cell::ConstStaticCell<
|
||||||
.grow(
|
[u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
||||||
SUBPOOL_1.take(),
|
> = static_cell::ConstStaticCell::new(
|
||||||
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
|
[0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
||||||
SUBPOOL_1_NUM_ELEMENTS,
|
);
|
||||||
true
|
|
||||||
)
|
static [<$prefix _SUBPOOL_1_SIZES>]: std::sync::Mutex<
|
||||||
.is_ok());
|
std::cell::UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>
|
||||||
assert!(heapless_pool
|
> = std::sync::Mutex::new(
|
||||||
.grow(
|
std::cell::UnsafeCell::new([STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize])
|
||||||
SUBPOOL_2.take(),
|
);
|
||||||
SUBPOOL_2_SIZES.take(),
|
|
||||||
SUBPOOL_2_NUM_ELEMENTS,
|
static [<$prefix _SUBPOOL_2>]: static_cell::ConstStaticCell<
|
||||||
true
|
[u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
||||||
)
|
> = static_cell::ConstStaticCell::new(
|
||||||
.is_ok());
|
[0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
||||||
assert!(heapless_pool
|
);
|
||||||
.grow(
|
|
||||||
SUBPOOL_3.take(),
|
static [<$prefix _SUBPOOL_2_SIZES>]: static_cell::ConstStaticCell<
|
||||||
SUBPOOL_3_SIZES.take(),
|
[usize; SUBPOOL_2_NUM_ELEMENTS as usize],
|
||||||
SUBPOOL_3_NUM_ELEMENTS,
|
> = static_cell::ConstStaticCell::new(
|
||||||
true
|
[STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]
|
||||||
)
|
);
|
||||||
.is_ok());
|
|
||||||
heapless_pool
|
static [<$prefix _SUBPOOL_3>]: static_cell::ConstStaticCell<
|
||||||
|
[u8; SUBPOOL_3_NUM_ELEMENTS as usize * SUBPOOL_3_BLOCK_SIZE],
|
||||||
|
> = static_cell::ConstStaticCell::new(
|
||||||
|
[0; SUBPOOL_3_NUM_ELEMENTS as usize * SUBPOOL_3_BLOCK_SIZE],
|
||||||
|
);
|
||||||
|
|
||||||
|
static [<$prefix _SUBPOOL_3_SIZES>]: static_cell::ConstStaticCell<
|
||||||
|
[usize; SUBPOOL_3_NUM_ELEMENTS as usize],
|
||||||
|
> = static_cell::ConstStaticCell::new(
|
||||||
|
[STORE_FREE; SUBPOOL_3_NUM_ELEMENTS as usize]
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
|
||||||
|
StaticHeaplessMemoryPool::new(false);
|
||||||
|
|
||||||
|
heapless_pool
|
||||||
|
.grow(
|
||||||
|
[<$prefix _SUBPOOL_1>].take(),
|
||||||
|
unsafe { &mut *[<$prefix _SUBPOOL_1_SIZES>].lock().unwrap().get() },
|
||||||
|
SUBPOOL_1_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
heapless_pool
|
||||||
|
.grow(
|
||||||
|
[<$prefix _SUBPOOL_2>].take(),
|
||||||
|
[<$prefix _SUBPOOL_2_SIZES>].take(),
|
||||||
|
SUBPOOL_2_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
heapless_pool
|
||||||
|
.grow(
|
||||||
|
[<$prefix _SUBPOOL_3>].take(),
|
||||||
|
[<$prefix _SUBPOOL_3_SIZES>].take(),
|
||||||
|
SUBPOOL_3_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
heapless_pool
|
||||||
|
}
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_heapless_add_and_read() {
|
fn test_heapless_add_and_read() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T0);
|
||||||
generic_test_add_and_read::<16>(&mut pool_provider);
|
generic_test_add_and_read::<16>(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_smaller_than_full_slot() {
|
fn test_add_smaller_than_full_slot() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T1);
|
||||||
generic_test_add_smaller_than_full_slot(&mut pool_provider);
|
generic_test_add_smaller_than_full_slot(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete() {
|
fn test_delete() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T2);
|
||||||
generic_test_delete(&mut pool_provider);
|
generic_test_delete(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_modify() {
|
fn test_modify() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T3);
|
||||||
generic_test_modify(&mut pool_provider);
|
generic_test_modify(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_consecutive_reservation() {
|
fn test_consecutive_reservation() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T4);
|
||||||
generic_test_consecutive_reservation(&mut pool_provider);
|
generic_test_consecutive_reservation(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_does_not_exist() {
|
fn test_read_does_not_exist() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T5);
|
||||||
generic_test_read_does_not_exist(&mut pool_provider);
|
generic_test_read_does_not_exist(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_store_full() {
|
fn test_store_full() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T6);
|
||||||
generic_test_store_full(&mut pool_provider);
|
generic_test_store_full(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_pool_idx() {
|
fn test_invalid_pool_idx() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T7);
|
||||||
generic_test_invalid_pool_idx(&mut pool_provider);
|
generic_test_invalid_pool_idx(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_packet_idx() {
|
fn test_invalid_packet_idx() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T8);
|
||||||
generic_test_invalid_packet_idx(&mut pool_provider);
|
generic_test_invalid_packet_idx(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_too_large() {
|
fn test_add_too_large() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T9);
|
||||||
generic_test_add_too_large(&mut pool_provider);
|
generic_test_add_too_large(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_data_too_large_1() {
|
fn test_data_too_large_1() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T10);
|
||||||
generic_test_data_too_large_1(&mut pool_provider);
|
generic_test_data_too_large_1(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_free_element_too_large() {
|
fn test_free_element_too_large() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T11);
|
||||||
generic_test_free_element_too_large(&mut pool_provider);
|
generic_test_free_element_too_large(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_guard_deletion_man_creation() {
|
fn test_pool_guard_deletion_man_creation() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T12);
|
||||||
generic_test_pool_guard_deletion_man_creation(&mut pool_provider);
|
generic_test_pool_guard_deletion_man_creation(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_guard_deletion() {
|
fn test_pool_guard_deletion() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T13);
|
||||||
generic_test_pool_guard_deletion(&mut pool_provider);
|
generic_test_pool_guard_deletion(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_guard_with_release() {
|
fn test_pool_guard_with_release() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T14);
|
||||||
generic_test_pool_guard_with_release(&mut pool_provider);
|
generic_test_pool_guard_with_release(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_modify_guard_man_creation() {
|
fn test_pool_modify_guard_man_creation() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T15);
|
||||||
generic_test_pool_modify_guard_man_creation(&mut pool_provider);
|
generic_test_pool_modify_guard_man_creation(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_modify_guard() {
|
fn test_pool_modify_guard() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T16);
|
||||||
generic_test_pool_modify_guard(&mut pool_provider);
|
generic_test_pool_modify_guard(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn modify_pool_index_above_0() {
|
fn modify_pool_index_above_0() {
|
||||||
let mut pool_provider = small_heapless_pool();
|
let mut pool_provider = make_heapless_pool!(T17);
|
||||||
generic_modify_pool_index_above_0(&mut pool_provider);
|
generic_modify_pool_index_above_0(&mut pool_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_spills_to_higher_subpools() {
|
fn test_spills_to_higher_subpools() {
|
||||||
|
static_subpool!(
|
||||||
|
SUBPOOL_2_T18,
|
||||||
|
SUBPOOL_2_SIZES_T18,
|
||||||
|
SUBPOOL_2_NUM_ELEMENTS as usize,
|
||||||
|
SUBPOOL_2_BLOCK_SIZE
|
||||||
|
);
|
||||||
|
static_subpool!(
|
||||||
|
SUBPOOL_4_T18,
|
||||||
|
SUBPOOL_4_SIZES_T18,
|
||||||
|
SUBPOOL_4_NUM_ELEMENTS as usize,
|
||||||
|
SUBPOOL_4_BLOCK_SIZE
|
||||||
|
);
|
||||||
let mut heapless_pool: StaticHeaplessMemoryPool<2> =
|
let mut heapless_pool: StaticHeaplessMemoryPool<2> =
|
||||||
StaticHeaplessMemoryPool::new(true);
|
StaticHeaplessMemoryPool::new(true);
|
||||||
assert!(heapless_pool
|
assert!(
|
||||||
.grow(
|
heapless_pool
|
||||||
SUBPOOL_2.take(),
|
.grow(
|
||||||
SUBPOOL_2_SIZES.take(),
|
SUBPOOL_2_T18.take(),
|
||||||
SUBPOOL_2_NUM_ELEMENTS,
|
SUBPOOL_2_SIZES_T18.take(),
|
||||||
true
|
SUBPOOL_2_NUM_ELEMENTS,
|
||||||
)
|
true
|
||||||
.is_ok());
|
)
|
||||||
assert!(heapless_pool
|
.is_ok()
|
||||||
.grow(
|
);
|
||||||
SUBPOOL_4.take(),
|
assert!(
|
||||||
SUBPOOL_4_SIZES.take(),
|
heapless_pool
|
||||||
SUBPOOL_4_NUM_ELEMENTS,
|
.grow(
|
||||||
true
|
SUBPOOL_4_T18.take(),
|
||||||
)
|
SUBPOOL_4_SIZES_T18.take(),
|
||||||
.is_ok());
|
SUBPOOL_4_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
generic_test_spills_to_higher_subpools(&mut heapless_pool);
|
generic_test_spills_to_higher_subpools(&mut heapless_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1816,22 +1858,38 @@ mod tests {
|
|||||||
fn test_spillage_fails_as_well() {
|
fn test_spillage_fails_as_well() {
|
||||||
let mut heapless_pool: StaticHeaplessMemoryPool<2> =
|
let mut heapless_pool: StaticHeaplessMemoryPool<2> =
|
||||||
StaticHeaplessMemoryPool::new(true);
|
StaticHeaplessMemoryPool::new(true);
|
||||||
assert!(heapless_pool
|
static_subpool!(
|
||||||
.grow(
|
SUBPOOL_5,
|
||||||
SUBPOOL_5.take(),
|
SUBPOOL_5_SIZES,
|
||||||
SUBPOOL_5_SIZES.take(),
|
SUBPOOL_5_NUM_ELEMENTS as usize,
|
||||||
SUBPOOL_5_NUM_ELEMENTS,
|
SUBPOOL_5_BLOCK_SIZE
|
||||||
true
|
);
|
||||||
)
|
static_subpool!(
|
||||||
.is_ok());
|
SUBPOOL_3,
|
||||||
assert!(heapless_pool
|
SUBPOOL_3_SIZES,
|
||||||
.grow(
|
SUBPOOL_3_NUM_ELEMENTS as usize,
|
||||||
SUBPOOL_3.take(),
|
SUBPOOL_3_BLOCK_SIZE
|
||||||
SUBPOOL_3_SIZES.take(),
|
);
|
||||||
SUBPOOL_3_NUM_ELEMENTS,
|
assert!(
|
||||||
true
|
heapless_pool
|
||||||
)
|
.grow(
|
||||||
.is_ok());
|
SUBPOOL_5.take(),
|
||||||
|
SUBPOOL_5_SIZES.take(),
|
||||||
|
SUBPOOL_5_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
heapless_pool
|
||||||
|
.grow(
|
||||||
|
SUBPOOL_3.take(),
|
||||||
|
SUBPOOL_3_SIZES.take(),
|
||||||
|
SUBPOOL_3_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
generic_test_spillage_fails_as_well(&mut heapless_pool);
|
generic_test_spillage_fails_as_well(&mut heapless_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1839,61 +1897,109 @@ mod tests {
|
|||||||
fn test_spillage_works_across_multiple_subpools() {
|
fn test_spillage_works_across_multiple_subpools() {
|
||||||
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
|
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
|
||||||
StaticHeaplessMemoryPool::new(true);
|
StaticHeaplessMemoryPool::new(true);
|
||||||
assert!(heapless_pool
|
static_subpool!(
|
||||||
.grow(
|
SUBPOOL_3,
|
||||||
SUBPOOL_5.take(),
|
SUBPOOL_3_SIZES,
|
||||||
SUBPOOL_5_SIZES.take(),
|
SUBPOOL_3_NUM_ELEMENTS as usize,
|
||||||
SUBPOOL_5_NUM_ELEMENTS,
|
SUBPOOL_3_BLOCK_SIZE
|
||||||
true
|
);
|
||||||
)
|
static_subpool!(
|
||||||
.is_ok());
|
SUBPOOL_5,
|
||||||
assert!(heapless_pool
|
SUBPOOL_5_SIZES,
|
||||||
.grow(
|
SUBPOOL_5_NUM_ELEMENTS as usize,
|
||||||
SUBPOOL_6.take(),
|
SUBPOOL_5_BLOCK_SIZE
|
||||||
SUBPOOL_6_SIZES.take(),
|
);
|
||||||
SUBPOOL_6_NUM_ELEMENTS,
|
static_subpool!(
|
||||||
true
|
SUBPOOL_6,
|
||||||
)
|
SUBPOOL_6_SIZES,
|
||||||
.is_ok());
|
SUBPOOL_6_NUM_ELEMENTS as usize,
|
||||||
assert!(heapless_pool
|
SUBPOOL_6_BLOCK_SIZE
|
||||||
.grow(
|
);
|
||||||
SUBPOOL_3.take(),
|
assert!(
|
||||||
SUBPOOL_3_SIZES.take(),
|
heapless_pool
|
||||||
SUBPOOL_3_NUM_ELEMENTS,
|
.grow(
|
||||||
true
|
SUBPOOL_5.take(),
|
||||||
)
|
SUBPOOL_5_SIZES.take(),
|
||||||
.is_ok());
|
SUBPOOL_5_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
heapless_pool
|
||||||
|
.grow(
|
||||||
|
SUBPOOL_6.take(),
|
||||||
|
SUBPOOL_6_SIZES.take(),
|
||||||
|
SUBPOOL_6_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
heapless_pool
|
||||||
|
.grow(
|
||||||
|
SUBPOOL_3.take(),
|
||||||
|
SUBPOOL_3_SIZES.take(),
|
||||||
|
SUBPOOL_3_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
generic_test_spillage_works_across_multiple_subpools(&mut heapless_pool);
|
generic_test_spillage_works_across_multiple_subpools(&mut heapless_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_spillage_fails_across_multiple_subpools() {
|
fn test_spillage_fails_across_multiple_subpools() {
|
||||||
|
static_subpool!(
|
||||||
|
SUBPOOL_3,
|
||||||
|
SUBPOOL_3_SIZES,
|
||||||
|
SUBPOOL_3_NUM_ELEMENTS as usize,
|
||||||
|
SUBPOOL_3_BLOCK_SIZE
|
||||||
|
);
|
||||||
|
static_subpool!(
|
||||||
|
SUBPOOL_5,
|
||||||
|
SUBPOOL_5_SIZES,
|
||||||
|
SUBPOOL_5_NUM_ELEMENTS as usize,
|
||||||
|
SUBPOOL_5_BLOCK_SIZE
|
||||||
|
);
|
||||||
|
static_subpool!(
|
||||||
|
SUBPOOL_6,
|
||||||
|
SUBPOOL_6_SIZES,
|
||||||
|
SUBPOOL_6_NUM_ELEMENTS as usize,
|
||||||
|
SUBPOOL_6_BLOCK_SIZE
|
||||||
|
);
|
||||||
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
|
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
|
||||||
StaticHeaplessMemoryPool::new(true);
|
StaticHeaplessMemoryPool::new(true);
|
||||||
assert!(heapless_pool
|
assert!(
|
||||||
.grow(
|
heapless_pool
|
||||||
SUBPOOL_5.take(),
|
.grow(
|
||||||
SUBPOOL_5_SIZES.take(),
|
SUBPOOL_5.take(),
|
||||||
SUBPOOL_5_NUM_ELEMENTS,
|
SUBPOOL_5_SIZES.take(),
|
||||||
true
|
SUBPOOL_5_NUM_ELEMENTS,
|
||||||
)
|
true
|
||||||
.is_ok());
|
)
|
||||||
assert!(heapless_pool
|
.is_ok()
|
||||||
.grow(
|
);
|
||||||
SUBPOOL_6.take(),
|
assert!(
|
||||||
SUBPOOL_6_SIZES.take(),
|
heapless_pool
|
||||||
SUBPOOL_6_NUM_ELEMENTS,
|
.grow(
|
||||||
true
|
SUBPOOL_6.take(),
|
||||||
)
|
SUBPOOL_6_SIZES.take(),
|
||||||
.is_ok());
|
SUBPOOL_6_NUM_ELEMENTS,
|
||||||
assert!(heapless_pool
|
true
|
||||||
.grow(
|
)
|
||||||
SUBPOOL_3.take(),
|
.is_ok()
|
||||||
SUBPOOL_3_SIZES.take(),
|
);
|
||||||
SUBPOOL_3_NUM_ELEMENTS,
|
assert!(
|
||||||
true
|
heapless_pool
|
||||||
)
|
.grow(
|
||||||
.is_ok());
|
SUBPOOL_3.take(),
|
||||||
|
SUBPOOL_3_SIZES.take(),
|
||||||
|
SUBPOOL_3_NUM_ELEMENTS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
generic_test_spillage_fails_across_multiple_subpools(&mut heapless_pool);
|
generic_test_spillage_fails_across_multiple_subpools(&mut heapless_pool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ mod tests {
|
|||||||
|
|
||||||
use std::sync::mpsc::{self, TryRecvError};
|
use std::sync::mpsc::{self, TryRecvError};
|
||||||
|
|
||||||
use crate::{queue::GenericSendError, request::GenericMessage, ComponentId};
|
use crate::{ComponentId, queue::GenericSendError, request::GenericMessage};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@@ -65,13 +65,13 @@ impl GenericActionReplyPus {
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub mod alloc_mod {
|
pub mod alloc_mod {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
action::ActionRequest,
|
action::ActionRequest,
|
||||||
queue::{GenericReceiveError, GenericSendError},
|
queue::{GenericReceiveError, GenericSendError},
|
||||||
request::{
|
request::{
|
||||||
GenericMessage, MessageReceiverProvider, MessageSenderAndReceiver,
|
GenericMessage, MessageReceiverProvider, MessageSenderAndReceiver,
|
||||||
MessageSenderProvider, MessageSenderStoreProvider, RequestId,
|
MessageSenderProvider, MessageSenderStoreProvider, RequestId,
|
||||||
},
|
},
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ActionReplyPus;
|
use super::ActionReplyPus;
|
||||||
@@ -81,10 +81,10 @@ pub mod alloc_mod {
|
|||||||
MessageSenderAndReceiver<ActionReplyPus, ActionRequest, Sender, Receiver, ReplySenderStore>;
|
MessageSenderAndReceiver<ActionReplyPus, ActionRequest, Sender, Receiver, ReplySenderStore>;
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: MessageSenderProvider<ActionReplyPus>,
|
Sender: MessageSenderProvider<ActionReplyPus>,
|
||||||
Receiver: MessageReceiverProvider<ActionRequest>,
|
Receiver: MessageReceiverProvider<ActionRequest>,
|
||||||
ReplySender: MessageSenderStoreProvider<ActionReplyPus, Sender>,
|
ReplySender: MessageSenderStoreProvider<ActionReplyPus, Sender>,
|
||||||
> ActionRequestHandlerInterface<Sender, Receiver, ReplySender>
|
> ActionRequestHandlerInterface<Sender, Receiver, ReplySender>
|
||||||
{
|
{
|
||||||
pub fn try_recv_action_request(
|
pub fn try_recv_action_request(
|
||||||
&self,
|
&self,
|
||||||
@@ -114,10 +114,10 @@ pub mod alloc_mod {
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: MessageSenderProvider<ActionRequest>,
|
Sender: MessageSenderProvider<ActionRequest>,
|
||||||
Receiver: MessageReceiverProvider<ActionReplyPus>,
|
Receiver: MessageReceiverProvider<ActionReplyPus>,
|
||||||
RequestSenderStore: MessageSenderStoreProvider<ActionRequest, Sender>,
|
RequestSenderStore: MessageSenderStoreProvider<ActionRequest, Sender>,
|
||||||
> ActionRequestorInterface<Sender, Receiver, RequestSenderStore>
|
> ActionRequestorInterface<Sender, Receiver, RequestSenderStore>
|
||||||
{
|
{
|
||||||
pub fn try_recv_action_reply(
|
pub fn try_recv_action_reply(
|
||||||
&self,
|
&self,
|
||||||
@@ -141,12 +141,12 @@ pub mod std_mod {
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
pus::{
|
pus::{
|
||||||
|
ActivePusRequestStd, ActiveRequest, DefaultActiveRequestMap,
|
||||||
verification::{self, TcStateToken},
|
verification::{self, TcStateToken},
|
||||||
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap,
|
|
||||||
},
|
},
|
||||||
request::{MessageSenderMap, OneMessageSender},
|
request::{MessageSenderMap, OneMessageSender},
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -157,7 +157,7 @@ pub mod std_mod {
|
|||||||
common: ActivePusRequestStd,
|
common: ActivePusRequestStd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveRequestProvider for ActivePusActionRequestStd {
|
impl ActiveRequest for ActivePusActionRequestStd {
|
||||||
delegate::delegate! {
|
delegate::delegate! {
|
||||||
to self.common {
|
to self.common {
|
||||||
fn target_id(&self) -> ComponentId;
|
fn target_id(&self) -> ComponentId;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
use crate::pus::source_buffer_large_enough;
|
use crate::pus::source_buffer_large_enough;
|
||||||
|
use arbitrary_int::u11;
|
||||||
|
use spacepackets::ByteConversionError;
|
||||||
|
use spacepackets::SpHeader;
|
||||||
|
use spacepackets::ecss::CreatorConfig;
|
||||||
|
use spacepackets::ecss::EcssEnumeration;
|
||||||
|
use spacepackets::ecss::MessageTypeId;
|
||||||
use spacepackets::ecss::tm::PusTmCreator;
|
use spacepackets::ecss::tm::PusTmCreator;
|
||||||
use spacepackets::ecss::tm::PusTmSecondaryHeader;
|
use spacepackets::ecss::tm::PusTmSecondaryHeader;
|
||||||
use spacepackets::ecss::EcssEnumeration;
|
|
||||||
use spacepackets::ByteConversionError;
|
|
||||||
use spacepackets::{SpHeader, MAX_APID};
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
@@ -11,16 +14,13 @@ pub use alloc_mod::*;
|
|||||||
pub use spacepackets::ecss::event::*;
|
pub use spacepackets::ecss::event::*;
|
||||||
|
|
||||||
pub struct EventReportCreator {
|
pub struct EventReportCreator {
|
||||||
apid: u16,
|
apid: u11,
|
||||||
pub dest_id: u16,
|
pub dest_id: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventReportCreator {
|
impl EventReportCreator {
|
||||||
pub fn new(apid: u16, dest_id: u16) -> Option<Self> {
|
pub fn new(apid: u11, dest_id: u16) -> Self {
|
||||||
if apid > MAX_APID {
|
Self { dest_id, apid }
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Self { dest_id, apid })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn event_info<'time, 'src_data>(
|
pub fn event_info<'time, 'src_data>(
|
||||||
@@ -31,7 +31,7 @@ impl EventReportCreator {
|
|||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.generate_and_send_generic_tm(
|
self.generate_and_send_generic_tm(
|
||||||
Subservice::TmInfoReport,
|
MessageSubtypeId::TmInfoReport,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
event_id,
|
event_id,
|
||||||
params,
|
params,
|
||||||
@@ -47,7 +47,7 @@ impl EventReportCreator {
|
|||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.generate_and_send_generic_tm(
|
self.generate_and_send_generic_tm(
|
||||||
Subservice::TmLowSeverityReport,
|
MessageSubtypeId::TmLowSeverityReport,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
event_id,
|
event_id,
|
||||||
params,
|
params,
|
||||||
@@ -63,7 +63,7 @@ impl EventReportCreator {
|
|||||||
buf: &'src_data mut [u8],
|
buf: &'src_data mut [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.generate_and_send_generic_tm(
|
self.generate_and_send_generic_tm(
|
||||||
Subservice::TmMediumSeverityReport,
|
MessageSubtypeId::TmMediumSeverityReport,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
event_id,
|
event_id,
|
||||||
params,
|
params,
|
||||||
@@ -79,7 +79,7 @@ impl EventReportCreator {
|
|||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.generate_and_send_generic_tm(
|
self.generate_and_send_generic_tm(
|
||||||
Subservice::TmHighSeverityReport,
|
MessageSubtypeId::TmHighSeverityReport,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
event_id,
|
event_id,
|
||||||
params,
|
params,
|
||||||
@@ -89,7 +89,7 @@ impl EventReportCreator {
|
|||||||
|
|
||||||
fn generate_and_send_generic_tm<'time, 'src_data>(
|
fn generate_and_send_generic_tm<'time, 'src_data>(
|
||||||
&self,
|
&self,
|
||||||
subservice: Subservice,
|
subservice: MessageSubtypeId,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
event_id: impl EcssEnumeration,
|
event_id: impl EcssEnumeration,
|
||||||
params: Option<&'src_data [u8]>,
|
params: Option<&'src_data [u8]>,
|
||||||
@@ -100,7 +100,7 @@ impl EventReportCreator {
|
|||||||
|
|
||||||
fn generate_generic_event_tm<'time, 'src_data>(
|
fn generate_generic_event_tm<'time, 'src_data>(
|
||||||
&self,
|
&self,
|
||||||
subservice: Subservice,
|
subservice: MessageSubtypeId,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
event_id: impl EcssEnumeration,
|
event_id: impl EcssEnumeration,
|
||||||
params: Option<&'src_data [u8]>,
|
params: Option<&'src_data [u8]>,
|
||||||
@@ -111,8 +111,12 @@ impl EventReportCreator {
|
|||||||
src_data_len += aux_data.len();
|
src_data_len += aux_data.len();
|
||||||
}
|
}
|
||||||
source_buffer_large_enough(src_data_buf.len(), src_data_len)?;
|
source_buffer_large_enough(src_data_buf.len(), src_data_len)?;
|
||||||
let sec_header =
|
let sec_header = PusTmSecondaryHeader::new(
|
||||||
PusTmSecondaryHeader::new(5, subservice.into(), 0, self.dest_id, time_stamp);
|
MessageTypeId::new(5, subservice.into()),
|
||||||
|
0,
|
||||||
|
self.dest_id,
|
||||||
|
time_stamp,
|
||||||
|
);
|
||||||
let mut current_idx = 0;
|
let mut current_idx = 0;
|
||||||
event_id.write_to_be_bytes(&mut src_data_buf[0..event_id.size()])?;
|
event_id.write_to_be_bytes(&mut src_data_buf[0..event_id.size()])?;
|
||||||
current_idx += event_id.size();
|
current_idx += event_id.size();
|
||||||
@@ -124,7 +128,7 @@ impl EventReportCreator {
|
|||||||
SpHeader::new_from_apid(self.apid),
|
SpHeader::new_from_apid(self.apid),
|
||||||
sec_header,
|
sec_header,
|
||||||
&src_data_buf[0..current_idx],
|
&src_data_buf[0..current_idx],
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,64 +136,64 @@ impl EventReportCreator {
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
mod alloc_mod {
|
mod alloc_mod {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::pus::{EcssTmSender, EcssTmtcError};
|
|
||||||
use crate::ComponentId;
|
use crate::ComponentId;
|
||||||
|
use crate::pus::{EcssTmSender, EcssTmtcError};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use spacepackets::ecss::PusError;
|
use spacepackets::ecss::PusError;
|
||||||
|
|
||||||
pub trait EventTmHookProvider {
|
pub trait EventTmHook {
|
||||||
fn modify_tm(&self, tm: &mut PusTmCreator);
|
fn modify_tm(&self, tm: &mut PusTmCreator);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct DummyEventHook {}
|
pub struct DummyEventHook {}
|
||||||
|
|
||||||
impl EventTmHookProvider for DummyEventHook {
|
impl EventTmHook for DummyEventHook {
|
||||||
fn modify_tm(&self, _tm: &mut PusTmCreator) {}
|
fn modify_tm(&self, _tm: &mut PusTmCreator) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventReporter<EventTmHook: EventTmHookProvider = DummyEventHook> {
|
pub struct EventReporter<EventTmHookInstance: EventTmHook = DummyEventHook> {
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
// Use interior mutability pattern here. This is just an intermediate buffer to the PUS event packet
|
// Use interior mutability pattern here. This is just an intermediate buffer to the PUS event packet
|
||||||
// generation.
|
// generation.
|
||||||
source_data_buf: RefCell<Vec<u8>>,
|
source_data_buf: RefCell<Vec<u8>>,
|
||||||
pub report_creator: EventReportCreator,
|
pub report_creator: EventReportCreator,
|
||||||
pub tm_hook: EventTmHook,
|
pub tm_hook: EventTmHookInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventReporter<DummyEventHook> {
|
impl EventReporter<DummyEventHook> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
default_apid: u16,
|
default_apid: u11,
|
||||||
default_dest_id: u16,
|
default_dest_id: u16,
|
||||||
max_event_id_and_aux_data_size: usize,
|
max_event_id_and_aux_data_size: usize,
|
||||||
) -> Option<Self> {
|
) -> Self {
|
||||||
let reporter = EventReportCreator::new(default_apid, default_dest_id)?;
|
let reporter = EventReportCreator::new(default_apid, default_dest_id);
|
||||||
Some(Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
|
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
|
||||||
report_creator: reporter,
|
report_creator: reporter,
|
||||||
tm_hook: DummyEventHook::default(),
|
tm_hook: DummyEventHook::default(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<EventTmHook: EventTmHookProvider> EventReporter<EventTmHook> {
|
impl<EventTmHookInstance: EventTmHook> EventReporter<EventTmHookInstance> {
|
||||||
pub fn new_with_hook(
|
pub fn new_with_hook(
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
default_apid: u16,
|
default_apid: u11,
|
||||||
default_dest_id: u16,
|
default_dest_id: u16,
|
||||||
max_event_id_and_aux_data_size: usize,
|
max_event_id_and_aux_data_size: usize,
|
||||||
tm_hook: EventTmHook,
|
tm_hook: EventTmHookInstance,
|
||||||
) -> Option<Self> {
|
) -> Self {
|
||||||
let reporter = EventReportCreator::new(default_apid, default_dest_id)?;
|
let reporter = EventReportCreator::new(default_apid, default_dest_id);
|
||||||
Some(Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
|
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
|
||||||
report_creator: reporter,
|
report_creator: reporter,
|
||||||
tm_hook,
|
tm_hook,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn event_info(
|
pub fn event_info(
|
||||||
@@ -265,18 +269,18 @@ mod alloc_mod {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::events::{EventU32, Severity};
|
use crate::ComponentId;
|
||||||
|
use crate::events_legacy::{EventU32, Severity};
|
||||||
use crate::pus::test_util::TEST_COMPONENT_ID_0;
|
use crate::pus::test_util::TEST_COMPONENT_ID_0;
|
||||||
use crate::pus::tests::CommonTmInfo;
|
use crate::pus::tests::CommonTmInfo;
|
||||||
use crate::pus::{ChannelWithId, EcssTmSender, EcssTmtcError, PusTmVariant};
|
use crate::pus::{ChannelWithId, EcssTmSender, EcssTmtcError, PusTmVariant};
|
||||||
use crate::ComponentId;
|
|
||||||
use spacepackets::ecss::PusError;
|
|
||||||
use spacepackets::ByteConversionError;
|
use spacepackets::ByteConversionError;
|
||||||
|
use spacepackets::ecss::PusError;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
const EXAMPLE_APID: u16 = 0xee;
|
const EXAMPLE_APID: u11 = u11::new(0xee);
|
||||||
const EXAMPLE_GROUP_ID: u16 = 2;
|
const EXAMPLE_GROUP_ID: u16 = 2;
|
||||||
const EXAMPLE_EVENT_ID_0: u16 = 1;
|
const EXAMPLE_EVENT_ID_0: u16 = 1;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -329,12 +333,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn severity_to_subservice(severity: Severity) -> Subservice {
|
fn severity_to_subservice(severity: Severity) -> MessageSubtypeId {
|
||||||
match severity {
|
match severity {
|
||||||
Severity::Info => Subservice::TmInfoReport,
|
Severity::Info => MessageSubtypeId::TmInfoReport,
|
||||||
Severity::Low => Subservice::TmLowSeverityReport,
|
Severity::Low => MessageSubtypeId::TmLowSeverityReport,
|
||||||
Severity::Medium => Subservice::TmMediumSeverityReport,
|
Severity::Medium => MessageSubtypeId::TmMediumSeverityReport,
|
||||||
Severity::High => Subservice::TmHighSeverityReport,
|
Severity::High => MessageSubtypeId::TmHighSeverityReport,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,14 +380,12 @@ mod tests {
|
|||||||
error_data: Option<&[u8]>,
|
error_data: Option<&[u8]>,
|
||||||
) {
|
) {
|
||||||
let mut sender = TestSender::default();
|
let mut sender = TestSender::default();
|
||||||
let reporter = EventReporter::new(
|
let mut reporter = EventReporter::new(
|
||||||
TEST_COMPONENT_ID_0.id(),
|
TEST_COMPONENT_ID_0.id(),
|
||||||
EXAMPLE_APID,
|
EXAMPLE_APID,
|
||||||
0,
|
0,
|
||||||
max_event_aux_data_buf,
|
max_event_aux_data_buf,
|
||||||
);
|
);
|
||||||
assert!(reporter.is_some());
|
|
||||||
let mut reporter = reporter.unwrap();
|
|
||||||
let time_stamp_empty: [u8; 7] = [0; 7];
|
let time_stamp_empty: [u8; 7] = [0; 7];
|
||||||
let mut error_copy = Vec::new();
|
let mut error_copy = Vec::new();
|
||||||
if let Some(err_data) = error_data {
|
if let Some(err_data) = error_data {
|
||||||
@@ -474,9 +476,7 @@ mod tests {
|
|||||||
fn insufficient_buffer() {
|
fn insufficient_buffer() {
|
||||||
let mut sender = TestSender::default();
|
let mut sender = TestSender::default();
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
let reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
|
let mut reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
|
||||||
assert!(reporter.is_some());
|
|
||||||
let mut reporter = reporter.unwrap();
|
|
||||||
check_buf_too_small(&mut reporter, &mut sender, i);
|
check_buf_too_small(&mut reporter, &mut sender, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
use crate::events::{EventU32, GenericEvent, Severity};
|
use crate::events_legacy::{EventU32, GenericEvent, Severity};
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use crate::events::{EventU32TypedSev, HasSeverity};
|
use crate::events_legacy::{EventU32TypedSev, HasSeverity};
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
pub use crate::pus::event::EventReporter;
|
|
||||||
use crate::pus::verification::TcStateToken;
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use crate::pus::EcssTmSender;
|
use crate::pus::EcssTmSender;
|
||||||
use crate::pus::EcssTmtcError;
|
use crate::pus::EcssTmtcError;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
pub use crate::pus::event::EventReporter;
|
||||||
|
use crate::pus::verification::TcStateToken;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
#[cfg(feature = "heapless")]
|
#[cfg(feature = "heapless")]
|
||||||
pub use heapless_mod::*;
|
pub use heapless_mod::*;
|
||||||
@@ -46,7 +46,7 @@ pub mod heapless_mod {
|
|||||||
// regular Event type again.
|
// regular Event type again.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct HeaplessPusMgmtBackendProvider<const N: usize, Provider: GenericEvent> {
|
pub struct HeaplessPusMgmtBackendProvider<const N: usize, Provider: GenericEvent> {
|
||||||
disabled: heapless::FnvIndexSet<LargestEventRaw, N>,
|
disabled: heapless::index_set::FnvIndexSet<LargestEventRaw, N>,
|
||||||
phantom: PhantomData<Provider>,
|
phantom: PhantomData<Provider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +100,9 @@ pub mod alloc_mod {
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::EventU16,
|
events_legacy::EventU16,
|
||||||
params::{Params, WritableToBeBytes},
|
params::{Params, WritableToBeBytes},
|
||||||
pus::event::{DummyEventHook, EventTmHookProvider},
|
pus::event::{DummyEventHook, EventTmHook},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -151,20 +151,20 @@ pub mod alloc_mod {
|
|||||||
pub struct PusEventTmCreatorWithMap<
|
pub struct PusEventTmCreatorWithMap<
|
||||||
ReportingMap: PusEventReportingMapProvider<Event>,
|
ReportingMap: PusEventReportingMapProvider<Event>,
|
||||||
Event: GenericEvent,
|
Event: GenericEvent,
|
||||||
EventTmHook: EventTmHookProvider = DummyEventHook,
|
EventTmHookInstance: EventTmHook = DummyEventHook,
|
||||||
> {
|
> {
|
||||||
pub reporter: EventReporter<EventTmHook>,
|
pub reporter: EventReporter<EventTmHookInstance>,
|
||||||
reporting_map: ReportingMap,
|
reporting_map: ReportingMap,
|
||||||
phantom: PhantomData<Event>,
|
phantom: PhantomData<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
ReportingMap: PusEventReportingMapProvider<Event>,
|
ReportingMap: PusEventReportingMapProvider<Event>,
|
||||||
Event: GenericEvent,
|
Event: GenericEvent,
|
||||||
EventTmHook: EventTmHookProvider,
|
EventTmHookInstance: EventTmHook,
|
||||||
> PusEventTmCreatorWithMap<ReportingMap, Event, EventTmHook>
|
> PusEventTmCreatorWithMap<ReportingMap, Event, EventTmHookInstance>
|
||||||
{
|
{
|
||||||
pub fn new(reporter: EventReporter<EventTmHook>, backend: ReportingMap) -> Self {
|
pub fn new(reporter: EventReporter<EventTmHookInstance>, backend: ReportingMap) -> Self {
|
||||||
Self {
|
Self {
|
||||||
reporter,
|
reporter,
|
||||||
reporting_map: backend,
|
reporting_map: backend,
|
||||||
@@ -262,10 +262,10 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Event: GenericEvent + Copy + PartialEq + Eq + Hash, EventTmHook: EventTmHookProvider>
|
impl<Event: GenericEvent + Copy + PartialEq + Eq + Hash, EventTmHookInstance: EventTmHook>
|
||||||
PusEventTmCreatorWithMap<DefaultPusEventReportingMap<Event>, Event, EventTmHook>
|
PusEventTmCreatorWithMap<DefaultPusEventReportingMap<Event>, Event, EventTmHookInstance>
|
||||||
{
|
{
|
||||||
pub fn new_with_default_backend(reporter: EventReporter<EventTmHook>) -> Self {
|
pub fn new_with_default_backend(reporter: EventReporter<EventTmHookInstance>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
reporter,
|
reporter,
|
||||||
reporting_map: DefaultPusEventReportingMap::default(),
|
reporting_map: DefaultPusEventReportingMap::default(),
|
||||||
@@ -311,29 +311,28 @@ pub mod alloc_mod {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use spacepackets::ecss::event::Subservice;
|
use arbitrary_int::{u11, u21};
|
||||||
use spacepackets::ecss::tm::PusTmReader;
|
|
||||||
use spacepackets::ecss::PusPacket;
|
use spacepackets::ecss::PusPacket;
|
||||||
|
use spacepackets::ecss::event::MessageSubtypeId;
|
||||||
|
use spacepackets::ecss::tm::PusTmReader;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::request::UniqueApidTargetId;
|
use crate::request::UniqueApidTargetId;
|
||||||
use crate::{events::SeverityInfo, tmtc::PacketAsVec};
|
use crate::{events_legacy::SeverityInfo, tmtc::PacketAsVec};
|
||||||
use std::sync::mpsc::{self, TryRecvError};
|
use std::sync::mpsc::{self, TryRecvError};
|
||||||
|
|
||||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
|
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
|
||||||
const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
|
const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
|
||||||
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||||
const TEST_APID: u16 = 0x02;
|
const TEST_APID: u11 = u11::new(0x02);
|
||||||
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
|
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, u21::new(0x05));
|
||||||
|
|
||||||
fn create_basic_man_1() -> DefaultPusEventU32TmCreator {
|
fn create_basic_man_1() -> DefaultPusEventU32TmCreator {
|
||||||
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
|
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128);
|
||||||
.expect("Creating event repoter failed");
|
|
||||||
PusEventTmCreatorWithMap::new_with_default_backend(reporter)
|
PusEventTmCreatorWithMap::new_with_default_backend(reporter)
|
||||||
}
|
}
|
||||||
fn create_basic_man_2() -> DefaultPusEventU32TmCreator {
|
fn create_basic_man_2() -> DefaultPusEventU32TmCreator {
|
||||||
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
|
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128);
|
||||||
.expect("Creating event repoter failed");
|
|
||||||
let backend = DefaultPusEventReportingMap::default();
|
let backend = DefaultPusEventReportingMap::default();
|
||||||
PusEventTmCreatorWithMap::new(reporter, backend)
|
PusEventTmCreatorWithMap::new(reporter, backend)
|
||||||
}
|
}
|
||||||
@@ -410,8 +409,11 @@ mod tests {
|
|||||||
assert!(res.params_were_propagated);
|
assert!(res.params_were_propagated);
|
||||||
let event_tm = event_rx.try_recv().expect("no event received");
|
let event_tm = event_rx.try_recv().expect("no event received");
|
||||||
let tm = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
|
let tm = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
|
||||||
assert_eq!(tm.service(), 5);
|
assert_eq!(tm.service_type_id(), 5);
|
||||||
assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
|
assert_eq!(
|
||||||
|
tm.message_subtype_id(),
|
||||||
|
MessageSubtypeId::TmInfoReport as u8
|
||||||
|
);
|
||||||
assert_eq!(tm.user_data().len(), 4 + param_data.len());
|
assert_eq!(tm.user_data().len(), 4 + param_data.len());
|
||||||
let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap());
|
let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap());
|
||||||
assert_eq!(u32_event, INFO_EVENT.raw());
|
assert_eq!(u32_event, INFO_EVENT.raw());
|
||||||
@@ -438,8 +440,11 @@ mod tests {
|
|||||||
assert!(res.params_were_propagated);
|
assert!(res.params_were_propagated);
|
||||||
let event_tm = event_rx.try_recv().expect("no event received");
|
let event_tm = event_rx.try_recv().expect("no event received");
|
||||||
let tm = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
|
let tm = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
|
||||||
assert_eq!(tm.service(), 5);
|
assert_eq!(tm.service_type_id(), 5);
|
||||||
assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
|
assert_eq!(
|
||||||
|
tm.message_subtype_id(),
|
||||||
|
MessageSubtypeId::TmInfoReport as u8
|
||||||
|
);
|
||||||
assert_eq!(tm.user_data().len(), 4 + param_data.len());
|
assert_eq!(tm.user_data().len(), 4 + param_data.len());
|
||||||
let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap());
|
let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap());
|
||||||
assert_eq!(u32_event, INFO_EVENT.raw());
|
assert_eq!(u32_event, INFO_EVENT.raw());
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
use crate::events::EventU32;
|
use crate::events_legacy::EventU32;
|
||||||
use crate::pus::event_man::{EventRequest, EventRequestWithToken};
|
use crate::pus::event_man::{EventRequest, EventRequestWithToken};
|
||||||
use crate::pus::verification::TcStateToken;
|
use crate::pus::verification::TcStateToken;
|
||||||
use crate::pus::{DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError};
|
use crate::pus::{DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError};
|
||||||
use crate::queue::GenericSendError;
|
use crate::queue::GenericSendError;
|
||||||
use spacepackets::ecss::event::Subservice;
|
|
||||||
use spacepackets::ecss::PusPacket;
|
use spacepackets::ecss::PusPacket;
|
||||||
|
use spacepackets::ecss::event::MessageSubtypeId;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use super::verification::VerificationReportingProvider;
|
use super::verification::VerificationReportingProvider;
|
||||||
use super::{
|
use super::{
|
||||||
EcssTcInMemConversionProvider, EcssTcReceiver, EcssTmSender, GenericConversionError,
|
CacheAndReadRawEcssTc, EcssTcReceiver, EcssTmSender, GenericConversionError,
|
||||||
GenericRoutingError, HandlingStatus, PusServiceHelper,
|
GenericRoutingError, HandlingStatus, PusServiceHelper,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PusEventServiceHandler<
|
pub struct PusEventServiceHandler<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
> {
|
> {
|
||||||
pub service_helper:
|
pub service_helper:
|
||||||
@@ -25,11 +25,11 @@ pub struct PusEventServiceHandler<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
> PusEventServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
|
> PusEventServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
service_helper: PusServiceHelper<
|
service_helper: PusServiceHelper<
|
||||||
@@ -60,11 +60,11 @@ impl<
|
|||||||
.tc_in_mem_converter_mut()
|
.tc_in_mem_converter_mut()
|
||||||
.cache(&ecss_tc_and_token.tc_in_memory)?;
|
.cache(&ecss_tc_and_token.tc_in_memory)?;
|
||||||
let tc = self.service_helper.tc_in_mem_converter().convert()?;
|
let tc = self.service_helper.tc_in_mem_converter().convert()?;
|
||||||
let subservice = tc.subservice();
|
let subservice = tc.message_subtype_id();
|
||||||
let srv = Subservice::try_from(subservice);
|
let srv = MessageSubtypeId::try_from(subservice);
|
||||||
if srv.is_err() {
|
if srv.is_err() {
|
||||||
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
|
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
|
||||||
tc.subservice(),
|
tc.message_subtype_id(),
|
||||||
ecss_tc_and_token.token,
|
ecss_tc_and_token.token,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -116,21 +116,21 @@ impl<
|
|||||||
};
|
};
|
||||||
|
|
||||||
match srv.unwrap() {
|
match srv.unwrap() {
|
||||||
Subservice::TmInfoReport
|
MessageSubtypeId::TmInfoReport
|
||||||
| Subservice::TmLowSeverityReport
|
| MessageSubtypeId::TmLowSeverityReport
|
||||||
| Subservice::TmMediumSeverityReport
|
| MessageSubtypeId::TmMediumSeverityReport
|
||||||
| Subservice::TmHighSeverityReport => {
|
| MessageSubtypeId::TmHighSeverityReport => {
|
||||||
return Err(PusPacketHandlingError::RequestConversion(
|
return Err(PusPacketHandlingError::RequestConversion(
|
||||||
GenericConversionError::WrongService(tc.subservice()),
|
GenericConversionError::WrongService(tc.message_subtype_id()),
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
Subservice::TcEnableEventGeneration => {
|
MessageSubtypeId::TcEnableEventGeneration => {
|
||||||
handle_enable_disable_request(true)?;
|
handle_enable_disable_request(true)?;
|
||||||
}
|
}
|
||||||
Subservice::TcDisableEventGeneration => {
|
MessageSubtypeId::TcDisableEventGeneration => {
|
||||||
handle_enable_disable_request(false)?;
|
handle_enable_disable_request(false)?;
|
||||||
}
|
}
|
||||||
Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => {
|
MessageSubtypeId::TcReportDisabledList | MessageSubtypeId::TmDisabledEventsReport => {
|
||||||
return Ok(DirectPusPacketHandlerResult::SubserviceNotImplemented(
|
return Ok(DirectPusPacketHandlerResult::SubserviceNotImplemented(
|
||||||
subservice,
|
subservice,
|
||||||
ecss_tc_and_token.token,
|
ecss_tc_and_token.token,
|
||||||
@@ -144,16 +144,19 @@ impl<
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
|
use arbitrary_int::u14;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
use spacepackets::ecss::event::Subservice;
|
use spacepackets::ecss::event::MessageSubtypeId;
|
||||||
use spacepackets::time::{cds, TimeWriter};
|
use spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
|
use spacepackets::time::{TimeWriter, cds};
|
||||||
use spacepackets::util::UnsignedEnum;
|
use spacepackets::util::UnsignedEnum;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
|
SpHeader,
|
||||||
ecss::{
|
ecss::{
|
||||||
tc::{PusTcCreator, PusTcSecondaryHeader},
|
tc::{PusTcCreator, PusTcSecondaryHeader},
|
||||||
tm::PusTmReader,
|
tm::PusTmReader,
|
||||||
},
|
},
|
||||||
SpHeader,
|
|
||||||
};
|
};
|
||||||
use std::sync::mpsc::{self, Sender};
|
use std::sync::mpsc::{self, Sender};
|
||||||
|
|
||||||
@@ -165,25 +168,25 @@ mod tests {
|
|||||||
use crate::pus::{GenericConversionError, HandlingStatus, MpscTcReceiver};
|
use crate::pus::{GenericConversionError, HandlingStatus, MpscTcReceiver};
|
||||||
use crate::tmtc::PacketSenderWithSharedPool;
|
use crate::tmtc::PacketSenderWithSharedPool;
|
||||||
use crate::{
|
use crate::{
|
||||||
events::EventU32,
|
events_legacy::EventU32,
|
||||||
pus::{
|
pus::{
|
||||||
|
DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, PusPacketHandlingError,
|
||||||
event_man::EventRequestWithToken,
|
event_man::EventRequestWithToken,
|
||||||
tests::PusServiceHandlerWithSharedStoreCommon,
|
tests::PusServiceHandlerWithSharedStoreCommon,
|
||||||
verification::{TcStateAccepted, VerificationToken},
|
verification::{TcStateAccepted, VerificationToken},
|
||||||
DirectPusPacketHandlerResult, EcssTcInSharedPoolConverter, PusPacketHandlingError,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::PusEventServiceHandler;
|
use super::PusEventServiceHandler;
|
||||||
|
|
||||||
const TEST_EVENT_0: EventU32 = EventU32::new(crate::events::Severity::Info, 5, 25);
|
const TEST_EVENT_0: EventU32 = EventU32::new(crate::events_legacy::Severity::Info, 5, 25);
|
||||||
|
|
||||||
struct Pus5HandlerWithStoreTester {
|
struct Pus5HandlerWithStoreTester {
|
||||||
common: PusServiceHandlerWithSharedStoreCommon,
|
common: PusServiceHandlerWithSharedStoreCommon,
|
||||||
handler: PusEventServiceHandler<
|
handler: PusEventServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
PacketSenderWithSharedPool,
|
PacketSenderWithSharedPool,
|
||||||
EcssTcInSharedPoolConverter,
|
EcssTcInSharedPoolCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
@@ -238,17 +241,17 @@ mod tests {
|
|||||||
|
|
||||||
fn event_test(
|
fn event_test(
|
||||||
test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler),
|
test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler),
|
||||||
subservice: Subservice,
|
subservice: MessageSubtypeId,
|
||||||
expected_event_req: EventRequest,
|
expected_event_req: EventRequest,
|
||||||
event_req_receiver: mpsc::Receiver<EventRequestWithToken>,
|
event_req_receiver: mpsc::Receiver<EventRequestWithToken>,
|
||||||
) {
|
) {
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(5, subservice as u8);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(5, subservice as u8));
|
||||||
let mut app_data = [0; 4];
|
let mut app_data = [0; 4];
|
||||||
TEST_EVENT_0
|
TEST_EVENT_0
|
||||||
.write_to_be_bytes(&mut app_data)
|
.write_to_be_bytes(&mut app_data)
|
||||||
.expect("writing test event failed");
|
.expect("writing test event failed");
|
||||||
let ping_tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
let ping_tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||||
let token = test_harness.start_verification(&ping_tc);
|
let token = test_harness.start_verification(&ping_tc);
|
||||||
test_harness.send_tc(&token, &ping_tc);
|
test_harness.send_tc(&token, &ping_tc);
|
||||||
let request_id = token.request_id();
|
let request_id = token.request_id();
|
||||||
@@ -269,7 +272,7 @@ mod tests {
|
|||||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||||
event_test(
|
event_test(
|
||||||
&mut test_harness,
|
&mut test_harness,
|
||||||
Subservice::TcEnableEventGeneration,
|
MessageSubtypeId::TcEnableEventGeneration,
|
||||||
EventRequest::Enable(TEST_EVENT_0),
|
EventRequest::Enable(TEST_EVENT_0),
|
||||||
event_request_rx,
|
event_request_rx,
|
||||||
);
|
);
|
||||||
@@ -281,7 +284,7 @@ mod tests {
|
|||||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||||
event_test(
|
event_test(
|
||||||
&mut test_harness,
|
&mut test_harness,
|
||||||
Subservice::TcDisableEventGeneration,
|
MessageSubtypeId::TcDisableEventGeneration,
|
||||||
EventRequest::Disable(TEST_EVENT_0),
|
EventRequest::Disable(TEST_EVENT_0),
|
||||||
event_request_rx,
|
event_request_rx,
|
||||||
);
|
);
|
||||||
@@ -307,9 +310,10 @@ mod tests {
|
|||||||
fn test_sending_custom_subservice() {
|
fn test_sending_custom_subservice() {
|
||||||
let (event_request_tx, _) = mpsc::channel();
|
let (event_request_tx, _) = mpsc::channel();
|
||||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(5, 200);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(5, 200));
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
let ping_tc =
|
||||||
|
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||||
let token = test_harness.start_verification(&ping_tc);
|
let token = test_harness.start_verification(&ping_tc);
|
||||||
test_harness.send_tc(&token, &ping_tc);
|
test_harness.send_tc(&token, &ping_tc);
|
||||||
let result = test_harness.handle_one_tc();
|
let result = test_harness.handle_one_tc();
|
||||||
@@ -326,10 +330,13 @@ mod tests {
|
|||||||
fn test_sending_invalid_app_data() {
|
fn test_sending_invalid_app_data() {
|
||||||
let (event_request_tx, _) = mpsc::channel();
|
let (event_request_tx, _) = mpsc::channel();
|
||||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header =
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
|
||||||
PusTcSecondaryHeader::new_simple(5, Subservice::TcEnableEventGeneration as u8);
|
5,
|
||||||
let ping_tc = PusTcCreator::new(sp_header, sec_header, &[0, 1, 2], true);
|
MessageSubtypeId::TcEnableEventGeneration as u8,
|
||||||
|
));
|
||||||
|
let ping_tc =
|
||||||
|
PusTcCreator::new(sp_header, sec_header, &[0, 1, 2], CreatorConfig::default());
|
||||||
let token = test_harness.start_verification(&ping_tc);
|
let token = test_harness.start_verification(&ping_tc);
|
||||||
test_harness.send_tc(&token, &ping_tc);
|
test_harness.send_tc(&token, &ping_tc);
|
||||||
let result = test_harness.handle_one_tc();
|
let result = test_harness.handle_one_tc();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! This module contains structures to make working with the PUS C standard easier.
|
//! This module contains structures to make working with the PUS C standard easier.
|
||||||
//! The satrs-example application contains various usage examples of these components.
|
//! The satrs-example application contains various usage examples of these components.
|
||||||
|
use crate::ComponentId;
|
||||||
use crate::pool::{PoolAddr, PoolError};
|
use crate::pool::{PoolAddr, PoolError};
|
||||||
use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken};
|
use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken};
|
||||||
use crate::queue::{GenericReceiveError, GenericSendError};
|
use crate::queue::{GenericReceiveError, GenericSendError};
|
||||||
@@ -9,19 +10,18 @@ use crate::request::{GenericMessage, MessageMetadata, RequestId};
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use crate::tmtc::PacketAsVec;
|
use crate::tmtc::PacketAsVec;
|
||||||
use crate::tmtc::PacketInPool;
|
use crate::tmtc::PacketInPool;
|
||||||
use crate::ComponentId;
|
|
||||||
use core::fmt::{Display, Formatter};
|
use core::fmt::{Display, Formatter};
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use downcast_rs::{impl_downcast, Downcast};
|
use downcast_rs::{Downcast, impl_downcast};
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
use spacepackets::ecss::PusError;
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
|
||||||
use spacepackets::ecss::tm::PusTmCreator;
|
use spacepackets::ecss::tm::PusTmCreator;
|
||||||
use spacepackets::ecss::PusError;
|
|
||||||
use spacepackets::{ByteConversionError, SpHeader};
|
use spacepackets::{ByteConversionError, SpHeader};
|
||||||
|
|
||||||
pub mod action;
|
pub mod action;
|
||||||
@@ -296,7 +296,7 @@ pub trait PacketSenderPusTc: Send {
|
|||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ActiveRequestMapProvider<V>: Sized {
|
pub trait ActiveRequestStore<V>: Sized {
|
||||||
fn insert(&mut self, request_id: &RequestId, request_info: V);
|
fn insert(&mut self, request_id: &RequestId, request_info: V);
|
||||||
fn get(&self, request_id: RequestId) -> Option<&V>;
|
fn get(&self, request_id: RequestId) -> Option<&V>;
|
||||||
fn get_mut(&mut self, request_id: RequestId) -> Option<&mut V>;
|
fn get_mut(&mut self, request_id: RequestId) -> Option<&mut V>;
|
||||||
@@ -309,7 +309,7 @@ pub trait ActiveRequestMapProvider<V>: Sized {
|
|||||||
fn for_each_mut<F: FnMut(&RequestId, &mut V)>(&mut self, f: F);
|
fn for_each_mut<F: FnMut(&RequestId, &mut V)>(&mut self, f: F);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ActiveRequestProvider {
|
pub trait ActiveRequest {
|
||||||
fn target_id(&self) -> ComponentId;
|
fn target_id(&self) -> ComponentId;
|
||||||
fn token(&self) -> TcStateToken;
|
fn token(&self) -> TcStateToken;
|
||||||
fn set_token(&mut self, token: TcStateToken);
|
fn set_token(&mut self, token: TcStateToken);
|
||||||
@@ -330,7 +330,7 @@ pub trait PusRequestRouter<Request> {
|
|||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PusReplyHandler<ActiveRequestInfo: ActiveRequestProvider, ReplyType> {
|
pub trait PusReplyHandler<ActiveRequestInfo: ActiveRequest, ReplyType> {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
/// This function handles a reply for a given PUS request and returns whether that request
|
/// This function handles a reply for a given PUS request and returns whether that request
|
||||||
@@ -449,7 +449,7 @@ pub mod alloc_mod {
|
|||||||
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
|
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
|
||||||
/// The only requirement is that a valid active request information instance and a request
|
/// The only requirement is that a valid active request information instance and a request
|
||||||
/// are returned by the core conversion function. The active request type needs to fulfill
|
/// are returned by the core conversion function. The active request type needs to fulfill
|
||||||
/// the [ActiveRequestProvider] trait bound.
|
/// the [ActiveRequest] trait bound.
|
||||||
///
|
///
|
||||||
/// The user should take care of performing the error handling as well. Some of the following
|
/// The user should take care of performing the error handling as well. Some of the following
|
||||||
/// aspects might be relevant:
|
/// aspects might be relevant:
|
||||||
@@ -459,7 +459,7 @@ pub mod alloc_mod {
|
|||||||
///
|
///
|
||||||
/// A [VerificationReportingProvider] instance is passed to the user to also allow handling
|
/// A [VerificationReportingProvider] instance is passed to the user to also allow handling
|
||||||
/// of the verification process as part of the PUS standard requirements.
|
/// of the verification process as part of the PUS standard requirements.
|
||||||
pub trait PusTcToRequestConverter<ActiveRequestInfo: ActiveRequestProvider, Request> {
|
pub trait PusTcToRequestConverter<ActiveRequestInfo: ActiveRequest, Request> {
|
||||||
type Error;
|
type Error;
|
||||||
fn convert(
|
fn convert(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -480,7 +480,7 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> ActiveRequestMapProvider<V> for DefaultActiveRequestMap<V> {
|
impl<V> ActiveRequestStore<V> for DefaultActiveRequestMap<V> {
|
||||||
fn insert(&mut self, request_id: &RequestId, request: V) {
|
fn insert(&mut self, request_id: &RequestId, request: V) {
|
||||||
self.0.insert(*request_id, request);
|
self.0.insert(*request_id, request);
|
||||||
}
|
}
|
||||||
@@ -659,18 +659,18 @@ pub mod alloc_mod {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod std_mod {
|
pub mod std_mod {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::ComponentId;
|
||||||
use crate::pool::{
|
use crate::pool::{
|
||||||
PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool,
|
PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool,
|
||||||
};
|
};
|
||||||
use crate::pus::verification::{TcStateAccepted, VerificationToken};
|
use crate::pus::verification::{TcStateAccepted, VerificationToken};
|
||||||
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
||||||
use crate::ComponentId;
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use spacepackets::ecss::tc::PusTcReader;
|
|
||||||
use spacepackets::ecss::WritablePusPacket;
|
|
||||||
use spacepackets::time::StdTimestampError;
|
|
||||||
use spacepackets::ByteConversionError;
|
use spacepackets::ByteConversionError;
|
||||||
|
use spacepackets::ecss::WritablePusPacket;
|
||||||
|
use spacepackets::ecss::tc::PusTcReader;
|
||||||
|
use spacepackets::time::StdTimestampError;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::mpsc::TryRecvError;
|
use std::sync::mpsc::TryRecvError;
|
||||||
@@ -680,7 +680,7 @@ pub mod std_mod {
|
|||||||
pub use cb_mod::*;
|
pub use cb_mod::*;
|
||||||
|
|
||||||
use super::verification::{TcStateToken, VerificationReportingProvider};
|
use super::verification::{TcStateToken, VerificationReportingProvider};
|
||||||
use super::{AcceptedEcssTcAndToken, ActiveRequestProvider, TcInMemory};
|
use super::{AcceptedEcssTcAndToken, ActiveRequest, TcInMemory};
|
||||||
use crate::tmtc::PacketInPool;
|
use crate::tmtc::PacketInPool;
|
||||||
|
|
||||||
impl From<mpsc::SendError<PoolAddr>> for EcssTmtcError {
|
impl From<mpsc::SendError<PoolAddr>> for EcssTmtcError {
|
||||||
@@ -845,7 +845,7 @@ pub mod std_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveRequestProvider for ActivePusRequestStd {
|
impl ActiveRequest for ActivePusRequestStd {
|
||||||
fn target_id(&self) -> ComponentId {
|
fn target_id(&self) -> ComponentId {
|
||||||
self.target_id
|
self.target_id
|
||||||
}
|
}
|
||||||
@@ -947,7 +947,9 @@ pub mod std_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EcssTcInMemConversionProvider {
|
/// This trait provides an abstraction for caching a raw ECSS telecommand and then
|
||||||
|
/// providing the [PusTcReader] abstraction to read the cache raw telecommand.
|
||||||
|
pub trait CacheAndReadRawEcssTc {
|
||||||
fn cache(&mut self, possible_packet: &TcInMemory) -> Result<(), PusTcFromMemError>;
|
fn cache(&mut self, possible_packet: &TcInMemory) -> Result<(), PusTcFromMemError>;
|
||||||
|
|
||||||
fn tc_slice_raw(&self) -> &[u8];
|
fn tc_slice_raw(&self) -> &[u8];
|
||||||
@@ -971,12 +973,12 @@ pub mod std_mod {
|
|||||||
/// Please note that this structure is not able to convert TCs which are stored inside a
|
/// Please note that this structure is not able to convert TCs which are stored inside a
|
||||||
/// [SharedStaticMemoryPool].
|
/// [SharedStaticMemoryPool].
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct EcssTcInVecConverter {
|
pub struct EcssTcVecCacher {
|
||||||
sender_id: Option<ComponentId>,
|
sender_id: Option<ComponentId>,
|
||||||
pub pus_tc_raw: Option<Vec<u8>>,
|
pub pus_tc_raw: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EcssTcInMemConversionProvider for EcssTcInVecConverter {
|
impl CacheAndReadRawEcssTc for EcssTcVecCacher {
|
||||||
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
|
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
|
||||||
self.pus_tc_raw = None;
|
self.pus_tc_raw = None;
|
||||||
match tc_in_memory {
|
match tc_in_memory {
|
||||||
@@ -1008,13 +1010,13 @@ pub mod std_mod {
|
|||||||
/// packets should be avoided. Please note that this structure is not able to convert TCs which
|
/// packets should be avoided. Please note that this structure is not able to convert TCs which
|
||||||
/// are stored as a `Vec<u8>`.
|
/// are stored as a `Vec<u8>`.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EcssTcInSharedPoolConverter {
|
pub struct EcssTcInSharedPoolCacher {
|
||||||
sender_id: Option<ComponentId>,
|
sender_id: Option<ComponentId>,
|
||||||
shared_tc_pool: SharedStaticMemoryPool,
|
shared_tc_pool: SharedStaticMemoryPool,
|
||||||
pus_buf: Vec<u8>,
|
pus_buf: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EcssTcInSharedPoolConverter {
|
impl EcssTcInSharedPoolCacher {
|
||||||
pub fn new(shared_tc_store: SharedStaticMemoryPool, max_expected_tc_size: usize) -> Self {
|
pub fn new(shared_tc_store: SharedStaticMemoryPool, max_expected_tc_size: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sender_id: None,
|
sender_id: None,
|
||||||
@@ -1045,7 +1047,7 @@ pub mod std_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EcssTcInMemConversionProvider for EcssTcInSharedPoolConverter {
|
impl CacheAndReadRawEcssTc for EcssTcInSharedPoolCacher {
|
||||||
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
|
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
|
||||||
match tc_in_memory {
|
match tc_in_memory {
|
||||||
super::TcInMemory::Pool(packet_in_pool) => {
|
super::TcInMemory::Pool(packet_in_pool) => {
|
||||||
@@ -1070,38 +1072,38 @@ pub mod std_mod {
|
|||||||
|
|
||||||
// TODO: alloc feature flag?
|
// TODO: alloc feature flag?
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum EcssTcInMemConverter {
|
pub enum EcssTcCacher {
|
||||||
Static(EcssTcInSharedPoolConverter),
|
Static(EcssTcInSharedPoolCacher),
|
||||||
Heap(EcssTcInVecConverter),
|
Heap(EcssTcVecCacher),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EcssTcInMemConverter {
|
impl EcssTcCacher {
|
||||||
pub fn new_static(static_store_converter: EcssTcInSharedPoolConverter) -> Self {
|
pub fn new_static(static_store_converter: EcssTcInSharedPoolCacher) -> Self {
|
||||||
EcssTcInMemConverter::Static(static_store_converter)
|
Self::Static(static_store_converter)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_heap(heap_converter: EcssTcInVecConverter) -> Self {
|
pub fn new_heap(heap_converter: EcssTcVecCacher) -> Self {
|
||||||
EcssTcInMemConverter::Heap(heap_converter)
|
Self::Heap(heap_converter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EcssTcInMemConversionProvider for EcssTcInMemConverter {
|
impl CacheAndReadRawEcssTc for EcssTcCacher {
|
||||||
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
|
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
|
||||||
match self {
|
match self {
|
||||||
EcssTcInMemConverter::Static(converter) => converter.cache(tc_in_memory),
|
Self::Static(converter) => converter.cache(tc_in_memory),
|
||||||
EcssTcInMemConverter::Heap(converter) => converter.cache(tc_in_memory),
|
Self::Heap(converter) => converter.cache(tc_in_memory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn tc_slice_raw(&self) -> &[u8] {
|
fn tc_slice_raw(&self) -> &[u8] {
|
||||||
match self {
|
match self {
|
||||||
EcssTcInMemConverter::Static(converter) => converter.tc_slice_raw(),
|
Self::Static(converter) => converter.tc_slice_raw(),
|
||||||
EcssTcInMemConverter::Heap(converter) => converter.tc_slice_raw(),
|
Self::Heap(converter) => converter.tc_slice_raw(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn sender_id(&self) -> Option<ComponentId> {
|
fn sender_id(&self) -> Option<ComponentId> {
|
||||||
match self {
|
match self {
|
||||||
EcssTcInMemConverter::Static(converter) => converter.sender_id(),
|
Self::Static(converter) => converter.sender_id(),
|
||||||
EcssTcInMemConverter::Heap(converter) => converter.sender_id(),
|
Self::Heap(converter) => converter.sender_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1124,12 +1126,12 @@ pub mod std_mod {
|
|||||||
/// groups (for example individual services).
|
/// groups (for example individual services).
|
||||||
///
|
///
|
||||||
/// This base class can handle PUS telecommands backed by different memory storage machanisms
|
/// This base class can handle PUS telecommands backed by different memory storage machanisms
|
||||||
/// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
|
/// by using the [CacheAndReadRawEcssTc] abstraction. This object provides some convenience
|
||||||
/// methods to make the generic parts of TC handling easier.
|
/// methods to make the generic parts of TC handling easier.
|
||||||
pub struct PusServiceHelper<
|
pub struct PusServiceHelper<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
> {
|
> {
|
||||||
pub common: PusServiceBase<TcReceiver, TmSender, VerificationReporter>,
|
pub common: PusServiceBase<TcReceiver, TmSender, VerificationReporter>,
|
||||||
@@ -1137,11 +1139,11 @@ pub mod std_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
> PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
|
> PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
@@ -1256,18 +1258,19 @@ pub(crate) fn source_buffer_large_enough(
|
|||||||
|
|
||||||
#[cfg(any(feature = "test_util", test))]
|
#[cfg(any(feature = "test_util", test))]
|
||||||
pub mod test_util {
|
pub mod test_util {
|
||||||
|
use arbitrary_int::{u11, u21};
|
||||||
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
|
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
|
||||||
|
|
||||||
use crate::request::UniqueApidTargetId;
|
use crate::request::UniqueApidTargetId;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
verification::{self, TcStateAccepted, VerificationToken},
|
|
||||||
DirectPusPacketHandlerResult, PusPacketHandlingError,
|
DirectPusPacketHandlerResult, PusPacketHandlingError,
|
||||||
|
verification::{self, TcStateAccepted, VerificationToken},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const TEST_APID: u16 = 0x101;
|
pub const TEST_APID: u11 = u11::new(0x101);
|
||||||
pub const TEST_UNIQUE_ID_0: u32 = 0x05;
|
pub const TEST_UNIQUE_ID_0: u21 = u21::new(0x05);
|
||||||
pub const TEST_UNIQUE_ID_1: u32 = 0x06;
|
pub const TEST_UNIQUE_ID_1: u21 = u21::new(0x06);
|
||||||
|
|
||||||
pub const TEST_COMPONENT_ID_0: UniqueApidTargetId =
|
pub const TEST_COMPONENT_ID_0: UniqueApidTargetId =
|
||||||
UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0);
|
UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0);
|
||||||
@@ -1288,7 +1291,7 @@ pub mod test_util {
|
|||||||
|
|
||||||
pub trait SimplePusPacketHandler {
|
pub trait SimplePusPacketHandler {
|
||||||
fn handle_one_tc(&mut self)
|
fn handle_one_tc(&mut self)
|
||||||
-> Result<DirectPusPacketHandlerResult, PusPacketHandlingError>;
|
-> Result<DirectPusPacketHandlerResult, PusPacketHandlingError>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1296,33 +1299,35 @@ pub mod test_util {
|
|||||||
pub mod tests {
|
pub mod tests {
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use std::sync::mpsc::TryRecvError;
|
use std::sync::mpsc::TryRecvError;
|
||||||
use std::sync::{mpsc, RwLock};
|
use std::sync::{RwLock, mpsc};
|
||||||
|
|
||||||
use alloc::collections::VecDeque;
|
use alloc::collections::VecDeque;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use arbitrary_int::{u11, u14};
|
||||||
use satrs_shared::res_code::ResultU16;
|
use satrs_shared::res_code::ResultU16;
|
||||||
|
use spacepackets::CcsdsPacket;
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
|
||||||
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
|
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
|
||||||
use spacepackets::ecss::{PusPacket, WritablePusPacket};
|
use spacepackets::ecss::{PusPacket, WritablePusPacket};
|
||||||
use spacepackets::CcsdsPacket;
|
|
||||||
use test_util::{TEST_APID, TEST_COMPONENT_ID_0};
|
use test_util::{TEST_APID, TEST_COMPONENT_ID_0};
|
||||||
|
|
||||||
|
use crate::ComponentId;
|
||||||
use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
|
use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
|
||||||
use crate::pus::verification::{RequestId, VerificationReporter};
|
use crate::pus::verification::{RequestId, VerificationReporter};
|
||||||
use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool};
|
use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool};
|
||||||
use crate::ComponentId;
|
|
||||||
|
|
||||||
use super::verification::test_util::TestVerificationReporter;
|
use super::verification::test_util::TestVerificationReporter;
|
||||||
use super::verification::{
|
use super::verification::{
|
||||||
TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
|
TcStateAccepted, VerificationReporterConfig, VerificationReportingProvider,
|
||||||
|
VerificationToken,
|
||||||
};
|
};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub(crate) struct CommonTmInfo {
|
pub(crate) struct CommonTmInfo {
|
||||||
pub subservice: u8,
|
pub subservice: u8,
|
||||||
pub apid: u16,
|
pub apid: u11,
|
||||||
pub seq_count: u16,
|
pub seq_count: u14,
|
||||||
pub msg_counter: u16,
|
pub msg_counter: u16,
|
||||||
pub dest_id: u16,
|
pub dest_id: u16,
|
||||||
pub timestamp: Vec<u8>,
|
pub timestamp: Vec<u8>,
|
||||||
@@ -1331,8 +1336,8 @@ pub mod tests {
|
|||||||
impl CommonTmInfo {
|
impl CommonTmInfo {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
apid: u16,
|
apid: u11,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_counter: u16,
|
msg_counter: u16,
|
||||||
dest_id: u16,
|
dest_id: u16,
|
||||||
timestamp: &[u8],
|
timestamp: &[u8],
|
||||||
@@ -1348,21 +1353,21 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
pub fn new_zero_seq_count(
|
pub fn new_zero_seq_count(
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
apid: u16,
|
apid: u11,
|
||||||
dest_id: u16,
|
dest_id: u16,
|
||||||
timestamp: &[u8],
|
timestamp: &[u8],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(subservice, apid, 0, 0, dest_id, timestamp)
|
Self::new(subservice, apid, u14::new(0), 0, dest_id, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_tm(tm: &PusTmCreator) -> Self {
|
pub fn new_from_tm(tm: &PusTmCreator) -> Self {
|
||||||
let mut timestamp = [0; 7];
|
let mut timestamp = [0; 7];
|
||||||
timestamp.clone_from_slice(&tm.timestamp()[0..7]);
|
timestamp.clone_from_slice(&tm.timestamp()[0..7]);
|
||||||
Self {
|
Self {
|
||||||
subservice: PusPacket::subservice(tm),
|
subservice: PusPacket::message_subtype_id(tm),
|
||||||
apid: tm.apid(),
|
apid: tm.apid(),
|
||||||
seq_count: tm.seq_count(),
|
seq_count: tm.seq_count(),
|
||||||
msg_counter: tm.msg_counter(),
|
msg_counter: tm.msg_type_counter(),
|
||||||
dest_id: tm.dest_id(),
|
dest_id: tm.dest_id(),
|
||||||
timestamp: timestamp.to_vec(),
|
timestamp: timestamp.to_vec(),
|
||||||
}
|
}
|
||||||
@@ -1382,7 +1387,7 @@ pub mod tests {
|
|||||||
pub type PusServiceHelperStatic = PusServiceHelper<
|
pub type PusServiceHelperStatic = PusServiceHelper<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
PacketSenderWithSharedPool,
|
PacketSenderWithSharedPool,
|
||||||
EcssTcInSharedPoolConverter,
|
EcssTcInSharedPoolCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -1404,12 +1409,12 @@ pub mod tests {
|
|||||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::sync_channel(10);
|
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::sync_channel(10);
|
||||||
let (tm_tx, tm_rx) = mpsc::sync_channel(10);
|
let (tm_tx, tm_rx) = mpsc::sync_channel(10);
|
||||||
|
|
||||||
let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||||
let verification_handler =
|
let verification_handler =
|
||||||
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
|
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
|
||||||
let test_srv_tm_sender =
|
let test_srv_tm_sender =
|
||||||
PacketSenderWithSharedPool::new(tm_tx, shared_tm_pool_wrapper.clone());
|
PacketSenderWithSharedPool::new(tm_tx, shared_tm_pool_wrapper.clone());
|
||||||
let in_store_converter = EcssTcInSharedPoolConverter::new(shared_tc_pool.clone(), 2048);
|
let in_store_converter = EcssTcInSharedPoolCacher::new(shared_tc_pool.clone(), 2048);
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
pus_buf: RefCell::new([0; 2048]),
|
pus_buf: RefCell::new([0; 2048]),
|
||||||
@@ -1473,8 +1478,8 @@ pub mod tests {
|
|||||||
let tm_pool = self.tm_pool.0.read().unwrap();
|
let tm_pool = self.tm_pool.0.read().unwrap();
|
||||||
let tm_raw = tm_pool.read_as_vec(&tm_in_pool.store_addr).unwrap();
|
let tm_raw = tm_pool.read_as_vec(&tm_in_pool.store_addr).unwrap();
|
||||||
let tm = PusTmReader::new(&tm_raw, 7).unwrap();
|
let tm = PusTmReader::new(&tm_raw, 7).unwrap();
|
||||||
assert_eq!(PusPacket::service(&tm), 1);
|
assert_eq!(PusPacket::service_type_id(&tm), 1);
|
||||||
assert_eq!(PusPacket::subservice(&tm), subservice);
|
assert_eq!(PusPacket::message_subtype_id(&tm), subservice);
|
||||||
assert_eq!(tm.apid(), TEST_APID);
|
assert_eq!(tm.apid(), TEST_APID);
|
||||||
let req_id =
|
let req_id =
|
||||||
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
||||||
@@ -1487,12 +1492,8 @@ pub mod tests {
|
|||||||
tc_sender: mpsc::Sender<EcssTcAndToken>,
|
tc_sender: mpsc::Sender<EcssTcAndToken>,
|
||||||
tm_receiver: mpsc::Receiver<PacketAsVec>,
|
tm_receiver: mpsc::Receiver<PacketAsVec>,
|
||||||
}
|
}
|
||||||
pub type PusServiceHelperDynamic = PusServiceHelper<
|
pub type PusServiceHelperDynamic =
|
||||||
MpscTcReceiver,
|
PusServiceHelper<MpscTcReceiver, MpscTmAsVecSender, EcssTcVecCacher, VerificationReporter>;
|
||||||
MpscTmAsVecSender,
|
|
||||||
EcssTcInVecConverter,
|
|
||||||
VerificationReporter,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl PusServiceHandlerWithVecCommon {
|
impl PusServiceHandlerWithVecCommon {
|
||||||
pub fn new_with_standard_verif_reporter(
|
pub fn new_with_standard_verif_reporter(
|
||||||
@@ -1501,10 +1502,10 @@ pub mod tests {
|
|||||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
||||||
let (tm_tx, tm_rx) = mpsc::channel();
|
let (tm_tx, tm_rx) = mpsc::channel();
|
||||||
|
|
||||||
let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||||
let verification_handler =
|
let verification_handler =
|
||||||
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
|
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
|
||||||
let in_store_converter = EcssTcInVecConverter::default();
|
let in_store_converter = EcssTcVecCacher::default();
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
current_tm: None,
|
current_tm: None,
|
||||||
@@ -1530,14 +1531,14 @@ pub mod tests {
|
|||||||
PusServiceHelper<
|
PusServiceHelper<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
MpscTmAsVecSender,
|
MpscTmAsVecSender,
|
||||||
EcssTcInVecConverter,
|
EcssTcVecCacher,
|
||||||
TestVerificationReporter,
|
TestVerificationReporter,
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
||||||
let (tm_tx, tm_rx) = mpsc::channel();
|
let (tm_tx, tm_rx) = mpsc::channel();
|
||||||
|
|
||||||
let in_store_converter = EcssTcInVecConverter::default();
|
let in_store_converter = EcssTcVecCacher::default();
|
||||||
let verification_handler = TestVerificationReporter::new(id);
|
let verification_handler = TestVerificationReporter::new(id);
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
@@ -1596,8 +1597,8 @@ pub mod tests {
|
|||||||
assert!(next_msg.is_ok());
|
assert!(next_msg.is_ok());
|
||||||
let next_msg = next_msg.unwrap();
|
let next_msg = next_msg.unwrap();
|
||||||
let tm = PusTmReader::new(next_msg.packet.as_slice(), 7).unwrap();
|
let tm = PusTmReader::new(next_msg.packet.as_slice(), 7).unwrap();
|
||||||
assert_eq!(PusPacket::service(&tm), 1);
|
assert_eq!(PusPacket::service_type_id(&tm), 1);
|
||||||
assert_eq!(PusPacket::subservice(&tm), subservice);
|
assert_eq!(PusPacket::message_subtype_id(&tm), subservice);
|
||||||
assert_eq!(tm.apid(), TEST_APID);
|
assert_eq!(tm.apid(), TEST_APID);
|
||||||
let req_id =
|
let req_id =
|
||||||
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
||||||
@@ -1614,9 +1615,9 @@ pub mod tests {
|
|||||||
|
|
||||||
impl<const SERVICE: u8> TestConverter<SERVICE> {
|
impl<const SERVICE: u8> TestConverter<SERVICE> {
|
||||||
pub fn check_service(&self, tc: &PusTcReader) -> Result<(), PusPacketHandlingError> {
|
pub fn check_service(&self, tc: &PusTcReader) -> Result<(), PusPacketHandlingError> {
|
||||||
if tc.service() != SERVICE {
|
if tc.service_type_id() != SERVICE {
|
||||||
return Err(PusPacketHandlingError::RequestConversion(
|
return Err(PusPacketHandlingError::RequestConversion(
|
||||||
GenericConversionError::WrongService(tc.service()),
|
GenericConversionError::WrongService(tc.service_type_id()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ mod tests {
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
mode::{
|
mode::{
|
||||||
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
|
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
|
||||||
ModeRequestorAndHandlerMpsc, ModeRequestorOneChildMpsc,
|
ModeRequestorAndHandlerMpsc, ModeRequestorOneChildMpsc,
|
||||||
@@ -44,9 +45,9 @@ mod tests {
|
|||||||
request::{GenericMessage, MessageMetadata},
|
request::{GenericMessage, MessageMetadata},
|
||||||
};
|
};
|
||||||
|
|
||||||
const TEST_COMPONENT_ID_0: u64 = 5;
|
const TEST_COMPONENT_ID_0: ComponentId = 5;
|
||||||
const TEST_COMPONENT_ID_1: u64 = 6;
|
const TEST_COMPONENT_ID_1: ComponentId = 6;
|
||||||
const TEST_COMPONENT_ID_2: u64 = 7;
|
const TEST_COMPONENT_ID_2: ComponentId = 7;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_mode_requestor() {
|
fn test_simple_mode_requestor() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! The core data structure of this module is the [PusScheduler]. This structure can be used
|
//! The core data structure of this module is the [PusScheduler]. This structure can be used
|
||||||
//! to perform the scheduling of telecommands like specified in the ECSS standard.
|
//! to perform the scheduling of telecommands like specified in the ECSS standard.
|
||||||
|
use arbitrary_int::{u11, u14};
|
||||||
use core::fmt::{Debug, Display, Formatter};
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
@@ -24,22 +25,26 @@ pub use alloc_mod::*;
|
|||||||
/// the source ID found in the secondary header of PUS telecommands.
|
/// the source ID found in the secondary header of PUS telecommands.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct RequestId {
|
pub struct RequestId {
|
||||||
pub(crate) source_id: u16,
|
pub(crate) source_id: u16,
|
||||||
pub(crate) apid: u16,
|
pub(crate) apid: u11,
|
||||||
pub(crate) seq_count: u16,
|
pub(crate) seq_count: u14,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequestId {
|
impl RequestId {
|
||||||
pub fn source_id(&self) -> u16 {
|
#[inline]
|
||||||
|
pub const fn source_id(&self) -> u16 {
|
||||||
self.source_id
|
self.source_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apid(&self) -> u16 {
|
#[inline]
|
||||||
|
pub const fn apid(&self) -> u11 {
|
||||||
self.apid
|
self.apid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn seq_count(&self) -> u16 {
|
#[inline]
|
||||||
|
pub const fn seq_count(&self) -> u14 {
|
||||||
self.seq_count
|
self.seq_count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +58,10 @@ impl RequestId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_u64(&self) -> u64 {
|
pub const fn as_u64(&self) -> u64 {
|
||||||
((self.source_id as u64) << 32) | ((self.apid as u64) << 16) | self.seq_count as u64
|
((self.source_id as u64) << 32)
|
||||||
|
| ((self.apid.value() as u64) << 16)
|
||||||
|
| self.seq_count.value() as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,14 +272,16 @@ pub trait PusSchedulerProvider {
|
|||||||
pus_tc: &(impl IsPusTelecommand + PusPacket + GenericPusTcSecondaryHeader),
|
pus_tc: &(impl IsPusTelecommand + PusPacket + GenericPusTcSecondaryHeader),
|
||||||
pool: &mut (impl PoolProvider + ?Sized),
|
pool: &mut (impl PoolProvider + ?Sized),
|
||||||
) -> Result<TcInfo, ScheduleError> {
|
) -> Result<TcInfo, ScheduleError> {
|
||||||
if PusPacket::service(pus_tc) != 11 {
|
if PusPacket::service_type_id(pus_tc) != 11 {
|
||||||
return Err(ScheduleError::WrongService(PusPacket::service(pus_tc)));
|
return Err(ScheduleError::WrongService(PusPacket::service_type_id(
|
||||||
}
|
|
||||||
if PusPacket::subservice(pus_tc) != 4 {
|
|
||||||
return Err(ScheduleError::WrongSubservice(PusPacket::subservice(
|
|
||||||
pus_tc,
|
pus_tc,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
if PusPacket::message_subtype_id(pus_tc) != 4 {
|
||||||
|
return Err(ScheduleError::WrongSubservice(
|
||||||
|
PusPacket::message_subtype_id(pus_tc),
|
||||||
|
));
|
||||||
|
}
|
||||||
if pus_tc.user_data().is_empty() {
|
if pus_tc.user_data().is_empty() {
|
||||||
return Err(ScheduleError::TcDataEmpty);
|
return Err(ScheduleError::TcDataEmpty);
|
||||||
}
|
}
|
||||||
@@ -292,7 +301,9 @@ pub trait PusSchedulerProvider {
|
|||||||
pool: &mut (impl PoolProvider + ?Sized),
|
pool: &mut (impl PoolProvider + ?Sized),
|
||||||
) -> Result<TcInfo, ScheduleError> {
|
) -> Result<TcInfo, ScheduleError> {
|
||||||
let check_tc = PusTcReader::new(tc)?;
|
let check_tc = PusTcReader::new(tc)?;
|
||||||
if PusPacket::service(&check_tc) == 11 && PusPacket::subservice(&check_tc) == 4 {
|
if PusPacket::service_type_id(&check_tc) == 11
|
||||||
|
&& PusPacket::message_subtype_id(&check_tc) == 4
|
||||||
|
{
|
||||||
return Err(ScheduleError::NestedScheduledTc);
|
return Err(ScheduleError::NestedScheduledTc);
|
||||||
}
|
}
|
||||||
let req_id = RequestId::from_tc(&check_tc);
|
let req_id = RequestId::from_tc(&check_tc);
|
||||||
@@ -340,8 +351,8 @@ pub fn generate_insert_telecommand_app_data(
|
|||||||
pub mod alloc_mod {
|
pub mod alloc_mod {
|
||||||
use alloc::{
|
use alloc::{
|
||||||
collections::{
|
collections::{
|
||||||
btree_map::{Entry, Range},
|
|
||||||
BTreeMap,
|
BTreeMap,
|
||||||
|
btree_map::{Entry, Range},
|
||||||
},
|
},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@@ -480,7 +491,9 @@ pub mod alloc_mod {
|
|||||||
pool: &mut (impl PoolProvider + ?Sized),
|
pool: &mut (impl PoolProvider + ?Sized),
|
||||||
) -> Result<TcInfo, ScheduleError> {
|
) -> Result<TcInfo, ScheduleError> {
|
||||||
let check_tc = PusTcReader::new(tc)?;
|
let check_tc = PusTcReader::new(tc)?;
|
||||||
if PusPacket::service(&check_tc) == 11 && PusPacket::subservice(&check_tc) == 4 {
|
if PusPacket::service_type_id(&check_tc) == 11
|
||||||
|
&& PusPacket::message_subtype_id(&check_tc) == 4
|
||||||
|
{
|
||||||
return Err(ScheduleError::NestedScheduledTc);
|
return Err(ScheduleError::NestedScheduledTc);
|
||||||
}
|
}
|
||||||
let req_id = RequestId::from_tc(&check_tc);
|
let req_id = RequestId::from_tc(&check_tc);
|
||||||
@@ -855,10 +868,12 @@ mod tests {
|
|||||||
PoolAddr, PoolError, PoolProvider, StaticMemoryPool, StaticPoolAddr, StaticPoolConfig,
|
PoolAddr, PoolError, PoolProvider, StaticMemoryPool, StaticPoolAddr, StaticPoolConfig,
|
||||||
};
|
};
|
||||||
use alloc::collections::btree_map::Range;
|
use alloc::collections::btree_map::Range;
|
||||||
|
use arbitrary_int::traits::Integer;
|
||||||
|
use arbitrary_int::{u11, u14};
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
||||||
use spacepackets::ecss::WritablePusPacket;
|
use spacepackets::ecss::{CreatorConfig, MessageTypeId, WritablePusPacket};
|
||||||
use spacepackets::time::{cds, TimeWriter, UnixTime};
|
use spacepackets::time::{TimeWriter, UnixTime, cds};
|
||||||
use spacepackets::{PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader};
|
use spacepackets::{PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@@ -869,59 +884,94 @@ mod tests {
|
|||||||
cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent)
|
cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
|
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
|
||||||
let len_packet = base_ping_tc_simple_ctor(0, &[])
|
let len_packet = base_ping_tc_simple_ctor(u14::new(0), &[])
|
||||||
.write_to_bytes(&mut buf[len_time_stamp..])
|
.write_to_bytes(&mut buf[len_time_stamp..])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(
|
(
|
||||||
SpHeader::new_for_unseg_tc(0x02, 0x34, len_packet as u16),
|
SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), len_packet as u16),
|
||||||
len_packet + len_time_stamp,
|
len_packet + len_time_stamp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scheduled_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
|
fn scheduled_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||||
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
||||||
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_app_data], true)
|
PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(11, 4),
|
||||||
|
&buf[..len_app_data],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrong_tc_service(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
|
fn wrong_tc_service(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||||
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
||||||
PusTcCreator::new_simple(sph, 12, 4, &buf[..len_app_data], true)
|
PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(12, 4),
|
||||||
|
&buf[..len_app_data],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrong_tc_subservice(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
|
fn wrong_tc_subservice(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||||
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
||||||
PusTcCreator::new_simple(sph, 11, 5, &buf[..len_app_data], true)
|
PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(11, 5),
|
||||||
|
&buf[..len_app_data],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn double_wrapped_time_tagged_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
|
fn double_wrapped_time_tagged_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||||
let cds_time =
|
let cds_time =
|
||||||
cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent)
|
cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
|
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
|
||||||
let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 0);
|
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
|
||||||
// app data should not matter, double wrapped time-tagged commands should be rejected right
|
// app data should not matter, double wrapped time-tagged commands should be rejected right
|
||||||
// away
|
// away
|
||||||
let inner_time_tagged_tc = PusTcCreator::new_simple(sph, 11, 4, &[], true);
|
let inner_time_tagged_tc = PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(11, 4),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let packet_len = inner_time_tagged_tc
|
let packet_len = inner_time_tagged_tc
|
||||||
.write_to_bytes(&mut buf[len_time_stamp..])
|
.write_to_bytes(&mut buf[len_time_stamp..])
|
||||||
.expect("writing inner time tagged tc failed");
|
.expect("writing inner time tagged tc failed");
|
||||||
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_time_stamp + packet_len], true)
|
PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(11, 4),
|
||||||
|
&buf[..len_time_stamp + packet_len],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalid_time_tagged_cmd() -> PusTcCreator<'static> {
|
fn invalid_time_tagged_cmd() -> PusTcCreator<'static> {
|
||||||
let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 1);
|
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 1);
|
||||||
PusTcCreator::new_simple(sph, 11, 4, &[], true)
|
PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(11, 4),
|
||||||
|
&[],
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_ping_tc_simple_ctor(seq_count: u16, app_data: &'static [u8]) -> PusTcCreator<'static> {
|
fn base_ping_tc_simple_ctor(seq_count: u14, app_data: &'static [u8]) -> PusTcCreator<'static> {
|
||||||
let sph = SpHeader::new_for_unseg_tc(0x02, seq_count, 0);
|
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), seq_count, 0);
|
||||||
PusTcCreator::new_simple(sph, 17, 1, app_data, true)
|
PusTcCreator::new_simple(
|
||||||
|
sph,
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
app_data,
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ping_tc_to_store(
|
fn ping_tc_to_store(
|
||||||
pool: &mut StaticMemoryPool,
|
pool: &mut StaticMemoryPool,
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
app_data: &'static [u8],
|
app_data: &'static [u8],
|
||||||
) -> TcInfo {
|
) -> TcInfo {
|
||||||
let ping_tc = base_ping_tc_simple_ctor(seq_count, app_data);
|
let ping_tc = base_ping_tc_simple_ctor(seq_count, app_data);
|
||||||
@@ -949,7 +999,7 @@ mod tests {
|
|||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
|
||||||
|
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(
|
.insert_unwrapped_and_stored_tc(
|
||||||
@@ -959,7 +1009,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let app_data = &[0, 1, 2];
|
let app_data = &[0, 1, 2];
|
||||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, app_data);
|
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), app_data);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(
|
.insert_unwrapped_and_stored_tc(
|
||||||
UnixTime::new_only_secs(200),
|
UnixTime::new_only_secs(200),
|
||||||
@@ -968,7 +1018,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let app_data = &[0, 1, 2];
|
let app_data = &[0, 1, 2];
|
||||||
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, app_data);
|
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, u14::new(2), app_data);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(
|
.insert_unwrapped_and_stored_tc(
|
||||||
UnixTime::new_only_secs(300),
|
UnixTime::new_only_secs(300),
|
||||||
@@ -999,8 +1049,8 @@ mod tests {
|
|||||||
packet_idx: 1,
|
packet_idx: 1,
|
||||||
}),
|
}),
|
||||||
RequestId {
|
RequestId {
|
||||||
seq_count: 1,
|
seq_count: u14::new(1),
|
||||||
apid: 0,
|
apid: u11::ZERO,
|
||||||
source_id: 0,
|
source_id: 0,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -1016,8 +1066,8 @@ mod tests {
|
|||||||
packet_idx: 2,
|
packet_idx: 2,
|
||||||
}),
|
}),
|
||||||
RequestId {
|
RequestId {
|
||||||
seq_count: 2,
|
seq_count: u14::new(2),
|
||||||
apid: 1,
|
apid: u11::new(1),
|
||||||
source_id: 5,
|
source_id: 5,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -1035,8 +1085,8 @@ mod tests {
|
|||||||
.into(),
|
.into(),
|
||||||
RequestId {
|
RequestId {
|
||||||
source_id: 10,
|
source_id: 10,
|
||||||
seq_count: 20,
|
seq_count: u14::new(20),
|
||||||
apid: 23,
|
apid: u11::new(23),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -1077,19 +1127,22 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_request_id() {
|
fn test_request_id() {
|
||||||
let src_id_to_set = 12;
|
let src_id_to_set = 12;
|
||||||
let apid_to_set = 0x22;
|
let apid_to_set = u11::new(0x22);
|
||||||
let seq_count = 105;
|
let seq_count = u14::new(105);
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(apid_to_set, 105, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(apid_to_set, u14::new(105), 0);
|
||||||
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let mut sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
sec_header.source_id = src_id_to_set;
|
sec_header.source_id = src_id_to_set;
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
let ping_tc =
|
||||||
|
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||||
let req_id = RequestId::from_tc(&ping_tc);
|
let req_id = RequestId::from_tc(&ping_tc);
|
||||||
assert_eq!(req_id.source_id(), src_id_to_set);
|
assert_eq!(req_id.source_id(), src_id_to_set);
|
||||||
assert_eq!(req_id.apid(), apid_to_set);
|
assert_eq!(req_id.apid(), apid_to_set);
|
||||||
assert_eq!(req_id.seq_count(), seq_count);
|
assert_eq!(req_id.seq_count(), seq_count);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req_id.as_u64(),
|
req_id.as_u64(),
|
||||||
((src_id_to_set as u64) << 32) | (apid_to_set as u64) << 16 | seq_count as u64
|
((src_id_to_set as u64) << 32)
|
||||||
|
| (apid_to_set.value() as u64) << 16
|
||||||
|
| seq_count.value() as u64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1101,13 +1154,13 @@ mod tests {
|
|||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
|
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
|
|
||||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
@@ -1169,13 +1222,13 @@ mod tests {
|
|||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
|
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
|
|
||||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
@@ -1231,13 +1284,13 @@ mod tests {
|
|||||||
scheduler.disable();
|
scheduler.disable();
|
||||||
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
|
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
|
|
||||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
@@ -1298,7 +1351,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
|
|
||||||
let info = scheduler
|
let info = scheduler
|
||||||
.insert_unwrapped_tc(
|
.insert_unwrapped_tc(
|
||||||
@@ -1313,7 +1366,7 @@ mod tests {
|
|||||||
let mut read_buf: [u8; 64] = [0; 64];
|
let mut read_buf: [u8; 64] = [0; 64];
|
||||||
pool.read(&tc_info_0.addr(), &mut read_buf).unwrap();
|
pool.read(&tc_info_0.addr(), &mut read_buf).unwrap();
|
||||||
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
|
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
|
||||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(0, &[]));
|
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
|
||||||
|
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
|
||||||
|
|
||||||
@@ -1335,8 +1388,8 @@ mod tests {
|
|||||||
|
|
||||||
let read_len = pool.read(&addr_vec[0], &mut read_buf).unwrap();
|
let read_len = pool.read(&addr_vec[0], &mut read_buf).unwrap();
|
||||||
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
|
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
|
||||||
assert_eq!(read_len, check_tc.total_len());
|
assert_eq!(read_len, check_tc.packet_len());
|
||||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(0, &[]));
|
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1362,8 +1415,8 @@ mod tests {
|
|||||||
|
|
||||||
let read_len = pool.read(&info.addr, &mut buf).unwrap();
|
let read_len = pool.read(&info.addr, &mut buf).unwrap();
|
||||||
let check_tc = PusTcReader::new(&buf).expect("incorrect Pus tc raw data");
|
let check_tc = PusTcReader::new(&buf).expect("incorrect Pus tc raw data");
|
||||||
assert_eq!(read_len, check_tc.total_len());
|
assert_eq!(read_len, check_tc.packet_len());
|
||||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(0, &[]));
|
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
|
||||||
|
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
|
||||||
|
|
||||||
@@ -1387,8 +1440,8 @@ mod tests {
|
|||||||
|
|
||||||
let read_len = pool.read(&addr_vec[0], &mut buf).unwrap();
|
let read_len = pool.read(&addr_vec[0], &mut buf).unwrap();
|
||||||
let check_tc = PusTcReader::new(&buf).expect("incorrect PUS tc raw data");
|
let check_tc = PusTcReader::new(&buf).expect("incorrect PUS tc raw data");
|
||||||
assert_eq!(read_len, check_tc.total_len());
|
assert_eq!(read_len, check_tc.packet_len());
|
||||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(0, &[]));
|
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1531,7 +1584,7 @@ mod tests {
|
|||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
@@ -1568,7 +1621,7 @@ mod tests {
|
|||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
@@ -1594,7 +1647,7 @@ mod tests {
|
|||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("inserting tc failed");
|
.expect("inserting tc failed");
|
||||||
@@ -1615,7 +1668,7 @@ mod tests {
|
|||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("inserting tc failed");
|
.expect("inserting tc failed");
|
||||||
@@ -1636,15 +1689,15 @@ mod tests {
|
|||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("inserting tc failed");
|
.expect("inserting tc failed");
|
||||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
|
||||||
.expect("inserting tc failed");
|
.expect("inserting tc failed");
|
||||||
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, &[]);
|
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, u14::new(2), &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_2)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_2)
|
||||||
.expect("inserting tc failed");
|
.expect("inserting tc failed");
|
||||||
@@ -1703,7 +1756,7 @@ mod tests {
|
|||||||
fn insert_command_with_release_time(
|
fn insert_command_with_release_time(
|
||||||
pool: &mut StaticMemoryPool,
|
pool: &mut StaticMemoryPool,
|
||||||
scheduler: &mut PusScheduler,
|
scheduler: &mut PusScheduler,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
release_secs: u64,
|
release_secs: u64,
|
||||||
) -> TcInfo {
|
) -> TcInfo {
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
@@ -1722,8 +1775,8 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
||||||
let check_range = |range: Range<UnixTime, Vec<TcInfo>>| {
|
let check_range = |range: Range<UnixTime, Vec<TcInfo>>| {
|
||||||
let mut tcs_in_range = 0;
|
let mut tcs_in_range = 0;
|
||||||
@@ -1754,9 +1807,9 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||||
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||||
&UnixTime::new_only_secs(100),
|
&UnixTime::new_only_secs(100),
|
||||||
cds::SubmillisPrecision::Absent,
|
cds::SubmillisPrecision::Absent,
|
||||||
@@ -1789,9 +1842,9 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
||||||
|
|
||||||
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||||
@@ -1824,10 +1877,10 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200);
|
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 200);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
|
||||||
|
|
||||||
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||||
@@ -1865,8 +1918,8 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
||||||
let del_res = scheduler.delete_all(&mut pool);
|
let del_res = scheduler.delete_all(&mut pool);
|
||||||
assert!(del_res.is_ok());
|
assert!(del_res.is_ok());
|
||||||
@@ -1875,8 +1928,8 @@ mod tests {
|
|||||||
// Contrary to reset, this does not disable the scheduler.
|
// Contrary to reset, this does not disable the scheduler.
|
||||||
assert!(scheduler.is_enabled());
|
assert!(scheduler.is_enabled());
|
||||||
|
|
||||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
||||||
let del_res = scheduler
|
let del_res = scheduler
|
||||||
.delete_by_time_filter(TimeWindow::<cds::CdsTime>::new_select_all(), &mut pool);
|
.delete_by_time_filter(TimeWindow::<cds::CdsTime>::new_select_all(), &mut pool);
|
||||||
@@ -1894,9 +1947,11 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
let cmd_0_to_delete =
|
||||||
let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
|
let cmd_1_to_delete =
|
||||||
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
||||||
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||||
&UnixTime::new_only_secs(100),
|
&UnixTime::new_only_secs(100),
|
||||||
@@ -1919,9 +1974,11 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
let cmd_0_to_delete =
|
||||||
let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
let cmd_1_to_delete =
|
||||||
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
||||||
|
|
||||||
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||||
@@ -1945,11 +2002,14 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
let cmd_out_of_range_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
let cmd_out_of_range_0 =
|
||||||
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||||
let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
let cmd_0_to_delete =
|
||||||
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||||
|
let cmd_1_to_delete =
|
||||||
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||||
let cmd_out_of_range_1 =
|
let cmd_out_of_range_1 =
|
||||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200);
|
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 200);
|
||||||
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
|
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
|
||||||
|
|
||||||
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||||
@@ -1982,13 +2042,13 @@ mod tests {
|
|||||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||||
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
|
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||||
|
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
|
|
||||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||||
scheduler
|
scheduler
|
||||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
||||||
.expect("insertion failed");
|
.expect("insertion failed");
|
||||||
@@ -2017,12 +2077,12 @@ mod tests {
|
|||||||
fn test_generic_insert_app_data_test() {
|
fn test_generic_insert_app_data_test() {
|
||||||
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
||||||
let sph = SpHeader::new(
|
let sph = SpHeader::new(
|
||||||
PacketId::new(PacketType::Tc, true, 0x002),
|
PacketId::new(PacketType::Tc, true, u11::new(0x002)),
|
||||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
|
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
|
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
|
||||||
let mut buf: [u8; 64] = [0; 64];
|
let mut buf: [u8; 64] = [0; 64];
|
||||||
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
|
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
@@ -2039,12 +2099,12 @@ mod tests {
|
|||||||
fn test_generic_insert_app_data_test_byte_conv_error() {
|
fn test_generic_insert_app_data_test_byte_conv_error() {
|
||||||
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
||||||
let sph = SpHeader::new(
|
let sph = SpHeader::new(
|
||||||
PacketId::new(PacketType::Tc, true, 0x002),
|
PacketId::new(PacketType::Tc, true, u11::new(0x002)),
|
||||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
|
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
|
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
|
||||||
let mut buf: [u8; 16] = [0; 16];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
|
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
@@ -2068,12 +2128,12 @@ mod tests {
|
|||||||
fn test_generic_insert_app_data_test_as_vec() {
|
fn test_generic_insert_app_data_test_as_vec() {
|
||||||
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
||||||
let sph = SpHeader::new(
|
let sph = SpHeader::new(
|
||||||
PacketId::new(PacketType::Tc, true, 0x002),
|
PacketId::new(PacketType::Tc, true, u11::new(0x002)),
|
||||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
|
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
|
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
|
||||||
let mut buf: [u8; 64] = [0; 64];
|
let mut buf: [u8; 64] = [0; 64];
|
||||||
generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap();
|
generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap();
|
||||||
let vec = generate_insert_telecommand_app_data_as_vec(&time_writer, &ping_tc)
|
let vec = generate_insert_telecommand_app_data_as_vec(&time_writer, &ping_tc)
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use super::scheduler::PusSchedulerProvider;
|
use super::scheduler::PusSchedulerProvider;
|
||||||
use super::verification::{VerificationReporter, VerificationReportingProvider};
|
use super::verification::{VerificationReporter, VerificationReportingProvider};
|
||||||
use super::{
|
use super::{
|
||||||
DirectPusPacketHandlerResult, EcssTcInMemConversionProvider, EcssTcInSharedPoolConverter,
|
CacheAndReadRawEcssTc, DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, EcssTcReceiver,
|
||||||
EcssTcInVecConverter, EcssTcReceiver, EcssTmSender, HandlingStatus, MpscTcReceiver,
|
EcssTcVecCacher, EcssTmSender, HandlingStatus, MpscTcReceiver, PartialPusHandlingError,
|
||||||
PartialPusHandlingError, PusServiceHelper,
|
PusServiceHelper,
|
||||||
};
|
};
|
||||||
use crate::pool::PoolProvider;
|
use crate::pool::PoolProvider;
|
||||||
use crate::pus::PusPacketHandlingError;
|
use crate::pus::PusPacketHandlingError;
|
||||||
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use spacepackets::ecss::{scheduling, PusPacket};
|
use spacepackets::ecss::{PusPacket, scheduling};
|
||||||
use spacepackets::time::cds::CdsTime;
|
use spacepackets::time::cds::CdsTime;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ use std::sync::mpsc;
|
|||||||
pub struct PusSchedServiceHandler<
|
pub struct PusSchedServiceHandler<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
PusScheduler: PusSchedulerProvider,
|
PusScheduler: PusSchedulerProvider,
|
||||||
> {
|
> {
|
||||||
@@ -34,13 +34,12 @@ pub struct PusSchedServiceHandler<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
Scheduler: PusSchedulerProvider,
|
Scheduler: PusSchedulerProvider,
|
||||||
>
|
> PusSchedServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter, Scheduler>
|
||||||
PusSchedServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter, Scheduler>
|
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
service_helper: PusServiceHelper<
|
service_helper: PusServiceHelper<
|
||||||
@@ -80,8 +79,8 @@ impl<
|
|||||||
.tc_in_mem_converter_mut()
|
.tc_in_mem_converter_mut()
|
||||||
.cache(&ecss_tc_and_token.tc_in_memory)?;
|
.cache(&ecss_tc_and_token.tc_in_memory)?;
|
||||||
let tc = self.service_helper.tc_in_mem_converter().convert()?;
|
let tc = self.service_helper.tc_in_mem_converter().convert()?;
|
||||||
let subservice = PusPacket::subservice(&tc);
|
let subservice = PusPacket::message_subtype_id(&tc);
|
||||||
let standard_subservice = scheduling::Subservice::try_from(subservice);
|
let standard_subservice = scheduling::MessageSubtypeId::try_from(subservice);
|
||||||
if standard_subservice.is_err() {
|
if standard_subservice.is_err() {
|
||||||
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
|
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
|
||||||
subservice,
|
subservice,
|
||||||
@@ -89,7 +88,7 @@ impl<
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
match standard_subservice.unwrap() {
|
match standard_subservice.unwrap() {
|
||||||
scheduling::Subservice::TcEnableScheduling => {
|
scheduling::MessageSubtypeId::TcEnableScheduling => {
|
||||||
let opt_started_token = match self.service_helper.verif_reporter().start_success(
|
let opt_started_token = match self.service_helper.verif_reporter().start_success(
|
||||||
&self.service_helper.common.tm_sender,
|
&self.service_helper.common.tm_sender,
|
||||||
ecss_tc_and_token.token,
|
ecss_tc_and_token.token,
|
||||||
@@ -102,11 +101,12 @@ impl<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.scheduler.enable();
|
self.scheduler.enable();
|
||||||
|
if self.scheduler.is_enabled()
|
||||||
if self.scheduler.is_enabled() && opt_started_token.is_some() {
|
&& let Some(started_token) = opt_started_token
|
||||||
|
{
|
||||||
if let Err(e) = self.service_helper.verif_reporter().completion_success(
|
if let Err(e) = self.service_helper.verif_reporter().completion_success(
|
||||||
&self.service_helper.common.tm_sender,
|
&self.service_helper.common.tm_sender,
|
||||||
opt_started_token.unwrap(),
|
started_token,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
) {
|
) {
|
||||||
error_callback(&PartialPusHandlingError::Verification(e));
|
error_callback(&PartialPusHandlingError::Verification(e));
|
||||||
@@ -117,7 +117,7 @@ impl<
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scheduling::Subservice::TcDisableScheduling => {
|
scheduling::MessageSubtypeId::TcDisableScheduling => {
|
||||||
let opt_started_token = match self.service_helper.verif_reporter().start_success(
|
let opt_started_token = match self.service_helper.verif_reporter().start_success(
|
||||||
&self.service_helper.common.tm_sender,
|
&self.service_helper.common.tm_sender,
|
||||||
ecss_tc_and_token.token,
|
ecss_tc_and_token.token,
|
||||||
@@ -131,10 +131,12 @@ impl<
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.scheduler.disable();
|
self.scheduler.disable();
|
||||||
if !self.scheduler.is_enabled() && opt_started_token.is_some() {
|
if !self.scheduler.is_enabled()
|
||||||
|
&& let Some(started_token) = opt_started_token
|
||||||
|
{
|
||||||
if let Err(e) = self.service_helper.verif_reporter().completion_success(
|
if let Err(e) = self.service_helper.verif_reporter().completion_success(
|
||||||
&self.service_helper.common.tm_sender,
|
&self.service_helper.common.tm_sender,
|
||||||
opt_started_token.unwrap(),
|
started_token,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
) {
|
) {
|
||||||
error_callback(&PartialPusHandlingError::Verification(e));
|
error_callback(&PartialPusHandlingError::Verification(e));
|
||||||
@@ -145,7 +147,7 @@ impl<
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scheduling::Subservice::TcResetScheduling => {
|
scheduling::MessageSubtypeId::TcResetScheduling => {
|
||||||
let start_token = self
|
let start_token = self
|
||||||
.service_helper
|
.service_helper
|
||||||
.verif_reporter()
|
.verif_reporter()
|
||||||
@@ -169,7 +171,7 @@ impl<
|
|||||||
)
|
)
|
||||||
.expect("Error sending completion success");
|
.expect("Error sending completion success");
|
||||||
}
|
}
|
||||||
scheduling::Subservice::TcInsertActivity => {
|
scheduling::MessageSubtypeId::TcInsertActivity => {
|
||||||
let start_token = self
|
let start_token = self
|
||||||
.service_helper
|
.service_helper
|
||||||
.common
|
.common
|
||||||
@@ -211,7 +213,7 @@ impl<
|
|||||||
pub type PusService11SchedHandlerDynWithMpsc<PusScheduler> = PusSchedServiceHandler<
|
pub type PusService11SchedHandlerDynWithMpsc<PusScheduler> = PusSchedServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
mpsc::Sender<PacketAsVec>,
|
mpsc::Sender<PacketAsVec>,
|
||||||
EcssTcInVecConverter,
|
EcssTcVecCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
PusScheduler,
|
PusScheduler,
|
||||||
>;
|
>;
|
||||||
@@ -220,7 +222,7 @@ pub type PusService11SchedHandlerDynWithMpsc<PusScheduler> = PusSchedServiceHand
|
|||||||
pub type PusService11SchedHandlerDynWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler<
|
pub type PusService11SchedHandlerDynWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
mpsc::SyncSender<PacketAsVec>,
|
mpsc::SyncSender<PacketAsVec>,
|
||||||
EcssTcInVecConverter,
|
EcssTcVecCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
PusScheduler,
|
PusScheduler,
|
||||||
>;
|
>;
|
||||||
@@ -229,7 +231,7 @@ pub type PusService11SchedHandlerDynWithBoundedMpsc<PusScheduler> = PusSchedServ
|
|||||||
pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusSchedServiceHandler<
|
pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusSchedServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
PacketSenderWithSharedPool,
|
PacketSenderWithSharedPool,
|
||||||
EcssTcInSharedPoolConverter,
|
EcssTcInSharedPoolCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
PusScheduler,
|
PusScheduler,
|
||||||
>;
|
>;
|
||||||
@@ -238,7 +240,7 @@ pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusSchedServiceH
|
|||||||
pub type PusService11SchedHandlerStaticWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler<
|
pub type PusService11SchedHandlerStaticWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
PacketSenderWithSharedPool,
|
PacketSenderWithSharedPool,
|
||||||
EcssTcInSharedPoolConverter,
|
EcssTcInSharedPoolCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
PusScheduler,
|
PusScheduler,
|
||||||
>;
|
>;
|
||||||
@@ -249,21 +251,23 @@ mod tests {
|
|||||||
use crate::pus::test_util::{PusTestHarness, TEST_APID};
|
use crate::pus::test_util::{PusTestHarness, TEST_APID};
|
||||||
use crate::pus::verification::{VerificationReporter, VerificationReportingProvider};
|
use crate::pus::verification::{VerificationReporter, VerificationReportingProvider};
|
||||||
|
|
||||||
|
use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError};
|
||||||
use crate::pus::{
|
use crate::pus::{
|
||||||
|
EcssTcInSharedPoolCacher,
|
||||||
scheduler::{self, PusSchedulerProvider, TcInfo},
|
scheduler::{self, PusSchedulerProvider, TcInfo},
|
||||||
tests::PusServiceHandlerWithSharedStoreCommon,
|
tests::PusServiceHandlerWithSharedStoreCommon,
|
||||||
verification::{RequestId, TcStateAccepted, VerificationToken},
|
verification::{RequestId, TcStateAccepted, VerificationToken},
|
||||||
EcssTcInSharedPoolConverter,
|
|
||||||
};
|
};
|
||||||
use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError};
|
|
||||||
use crate::tmtc::PacketSenderWithSharedPool;
|
use crate::tmtc::PacketSenderWithSharedPool;
|
||||||
use alloc::collections::VecDeque;
|
use alloc::collections::VecDeque;
|
||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
|
use arbitrary_int::u14;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
use spacepackets::ecss::scheduling::Subservice;
|
|
||||||
use spacepackets::ecss::tc::PusTcSecondaryHeader;
|
|
||||||
use spacepackets::ecss::WritablePusPacket;
|
|
||||||
use spacepackets::time::TimeWriter;
|
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
|
use spacepackets::ecss::scheduling::MessageSubtypeId;
|
||||||
|
use spacepackets::ecss::tc::PusTcSecondaryHeader;
|
||||||
|
use spacepackets::ecss::{CreatorConfig, MessageTypeId, WritablePusPacket};
|
||||||
|
use spacepackets::time::TimeWriter;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, tm::PusTmReader},
|
ecss::{tc::PusTcCreator, tm::PusTmReader},
|
||||||
time::cds,
|
time::cds,
|
||||||
@@ -276,7 +280,7 @@ mod tests {
|
|||||||
handler: PusSchedServiceHandler<
|
handler: PusSchedServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
PacketSenderWithSharedPool,
|
PacketSenderWithSharedPool,
|
||||||
EcssTcInSharedPoolConverter,
|
EcssTcInSharedPoolCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
TestScheduler,
|
TestScheduler,
|
||||||
>,
|
>,
|
||||||
@@ -382,11 +386,12 @@ mod tests {
|
|||||||
|
|
||||||
fn generic_subservice_send(
|
fn generic_subservice_send(
|
||||||
test_harness: &mut Pus11HandlerWithStoreTester,
|
test_harness: &mut Pus11HandlerWithStoreTester,
|
||||||
subservice: Subservice,
|
subservice: MessageSubtypeId,
|
||||||
) {
|
) {
|
||||||
let reply_header = SpHeader::new_for_unseg_tm(TEST_APID, 0, 0);
|
let reply_header = SpHeader::new_for_unseg_tm(TEST_APID, u14::ZERO, 0);
|
||||||
let tc_header = PusTcSecondaryHeader::new_simple(11, subservice as u8);
|
let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(11, subservice as u8));
|
||||||
let enable_scheduling = PusTcCreator::new(reply_header, tc_header, &[0; 7], true);
|
let enable_scheduling =
|
||||||
|
PusTcCreator::new(reply_header, tc_header, &[0; 7], CreatorConfig::default());
|
||||||
let token = test_harness.start_verification(&enable_scheduling);
|
let token = test_harness.start_verification(&enable_scheduling);
|
||||||
test_harness.send_tc(&token, &enable_scheduling);
|
test_harness.send_tc(&token, &enable_scheduling);
|
||||||
|
|
||||||
@@ -406,7 +411,7 @@ mod tests {
|
|||||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||||
test_harness.handler.scheduler_mut().disable();
|
test_harness.handler.scheduler_mut().disable();
|
||||||
assert!(!test_harness.handler.scheduler().is_enabled());
|
assert!(!test_harness.handler.scheduler().is_enabled());
|
||||||
generic_subservice_send(&mut test_harness, Subservice::TcEnableScheduling);
|
generic_subservice_send(&mut test_harness, MessageSubtypeId::TcEnableScheduling);
|
||||||
assert!(test_harness.handler.scheduler().is_enabled());
|
assert!(test_harness.handler.scheduler().is_enabled());
|
||||||
assert_eq!(test_harness.handler.scheduler().enabled_count, 1);
|
assert_eq!(test_harness.handler.scheduler().enabled_count, 1);
|
||||||
}
|
}
|
||||||
@@ -416,7 +421,7 @@ mod tests {
|
|||||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||||
test_harness.handler.scheduler_mut().enable();
|
test_harness.handler.scheduler_mut().enable();
|
||||||
assert!(test_harness.handler.scheduler().is_enabled());
|
assert!(test_harness.handler.scheduler().is_enabled());
|
||||||
generic_subservice_send(&mut test_harness, Subservice::TcDisableScheduling);
|
generic_subservice_send(&mut test_harness, MessageSubtypeId::TcDisableScheduling);
|
||||||
assert!(!test_harness.handler.scheduler().is_enabled());
|
assert!(!test_harness.handler.scheduler().is_enabled());
|
||||||
assert_eq!(test_harness.handler.scheduler().disabled_count, 1);
|
assert_eq!(test_harness.handler.scheduler().disabled_count, 1);
|
||||||
}
|
}
|
||||||
@@ -424,16 +429,16 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_reset_scheduler_tc() {
|
fn test_reset_scheduler_tc() {
|
||||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||||
generic_subservice_send(&mut test_harness, Subservice::TcResetScheduling);
|
generic_subservice_send(&mut test_harness, MessageSubtypeId::TcResetScheduling);
|
||||||
assert_eq!(test_harness.handler.scheduler().reset_count, 1);
|
assert_eq!(test_harness.handler.scheduler().reset_count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_insert_activity_tc() {
|
fn test_insert_activity_tc() {
|
||||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||||
let mut reply_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let mut reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let mut sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
let ping_tc = PusTcCreator::new(reply_header, sec_header, &[], true);
|
let ping_tc = PusTcCreator::new(reply_header, sec_header, &[], CreatorConfig::default());
|
||||||
let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
|
let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
|
||||||
let stamper = cds::CdsTime::now_with_u16_days().expect("time provider failed");
|
let stamper = cds::CdsTime::now_with_u16_days().expect("time provider failed");
|
||||||
let mut sched_app_data: [u8; 64] = [0; 64];
|
let mut sched_app_data: [u8; 64] = [0; 64];
|
||||||
@@ -441,13 +446,16 @@ mod tests {
|
|||||||
let ping_raw = ping_tc.to_vec().expect("generating raw tc failed");
|
let ping_raw = ping_tc.to_vec().expect("generating raw tc failed");
|
||||||
sched_app_data[written_len..written_len + ping_raw.len()].copy_from_slice(&ping_raw);
|
sched_app_data[written_len..written_len + ping_raw.len()].copy_from_slice(&ping_raw);
|
||||||
written_len += ping_raw.len();
|
written_len += ping_raw.len();
|
||||||
reply_header = SpHeader::new_for_unseg_tc(TEST_APID, 1, 0);
|
reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
|
sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
|
||||||
|
11,
|
||||||
|
MessageSubtypeId::TcInsertActivity as u8,
|
||||||
|
));
|
||||||
let enable_scheduling = PusTcCreator::new(
|
let enable_scheduling = PusTcCreator::new(
|
||||||
reply_header,
|
reply_header,
|
||||||
sec_header,
|
sec_header,
|
||||||
&sched_app_data[..written_len],
|
&sched_app_data[..written_len],
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
);
|
);
|
||||||
let token = test_harness.start_verification(&enable_scheduling);
|
let token = test_harness.start_verification(&enable_scheduling);
|
||||||
test_harness.send_tc(&token, &enable_scheduling);
|
test_harness.send_tc(&token, &enable_scheduling);
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ use crate::pus::{
|
|||||||
DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError, PusTmVariant,
|
DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError, PusTmVariant,
|
||||||
};
|
};
|
||||||
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
||||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
use arbitrary_int::traits::Integer as _;
|
||||||
use spacepackets::ecss::PusPacket;
|
use arbitrary_int::u14;
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
|
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
|
use spacepackets::ecss::{CreatorConfig, MessageTypeId, PusPacket};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use super::verification::{VerificationReporter, VerificationReportingProvider};
|
use super::verification::{VerificationReporter, VerificationReportingProvider};
|
||||||
use super::{
|
use super::{
|
||||||
EcssTcInMemConversionProvider, EcssTcInSharedPoolConverter, EcssTcInVecConverter,
|
CacheAndReadRawEcssTc, EcssTcInSharedPoolCacher, EcssTcReceiver, EcssTcVecCacher, EcssTmSender,
|
||||||
EcssTcReceiver, EcssTmSender, GenericConversionError, HandlingStatus, MpscTcReceiver,
|
GenericConversionError, HandlingStatus, MpscTcReceiver, PusServiceHelper,
|
||||||
PusServiceHelper,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
|
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
|
||||||
@@ -19,7 +20,7 @@ use super::{
|
|||||||
pub struct PusService17TestHandler<
|
pub struct PusService17TestHandler<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
> {
|
> {
|
||||||
pub service_helper:
|
pub service_helper:
|
||||||
@@ -27,11 +28,11 @@ pub struct PusService17TestHandler<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
TmSender: EcssTmSender,
|
TmSender: EcssTmSender,
|
||||||
TcInMemConverter: EcssTcInMemConversionProvider,
|
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
> PusService17TestHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
|
> PusService17TestHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
service_helper: PusServiceHelper<
|
service_helper: PusServiceHelper<
|
||||||
@@ -58,10 +59,10 @@ impl<
|
|||||||
.tc_in_mem_converter_mut()
|
.tc_in_mem_converter_mut()
|
||||||
.cache(&ecss_tc_and_token.tc_in_memory)?;
|
.cache(&ecss_tc_and_token.tc_in_memory)?;
|
||||||
let tc = self.service_helper.tc_in_mem_converter().convert()?;
|
let tc = self.service_helper.tc_in_mem_converter().convert()?;
|
||||||
if tc.service() != 17 {
|
if tc.service_type_id() != 17 {
|
||||||
return Err(GenericConversionError::WrongService(tc.service()).into());
|
return Err(GenericConversionError::WrongService(tc.service_type_id()).into());
|
||||||
}
|
}
|
||||||
if tc.subservice() == 1 {
|
if tc.message_subtype_id() == 1 {
|
||||||
let opt_started_token = match self.service_helper.verif_reporter().start_success(
|
let opt_started_token = match self.service_helper.verif_reporter().start_success(
|
||||||
&self.service_helper.common.tm_sender,
|
&self.service_helper.common.tm_sender,
|
||||||
ecss_tc_and_token.token,
|
ecss_tc_and_token.token,
|
||||||
@@ -76,10 +77,14 @@ impl<
|
|||||||
// Sequence count will be handled centrally in TM funnel.
|
// Sequence count will be handled centrally in TM funnel.
|
||||||
// It is assumed that the verification reporter was built with a valid APID, so we use
|
// It is assumed that the verification reporter was built with a valid APID, so we use
|
||||||
// the unchecked API here.
|
// the unchecked API here.
|
||||||
let reply_header =
|
let reply_header = SpHeader::new_for_unseg_tm(
|
||||||
SpHeader::new_for_unseg_tm(self.service_helper.verif_reporter().apid(), 0, 0);
|
self.service_helper.verif_reporter().apid(),
|
||||||
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp);
|
u14::ZERO,
|
||||||
let ping_reply = PusTmCreator::new(reply_header, tc_header, &[], true);
|
0,
|
||||||
|
);
|
||||||
|
let tc_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), time_stamp);
|
||||||
|
let ping_reply =
|
||||||
|
PusTmCreator::new(reply_header, tc_header, &[], CreatorConfig::default());
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
.service_helper
|
.service_helper
|
||||||
.common
|
.common
|
||||||
@@ -99,7 +104,7 @@ impl<
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
|
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
|
||||||
tc.subservice(),
|
tc.message_subtype_id(),
|
||||||
ecss_tc_and_token.token,
|
ecss_tc_and_token.token,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -112,7 +117,7 @@ impl<
|
|||||||
pub type PusService17TestHandlerDynWithMpsc = PusService17TestHandler<
|
pub type PusService17TestHandlerDynWithMpsc = PusService17TestHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
mpsc::Sender<PacketAsVec>,
|
mpsc::Sender<PacketAsVec>,
|
||||||
EcssTcInVecConverter,
|
EcssTcVecCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>;
|
>;
|
||||||
/// Helper type definition for a PUS 17 handler with a dynamic TMTC memory backend and bounded MPSC
|
/// Helper type definition for a PUS 17 handler with a dynamic TMTC memory backend and bounded MPSC
|
||||||
@@ -120,7 +125,7 @@ pub type PusService17TestHandlerDynWithMpsc = PusService17TestHandler<
|
|||||||
pub type PusService17TestHandlerDynWithBoundedMpsc = PusService17TestHandler<
|
pub type PusService17TestHandlerDynWithBoundedMpsc = PusService17TestHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
mpsc::SyncSender<PacketAsVec>,
|
mpsc::SyncSender<PacketAsVec>,
|
||||||
EcssTcInVecConverter,
|
EcssTcVecCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>;
|
>;
|
||||||
/// Helper type definition for a PUS 17 handler with a shared store TMTC memory backend and bounded
|
/// Helper type definition for a PUS 17 handler with a shared store TMTC memory backend and bounded
|
||||||
@@ -128,12 +133,13 @@ pub type PusService17TestHandlerDynWithBoundedMpsc = PusService17TestHandler<
|
|||||||
pub type PusService17TestHandlerStaticWithBoundedMpsc = PusService17TestHandler<
|
pub type PusService17TestHandlerStaticWithBoundedMpsc = PusService17TestHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
PacketSenderWithSharedPool,
|
PacketSenderWithSharedPool,
|
||||||
EcssTcInSharedPoolConverter,
|
EcssTcInSharedPoolCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::ComponentId;
|
||||||
use crate::pus::test_util::{PusTestHarness, SimplePusPacketHandler, TEST_APID};
|
use crate::pus::test_util::{PusTestHarness, SimplePusPacketHandler, TEST_APID};
|
||||||
use crate::pus::tests::{
|
use crate::pus::tests::{
|
||||||
PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon,
|
PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon,
|
||||||
@@ -143,18 +149,19 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use crate::pus::verification::{TcStateAccepted, VerificationToken};
|
use crate::pus::verification::{TcStateAccepted, VerificationToken};
|
||||||
use crate::pus::{
|
use crate::pus::{
|
||||||
DirectPusPacketHandlerResult, EcssTcInSharedPoolConverter, EcssTcInVecConverter,
|
DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, EcssTcVecCacher,
|
||||||
GenericConversionError, HandlingStatus, MpscTcReceiver, MpscTmAsVecSender,
|
GenericConversionError, HandlingStatus, MpscTcReceiver, MpscTmAsVecSender,
|
||||||
PartialPusHandlingError, PusPacketHandlingError,
|
PartialPusHandlingError, PusPacketHandlingError,
|
||||||
};
|
};
|
||||||
use crate::tmtc::PacketSenderWithSharedPool;
|
use crate::tmtc::PacketSenderWithSharedPool;
|
||||||
use crate::ComponentId;
|
use arbitrary_int::traits::Integer as _;
|
||||||
|
use arbitrary_int::u14;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
|
use spacepackets::SpHeader;
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
||||||
use spacepackets::ecss::tm::PusTmReader;
|
use spacepackets::ecss::tm::PusTmReader;
|
||||||
use spacepackets::ecss::PusPacket;
|
use spacepackets::ecss::{CreatorConfig, MessageTypeId, PusPacket};
|
||||||
use spacepackets::time::{cds, TimeWriter};
|
use spacepackets::time::{TimeWriter, cds};
|
||||||
use spacepackets::SpHeader;
|
|
||||||
|
|
||||||
use super::PusService17TestHandler;
|
use super::PusService17TestHandler;
|
||||||
|
|
||||||
@@ -163,7 +170,7 @@ mod tests {
|
|||||||
handler: PusService17TestHandler<
|
handler: PusService17TestHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
PacketSenderWithSharedPool,
|
PacketSenderWithSharedPool,
|
||||||
EcssTcInSharedPoolConverter,
|
EcssTcInSharedPoolCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
@@ -225,7 +232,7 @@ mod tests {
|
|||||||
handler: PusService17TestHandler<
|
handler: PusService17TestHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
MpscTmAsVecSender,
|
MpscTmAsVecSender,
|
||||||
EcssTcInVecConverter,
|
EcssTcVecCacher,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
@@ -284,9 +291,10 @@ mod tests {
|
|||||||
|
|
||||||
fn ping_test(test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler)) {
|
fn ping_test(test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler)) {
|
||||||
// Create a ping TC, verify acceptance.
|
// Create a ping TC, verify acceptance.
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
let ping_tc =
|
||||||
|
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||||
let token = test_harness.start_verification(&ping_tc);
|
let token = test_harness.start_verification(&ping_tc);
|
||||||
test_harness.send_tc(&token, &ping_tc);
|
test_harness.send_tc(&token, &ping_tc);
|
||||||
let request_id = token.request_id();
|
let request_id = token.request_id();
|
||||||
@@ -303,8 +311,8 @@ mod tests {
|
|||||||
|
|
||||||
// Ping reply
|
// Ping reply
|
||||||
let tm = test_harness.read_next_tm();
|
let tm = test_harness.read_next_tm();
|
||||||
assert_eq!(tm.service(), 17);
|
assert_eq!(tm.service_type_id(), 17);
|
||||||
assert_eq!(tm.subservice(), 2);
|
assert_eq!(tm.message_subtype_id(), 2);
|
||||||
assert!(tm.user_data().is_empty());
|
assert!(tm.user_data().is_empty());
|
||||||
|
|
||||||
// TM completion
|
// TM completion
|
||||||
@@ -339,9 +347,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_sending_unsupported_service() {
|
fn test_sending_unsupported_service() {
|
||||||
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
|
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(3, 1);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(3, 1));
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
let ping_tc =
|
||||||
|
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||||
let token = test_harness.start_verification(&ping_tc);
|
let token = test_harness.start_verification(&ping_tc);
|
||||||
test_harness.send_tc(&token, &ping_tc);
|
test_harness.send_tc(&token, &ping_tc);
|
||||||
let result = test_harness.handle_one_tc();
|
let result = test_harness.handle_one_tc();
|
||||||
@@ -360,9 +369,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_sending_custom_subservice() {
|
fn test_sending_custom_subservice() {
|
||||||
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
|
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 200);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 200));
|
||||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
let ping_tc =
|
||||||
|
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||||
let token = test_harness.start_verification(&ping_tc);
|
let token = test_harness.start_verification(&ping_tc);
|
||||||
test_harness.send_tc(&token, &ping_tc);
|
test_harness.send_tc(&token, &ping_tc);
|
||||||
let result = test_harness.handle_one_tc();
|
let result = test_harness.handle_one_tc();
|
||||||
|
|||||||
@@ -17,19 +17,19 @@
|
|||||||
//! use std::time::Duration;
|
//! use std::time::Duration;
|
||||||
//! use satrs::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
|
//! use satrs::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
|
||||||
//! use satrs::pus::verification::{
|
//! use satrs::pus::verification::{
|
||||||
//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporter
|
//! VerificationReportingProvider, VerificationReporterConfig, VerificationReporter
|
||||||
//! };
|
//! };
|
||||||
//! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool};
|
//! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool};
|
||||||
//! use satrs::spacepackets::seq_count::SeqCountProviderSimple;
|
|
||||||
//! use satrs::request::UniqueApidTargetId;
|
//! use satrs::request::UniqueApidTargetId;
|
||||||
//! use spacepackets::ecss::PusPacket;
|
//! use spacepackets::ecss::PusPacket;
|
||||||
//! use spacepackets::SpHeader;
|
//! use spacepackets::SpHeader;
|
||||||
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
//! use spacepackets::ecss::tc::{MessageTypeId, PusTcCreator, PusTcSecondaryHeader, CreatorConfig};
|
||||||
//! use spacepackets::ecss::tm::PusTmReader;
|
//! use spacepackets::ecss::tm::PusTmReader;
|
||||||
|
//! use arbitrary_int::{u11, u21};
|
||||||
//!
|
//!
|
||||||
//! const EMPTY_STAMP: [u8; 7] = [0; 7];
|
//! const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||||
//! const TEST_APID: u16 = 0x02;
|
//! const TEST_APID: u11 = u11::new(0x02);
|
||||||
//! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
|
//! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, u21::new(0x05));
|
||||||
//!
|
//!
|
||||||
//! let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
|
//! let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
|
||||||
//! vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false
|
//! vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false
|
||||||
@@ -38,14 +38,14 @@
|
|||||||
//! let shared_tm_pool = SharedStaticMemoryPool::new(RwLock::new(tm_pool));
|
//! let shared_tm_pool = SharedStaticMemoryPool::new(RwLock::new(tm_pool));
|
||||||
//! let (verif_tx, verif_rx) = mpsc::sync_channel(10);
|
//! let (verif_tx, verif_rx) = mpsc::sync_channel(10);
|
||||||
//! let sender = PacketSenderWithSharedPool::new_with_shared_packet_pool(verif_tx, &shared_tm_pool);
|
//! let sender = PacketSenderWithSharedPool::new_with_shared_packet_pool(verif_tx, &shared_tm_pool);
|
||||||
//! let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
//! let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||||
//! let mut reporter = VerificationReporter::new(TEST_COMPONENT_ID.id(), &cfg);
|
//! let mut reporter = VerificationReporter::new(TEST_COMPONENT_ID.id(), &cfg);
|
||||||
//!
|
//!
|
||||||
//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
//! let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
//! let pus_tc_0 = PusTcCreator::new_no_app_data(
|
//! let pus_tc_0 = PusTcCreator::new_no_app_data(
|
||||||
//! SpHeader::new_from_apid(TEST_APID),
|
//! SpHeader::new_from_apid(TEST_APID),
|
||||||
//! tc_header,
|
//! tc_header,
|
||||||
//! true
|
//! CreatorConfig::default()
|
||||||
//! );
|
//! );
|
||||||
//! let init_token = reporter.start_verification(&pus_tc_0);
|
//! let init_token = reporter.start_verification(&pus_tc_0);
|
||||||
//!
|
//!
|
||||||
@@ -67,11 +67,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! let pus_tm = PusTmReader::new(&tm_buf[0..tm_len], 7).expect("Error reading verification TM");
|
//! let pus_tm = PusTmReader::new(&tm_buf[0..tm_len], 7).expect("Error reading verification TM");
|
||||||
//! if packet_idx == 0 {
|
//! if packet_idx == 0 {
|
||||||
//! assert_eq!(pus_tm.subservice(), 1);
|
//! assert_eq!(pus_tm.message_subtype_id(), 1);
|
||||||
//! } else if packet_idx == 1 {
|
//! } else if packet_idx == 1 {
|
||||||
//! assert_eq!(pus_tm.subservice(), 3);
|
//! assert_eq!(pus_tm.message_subtype_id(), 3);
|
||||||
//! } else if packet_idx == 2 {
|
//! } else if packet_idx == 2 {
|
||||||
//! assert_eq!(pus_tm.subservice(), 7);
|
//! assert_eq!(pus_tm.message_subtype_id(), 7);
|
||||||
//! }
|
//! }
|
||||||
//! packet_idx += 1;
|
//! packet_idx += 1;
|
||||||
//! }
|
//! }
|
||||||
@@ -81,7 +81,8 @@
|
|||||||
//! for the verification module contains examples how this module could be used in a more complex
|
//! for the verification module contains examples how this module could be used in a more complex
|
||||||
//! context involving multiple threads
|
//! context involving multiple threads
|
||||||
use crate::params::{Params, WritableToBeBytes};
|
use crate::params::{Params, WritableToBeBytes};
|
||||||
use crate::pus::{source_buffer_large_enough, EcssTmSender, EcssTmtcError};
|
use crate::pus::{EcssTmSender, EcssTmtcError, source_buffer_large_enough};
|
||||||
|
use arbitrary_int::{u3, u11, u14};
|
||||||
use core::fmt::{Debug, Display, Formatter};
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
@@ -90,20 +91,20 @@ use core::mem::size_of;
|
|||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use spacepackets::SpHeader;
|
||||||
use spacepackets::ecss::tc::IsPusTelecommand;
|
use spacepackets::ecss::tc::IsPusTelecommand;
|
||||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
use spacepackets::ecss::EcssEnumeration;
|
use spacepackets::ecss::{CreatorConfig, EcssEnumeration, MessageTypeId};
|
||||||
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceCtrl};
|
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl};
|
||||||
use spacepackets::{SpHeader, MAX_APID};
|
|
||||||
|
|
||||||
pub use spacepackets::ecss::verification::*;
|
pub use spacepackets::ecss::verification::*;
|
||||||
pub use spacepackets::seq_count::SeqCountProviderSimple;
|
pub use spacepackets::seq_count::SequenceCounterSimple;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
|
|
||||||
use crate::request::Apid;
|
|
||||||
use crate::ComponentId;
|
use crate::ComponentId;
|
||||||
|
use crate::request::Apid;
|
||||||
|
|
||||||
/// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard.
|
/// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard.
|
||||||
///
|
///
|
||||||
@@ -113,9 +114,9 @@ use crate::ComponentId;
|
|||||||
#[derive(Debug, Eq, Copy, Clone)]
|
#[derive(Debug, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct RequestId {
|
pub struct RequestId {
|
||||||
version_number: u8,
|
version_number: u3,
|
||||||
packet_id: PacketId,
|
packet_id: PacketId,
|
||||||
psc: PacketSequenceCtrl,
|
psc: PacketSequenceControl,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RequestId {
|
impl Display for RequestId {
|
||||||
@@ -157,16 +158,18 @@ impl RequestId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw(&self) -> u32 {
|
pub fn raw(&self) -> u32 {
|
||||||
((self.version_number as u32) << 29)
|
((self.version_number.value() as u32) << 29)
|
||||||
| ((self.packet_id.raw() as u32) << 16)
|
| ((self.packet_id.raw() as u32) << 16)
|
||||||
| self.psc.raw() as u32
|
| self.psc.raw() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn packet_id(&self) -> PacketId {
|
#[inline]
|
||||||
|
pub const fn packet_id(&self) -> PacketId {
|
||||||
self.packet_id
|
self.packet_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn packet_seq_ctrl(&self) -> PacketSequenceCtrl {
|
#[inline]
|
||||||
|
pub const fn packet_seq_ctrl(&self) -> PacketSequenceControl {
|
||||||
self.psc
|
self.psc
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,9 +184,9 @@ impl RequestId {
|
|||||||
}
|
}
|
||||||
let raw = u32::from_be_bytes(buf[0..Self::SIZE_AS_BYTES].try_into().unwrap());
|
let raw = u32::from_be_bytes(buf[0..Self::SIZE_AS_BYTES].try_into().unwrap());
|
||||||
Some(Self {
|
Some(Self {
|
||||||
version_number: ((raw >> 29) & 0b111) as u8,
|
version_number: u3::new(((raw >> 29) & 0b111) as u8),
|
||||||
packet_id: PacketId::from(((raw >> 16) & 0xffff) as u16),
|
packet_id: PacketId::from(((raw >> 16) & 0xffff) as u16),
|
||||||
psc: PacketSequenceCtrl::from((raw & 0xffff) as u16),
|
psc: PacketSequenceControl::from((raw & 0xffff) as u16),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,9 +194,9 @@ impl RequestId {
|
|||||||
impl From<u32> for RequestId {
|
impl From<u32> for RequestId {
|
||||||
fn from(value: u32) -> Self {
|
fn from(value: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
version_number: ((value >> 29) & 0b111) as u8,
|
version_number: u3::new(((value >> 29) & 0b111) as u8),
|
||||||
packet_id: PacketId::from(((value >> 16) & 0xffff) as u16),
|
packet_id: PacketId::from(((value >> 16) & 0xffff) as u16),
|
||||||
psc: PacketSequenceCtrl::from((value & 0xffff) as u16),
|
psc: PacketSequenceControl::from((value & 0xffff) as u16),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,26 +483,19 @@ pub trait VerificationReportingProvider {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct VerificationReportCreator {
|
pub struct VerificationReportCreator {
|
||||||
pub dest_id: u16,
|
pub dest_id: u16,
|
||||||
apid: u16,
|
apid: u11,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationReportCreator {
|
impl VerificationReportCreator {
|
||||||
pub fn new(apid: u16) -> Option<Self> {
|
pub fn new(apid: u11) -> Self {
|
||||||
if apid > MAX_APID {
|
Self { apid, dest_id: 0 }
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Self { apid, dest_id: 0 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_apid(&mut self, apid: u16) -> bool {
|
pub fn set_apid(&mut self, apid: u11) {
|
||||||
if apid > MAX_APID {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
self.apid = apid;
|
self.apid = apid;
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apid(&self) -> u16 {
|
pub fn apid(&self) -> u11 {
|
||||||
self.apid
|
self.apid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,9 +507,7 @@ impl VerificationReportCreator {
|
|||||||
self.dest_id = dest_id;
|
self.dest_id = dest_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize verification handling by passing a TC reference. This returns a token required
|
pub fn read_request_id(&self, pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> RequestId {
|
||||||
/// to call the acceptance functions
|
|
||||||
pub fn read_request_id_from_tc(pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> RequestId {
|
|
||||||
RequestId::new(pus_tc)
|
RequestId::new(pus_tc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +516,7 @@ impl VerificationReportCreator {
|
|||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
@@ -545,7 +539,7 @@ impl VerificationReportCreator {
|
|||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
step: Option<&(impl EcssEnumeration + ?Sized)>,
|
step: Option<&(impl EcssEnumeration + ?Sized)>,
|
||||||
params: &FailParams<'time, '_>,
|
params: &FailParams<'time, '_>,
|
||||||
@@ -567,13 +561,13 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
let tm_creator = self.success_verification_no_step(
|
let tm_creator = self.success_verification_no_step(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmAcceptanceSuccess.into(),
|
MessageSubtypeId::TmAcceptanceSuccess.into(),
|
||||||
request_id,
|
request_id,
|
||||||
seq_count,
|
seq_count,
|
||||||
msg_count,
|
msg_count,
|
||||||
@@ -587,13 +581,13 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
params: FailParams<'time, '_>,
|
params: FailParams<'time, '_>,
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.failure_verification_no_step(
|
self.failure_verification_no_step(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmAcceptanceFailure.into(),
|
MessageSubtypeId::TmAcceptanceFailure.into(),
|
||||||
request_id,
|
request_id,
|
||||||
seq_count,
|
seq_count,
|
||||||
msg_count,
|
msg_count,
|
||||||
@@ -609,13 +603,13 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
let tm_creator = self.success_verification_no_step(
|
let tm_creator = self.success_verification_no_step(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmStartSuccess.into(),
|
MessageSubtypeId::TmStartSuccess.into(),
|
||||||
request_id,
|
request_id,
|
||||||
seq_count,
|
seq_count,
|
||||||
msg_count,
|
msg_count,
|
||||||
@@ -632,13 +626,13 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
params: FailParams<'time, '_>,
|
params: FailParams<'time, '_>,
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.failure_verification_no_step(
|
self.failure_verification_no_step(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmStartFailure.into(),
|
MessageSubtypeId::TmStartFailure.into(),
|
||||||
request_id,
|
request_id,
|
||||||
seq_count,
|
seq_count,
|
||||||
msg_count,
|
msg_count,
|
||||||
@@ -654,14 +648,14 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
step: impl EcssEnumeration,
|
step: impl EcssEnumeration,
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.create_pus_verif_success_tm(
|
self.create_pus_verif_success_tm(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmStepSuccess.into(),
|
MessageSubtypeId::TmStepSuccess.into(),
|
||||||
seq_count,
|
seq_count,
|
||||||
msg_count,
|
msg_count,
|
||||||
request_id,
|
request_id,
|
||||||
@@ -678,13 +672,13 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
token: VerificationToken<TcStateStarted>,
|
token: VerificationToken<TcStateStarted>,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
params: FailParamsWithStep<'time, '_>,
|
params: FailParamsWithStep<'time, '_>,
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.create_pus_verif_fail_tm(
|
self.create_pus_verif_fail_tm(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmStepFailure.into(),
|
MessageSubtypeId::TmStepFailure.into(),
|
||||||
seq_count,
|
seq_count,
|
||||||
msg_count,
|
msg_count,
|
||||||
&token.request_id(),
|
&token.request_id(),
|
||||||
@@ -701,13 +695,13 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_counter: u16,
|
seq_counter: u14,
|
||||||
msg_counter: u16,
|
msg_counter: u16,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.success_verification_no_step(
|
self.success_verification_no_step(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmCompletionSuccess.into(),
|
MessageSubtypeId::TmCompletionSuccess.into(),
|
||||||
request_id,
|
request_id,
|
||||||
seq_counter,
|
seq_counter,
|
||||||
msg_counter,
|
msg_counter,
|
||||||
@@ -723,13 +717,13 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
request_id: &RequestId,
|
request_id: &RequestId,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_count: u16,
|
msg_count: u16,
|
||||||
params: FailParams<'time, '_>,
|
params: FailParams<'time, '_>,
|
||||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||||
self.failure_verification_no_step(
|
self.failure_verification_no_step(
|
||||||
src_data_buf,
|
src_data_buf,
|
||||||
Subservice::TmCompletionFailure.into(),
|
MessageSubtypeId::TmCompletionFailure.into(),
|
||||||
request_id,
|
request_id,
|
||||||
seq_count,
|
seq_count,
|
||||||
msg_count,
|
msg_count,
|
||||||
@@ -744,7 +738,7 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_counter: u16,
|
msg_counter: u16,
|
||||||
req_id: &RequestId,
|
req_id: &RequestId,
|
||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
@@ -780,7 +774,7 @@ impl VerificationReportCreator {
|
|||||||
&self,
|
&self,
|
||||||
src_data_buf: &'src_data mut [u8],
|
src_data_buf: &'src_data mut [u8],
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
msg_counter: u16,
|
msg_counter: u16,
|
||||||
req_id: &RequestId,
|
req_id: &RequestId,
|
||||||
step: Option<&(impl EcssEnumeration + ?Sized)>,
|
step: Option<&(impl EcssEnumeration + ?Sized)>,
|
||||||
@@ -826,19 +820,24 @@ impl VerificationReportCreator {
|
|||||||
time_stamp: &'time [u8],
|
time_stamp: &'time [u8],
|
||||||
source_data_len: usize,
|
source_data_len: usize,
|
||||||
) -> PusTmCreator<'time, 'src_data> {
|
) -> PusTmCreator<'time, 'src_data> {
|
||||||
let tm_sec_header =
|
let tm_sec_header = PusTmSecondaryHeader::new(
|
||||||
PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, time_stamp);
|
MessageTypeId::new(1, subservice),
|
||||||
|
msg_counter,
|
||||||
|
self.dest_id,
|
||||||
|
time_stamp,
|
||||||
|
);
|
||||||
PusTmCreator::new(
|
PusTmCreator::new(
|
||||||
sp_header,
|
sp_header,
|
||||||
tm_sec_header,
|
tm_sec_header,
|
||||||
&src_data_buf[0..source_data_len],
|
&src_data_buf[0..source_data_len],
|
||||||
true,
|
CreatorConfig::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub mod alloc_mod {
|
pub mod alloc_mod {
|
||||||
|
use arbitrary_int::traits::Integer as _;
|
||||||
use spacepackets::ecss::PusError;
|
use spacepackets::ecss::PusError;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -846,29 +845,26 @@ pub mod alloc_mod {
|
|||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VerificationReporterCfg {
|
pub struct VerificationReporterConfig {
|
||||||
apid: u16,
|
apid: u11,
|
||||||
pub step_field_width: usize,
|
pub step_field_width: usize,
|
||||||
pub fail_code_field_width: usize,
|
pub fail_code_field_width: usize,
|
||||||
pub max_fail_data_len: usize,
|
pub max_fail_data_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationReporterCfg {
|
impl VerificationReporterConfig {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
apid: u16,
|
apid: u11,
|
||||||
step_field_width: usize,
|
step_field_width: usize,
|
||||||
fail_code_field_width: usize,
|
fail_code_field_width: usize,
|
||||||
max_fail_data_len: usize,
|
max_fail_data_len: usize,
|
||||||
) -> Option<Self> {
|
) -> Self {
|
||||||
if apid > MAX_APID {
|
Self {
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Self {
|
|
||||||
apid,
|
apid,
|
||||||
step_field_width,
|
step_field_width,
|
||||||
fail_code_field_width,
|
fail_code_field_width,
|
||||||
max_fail_data_len,
|
max_fail_data_len,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,17 +872,17 @@ pub mod alloc_mod {
|
|||||||
///
|
///
|
||||||
/// The [Self::modify_tm] function is called before the TM is sent. This allows users to change
|
/// The [Self::modify_tm] function is called before the TM is sent. This allows users to change
|
||||||
/// fields like the message count or sequence counter before the TM is sent.
|
/// fields like the message count or sequence counter before the TM is sent.
|
||||||
pub trait VerificationHookProvider {
|
pub trait VerificationHook {
|
||||||
fn modify_tm(&self, tm: &mut PusTmCreator);
|
fn modify_tm(&self, tm: &mut PusTmCreator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [VerificationHookProvider] which does nothing. This is the default hook variant for
|
/// [VerificationHook] which does nothing. This is the default hook variant for
|
||||||
/// the [VerificationReporter], assuming that any necessary packet manipulation is performed by
|
/// the [VerificationReporter], assuming that any necessary packet manipulation is performed by
|
||||||
/// a centralized TM funnel or inlet.
|
/// a centralized TM funnel or inlet.
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub struct DummyVerificationHook {}
|
pub struct DummyVerificationHook {}
|
||||||
|
|
||||||
impl VerificationHookProvider for DummyVerificationHook {
|
impl VerificationHook for DummyVerificationHook {
|
||||||
fn modify_tm(&self, _tm: &mut PusTmCreator) {}
|
fn modify_tm(&self, _tm: &mut PusTmCreator) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,17 +895,17 @@ pub mod alloc_mod {
|
|||||||
/// destination fields are assumed to be constant for a given repoter instance.
|
/// destination fields are assumed to be constant for a given repoter instance.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VerificationReporter<
|
pub struct VerificationReporter<
|
||||||
VerificationHook: VerificationHookProvider = DummyVerificationHook,
|
VerificationHookInstance: VerificationHook = DummyVerificationHook,
|
||||||
> {
|
> {
|
||||||
owner_id: ComponentId,
|
owner_id: ComponentId,
|
||||||
source_data_buf: RefCell<alloc::vec::Vec<u8>>,
|
source_data_buf: RefCell<alloc::vec::Vec<u8>>,
|
||||||
pub reporter_creator: VerificationReportCreator,
|
pub report_creator: VerificationReportCreator,
|
||||||
pub tm_hook: VerificationHook,
|
pub tm_hook: VerificationHookInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationReporter<DummyVerificationHook> {
|
impl VerificationReporter<DummyVerificationHook> {
|
||||||
pub fn new(owner_id: ComponentId, cfg: &VerificationReporterCfg) -> Self {
|
pub fn new(owner_id: ComponentId, cfg: &VerificationReporterConfig) -> Self {
|
||||||
let reporter = VerificationReportCreator::new(cfg.apid).unwrap();
|
let reporter = VerificationReportCreator::new(cfg.apid);
|
||||||
Self {
|
Self {
|
||||||
owner_id,
|
owner_id,
|
||||||
source_data_buf: RefCell::new(alloc::vec![
|
source_data_buf: RefCell::new(alloc::vec![
|
||||||
@@ -919,21 +915,21 @@ pub mod alloc_mod {
|
|||||||
+ cfg.fail_code_field_width
|
+ cfg.fail_code_field_width
|
||||||
+ cfg.max_fail_data_len
|
+ cfg.max_fail_data_len
|
||||||
]),
|
]),
|
||||||
reporter_creator: reporter,
|
report_creator: reporter,
|
||||||
tm_hook: DummyVerificationHook::default(),
|
tm_hook: DummyVerificationHook::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VerificationHook: VerificationHookProvider> VerificationReporter<VerificationHook> {
|
impl<VerificationHookInstance: VerificationHook> VerificationReporter<VerificationHookInstance> {
|
||||||
/// The provided [VerificationHookProvider] can be used to modify a verification packet
|
/// The provided [VerificationHook] can be used to modify a verification packet
|
||||||
/// before it is sent.
|
/// before it is sent.
|
||||||
pub fn new_with_hook(
|
pub fn new_with_hook(
|
||||||
owner_id: ComponentId,
|
owner_id: ComponentId,
|
||||||
cfg: &VerificationReporterCfg,
|
cfg: &VerificationReporterConfig,
|
||||||
tm_hook: VerificationHook,
|
tm_hook: VerificationHookInstance,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let reporter = VerificationReportCreator::new(cfg.apid).unwrap();
|
let reporter = VerificationReportCreator::new(cfg.apid);
|
||||||
Self {
|
Self {
|
||||||
owner_id,
|
owner_id,
|
||||||
source_data_buf: RefCell::new(alloc::vec![
|
source_data_buf: RefCell::new(alloc::vec![
|
||||||
@@ -943,7 +939,7 @@ pub mod alloc_mod {
|
|||||||
+ cfg.fail_code_field_width
|
+ cfg.fail_code_field_width
|
||||||
+ cfg.max_fail_data_len
|
+ cfg.max_fail_data_len
|
||||||
]),
|
]),
|
||||||
reporter_creator: reporter,
|
report_creator: reporter,
|
||||||
tm_hook,
|
tm_hook,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -952,9 +948,7 @@ pub mod alloc_mod {
|
|||||||
&self,
|
&self,
|
||||||
pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
|
pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
|
||||||
) -> VerificationToken<TcStateNone> {
|
) -> VerificationToken<TcStateNone> {
|
||||||
VerificationToken::<TcStateNone>::new(
|
VerificationToken::<TcStateNone>::new(self.report_creator.read_request_id(pus_tc))
|
||||||
VerificationReportCreator::read_request_id_from_tc(pus_tc),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_verification_with_req_id(
|
pub fn start_verification_with_req_id(
|
||||||
@@ -965,9 +959,9 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delegate!(
|
delegate!(
|
||||||
to self.reporter_creator {
|
to self.report_creator {
|
||||||
pub fn set_apid(&mut self, apid: u16) -> bool;
|
pub fn set_apid(&mut self, apid: u11);
|
||||||
pub fn apid(&self) -> u16;
|
pub fn apid(&self) -> u11;
|
||||||
pub fn dest_id(&self) -> u16;
|
pub fn dest_id(&self) -> u16;
|
||||||
pub fn set_dest_id(&mut self, dest_id: u16);
|
pub fn set_dest_id(&mut self, dest_id: u16);
|
||||||
}
|
}
|
||||||
@@ -978,11 +972,11 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VerificationHook: VerificationHookProvider> VerificationReportingProvider
|
impl<VerificationHookInstance: VerificationHook> VerificationReportingProvider
|
||||||
for VerificationReporter<VerificationHook>
|
for VerificationReporter<VerificationHookInstance>
|
||||||
{
|
{
|
||||||
delegate!(
|
delegate!(
|
||||||
to self.reporter_creator {
|
to self.report_creator {
|
||||||
fn set_apid(&mut self, apid: Apid);
|
fn set_apid(&mut self, apid: Apid);
|
||||||
fn apid(&self) -> Apid;
|
fn apid(&self) -> Apid;
|
||||||
}
|
}
|
||||||
@@ -1008,11 +1002,11 @@ pub mod alloc_mod {
|
|||||||
) -> Result<VerificationToken<TcStateAccepted>, EcssTmtcError> {
|
) -> Result<VerificationToken<TcStateAccepted>, EcssTmtcError> {
|
||||||
let mut source_data_buf = self.source_data_buf.borrow_mut();
|
let mut source_data_buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.acceptance_success(
|
.acceptance_success(
|
||||||
source_data_buf.as_mut_slice(),
|
source_data_buf.as_mut_slice(),
|
||||||
&token.request_id(),
|
&token.request_id(),
|
||||||
0,
|
u14::ZERO,
|
||||||
0,
|
0,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
)
|
)
|
||||||
@@ -1031,8 +1025,14 @@ pub mod alloc_mod {
|
|||||||
) -> Result<(), EcssTmtcError> {
|
) -> Result<(), EcssTmtcError> {
|
||||||
let mut buf = self.source_data_buf.borrow_mut();
|
let mut buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.acceptance_failure(buf.as_mut_slice(), &token.request_id(), 0, 0, params)
|
.acceptance_failure(
|
||||||
|
buf.as_mut_slice(),
|
||||||
|
&token.request_id(),
|
||||||
|
u14::ZERO,
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
)
|
||||||
.map_err(PusError::ByteConversion)?;
|
.map_err(PusError::ByteConversion)?;
|
||||||
self.tm_hook.modify_tm(&mut tm_creator);
|
self.tm_hook.modify_tm(&mut tm_creator);
|
||||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||||
@@ -1050,8 +1050,14 @@ pub mod alloc_mod {
|
|||||||
) -> Result<VerificationToken<TcStateStarted>, EcssTmtcError> {
|
) -> Result<VerificationToken<TcStateStarted>, EcssTmtcError> {
|
||||||
let mut buf = self.source_data_buf.borrow_mut();
|
let mut buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.start_success(buf.as_mut_slice(), &token.request_id(), 0, 0, time_stamp)
|
.start_success(
|
||||||
|
buf.as_mut_slice(),
|
||||||
|
&token.request_id(),
|
||||||
|
u14::ZERO,
|
||||||
|
0,
|
||||||
|
time_stamp,
|
||||||
|
)
|
||||||
.map_err(PusError::ByteConversion)?;
|
.map_err(PusError::ByteConversion)?;
|
||||||
self.tm_hook.modify_tm(&mut tm_creator);
|
self.tm_hook.modify_tm(&mut tm_creator);
|
||||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||||
@@ -1070,8 +1076,14 @@ pub mod alloc_mod {
|
|||||||
) -> Result<(), EcssTmtcError> {
|
) -> Result<(), EcssTmtcError> {
|
||||||
let mut buf = self.source_data_buf.borrow_mut();
|
let mut buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.start_failure(buf.as_mut_slice(), &token.request_id(), 0, 0, params)
|
.start_failure(
|
||||||
|
buf.as_mut_slice(),
|
||||||
|
&token.request_id(),
|
||||||
|
u14::ZERO,
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
)
|
||||||
.map_err(PusError::ByteConversion)?;
|
.map_err(PusError::ByteConversion)?;
|
||||||
self.tm_hook.modify_tm(&mut tm_creator);
|
self.tm_hook.modify_tm(&mut tm_creator);
|
||||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||||
@@ -1090,11 +1102,11 @@ pub mod alloc_mod {
|
|||||||
) -> Result<(), EcssTmtcError> {
|
) -> Result<(), EcssTmtcError> {
|
||||||
let mut buf = self.source_data_buf.borrow_mut();
|
let mut buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.step_success(
|
.step_success(
|
||||||
buf.as_mut_slice(),
|
buf.as_mut_slice(),
|
||||||
&token.request_id(),
|
&token.request_id(),
|
||||||
0,
|
u14::ZERO,
|
||||||
0,
|
0,
|
||||||
time_stamp,
|
time_stamp,
|
||||||
step,
|
step,
|
||||||
@@ -1117,8 +1129,8 @@ pub mod alloc_mod {
|
|||||||
) -> Result<(), EcssTmtcError> {
|
) -> Result<(), EcssTmtcError> {
|
||||||
let mut buf = self.source_data_buf.borrow_mut();
|
let mut buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.step_failure(buf.as_mut_slice(), token, 0, 0, params)
|
.step_failure(buf.as_mut_slice(), token, u14::ZERO, 0, params)
|
||||||
.map_err(PusError::ByteConversion)?;
|
.map_err(PusError::ByteConversion)?;
|
||||||
self.tm_hook.modify_tm(&mut tm_creator);
|
self.tm_hook.modify_tm(&mut tm_creator);
|
||||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||||
@@ -1138,8 +1150,14 @@ pub mod alloc_mod {
|
|||||||
) -> Result<(), EcssTmtcError> {
|
) -> Result<(), EcssTmtcError> {
|
||||||
let mut buf = self.source_data_buf.borrow_mut();
|
let mut buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.completion_success(buf.as_mut_slice(), &token.request_id(), 0, 0, time_stamp)
|
.completion_success(
|
||||||
|
buf.as_mut_slice(),
|
||||||
|
&token.request_id(),
|
||||||
|
u14::ZERO,
|
||||||
|
0,
|
||||||
|
time_stamp,
|
||||||
|
)
|
||||||
.map_err(PusError::ByteConversion)?;
|
.map_err(PusError::ByteConversion)?;
|
||||||
self.tm_hook.modify_tm(&mut tm_creator);
|
self.tm_hook.modify_tm(&mut tm_creator);
|
||||||
sender.send_tm(self.owner_id, PusTmVariant::Direct(tm_creator))?;
|
sender.send_tm(self.owner_id, PusTmVariant::Direct(tm_creator))?;
|
||||||
@@ -1158,8 +1176,14 @@ pub mod alloc_mod {
|
|||||||
) -> Result<(), EcssTmtcError> {
|
) -> Result<(), EcssTmtcError> {
|
||||||
let mut buf = self.source_data_buf.borrow_mut();
|
let mut buf = self.source_data_buf.borrow_mut();
|
||||||
let mut tm_creator = self
|
let mut tm_creator = self
|
||||||
.reporter_creator
|
.report_creator
|
||||||
.completion_failure(buf.as_mut_slice(), &token.request_id(), 0, 00, params)
|
.completion_failure(
|
||||||
|
buf.as_mut_slice(),
|
||||||
|
&token.request_id(),
|
||||||
|
u14::ZERO,
|
||||||
|
0,
|
||||||
|
params,
|
||||||
|
)
|
||||||
.map_err(PusError::ByteConversion)?;
|
.map_err(PusError::ByteConversion)?;
|
||||||
self.tm_hook.modify_tm(&mut tm_creator);
|
self.tm_hook.modify_tm(&mut tm_creator);
|
||||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||||
@@ -1309,6 +1333,7 @@ pub fn handle_step_failure_with_generic_params(
|
|||||||
#[cfg(any(feature = "test_util", test))]
|
#[cfg(any(feature = "test_util", test))]
|
||||||
pub mod test_util {
|
pub mod test_util {
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use arbitrary_int::traits::Integer;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
@@ -1370,7 +1395,7 @@ pub mod test_util {
|
|||||||
fn set_apid(&mut self, _apid: Apid) {}
|
fn set_apid(&mut self, _apid: Apid) {}
|
||||||
|
|
||||||
fn apid(&self) -> Apid {
|
fn apid(&self) -> Apid {
|
||||||
0
|
Apid::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fn acceptance_success(
|
fn acceptance_success(
|
||||||
@@ -1402,7 +1427,7 @@ pub mod test_util {
|
|||||||
token.request_id(),
|
token.request_id(),
|
||||||
VerificationReportInfo::AcceptanceFailure(FailureData {
|
VerificationReportInfo::AcceptanceFailure(FailureData {
|
||||||
sender: self.owner_id(),
|
sender: self.owner_id(),
|
||||||
error_enum: params.failure_code.value(),
|
error_enum: params.failure_code.value_raw(),
|
||||||
fail_data: params.failure_data.to_vec(),
|
fail_data: params.failure_data.to_vec(),
|
||||||
time_stamp: params.time_stamp.to_vec(),
|
time_stamp: params.time_stamp.to_vec(),
|
||||||
}),
|
}),
|
||||||
@@ -1439,7 +1464,7 @@ pub mod test_util {
|
|||||||
token.request_id(),
|
token.request_id(),
|
||||||
VerificationReportInfo::StartedFailure(FailureData {
|
VerificationReportInfo::StartedFailure(FailureData {
|
||||||
sender: self.owner_id(),
|
sender: self.owner_id(),
|
||||||
error_enum: params.failure_code.value(),
|
error_enum: params.failure_code.value_raw(),
|
||||||
fail_data: params.failure_data.to_vec(),
|
fail_data: params.failure_data.to_vec(),
|
||||||
time_stamp: params.time_stamp.to_vec(),
|
time_stamp: params.time_stamp.to_vec(),
|
||||||
}),
|
}),
|
||||||
@@ -1461,7 +1486,7 @@ pub mod test_util {
|
|||||||
sender: self.owner_id(),
|
sender: self.owner_id(),
|
||||||
time_stamp: time_stamp.to_vec(),
|
time_stamp: time_stamp.to_vec(),
|
||||||
},
|
},
|
||||||
step: step.value() as u16,
|
step: step.value_raw() as u16,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1477,7 +1502,7 @@ pub mod test_util {
|
|||||||
token.request_id(),
|
token.request_id(),
|
||||||
VerificationReportInfo::StepFailure(FailureData {
|
VerificationReportInfo::StepFailure(FailureData {
|
||||||
sender: self.owner_id(),
|
sender: self.owner_id(),
|
||||||
error_enum: params.common.failure_code.value(),
|
error_enum: params.common.failure_code.value_raw(),
|
||||||
fail_data: params.common.failure_data.to_vec(),
|
fail_data: params.common.failure_data.to_vec(),
|
||||||
time_stamp: params.common.time_stamp.to_vec(),
|
time_stamp: params.common.time_stamp.to_vec(),
|
||||||
}),
|
}),
|
||||||
@@ -1511,7 +1536,7 @@ pub mod test_util {
|
|||||||
token.request_id(),
|
token.request_id(),
|
||||||
VerificationReportInfo::CompletionFailure(FailureData {
|
VerificationReportInfo::CompletionFailure(FailureData {
|
||||||
sender: self.owner_id(),
|
sender: self.owner_id(),
|
||||||
error_enum: params.failure_code.value(),
|
error_enum: params.failure_code.value_raw(),
|
||||||
fail_data: params.failure_data.to_vec(),
|
fail_data: params.failure_data.to_vec(),
|
||||||
time_stamp: params.time_stamp.to_vec(),
|
time_stamp: params.time_stamp.to_vec(),
|
||||||
}),
|
}),
|
||||||
@@ -1686,39 +1711,41 @@ pub mod test_util {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
|
use crate::ComponentId;
|
||||||
use crate::params::Params;
|
use crate::params::Params;
|
||||||
use crate::pool::{SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
|
use crate::pool::{SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
|
||||||
use crate::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
|
use crate::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
|
||||||
use crate::pus::tests::CommonTmInfo;
|
use crate::pus::tests::CommonTmInfo;
|
||||||
use crate::pus::verification::{
|
use crate::pus::verification::{
|
||||||
handle_step_failure_with_generic_params, EcssTmSender, EcssTmtcError, FailParams,
|
EcssTmSender, EcssTmtcError, FailParams, FailParamsWithStep, RequestId, TcStateNone,
|
||||||
FailParamsWithStep, RequestId, TcStateNone, VerificationReporter, VerificationReporterCfg,
|
VerificationReporter, VerificationReporterConfig, VerificationToken,
|
||||||
VerificationToken,
|
handle_step_failure_with_generic_params,
|
||||||
};
|
};
|
||||||
use crate::pus::{ChannelWithId, PusTmVariant};
|
use crate::pus::{ChannelWithId, PusTmVariant};
|
||||||
use crate::request::MessageMetadata;
|
use crate::request::MessageMetadata;
|
||||||
use crate::spacepackets::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProvider};
|
use crate::spacepackets::seq_count::{SequenceCounter, SequenceCounterCcsdsSimple};
|
||||||
use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
||||||
use crate::ComponentId;
|
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
|
use arbitrary_int::traits::Integer;
|
||||||
|
use arbitrary_int::{u11, u14};
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
||||||
use spacepackets::ecss::{
|
use spacepackets::ecss::{
|
||||||
EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusError, PusPacket,
|
CreatorConfig, EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, MessageTypeId,
|
||||||
WritablePusPacket,
|
PusError, PusPacket, WritablePusPacket,
|
||||||
};
|
};
|
||||||
use spacepackets::util::UnsignedEnum;
|
use spacepackets::util::UnsignedEnum;
|
||||||
use spacepackets::{ByteConversionError, SpHeader};
|
use spacepackets::{ByteConversionError, SpHeader};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::{mpsc, RwLock};
|
use std::sync::{RwLock, mpsc};
|
||||||
use std::vec;
|
use std::vec;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
handle_completion_failure_with_generic_params, DummyVerificationHook, FailParamHelper,
|
DummyVerificationHook, FailParamHelper, SequenceCounterSimple, TcStateAccepted,
|
||||||
SeqCountProviderSimple, TcStateAccepted, TcStateStarted, VerificationHookProvider,
|
TcStateStarted, VerificationHook, VerificationReportingProvider, WasAtLeastAccepted,
|
||||||
VerificationReportingProvider, WasAtLeastAccepted,
|
handle_completion_failure_with_generic_params,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn is_send<T: Send>(_: &T) {}
|
fn is_send<T: Send>(_: &T) {}
|
||||||
@@ -1756,7 +1783,7 @@ pub mod tests {
|
|||||||
panic!("TestSender: Can not deal with addresses");
|
panic!("TestSender: Can not deal with addresses");
|
||||||
}
|
}
|
||||||
PusTmVariant::Direct(tm) => {
|
PusTmVariant::Direct(tm) => {
|
||||||
assert_eq!(PusPacket::service(&tm), 1);
|
assert_eq!(PusPacket::service_type_id(&tm), 1);
|
||||||
assert!(!tm.source_data().is_empty());
|
assert!(!tm.source_data().is_empty());
|
||||||
let mut time_stamp = [0; 7];
|
let mut time_stamp = [0; 7];
|
||||||
time_stamp.clone_from_slice(&tm.timestamp()[0..7]);
|
time_stamp.clone_from_slice(&tm.timestamp()[0..7]);
|
||||||
@@ -1783,41 +1810,41 @@ pub mod tests {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SequenceCounterHook {
|
pub struct SequenceCounterHook {
|
||||||
pub seq_counter: CcsdsSimpleSeqCountProvider,
|
pub seq_counter: SequenceCounterCcsdsSimple,
|
||||||
pub msg_counter: SeqCountProviderSimple<u16>,
|
pub msg_counter: SequenceCounterSimple<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationHookProvider for SequenceCounterHook {
|
impl VerificationHook for SequenceCounterHook {
|
||||||
fn modify_tm(&self, tm: &mut spacepackets::ecss::tm::PusTmCreator) {
|
fn modify_tm(&self, tm: &mut spacepackets::ecss::tm::PusTmCreator) {
|
||||||
tm.set_seq_count(self.seq_counter.get_and_increment());
|
tm.set_seq_count(u14::new(self.seq_counter.get_and_increment()));
|
||||||
tm.set_msg_counter(self.msg_counter.get_and_increment());
|
tm.set_msg_counter(self.msg_counter.get_and_increment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VerificationReporterTestbench<
|
struct VerificationReporterTestbench<
|
||||||
VerificationHook: VerificationHookProvider = DummyVerificationHook,
|
VerificationHookInstance: VerificationHook = DummyVerificationHook,
|
||||||
> {
|
> {
|
||||||
pub id: ComponentId,
|
pub id: ComponentId,
|
||||||
sender: TestSender,
|
sender: TestSender,
|
||||||
reporter: VerificationReporter<VerificationHook>,
|
reporter: VerificationReporter<VerificationHookInstance>,
|
||||||
pub request_id: RequestId,
|
pub request_id: RequestId,
|
||||||
tc: Vec<u8>,
|
tc: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_reporter(id: ComponentId, max_fail_data_len: usize) -> VerificationReporter {
|
fn base_reporter(id: ComponentId, max_fail_data_len: usize) -> VerificationReporter {
|
||||||
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, max_fail_data_len).unwrap();
|
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, max_fail_data_len);
|
||||||
VerificationReporter::new(id, &cfg)
|
VerificationReporter::new(id, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reporter_with_hook<VerificationHook: VerificationHookProvider>(
|
fn reporter_with_hook<VerificationHookInstance: VerificationHook>(
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
hook: VerificationHook,
|
hook: VerificationHookInstance,
|
||||||
) -> VerificationReporter<VerificationHook> {
|
) -> VerificationReporter<VerificationHookInstance> {
|
||||||
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||||
VerificationReporter::new_with_hook(id, &cfg, hook)
|
VerificationReporter::new_with_hook(id, &cfg, hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VerificiationHook: VerificationHookProvider> VerificationReporterTestbench<VerificiationHook> {
|
impl<VerificiationHook: VerificationHook> VerificationReporterTestbench<VerificiationHook> {
|
||||||
fn new_with_hook(id: ComponentId, tc: PusTcCreator, tm_hook: VerificiationHook) -> Self {
|
fn new_with_hook(id: ComponentId, tc: PusTcCreator, tm_hook: VerificiationHook) -> Self {
|
||||||
let reporter = reporter_with_hook(id, tm_hook);
|
let reporter = reporter_with_hook(id, tm_hook);
|
||||||
Self {
|
Self {
|
||||||
@@ -1912,7 +1939,14 @@ pub mod tests {
|
|||||||
fn check_acceptance_success(&self, timestamp: &[u8; 7]) {
|
fn check_acceptance_success(&self, timestamp: &[u8; 7]) {
|
||||||
let cmp_info = TmInfo {
|
let cmp_info = TmInfo {
|
||||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||||
common: CommonTmInfo::new(1, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp),
|
common: CommonTmInfo::new(
|
||||||
|
1,
|
||||||
|
TEST_APID,
|
||||||
|
u14::ZERO,
|
||||||
|
0,
|
||||||
|
self.reporter.dest_id(),
|
||||||
|
timestamp,
|
||||||
|
),
|
||||||
additional_data: None,
|
additional_data: None,
|
||||||
};
|
};
|
||||||
let mut service_queue = self.sender.service_queue.borrow_mut();
|
let mut service_queue = self.sender.service_queue.borrow_mut();
|
||||||
@@ -1921,7 +1955,7 @@ pub mod tests {
|
|||||||
assert_eq!(info, cmp_info);
|
assert_eq!(info, cmp_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_start_success(&mut self, seq_count: u16, msg_counter: u16, timestamp: &[u8]) {
|
fn check_start_success(&mut self, seq_count: u14, msg_counter: u16, timestamp: &[u8]) {
|
||||||
let mut srv_queue = self.sender.service_queue.borrow_mut();
|
let mut srv_queue = self.sender.service_queue.borrow_mut();
|
||||||
let cmp_info = TmInfo {
|
let cmp_info = TmInfo {
|
||||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||||
@@ -1939,7 +1973,7 @@ pub mod tests {
|
|||||||
assert_eq!(info, cmp_info);
|
assert_eq!(info, cmp_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_completion_success(&mut self, seq_count: u16, msg_counter: u16) {
|
fn check_completion_success(&mut self, seq_count: u14, msg_counter: u16) {
|
||||||
let cmp_info = TmInfo {
|
let cmp_info = TmInfo {
|
||||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||||
common: CommonTmInfo::new(
|
common: CommonTmInfo::new(
|
||||||
@@ -1972,7 +2006,14 @@ pub mod tests {
|
|||||||
fn check_acceptance_failure(&mut self, timestamp: &[u8; 7]) {
|
fn check_acceptance_failure(&mut self, timestamp: &[u8; 7]) {
|
||||||
let cmp_info = TmInfo {
|
let cmp_info = TmInfo {
|
||||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||||
common: CommonTmInfo::new(2, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp),
|
common: CommonTmInfo::new(
|
||||||
|
2,
|
||||||
|
TEST_APID,
|
||||||
|
u14::ZERO,
|
||||||
|
0,
|
||||||
|
self.reporter.dest_id(),
|
||||||
|
timestamp,
|
||||||
|
),
|
||||||
additional_data: Some([0, 2].to_vec()),
|
additional_data: Some([0, 2].to_vec()),
|
||||||
};
|
};
|
||||||
let service_queue = self.sender.service_queue.get_mut();
|
let service_queue = self.sender.service_queue.get_mut();
|
||||||
@@ -2060,9 +2101,9 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_generic_ping() -> PusTcCreator<'static> {
|
fn create_generic_ping() -> PusTcCreator<'static> {
|
||||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID, 0x34, 0);
|
let sph = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0x34), 0);
|
||||||
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
PusTcCreator::new(sph, tc_header, &[], true)
|
PusTcCreator::new(sph, tc_header, &[], CreatorConfig::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2082,8 +2123,8 @@ pub mod tests {
|
|||||||
fn test_state() {
|
fn test_state() {
|
||||||
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
|
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
|
||||||
assert_eq!(testbench.reporter.apid(), TEST_APID);
|
assert_eq!(testbench.reporter.apid(), TEST_APID);
|
||||||
testbench.reporter.set_apid(TEST_APID + 1);
|
testbench.reporter.set_apid(u11::new(TEST_APID.value() + 1));
|
||||||
assert_eq!(testbench.reporter.apid(), TEST_APID + 1);
|
assert_eq!(testbench.reporter.apid().value(), TEST_APID.value() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2233,7 +2274,7 @@ pub mod tests {
|
|||||||
.expect("step 1 failed");
|
.expect("step 1 failed");
|
||||||
assert_eq!(testbench.sender.service_queue.borrow().len(), 4);
|
assert_eq!(testbench.sender.service_queue.borrow().len(), 4);
|
||||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||||
testbench.check_start_success(0, 0, &EMPTY_STAMP);
|
testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
|
||||||
testbench.check_step_success(0, &EMPTY_STAMP);
|
testbench.check_step_success(0, &EMPTY_STAMP);
|
||||||
testbench.check_step_success(1, &EMPTY_STAMP);
|
testbench.check_step_success(1, &EMPTY_STAMP);
|
||||||
}
|
}
|
||||||
@@ -2267,7 +2308,7 @@ pub mod tests {
|
|||||||
.step_failure(started_token, fail_params)
|
.step_failure(started_token, fail_params)
|
||||||
.expect("Step failure failed");
|
.expect("Step failure failed");
|
||||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||||
testbench.check_start_success(0, 0, DUMMY_STAMP);
|
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
|
||||||
testbench.check_step_success(0, &EMPTY_STAMP);
|
testbench.check_step_success(0, &EMPTY_STAMP);
|
||||||
testbench.check_step_failure(&fail_step, &fail_code, &fail_data_raw);
|
testbench.check_step_failure(&fail_step, &fail_code, &fail_data_raw);
|
||||||
}
|
}
|
||||||
@@ -2289,7 +2330,7 @@ pub mod tests {
|
|||||||
.completion_failure(started_token, fail_params)
|
.completion_failure(started_token, fail_params)
|
||||||
.expect("Completion failure");
|
.expect("Completion failure");
|
||||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||||
testbench.check_start_success(0, 0, DUMMY_STAMP);
|
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
|
||||||
|
|
||||||
testbench.check_completion_failure(&fail_code, &[]);
|
testbench.check_completion_failure(&fail_code, &[]);
|
||||||
}
|
}
|
||||||
@@ -2309,8 +2350,8 @@ pub mod tests {
|
|||||||
.completion_success(started_token, &EMPTY_STAMP)
|
.completion_success(started_token, &EMPTY_STAMP)
|
||||||
.expect("Sending completion success failed");
|
.expect("Sending completion success failed");
|
||||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||||
testbench.check_start_success(0, 0, DUMMY_STAMP);
|
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
|
||||||
testbench.check_completion_success(0, 0);
|
testbench.check_completion_success(u14::ZERO, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2331,8 +2372,8 @@ pub mod tests {
|
|||||||
.completion_success(started_token, &EMPTY_STAMP)
|
.completion_success(started_token, &EMPTY_STAMP)
|
||||||
.expect("Sending completion success failed");
|
.expect("Sending completion success failed");
|
||||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||||
testbench.check_start_success(1, 1, DUMMY_STAMP);
|
testbench.check_start_success(u14::new(1), 1, DUMMY_STAMP);
|
||||||
testbench.check_completion_success(2, 2);
|
testbench.check_completion_success(u14::new(2), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2391,7 +2432,7 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
assert!(result.unwrap());
|
assert!(result.unwrap());
|
||||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||||
testbench.check_start_success(0, 0, &EMPTY_STAMP);
|
testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
|
||||||
testbench.check_step_failure(&step, &fail_code, fail_data.as_bytes());
|
testbench.check_step_failure(&step, &fail_code, fail_data.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use arbitrary_int::{prelude::*, u11, u21};
|
||||||
use core::{fmt, marker::PhantomData};
|
use core::{fmt, marker::PhantomData};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -9,13 +10,13 @@ pub use alloc_mod::*;
|
|||||||
pub use std_mod::*;
|
pub use std_mod::*;
|
||||||
|
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::IsPusTelecommand, PusPacket},
|
|
||||||
ByteConversionError,
|
ByteConversionError,
|
||||||
|
ecss::{PusPacket, tc::IsPusTelecommand},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
queue::{GenericReceiveError, GenericSendError},
|
|
||||||
ComponentId,
|
ComponentId,
|
||||||
|
queue::{GenericReceiveError, GenericSendError},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generic request ID type. Requests can be associated with an ID to have a unique identifier
|
/// Generic request ID type. Requests can be associated with an ID to have a unique identifier
|
||||||
@@ -23,16 +24,16 @@ use crate::{
|
|||||||
pub type RequestId = u32;
|
pub type RequestId = u32;
|
||||||
|
|
||||||
/// CCSDS APID type definition. Please note that the APID is a 14 bit value.
|
/// CCSDS APID type definition. Please note that the APID is a 14 bit value.
|
||||||
pub type Apid = u16;
|
pub type Apid = u11;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct UniqueApidTargetId {
|
pub struct UniqueApidTargetId {
|
||||||
pub apid: Apid,
|
pub apid: Apid,
|
||||||
pub unique_id: u32,
|
pub unique_id: u21,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UniqueApidTargetId {
|
impl UniqueApidTargetId {
|
||||||
pub const fn new(apid: Apid, target: u32) -> Self {
|
pub const fn new(apid: Apid, target: u21) -> Self {
|
||||||
Self {
|
Self {
|
||||||
apid,
|
apid,
|
||||||
unique_id: target,
|
unique_id: target,
|
||||||
@@ -40,7 +41,7 @@ impl UniqueApidTargetId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw(&self) -> ComponentId {
|
pub fn raw(&self) -> ComponentId {
|
||||||
((self.apid as u64) << 32) | (self.unique_id as u64)
|
((self.apid.value() as u32) << 21) | (self.unique_id.value())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> ComponentId {
|
pub fn id(&self) -> ComponentId {
|
||||||
@@ -60,21 +61,21 @@ impl UniqueApidTargetId {
|
|||||||
}
|
}
|
||||||
Ok(Self::new(
|
Ok(Self::new(
|
||||||
tc.apid(),
|
tc.apid(),
|
||||||
u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
|
u21::new(u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap())),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u64> for UniqueApidTargetId {
|
impl From<ComponentId> for UniqueApidTargetId {
|
||||||
fn from(raw: u64) -> Self {
|
fn from(raw: ComponentId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
apid: (raw >> 32) as u16,
|
apid: u11::new((raw >> 21) as u16),
|
||||||
unique_id: raw as u32,
|
unique_id: u21::new(raw & u21::MAX.value()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UniqueApidTargetId> for u64 {
|
impl From<UniqueApidTargetId> for ComponentId {
|
||||||
fn from(target_and_apid_id: UniqueApidTargetId) -> Self {
|
fn from(target_and_apid_id: UniqueApidTargetId) -> Self {
|
||||||
target_and_apid_id.raw()
|
target_and_apid_id.raw()
|
||||||
}
|
}
|
||||||
@@ -335,12 +336,12 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
To,
|
To,
|
||||||
From,
|
From,
|
||||||
Sender: MessageSenderProvider<To>,
|
Sender: MessageSenderProvider<To>,
|
||||||
Receiver: MessageReceiverProvider<From>,
|
Receiver: MessageReceiverProvider<From>,
|
||||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||||
> MessageSenderAndReceiver<To, From, Sender, Receiver, SenderStore>
|
> MessageSenderAndReceiver<To, From, Sender, Receiver, SenderStore>
|
||||||
{
|
{
|
||||||
pub fn new(local_channel_id: ComponentId, message_receiver: Receiver) -> Self {
|
pub fn new(local_channel_id: ComponentId, message_receiver: Receiver) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -403,15 +404,15 @@ pub mod alloc_mod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Request,
|
Request,
|
||||||
ReqSender: MessageSenderProvider<Request>,
|
ReqSender: MessageSenderProvider<Request>,
|
||||||
ReqReceiver: MessageReceiverProvider<Request>,
|
ReqReceiver: MessageReceiverProvider<Request>,
|
||||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||||
Reply,
|
Reply,
|
||||||
ReplySender: MessageSenderProvider<Reply>,
|
ReplySender: MessageSenderProvider<Reply>,
|
||||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||||
>
|
>
|
||||||
RequestAndReplySenderAndReceiver<
|
RequestAndReplySenderAndReceiver<
|
||||||
Request,
|
Request,
|
||||||
ReqSender,
|
ReqSender,
|
||||||
@@ -496,33 +497,38 @@ mod tests {
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
|
use arbitrary_int::{u11, u14, u21};
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::tc::{PusTcCreator, PusTcSecondaryHeader},
|
|
||||||
ByteConversionError, SpHeader,
|
ByteConversionError, SpHeader,
|
||||||
|
ecss::{
|
||||||
|
CreatorConfig, MessageTypeId,
|
||||||
|
tc::{PusTcCreator, PusTcSecondaryHeader},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
queue::{GenericReceiveError, GenericSendError},
|
queue::{GenericReceiveError, GenericSendError},
|
||||||
request::{MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
|
request::{Apid, MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
|
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
|
||||||
|
|
||||||
const TEST_CHANNEL_ID_0: u64 = 1;
|
const TEST_CHANNEL_ID_0: ComponentId = 1;
|
||||||
const TEST_CHANNEL_ID_1: u64 = 2;
|
const TEST_CHANNEL_ID_1: ComponentId = 2;
|
||||||
const TEST_CHANNEL_ID_2: u64 = 3;
|
const TEST_CHANNEL_ID_2: ComponentId = 3;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic_target_id_with_apid() {
|
fn test_basic_target_id_with_apid() {
|
||||||
let id = UniqueApidTargetId::new(0x111, 0x01);
|
let id = UniqueApidTargetId::new(Apid::new(0x111), u21::new(0x01));
|
||||||
assert_eq!(id.apid, 0x111);
|
assert_eq!(id.apid.value(), 0x111);
|
||||||
assert_eq!(id.unique_id, 0x01);
|
assert_eq!(id.unique_id.value(), 0x01);
|
||||||
assert_eq!(id.id(), id.raw());
|
assert_eq!(id.id(), id.raw());
|
||||||
assert_eq!(u64::from(id), id.raw());
|
assert_eq!(ComponentId::from(id), id.raw());
|
||||||
let id_raw = id.raw();
|
let id_raw = id.raw();
|
||||||
let id_from_raw = UniqueApidTargetId::from(id_raw);
|
let id_from_raw = UniqueApidTargetId::from(id_raw);
|
||||||
assert_eq!(id_from_raw, id);
|
assert_eq!(id_from_raw, id);
|
||||||
assert_eq!(id.id(), (0x111 << 32) | 0x01);
|
assert_eq!(id.id(), (0x111 << 21) | 0x01);
|
||||||
let string = id.to_string();
|
let string = id.to_string();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
string,
|
string,
|
||||||
@@ -532,19 +538,24 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic_target_id_with_apid_from_pus_tc() {
|
fn test_basic_target_id_with_apid_from_pus_tc() {
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(u11::new(0x111), u14::new(5), 0);
|
||||||
let app_data = 1_u32.to_be_bytes();
|
let app_data = 1_u32.to_be_bytes();
|
||||||
let pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &app_data, true);
|
let pus_tc = PusTcCreator::new_simple(
|
||||||
|
sp_header,
|
||||||
|
MessageTypeId::new(17, 1),
|
||||||
|
&app_data,
|
||||||
|
CreatorConfig::default(),
|
||||||
|
);
|
||||||
let id = UniqueApidTargetId::from_pus_tc(&pus_tc).unwrap();
|
let id = UniqueApidTargetId::from_pus_tc(&pus_tc).unwrap();
|
||||||
assert_eq!(id.apid, 0x111);
|
assert_eq!(id.apid.value(), 0x111);
|
||||||
assert_eq!(id.unique_id, 1);
|
assert_eq!(id.unique_id.value(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic_target_id_with_apid_from_pus_tc_invalid_app_data() {
|
fn test_basic_target_id_with_apid_from_pus_tc_invalid_app_data() {
|
||||||
let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0);
|
let sp_header = SpHeader::new_for_unseg_tc(u11::new(0x111), u14::new(5), 0);
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
|
||||||
let pus_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
let pus_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||||
let error = UniqueApidTargetId::from_pus_tc(&pus_tc);
|
let error = UniqueApidTargetId::from_pus_tc(&pus_tc);
|
||||||
assert!(error.is_err());
|
assert!(error.is_err());
|
||||||
let error = error.unwrap_err();
|
let error = error.unwrap_err();
|
||||||
|
|||||||
@@ -211,8 +211,8 @@ mod tests {
|
|||||||
println,
|
println,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{self, TryRecvError},
|
|
||||||
Arc, Mutex,
|
Arc, Mutex,
|
||||||
|
mpsc::{self, TryRecvError},
|
||||||
},
|
},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
health::{HealthState, HealthTableProvider},
|
health::{HealthState, HealthTableProvider},
|
||||||
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, ModeRequestSender, UNKNOWN_MODE_VAL},
|
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, ModeRequestSender, UNKNOWN_MODE_VAL},
|
||||||
mode_tree::{
|
mode_tree::{
|
||||||
@@ -7,7 +8,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
queue::GenericTargetedMessagingError,
|
queue::GenericTargetedMessagingError,
|
||||||
request::{GenericMessage, RequestId},
|
request::{GenericMessage, RequestId},
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
@@ -252,10 +252,10 @@ impl SequenceExecutionHelper {
|
|||||||
Ok(ModeCommandingResult::AwaitingSuccessCheck)
|
Ok(ModeCommandingResult::AwaitingSuccessCheck)
|
||||||
} else if seq_table_value.entries.len() - 1 == sequence_idx {
|
} else if seq_table_value.entries.len() - 1 == sequence_idx {
|
||||||
self.state = SequenceExecutionHelperState::Idle;
|
self.state = SequenceExecutionHelperState::Idle;
|
||||||
return Ok(ModeCommandingResult::Done);
|
Ok(ModeCommandingResult::Done)
|
||||||
} else {
|
} else {
|
||||||
self.current_sequence_index = Some(sequence_idx + 1);
|
self.current_sequence_index = Some(sequence_idx + 1);
|
||||||
return Ok(ModeCommandingResult::StepDone);
|
Ok(ModeCommandingResult::StepDone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,7 +597,7 @@ impl SubsystemCommandingHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_internal_req_id(&mut self) {
|
fn update_internal_req_id(&mut self) {
|
||||||
let new_internal_req_id = self.request_id().unwrap() << 8
|
let new_internal_req_id = (self.request_id().unwrap() << 8)
|
||||||
| self.seq_exec_helper.current_sequence_index().unwrap() as u32;
|
| self.seq_exec_helper.current_sequence_index().unwrap() as u32;
|
||||||
self.seq_exec_helper.set_request_id(new_internal_req_id);
|
self.seq_exec_helper.set_request_id(new_internal_req_id);
|
||||||
self.active_internal_request_id = Some(new_internal_req_id);
|
self.active_internal_request_id = Some(new_internal_req_id);
|
||||||
@@ -682,9 +682,10 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ComponentId,
|
||||||
mode::{
|
mode::{
|
||||||
tests::{ModeReqSenderMock, ModeReqWrapper},
|
|
||||||
Mode, ModeAndSubmode, ModeReply, ModeRequest, UNKNOWN_MODE,
|
Mode, ModeAndSubmode, ModeReply, ModeRequest, UNKNOWN_MODE,
|
||||||
|
tests::{ModeReqSenderMock, ModeReqWrapper},
|
||||||
},
|
},
|
||||||
mode_tree::{
|
mode_tree::{
|
||||||
ModeStoreProvider, ModeStoreVec, SequenceModeTables, SequenceTableEntry,
|
ModeStoreProvider, ModeStoreVec, SequenceModeTables, SequenceTableEntry,
|
||||||
@@ -693,7 +694,6 @@ mod tests {
|
|||||||
queue::GenericTargetedMessagingError,
|
queue::GenericTargetedMessagingError,
|
||||||
request::{GenericMessage, MessageMetadata, RequestId},
|
request::{GenericMessage, MessageMetadata, RequestId},
|
||||||
subsystem::{ModeCommandingResult, ModeTreeHelperState, SequenceExecutionHelperState},
|
subsystem::{ModeCommandingResult, ModeTreeHelperState, SequenceExecutionHelperState},
|
||||||
ComponentId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -782,7 +782,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(self.sender.requests.borrow().len(), 2);
|
assert_eq!(self.sender.requests.borrow().len(), 2);
|
||||||
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
|
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
|
||||||
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as u64);
|
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as ComponentId);
|
||||||
assert_eq!(req_0.request_id, expected_req_id);
|
assert_eq!(req_0.request_id, expected_req_id);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req_0.request,
|
req_0.request,
|
||||||
@@ -792,7 +792,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
|
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as u64);
|
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as ComponentId);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req_1.request,
|
req_1.request,
|
||||||
ModeRequest::SetMode {
|
ModeRequest::SetMode {
|
||||||
@@ -808,7 +808,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(self.sender.requests.borrow().len(), 1);
|
assert_eq!(self.sender.requests.borrow().len(), 1);
|
||||||
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
|
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
|
||||||
assert_eq!(req_0.target_id, ExampleTargetId::Target2 as u64);
|
assert_eq!(req_0.target_id, ExampleTargetId::Target2 as ComponentId);
|
||||||
assert_eq!(req_0.request_id, expected_req_id);
|
assert_eq!(req_0.request_id, expected_req_id);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req_0.request,
|
req_0.request,
|
||||||
@@ -827,7 +827,7 @@ mod tests {
|
|||||||
assert_eq!(self.execution_helper.current_sequence_index().unwrap(), 0);
|
assert_eq!(self.execution_helper.current_sequence_index().unwrap(), 0);
|
||||||
assert_eq!(self.sender.requests.borrow().len(), 2);
|
assert_eq!(self.sender.requests.borrow().len(), 2);
|
||||||
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
|
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
|
||||||
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as u64);
|
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as ComponentId);
|
||||||
assert_eq!(req_0.request_id, expected_req_id);
|
assert_eq!(req_0.request_id, expected_req_id);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req_0.request,
|
req_0.request,
|
||||||
@@ -837,7 +837,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
|
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as u64);
|
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as ComponentId);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req_1.request,
|
req_1.request,
|
||||||
ModeRequest::SetMode {
|
ModeRequest::SetMode {
|
||||||
@@ -850,9 +850,9 @@ mod tests {
|
|||||||
|
|
||||||
fn create_default_mode_store() -> ModeStoreVec {
|
fn create_default_mode_store() -> ModeStoreVec {
|
||||||
let mut mode_store = ModeStoreVec::default();
|
let mut mode_store = ModeStoreVec::default();
|
||||||
mode_store.add_component(ExampleTargetId::Target0 as u64, UNKNOWN_MODE);
|
mode_store.add_component(ExampleTargetId::Target0 as ComponentId, UNKNOWN_MODE);
|
||||||
mode_store.add_component(ExampleTargetId::Target1 as u64, UNKNOWN_MODE);
|
mode_store.add_component(ExampleTargetId::Target1 as ComponentId, UNKNOWN_MODE);
|
||||||
mode_store.add_component(ExampleTargetId::Target2 as u64, UNKNOWN_MODE);
|
mode_store.add_component(ExampleTargetId::Target2 as ComponentId, UNKNOWN_MODE);
|
||||||
mode_store
|
mode_store
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,13 +863,13 @@ mod tests {
|
|||||||
let mut table_seq_0 = SequenceTableMapTable::new("MODE_0_SEQ_0");
|
let mut table_seq_0 = SequenceTableMapTable::new("MODE_0_SEQ_0");
|
||||||
table_seq_0.add_entry(SequenceTableEntry::new(
|
table_seq_0.add_entry(SequenceTableEntry::new(
|
||||||
"TARGET_0",
|
"TARGET_0",
|
||||||
ExampleTargetId::Target0 as u64,
|
ExampleTargetId::Target0 as ComponentId,
|
||||||
SUBSYSTEM_MD0_TGT0_MODE,
|
SUBSYSTEM_MD0_TGT0_MODE,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
table_seq_0.add_entry(SequenceTableEntry::new(
|
table_seq_0.add_entry(SequenceTableEntry::new(
|
||||||
"TARGET_1",
|
"TARGET_1",
|
||||||
ExampleTargetId::Target1 as u64,
|
ExampleTargetId::Target1 as ComponentId,
|
||||||
SUBSYSTEM_MD0_TGT1_MODE,
|
SUBSYSTEM_MD0_TGT1_MODE,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
@@ -881,13 +881,13 @@ mod tests {
|
|||||||
let mut table_seq_0 = SequenceTableMapTable::new("MODE_1_SEQ_0");
|
let mut table_seq_0 = SequenceTableMapTable::new("MODE_1_SEQ_0");
|
||||||
table_seq_0.add_entry(SequenceTableEntry::new(
|
table_seq_0.add_entry(SequenceTableEntry::new(
|
||||||
"MD1_SEQ0_TGT0",
|
"MD1_SEQ0_TGT0",
|
||||||
ExampleTargetId::Target0 as u64,
|
ExampleTargetId::Target0 as ComponentId,
|
||||||
SUBSYSTEM_MD1_ST0_TGT0_MODE,
|
SUBSYSTEM_MD1_ST0_TGT0_MODE,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
table_seq_0.add_entry(SequenceTableEntry::new(
|
table_seq_0.add_entry(SequenceTableEntry::new(
|
||||||
"MD1_SEQ0_TGT1",
|
"MD1_SEQ0_TGT1",
|
||||||
ExampleTargetId::Target1 as u64,
|
ExampleTargetId::Target1 as ComponentId,
|
||||||
SUBSYSTEM_MD1_ST0_TGT1_MODE,
|
SUBSYSTEM_MD1_ST0_TGT1_MODE,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
@@ -895,7 +895,7 @@ mod tests {
|
|||||||
let mut table_seq_1 = SequenceTableMapTable::new("MODE_1_SEQ_1");
|
let mut table_seq_1 = SequenceTableMapTable::new("MODE_1_SEQ_1");
|
||||||
table_seq_1.add_entry(SequenceTableEntry::new(
|
table_seq_1.add_entry(SequenceTableEntry::new(
|
||||||
"MD1_SEQ1_TGT2",
|
"MD1_SEQ1_TGT2",
|
||||||
ExampleTargetId::Target2 as u64,
|
ExampleTargetId::Target2 as ComponentId,
|
||||||
SUBSYSTEM_MD1_ST1_TGT2_MODE,
|
SUBSYSTEM_MD1_ST1_TGT2_MODE,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
@@ -1263,11 +1263,11 @@ mod tests {
|
|||||||
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
|
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
|
||||||
};
|
};
|
||||||
let req0 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
let req0 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
check_req(req0, ExampleTargetId::Target0 as u64);
|
check_req(req0, ExampleTargetId::Target0 as ComponentId);
|
||||||
let req1 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
let req1 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
check_req(req1, ExampleTargetId::Target1 as u64);
|
check_req(req1, ExampleTargetId::Target1 as ComponentId);
|
||||||
let req2 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
let req2 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
check_req(req2, ExampleTargetId::Target2 as u64);
|
check_req(req2, ExampleTargetId::Target2 as ComponentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1283,11 +1283,11 @@ mod tests {
|
|||||||
assert_eq!(req.request, ModeRequest::AnnounceMode);
|
assert_eq!(req.request, ModeRequest::AnnounceMode);
|
||||||
};
|
};
|
||||||
let req0 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
let req0 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
check_req(req0, ExampleTargetId::Target0 as u64);
|
check_req(req0, ExampleTargetId::Target0 as ComponentId);
|
||||||
let req1 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
let req1 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
check_req(req1, ExampleTargetId::Target1 as u64);
|
check_req(req1, ExampleTargetId::Target1 as ComponentId);
|
||||||
let req2 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
let req2 = tb.sender.requests.borrow_mut().pop_front().unwrap();
|
||||||
check_req(req2, ExampleTargetId::Target2 as u64);
|
check_req(req2, ExampleTargetId::Target2 as ComponentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
/// Generic abstraction for a check/countdown timer.
|
/// Generic abstraction for a check/countdown timer.
|
||||||
pub trait CountdownProvider: Debug {
|
pub trait Countdown: Debug {
|
||||||
fn has_expired(&self) -> bool;
|
fn has_expired(&self) -> bool;
|
||||||
fn reset(&mut self);
|
fn reset(&mut self);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,20 +9,20 @@
|
|||||||
//! They only need to send the received and generated data to these objects.
|
//! They only need to send the received and generated data to these objects.
|
||||||
use crate::queue::GenericSendError;
|
use crate::queue::GenericSendError;
|
||||||
use crate::{
|
use crate::{
|
||||||
pool::{PoolAddr, PoolError},
|
|
||||||
ComponentId,
|
ComponentId,
|
||||||
|
pool::{PoolAddr, PoolError},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use alloc_mod::*;
|
pub use alloc_mod::*;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use downcast_rs::{impl_downcast, Downcast};
|
use downcast_rs::{Downcast, impl_downcast};
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
|
SpHeader,
|
||||||
ecss::{
|
ecss::{
|
||||||
tc::PusTcReader,
|
tc::PusTcReader,
|
||||||
tm::{PusTmCreator, PusTmReader},
|
tm::{PusTmCreator, PusTmReader},
|
||||||
},
|
},
|
||||||
SpHeader,
|
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
@@ -50,20 +50,20 @@ impl PacketInPool {
|
|||||||
|
|
||||||
/// Generic trait for object which can send any packets in form of a raw bytestream, with
|
/// Generic trait for object which can send any packets in form of a raw bytestream, with
|
||||||
/// no assumptions about the received protocol.
|
/// no assumptions about the received protocol.
|
||||||
pub trait PacketSenderRaw: Send {
|
pub trait PacketHandler: Send {
|
||||||
type Error;
|
type Error;
|
||||||
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error>;
|
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extension trait of [PacketSenderRaw] which allows downcasting by implementing [Downcast].
|
/// Extension trait of [PacketHandler] which allows downcasting by implementing [Downcast].
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub trait PacketSenderRawExt: PacketSenderRaw + Downcast {
|
pub trait PacketSenderRawExt: PacketHandler + Downcast {
|
||||||
// Remove this once trait upcasting coercion has been implemented.
|
// Remove this once trait upcasting coercion has been implemented.
|
||||||
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
||||||
fn upcast(&self) -> &dyn PacketSenderRaw<Error = Self::Error>;
|
fn upcast(&self) -> &dyn PacketHandler<Error = Self::Error>;
|
||||||
// Remove this once trait upcasting coercion has been implemented.
|
// Remove this once trait upcasting coercion has been implemented.
|
||||||
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
||||||
fn upcast_mut(&mut self) -> &mut dyn PacketSenderRaw<Error = Self::Error>;
|
fn upcast_mut(&mut self) -> &mut dyn PacketHandler<Error = Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blanket implementation to automatically implement [PacketSenderRawExt] when the [alloc]
|
/// Blanket implementation to automatically implement [PacketSenderRawExt] when the [alloc]
|
||||||
@@ -71,16 +71,16 @@ pub trait PacketSenderRawExt: PacketSenderRaw + Downcast {
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
impl<T> PacketSenderRawExt for T
|
impl<T> PacketSenderRawExt for T
|
||||||
where
|
where
|
||||||
T: PacketSenderRaw + Send + 'static,
|
T: PacketHandler + Send + 'static,
|
||||||
{
|
{
|
||||||
// Remove this once trait upcasting coercion has been implemented.
|
// Remove this once trait upcasting coercion has been implemented.
|
||||||
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
||||||
fn upcast(&self) -> &dyn PacketSenderRaw<Error = Self::Error> {
|
fn upcast(&self) -> &dyn PacketHandler<Error = Self::Error> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
// Remove this once trait upcasting coercion has been implemented.
|
// Remove this once trait upcasting coercion has been implemented.
|
||||||
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
|
||||||
fn upcast_mut(&mut self) -> &mut dyn PacketSenderRaw<Error = Self::Error> {
|
fn upcast_mut(&mut self) -> &mut dyn PacketHandler<Error = Self::Error> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,19 +288,19 @@ pub mod std_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSenderRaw for mpsc::Sender<PacketAsVec> {
|
impl PacketHandler for mpsc::Sender<PacketAsVec> {
|
||||||
type Error = GenericSendError;
|
type Error = GenericSendError;
|
||||||
|
|
||||||
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.send(PacketAsVec::new(sender_id, packet.to_vec()))
|
self.send(PacketAsVec::new(sender_id, packet.to_vec()))
|
||||||
.map_err(|_| GenericSendError::RxDisconnected)
|
.map_err(|_| GenericSendError::RxDisconnected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSenderRaw for mpsc::SyncSender<PacketAsVec> {
|
impl PacketHandler for mpsc::SyncSender<PacketAsVec> {
|
||||||
type Error = GenericSendError;
|
type Error = GenericSendError;
|
||||||
|
|
||||||
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.try_send(PacketAsVec::new(sender_id, tc_raw.to_vec()))
|
self.try_send(PacketAsVec::new(sender_id, tc_raw.to_vec()))
|
||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
|
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
|
||||||
@@ -402,12 +402,12 @@ pub mod std_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Sender: PacketInPoolSender, PacketStore: CcsdsPacketPool + Send> PacketSenderRaw
|
impl<Sender: PacketInPoolSender, PacketStore: CcsdsPacketPool + Send> PacketHandler
|
||||||
for PacketSenderWithSharedPool<Sender, PacketStore>
|
for PacketSenderWithSharedPool<Sender, PacketStore>
|
||||||
{
|
{
|
||||||
type Error = StoreAndSendError;
|
type Error = StoreAndSendError;
|
||||||
|
|
||||||
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
|
||||||
let mut shared_pool = self.shared_pool.borrow_mut();
|
let mut shared_pool = self.shared_pool.borrow_mut();
|
||||||
let store_addr = shared_pool.add_raw_tc(packet)?;
|
let store_addr = shared_pool.add_raw_tc(packet)?;
|
||||||
drop(shared_pool);
|
drop(shared_pool);
|
||||||
@@ -450,7 +450,7 @@ pub mod std_mod {
|
|||||||
_sp_header: &SpHeader,
|
_sp_header: &SpHeader,
|
||||||
tc_raw: &[u8],
|
tc_raw: &[u8],
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.send_packet(sender_id, tc_raw)
|
self.handle_packet(sender_id, tc_raw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,10 +494,10 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
pub(crate) fn send_with_sender<SendError>(
|
pub(crate) fn send_with_sender<SendError>(
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
|
packet_sender: &(impl PacketHandler<Error = SendError> + ?Sized),
|
||||||
packet: &[u8],
|
packet: &[u8],
|
||||||
) -> Result<(), SendError> {
|
) -> Result<(), SendError> {
|
||||||
packet_sender.send_packet(sender_id, packet)
|
packet_sender.handle_packet(sender_id, packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
use arbitrary_int::{u11, u14};
|
||||||
use spacepackets::time::cds::CdsTime;
|
|
||||||
use spacepackets::time::TimeWriter;
|
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
|
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
|
use spacepackets::ecss::{CreatorConfig, MessageTypeId};
|
||||||
|
use spacepackets::time::cds::CdsTime;
|
||||||
|
|
||||||
pub struct PusTmWithCdsShortHelper {
|
pub struct PusTmWithCdsShortHelper {
|
||||||
apid: u16,
|
apid: u11,
|
||||||
cds_short_buf: [u8; 7],
|
cds_short_buf: [u8; 7],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTmWithCdsShortHelper {
|
impl PusTmWithCdsShortHelper {
|
||||||
pub fn new(apid: u16) -> Self {
|
pub fn new(apid: u11) -> Self {
|
||||||
Self {
|
Self {
|
||||||
apid,
|
apid,
|
||||||
cds_short_buf: [0; 7],
|
cds_short_buf: [0; 7],
|
||||||
@@ -22,7 +23,7 @@ impl PusTmWithCdsShortHelper {
|
|||||||
service: u8,
|
service: u8,
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
source_data: &'data [u8],
|
source_data: &'data [u8],
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
) -> PusTmCreator<'_, 'data> {
|
) -> PusTmCreator<'_, 'data> {
|
||||||
let time_stamp = CdsTime::now_with_u16_days().unwrap();
|
let time_stamp = CdsTime::now_with_u16_days().unwrap();
|
||||||
time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||||
@@ -35,7 +36,7 @@ impl PusTmWithCdsShortHelper {
|
|||||||
subservice: u8,
|
subservice: u8,
|
||||||
source_data: &'data [u8],
|
source_data: &'data [u8],
|
||||||
stamper: &CdsTime,
|
stamper: &CdsTime,
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
) -> PusTmCreator<'_, 'data> {
|
) -> PusTmCreator<'_, 'data> {
|
||||||
stamper.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
stamper.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||||
self.create_pus_tm_common(service, subservice, source_data, seq_count)
|
self.create_pus_tm_common(service, subservice, source_data, seq_count)
|
||||||
@@ -46,40 +47,50 @@ impl PusTmWithCdsShortHelper {
|
|||||||
service: u8,
|
service: u8,
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
source_data: &'data [u8],
|
source_data: &'data [u8],
|
||||||
seq_count: u16,
|
seq_count: u14,
|
||||||
) -> PusTmCreator<'_, 'data> {
|
) -> PusTmCreator<'_, 'data> {
|
||||||
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
|
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
|
||||||
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
|
let tc_header = PusTmSecondaryHeader::new_simple(
|
||||||
PusTmCreator::new(reply_header, tc_header, source_data, true)
|
MessageTypeId::new(service, subservice),
|
||||||
|
&self.cds_short_buf,
|
||||||
|
);
|
||||||
|
PusTmCreator::new(
|
||||||
|
reply_header,
|
||||||
|
tc_header,
|
||||||
|
source_data,
|
||||||
|
CreatorConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use spacepackets::{ecss::PusPacket, time::cds::CdsTime, CcsdsPacket};
|
use arbitrary_int::{u11, u14};
|
||||||
|
use spacepackets::{CcsdsPacket, ecss::PusPacket, time::cds::CdsTime};
|
||||||
|
|
||||||
use super::PusTmWithCdsShortHelper;
|
use super::PusTmWithCdsShortHelper;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_helper_with_stamper() {
|
fn test_helper_with_stamper() {
|
||||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(u11::new(0x123));
|
||||||
let stamper = CdsTime::new_with_u16_days(0, 0);
|
let stamper = CdsTime::new_with_u16_days(0, 0);
|
||||||
let tm = pus_tm_helper.create_pus_tm_with_stamper(17, 1, &[1, 2, 3, 4], &stamper, 25);
|
let tm =
|
||||||
assert_eq!(tm.service(), 17);
|
pus_tm_helper.create_pus_tm_with_stamper(17, 1, &[1, 2, 3, 4], &stamper, u14::new(25));
|
||||||
assert_eq!(tm.subservice(), 1);
|
assert_eq!(tm.service_type_id(), 17);
|
||||||
|
assert_eq!(tm.message_subtype_id(), 1);
|
||||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||||
assert_eq!(tm.seq_count(), 25);
|
assert_eq!(tm.seq_count().value(), 25);
|
||||||
assert_eq!(tm.timestamp(), [64, 0, 0, 0, 0, 0, 0])
|
assert_eq!(tm.timestamp(), [64, 0, 0, 0, 0, 0, 0])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_helper_from_now() {
|
fn test_helper_from_now() {
|
||||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(u11::new(0x123));
|
||||||
let tm = pus_tm_helper.create_pus_tm_timestamp_now(17, 1, &[1, 2, 3, 4], 25);
|
let tm = pus_tm_helper.create_pus_tm_timestamp_now(17, 1, &[1, 2, 3, 4], u14::new(25));
|
||||||
assert_eq!(tm.service(), 17);
|
assert_eq!(tm.service_type_id(), 17);
|
||||||
assert_eq!(tm.subservice(), 1);
|
assert_eq!(tm.message_subtype_id(), 1);
|
||||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||||
assert_eq!(tm.seq_count(), 25);
|
assert_eq!(tm.seq_count().value(), 25);
|
||||||
assert_eq!(tm.timestamp().len(), 7);
|
assert_eq!(tm.timestamp().len(), 7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user