Compare commits

..

2 Commits

Author SHA1 Message Date
Robin Mueller
45a99cabce bump spacepackets 2025-09-11 09:29:34 +02:00
Robin Mueller
ab44e3312c update all the code 2025-09-10 19:01:44 +02:00
79 changed files with 42334 additions and 5497 deletions

View File

@@ -47,8 +47,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check
docs:
@@ -57,7 +55,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc -p satrs --all-features
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
clippy:
name: Clippy
@@ -65,6 +63,4 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- run: cargo clippy -- -D warnings

View File

@@ -6,7 +6,6 @@ members = [
"satrs-example",
"satrs-minisim",
"satrs-shared",
"embedded-examples/embedded-client",
]
exclude = [

View File

@@ -1,18 +0,0 @@
[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 = { git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", 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"

View File

@@ -1,2 +0,0 @@
[interface]
serial_port = "/dev/ttyUSB0"

View File

@@ -1,107 +0,0 @@
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::{PacketTransport, 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()
}

View File

@@ -34,4 +34,4 @@ rustflags = [
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
[env]
DEFMT_LOG = "info"
DEFMT_LOG = "info"

View File

@@ -1,4 +1,4 @@
/target
/itm.txt
/.cargo/config.toml
/.cargo/config*
/.vscode

File diff suppressed because it is too large Load Diff

View File

@@ -9,33 +9,49 @@ default-run = "satrs-stm32f3-disco-rtic"
[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
defmt = "1"
defmt-rtt = { version = "1" }
panic-probe = { version = "1", features = ["print-defmt"] }
embedded-hal = "1"
defmt = "0.3"
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
panic-probe = { version = "0.3", features = ["print-defmt"] }
embedded-hal = "0.2.7"
cortex-m-semihosting = "0.5.0"
embassy-stm32 = { version = "0.4", features = ["defmt", "stm32f303vc", "unstable-pac"] }
enumset = "1"
heapless = "0.9"
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", 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"] }
heapless = "0.8"
rtic = { version = "2", features = ["thumbv7-backend"] }
rtic-sync = { version = "1" }
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
[dependencies.rtic]
version = "2"
features = ["thumbv7-backend"]
#[dependencies.satrs]
# path = "../../satrs"
#default-features = false
# features = ["defmt"]
[dependencies.rtic-monotonics]
version = "2"
features = ["cortex-m-systick"]
[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]
defmt-test = "0.4"
defmt-test = "0.3"
# cargo test
[profile.test]

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,12 @@
# 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]

View File

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

View File

@@ -0,0 +1,8 @@
/venv
/.tmtc-history.txt
/log
/.idea/*
!/.idea/runConfigurations
/seqcnt.txt
/tmtc_conf.json

View File

@@ -0,0 +1,4 @@
{
"com_if": "serial_cobs",
"serial_baudrate": 115200
}

View File

@@ -0,0 +1,305 @@
#!/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()

View File

@@ -0,0 +1,2 @@
tmtccmd == 8.0.1
# -e git+https://github.com/robamu-org/tmtccmd.git@main#egg=tmtccmd

View File

@@ -1,61 +1,76 @@
#![no_main]
#![no_std]
#![no_main]
use satrs_stm32f3_disco_rtic as _;
use panic_probe as _;
use rtic::app;
use stm32f3_discovery::leds::Leds;
use stm32f3_discovery::stm32f3xx_hal::delay::Delay;
use stm32f3_discovery::stm32f3xx_hal::{pac, prelude::*};
use stm32f3_discovery::switch_hal::{OutputSwitch, ToggleableOutputSwitch};
#[app(device = embassy_stm32)]
mod app {
use rtic_monotonics::fugit::ExtU32;
use rtic_monotonics::Monotonic as _;
use satrs_stm32f3_disco_rtic::{Direction, LedPinSet, Leds};
#[cortex_m_rt::entry]
fn main() -> ! {
defmt::println!("STM32F3 Discovery Blinky");
let dp = pac::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
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);
rtic_monotonics::systick_monotonic!(Mono, 1000);
let mut gpioe = dp.GPIOE.split(&mut rcc.ahb);
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);
#[shared]
struct Shared {}
//explicit on/off
leds.ld4_nw.on().ok();
delay.delay_ms(delay_ms);
leds.ld4_nw.off().ok();
delay.delay_ms(delay_ms);
#[local]
struct Local {
leds: Leds,
current_dir: Direction,
}
leds.ld5_ne.on().ok();
delay.delay_ms(delay_ms);
leds.ld5_ne.off().ok();
delay.delay_ms(delay_ms);
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
let p = embassy_stm32::init(Default::default());
leds.ld6_w.on().ok();
delay.delay_ms(delay_ms);
leds.ld6_w.off().ok();
delay.delay_ms(delay_ms);
defmt::info!("Starting sat-rs demo application for the STM32F3-Discovery using RTICv2");
leds.ld7_e.on().ok();
delay.delay_ms(delay_ms);
leds.ld7_e.off().ok();
delay.delay_ms(delay_ms);
let led_pin_set = LedPinSet {
pin_n: p.PE8,
pin_ne: p.PE9,
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.ld8_sw.on().ok();
delay.delay_ms(delay_ms);
leds.ld8_sw.off().ok();
delay.delay_ms(delay_ms);
// Initialize the systick interrupt & obtain the token to prove that we did
Mono::start(cx.core.SYST, 8_000_000);
blinky::spawn().expect("failed to spawn blinky task");
(
Shared {},
Local {
leds,
current_dir: Direction::North,
},
)
}
leds.ld9_se.on().ok();
delay.delay_ms(delay_ms);
leds.ld9_se.off().ok();
delay.delay_ms(delay_ms);
#[task(local = [leds, current_dir])]
async fn blinky(cx: blinky::Context) {
loop {
cx.local.leds.blink_next(cx.local.current_dir);
Mono::delay(200.millis()).await;
}
leds.ld10_s.on().ok();
delay.delay_ms(delay_ms);
leds.ld10_s.off().ok();
delay.delay_ms(delay_ms);
}
}

View File

@@ -1,190 +1,51 @@
#![no_main]
#![no_std]
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 cortex_m_semihosting::debug;
pub const APID: u11 = u11::new(0x02);
use defmt_brtt as _; // global logger
#[derive(defmt::Format, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Copy)]
pub enum Direction {
North,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest,
use stm32f3xx_hal as _; // memory layout
use panic_probe as _;
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
fn panic() -> ! {
cortex_m::asm::udf()
}
impl Direction {
pub fn switch_to_next(&mut self) -> (Self, Self) {
let curr = *self;
*self = match self {
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)
/// Terminates the application and makes a semihosting-capable debug tool exit
/// with status code 0.
pub fn exit() -> ! {
loop {
debug::exit(debug::EXIT_SUCCESS);
}
}
#[derive(Copy, Clone, Debug, defmt::Format, serde::Serialize, serde::Deserialize)]
pub enum Request {
Ping,
ChangeBlinkFrequency(Duration),
}
#[derive(Debug, defmt::Format, serde::Serialize, serde::Deserialize)]
pub struct TmHeader {
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);
/// 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(debug::EXIT_FAILURE);
}
}
pub struct LedPinSet {
pub pin_n: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE8>,
pub pin_ne: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE9>,
pub pin_e: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE10>,
pub pin_se: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE11>,
pub pin_s: embassy_stm32::Peri<'static, embassy_stm32::peripherals::PE12>,
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>,
}
// 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;
impl Leds {
pub fn new(pin_set: LedPinSet) -> Self {
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,
}
#[test]
fn it_works() {
assert!(true)
}
}

View File

@@ -1,349 +1,682 @@
#![no_std]
#![no_main]
use arbitrary_int::{u11, u14};
use cortex_m_semihosting::debug::{self, EXIT_FAILURE, EXIT_SUCCESS};
use satrs_stm32f3_disco_rtic::{create_tm_packet, tm_size, CcsdsPacketId, Request, Response};
use spacepackets::{CcsdsPacketCreationError, SpHeader};
use defmt_rtt as _; // global logger
use panic_probe as _;
use satrs::pus::verification::{
FailParams, TcStateAccepted, VerificationReportCreator, VerificationToken,
};
use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use satrs::spacepackets::ecss::EcssEnumU16;
use satrs::spacepackets::CcsdsPacket;
use satrs::spacepackets::{ByteConversionError, SpHeader};
// global logger + panicking-behavior + memory layout
use satrs_stm32f3_disco_rtic as _;
use rtic::app;
use heapless::{mpmc::Q8, Vec};
#[allow(unused_imports)]
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32};
use rtic_monotonics::systick::prelude::*;
use crate::app::Mono;
use satrs::seq_count::SequenceCountProviderCore;
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
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 DEFAULT_BLINK_FREQ_MS: u32 = 1000;
const TX_HANDLER_FREQ_MS: u32 = 20;
const MIN_DELAY_BETWEEN_TX_PACKETS_MS: u32 = 5;
const MAX_TC_LEN: usize = 128;
const MAX_TM_LEN: usize = 128;
pub const PUS_APID: u16 = 0x02;
pub const PUS_APID: u11 = u11::new(0x02);
type TxType = Tx<USART2, PA2<AF7<PushPull>>>;
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.
// It is simply the maximum packet lenght dividied by 254 rounded up.
const COBS_TM_OVERHEAD: usize = cobs::max_encoding_overhead(MAX_TM_LEN);
const COBS_TC_OVERHEAD: usize = (MAX_TC_LEN + 254 - 1) / 254;
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 TC_DMA_BUF_LEN: usize = 512;
// This is a static buffer which should ONLY (!) be used as the TX DMA
// 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 = heapless::Vec<u8, MAX_TM_LEN>;
type TmPacket = Vec<u8, MAX_TM_LEN>;
type TcPacket = Vec<u8, MAX_TC_LEN>;
static TM_QUEUE: heapless::mpmc::Queue<TmPacket, 16> = heapless::mpmc::Queue::new();
static TM_REQUESTS: Q8<TmPacket> = Q8::new();
#[derive(Debug, defmt::Format, thiserror::Error)]
pub enum TmSendError {
#[error("packet creation error: {0}")]
PacketCreation(#[from] CcsdsPacketCreationError),
#[error("queue error")]
Queue,
use core::sync::atomic::{AtomicU16, Ordering};
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 struct RequestWithTcId {
pub request: Request,
pub tc_id: CcsdsPacketId,
pub enum TmSendError {
ByteConversion(ByteConversionError),
Queue,
}
#[app(device = embassy_stm32)]
mod app {
use core::time::Duration;
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)]
pub enum Request {
Ping,
ChangeBlinkFrequency(u32),
}
#[derive(Debug, defmt::Format)]
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 {
use super::*;
use arbitrary_int::u14;
use rtic::Mutex;
use rtic_sync::{
channel::{Receiver, Sender},
make_channel,
};
use satrs_stm32f3_disco_rtic::{CcsdsPacketId, LedPinSet, Request, Response};
use spacepackets::CcsdsPacketReader;
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 stm32f3xx_hal::Switch;
#[allow(dead_code)]
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
systick_monotonic!(Mono, 1000);
embassy_stm32::bind_interrupts!(struct Irqs {
USART2 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART2>;
});
#[shared]
struct Shared {
blink_freq: Duration,
blink_freq: MillisDurationU32,
tx_shared: UartTxShared,
rx_transfer: Option<RxDmaTransferType>,
}
#[local]
struct Local {
leds: satrs_stm32f3_disco_rtic::Leds,
current_dir: satrs_stm32f3_disco_rtic::Direction,
seq_count: u14,
tx: embassy_stm32::usart::UartTx<'static, embassy_stm32::mode::Async>,
rx: embassy_stm32::usart::RingBufferedUartRx<'static>,
verif_reporter: VerificationReportCreator,
leds: Leds,
last_dir: Direction,
curr_dir: Iter<'static, Direction>,
}
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
static DMA_BUF: static_cell::ConstStaticCell<[u8; TC_DMA_BUF_LEN]> =
static_cell::ConstStaticCell::new([0; TC_DMA_BUF_LEN]);
let mut rcc = cx.device.RCC.constrain();
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
Mono::start(cx.core.SYST, 8_000_000);
defmt::info!("sat-rs demo application for the STM32F3-Discovery with RTICv2");
let led_pin_set = LedPinSet {
pin_n: p.PE8,
pin_ne: p.PE9,
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 = satrs_stm32f3_disco_rtic::Leds::new(led_pin_set);
let mut flash = cx.device.FLASH.constrain();
let clocks = rcc
.cfgr
.use_hse(8.MHz())
.sysclk(8.MHz())
.pclk1(8.MHz())
.freeze(&mut flash.acr);
let mut config = embassy_stm32::usart::Config::default();
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();
// Set up monotonic timer.
//let mono_timer = MonoTimer::new(cx.core.DWT, clocks, &mut cx.core.DCB);
let (tx, rx) = uart.split();
defmt::info!("Starting sat-rs demo application for the STM32F3-Discovery");
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");
blinky::spawn().unwrap();
blink::spawn().unwrap();
serial_tx_handler::spawn().unwrap();
serial_rx_handler::spawn(req_sender).unwrap();
req_handler::spawn(req_receiver).unwrap();
let verif_reporter = VerificationReportCreator::new(PUS_APID).unwrap();
(
Shared {
blink_freq: Duration::from_millis(DEFAULT_BLINK_FREQ_MS as u64),
blink_freq: MillisDurationU32::from_ticks(DEFAULT_BLINK_FREQ_MS),
tx_shared: UartTxShared {
last_completed: None,
state: UartTxState::Idle(Some(TxIdle {
tx: tx_serial,
dma_channel: dma1.ch7,
})),
},
rx_transfer: Some(rx_transfer),
},
Local {
verif_reporter,
leds,
tx,
seq_count: u14::new(0),
rx: rx.into_ring_buffered(DMA_BUF.take()),
current_dir: satrs_stm32f3_disco_rtic::Direction::North,
last_dir: Direction::North,
curr_dir: Direction::iter(),
},
)
}
#[task(local = [leds, current_dir], shared=[blink_freq])]
async fn blinky(mut cx: blinky::Context) {
#[task(local = [leds, curr_dir, last_dir], shared=[blink_freq])]
async fn blink(mut cx: blink::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 {
cx.local.leds.blink_next(cx.local.current_dir);
match curr_dir.next() {
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);
Mono::delay(MillisDurationU32::from_ticks(
current_blink_freq.as_millis() as u32,
))
.await;
Mono::delay(current_blink_freq).await;
}
}
#[task(
local = [
tx,
encoded_buf: [u8; TM_BUF_LEN] = [0; TM_BUF_LEN]
],
shared = [],
shared = [tx_shared],
)]
async fn serial_tx_handler(cx: serial_tx_handler::Context) {
async fn serial_tx_handler(mut cx: serial_tx_handler::Context) {
loop {
while let Some(vec) = TM_QUEUE.dequeue() {
let encoded_len =
cobs::encode_including_sentinels(&vec[0..vec.len()], cx.local.encoded_buf);
defmt::debug!("sending {} bytes over UART", encoded_len);
cx.local
.tx
.write(&cx.local.encoded_buf[0..encoded_len])
.await
.unwrap();
let is_idle = cx.shared.tx_shared.lock(|tx_shared| {
if let UartTxState::Idle(_) = tx_shared.state {
return true;
}
false
});
if is_idle {
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
if let Some(last_completed) = last_completed {
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;
}
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;
}
}
#[task(
local = [
rx,
read_buf: [u8; 128] = [0; 128],
verif_reporter,
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]
)]
async fn serial_rx_handler(
cx: serial_rx_handler::Context,
mut sender: Sender<'static, RequestWithTcId, 16>,
mut cx: serial_rx_handler::Context,
received_packet: Vec<u8, MAX_TC_LEN>,
) {
let mut decoder = cobs::CobsDecoder::new(cx.local.decode_buf);
loop {
match cx.local.rx.read(cx.local.read_buf).await {
Ok(bytes) => {
defmt::debug!("received {} bytes over UART", bytes);
for byte in cx.local.read_buf[0..bytes].iter() {
match decoder.feed(*byte) {
Ok(None) => (),
Ok(Some(packet_size)) => {
match CcsdsPacketReader::new_with_checksum(
&decoder.dest()[0..packet_size],
) {
Ok(packet) => {
let packet_id = packet.packet_id();
let psc = packet.psc();
let tc_packet_id = CcsdsPacketId { packet_id, psc };
if let Ok(request) =
postcard::from_bytes::<Request>(packet.packet_data())
{
sender
.send(RequestWithTcId {
request,
tc_id: tc_packet_id,
})
.await
.unwrap();
}
cx.local.timestamp[0] = P_FIELD_BASE;
defmt::info!("Received packet with {} bytes", received_packet.len());
let decode_buf = cx.local.decode_buf;
let packet = received_packet.as_slice();
let mut start_idx = None;
for (idx, byte) in packet.iter().enumerate() {
if *byte != 0 {
start_idx = Some(idx);
break;
}
}
if start_idx.is_none() {
defmt::warn!("decoding error, can only process cobs encoded frames, data is all 0");
return;
}
let start_idx = start_idx.unwrap();
match cobs::decode(&received_packet.as_slice()[start_idx..], decode_buf) {
Ok(len) => {
defmt::info!("Decoded packet length: {}", len);
let pus_tc = PusTcReader::new(decode_buf);
match pus_tc {
Ok((tc, _tc_len)) => {
match convert_pus_tc_to_request(
&tc,
cx.local.verif_reporter,
cx.local.src_data_buf,
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);
}
Err(e) => {
defmt::error!("error unpacking ccsds packet: {}", e);
Request::ChangeBlinkFrequency(new_freq_ms) => {
defmt::info!("Received blink frequency change request with new frequncy {}", new_freq_ms);
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) => {
defmt::error!("cobs decoding error: {}", e);
// TODO: Error handling: Send verification failure based on request error.
defmt::warn!("request error {}", e);
}
}
}
}
Err(e) => {
defmt::error!("uart read error: {}", e);
}
}
}
}
#[task(shared = [blink_freq], local = [seq_count])]
async fn req_handler(
mut cx: req_handler::Context,
mut receiver: Receiver<'static, RequestWithTcId, 16>,
) {
loop {
match receiver.recv().await {
Ok(request_with_tc_id) => {
let tm_send_result = match request_with_tc_id.request {
Request::Ping => handle_ping_request(&mut cx, request_with_tc_id.tc_id),
Request::ChangeBlinkFrequency(duration) => {
handle_change_blink_frequency_request(
&mut cx,
request_with_tc_id.tc_id,
duration,
)
}
};
if let Err(e) = tm_send_result {
defmt::error!("error sending TM response: {}", e);
Err(e) => {
defmt::warn!("Error unpacking PUS TC: {}", e);
}
}
Err(_e) => defmt::error!("request receive error"),
}
Err(_) => {
defmt::warn!("decoding error, can only process cobs encoded frames")
}
}
}
fn handle_ping_request(
cx: &mut req_handler::Context,
tc_packet_id: CcsdsPacketId,
) -> Result<(), TmSendError> {
defmt::info!("Received PUS ping telecommand, sending ping reply");
send_tm(tc_packet_id, Response::CommandDone, *cx.local.seq_count)?;
*cx.local.seq_count = cx.local.seq_count.wrapping_add(u14::new(1));
Ok(())
fn handle_ping_request(timestamp: &[u8]) {
defmt::info!("Received PUS ping telecommand, sending ping reply TM[17,2]");
let sp_header =
SpHeader::new_for_unseg_tc(PUS_APID, SEQ_COUNT_PROVIDER.get_and_increment(), 0);
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_change_blink_frequency_request(
cx: &mut req_handler::Context,
tc_packet_id: CcsdsPacketId,
duration: Duration,
) -> Result<(), TmSendError> {
defmt::info!(
"Received ChangeBlinkFrequency request, new frequency: {} ms",
duration.as_millis()
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,
&timestamp,
)
.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(
verif_reporter
.completion_success(
src_data_buf,
started_token,
SEQ_COUNT_PROVIDER.get(),
0,
timestamp,
)
.unwrap(),
);
if let Err(e) = result {
handle_tm_send_error(e);
}
}
#[task(binds = DMA1_CH6, shared = [rx_transfer])]
fn rx_dma_isr(mut cx: rx_dma_isr::Context) {
let mut tc_packet = TcPacket::new();
cx.shared.rx_transfer.lock(|rx_transfer| {
let rx_ref = rx_transfer.as_ref().unwrap();
if rx_ref.is_complete() {
let uart_rx_owned = rx_transfer.take().unwrap();
let (buf, c, rx) = uart_rx_owned.stop();
// 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 serial_isr(mut cx: serial_isr::Context) {
cx.shared
.blink_freq
.lock(|blink_freq| *blink_freq = duration);
send_tm(tc_packet_id, Response::CommandDone, *cx.local.seq_count)?;
*cx.local.seq_count = cx.local.seq_count.wrapping_add(u14::new(1));
Ok(())
}
}
fn send_tm(
tc_packet_id: CcsdsPacketId,
response: Response,
current_seq_count: u14,
) -> 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),
uptime_millis: Mono::now().duration_since_epoch().to_millis(),
};
let mut tm_packet = TmPacket::new();
let tm_size = tm_size(&tm_header, &response);
tm_packet.resize(tm_size, 0).expect("vec resize failed");
create_tm_packet(&mut tm_packet, sp_header, tm_header, response)?;
if TM_QUEUE.enqueue(tm_packet).is_err() {
defmt::warn!("TC queue full");
return Err(TmSendError::Queue);
}
Ok(())
}
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
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)
.tx_shared
.lock(|tx_shared| match &mut tx_shared.state {
UartTxState::Idle(_) => (),
UartTxState::Transmitting(transfer) => {
let transfer_ref = transfer.as_ref().unwrap();
if transfer_ref.is_complete() {
let transfer = transfer.take().unwrap();
let (_, dma_channel, mut tx) = transfer.stop();
tx.clear_event(TxEvent::TransmissionComplete);
tx_shared.state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
// We cache the last completed time to ensure that there is a minimum delay between consecutive
// transferred packets.
tx_shared.last_completed = Some(Mono::now());
}
}
});
let mut tc_packet = TcPacket::new();
cx.shared.rx_transfer.lock(|rx_transfer| {
let rx_transfer_ref = rx_transfer.as_ref().unwrap();
// Received a partial packet.
if rx_transfer_ref.is_event_triggered(RxEvent::Idle) {
let rx_transfer_owned = rx_transfer.take().unwrap();
let (buf, ch, mut rx, rx_len) = rx_transfer_owned.stop_and_return_received_bytes();
// 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
// allocated vector to do this.
tc_packet
.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]);
rx.clear_event(RxEvent::Idle);
serial_rx_handler::spawn(tc_packet).expect("spawning rx handler failed");
*rx_transfer = Some(rx.read_exact(buf, ch));
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,17 +16,16 @@ harness = false
[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
defmt = "1"
defmt = "0.3"
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
panic-probe = { version = "1", features = ["print-defmt"] }
panic-probe = { version = "0.3", features = ["print-defmt"] }
cortex-m-semihosting = "0.5.0"
# TODO: Replace with embassy-hal.
stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] }
embedded-alloc = "0.6"
rtic-sync = { version = "1", features = ["defmt-03"] }
[dependencies.smoltcp]
version = "0.12"
version = "0.11"
default-features = false
features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"]
@@ -40,12 +39,12 @@ features = ["cortex-m-systick"]
[dependencies.satrs]
path = "../../satrs"
# version = "0.2"
version = "0.2"
default-features = false
features = ["defmt", "heapless"]
[dev-dependencies]
defmt-test = "0.4"
defmt-test = "0.3"
# cargo build/run
[profile.dev]

View File

@@ -1,26 +0,0 @@
all: check build embedded test 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
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

View File

@@ -11,6 +11,8 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
fern = "0.7"
chrono = "0.4"
log = "0.4"
arbitrary-int = "2"
bitbybit = "1.4"
crossbeam-channel = "0.5"
delegate = "0.13"
zerocopy = "0.8"
@@ -21,8 +23,6 @@ lazy_static = "1"
strum = { version = "0.27", features = ["derive"] }
derive-new = "0.7"
cfg-if = "1"
arbitrary-int = "2"
bitbybit = "1.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@@ -38,8 +38,8 @@ version = "0.1.1"
path = "../satrs-mib"
[features]
default = ["heap_tmtc"]
heap_tmtc = []
default = ["heap_tmtc"]
[dev-dependencies]
env_logger = "0.11"

View File

@@ -484,7 +484,6 @@ mod tests {
sync::{mpsc, Arc},
};
use arbitrary_int::u21;
use satrs::{
mode::{ModeReply, ModeRequest},
mode_tree::ModeParent,
@@ -575,7 +574,7 @@ mod tests {
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_parent, reply_rx_to_parent) = mpsc::sync_channel(5);
let id = UniqueApidTargetId::new(Apid::Acs.raw_value(), u21::new(1));
let id = UniqueApidTargetId::new(Apid::Acs.raw_value(), 1);
let mode_node = ModeRequestHandlerMpscBounded::new(id.into(), request_rx);
let (composite_request_tx, composite_request_rx) = mpsc::channel();
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);

View File

@@ -2,7 +2,7 @@ use arbitrary_int::u11;
use satrs::pus::verification::RequestId;
use satrs::spacepackets::ecss::tc::PusTcCreator;
use satrs::spacepackets::ecss::tm::PusTmReader;
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::CreatorConfig;
use satrs::spacepackets::SpHeader;
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
use std::net::{IpAddr, SocketAddr, UdpSocket};
@@ -13,7 +13,8 @@ fn main() {
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
let pus_tc = PusTcCreator::new_simple(
SpHeader::new_from_apid(u11::new(0x02)),
MessageTypeId::new(17, 1),
17,
1,
&[],
CreatorConfig::default(),
);
@@ -34,9 +35,9 @@ fn main() {
match res {
Ok(_len) => {
let pus_tm = PusTmReader::new(&buf, 7).expect("Parsing PUS TM failed");
if pus_tm.service_type_id() == 17 && pus_tm.message_subtype_id() == 2 {
if pus_tm.service() == 17 && pus_tm.subservice() == 2 {
println!("Received PUS Ping Reply TM[17,2]")
} else if pus_tm.service_type_id() == 1 {
} else if pus_tm.service() == 1 {
if pus_tm.source_data().is_empty() {
println!("Invalid verification TM, no source data");
}
@@ -45,29 +46,28 @@ fn main() {
println!("Invalid verification TM source data, less than 4 bytes")
}
let req_id = RequestId::from_bytes(src_data).unwrap();
let subtype_id = pus_tm.message_subtype_id();
if subtype_id == 1 {
if pus_tm.subservice() == 1 {
println!("Received TM[1,1] acceptance success for request ID {req_id}")
} else if subtype_id == 2 {
} else if pus_tm.subservice() == 2 {
println!("Received TM[1,2] acceptance failure for request ID {req_id}")
} else if subtype_id == 3 {
} else if pus_tm.subservice() == 3 {
println!("Received TM[1,3] start success for request ID {req_id}")
} else if subtype_id == 4 {
} else if pus_tm.subservice() == 4 {
println!("Received TM[1,2] start failure for request ID {req_id}")
} else if subtype_id == 5 {
} else if pus_tm.subservice() == 5 {
println!("Received TM[1,5] step success for request ID {req_id}")
} else if subtype_id == 6 {
} else if pus_tm.subservice() == 6 {
println!("Received TM[1,6] step failure for request ID {req_id}")
} else if subtype_id == 7 {
} else if pus_tm.subservice() == 7 {
println!("Received TM[1,7] completion success for request ID {req_id}")
} else if subtype_id == 8 {
} else if pus_tm.subservice() == 8 {
println!("Received TM[1,8] completion failure for request ID {req_id}");
}
} else {
println!(
"Received TM[{}, {}] with {} bytes",
pus_tm.service_type_id(),
pus_tm.message_subtype_id(),
pus_tm.service(),
pus_tm.subservice(),
size
);
}

View File

@@ -1,4 +1,3 @@
use arbitrary_int::u11;
use lazy_static::lazy_static;
use satrs::{
res_code::ResultU16,
@@ -11,7 +10,7 @@ use strum::IntoEnumIterator;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use satrs::{
events_legacy::{EventU32TypedSev, SeverityInfo},
events::{EventU32TypedSev, SeverityInfo},
pool::{StaticMemoryPool, StaticPoolConfig},
};
@@ -45,7 +44,7 @@ lazy_static! {
pub static ref PACKET_ID_VALIDATOR: HashSet<PacketId> = {
let mut set = HashSet::new();
for id in crate::ids::Apid::iter() {
set.insert(PacketId::new(PacketType::Tc, true, u11::new(id as u16)));
set.insert(PacketId::new(PacketType::Tc, true, id.raw_value()));
}
set
};

View File

@@ -506,7 +506,6 @@ impl<ComInterface: SerialInterface> ModeChild for PcduHandler<ComInterface> {
mod tests {
use std::sync::mpsc;
use arbitrary_int::u21;
use satrs::{
mode::ModeRequest, power::SwitchStateBinary, request::GenericMessage, tmtc::PacketAsVec,
};
@@ -573,7 +572,7 @@ mod tests {
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
let mut handler = PcduHandler::new(
UniqueApidTargetId::new(Apid::Eps.raw_value(), u21::new(0)),
UniqueApidTargetId::new(Apid::Eps.raw_value(), 0),
"TEST_PCDU",
mode_node,
composite_request_rx,

View File

@@ -1,15 +1,14 @@
use std::sync::mpsc::{self};
use crate::pus::create_verification_reporter;
use arbitrary_int::traits::Integer as _;
use arbitrary_int::u11;
use satrs::event_man_legacy::{EventMessageU32, EventRoutingError};
use satrs::event_man::{EventMessageU32, EventRoutingError};
use satrs::pus::event::EventTmHook;
use satrs::pus::verification::VerificationReporter;
use satrs::pus::EcssTmSender;
use satrs::request::UniqueApidTargetId;
use satrs::{
event_man_legacy::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
event_man::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
pus::{
event_man::{
DefaultPusEventU32TmCreator, EventReporter, EventRequest, EventRequestWithToken,
@@ -61,11 +60,12 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
// telemetry for each event.
let event_reporter = EventReporter::new_with_hook(
PUS_EVENT_MANAGEMENT.raw(),
u11::ZERO,
u11::new(0),
0,
128,
EventApidSetter::default(),
);
)
.unwrap();
let pus_event_dispatcher =
DefaultPusEventU32TmCreator::new_with_default_backend(event_reporter);
let pus_event_man_send_provider = EventU32SenderMpscBounded::new(
@@ -218,9 +218,8 @@ impl<TmSender: EcssTmSender> EventHandler<TmSender> {
#[cfg(test)]
mod tests {
use arbitrary_int::u21;
use satrs::{
events_legacy::EventU32,
events::EventU32,
pus::verification::VerificationReporterConfig,
spacepackets::ecss::{tm::PusTmReader, PusPacket},
tmtc::PacketAsVec,
@@ -228,8 +227,8 @@ mod tests {
use super::*;
const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(u11::new(1), u21::new(2));
const TEST_EVENT: EventU32 = EventU32::new(satrs::events_legacy::Severity::Info, 1, 1);
const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(u11::new(1), 2);
const TEST_EVENT: EventU32 = EventU32::new(satrs::events::Severity::Info, 1, 1);
pub struct EventManagementTestbench {
pub event_tx: mpsc::SyncSender<EventMessageU32>,
@@ -243,7 +242,8 @@ mod tests {
let (event_tx, event_rx) = mpsc::sync_channel(10);
let (_event_req_tx, event_req_rx) = mpsc::sync_channel(10);
let (tm_sender, tm_receiver) = mpsc::channel();
let verif_reporter_cfg = VerificationReporterConfig::new(u11::new(0x05), 2, 2, 128);
let verif_reporter_cfg =
VerificationReporterConfig::new(u11::new(0x05), 2, 2, 128).unwrap();
let verif_reporter =
VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg);
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
@@ -269,7 +269,7 @@ mod tests {
.event_tx
.send(EventMessageU32::new(
TEST_CREATOR_ID.id(),
EventU32::new(satrs::events_legacy::Severity::Info, 1, 1),
EventU32::new(satrs::events::Severity::Info, 1, 1),
))
.expect("failed to send event");
testbench.pus_event_handler.handle_event_requests();

View File

@@ -1,9 +1,8 @@
use arbitrary_int::traits::Integer as _;
use derive_new::new;
use satrs::hk::UniqueId;
use satrs::request::UniqueApidTargetId;
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use satrs::spacepackets::ecss::{hk, CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::{hk, CreatorConfig};
use satrs::spacepackets::{ByteConversionError, SpHeader};
#[derive(Debug, new, Copy, Clone)]
@@ -30,7 +29,7 @@ impl HkUniqueId {
expected: 8,
});
}
buf[0..4].copy_from_slice(&self.target_id.unique_id.as_u32().to_be_bytes());
buf[0..4].copy_from_slice(&self.target_id.unique_id.to_be_bytes());
buf[4..8].copy_from_slice(&self.set_id.to_be_bytes());
Ok(8)
@@ -54,13 +53,9 @@ impl PusHkHelper {
hk_data_writer: &mut HkWriter,
buf: &'b mut [u8],
) -> Result<PusTmCreator<'a, 'b>, ByteConversionError> {
let sec_header = PusTmSecondaryHeader::new(
MessageTypeId::new(3, hk::Subservice::TmHkPacket as u8),
0,
0,
timestamp,
);
buf[0..4].copy_from_slice(&self.component_id.unique_id.as_u32().to_be_bytes());
let sec_header =
PusTmSecondaryHeader::new(3, hk::Subservice::TmHkPacket as u8, 0, 0, timestamp);
buf[0..4].copy_from_slice(&self.component_id.unique_id.to_be_bytes());
buf[4..8].copy_from_slice(&set_id.to_be_bytes());
let (_, second_half) = buf.split_at_mut(8);
let hk_data_len = hk_data_writer(second_half)?;

View File

@@ -2,7 +2,7 @@
use satrs::request::UniqueApidTargetId;
#[derive(Debug, PartialEq, Eq, strum::EnumIter)]
#[bitbybit::bitenum(u11)]
#[bitbybit::bitenum(u11, exhaustive = false)]
pub enum Apid {
Sched = 1,
GenericPus = 2,
@@ -13,9 +13,7 @@ pub enum Apid {
}
pub mod acs {
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u21, exhaustive = false)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Id {
Subsystem = 1,
Assembly = 2,
@@ -24,32 +22,30 @@ pub mod acs {
}
pub const SUBSYSTEM: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Subsystem.raw_value());
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Subsystem as u32);
pub const ASSEMBLY: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Assembly.raw_value());
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Assembly as u32);
pub const MGM0: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm0.raw_value());
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm0 as u32);
pub const MGM1: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm1.raw_value());
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm1 as u32);
}
pub mod eps {
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u21, exhaustive = false)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Id {
Pcdu = 0,
Subsystem = 1,
}
pub const PCDU: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Pcdu.raw_value());
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Pcdu as u32);
pub const SUBSYSTEM: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Subsystem.raw_value());
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Subsystem as u32);
}
pub mod generic_pus {
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u21, exhaustive = false)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Id {
PusEventManagement = 0,
PusRouting = 1,
@@ -61,49 +57,39 @@ pub mod generic_pus {
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(),
Id::PusEventManagement as u32,
);
pub const PUS_ROUTING: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusRouting as u32);
pub const PUS_TEST: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusTest as u32);
pub const PUS_ACTION: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusAction as u32);
pub const PUS_MODE: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusMode as u32);
pub const PUS_HK: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusHk.raw_value());
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusHk as u32);
}
pub mod sched {
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u21, exhaustive = false)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Id {
PusSched = 0,
}
pub const PUS_SCHED: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Sched.raw_value(), Id::PusSched.raw_value());
super::UniqueApidTargetId::new(super::Apid::Sched.raw_value(), Id::PusSched as u32);
}
pub mod tmtc {
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u21, exhaustive = false)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
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());
super::UniqueApidTargetId::new(super::Apid::Tmtc.raw_value(), Id::UdpServer as u32);
pub const TCP_SERVER: super::UniqueApidTargetId =
super::UniqueApidTargetId::new(super::Apid::Tmtc.raw_value(), Id::TcpServer.raw_value());
super::UniqueApidTargetId::new(super::Apid::Tmtc.raw_value(), Id::TcpServer as u32);
}

View File

@@ -5,7 +5,7 @@ use std::{
};
use log::{info, warn};
use satrs::hal::std::tcp_spacepackets_server::CcsdsPacketParser;
use satrs::tmtc::StoreAndSendError;
use satrs::{
encoding::ccsds::{SpValidity, SpacePacketValidator},
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
@@ -103,14 +103,16 @@ impl PacketSource for SyncTcpTmSource {
}
}
pub type TcpServer<ReceivesTc> = TcpSpacepacketsServer<
pub type TcpServer<ReceivesTc, SendError> = TcpSpacepacketsServer<
SyncTcpTmSource,
ReceivesTc,
SimplePacketValidator,
ConnectionFinishedHandler,
(),
SendError,
>;
pub struct TcpTask(pub TcpServer<TmTcSender>);
pub struct TcpTask(pub TcpServer<TmTcSender, StoreAndSendError>);
impl TcpTask {
pub fn new(
@@ -122,7 +124,8 @@ impl TcpTask {
Ok(Self(TcpSpacepacketsServer::new(
cfg,
tm_source,
CcsdsPacketParser::new(cfg.id, 2048, tc_sender, SimplePacketValidator { valid_ids }),
tc_sender,
SimplePacketValidator { valid_ids },
ConnectionFinishedHandler::default(),
None,
)?))

View File

@@ -1,15 +1,13 @@
#![allow(dead_code)]
use std::net::{SocketAddr, UdpSocket};
use std::sync::mpsc;
use log::{info, warn};
use satrs::hal::std::udp_server::{ReceiveResult, UdpTcServer};
use satrs::pus::HandlingStatus;
use satrs::queue::GenericSendError;
use satrs::tmtc::PacketAsVec;
use satrs::pool::{PoolProviderWithGuards, SharedStaticMemoryPool};
use satrs::tmtc::PacketInPool;
use satrs::tmtc::{PacketAsVec, PacketInPool, StoreAndSendError};
use satrs::{
hal::std::udp_server::{ReceiveResult, UdpTcServer},
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
};
use crate::tmtc::sender::TmTcSender;
@@ -17,6 +15,7 @@ pub trait UdpTmHandler {
fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
}
#[allow(dead_code)]
pub struct StaticUdpTmHandler {
pub tm_rx: mpsc::Receiver<PacketInPool>,
pub tm_store: SharedStaticMemoryPool,
@@ -69,7 +68,7 @@ impl UdpTmHandler for DynamicUdpTmHandler {
}
pub struct UdpTmtcServer<TmHandler: UdpTmHandler> {
pub udp_tc_server: UdpTcServer<TmTcSender, GenericSendError>,
pub udp_tc_server: UdpTcServer<TmTcSender, StoreAndSendError>,
pub tm_handler: TmHandler,
}
@@ -116,7 +115,7 @@ mod tests {
use arbitrary_int::traits::Integer as _;
use arbitrary_int::u14;
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::CreatorConfig;
use satrs::{
spacepackets::{
ecss::{tc::PusTcCreator, WritablePusPacket},
@@ -182,14 +181,9 @@ mod tests {
tm_handler,
};
let sph = SpHeader::new_for_unseg_tc(ids::Apid::GenericPus.raw_value(), u14::ZERO, 0);
let ping_tc = PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
&[],
CreatorConfig::default(),
)
.to_vec()
.unwrap();
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default())
.to_vec()
.unwrap();
let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed");
let client_addr = client.local_addr().unwrap();
println!("{}", server_addr);

View File

@@ -161,7 +161,7 @@ impl PusTcToRequestConverter<ActivePusActionRequestStd, ActionRequest> for Actio
verif_reporter: &impl VerificationReportingProvider,
time_stamp: &[u8],
) -> Result<(ActivePusActionRequestStd, ActionRequest), Self::Error> {
let subservice = tc.message_subtype_id();
let subservice = tc.subservice();
let user_data = tc.user_data();
if user_data.len() < 8 {
verif_reporter
@@ -270,14 +270,13 @@ impl TargetedPusService for ActionServiceWrapper {
#[cfg(test)]
mod tests {
use arbitrary_int::traits::Integer as _;
use satrs::pus::test_util::{
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, EcssTcVecCacher};
use satrs::request::MessageMetadata;
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::CreatorConfig;
use satrs::tmtc::PacketAsVec;
use satrs::ComponentId;
use satrs::{
@@ -450,13 +449,14 @@ mod tests {
);
// Create a basic action request and verify forwarding.
let sp_header = SpHeader::new_from_apid(TEST_APID);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
let action_id = 5_u32;
let mut app_data: [u8; 8] = [0; 8];
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_1.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_1.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, CreatorConfig::default());
testbench.add_tc(&pus8_packet);
let time_stamp: [u8; 7] = [0; 7];
testbench.verify_next_tc_is_handled_properly(&time_stamp);
@@ -492,7 +492,7 @@ mod tests {
TEST_COMPONENT_ID_1.id(),
);
// Create a basic action request and verify forwarding.
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
let action_id = 5_u32;
let mut app_data: [u8; 8] = [0; 8];
// Invalid ID, routing should fail.
@@ -518,11 +518,11 @@ mod tests {
TEST_COMPONENT_ID_0.raw(),
ActionRequestConverter::default(),
);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
let action_id = 5_u32;
let mut app_data: [u8; 8] = [0; 8];
// Invalid ID, routing should fail.
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
let pus8_packet = PusTcCreator::new(
SpHeader::new_from_apid(TEST_APID),
@@ -554,11 +554,11 @@ mod tests {
fn converter_action_req_with_data() {
let mut testbench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ActionRequestConverter::default());
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
let action_id = 5_u32;
let mut app_data: [u8; 16] = [0; 16];
// Invalid ID, routing should fail.
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
for i in 0..8 {
app_data[i + 8] = i as u8;
@@ -697,7 +697,7 @@ mod tests {
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default());
let action_reply = ActionReplyPus::new(5_u32, ActionReplyVariant::Completed);
let unrequested_reply =
GenericMessage::new(MessageMetadata::new(10_u32, 15_u32), action_reply);
GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), action_reply);
// Right now this function does not do a lot. We simply check that it does not panic or do
// weird stuff.
let result = testbench.handle_unrequested_reply(&unrequested_reply);

View File

@@ -164,7 +164,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
found: 4,
});
}
let subservice = tc.message_subtype_id();
let subservice = tc.subservice();
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());
@@ -302,13 +302,13 @@ impl TargetedPusService for HkServiceWrapper {
#[cfg(test)]
mod tests {
use arbitrary_int::traits::Integer as _;
use arbitrary_int::{u14, u21};
use arbitrary_int::traits::Integer;
use arbitrary_int::u14;
use satrs::pus::test_util::{
TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
};
use satrs::request::MessageMetadata;
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::CreatorConfig;
use satrs::{
hk::HkRequestVariant,
pus::test_util::TEST_APID,
@@ -327,20 +327,22 @@ mod tests {
use super::{HkReply, HkReplyHandler, HkRequestConverter};
pub const ZERO_SEQ: u14 = u14::ZERO;
#[test]
fn hk_converter_one_shot_req() {
let mut hk_bench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
let target_id = TEST_UNIQUE_ID_0;
let unique_id = 5_u32;
let mut app_data: [u8; 8] = [0; 8];
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&target_id.to_be_bytes());
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
let hk_req = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(3, Subservice::TcGenerateOneShotHk as u8),
3,
Subservice::TcGenerateOneShotHk as u8,
&app_data,
CreatorConfig::default(),
);
@@ -360,11 +362,11 @@ mod tests {
fn hk_converter_enable_periodic_generation() {
let mut hk_bench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
let target_id = TEST_UNIQUE_ID_0;
let unique_id = 5_u32;
let mut app_data: [u8; 8] = [0; 8];
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&target_id.to_be_bytes());
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
let mut generic_check = |tc: &PusTcCreator| {
let accepted_token = hk_bench.add_tc(tc);
@@ -379,14 +381,16 @@ mod tests {
};
let tc0 = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(3, Subservice::TcEnableHkGeneration as u8),
3,
Subservice::TcEnableHkGeneration as u8,
&app_data,
CreatorConfig::default(),
);
generic_check(&tc0);
let tc1 = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(3, Subservice::TcEnableDiagGeneration as u8),
3,
Subservice::TcEnableDiagGeneration as u8,
&app_data,
CreatorConfig::default(),
);
@@ -397,11 +401,11 @@ mod tests {
fn hk_conversion_disable_periodic_generation() {
let mut hk_bench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
let target_id = TEST_UNIQUE_ID_0;
let unique_id = 5_u32;
let mut app_data: [u8; 8] = [0; 8];
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&target_id.to_be_bytes());
app_data[4..8].copy_from_slice(&unique_id.to_be_bytes());
let mut generic_check = |tc: &PusTcCreator| {
let accepted_token = hk_bench.add_tc(tc);
@@ -416,14 +420,16 @@ mod tests {
};
let tc0 = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(3, Subservice::TcDisableHkGeneration as u8),
3,
Subservice::TcDisableHkGeneration as u8,
&app_data,
CreatorConfig::default(),
);
generic_check(&tc0);
let tc1 = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(3, Subservice::TcDisableDiagGeneration as u8),
3,
Subservice::TcDisableDiagGeneration as u8,
&app_data,
CreatorConfig::default(),
);
@@ -434,12 +440,12 @@ mod tests {
fn hk_conversion_modify_interval() {
let mut hk_bench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
let target_id = TEST_UNIQUE_ID_0;
let unique_id = 5_u32;
let mut app_data: [u8; 12] = [0; 12];
let collection_interval_factor = 5_u32;
app_data[0..4].copy_from_slice(&target_id.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&target_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());
@@ -457,14 +463,16 @@ mod tests {
};
let tc0 = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(3, Subservice::TcModifyHkCollectionInterval as u8),
3,
Subservice::TcModifyHkCollectionInterval as u8,
&app_data,
CreatorConfig::default(),
);
generic_check(&tc0);
let tc1 = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(3, Subservice::TcModifyDiagCollectionInterval as u8),
3,
Subservice::TcModifyDiagCollectionInterval as u8,
&app_data,
CreatorConfig::default(),
);
@@ -475,8 +483,8 @@ mod tests {
fn hk_reply_handler() {
let mut reply_testbench =
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), HkReplyHandler::default());
let sender_id = 2_u32;
let apid_target_id = u21::new(3);
let sender_id = 2_u64;
let apid_target_id = 3_u32;
let unique_id = 5_u32;
let (req_id, active_req) = reply_testbench.add_tc(TEST_APID, apid_target_id, &[]);
let reply = GenericMessage::new(
@@ -497,7 +505,7 @@ mod tests {
ReplyHandlerTestbench::new(TEST_COMPONENT_ID_1.id(), HkReplyHandler::default());
let action_reply = HkReply::new(5_u32, HkReplyVariant::Ack);
let unrequested_reply =
GenericMessage::new(MessageMetadata::new(10_u32, 15_u32), action_reply);
GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), action_reply);
// Right now this function does not do a lot. We simply check that it does not panic or do
// weird stuff.
let result = testbench.handle_unrequested_reply(&unrequested_reply);

View File

@@ -33,7 +33,7 @@ pub mod stack;
pub mod test;
pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter {
let verif_cfg = VerificationReporterConfig::new(apid, 1, 2, 8);
let verif_cfg = VerificationReporterConfig::new(apid, 1, 2, 8).unwrap();
// Every software component which needs to generate verification telemetry, gets a cloned
// verification reporter.
VerificationReporter::new(owner_id, &verif_cfg)
@@ -113,7 +113,7 @@ impl PusTcDistributor {
.verif_reporter
.acceptance_success(&self.tm_sender, init_token, self.stamp_helper.stamp())
.expect("Acceptance success failure");
let service = PusServiceId::try_from(pus_tc.service_type_id());
let service = PusServiceId::try_from(pus_tc.service());
let tc_in_memory: TcInMemory = if let Some(store_addr) = addr_opt {
PacketInPool::new(sender_id, store_addr).into()
} else {
@@ -531,11 +531,11 @@ pub fn generic_pus_request_timeout_handler(
pub(crate) mod tests {
use std::time::Duration;
use arbitrary_int::{u11, u21};
use arbitrary_int::u11;
use satrs::pus::test_util::TEST_COMPONENT_ID_0;
use satrs::pus::{MpscTmAsVecSender, PusTmVariant};
use satrs::request::RequestId;
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::CreatorConfig;
use satrs::{
pus::{
verification::test_util::TestVerificationReporter, ActivePusRequestStd,
@@ -593,11 +593,11 @@ pub(crate) mod tests {
pub fn add_tc(
&mut self,
apid: u11,
apid_target: u21,
apid_target: u32,
time_stamp: &[u8],
) -> (verification::RequestId, ActivePusRequestStd) {
let sp_header = SpHeader::new_from_apid(apid);
let sec_header_dummy = PusTcSecondaryHeader::new_simple(MessageTypeId::new(0, 0));
let sec_header_dummy = PusTcSecondaryHeader::new_simple(0, 0);
let init = self.verif_reporter.start_verification(&PusTcCreator::new(
sp_header,
sec_header_dummy,
@@ -722,7 +722,7 @@ pub(crate) mod tests {
token: VerificationToken<TcStateAccepted>,
time_stamp: &[u8],
expected_apid: u11,
expected_apid_target: u21,
expected_apid_target: u32,
) -> Result<(ActiveRequestInfo, Request), Converter::Error> {
if self.current_packet.is_none() {
return Err(GenericConversionError::InvalidAppData(

View File

@@ -1,8 +1,7 @@
use arbitrary_int::traits::Integer as _;
use arbitrary_int::u14;
use derive_new::new;
use satrs::mode_tree::{ModeNode, ModeParent};
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::CreatorConfig;
use satrs_example::ids;
use std::sync::mpsc;
use std::time::Duration;
@@ -80,13 +79,10 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
.write_to_be_bytes(&mut source_data)
.expect("writing mode reply failed");
let req_id = verification::RequestId::from(reply.request_id());
let sp_header = SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), u14::ZERO, 0);
let sec_header = PusTmSecondaryHeader::new(
MessageTypeId::new(200, Subservice::TmModeReply as u8),
0,
0,
time_stamp,
);
let sp_header =
SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), u14::new(0), 0);
let sec_header =
PusTmSecondaryHeader::new(200, Subservice::TmModeReply as u8, 0, 0, time_stamp);
let pus_tm = PusTmCreator::new(
sp_header,
sec_header,
@@ -158,7 +154,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
verif_reporter: &impl VerificationReportingProvider,
time_stamp: &[u8],
) -> Result<(ActivePusRequestStd, ModeRequest), Self::Error> {
let subservice = tc.message_subtype_id();
let subservice = tc.subservice();
let user_data = tc.user_data();
let not_enough_app_data = |expected: usize| {
verif_reporter
@@ -302,11 +298,10 @@ impl TargetedPusService for ModeServiceWrapper {
#[cfg(test)]
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::request::MessageMetadata;
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
use satrs::spacepackets::ecss::CreatorConfig;
use satrs::{
mode::{ModeAndSubmode, ModeReply, ModeRequest},
pus::mode::Subservice,
@@ -329,11 +324,10 @@ mod tests {
fn mode_converter_read_mode_request() {
let mut testbench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header =
PusTcSecondaryHeader::new_simple(MessageTypeId::new(200, Subservice::TcReadMode as u8));
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcReadMode as u8);
let mut app_data: [u8; 4] = [0; 4];
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
let token = testbench.add_tc(&tc);
let (_active_req, req) = testbench
@@ -346,12 +340,11 @@ mod tests {
fn mode_converter_set_mode_request() {
let mut testbench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header =
PusTcSecondaryHeader::new_simple(MessageTypeId::new(200, Subservice::TcSetMode as u8));
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcSetMode as u8);
let mut app_data: [u8; 4 + ModeAndSubmode::RAW_LEN] = [0; 4 + ModeAndSubmode::RAW_LEN];
let mode_and_submode = ModeAndSubmode::new(2, 1);
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
mode_and_submode
.write_to_be_bytes(&mut app_data[4..])
.unwrap();
@@ -373,13 +366,10 @@ mod tests {
fn mode_converter_announce_mode() {
let mut testbench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
200,
Subservice::TcAnnounceMode as u8,
));
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceMode as u8);
let mut app_data: [u8; 4] = [0; 4];
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
let token = testbench.add_tc(&tc);
let (_active_req, req) = testbench
@@ -392,13 +382,11 @@ mod tests {
fn mode_converter_announce_mode_recursively() {
let mut testbench =
PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
200,
Subservice::TcAnnounceModeRecursive as u8,
));
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
let sec_header =
PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceModeRecursive as u8);
let mut app_data: [u8; 4] = [0; 4];
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
let token = testbench.add_tc(&tc);
let (_active_req, req) = testbench
@@ -415,7 +403,7 @@ mod tests {
);
let mode_reply = ModeReply::ModeReply(ModeAndSubmode::new(5, 1));
let unrequested_reply =
GenericMessage::new(MessageMetadata::new(10_u32, 15_u32), mode_reply);
GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), mode_reply);
// Right now this function does not do a lot. We simply check that it does not panic or do
// weird stuff.
let result = testbench.handle_unrequested_reply(&unrequested_reply);

View File

@@ -1,7 +1,7 @@
use crate::pus::create_verification_reporter;
use crate::tmtc::sender::TmTcSender;
use log::info;
use satrs::event_man_legacy::{EventMessage, EventMessageU32};
use satrs::event_man::{EventMessage, EventMessageU32};
use satrs::pus::test::PusService17TestHandler;
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
use satrs::pus::PartialPusHandlingError;
@@ -122,7 +122,7 @@ impl DirectPusService for TestCustomServiceWrapper {
}
}
} else {
let fail_data = [tc.message_subtype_id()];
let fail_data = [tc.subservice()];
self.handler
.service_helper
.verif_reporter()

View File

@@ -55,7 +55,7 @@ impl GenericRequestRouter {
) {
warn!(
"Routing request for service {} failed: {error:?}",
tc.service_type_id()
tc.service()
);
let accepted_token: VerificationToken<TcStateAccepted> = active_request
.token()
@@ -66,8 +66,7 @@ impl GenericRequestRouter {
let apid_target_id = UniqueApidTargetId::from(id);
warn!("Target APID for request: {}", apid_target_id.apid);
warn!("Target Unique ID for request: {}", apid_target_id.unique_id);
let mut fail_data: [u8; core::mem::size_of::<ComponentId>()] =
[0; core::mem::size_of::<ComponentId>()];
let mut fail_data: [u8; 8] = [0; 8];
fail_data.copy_from_slice(&id.to_be_bytes());
verif_reporter
.completion_failure(

View File

@@ -4,7 +4,7 @@ use satrs::{
pus::EcssTmSender,
queue::GenericSendError,
spacepackets::ecss::WritablePusPacket,
tmtc::{PacketAsVec, PacketHandler, PacketSenderWithSharedPool},
tmtc::{PacketAsVec, PacketSenderRaw, PacketSenderWithSharedPool, StoreAndSendError},
ComponentId,
};
@@ -48,33 +48,26 @@ impl EcssTmSender for TmTcSender {
}
}
impl PacketHandler for TmTcSender {
type Error = GenericSendError;
impl PacketSenderRaw for TmTcSender {
type Error = StoreAndSendError;
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
match self {
TmTcSender::Static(packet_sender_with_shared_pool) => {
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();
packet_sender_with_shared_pool.send_packet(sender_id, packet)
}
TmTcSender::Heap(sync_sender) => sync_sender
.send_packet(sender_id, packet)
.map_err(StoreAndSendError::Send),
TmTcSender::Mock(sender) => sender.send_packet(sender_id, packet),
}
Ok(())
}
}
impl PacketHandler for MockSender {
type Error = GenericSendError;
impl PacketSenderRaw for MockSender {
type Error = StoreAndSendError;
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
let mut mut_queue = self.0.borrow_mut();
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
Ok(())

View File

@@ -9,11 +9,13 @@ use satrs::{
pool::PoolProvider,
spacepackets::{
ecss::{tm::PusTmZeroCopyWriter, PusPacket},
seq_count::SequenceCounter,
seq_count::SequenceCounterCcsdsSimple,
time::cds::MIN_CDS_FIELD_LEN,
CcsdsPacket,
},
};
use satrs::{
spacepackets::seq_count::SequenceCounter,
tmtc::{PacketAsVec, PacketInPool, SharedPacketPool},
};
@@ -59,7 +61,7 @@ impl TmFunnelCommon {
);
let entry = self
.msg_counter_map
.entry(zero_copy_writer.service_type_id())
.entry(zero_copy_writer.service())
.or_insert(0);
zero_copy_writer.set_msg_count(*entry);
if *entry == u16::MAX {
@@ -76,8 +78,8 @@ impl TmFunnelCommon {
fn packet_printout(tm: &PusTmZeroCopyWriter) {
info!(
"Sending PUS TM[{},{}] with APID {}",
tm.service_type_id(),
tm.message_subtype_id(),
tm.service(),
tm.subservice(),
tm.apid()
);
}

View File

@@ -11,9 +11,17 @@ license = "Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
serde = { version = "1", default-features = false, optional = true }
defmt = {version = "1", optional = true }
spacepackets = { git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", version = ">=0.14, <=0.16", default-features = false }
[dependencies.serde]
version = "1"
default-features = false
optional = true
[dependencies.defmt]
version = "1"
optional = true
[features]
serde = ["dep:serde", "spacepackets/serde"]

View File

@@ -1,4 +1,4 @@
//! This crates contains modules shared among other sat-rs framework crates.
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod res_code;

View File

@@ -14,7 +14,6 @@ pub struct ResultU16 {
}
impl ResultU16 {
#[inline]
pub const fn new(group_id: u8, unique_id: u8) -> Self {
Self {
group_id,
@@ -22,22 +21,18 @@ impl ResultU16 {
}
}
#[inline]
pub const fn raw(&self) -> u16 {
pub fn raw(&self) -> u16 {
((self.group_id as u16) << 8) | self.unique_id as u16
}
#[inline]
pub const fn group_id(&self) -> u8 {
pub fn group_id(&self) -> u8 {
self.group_id
}
#[inline]
pub const fn unique_id(&self) -> u8 {
pub fn unique_id(&self) -> u8 {
self.unique_id
}
#[inline]
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
Self::from(u16::from_be_bytes(bytes))
}
@@ -56,7 +51,6 @@ impl From<ResultU16> for EcssEnumU16 {
}
impl UnsignedEnum for ResultU16 {
#[inline]
fn size(&self) -> usize {
core::mem::size_of::<u16>()
}
@@ -73,14 +67,12 @@ impl UnsignedEnum for ResultU16 {
Ok(self.size())
}
#[inline]
fn value_raw(&self) -> u64 {
fn value(&self) -> u64 {
self.raw() as u64
}
}
impl EcssEnumeration for ResultU16 {
#[inline]
fn pfc(&self) -> u8 {
16
}

View File

@@ -8,12 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.3.0-alpha.3] 2025-09-??
- Bump `sat-rs` edition to 2024.
- Bumped `spacepackets` to v0.16
- `ComponentId` is u32 now
- Simplified TCP servers
## Changed
@@ -224,8 +219,7 @@ docs-rs hotfix
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
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.2...HEAD
[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

View File

@@ -14,26 +14,25 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
[dependencies]
satrs-shared = { version = "0.2", path = "../satrs-shared" }
spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
spacepackets = { git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", version = "0.16", default-features = false }
delegate = "0.13"
arbitrary-int = "2"
delegate = ">0.7, <=0.13"
paste = "1"
derive-new = "0.7"
num_enum = { version = "0.7", default-features = false }
cobs = { version = "0.5", default-features = false }
derive-new = ">=0.6, <=0.7"
num_enum = { version = ">0.5, <=0.7", default-features = false }
cobs = { version = "0.4", default-features = false }
thiserror = { version = "2", default-features = false }
hashbrown = { version = "0.16", optional = true }
static_cell = { version = "2" }
heapless = { version = "0.9", optional = true }
hashbrown = { version = ">=0.14, <=0.15", optional = true }
static_cell = { version = "2", optional = true }
dyn-clone = { version = "1", optional = true }
heapless = { version = "0.9", optional = true }
downcast-rs = { version = "2", default-features = false, optional = true }
bus = { version = "2.2", optional = true }
crossbeam-channel = { version = "0.5", default-features = false, optional = true }
postcard = { version = "1", features = ["alloc"] }
serde = { version = "1", default-features = false, optional = true }
socket2 = { version = "0.6", features = ["all"], optional = true }
arbitrary-int = "2"
mio = { version = "1", features = ["os-poll", "net"], optional = true }
defmt = { version = "1", optional = true }
@@ -49,7 +48,7 @@ tempfile = "3"
version = "1"
[features]
default = ["std", "heapless"]
default = ["std"]
std = [
"downcast-rs/std",
"alloc",
@@ -64,7 +63,6 @@ std = [
]
alloc = [
"serde/alloc",
"cobs/alloc",
"spacepackets/alloc",
"hashbrown",
"dyn-clone",
@@ -72,6 +70,7 @@ alloc = [
]
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
crossbeam = ["crossbeam-channel"]
heapless = ["dep:heapless", "static_cell"]
defmt = ["dep:defmt", "spacepackets/defmt"]
test_util = []

View File

@@ -296,18 +296,18 @@ mod tests {
fn test_mode_announce() {
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
assy_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as ComponentId, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
assy_helper
.send_announce_mode_cmd_to_children(1, &mode_req_sender, false)
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceMode);
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id2 as ComponentId);
assert_eq!(req.target_id, ExampleId::Id2 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceMode);
}
@@ -316,18 +316,18 @@ mod tests {
fn test_mode_announce_recursive() {
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
assy_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as ComponentId, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
assy_helper
.send_announce_mode_cmd_to_children(1, &mode_req_sender, true)
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id2 as ComponentId);
assert_eq!(req.target_id, ExampleId::Id2 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
}
@@ -337,12 +337,12 @@ mod tests {
let mut dev_mgmt_helper =
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode1 as u32, 0);
dev_mgmt_helper
.send_mode_cmd_to_one_child(
1,
ExampleId::Id1 as ComponentId,
ExampleId::Id1 as u64,
expected_mode,
false,
&mode_req_sender,
@@ -350,7 +350,7 @@ mod tests {
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 1);
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(
req.request,
@@ -368,7 +368,7 @@ mod tests {
assert_eq!(ctx.active_request_id, 1);
}
let reply = GenericMessage::new(
MessageMetadata::new(1, ExampleId::Id1 as ComponentId),
MessageMetadata::new(1, ExampleId::Id1 as u64),
ModeReply::ModeReply(expected_mode),
);
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {
@@ -387,15 +387,15 @@ mod tests {
let mut dev_mgmt_helper =
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as ComponentId, UNKNOWN_MODE);
dev_mgmt_helper.add_mode_child(ExampleId::Id2 as ComponentId, UNKNOWN_MODE);
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
dev_mgmt_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode2 as u32, 0);
dev_mgmt_helper
.send_mode_cmd_to_all_children(1, expected_mode, false, &mode_req_sender)
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as ComponentId);
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(
req.request,
@@ -405,7 +405,7 @@ mod tests {
}
);
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id2 as ComponentId);
assert_eq!(req.target_id, ExampleId::Id2 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(
req.request,
@@ -424,7 +424,7 @@ mod tests {
}
let reply = GenericMessage::new(
MessageMetadata::new(1, ExampleId::Id1 as ComponentId),
MessageMetadata::new(1, ExampleId::Id1 as u64),
ModeReply::ModeReply(expected_mode),
);
assert_eq!(
@@ -432,7 +432,7 @@ mod tests {
DevManagerHelperResult::Busy
);
let reply = GenericMessage::new(
MessageMetadata::new(1, ExampleId::Id2 as ComponentId),
MessageMetadata::new(1, ExampleId::Id2 as u64),
ModeReply::ModeReply(expected_mode),
);
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {

View File

@@ -1,6 +1,6 @@
use spacepackets::SpHeader;
use spacepackets::{CcsdsPacket, SpHeader};
use crate::{ComponentId, tmtc::PacketHandler};
use crate::{ComponentId, tmtc::PacketSenderRaw};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum SpValidity {
@@ -24,17 +24,13 @@ pub trait SpacePacketValidator {
#[derive(Default, Debug, PartialEq, Eq)]
pub struct ParseResult {
pub packets_found: u32,
pub parsed_bytes: usize,
// If an incomplete space packet was found, its start index is indicated by this value.
//pub incomplete_packet_start: Option<usize>,
/// If an incomplete space packet was found, its start index is indicated by this value.
pub incomplete_tail_start: Option<usize>,
}
/// This function parses a given buffer for tightly packed CCSDS space packets.
///
/// 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 parses a given buffer for tightly packed CCSDS space packets. 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
/// can read the full 7 bytes which constitue a space packet header plus one byte minimal size.
@@ -45,18 +41,17 @@ pub struct ParseResult {
/// [SpacePacketValidator]:
///
/// 1. [SpValidity::Valid]: The parser will forward all packets to the given `packet_sender` and
/// return the number of packets found.If the [PacketHandler::handle_packet] calls fails, the
/// return the number of packets found.If the [PacketSenderRaw::send_packet] calls fails, the
/// error will be returned.
/// 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.
/// 3. [SpValidity::Skip]: The parser skips the packet using the packet length determined from the
/// space packet header.
///
pub fn parse_buffer_for_ccsds_space_packets<SendError>(
buf: &[u8],
packet_validator: &(impl SpacePacketValidator + ?Sized),
sender_id: ComponentId,
packet_sender: &(impl PacketHandler<Error = SendError> + ?Sized),
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
) -> Result<ParseResult, SendError> {
let mut parse_result = ParseResult::default();
let mut current_idx = 0;
@@ -71,12 +66,11 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
let packet_size = sp_header.packet_len();
if (current_idx + packet_size) <= buf_len {
packet_sender
.handle_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
.send_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
parse_result.packets_found += 1;
} else {
// Move packet to start of buffer if applicable.
//parse_result.incomplete_packet_start = Some(current_idx);
break;
parse_result.incomplete_tail_start = Some(current_idx);
}
current_idx += packet_size;
continue;
@@ -90,7 +84,6 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
}
}
}
parse_result.parsed_bytes = current_idx;
Ok(parse_result)
}
@@ -99,7 +92,7 @@ mod tests {
use arbitrary_int::{u11, u14};
use spacepackets::{
CcsdsPacket, PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader,
ecss::{CreatorConfig, MessageTypeId, tc::PusTcCreator},
ecss::{CreatorConfig, tc::PusTcCreator},
};
use crate::{ComponentId, encoding::tests::TcCacher};
@@ -139,12 +132,7 @@ mod tests {
#[test]
fn test_basic() {
let sph = SpHeader::new_from_apid(TEST_APID_0);
let ping_tc = PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
&[],
CreatorConfig::default(),
);
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
let mut buffer: [u8; 32] = [0; 32];
let packet_len = ping_tc
.write_to_bytes(&mut buffer)
@@ -169,14 +157,8 @@ mod tests {
#[test]
fn test_multi_packet() {
let sph = SpHeader::new_from_apid(TEST_APID_0);
let ping_tc = PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
&[],
CreatorConfig::default(),
);
let action_tc =
PusTcCreator::new_simple(sph, MessageTypeId::new(8, 0), &[], CreatorConfig::default());
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], CreatorConfig::default());
let mut buffer: [u8; 32] = [0; 32];
let packet_len_ping = ping_tc
.write_to_bytes(&mut buffer)
@@ -210,15 +192,9 @@ mod tests {
#[test]
fn test_multi_apid() {
let sph = SpHeader::new_from_apid(TEST_APID_0);
let ping_tc = PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
&[],
CreatorConfig::default(),
);
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
let sph = SpHeader::new_from_apid(TEST_APID_1);
let action_tc =
PusTcCreator::new_simple(sph, MessageTypeId::new(8, 0), &[], CreatorConfig::default());
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], CreatorConfig::default());
let mut buffer: [u8; 32] = [0; 32];
let packet_len_ping = ping_tc
.write_to_bytes(&mut buffer)
@@ -248,13 +224,15 @@ mod tests {
fn test_split_packet_multi() {
let ping_tc = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_0),
MessageTypeId::new(17, 1),
17,
1,
&[],
CreatorConfig::default(),
);
let action_tc = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_1),
MessageTypeId::new(8, 0),
8,
0,
&[],
CreatorConfig::default(),
);
@@ -276,7 +254,8 @@ mod tests {
assert!(parse_result.is_ok());
let parse_result = parse_result.unwrap();
assert_eq!(parse_result.packets_found, 1);
let incomplete_tail_idx = parse_result.parsed_bytes;
assert!(parse_result.incomplete_tail_start.is_some());
let incomplete_tail_idx = parse_result.incomplete_tail_start.unwrap();
assert_eq!(incomplete_tail_idx, packet_len_ping);
let queue = tc_cacher.tc_queue.borrow();
@@ -289,7 +268,8 @@ mod tests {
fn test_one_split_packet() {
let ping_tc = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_0),
MessageTypeId::new(17, 1),
17,
1,
&[],
CreatorConfig::default(),
);

View File

@@ -1,5 +1,5 @@
use crate::{ComponentId, tmtc::PacketHandler};
use cobs::{decode_in_place, encode_including_sentinels, max_encoding_length};
use crate::{ComponentId, tmtc::PacketSenderRaw};
use cobs::{decode_in_place, encode, max_encoding_length};
/// 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
@@ -37,16 +37,15 @@ pub fn encode_packet_with_cobs(
if *current_idx + max_encoding_len + 2 > encoded_buf.len() {
return false;
}
*current_idx += encode_including_sentinels(packet, &mut encoded_buf[*current_idx..]);
encoded_buf[*current_idx] = 0;
*current_idx += 1;
*current_idx += encode(packet, &mut encoded_buf[*current_idx..]);
encoded_buf[*current_idx] = 0;
*current_idx += 1;
true
}
/// 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
/// This function parses a given buffer for COBS encoded packets. The packet structure is
/// 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
@@ -59,7 +58,7 @@ pub fn encode_packet_with_cobs(
pub fn parse_buffer_for_cobs_encoded_packets<SendError>(
buf: &mut [u8],
sender_id: ComponentId,
packet_sender: &(impl PacketHandler<Error = SendError> + ?Sized),
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
next_write_idx: &mut usize,
) -> Result<u32, SendError> {
let mut start_index_packet = 0;
@@ -80,7 +79,7 @@ pub fn parse_buffer_for_cobs_encoded_packets<SendError>(
let decode_result = decode_in_place(&mut buf[start_index_packet..i]);
if let Ok(packet_len) = decode_result {
packets_found += 1;
packet_sender.handle_packet(
packet_sender.send_packet(
sender_id,
&buf[start_index_packet..start_index_packet + packet_len],
)?;

View File

@@ -12,7 +12,7 @@ pub(crate) mod tests {
use crate::{
ComponentId,
tmtc::{PacketAsVec, PacketHandler},
tmtc::{PacketAsVec, PacketSenderRaw},
};
use super::cobs::encode_packet_with_cobs;
@@ -25,10 +25,10 @@ pub(crate) mod tests {
pub(crate) tc_queue: RefCell<VecDeque<PacketAsVec>>,
}
impl PacketHandler for TcCacher {
impl PacketSenderRaw for TcCacher {
type Error = ();
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
let mut mut_queue = self.tc_queue.borrow_mut();
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
Ok(())

File diff suppressed because it is too large Load Diff

View File

@@ -1,850 +0,0 @@
//! # 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");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,859 +0,0 @@
//! # 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());
}
}

View File

@@ -2,5 +2,5 @@
pub mod tcp_server;
pub mod udp_server;
pub mod tcp_cobs_server;
pub mod tcp_spacepackets_server;
mod tcp_cobs_server;
mod tcp_spacepackets_server;

View File

@@ -1,7 +1,5 @@
use alloc::sync::Arc;
use alloc::vec;
use cobs::CobsDecoderOwned;
use cobs::DecodeError;
use cobs::encode;
use core::sync::atomic::AtomicBool;
use core::time::Duration;
@@ -11,58 +9,40 @@ use std::io::Write;
use std::net::SocketAddr;
use std::vec::Vec;
use crate::queue::GenericSendError;
use crate::tmtc::PacketHandler;
use crate::encoding::parse_buffer_for_cobs_encoded_packets;
use crate::tmtc::PacketSenderRaw;
use crate::tmtc::PacketSource;
use crate::ComponentId;
use crate::hal::std::tcp_server::{
ConnectionResult, ServerConfig, TcpTcParser, TcpTmSender, TcpTmtcGenericServer,
ConnectionResult, ServerConfig, TcpTcParser, TcpTmSender, TcpTmtcError, TcpTmtcGenericServer,
};
use super::tcp_server::HandledConnectionHandler;
use super::tcp_server::HandledConnectionInfo;
/// Concrete [TcpTcParser] implementation for the [TcpTmtcInCobsServer].
pub struct CobsTcParser<PacketHandlerInstance: PacketHandler> {
sender_id: ComponentId,
owned_decoder: CobsDecoderOwned,
packet_handler: PacketHandlerInstance,
last_decode_error: Option<DecodeError>,
}
#[derive(Default)]
pub struct CobsTcParser {}
impl<PacketHandlerInstance: PacketHandler> CobsTcParser<PacketHandlerInstance> {
pub fn new(
impl<TmError, TcError: 'static> TcpTcParser<TmError, TcError> for CobsTcParser {
fn handle_tc_parsing(
&mut self,
tc_buffer: &mut [u8],
sender_id: ComponentId,
decoder_buf_size: usize,
packet_handler: PacketHandlerInstance,
) -> Self {
Self {
tc_sender: &(impl PacketSenderRaw<Error = TcError> + ?Sized),
conn_result: &mut HandledConnectionInfo,
current_write_idx: usize,
next_write_idx: &mut usize,
) -> Result<(), TcpTmtcError<TmError, TcError>> {
conn_result.num_received_tcs += parse_buffer_for_cobs_encoded_packets(
&mut tc_buffer[..current_write_idx],
sender_id,
owned_decoder: CobsDecoderOwned::new(decoder_buf_size),
packet_handler,
last_decode_error: None,
}
}
}
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),
}
}
tc_sender,
next_write_idx,
)
.map_err(|e| TcpTmtcError::TcError(e))?;
Ok(())
}
}
@@ -81,18 +61,22 @@ impl CobsTmSender {
}
}
impl TcpTmSender for CobsTmSender {
impl<TmError, TcError> TcpTmSender<TmError, TcError> for CobsTmSender {
fn handle_tm_sending(
&mut self,
tm_buffer: &mut [u8],
tm_source: &mut (impl PacketSource<Error = ()> + ?Sized),
tm_source: &mut (impl PacketSource<Error = TmError> + ?Sized),
conn_result: &mut HandledConnectionInfo,
stream: &mut TcpStream,
) -> Result<bool, std::io::Error> {
) -> Result<bool, TcpTmtcError<TmError, TcError>> {
let mut tm_was_sent = false;
// Write TM until TM source is exhausted or there is an unexpected error. For now, there
// is no limit for the amount of TM written this way.
while let Ok(read_tm_len) = tm_source.retrieve_packet(tm_buffer) {
loop {
// Write TM until TM source is exhausted. For now, there is no limit for the amount
// 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 {
return Ok(tm_was_sent);
}
@@ -111,7 +95,6 @@ impl TcpTmSender for CobsTmSender {
current_idx += 1;
stream.write_all(&self.tm_encoding_buffer[..current_idx])?;
}
Ok(tm_was_sent)
}
}
@@ -125,8 +108,8 @@ impl TcpTmSender for CobsTmSender {
///
/// 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
/// packets even from a data stream which is split up. The server wil use the streaming
/// [CobsDecoderOwned] decoder to parse for packets and pass them to a
/// packets even from a data stream which is split up. The server wil use the
/// [parse_buffer_for_cobs_encoded_packets] function to parse for packets and pass them to a
/// generic TC receiver. The user can use [crate::encoding::encode_packet_with_cobs] to encode
/// telecommands sent to the server.
///
@@ -135,19 +118,30 @@ impl TcpTmSender for CobsTmSender {
/// 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.
pub struct TcpTmtcInCobsServer<
TmSource: PacketSource<Error = ()>,
TcHandler: PacketHandler<Error = GenericSendError>,
TmSource: PacketSource<Error = TmError>,
TcSender: PacketSenderRaw<Error = SendError>,
HandledConnection: HandledConnectionHandler,
TmError,
SendError: 'static,
> {
pub generic_server:
TcpTmtcGenericServer<TmSource, CobsTmSender, CobsTcParser<TcHandler>, HandledConnection>,
pub generic_server: TcpTmtcGenericServer<
TmSource,
TcSender,
CobsTmSender,
CobsTcParser,
HandledConnection,
TmError,
SendError,
>,
}
impl<
TmSource: PacketSource<Error = ()>,
TcHandler: PacketHandler<Error = GenericSendError>,
TmSource: PacketSource<Error = TmError>,
TcReceiver: PacketSenderRaw<Error = TcError>,
HandledConnection: HandledConnectionHandler,
> TcpTmtcInCobsServer<TmSource, TcHandler, HandledConnection>
TmError: 'static,
TcError: 'static,
> TcpTmtcInCobsServer<TmSource, TcReceiver, HandledConnection, TmError, TcError>
{
/// Create a new TCP TMTC server which exchanges TMTC packets encoded with
/// [COBS protocol](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing).
@@ -162,16 +156,17 @@ impl<
pub fn new(
cfg: ServerConfig,
tm_source: TmSource,
cobs_tc_parser: CobsTcParser<TcHandler>,
tc_receiver: TcReceiver,
handled_connection: HandledConnection,
stop_signal: Option<Arc<AtomicBool>>,
) -> Result<Self, std::io::Error> {
Ok(Self {
generic_server: TcpTmtcGenericServer::new(
cfg,
cobs_tc_parser,
CobsTcParser::default(),
CobsTmSender::new(cfg.tm_buffer_size),
tm_source,
tc_receiver,
handled_connection,
stop_signal,
)?,
@@ -190,7 +185,7 @@ impl<
pub fn handle_all_connections(
&mut self,
poll_duration: Option<Duration>,
) -> Result<ConnectionResult, std::io::Error>;
) -> Result<ConnectionResult, TcpTmtcError<TmError, TcError>>;
}
}
}
@@ -217,6 +212,7 @@ mod tests {
ConnectionResult, ServerConfig,
tests::{ConnectionFinishedHandler, SyncTmSource},
},
queue::GenericSendError,
tmtc::PacketAsVec,
};
use alloc::sync::Arc;
@@ -247,12 +243,17 @@ mod tests {
tc_sender: mpsc::Sender<PacketAsVec>,
tm_source: SyncTmSource,
stop_signal: Option<Arc<AtomicBool>>,
) -> TcpTmtcInCobsServer<SyncTmSource, mpsc::Sender<PacketAsVec>, ConnectionFinishedHandler>
{
) -> TcpTmtcInCobsServer<
SyncTmSource,
mpsc::Sender<PacketAsVec>,
ConnectionFinishedHandler,
(),
GenericSendError,
> {
TcpTmtcInCobsServer::new(
ServerConfig::new(TCP_SERVER_ID, *addr, Duration::from_millis(2), 1024, 1024),
tm_source,
super::CobsTcParser::new(TCP_SERVER_ID, 1024, tc_sender),
tc_sender,
ConnectionFinishedHandler::default(),
stop_signal,
)

View File

@@ -12,7 +12,7 @@ use std::net::SocketAddr;
use std::thread;
use crate::ComponentId;
use crate::tmtc::PacketSource;
use crate::tmtc::{PacketSenderRaw, PacketSource};
use thiserror::Error;
// Re-export the TMTC in COBS server.
@@ -73,9 +73,11 @@ impl ServerConfig {
}
#[derive(Error, Debug)]
pub enum TcpTmError<TmError> {
pub enum TcpTmtcError<TmError, TcError> {
#[error("TM retrieval error: {0}")]
TmError(TmError),
#[error("TC retrieval error: {0}")]
TcError(TcError),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
}
@@ -114,29 +116,32 @@ pub trait HandledConnectionHandler {
}
/// Generic parser abstraction for an object which can parse for telecommands given a raw
/// bytestream received from a TCP socket and extract packets from them. This allows different
/// encoding schemes for telecommands.
pub trait TcpTcParser {
/// Reset the state of the parser.
fn reset(&mut self);
/// Pushes received data into the parser.
fn push(&mut self, tc_data: &[u8], conn_result: &mut HandledConnectionInfo);
/// bytestream received from a TCP socket and send them using a generic [PacketSenderRaw]
/// implementation. This allows different encoding schemes for telecommands.
pub trait TcpTcParser<TmError, SendError> {
fn handle_tc_parsing(
&mut self,
tc_buffer: &mut [u8],
sender_id: ComponentId,
tc_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
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
/// 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
/// sending back the data to a client.
pub trait TcpTmSender {
/// Returns whether any packets were sent back to the client.
pub trait TcpTmSender<TmError, TcError> {
fn handle_tm_sending(
&mut self,
tm_buffer: &mut [u8],
tm_source: &mut (impl PacketSource<Error = ()> + ?Sized),
tm_source: &mut (impl PacketSource<Error = TmError> + ?Sized),
conn_result: &mut HandledConnectionInfo,
stream: &mut TcpStream,
) -> Result<bool, std::io::Error>;
) -> Result<bool, TcpTmtcError<TmError, TcError>>;
}
/// TCP TMTC server implementation for exchange of generic TMTC packets in a generic way which
@@ -146,8 +151,7 @@ pub trait TcpTmSender {
/// through the following 4 core abstractions:
///
/// 1. [TcpTcParser] to parse for telecommands from the raw bytestream received from a client.
/// 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.
/// 2. Parsed telecommands will be sent using the [PacketSenderRaw] object.
/// 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].
///
@@ -159,10 +163,13 @@ pub trait TcpTmSender {
/// 1. [TcpTmtcInCobsServer] to exchange TMTC wrapped inside the COBS framing protocol.
/// 2. [TcpSpacepacketsServer] to exchange space packets via TCP.
pub struct TcpTmtcGenericServer<
TmSource: PacketSource<Error = ()>,
TmSender: TcpTmSender,
TcParser: TcpTcParser,
TmSource: PacketSource<Error = TmError>,
TcSender: PacketSenderRaw<Error = TcSendError>,
TmSender: TcpTmSender<TmError, TcSendError>,
TcParser: TcpTcParser<TmError, TcSendError>,
HandledConnection: HandledConnectionHandler,
TmError,
TcSendError,
> {
pub id: ComponentId,
pub finished_handler: HandledConnection,
@@ -170,6 +177,7 @@ pub struct TcpTmtcGenericServer<
pub(crate) inner_loop_delay: Duration,
pub(crate) tm_source: TmSource,
pub(crate) tm_buffer: Vec<u8>,
pub(crate) tc_sender: TcSender,
pub(crate) tc_buffer: Vec<u8>,
poll: Poll,
events: Events,
@@ -179,11 +187,23 @@ pub struct TcpTmtcGenericServer<
}
impl<
TmSource: PacketSource<Error = ()>,
TmSender: TcpTmSender,
TcParser: TcpTcParser,
TmSource: PacketSource<Error = TmError>,
TcSender: PacketSenderRaw<Error = TcSendError>,
TmSender: TcpTmSender<TmError, TcSendError>,
TcParser: TcpTcParser<TmError, TcSendError>,
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.
///
@@ -203,6 +223,7 @@ impl<
tc_parser: TcParser,
tm_sender: TmSender,
tm_source: TmSource,
tc_receiver: TcSender,
finished_handler: HandledConnection,
stop_signal: Option<Arc<AtomicBool>>,
) -> Result<Self, std::io::Error> {
@@ -242,6 +263,7 @@ impl<
inner_loop_delay: cfg.inner_loop_delay,
tm_source,
tm_buffer: vec![0; cfg.tm_buffer_size],
tc_sender: tc_receiver,
tc_buffer: vec![0; cfg.tc_buffer_size],
stop_signal,
finished_handler,
@@ -275,7 +297,7 @@ impl<
pub fn handle_all_connections(
&mut self,
poll_timeout: Option<Duration>,
) -> Result<ConnectionResult, std::io::Error> {
) -> Result<ConnectionResult, TcpTmtcError<TmError, TcSendError>> {
let mut handled_connections = 0;
// Poll Mio for events.
self.poll.poll(&mut self.events, poll_timeout)?;
@@ -305,7 +327,7 @@ impl<
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
Err(err) => {
self.reregister_poll_interest()?;
return Err(err);
return Err(TcpTmtcError::Io(err));
}
}
}
@@ -328,24 +350,58 @@ impl<
&mut self,
mut stream: TcpStream,
addr: SocketAddr,
) -> Result<(), std::io::Error> {
self.tc_handler.reset();
) -> Result<(), TcpTmtcError<TmError, TcSendError>> {
let mut current_write_idx;
let mut next_write_idx = 0;
let mut connection_result = HandledConnectionInfo::new(addr);
current_write_idx = next_write_idx;
loop {
let read_result = stream.read(&mut self.tc_buffer);
let read_result = stream.read(&mut self.tc_buffer[current_write_idx..]);
match read_result {
Ok(0) => {
// Connection closed by client.
// Connection closed by client. If any TC was read, parse for complete packets.
// 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;
}
Ok(read_len) => {
self.tc_handler
.push(&self.tc_buffer[0..read_len], &mut connection_result);
current_write_idx += read_len;
// TC buffer is full, we must parse for complete packets now.
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() {
// As per [TcpStream::set_read_timeout] documentation, this should work for
// both UNIX and Windows.
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(
&mut self.tm_buffer,
&mut self.tm_source,
@@ -370,7 +426,7 @@ impl<
}
}
_ => {
return Err(e);
return Err(TcpTmtcError::Io(e));
}
},
}
@@ -446,11 +502,8 @@ pub(crate) mod tests {
.connection_info
.pop_back()
.expect("no connection info available");
assert_eq!(
last_conn_result.num_received_tcs, num_tcs,
"received tcs mismatch"
);
assert_eq!(last_conn_result.num_sent_tms, num_tms, "sent tms missmatch");
assert_eq!(last_conn_result.num_received_tcs, num_tcs);
assert_eq!(last_conn_result.num_sent_tms, num_tms);
}
pub fn check_no_connections_left(&self) {

View File

@@ -7,104 +7,39 @@ use std::{io::Write, net::SocketAddr};
use crate::{
ComponentId,
encoding::{ccsds::SpacePacketValidator, parse_buffer_for_ccsds_space_packets},
queue::GenericSendError,
tmtc::{PacketHandler, PacketSource},
tmtc::{PacketSenderRaw, PacketSource},
};
use super::tcp_server::{
ConnectionResult, HandledConnectionHandler, HandledConnectionInfo, ServerConfig, TcpTcParser,
TcpTmSender, TcpTmtcGenericServer,
TcpTmSender, TcpTmtcError, TcpTmtcGenericServer,
};
pub struct CcsdsPacketParser<
PacketValidator: SpacePacketValidator,
PacketHandlerInstance: PacketHandler,
> {
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(
impl<T: SpacePacketValidator, TmError, TcError: 'static> TcpTcParser<TmError, TcError> for T {
fn handle_tc_parsing(
&mut self,
tc_buffer: &mut [u8],
sender_id: ComponentId,
parsing_buf_size: usize,
packet_handler: PacketHandlerInstance,
validator: PacketValidator,
) -> Self {
Self {
tc_sender: &(impl PacketSenderRaw<Error = TcError> + ?Sized),
conn_result: &mut HandledConnectionInfo,
current_write_idx: usize,
next_write_idx: &mut usize,
) -> Result<(), TcpTmtcError<TmError, TcError>> {
// 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,
parsing_buffer: alloc::vec![0; parsing_buf_size],
validator,
packet_handler,
current_write_index: 0,
}
}
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();
}
tc_sender,
)
.map_err(|e| TcpTmtcError::TcError(e))?;
if let Some(broken_tail_start) = parse_result.incomplete_tail_start {
// 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;
}
conn_result.num_received_tcs += parse_result.packets_found;
Ok(())
}
}
@@ -112,18 +47,21 @@ impl<PacketValidator: SpacePacketValidator, PacketHandlerInstance: PacketHandler
#[derive(Default)]
pub struct SpacepacketsTmSender {}
impl TcpTmSender for SpacepacketsTmSender {
impl<TmError, TcError> TcpTmSender<TmError, TcError> for SpacepacketsTmSender {
fn handle_tm_sending(
&mut self,
tm_buffer: &mut [u8],
tm_source: &mut (impl PacketSource<Error = ()> + ?Sized),
tm_source: &mut (impl PacketSource<Error = TmError> + ?Sized),
conn_result: &mut HandledConnectionInfo,
stream: &mut TcpStream,
) -> Result<bool, std::io::Error> {
) -> Result<bool, TcpTmtcError<TmError, TcError>> {
let mut tm_was_sent = false;
while let Ok(read_tm_len) = tm_source.retrieve_packet(tm_buffer) {
loop {
// Write TM until TM source is exhausted. For now, there is no limit for the amount
// 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 {
return Ok(tm_was_sent);
@@ -133,7 +71,6 @@ impl TcpTmSender for SpacepacketsTmSender {
stream.write_all(&tm_buffer[..read_tm_len])?;
}
Ok(tm_was_sent)
}
}
@@ -151,25 +88,32 @@ impl TcpTmSender for SpacepacketsTmSender {
/// 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.
pub struct TcpSpacepacketsServer<
TmSource: PacketSource<Error = ()>,
TcSender: PacketHandler<Error = GenericSendError>,
TmSource: PacketSource<Error = TmError>,
TcSender: PacketSenderRaw<Error = SendError>,
Validator: SpacePacketValidator,
HandledConnection: HandledConnectionHandler,
TmError,
SendError: 'static,
> {
pub generic_server: TcpTmtcGenericServer<
TmSource,
TcSender,
SpacepacketsTmSender,
CcsdsPacketParser<Validator, TcSender>,
Validator,
HandledConnection,
TmError,
SendError,
>,
}
impl<
TmSource: PacketSource<Error = ()>,
TcSender: PacketHandler<Error = GenericSendError>,
TmSource: PacketSource<Error = TmError>,
TcSender: PacketSenderRaw<Error = TcError>,
Validator: SpacePacketValidator,
HandledConnection: HandledConnectionHandler,
> TcpSpacepacketsServer<TmSource, TcSender, Validator, HandledConnection>
TmError: 'static,
TcError: 'static,
> TcpSpacepacketsServer<TmSource, TcSender, Validator, HandledConnection, TmError, TcError>
{
///
/// ## Parameter
@@ -178,7 +122,7 @@ impl<
/// * `tm_source` - Generic TM source used by the server to pull telemetry packets which are
/// then sent back to the client.
/// * `tc_sender` - Any received telecommands which were decoded successfully will be
/// forwarded using this [PacketHandler].
/// forwarded using this [PacketSenderRaw].
/// * `validator` - Used to determine the space packets relevant for further processing and
/// to detect broken space packets.
/// * `handled_connection_hook` - Called to notify the user about a succesfully handled
@@ -188,16 +132,18 @@ impl<
pub fn new(
cfg: ServerConfig,
tm_source: TmSource,
tc_parser: CcsdsPacketParser<Validator, TcSender>,
tc_sender: TcSender,
validator: Validator,
handled_connection_hook: HandledConnection,
stop_signal: Option<Arc<AtomicBool>>,
) -> Result<Self, std::io::Error> {
Ok(Self {
generic_server: TcpTmtcGenericServer::new(
cfg,
tc_parser,
validator,
SpacepacketsTmSender::default(),
tm_source,
tc_sender,
handled_connection_hook,
stop_signal,
)?,
@@ -216,7 +162,7 @@ impl<
pub fn handle_all_connections(
&mut self,
poll_timeout: Option<Duration>
) -> Result<ConnectionResult, std::io::Error>;
) -> Result<ConnectionResult, TcpTmtcError<TmError, TcError>>;
}
}
}
@@ -241,7 +187,7 @@ mod tests {
use hashbrown::HashSet;
use spacepackets::{
CcsdsPacket, PacketId, SpHeader,
ecss::{CreatorConfig, MessageTypeId, WritablePusPacket, tc::PusTcCreator},
ecss::{CreatorConfig, WritablePusPacket, tc::PusTcCreator},
};
use crate::{
@@ -251,6 +197,7 @@ mod tests {
ConnectionResult, ServerConfig,
tests::{ConnectionFinishedHandler, SyncTmSource},
},
queue::GenericSendError,
tmtc::PacketAsVec,
};
@@ -277,19 +224,23 @@ mod tests {
fn generic_tmtc_server(
addr: &SocketAddr,
tc_parser: super::CcsdsPacketParser<SimpleValidator, mpsc::Sender<PacketAsVec>>,
tc_sender: mpsc::Sender<PacketAsVec>,
tm_source: SyncTmSource,
validator: SimpleValidator,
stop_signal: Option<Arc<AtomicBool>>,
) -> TcpSpacepacketsServer<
SyncTmSource,
mpsc::Sender<PacketAsVec>,
SimpleValidator,
ConnectionFinishedHandler,
(),
GenericSendError,
> {
TcpSpacepacketsServer::new(
ServerConfig::new(TCP_SERVER_ID, *addr, Duration::from_millis(2), 1024, 1024),
tm_source,
tc_parser,
tc_sender,
validator,
ConnectionFinishedHandler::default(),
stop_signal,
)
@@ -305,8 +256,9 @@ mod tests {
validator.0.insert(TEST_PACKET_ID_0);
let mut tcp_server = generic_tmtc_server(
&auto_port_addr,
super::CcsdsPacketParser::new(TCP_SERVER_ID, 1024, tc_sender.clone(), validator),
tc_sender.clone(),
tm_source,
validator,
None,
);
let dest_addr = tcp_server
@@ -334,7 +286,8 @@ mod tests {
});
let ping_tc = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_0),
MessageTypeId::new(17, 1),
17,
1,
&[],
CreatorConfig::default(),
);
@@ -369,7 +322,8 @@ mod tests {
let mut total_tm_len = 0;
let verif_tm = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_0),
MessageTypeId::new(1, 1),
1,
1,
&[],
CreatorConfig::default(),
);
@@ -378,7 +332,8 @@ mod tests {
tm_source.add_tm(&tm_0);
let verif_tm = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_1),
MessageTypeId::new(1, 3),
1,
3,
&[],
CreatorConfig::default(),
);
@@ -392,8 +347,9 @@ mod tests {
validator.0.insert(TEST_PACKET_ID_1);
let mut tcp_server = generic_tmtc_server(
&auto_port_addr,
super::CcsdsPacketParser::new(TCP_SERVER_ID, 1024, tc_sender.clone(), validator),
tc_sender.clone(),
tm_source,
validator,
None,
);
let dest_addr = tcp_server
@@ -428,7 +384,8 @@ mod tests {
// Send telecommands
let ping_tc = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_0),
MessageTypeId::new(17, 1),
17,
1,
&[],
CreatorConfig::default(),
);
@@ -438,7 +395,8 @@ mod tests {
.expect("writing to TCP server failed");
let action_tc = PusTcCreator::new_simple(
SpHeader::new_from_apid(TEST_APID_1),
MessageTypeId::new(8, 0),
8,
0,
&[],
CreatorConfig::default(),
);

View File

@@ -1,6 +1,6 @@
//! Generic UDP TC server.
use crate::ComponentId;
use crate::tmtc::PacketHandler;
use crate::tmtc::PacketSenderRaw;
use core::fmt::Debug;
use std::io::{self, ErrorKind};
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
/// 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 [PacketHandler]. For example, this
/// sender in form of a special trait object which implements [PacketSenderRaw]. For example, this
/// can be used to send the telecommands to a centralized TC source component for further
/// processing and routing.
///
@@ -24,10 +24,9 @@ use std::vec::Vec;
/// use spacepackets::ecss::WritablePusPacket;
/// use satrs::hal::std::udp_server::UdpTcServer;
/// use satrs::ComponentId;
/// use satrs::tmtc::PacketHandler;
/// use satrs::tmtc::PacketSenderRaw;
/// use spacepackets::SpHeader;
/// use spacepackets::ecss::tc::{MessageTypeId, PusTcCreator, CreatorConfig};
/// use arbitrary_int::u11;
/// use spacepackets::ecss::tc::PusTcCreator;
///
/// const UDP_SERVER_ID: ComponentId = 0x05;
///
@@ -35,8 +34,8 @@ use std::vec::Vec;
/// 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)
/// .expect("Creating UDP TMTC server failed");
/// let sph = SpHeader::new_from_apid(u11::new(0x02));
/// let pus_tc = PusTcCreator::new_simple(sph, MessageTypeId::new(17, 1), &[], CreatorConfig::default());
/// let sph = SpHeader::new_from_apid(0x02);
/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
/// // Can not fail.
/// let ping_tc_raw = pus_tc.to_vec().unwrap();
///
@@ -60,7 +59,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)
/// 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.
pub struct UdpTcServer<TcSender: PacketHandler<Error = SendError>, SendError> {
pub struct UdpTcServer<TcSender: PacketSenderRaw<Error = SendError>, SendError> {
pub id: ComponentId,
pub socket: UdpSocket,
recv_buf: Vec<u8>,
@@ -78,7 +77,7 @@ pub enum ReceiveResult<SendError: Debug + 'static> {
Send(SendError),
}
impl<TcSender: PacketHandler<Error = SendError>, SendError: Debug + 'static>
impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
UdpTcServer<TcSender, SendError>
{
pub fn new<A: ToSocketAddrs>(
@@ -112,7 +111,7 @@ impl<TcSender: PacketHandler<Error = SendError>, SendError: Debug + 'static>
let (num_bytes, from) = res;
self.sender_addr = Some(from);
self.tc_sender
.handle_packet(self.id, &self.recv_buf[0..num_bytes])
.send_packet(self.id, &self.recv_buf[0..num_bytes])
.map_err(ReceiveResult::Send)?;
Ok(res)
}
@@ -127,12 +126,12 @@ mod tests {
use crate::ComponentId;
use crate::hal::std::udp_server::{ReceiveResult, UdpTcServer};
use crate::queue::GenericSendError;
use crate::tmtc::PacketHandler;
use crate::tmtc::PacketSenderRaw;
use arbitrary_int::u11;
use core::cell::RefCell;
use spacepackets::SpHeader;
use spacepackets::ecss::CreatorConfig;
use spacepackets::ecss::tc::PusTcCreator;
use spacepackets::ecss::{CreatorConfig, MessageTypeId};
use std::collections::VecDeque;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::vec::Vec;
@@ -146,10 +145,10 @@ mod tests {
pub sent_cmds: RefCell<VecDeque<Vec<u8>>>,
}
impl PacketHandler for PingReceiver {
impl PacketSenderRaw for PingReceiver {
type Error = GenericSendError;
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
assert_eq!(sender_id, UDP_SERVER_ID);
let mut sent_data = Vec::new();
sent_data.extend_from_slice(tc_raw);
@@ -169,12 +168,7 @@ mod tests {
.expect("Creating UDP TMTC server failed");
is_send(&udp_tc_server);
let sph = SpHeader::new_from_apid(u11::new(0x02));
let pus_tc = PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
&[],
CreatorConfig::default(),
);
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
let len = pus_tc
.write_to_bytes(&mut buf)
.expect("Error writing PUS TC packet");

View File

@@ -14,8 +14,8 @@
//! - 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/).
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(any(feature = "alloc", test))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
extern crate downcast_rs;
@@ -27,9 +27,7 @@ pub mod action;
pub mod dev_mgmt;
pub mod encoding;
pub mod event_man;
pub mod event_man_legacy;
pub mod events;
pub mod events_legacy;
#[cfg(feature = "std")]
pub mod executable;
pub mod hal;
@@ -57,7 +55,7 @@ pub use spacepackets;
use spacepackets::PacketId;
/// Generic component ID type.
pub type ComponentId = u32;
pub type ComponentId = u64;
pub trait ValidatorU16Id {
fn validate(&self, id: u16) -> bool;

View File

@@ -258,9 +258,6 @@ pub trait PoolProvider {
/// Delete data inside the pool given a [PoolAddr].
fn delete(&mut self, addr: PoolAddr) -> Result<(), PoolError>;
fn clear(&mut self) -> Result<(), PoolError>;
fn has_element_at(&self, addr: &PoolAddr) -> Result<bool, PoolError>;
/// Retrieve the length of the data at the given store address.
@@ -717,13 +714,6 @@ pub mod heapless_mod {
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> {
let addr = StaticPoolAddr::from(*addr);
self.validate_addr(&addr)?;
@@ -1065,13 +1055,6 @@ mod alloc_mod {
_ => size,
})
}
fn clear(&mut self) -> Result<(), PoolError> {
for size in self.sizes_lists.iter_mut() {
size.fill(STORE_FREE);
}
Ok(())
}
}
impl PoolProviderWithGuards for StaticMemoryPool {
@@ -1614,228 +1597,88 @@ mod tests {
mod heapless_tests {
use super::*;
use crate::static_subpool;
use std::cell::UnsafeCell;
use std::sync::Mutex;
const SUBPOOL_1_BLOCK_SIZE: usize = 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_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_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_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_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_BLOCK_SIZE: usize = 12;
static_subpool!(
SUBPOOL_6,
SUBPOOL_6_SIZES,
SUBPOOL_6_NUM_ELEMENTS as usize,
SUBPOOL_6_BLOCK_SIZE
);
macro_rules! make_heapless_pool {
($prefix:ident) => {{
paste::paste! {
static [<$prefix _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 [<$prefix _SUBPOOL_1_SIZES>]: std::sync::Mutex<
std::cell::UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>
> = std::sync::Mutex::new(
std::cell::UnsafeCell::new([STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize])
);
static [<$prefix _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 [<$prefix _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]
);
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]
fn test_heapless_add_and_read() {
let mut pool_provider = make_heapless_pool!(T0);
generic_test_add_and_read::<16>(&mut pool_provider);
}
#[test]
fn test_add_smaller_than_full_slot() {
let mut pool_provider = make_heapless_pool!(T1);
generic_test_add_smaller_than_full_slot(&mut pool_provider);
}
#[test]
fn test_delete() {
let mut pool_provider = make_heapless_pool!(T2);
generic_test_delete(&mut pool_provider);
}
#[test]
fn test_modify() {
let mut pool_provider = make_heapless_pool!(T3);
generic_test_modify(&mut pool_provider);
}
#[test]
fn test_consecutive_reservation() {
let mut pool_provider = make_heapless_pool!(T4);
generic_test_consecutive_reservation(&mut pool_provider);
}
#[test]
fn test_read_does_not_exist() {
let mut pool_provider = make_heapless_pool!(T5);
generic_test_read_does_not_exist(&mut pool_provider);
}
#[test]
fn test_store_full() {
let mut pool_provider = make_heapless_pool!(T6);
generic_test_store_full(&mut pool_provider);
}
#[test]
fn test_invalid_pool_idx() {
let mut pool_provider = make_heapless_pool!(T7);
generic_test_invalid_pool_idx(&mut pool_provider);
}
#[test]
fn test_invalid_packet_idx() {
let mut pool_provider = make_heapless_pool!(T8);
generic_test_invalid_packet_idx(&mut pool_provider);
}
#[test]
fn test_add_too_large() {
let mut pool_provider = make_heapless_pool!(T9);
generic_test_add_too_large(&mut pool_provider);
}
#[test]
fn test_data_too_large_1() {
let mut pool_provider = make_heapless_pool!(T10);
generic_test_data_too_large_1(&mut pool_provider);
}
#[test]
fn test_free_element_too_large() {
let mut pool_provider = make_heapless_pool!(T11);
generic_test_free_element_too_large(&mut pool_provider);
}
#[test]
fn test_pool_guard_deletion_man_creation() {
let mut pool_provider = make_heapless_pool!(T12);
generic_test_pool_guard_deletion_man_creation(&mut pool_provider);
}
#[test]
fn test_pool_guard_deletion() {
let mut pool_provider = make_heapless_pool!(T13);
generic_test_pool_guard_deletion(&mut pool_provider);
}
#[test]
fn test_pool_guard_with_release() {
let mut pool_provider = make_heapless_pool!(T14);
generic_test_pool_guard_with_release(&mut pool_provider);
}
#[test]
fn test_pool_modify_guard_man_creation() {
let mut pool_provider = make_heapless_pool!(T15);
generic_test_pool_modify_guard_man_creation(&mut pool_provider);
}
#[test]
fn test_pool_modify_guard() {
let mut pool_provider = make_heapless_pool!(T16);
generic_test_pool_modify_guard(&mut pool_provider);
}
#[test]
fn modify_pool_index_above_0() {
let mut pool_provider = make_heapless_pool!(T17);
generic_modify_pool_index_above_0(&mut pool_provider);
}
#[test]
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> =
StaticHeaplessMemoryPool::new(true);
fn small_heapless_pool() -> StaticHeaplessMemoryPool<3> {
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
StaticHeaplessMemoryPool::new(false);
assert!(
heapless_pool
.grow(
SUBPOOL_2_T18.take(),
SUBPOOL_2_SIZES_T18.take(),
SUBPOOL_1.take(),
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
SUBPOOL_1_NUM_ELEMENTS,
true
)
.is_ok()
);
assert!(
heapless_pool
.grow(
SUBPOOL_2.take(),
SUBPOOL_2_SIZES.take(),
SUBPOOL_2_NUM_ELEMENTS,
true
)
@@ -1844,8 +1687,143 @@ mod tests {
assert!(
heapless_pool
.grow(
SUBPOOL_4_T18.take(),
SUBPOOL_4_SIZES_T18.take(),
SUBPOOL_3.take(),
SUBPOOL_3_SIZES.take(),
SUBPOOL_3_NUM_ELEMENTS,
true
)
.is_ok()
);
heapless_pool
}
#[test]
fn test_heapless_add_and_read() {
let mut pool_provider = small_heapless_pool();
generic_test_add_and_read::<16>(&mut pool_provider);
}
#[test]
fn test_add_smaller_than_full_slot() {
let mut pool_provider = small_heapless_pool();
generic_test_add_smaller_than_full_slot(&mut pool_provider);
}
#[test]
fn test_delete() {
let mut pool_provider = small_heapless_pool();
generic_test_delete(&mut pool_provider);
}
#[test]
fn test_modify() {
let mut pool_provider = small_heapless_pool();
generic_test_modify(&mut pool_provider);
}
#[test]
fn test_consecutive_reservation() {
let mut pool_provider = small_heapless_pool();
generic_test_consecutive_reservation(&mut pool_provider);
}
#[test]
fn test_read_does_not_exist() {
let mut pool_provider = small_heapless_pool();
generic_test_read_does_not_exist(&mut pool_provider);
}
#[test]
fn test_store_full() {
let mut pool_provider = small_heapless_pool();
generic_test_store_full(&mut pool_provider);
}
#[test]
fn test_invalid_pool_idx() {
let mut pool_provider = small_heapless_pool();
generic_test_invalid_pool_idx(&mut pool_provider);
}
#[test]
fn test_invalid_packet_idx() {
let mut pool_provider = small_heapless_pool();
generic_test_invalid_packet_idx(&mut pool_provider);
}
#[test]
fn test_add_too_large() {
let mut pool_provider = small_heapless_pool();
generic_test_add_too_large(&mut pool_provider);
}
#[test]
fn test_data_too_large_1() {
let mut pool_provider = small_heapless_pool();
generic_test_data_too_large_1(&mut pool_provider);
}
#[test]
fn test_free_element_too_large() {
let mut pool_provider = small_heapless_pool();
generic_test_free_element_too_large(&mut pool_provider);
}
#[test]
fn test_pool_guard_deletion_man_creation() {
let mut pool_provider = small_heapless_pool();
generic_test_pool_guard_deletion_man_creation(&mut pool_provider);
}
#[test]
fn test_pool_guard_deletion() {
let mut pool_provider = small_heapless_pool();
generic_test_pool_guard_deletion(&mut pool_provider);
}
#[test]
fn test_pool_guard_with_release() {
let mut pool_provider = small_heapless_pool();
generic_test_pool_guard_with_release(&mut pool_provider);
}
#[test]
fn test_pool_modify_guard_man_creation() {
let mut pool_provider = small_heapless_pool();
generic_test_pool_modify_guard_man_creation(&mut pool_provider);
}
#[test]
fn test_pool_modify_guard() {
let mut pool_provider = small_heapless_pool();
generic_test_pool_modify_guard(&mut pool_provider);
}
#[test]
fn modify_pool_index_above_0() {
let mut pool_provider = small_heapless_pool();
generic_modify_pool_index_above_0(&mut pool_provider);
}
#[test]
fn test_spills_to_higher_subpools() {
let mut heapless_pool: StaticHeaplessMemoryPool<2> =
StaticHeaplessMemoryPool::new(true);
assert!(
heapless_pool
.grow(
SUBPOOL_2.take(),
SUBPOOL_2_SIZES.take(),
SUBPOOL_2_NUM_ELEMENTS,
true
)
.is_ok()
);
assert!(
heapless_pool
.grow(
SUBPOOL_4.take(),
SUBPOOL_4_SIZES.take(),
SUBPOOL_4_NUM_ELEMENTS,
true
)
@@ -1858,18 +1836,6 @@ mod tests {
fn test_spillage_fails_as_well() {
let mut heapless_pool: StaticHeaplessMemoryPool<2> =
StaticHeaplessMemoryPool::new(true);
static_subpool!(
SUBPOOL_5,
SUBPOOL_5_SIZES,
SUBPOOL_5_NUM_ELEMENTS as usize,
SUBPOOL_5_BLOCK_SIZE
);
static_subpool!(
SUBPOOL_3,
SUBPOOL_3_SIZES,
SUBPOOL_3_NUM_ELEMENTS as usize,
SUBPOOL_3_BLOCK_SIZE
);
assert!(
heapless_pool
.grow(
@@ -1897,24 +1863,6 @@ mod tests {
fn test_spillage_works_across_multiple_subpools() {
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
StaticHeaplessMemoryPool::new(true);
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
);
assert!(
heapless_pool
.grow(
@@ -1950,24 +1898,6 @@ mod tests {
#[test]
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> =
StaticHeaplessMemoryPool::new(true);
assert!(

View File

@@ -2,11 +2,8 @@ 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::PusTmSecondaryHeader;
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use spacepackets::ecss::{CreatorConfig, EcssEnumeration};
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
@@ -111,12 +108,8 @@ impl EventReportCreator {
src_data_len += aux_data.len();
}
source_buffer_large_enough(src_data_buf.len(), src_data_len)?;
let sec_header = PusTmSecondaryHeader::new(
MessageTypeId::new(5, subservice.into()),
0,
self.dest_id,
time_stamp,
);
let sec_header =
PusTmSecondaryHeader::new(5, subservice.into(), 0, self.dest_id, time_stamp);
let mut current_idx = 0;
event_id.write_to_be_bytes(&mut src_data_buf[0..event_id.size()])?;
current_idx += event_id.size();
@@ -169,14 +162,14 @@ mod alloc_mod {
default_apid: u11,
default_dest_id: u16,
max_event_id_and_aux_data_size: usize,
) -> Self {
) -> Option<Self> {
let reporter = EventReportCreator::new(default_apid, default_dest_id);
Self {
Some(Self {
id,
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
report_creator: reporter,
tm_hook: DummyEventHook::default(),
}
})
}
}
impl<EventTmHookInstance: EventTmHook> EventReporter<EventTmHookInstance> {
@@ -186,14 +179,14 @@ mod alloc_mod {
default_dest_id: u16,
max_event_id_and_aux_data_size: usize,
tm_hook: EventTmHookInstance,
) -> Self {
) -> Option<Self> {
let reporter = EventReportCreator::new(default_apid, default_dest_id);
Self {
Some(Self {
id,
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
report_creator: reporter,
tm_hook,
}
})
}
pub fn event_info(
@@ -270,7 +263,7 @@ mod alloc_mod {
mod tests {
use super::*;
use crate::ComponentId;
use crate::events_legacy::{EventU32, Severity};
use crate::events::{EventU32, Severity};
use crate::pus::test_util::TEST_COMPONENT_ID_0;
use crate::pus::tests::CommonTmInfo;
use crate::pus::{ChannelWithId, EcssTmSender, EcssTmtcError, PusTmVariant};
@@ -380,12 +373,14 @@ mod tests {
error_data: Option<&[u8]>,
) {
let mut sender = TestSender::default();
let mut reporter = EventReporter::new(
let reporter = EventReporter::new(
TEST_COMPONENT_ID_0.id(),
EXAMPLE_APID,
0,
max_event_aux_data_buf,
);
assert!(reporter.is_some());
let mut reporter = reporter.unwrap();
let time_stamp_empty: [u8; 7] = [0; 7];
let mut error_copy = Vec::new();
if let Some(err_data) = error_data {
@@ -476,7 +471,9 @@ mod tests {
fn insufficient_buffer() {
let mut sender = TestSender::default();
for i in 0..3 {
let mut reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
let 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);
}
}

View File

@@ -1,6 +1,6 @@
use crate::events_legacy::{EventU32, GenericEvent, Severity};
use crate::events::{EventU32, GenericEvent, Severity};
#[cfg(feature = "alloc")]
use crate::events_legacy::{EventU32TypedSev, HasSeverity};
use crate::events::{EventU32TypedSev, HasSeverity};
#[cfg(feature = "alloc")]
use core::hash::Hash;
#[cfg(feature = "alloc")]
@@ -100,7 +100,7 @@ pub mod alloc_mod {
use core::marker::PhantomData;
use crate::{
events_legacy::EventU16,
events::EventU16,
params::{Params, WritableToBeBytes},
pus::event::{DummyEventHook, EventTmHook},
};
@@ -311,28 +311,30 @@ pub mod alloc_mod {
mod tests {
use alloc::string::{String, ToString};
use alloc::vec;
use arbitrary_int::{u11, u21};
use arbitrary_int::u11;
use spacepackets::ecss::PusPacket;
use spacepackets::ecss::event::Subservice;
use spacepackets::ecss::tm::PusTmReader;
use super::*;
use crate::request::UniqueApidTargetId;
use crate::{events_legacy::SeverityInfo, tmtc::PacketAsVec};
use crate::{events::SeverityInfo, tmtc::PacketAsVec};
use std::sync::mpsc::{self, TryRecvError};
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
const EMPTY_STAMP: [u8; 7] = [0; 7];
const TEST_APID: u11 = u11::new(0x02);
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, u21::new(0x05));
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
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)
}
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();
PusEventTmCreatorWithMap::new(reporter, backend)
}
@@ -409,8 +411,8 @@ mod tests {
assert!(res.params_were_propagated);
let event_tm = event_rx.try_recv().expect("no event received");
let tm = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
assert_eq!(tm.service_type_id(), 5);
assert_eq!(tm.message_subtype_id(), Subservice::TmInfoReport as u8);
assert_eq!(tm.service(), 5);
assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
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());
assert_eq!(u32_event, INFO_EVENT.raw());
@@ -437,8 +439,8 @@ mod tests {
assert!(res.params_were_propagated);
let event_tm = event_rx.try_recv().expect("no event received");
let tm = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
assert_eq!(tm.service_type_id(), 5);
assert_eq!(tm.message_subtype_id(), Subservice::TmInfoReport as u8);
assert_eq!(tm.service(), 5);
assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
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());
assert_eq!(u32_event, INFO_EVENT.raw());

View File

@@ -1,4 +1,4 @@
use crate::events_legacy::EventU32;
use crate::events::EventU32;
use crate::pus::event_man::{EventRequest, EventRequestWithToken};
use crate::pus::verification::TcStateToken;
use crate::pus::{DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError};
@@ -60,11 +60,11 @@ impl<
.tc_in_mem_converter_mut()
.cache(&ecss_tc_and_token.tc_in_memory)?;
let tc = self.service_helper.tc_in_mem_converter().convert()?;
let subservice = tc.message_subtype_id();
let subservice = tc.subservice();
let srv = Subservice::try_from(subservice);
if srv.is_err() {
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
tc.message_subtype_id(),
tc.subservice(),
ecss_tc_and_token.token,
));
}
@@ -121,7 +121,7 @@ impl<
| Subservice::TmMediumSeverityReport
| Subservice::TmHighSeverityReport => {
return Err(PusPacketHandlingError::RequestConversion(
GenericConversionError::WrongService(tc.message_subtype_id()),
GenericConversionError::WrongService(tc.subservice()),
));
}
Subservice::TcEnableEventGeneration => {
@@ -147,8 +147,8 @@ mod tests {
use arbitrary_int::traits::Integer as _;
use arbitrary_int::u14;
use delegate::delegate;
use spacepackets::ecss::CreatorConfig;
use spacepackets::ecss::event::Subservice;
use spacepackets::ecss::{CreatorConfig, MessageTypeId};
use spacepackets::time::{TimeWriter, cds};
use spacepackets::util::UnsignedEnum;
use spacepackets::{
@@ -168,7 +168,7 @@ mod tests {
use crate::pus::{GenericConversionError, HandlingStatus, MpscTcReceiver};
use crate::tmtc::PacketSenderWithSharedPool;
use crate::{
events_legacy::EventU32,
events::EventU32,
pus::{
DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, PusPacketHandlingError,
event_man::EventRequestWithToken,
@@ -179,7 +179,7 @@ mod tests {
use super::PusEventServiceHandler;
const TEST_EVENT_0: EventU32 = EventU32::new(crate::events_legacy::Severity::Info, 5, 25);
const TEST_EVENT_0: EventU32 = EventU32::new(crate::events::Severity::Info, 5, 25);
struct Pus5HandlerWithStoreTester {
common: PusServiceHandlerWithSharedStoreCommon,
@@ -246,7 +246,7 @@ mod tests {
event_req_receiver: mpsc::Receiver<EventRequestWithToken>,
) {
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(5, subservice as u8));
let sec_header = PusTcSecondaryHeader::new_simple(5, subservice as u8);
let mut app_data = [0; 4];
TEST_EVENT_0
.write_to_be_bytes(&mut app_data)
@@ -311,7 +311,7 @@ mod tests {
let (event_request_tx, _) = mpsc::channel();
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(5, 200));
let sec_header = PusTcSecondaryHeader::new_simple(5, 200);
let ping_tc =
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
let token = test_harness.start_verification(&ping_tc);
@@ -331,10 +331,8 @@ mod tests {
let (event_request_tx, _) = mpsc::channel();
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
5,
Subservice::TcEnableEventGeneration as u8,
));
let sec_header =
PusTcSecondaryHeader::new_simple(5, Subservice::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);

View File

@@ -449,7 +449,7 @@ pub mod alloc_mod {
/// 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
/// are returned by the core conversion function. The active request type needs to fulfill
/// the [ActiveRequest] trait bound.
/// the [ActiveRequestProvider] trait bound.
///
/// The user should take care of performing the error handling as well. Some of the following
/// aspects might be relevant:
@@ -1126,7 +1126,7 @@ pub mod std_mod {
/// groups (for example individual services).
///
/// This base class can handle PUS telecommands backed by different memory storage machanisms
/// by using the [CacheAndReadRawEcssTc] abstraction. This object provides some convenience
/// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
/// methods to make the generic parts of TC handling easier.
pub struct PusServiceHelper<
TcReceiver: EcssTcReceiver,
@@ -1258,7 +1258,7 @@ pub(crate) fn source_buffer_large_enough(
#[cfg(any(feature = "test_util", test))]
pub mod test_util {
use arbitrary_int::{u11, u21};
use arbitrary_int::u11;
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
use crate::request::UniqueApidTargetId;
@@ -1269,8 +1269,8 @@ pub mod test_util {
};
pub const TEST_APID: u11 = u11::new(0x101);
pub const TEST_UNIQUE_ID_0: u21 = u21::new(0x05);
pub const TEST_UNIQUE_ID_1: u21 = u21::new(0x06);
pub const TEST_UNIQUE_ID_0: u32 = 0x05;
pub const TEST_UNIQUE_ID_1: u32 = 0x06;
pub const TEST_COMPONENT_ID_0: UniqueApidTargetId =
UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0);
@@ -1364,10 +1364,10 @@ pub mod tests {
let mut timestamp = [0; 7];
timestamp.clone_from_slice(&tm.timestamp()[0..7]);
Self {
subservice: PusPacket::message_subtype_id(tm),
subservice: PusPacket::subservice(tm),
apid: tm.apid(),
seq_count: tm.seq_count(),
msg_counter: tm.msg_type_counter(),
msg_counter: tm.msg_counter(),
dest_id: tm.dest_id(),
timestamp: timestamp.to_vec(),
}
@@ -1409,7 +1409,7 @@ pub mod tests {
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::sync_channel(10);
let (tm_tx, tm_rx) = mpsc::sync_channel(10);
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
let verification_handler =
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
let test_srv_tm_sender =
@@ -1478,8 +1478,8 @@ pub mod tests {
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 = PusTmReader::new(&tm_raw, 7).unwrap();
assert_eq!(PusPacket::service_type_id(&tm), 1);
assert_eq!(PusPacket::message_subtype_id(&tm), subservice);
assert_eq!(PusPacket::service(&tm), 1);
assert_eq!(PusPacket::subservice(&tm), subservice);
assert_eq!(tm.apid(), TEST_APID);
let req_id =
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
@@ -1502,7 +1502,7 @@ pub mod tests {
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
let (tm_tx, tm_rx) = mpsc::channel();
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
let verification_handler =
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
let in_store_converter = EcssTcVecCacher::default();
@@ -1597,8 +1597,8 @@ pub mod tests {
assert!(next_msg.is_ok());
let next_msg = next_msg.unwrap();
let tm = PusTmReader::new(next_msg.packet.as_slice(), 7).unwrap();
assert_eq!(PusPacket::service_type_id(&tm), 1);
assert_eq!(PusPacket::message_subtype_id(&tm), subservice);
assert_eq!(PusPacket::service(&tm), 1);
assert_eq!(PusPacket::subservice(&tm), subservice);
assert_eq!(tm.apid(), TEST_APID);
let req_id =
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
@@ -1615,9 +1615,9 @@ pub mod tests {
impl<const SERVICE: u8> TestConverter<SERVICE> {
pub fn check_service(&self, tc: &PusTcReader) -> Result<(), PusPacketHandlingError> {
if tc.service_type_id() != SERVICE {
if tc.service() != SERVICE {
return Err(PusPacketHandlingError::RequestConversion(
GenericConversionError::WrongService(tc.service_type_id()),
GenericConversionError::WrongService(tc.service()),
));
}
Ok(())

View File

@@ -37,7 +37,6 @@ mod tests {
use std::sync::mpsc;
use crate::{
ComponentId,
mode::{
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
ModeRequestorAndHandlerMpsc, ModeRequestorOneChildMpsc,
@@ -45,9 +44,9 @@ mod tests {
request::{GenericMessage, MessageMetadata},
};
const TEST_COMPONENT_ID_0: ComponentId = 5;
const TEST_COMPONENT_ID_1: ComponentId = 6;
const TEST_COMPONENT_ID_2: ComponentId = 7;
const TEST_COMPONENT_ID_0: u64 = 5;
const TEST_COMPONENT_ID_1: u64 = 6;
const TEST_COMPONENT_ID_2: u64 = 7;
#[test]
fn test_simple_mode_requestor() {

View File

@@ -25,7 +25,6 @@ pub use alloc_mod::*;
/// the source ID found in the secondary header of PUS telecommands.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RequestId {
pub(crate) source_id: u16,
pub(crate) apid: u11,
@@ -58,6 +57,7 @@ impl RequestId {
}
}
#[inline]
pub const fn as_u64(&self) -> u64 {
((self.source_id as u64) << 32)
| ((self.apid.value() as u64) << 16)
@@ -272,16 +272,14 @@ pub trait PusSchedulerProvider {
pus_tc: &(impl IsPusTelecommand + PusPacket + GenericPusTcSecondaryHeader),
pool: &mut (impl PoolProvider + ?Sized),
) -> Result<TcInfo, ScheduleError> {
if PusPacket::service_type_id(pus_tc) != 11 {
return Err(ScheduleError::WrongService(PusPacket::service_type_id(
if PusPacket::service(pus_tc) != 11 {
return Err(ScheduleError::WrongService(PusPacket::service(pus_tc)));
}
if PusPacket::subservice(pus_tc) != 4 {
return Err(ScheduleError::WrongSubservice(PusPacket::subservice(
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() {
return Err(ScheduleError::TcDataEmpty);
}
@@ -301,9 +299,7 @@ pub trait PusSchedulerProvider {
pool: &mut (impl PoolProvider + ?Sized),
) -> Result<TcInfo, ScheduleError> {
let check_tc = PusTcReader::new(tc)?;
if PusPacket::service_type_id(&check_tc) == 11
&& PusPacket::message_subtype_id(&check_tc) == 4
{
if PusPacket::service(&check_tc) == 11 && PusPacket::subservice(&check_tc) == 4 {
return Err(ScheduleError::NestedScheduledTc);
}
let req_id = RequestId::from_tc(&check_tc);
@@ -491,9 +487,7 @@ pub mod alloc_mod {
pool: &mut (impl PoolProvider + ?Sized),
) -> Result<TcInfo, ScheduleError> {
let check_tc = PusTcReader::new(tc)?;
if PusPacket::service_type_id(&check_tc) == 11
&& PusPacket::message_subtype_id(&check_tc) == 4
{
if PusPacket::service(&check_tc) == 11 && PusPacket::subservice(&check_tc) == 4 {
return Err(ScheduleError::NestedScheduledTc);
}
let req_id = RequestId::from_tc(&check_tc);
@@ -868,10 +862,9 @@ mod tests {
PoolAddr, PoolError, PoolProvider, StaticMemoryPool, StaticPoolAddr, StaticPoolConfig,
};
use alloc::collections::btree_map::Range;
use arbitrary_int::traits::Integer;
use arbitrary_int::{u11, u14};
use arbitrary_int::traits::Integer as _;
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
use spacepackets::ecss::{CreatorConfig, MessageTypeId, WritablePusPacket};
use spacepackets::ecss::{CreatorConfig, WritablePusPacket};
use spacepackets::time::{TimeWriter, UnixTime, cds};
use spacepackets::{PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader};
use std::time::Duration;
@@ -879,6 +872,8 @@ mod tests {
#[allow(unused_imports)]
use std::{println, vec};
const ZERO_SEQ: u14 = u14::ZERO;
fn pus_tc_base(timestamp: UnixTime, buf: &mut [u8]) -> (SpHeader, usize) {
let cds_time =
cds::CdsTime::from_unix_time_with_u16_days(&timestamp, cds::SubmillisPrecision::Absent)
@@ -895,32 +890,17 @@ mod tests {
fn scheduled_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(
sph,
MessageTypeId::new(11, 4),
&buf[..len_app_data],
CreatorConfig::default(),
)
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_app_data], CreatorConfig::default())
}
fn wrong_tc_service(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(
sph,
MessageTypeId::new(12, 4),
&buf[..len_app_data],
CreatorConfig::default(),
)
PusTcCreator::new_simple(sph, 12, 4, &buf[..len_app_data], CreatorConfig::default())
}
fn wrong_tc_subservice(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(
sph,
MessageTypeId::new(11, 5),
&buf[..len_app_data],
CreatorConfig::default(),
)
PusTcCreator::new_simple(sph, 11, 5, &buf[..len_app_data], CreatorConfig::default())
}
fn double_wrapped_time_tagged_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
@@ -931,18 +911,15 @@ mod tests {
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
// away
let inner_time_tagged_tc = PusTcCreator::new_simple(
sph,
MessageTypeId::new(11, 4),
&[],
CreatorConfig::default(),
);
let inner_time_tagged_tc =
PusTcCreator::new_simple(sph, 11, 4, &[], CreatorConfig::default());
let packet_len = inner_time_tagged_tc
.write_to_bytes(&mut buf[len_time_stamp..])
.expect("writing inner time tagged tc failed");
PusTcCreator::new_simple(
sph,
MessageTypeId::new(11, 4),
11,
4,
&buf[..len_time_stamp + packet_len],
CreatorConfig::default(),
)
@@ -950,22 +927,12 @@ mod tests {
fn invalid_time_tagged_cmd() -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 1);
PusTcCreator::new_simple(
sph,
MessageTypeId::new(11, 4),
&[],
CreatorConfig::default(),
)
PusTcCreator::new_simple(sph, 11, 4, &[], CreatorConfig::default())
}
fn base_ping_tc_simple_ctor(seq_count: u14, app_data: &'static [u8]) -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), seq_count, 0);
PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
app_data,
CreatorConfig::default(),
)
PusTcCreator::new_simple(sph, 17, 1, app_data, CreatorConfig::default())
}
fn ping_tc_to_store(
@@ -1050,7 +1017,7 @@ mod tests {
}),
RequestId {
seq_count: u14::new(1),
apid: u11::ZERO,
apid: u11::new(0),
source_id: 0,
},
),
@@ -1127,22 +1094,20 @@ mod tests {
#[test]
fn test_request_id() {
let src_id_to_set = 12;
let apid_to_set = u11::new(0x22);
let seq_count = u14::new(105);
let sp_header = SpHeader::new_for_unseg_tc(apid_to_set, u14::new(105), 0);
let mut sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let apid_to_set = 0x22;
let seq_count = 105;
let sp_header = SpHeader::new_for_unseg_tc(u11::new(apid_to_set), u14::new(105), 0);
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
sec_header.source_id = src_id_to_set;
let ping_tc =
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
let req_id = RequestId::from_tc(&ping_tc);
assert_eq!(req_id.source_id(), src_id_to_set);
assert_eq!(req_id.apid(), apid_to_set);
assert_eq!(req_id.seq_count(), seq_count);
assert_eq!(req_id.apid().value(), apid_to_set);
assert_eq!(req_id.seq_count().value(), seq_count);
assert_eq!(
req_id.as_u64(),
((src_id_to_set as u64) << 32)
| (apid_to_set.value() as u64) << 16
| seq_count.value() as u64
((src_id_to_set as u64) << 32) | (apid_to_set as u64) << 16 | seq_count as u64
);
}
#[test]
@@ -1154,7 +1119,7 @@ mod tests {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
@@ -1222,7 +1187,7 @@ mod tests {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
@@ -1284,7 +1249,7 @@ mod tests {
scheduler.disable();
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
@@ -1351,7 +1316,7 @@ mod tests {
false,
));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
let info = scheduler
.insert_unwrapped_tc(
@@ -1366,7 +1331,7 @@ mod tests {
let mut read_buf: [u8; 64] = [0; 64];
pool.read(&tc_info_0.addr(), &mut read_buf).unwrap();
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
@@ -1416,7 +1381,7 @@ mod tests {
let read_len = pool.read(&info.addr, &mut buf).unwrap();
let check_tc = PusTcReader::new(&buf).expect("incorrect Pus tc raw data");
assert_eq!(read_len, check_tc.packet_len());
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
@@ -1584,7 +1549,7 @@ mod tests {
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
@@ -1621,7 +1586,7 @@ mod tests {
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
@@ -1647,7 +1612,7 @@ mod tests {
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("inserting tc failed");
@@ -1668,7 +1633,7 @@ mod tests {
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("inserting tc failed");
@@ -1689,7 +1654,7 @@ mod tests {
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("inserting tc failed");
@@ -1775,8 +1740,10 @@ mod tests {
false,
));
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, u14::ZERO, 50);
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
let tc_info_0 =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 50);
let tc_info_1 =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 100);
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
let check_range = |range: Range<UnixTime, Vec<TcInfo>>| {
let mut tcs_in_range = 0;
@@ -1807,9 +1774,11 @@ mod tests {
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
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, u14::ZERO, 100);
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, u14::new(0), 50);
let tc_info_1 =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 100);
let tc_info_2 =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 150);
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
&UnixTime::new_only_secs(100),
cds::SubmillisPrecision::Absent,
@@ -1842,9 +1811,11 @@ mod tests {
false,
));
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, u14::ZERO, 50);
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, u14::ZERO, 150);
let tc_info_0 =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 50);
let tc_info_1 =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 100);
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 150);
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
@@ -1877,10 +1848,10 @@ mod tests {
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
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, u14::ZERO, 100);
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, u14::ZERO, 200);
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 200);
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
@@ -1918,8 +1889,8 @@ mod tests {
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
let del_res = scheduler.delete_all(&mut pool);
assert!(del_res.is_ok());
@@ -1928,8 +1899,8 @@ mod tests {
// Contrary to reset, this does not disable the scheduler.
assert!(scheduler.is_enabled());
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
let del_res = scheduler
.delete_by_time_filter(TimeWindow::<cds::CdsTime>::new_select_all(), &mut pool);
@@ -1947,11 +1918,11 @@ mod tests {
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
let cmd_0_to_delete =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
let cmd_1_to_delete =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
&UnixTime::new_only_secs(100),
@@ -1975,10 +1946,10 @@ mod tests {
));
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, u14::ZERO, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
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);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
@@ -2003,13 +1974,13 @@ mod tests {
));
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, u14::ZERO, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
let cmd_0_to_delete =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
let cmd_1_to_delete =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
let cmd_out_of_range_1 =
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 200);
insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 200);
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
@@ -2042,7 +2013,7 @@ mod tests {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
@@ -2081,7 +2052,7 @@ mod tests {
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
0,
);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
let mut buf: [u8; 64] = [0; 64];
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
@@ -2103,7 +2074,7 @@ mod tests {
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
0,
);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
let mut buf: [u8; 16] = [0; 16];
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
@@ -2132,7 +2103,7 @@ mod tests {
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
0,
);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
let mut buf: [u8; 64] = [0; 64];
generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap();

View File

@@ -79,7 +79,7 @@ impl<
.tc_in_mem_converter_mut()
.cache(&ecss_tc_and_token.tc_in_memory)?;
let tc = self.service_helper.tc_in_mem_converter().convert()?;
let subservice = PusPacket::message_subtype_id(&tc);
let subservice = PusPacket::subservice(&tc);
let standard_subservice = scheduling::Subservice::try_from(subservice);
if standard_subservice.is_err() {
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
@@ -260,13 +260,13 @@ mod tests {
};
use crate::tmtc::PacketSenderWithSharedPool;
use alloc::collections::VecDeque;
use arbitrary_int::traits::Integer as _;
use arbitrary_int::traits::Integer;
use arbitrary_int::u14;
use delegate::delegate;
use spacepackets::SpHeader;
use spacepackets::ecss::scheduling::Subservice;
use spacepackets::ecss::tc::PusTcSecondaryHeader;
use spacepackets::ecss::{CreatorConfig, MessageTypeId, WritablePusPacket};
use spacepackets::ecss::{CreatorConfig, WritablePusPacket};
use spacepackets::time::TimeWriter;
use spacepackets::{
ecss::{tc::PusTcCreator, tm::PusTmReader},
@@ -389,7 +389,7 @@ mod tests {
subservice: Subservice,
) {
let reply_header = SpHeader::new_for_unseg_tm(TEST_APID, u14::ZERO, 0);
let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(11, subservice as u8));
let tc_header = PusTcSecondaryHeader::new_simple(11, subservice as u8);
let enable_scheduling =
PusTcCreator::new(reply_header, tc_header, &[0; 7], CreatorConfig::default());
let token = test_harness.start_verification(&enable_scheduling);
@@ -437,7 +437,7 @@ mod tests {
fn test_insert_activity_tc() {
let mut test_harness = Pus11HandlerWithStoreTester::new();
let mut reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let mut sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new(reply_header, sec_header, &[], CreatorConfig::default());
let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
let stamper = cds::CdsTime::now_with_u16_days().expect("time provider failed");
@@ -446,11 +446,8 @@ mod tests {
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);
written_len += ping_raw.len();
reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
11,
Subservice::TcInsertActivity as u8,
));
reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(1), 0);
sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
let enable_scheduling = PusTcCreator::new(
reply_header,
sec_header,

View File

@@ -2,11 +2,10 @@ use crate::pus::{
DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError, PusTmVariant,
};
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use arbitrary_int::traits::Integer as _;
use arbitrary_int::u14;
use spacepackets::SpHeader;
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use spacepackets::ecss::{CreatorConfig, MessageTypeId, PusPacket};
use spacepackets::ecss::{CreatorConfig, PusPacket};
use std::sync::mpsc;
use super::verification::{VerificationReporter, VerificationReportingProvider};
@@ -59,10 +58,10 @@ impl<
.tc_in_mem_converter_mut()
.cache(&ecss_tc_and_token.tc_in_memory)?;
let tc = self.service_helper.tc_in_mem_converter().convert()?;
if tc.service_type_id() != 17 {
return Err(GenericConversionError::WrongService(tc.service_type_id()).into());
if tc.service() != 17 {
return Err(GenericConversionError::WrongService(tc.service()).into());
}
if tc.message_subtype_id() == 1 {
if tc.subservice() == 1 {
let opt_started_token = match self.service_helper.verif_reporter().start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
@@ -79,10 +78,10 @@ impl<
// the unchecked API here.
let reply_header = SpHeader::new_for_unseg_tm(
self.service_helper.verif_reporter().apid(),
u14::ZERO,
u14::new(0),
0,
);
let tc_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), time_stamp);
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp);
let ping_reply =
PusTmCreator::new(reply_header, tc_header, &[], CreatorConfig::default());
if let Err(e) = self
@@ -104,7 +103,7 @@ impl<
}
} else {
return Ok(DirectPusPacketHandlerResult::CustomSubservice(
tc.message_subtype_id(),
tc.subservice(),
ecss_tc_and_token.token,
));
}
@@ -154,13 +153,12 @@ mod tests {
PartialPusHandlingError, PusPacketHandlingError,
};
use crate::tmtc::PacketSenderWithSharedPool;
use arbitrary_int::traits::Integer as _;
use arbitrary_int::u14;
use delegate::delegate;
use spacepackets::SpHeader;
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::{CreatorConfig, MessageTypeId, PusPacket};
use spacepackets::ecss::{CreatorConfig, PusPacket};
use spacepackets::time::{TimeWriter, cds};
use super::PusService17TestHandler;
@@ -291,8 +289,8 @@ mod tests {
fn ping_test(test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler)) {
// Create a ping TC, verify acceptance.
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc =
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
let token = test_harness.start_verification(&ping_tc);
@@ -311,8 +309,8 @@ mod tests {
// Ping reply
let tm = test_harness.read_next_tm();
assert_eq!(tm.service_type_id(), 17);
assert_eq!(tm.message_subtype_id(), 2);
assert_eq!(tm.service(), 17);
assert_eq!(tm.subservice(), 2);
assert!(tm.user_data().is_empty());
// TM completion
@@ -347,8 +345,8 @@ mod tests {
#[test]
fn test_sending_unsupported_service() {
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(3, 1));
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
let sec_header = PusTcSecondaryHeader::new_simple(3, 1);
let ping_tc =
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
let token = test_harness.start_verification(&ping_tc);
@@ -369,8 +367,8 @@ mod tests {
#[test]
fn test_sending_custom_subservice() {
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 200));
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
let sec_header = PusTcSecondaryHeader::new_simple(17, 200);
let ping_tc =
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
let token = test_harness.start_verification(&ping_tc);

View File

@@ -20,16 +20,16 @@
//! VerificationReportingProvider, VerificationReporterConfig, VerificationReporter
//! };
//! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool};
//! use satrs::spacepackets::seq_count::SeqCountProviderSimple;
//! use satrs::request::UniqueApidTargetId;
//! use spacepackets::ecss::PusPacket;
//! use spacepackets::SpHeader;
//! use spacepackets::ecss::tc::{MessageTypeId, PusTcCreator, PusTcSecondaryHeader, CreatorConfig};
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
//! use spacepackets::ecss::tm::PusTmReader;
//! use arbitrary_int::{u11, u21};
//!
//! const EMPTY_STAMP: [u8; 7] = [0; 7];
//! const TEST_APID: u11 = u11::new(0x02);
//! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, u21::new(0x05));
//! const TEST_APID: u16 = 0x02;
//! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
//!
//! let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
//! 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 (verif_tx, verif_rx) = mpsc::sync_channel(10);
//! let sender = PacketSenderWithSharedPool::new_with_shared_packet_pool(verif_tx, &shared_tm_pool);
//! let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
//! let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
//! let mut reporter = VerificationReporter::new(TEST_COMPONENT_ID.id(), &cfg);
//!
//! let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
//! let pus_tc_0 = PusTcCreator::new_no_app_data(
//! SpHeader::new_from_apid(TEST_APID),
//! tc_header,
//! CreatorConfig::default()
//! true
//! );
//! 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");
//! if packet_idx == 0 {
//! assert_eq!(pus_tm.message_subtype_id(), 1);
//! assert_eq!(pus_tm.subservice(), 1);
//! } else if packet_idx == 1 {
//! assert_eq!(pus_tm.message_subtype_id(), 3);
//! assert_eq!(pus_tm.subservice(), 3);
//! } else if packet_idx == 2 {
//! assert_eq!(pus_tm.message_subtype_id(), 7);
//! assert_eq!(pus_tm.subservice(), 7);
//! }
//! packet_idx += 1;
//! }
@@ -82,6 +82,7 @@
//! context involving multiple threads
use crate::params::{Params, WritableToBeBytes};
use crate::pus::{EcssTmSender, EcssTmtcError, source_buffer_large_enough};
use arbitrary_int::traits::Integer as _;
use arbitrary_int::{u3, u11, u14};
use core::fmt::{Debug, Display, Formatter};
use core::hash::{Hash, Hasher};
@@ -91,14 +92,13 @@ use core::mem::size_of;
use delegate::delegate;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use spacepackets::SpHeader;
use spacepackets::ecss::tc::IsPusTelecommand;
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use spacepackets::ecss::{CreatorConfig, EcssEnumeration, MessageTypeId};
use spacepackets::ecss::{CreatorConfig, EcssEnumeration};
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl};
use spacepackets::{MAX_APID, SpHeader};
pub use spacepackets::ecss::verification::*;
pub use spacepackets::seq_count::SequenceCounterSimple;
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
@@ -158,18 +158,16 @@ impl RequestId {
}
pub fn raw(&self) -> u32 {
((self.version_number.value() as u32) << 29)
((self.version_number.as_u32()) << 29)
| ((self.packet_id.raw() as u32) << 16)
| self.psc.raw() as u32
}
#[inline]
pub const fn packet_id(&self) -> PacketId {
pub fn packet_id(&self) -> PacketId {
self.packet_id
}
#[inline]
pub const fn packet_seq_ctrl(&self) -> PacketSequenceControl {
pub fn packet_sequence_control(&self) -> PacketSequenceControl {
self.psc
}
@@ -507,7 +505,9 @@ impl VerificationReportCreator {
self.dest_id = dest_id;
}
pub fn read_request_id(&self, pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> RequestId {
/// Initialize verification handling by passing a TC reference. This returns a token required
/// to call the acceptance functions
pub fn read_request_id_from_tc(pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> RequestId {
RequestId::new(pus_tc)
}
@@ -820,12 +820,8 @@ impl VerificationReportCreator {
time_stamp: &'time [u8],
source_data_len: usize,
) -> PusTmCreator<'time, 'src_data> {
let tm_sec_header = PusTmSecondaryHeader::new(
MessageTypeId::new(1, subservice),
msg_counter,
self.dest_id,
time_stamp,
);
let tm_sec_header =
PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, time_stamp);
PusTmCreator::new(
sp_header,
tm_sec_header,
@@ -837,7 +833,7 @@ impl VerificationReportCreator {
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use arbitrary_int::traits::Integer as _;
use arbitrary_int::u11;
use spacepackets::ecss::PusError;
use super::*;
@@ -858,13 +854,16 @@ pub mod alloc_mod {
step_field_width: usize,
fail_code_field_width: usize,
max_fail_data_len: usize,
) -> Self {
Self {
) -> Option<Self> {
if apid > MAX_APID {
return None;
}
Some(Self {
apid,
step_field_width,
fail_code_field_width,
max_fail_data_len,
}
})
}
}
@@ -876,7 +875,7 @@ pub mod alloc_mod {
fn modify_tm(&self, tm: &mut PusTmCreator);
}
/// [VerificationHook] which does nothing. This is the default hook variant for
/// [VerificationHookProvider] which does nothing. This is the default hook variant for
/// the [VerificationReporter], assuming that any necessary packet manipulation is performed by
/// a centralized TM funnel or inlet.
#[derive(Default, Copy, Clone)]
@@ -899,7 +898,7 @@ pub mod alloc_mod {
> {
owner_id: ComponentId,
source_data_buf: RefCell<alloc::vec::Vec<u8>>,
pub report_creator: VerificationReportCreator,
pub reporter_creator: VerificationReportCreator,
pub tm_hook: VerificationHookInstance,
}
@@ -915,14 +914,14 @@ pub mod alloc_mod {
+ cfg.fail_code_field_width
+ cfg.max_fail_data_len
]),
report_creator: reporter,
reporter_creator: reporter,
tm_hook: DummyVerificationHook::default(),
}
}
}
impl<VerificationHookInstance: VerificationHook> VerificationReporter<VerificationHookInstance> {
/// The provided [VerificationHook] can be used to modify a verification packet
/// The provided [VerificationHookProvider] can be used to modify a verification packet
/// before it is sent.
pub fn new_with_hook(
owner_id: ComponentId,
@@ -939,7 +938,7 @@ pub mod alloc_mod {
+ cfg.fail_code_field_width
+ cfg.max_fail_data_len
]),
report_creator: reporter,
reporter_creator: reporter,
tm_hook,
}
}
@@ -948,7 +947,9 @@ pub mod alloc_mod {
&self,
pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
) -> VerificationToken<TcStateNone> {
VerificationToken::<TcStateNone>::new(self.report_creator.read_request_id(pus_tc))
VerificationToken::<TcStateNone>::new(
VerificationReportCreator::read_request_id_from_tc(pus_tc),
)
}
pub fn start_verification_with_req_id(
@@ -959,7 +960,7 @@ pub mod alloc_mod {
}
delegate!(
to self.report_creator {
to self.reporter_creator {
pub fn set_apid(&mut self, apid: u11);
pub fn apid(&self) -> u11;
pub fn dest_id(&self) -> u16;
@@ -976,7 +977,7 @@ pub mod alloc_mod {
for VerificationReporter<VerificationHookInstance>
{
delegate!(
to self.report_creator {
to self.reporter_creator {
fn set_apid(&mut self, apid: Apid);
fn apid(&self) -> Apid;
}
@@ -1002,11 +1003,11 @@ pub mod alloc_mod {
) -> Result<VerificationToken<TcStateAccepted>, EcssTmtcError> {
let mut source_data_buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.reporter_creator
.acceptance_success(
source_data_buf.as_mut_slice(),
&token.request_id(),
u14::ZERO,
u14::new(0),
0,
time_stamp,
)
@@ -1025,11 +1026,11 @@ pub mod alloc_mod {
) -> Result<(), EcssTmtcError> {
let mut buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.reporter_creator
.acceptance_failure(
buf.as_mut_slice(),
&token.request_id(),
u14::ZERO,
u14::new(0),
0,
params,
)
@@ -1050,11 +1051,11 @@ pub mod alloc_mod {
) -> Result<VerificationToken<TcStateStarted>, EcssTmtcError> {
let mut buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.reporter_creator
.start_success(
buf.as_mut_slice(),
&token.request_id(),
u14::ZERO,
u14::new(0),
0,
time_stamp,
)
@@ -1076,11 +1077,11 @@ pub mod alloc_mod {
) -> Result<(), EcssTmtcError> {
let mut buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.reporter_creator
.start_failure(
buf.as_mut_slice(),
&token.request_id(),
u14::ZERO,
u14::new(0),
0,
params,
)
@@ -1102,11 +1103,11 @@ pub mod alloc_mod {
) -> Result<(), EcssTmtcError> {
let mut buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.reporter_creator
.step_success(
buf.as_mut_slice(),
&token.request_id(),
u14::ZERO,
u14::new(0),
0,
time_stamp,
step,
@@ -1129,8 +1130,8 @@ pub mod alloc_mod {
) -> Result<(), EcssTmtcError> {
let mut buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.step_failure(buf.as_mut_slice(), token, u14::ZERO, 0, params)
.reporter_creator
.step_failure(buf.as_mut_slice(), token, u14::new(0), 0, params)
.map_err(PusError::ByteConversion)?;
self.tm_hook.modify_tm(&mut tm_creator);
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
@@ -1150,11 +1151,11 @@ pub mod alloc_mod {
) -> Result<(), EcssTmtcError> {
let mut buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.reporter_creator
.completion_success(
buf.as_mut_slice(),
&token.request_id(),
u14::ZERO,
u14::new(0),
0,
time_stamp,
)
@@ -1176,11 +1177,11 @@ pub mod alloc_mod {
) -> Result<(), EcssTmtcError> {
let mut buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.reporter_creator
.completion_failure(
buf.as_mut_slice(),
&token.request_id(),
u14::ZERO,
u14::new(0),
0,
params,
)
@@ -1333,7 +1334,7 @@ pub fn handle_step_failure_with_generic_params(
#[cfg(any(feature = "test_util", test))]
pub mod test_util {
use alloc::vec::Vec;
use arbitrary_int::traits::Integer;
use arbitrary_int::u11;
use core::cell::RefCell;
use std::collections::VecDeque;
@@ -1395,7 +1396,7 @@ pub mod test_util {
fn set_apid(&mut self, _apid: Apid) {}
fn apid(&self) -> Apid {
Apid::ZERO
u11::new(0)
}
fn acceptance_success(
@@ -1427,7 +1428,7 @@ pub mod test_util {
token.request_id(),
VerificationReportInfo::AcceptanceFailure(FailureData {
sender: self.owner_id(),
error_enum: params.failure_code.value_raw(),
error_enum: params.failure_code.value(),
fail_data: params.failure_data.to_vec(),
time_stamp: params.time_stamp.to_vec(),
}),
@@ -1464,7 +1465,7 @@ pub mod test_util {
token.request_id(),
VerificationReportInfo::StartedFailure(FailureData {
sender: self.owner_id(),
error_enum: params.failure_code.value_raw(),
error_enum: params.failure_code.value(),
fail_data: params.failure_data.to_vec(),
time_stamp: params.time_stamp.to_vec(),
}),
@@ -1486,7 +1487,7 @@ pub mod test_util {
sender: self.owner_id(),
time_stamp: time_stamp.to_vec(),
},
step: step.value_raw() as u16,
step: step.value() as u16,
},
));
Ok(())
@@ -1502,7 +1503,7 @@ pub mod test_util {
token.request_id(),
VerificationReportInfo::StepFailure(FailureData {
sender: self.owner_id(),
error_enum: params.common.failure_code.value_raw(),
error_enum: params.common.failure_code.value(),
fail_data: params.common.failure_data.to_vec(),
time_stamp: params.common.time_stamp.to_vec(),
}),
@@ -1536,7 +1537,7 @@ pub mod test_util {
token.request_id(),
VerificationReportInfo::CompletionFailure(FailureData {
sender: self.owner_id(),
error_enum: params.failure_code.value_raw(),
error_enum: params.failure_code.value(),
fail_data: params.failure_data.to_vec(),
time_stamp: params.time_stamp.to_vec(),
}),
@@ -1727,13 +1728,13 @@ pub mod tests {
use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
use alloc::format;
use alloc::string::ToString;
use arbitrary_int::traits::Integer;
use arbitrary_int::{u11, u14};
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
use spacepackets::ecss::{
CreatorConfig, EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, MessageTypeId,
PusError, PusPacket, WritablePusPacket,
CreatorConfig, EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, PusError, PusPacket,
WritablePusPacket,
};
use spacepackets::seq_count::SequenceCounterSimple;
use spacepackets::util::UnsignedEnum;
use spacepackets::{ByteConversionError, SpHeader};
use std::cell::RefCell;
@@ -1743,8 +1744,8 @@ pub mod tests {
use std::vec::Vec;
use super::{
DummyVerificationHook, FailParamHelper, SequenceCounterSimple, TcStateAccepted,
TcStateStarted, VerificationHook, VerificationReportingProvider, WasAtLeastAccepted,
DummyVerificationHook, FailParamHelper, TcStateAccepted, TcStateStarted, VerificationHook,
VerificationReportingProvider, WasAtLeastAccepted,
handle_completion_failure_with_generic_params,
};
@@ -1783,7 +1784,7 @@ pub mod tests {
panic!("TestSender: Can not deal with addresses");
}
PusTmVariant::Direct(tm) => {
assert_eq!(PusPacket::service_type_id(&tm), 1);
assert_eq!(PusPacket::service(&tm), 1);
assert!(!tm.source_data().is_empty());
let mut time_stamp = [0; 7];
time_stamp.clone_from_slice(&tm.timestamp()[0..7]);
@@ -1832,7 +1833,7 @@ pub mod tests {
}
fn base_reporter(id: ComponentId, max_fail_data_len: usize) -> VerificationReporter {
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, max_fail_data_len);
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, max_fail_data_len).unwrap();
VerificationReporter::new(id, &cfg)
}
@@ -1840,7 +1841,7 @@ pub mod tests {
id: ComponentId,
hook: VerificationHookInstance,
) -> VerificationReporter<VerificationHookInstance> {
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
VerificationReporter::new_with_hook(id, &cfg, hook)
}
@@ -1942,7 +1943,7 @@ pub mod tests {
common: CommonTmInfo::new(
1,
TEST_APID,
u14::ZERO,
u14::new(0),
0,
self.reporter.dest_id(),
timestamp,
@@ -2009,7 +2010,7 @@ pub mod tests {
common: CommonTmInfo::new(
2,
TEST_APID,
u14::ZERO,
u14::new(0),
0,
self.reporter.dest_id(),
timestamp,
@@ -2102,7 +2103,7 @@ pub mod tests {
fn create_generic_ping() -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0x34), 0);
let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
PusTcCreator::new(sph, tc_header, &[], CreatorConfig::default())
}
@@ -2274,7 +2275,7 @@ pub mod tests {
.expect("step 1 failed");
assert_eq!(testbench.sender.service_queue.borrow().len(), 4);
testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
testbench.check_start_success(u14::new(0), 0, &EMPTY_STAMP);
testbench.check_step_success(0, &EMPTY_STAMP);
testbench.check_step_success(1, &EMPTY_STAMP);
}
@@ -2308,7 +2309,7 @@ pub mod tests {
.step_failure(started_token, fail_params)
.expect("Step failure failed");
testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
testbench.check_start_success(u14::new(0), 0, DUMMY_STAMP);
testbench.check_step_success(0, &EMPTY_STAMP);
testbench.check_step_failure(&fail_step, &fail_code, &fail_data_raw);
}
@@ -2330,7 +2331,7 @@ pub mod tests {
.completion_failure(started_token, fail_params)
.expect("Completion failure");
testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
testbench.check_start_success(u14::new(0), 0, DUMMY_STAMP);
testbench.check_completion_failure(&fail_code, &[]);
}
@@ -2350,8 +2351,8 @@ pub mod tests {
.completion_success(started_token, &EMPTY_STAMP)
.expect("Sending completion success failed");
testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
testbench.check_completion_success(u14::ZERO, 0);
testbench.check_start_success(u14::new(0), 0, DUMMY_STAMP);
testbench.check_completion_success(u14::new(0), 0);
}
#[test]
@@ -2432,7 +2433,7 @@ pub mod tests {
);
assert!(result.unwrap());
testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
testbench.check_start_success(u14::new(0), 0, &EMPTY_STAMP);
testbench.check_step_failure(&step, &fail_code, fail_data.as_bytes());
}

View File

@@ -1,4 +1,4 @@
use arbitrary_int::{prelude::*, u11, u21};
use arbitrary_int::{traits::Integer as _, u11};
use core::{fmt, marker::PhantomData};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@@ -23,17 +23,17 @@ use crate::{
/// for them. This can be useful for tasks like tracking their progress.
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 11 bit value.
pub type Apid = u11;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct UniqueApidTargetId {
pub apid: Apid,
pub unique_id: u21,
pub unique_id: u32,
}
impl UniqueApidTargetId {
pub const fn new(apid: Apid, target: u21) -> Self {
pub const fn new(apid: Apid, target: u32) -> Self {
Self {
apid,
unique_id: target,
@@ -41,7 +41,7 @@ impl UniqueApidTargetId {
}
pub fn raw(&self) -> ComponentId {
((self.apid.value() as u32) << 21) | (self.unique_id.value())
((self.apid.as_u64()) << 32) | (self.unique_id as u64)
}
pub fn id(&self) -> ComponentId {
@@ -61,21 +61,21 @@ impl UniqueApidTargetId {
}
Ok(Self::new(
tc.apid(),
u21::new(u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap())),
u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
))
}
}
impl From<ComponentId> for UniqueApidTargetId {
fn from(raw: ComponentId) -> Self {
impl From<u64> for UniqueApidTargetId {
fn from(raw: u64) -> Self {
Self {
apid: u11::new((raw >> 21) as u16),
unique_id: u21::new(raw & u21::MAX.value()),
apid: u11::new((raw >> 32) as u16),
unique_id: raw as u32,
}
}
}
impl From<UniqueApidTargetId> for ComponentId {
impl From<UniqueApidTargetId> for u64 {
fn from(target_and_apid_id: UniqueApidTargetId) -> Self {
target_and_apid_id.raw()
}
@@ -496,39 +496,37 @@ pub mod std_mod {
mod tests {
use std::sync::mpsc;
use crate::{
queue::{GenericReceiveError, GenericSendError},
request::{MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
};
use alloc::string::ToString;
use arbitrary_int::{u11, u14, u21};
use arbitrary_int::{u11, u14};
use spacepackets::{
ByteConversionError, SpHeader,
ecss::{
CreatorConfig, MessageTypeId,
CreatorConfig,
tc::{PusTcCreator, PusTcSecondaryHeader},
},
};
use crate::{
ComponentId,
queue::{GenericReceiveError, GenericSendError},
request::{Apid, MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
};
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
const TEST_CHANNEL_ID_0: ComponentId = 1;
const TEST_CHANNEL_ID_1: ComponentId = 2;
const TEST_CHANNEL_ID_2: ComponentId = 3;
const TEST_CHANNEL_ID_0: u64 = 1;
const TEST_CHANNEL_ID_1: u64 = 2;
const TEST_CHANNEL_ID_2: u64 = 3;
#[test]
fn test_basic_target_id_with_apid() {
let id = UniqueApidTargetId::new(Apid::new(0x111), u21::new(0x01));
let id = UniqueApidTargetId::new(u11::new(0x111), 0x01);
assert_eq!(id.apid.value(), 0x111);
assert_eq!(id.unique_id.value(), 0x01);
assert_eq!(id.unique_id, 0x01);
assert_eq!(id.id(), id.raw());
assert_eq!(ComponentId::from(id), id.raw());
assert_eq!(u64::from(id), id.raw());
let id_raw = id.raw();
let id_from_raw = UniqueApidTargetId::from(id_raw);
assert_eq!(id_from_raw, id);
assert_eq!(id.id(), (0x111 << 21) | 0x01);
assert_eq!(id.id(), (0x111 << 32) | 0x01);
let string = id.to_string();
assert_eq!(
string,
@@ -540,21 +538,17 @@ mod tests {
fn test_basic_target_id_with_apid_from_pus_tc() {
let sp_header = SpHeader::new_for_unseg_tc(u11::new(0x111), u14::new(5), 0);
let app_data = 1_u32.to_be_bytes();
let pus_tc = PusTcCreator::new_simple(
sp_header,
MessageTypeId::new(17, 1),
&app_data,
CreatorConfig::default(),
);
let pus_tc =
PusTcCreator::new_simple(sp_header, 17, 1, &app_data, CreatorConfig::default());
let id = UniqueApidTargetId::from_pus_tc(&pus_tc).unwrap();
assert_eq!(id.apid.value(), 0x111);
assert_eq!(id.unique_id.value(), 1);
assert_eq!(id.unique_id, 1);
}
#[test]
fn test_basic_target_id_with_apid_from_pus_tc_invalid_app_data() {
let sp_header = SpHeader::new_for_unseg_tc(u11::new(0x111), u14::new(5), 0);
let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let pus_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
let error = UniqueApidTargetId::from_pus_tc(&pus_tc);
assert!(error.is_err());

View File

@@ -782,7 +782,7 @@ mod tests {
);
assert_eq!(self.sender.requests.borrow().len(), 2);
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as ComponentId);
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as u64);
assert_eq!(req_0.request_id, expected_req_id);
assert_eq!(
req_0.request,
@@ -792,7 +792,7 @@ mod tests {
}
);
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as ComponentId);
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as u64);
assert_eq!(
req_1.request,
ModeRequest::SetMode {
@@ -808,7 +808,7 @@ mod tests {
);
assert_eq!(self.sender.requests.borrow().len(), 1);
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
assert_eq!(req_0.target_id, ExampleTargetId::Target2 as ComponentId);
assert_eq!(req_0.target_id, ExampleTargetId::Target2 as u64);
assert_eq!(req_0.request_id, expected_req_id);
assert_eq!(
req_0.request,
@@ -827,7 +827,7 @@ mod tests {
assert_eq!(self.execution_helper.current_sequence_index().unwrap(), 0);
assert_eq!(self.sender.requests.borrow().len(), 2);
let req_0 = self.sender.requests.get_mut().pop_front().unwrap();
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as ComponentId);
assert_eq!(req_0.target_id, ExampleTargetId::Target0 as u64);
assert_eq!(req_0.request_id, expected_req_id);
assert_eq!(
req_0.request,
@@ -837,7 +837,7 @@ mod tests {
}
);
let req_1 = self.sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as ComponentId);
assert_eq!(req_1.target_id, ExampleTargetId::Target1 as u64);
assert_eq!(
req_1.request,
ModeRequest::SetMode {
@@ -850,9 +850,9 @@ mod tests {
fn create_default_mode_store() -> ModeStoreVec {
let mut mode_store = ModeStoreVec::default();
mode_store.add_component(ExampleTargetId::Target0 as ComponentId, UNKNOWN_MODE);
mode_store.add_component(ExampleTargetId::Target1 as ComponentId, UNKNOWN_MODE);
mode_store.add_component(ExampleTargetId::Target2 as ComponentId, UNKNOWN_MODE);
mode_store.add_component(ExampleTargetId::Target0 as u64, UNKNOWN_MODE);
mode_store.add_component(ExampleTargetId::Target1 as u64, UNKNOWN_MODE);
mode_store.add_component(ExampleTargetId::Target2 as u64, UNKNOWN_MODE);
mode_store
}
@@ -863,13 +863,13 @@ mod tests {
let mut table_seq_0 = SequenceTableMapTable::new("MODE_0_SEQ_0");
table_seq_0.add_entry(SequenceTableEntry::new(
"TARGET_0",
ExampleTargetId::Target0 as ComponentId,
ExampleTargetId::Target0 as u64,
SUBSYSTEM_MD0_TGT0_MODE,
false,
));
table_seq_0.add_entry(SequenceTableEntry::new(
"TARGET_1",
ExampleTargetId::Target1 as ComponentId,
ExampleTargetId::Target1 as u64,
SUBSYSTEM_MD0_TGT1_MODE,
false,
));
@@ -881,13 +881,13 @@ mod tests {
let mut table_seq_0 = SequenceTableMapTable::new("MODE_1_SEQ_0");
table_seq_0.add_entry(SequenceTableEntry::new(
"MD1_SEQ0_TGT0",
ExampleTargetId::Target0 as ComponentId,
ExampleTargetId::Target0 as u64,
SUBSYSTEM_MD1_ST0_TGT0_MODE,
false,
));
table_seq_0.add_entry(SequenceTableEntry::new(
"MD1_SEQ0_TGT1",
ExampleTargetId::Target1 as ComponentId,
ExampleTargetId::Target1 as u64,
SUBSYSTEM_MD1_ST0_TGT1_MODE,
false,
));
@@ -895,7 +895,7 @@ mod tests {
let mut table_seq_1 = SequenceTableMapTable::new("MODE_1_SEQ_1");
table_seq_1.add_entry(SequenceTableEntry::new(
"MD1_SEQ1_TGT2",
ExampleTargetId::Target2 as ComponentId,
ExampleTargetId::Target2 as u64,
SUBSYSTEM_MD1_ST1_TGT2_MODE,
false,
));
@@ -1263,11 +1263,11 @@ mod tests {
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
};
let req0 = tb.sender.requests.borrow_mut().pop_front().unwrap();
check_req(req0, ExampleTargetId::Target0 as ComponentId);
check_req(req0, ExampleTargetId::Target0 as u64);
let req1 = tb.sender.requests.borrow_mut().pop_front().unwrap();
check_req(req1, ExampleTargetId::Target1 as ComponentId);
check_req(req1, ExampleTargetId::Target1 as u64);
let req2 = tb.sender.requests.borrow_mut().pop_front().unwrap();
check_req(req2, ExampleTargetId::Target2 as ComponentId);
check_req(req2, ExampleTargetId::Target2 as u64);
}
#[test]
@@ -1283,11 +1283,11 @@ mod tests {
assert_eq!(req.request, ModeRequest::AnnounceMode);
};
let req0 = tb.sender.requests.borrow_mut().pop_front().unwrap();
check_req(req0, ExampleTargetId::Target0 as ComponentId);
check_req(req0, ExampleTargetId::Target0 as u64);
let req1 = tb.sender.requests.borrow_mut().pop_front().unwrap();
check_req(req1, ExampleTargetId::Target1 as ComponentId);
check_req(req1, ExampleTargetId::Target1 as u64);
let req2 = tb.sender.requests.borrow_mut().pop_front().unwrap();
check_req(req2, ExampleTargetId::Target2 as ComponentId);
check_req(req2, ExampleTargetId::Target2 as u64);
}
#[test]

View File

@@ -50,20 +50,20 @@ impl PacketInPool {
/// Generic trait for object which can send any packets in form of a raw bytestream, with
/// no assumptions about the received protocol.
pub trait PacketHandler: Send {
pub trait PacketSenderRaw: Send {
type Error;
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error>;
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error>;
}
/// Extension trait of [PacketHandler] which allows downcasting by implementing [Downcast].
/// Extension trait of [PacketSenderRaw] which allows downcasting by implementing [Downcast].
#[cfg(feature = "alloc")]
pub trait PacketSenderRawExt: PacketHandler + Downcast {
pub trait PacketSenderRawExt: PacketSenderRaw + Downcast {
// Remove this once trait upcasting coercion has been implemented.
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
fn upcast(&self) -> &dyn PacketHandler<Error = Self::Error>;
fn upcast(&self) -> &dyn PacketSenderRaw<Error = Self::Error>;
// Remove this once trait upcasting coercion has been implemented.
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
fn upcast_mut(&mut self) -> &mut dyn PacketHandler<Error = Self::Error>;
fn upcast_mut(&mut self) -> &mut dyn PacketSenderRaw<Error = Self::Error>;
}
/// Blanket implementation to automatically implement [PacketSenderRawExt] when the [alloc]
@@ -71,16 +71,16 @@ pub trait PacketSenderRawExt: PacketHandler + Downcast {
#[cfg(feature = "alloc")]
impl<T> PacketSenderRawExt for T
where
T: PacketHandler + Send + 'static,
T: PacketSenderRaw + Send + 'static,
{
// Remove this once trait upcasting coercion has been implemented.
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
fn upcast(&self) -> &dyn PacketHandler<Error = Self::Error> {
fn upcast(&self) -> &dyn PacketSenderRaw<Error = Self::Error> {
self
}
// Remove this once trait upcasting coercion has been implemented.
// Tracking issue: https://github.com/rust-lang/rust/issues/65991
fn upcast_mut(&mut self) -> &mut dyn PacketHandler<Error = Self::Error> {
fn upcast_mut(&mut self) -> &mut dyn PacketSenderRaw<Error = Self::Error> {
self
}
}
@@ -288,19 +288,19 @@ pub mod std_mod {
}
}
impl PacketHandler for mpsc::Sender<PacketAsVec> {
impl PacketSenderRaw for mpsc::Sender<PacketAsVec> {
type Error = GenericSendError;
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
self.send(PacketAsVec::new(sender_id, packet.to_vec()))
.map_err(|_| GenericSendError::RxDisconnected)
}
}
impl PacketHandler for mpsc::SyncSender<PacketAsVec> {
impl PacketSenderRaw for mpsc::SyncSender<PacketAsVec> {
type Error = GenericSendError;
fn handle_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
self.try_send(PacketAsVec::new(sender_id, tc_raw.to_vec()))
.map_err(|e| match e {
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
@@ -402,12 +402,12 @@ pub mod std_mod {
}
}
impl<Sender: PacketInPoolSender, PacketStore: CcsdsPacketPool + Send> PacketHandler
impl<Sender: PacketInPoolSender, PacketStore: CcsdsPacketPool + Send> PacketSenderRaw
for PacketSenderWithSharedPool<Sender, PacketStore>
{
type Error = StoreAndSendError;
fn handle_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
let mut shared_pool = self.shared_pool.borrow_mut();
let store_addr = shared_pool.add_raw_tc(packet)?;
drop(shared_pool);
@@ -450,7 +450,7 @@ pub mod std_mod {
_sp_header: &SpHeader,
tc_raw: &[u8],
) -> Result<(), Self::Error> {
self.handle_packet(sender_id, tc_raw)
self.send_packet(sender_id, tc_raw)
}
}
@@ -494,10 +494,10 @@ pub(crate) mod tests {
pub(crate) fn send_with_sender<SendError>(
sender_id: ComponentId,
packet_sender: &(impl PacketHandler<Error = SendError> + ?Sized),
packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
packet: &[u8],
) -> Result<(), SendError> {
packet_sender.handle_packet(sender_id, packet)
packet_sender.send_packet(sender_id, packet)
}
#[test]

View File

@@ -1,7 +1,7 @@
use arbitrary_int::{u11, u14};
use spacepackets::SpHeader;
use spacepackets::ecss::CreatorConfig;
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use spacepackets::ecss::{CreatorConfig, MessageTypeId};
use spacepackets::time::cds::CdsTime;
pub struct PusTmWithCdsShortHelper {
@@ -50,10 +50,7 @@ impl PusTmWithCdsShortHelper {
seq_count: u14,
) -> PusTmCreator<'_, 'data> {
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
let tc_header = PusTmSecondaryHeader::new_simple(
MessageTypeId::new(service, subservice),
&self.cds_short_buf,
);
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
PusTmCreator::new(
reply_header,
tc_header,
@@ -65,10 +62,9 @@ impl PusTmWithCdsShortHelper {
#[cfg(test)]
mod tests {
use arbitrary_int::{u11, u14};
use spacepackets::{CcsdsPacket, ecss::PusPacket, time::cds::CdsTime};
use super::PusTmWithCdsShortHelper;
use super::*;
#[test]
fn test_helper_with_stamper() {
@@ -76,8 +72,8 @@ mod tests {
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, u14::new(25));
assert_eq!(tm.service_type_id(), 17);
assert_eq!(tm.message_subtype_id(), 1);
assert_eq!(tm.service(), 17);
assert_eq!(tm.subservice(), 1);
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
assert_eq!(tm.seq_count().value(), 25);
assert_eq!(tm.timestamp(), [64, 0, 0, 0, 0, 0, 0])
@@ -87,8 +83,8 @@ mod tests {
fn test_helper_from_now() {
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], u14::new(25));
assert_eq!(tm.service_type_id(), 17);
assert_eq!(tm.message_subtype_id(), 1);
assert_eq!(tm.service(), 17);
assert_eq!(tm.subservice(), 1);
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
assert_eq!(tm.seq_count().value(), 25);
assert_eq!(tm.timestamp().len(), 7);

View File

@@ -45,7 +45,7 @@ pub enum AcsMode {
}
#[derive(Debug, TryFromPrimitive)]
#[repr(u32)]
#[repr(u64)]
pub enum TestComponentId {
MagnetometerDevice0 = 1,
MagnetometerDevice1 = 2,
@@ -299,7 +299,7 @@ struct AcsSubsystem {
impl AcsSubsystem {
pub fn id() -> ComponentId {
TestComponentId::AcsSubsystem as ComponentId
TestComponentId::AcsSubsystem as u64
}
pub fn new(mode_node: ModeRequestorAndHandlerMpscBounded) -> Self {
@@ -518,7 +518,7 @@ struct MgmAssembly {
impl MgmAssembly {
pub fn id() -> ComponentId {
TestComponentId::MagnetometerAssembly as ComponentId
TestComponentId::MagnetometerAssembly as u64
}
pub fn new(mode_node: ModeRequestorAndHandlerMpscBounded) -> Self {
Self {
@@ -923,7 +923,7 @@ impl ModeRequestHandler for CommonDevice {
mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), ModeError> {
if self.id() == TestComponentId::MagnetorquerDevice as ComponentId {
if self.id() == TestComponentId::MagnetorquerDevice as u64 {
println!("test");
}
self.mode_and_submode = mode_and_submode;
@@ -996,7 +996,7 @@ pub struct AcsController {
impl AcsController {
pub fn id() -> ComponentId {
TestComponentId::AcsController as ComponentId
TestComponentId::AcsController as u64
}
pub fn new(mode_node: ModeRequestHandlerMpscBounded) -> Self {
Self {
@@ -1020,7 +1020,7 @@ impl AcsController {
impl ModeNode for AcsController {
fn id(&self) -> ComponentId {
TestComponentId::AcsController as ComponentId
TestComponentId::AcsController as u64
}
}
@@ -1185,22 +1185,22 @@ impl TreeTestbench {
let mut mgm_dev_0 = CommonDevice::new(
"MGM_0",
TestComponentId::MagnetometerDevice0 as ComponentId,
TestComponentId::MagnetometerDevice0 as u64,
mgm_dev_node_0,
);
let mut mgm_dev_1 = CommonDevice::new(
"MGM_1",
TestComponentId::MagnetometerDevice1 as ComponentId,
TestComponentId::MagnetometerDevice1 as u64,
mgm_dev_node_1,
);
let mut mgt_dev = CommonDevice::new(
"MGT",
TestComponentId::MagnetorquerDevice as ComponentId,
TestComponentId::MagnetorquerDevice as u64,
mgt_dev_node,
);
let mut mgt_manager = DeviceManager::new(
"MGT_MANAGER",
TestComponentId::MgtDevManager as ComponentId,
TestComponentId::MgtDevManager as u64,
mgt_dev_mgmt_node,
);
let mut mgm_assy = MgmAssembly::new(mgm_assy_node);
@@ -1212,38 +1212,38 @@ impl TreeTestbench {
let mut target_table_safe = TargetTablesMapValue::new("SAFE_TARGET_TBL", None);
target_table_safe.add_entry(TargetTableEntry::new(
"CTRL_SAFE",
TestComponentId::AcsController as ComponentId,
TestComponentId::AcsController as u64,
ModeAndSubmode::new(AcsMode::SAFE as u32, 0),
// All submodes allowed.
Some(0xffff),
));
target_table_safe.add_entry(TargetTableEntry::new_with_precise_submode(
"MGM_A_NML",
TestComponentId::MagnetometerAssembly as ComponentId,
TestComponentId::MagnetometerAssembly as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0),
));
target_table_safe.add_entry(TargetTableEntry::new_with_precise_submode(
"MGT_MAN_NML",
TestComponentId::MgtDevManager as ComponentId,
TestComponentId::MgtDevManager as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0),
));
let mut sequence_tbl_safe_0 = SequenceTableMapTable::new("SAFE_SEQ_0_TBL");
sequence_tbl_safe_0.add_entry(SequenceTableEntry::new(
"SAFE_SEQ_0_MGM_A",
TestComponentId::MagnetometerAssembly as ComponentId,
TestComponentId::MagnetometerAssembly as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as Mode, 0),
false,
));
sequence_tbl_safe_0.add_entry(SequenceTableEntry::new(
"SAFE_SEQ_0_MGT_MAN",
TestComponentId::MgtDevManager as ComponentId,
TestComponentId::MgtDevManager as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as Mode, 0),
false,
));
let mut sequence_tbl_safe_1 = SequenceTableMapTable::new("SAFE_SEQ_1_TBL");
sequence_tbl_safe_1.add_entry(SequenceTableEntry::new(
"SAFE_SEQ_1_ACS_CTRL",
TestComponentId::AcsController as ComponentId,
TestComponentId::AcsController as u64,
ModeAndSubmode::new(AcsMode::SAFE as Mode, 0),
false,
));
@@ -1408,7 +1408,7 @@ fn announce_recursively() {
fn generic_mode_reply_checker(
reply_meta: MessageMetadata,
mode_and_submode: ModeAndSubmode,
expected_modes: &mut HashMap<ComponentId, ModeAndSubmode>,
expected_modes: &mut HashMap<u64, ModeAndSubmode>,
) {
let id = TestComponentId::try_from(reply_meta.sender_id()).expect("invalid sender id");
if !expected_modes.contains_key(&reply_meta.sender_id()) {
@@ -1528,15 +1528,15 @@ fn command_safe_mode() {
);
let mut expected_modes = HashMap::new();
expected_modes.insert(
TestComponentId::AcsController as ComponentId,
TestComponentId::AcsController as u64,
ModeAndSubmode::new(AcsMode::SAFE as u32, 0),
);
expected_modes.insert(
TestComponentId::MagnetometerAssembly as ComponentId,
TestComponentId::MagnetometerAssembly as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0),
);
expected_modes.insert(
TestComponentId::MgtDevManager as ComponentId,
TestComponentId::MgtDevManager as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0),
);
while let Some(reply) = tb.subsystem.mode_reply_mock.mode_reply_messages.pop_front() {
@@ -1558,11 +1558,11 @@ fn command_safe_mode() {
);
let mut expected_modes = HashMap::new();
expected_modes.insert(
TestComponentId::MagnetometerDevice0 as ComponentId,
TestComponentId::MagnetometerDevice0 as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0),
);
expected_modes.insert(
TestComponentId::MagnetometerDevice1 as ComponentId,
TestComponentId::MagnetometerDevice1 as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0),
);
while let Some(reply) = tb.mgm_assy.mode_reply_mock.mode_reply_messages.pop_front() {
@@ -1578,7 +1578,7 @@ fn command_safe_mode() {
);
let mut expected_modes = HashMap::new();
expected_modes.insert(
TestComponentId::MagnetorquerDevice as ComponentId,
TestComponentId::MagnetorquerDevice as u64,
ModeAndSubmode::new(DefaultMode::NORMAL as u32, 0),
);
let reply = tb

View File

@@ -0,0 +1,94 @@
#![allow(dead_code, unused_imports)]
use satrs::events::{
EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw,
Severity, SeverityInfo, SeverityLow, SeverityMedium,
};
use std::convert::AsRef;
#[derive(Debug)]
struct GroupIdIntrospection {
name: &'static str,
id: LargestGroupIdRaw,
}
#[derive(Debug)]
struct EventIntrospection {
name: &'static str,
group_id: GroupIdIntrospection,
event: &'static EventU32,
info: &'static str,
}
//#[event(descr="This is some info event")]
const INFO_EVENT_0: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(0, 0);
const INFO_EVENT_0_ERASED: EventU32 = EventU32::const_from_info(INFO_EVENT_0);
// This is ideally auto-generated
const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection {
name: "INFO_EVENT_0",
group_id: GroupIdIntrospection {
id: 0,
name: "Group ID 0 without name",
},
event: &INFO_EVENT_0_ERASED,
info: "This is some info event",
};
//#[event(descr="This is some low severity event")]
const SOME_LOW_SEV_EVENT: EventU32TypedSev<SeverityLow> = EventU32TypedSev::new(0, 12);
//const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT];
//#[event_group]
const TEST_GROUP_NAME: u16 = 1;
// Auto-generated?
const TEST_GROUP_NAME_NAME: &str = "TEST_GROUP_NAME";
//#[event(desc="Some medium severity event")]
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev<SeverityMedium> =
EventU32TypedSev::new(TEST_GROUP_NAME, 0);
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED: EventU32 =
EventU32::const_from_medium(MEDIUM_SEV_EVENT_IN_OTHER_GROUP);
// Also auto-generated
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION: EventIntrospection = EventIntrospection {
name: "MEDIUM_SEV_EVENT_IN_OTHER_GROUP",
group_id: GroupIdIntrospection {
name: TEST_GROUP_NAME_NAME,
id: TEST_GROUP_NAME,
},
event: &MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED,
info: "Some medium severity event",
};
const CONST_SLICE: &[u8] = &[0, 1, 2, 3];
const INTROSPECTION_FOR_TEST_GROUP_0: [&EventIntrospection; 2] =
[&INFO_EVENT_0_INTROSPECTION, &INFO_EVENT_0_INTROSPECTION];
//const INTROSPECTION_FOR_TABLE: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_0;
const INTROSPECTION_FOR_TEST_GROUP_NAME: [&EventIntrospection; 1] =
[&MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION];
//const BLAH: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_NAME;
const ALL_EVENTS: [&[&EventIntrospection]; 2] = [
&INTROSPECTION_FOR_TEST_GROUP_0,
&INTROSPECTION_FOR_TEST_GROUP_NAME,
];
#[test]
fn main() {
//let test = stringify!(INFO_EVENT);
//println!("{:?}", test);
//for event in EVENT_LIST {
// println!("{:?}", event);
//}
//for events in ALL_EVENTS.into_iter().flatten() {
// dbg!("{:?}", events);
//}
//for introspection_info in INTROSPECTION_FOR_TEST_GROUP {
// dbg!("{:?}", introspection_info);
//}
//let test_struct =
}

View File

@@ -1,9 +1,9 @@
use arbitrary_int::{u11, u21};
use satrs::event_man_legacy::{
use arbitrary_int::u11;
use satrs::event_man::{
EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider,
EventU32SenderMpsc,
};
use satrs::events_legacy::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
use satrs::params::U32Pair;
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap};
@@ -18,7 +18,7 @@ const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityIn
const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
const EMPTY_STAMP: [u8; 7] = [0; 7];
const TEST_APID: u11 = u11::new(0x02);
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, u21::new(0x05));
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
#[derive(Debug, Clone)]
pub enum CustomTmSenderError {
@@ -36,7 +36,8 @@ fn test_threaded_usage() {
event_man.subscribe_all(pus_event_man_send_provider.target_id());
event_man.add_sender(pus_event_man_send_provider);
let (event_packet_tx, event_packet_rx) = mpsc::channel::<PacketAsVec>();
let reporter = EventReporter::new(TEST_ID.raw(), u11::new(0x02), 0, 128);
let reporter = EventReporter::new(TEST_ID.raw(), u11::new(0x02), 0, 128)
.expect("Creating event reporter failed");
let pus_event_man =
PusEventTmCreatorWithMap::new(reporter, DefaultPusEventReportingMap::default());
let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| {
@@ -107,8 +108,8 @@ fn test_threaded_usage() {
Ok(event_tm) => {
let tm = PusTmReader::new(event_tm.packet.as_slice(), 7)
.expect("Deserializing TM failed");
assert_eq!(tm.service_type_id(), 5);
assert_eq!(tm.message_subtype_id(), 1);
assert_eq!(tm.service(), 5);
assert_eq!(tm.subservice(), 1);
let src_data = tm.source_data();
assert!(!src_data.is_empty());
assert_eq!(src_data.len(), 4);
@@ -137,8 +138,8 @@ fn test_threaded_usage() {
Ok(event_tm) => {
let tm = PusTmReader::new(event_tm.packet.as_slice(), 7)
.expect("Deserializing TM failed");
assert_eq!(tm.service_type_id(), 5);
assert_eq!(tm.message_subtype_id(), 2);
assert_eq!(tm.service(), 5);
assert_eq!(tm.subservice(), 2);
let src_data = tm.source_data();
assert!(!src_data.is_empty());
assert_eq!(src_data.len(), 12);

View File

@@ -13,9 +13,7 @@ pub mod crossbeam_test {
use spacepackets::SpHeader;
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::{
CreatorConfig, EcssEnumU8, EcssEnumU16, MessageTypeId, WritablePusPacket,
};
use spacepackets::ecss::{CreatorConfig, EcssEnumU8, EcssEnumU16, WritablePusPacket};
use std::sync::RwLock;
use std::thread;
use std::time::Duration;
@@ -35,7 +33,7 @@ pub mod crossbeam_test {
// We use a synced sequence count provider here because both verification reporters have the
// the same APID. If they had distinct APIDs, the more correct approach would be to have
// each reporter have an own sequence count provider.
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
// Shared pool object to store the verification PUS telemetry
let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (10, 64), (10, 128), (10, 1024)],
@@ -62,7 +60,7 @@ pub mod crossbeam_test {
{
let mut tc_guard = shared_tc_pool.write().unwrap();
let sph = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
let pus_tc_0 = PusTcCreator::new_no_app_data(sph, tc_header, CreatorConfig::default());
req_id_0 = RequestId::new(&pus_tc_0);
let addr = tc_guard
@@ -72,7 +70,7 @@ pub mod crossbeam_test {
.unwrap();
tx_tc_0.send(addr).unwrap();
let sph = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(1), 0);
let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(5, 1));
let tc_header = PusTcSecondaryHeader::new_simple(5, 1);
let pus_tc_1 = PusTcCreator::new_no_app_data(sph, tc_header, CreatorConfig::default());
req_id_1 = RequestId::new(&pus_tc_1);
let addr = tc_guard
@@ -166,11 +164,11 @@ pub mod crossbeam_test {
RequestId::from_bytes(&pus_tm.source_data()[0..RequestId::SIZE_AS_BYTES])
.expect("reading request ID from PUS TM source data failed");
if !verif_map.contains_key(&req_id) {
let content = vec![pus_tm.service_type_id()];
let content = vec![pus_tm.subservice()];
verif_map.insert(req_id, content);
} else {
let content = verif_map.get_mut(&req_id).unwrap();
content.push(pus_tm.message_subtype_id())
content.push(pus_tm.subservice())
}
packet_counter += 1;
}

View File

@@ -21,7 +21,7 @@ use std::{
thread,
};
use arbitrary_int::{u11, u14};
use arbitrary_int::{traits::Integer, u11, u14};
use hashbrown::HashSet;
use satrs::{
ComponentId,
@@ -29,18 +29,15 @@ use satrs::{
ccsds::{SpValidity, SpacePacketValidator},
cobs::encode_packet_with_cobs,
},
hal::std::{
tcp_server::{
CobsTcParser, ConnectionResult, HandledConnectionHandler, HandledConnectionInfo,
ServerConfig, TcpSpacepacketsServer, TcpTmtcInCobsServer,
},
tcp_spacepackets_server::CcsdsPacketParser,
hal::std::tcp_server::{
ConnectionResult, HandledConnectionHandler, HandledConnectionInfo, ServerConfig,
TcpSpacepacketsServer, TcpTmtcInCobsServer,
},
tmtc::PacketSource,
};
use spacepackets::{
CcsdsPacket, PacketId, SpHeader,
ecss::{CreatorConfig, MessageTypeId, WritablePusPacket, tc::PusTcCreator},
ecss::{CreatorConfig, WritablePusPacket, tc::PusTcCreator},
};
use std::{collections::VecDeque, sync::Arc, vec::Vec};
@@ -124,7 +121,7 @@ fn test_cobs_server() {
1024,
),
tm_source,
CobsTcParser::new(TCP_SERVER_ID, 1024, tc_sender.clone()),
tc_sender.clone(),
ConnectionFinishedHandler::default(),
None,
)
@@ -222,8 +219,7 @@ fn test_ccsds_server() {
let (tc_sender, tc_receiver) = mpsc::channel();
let mut tm_source = SyncTmSource::default();
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::new(0), 0);
let verif_tm =
PusTcCreator::new_simple(sph, MessageTypeId::new(1, 1), &[], CreatorConfig::default());
let verif_tm = PusTcCreator::new_simple(sph, 1, 1, &[], CreatorConfig::default());
let tm_0 = verif_tm.to_vec().expect("tm generation failed");
tm_source.add_tm(&tm_0);
let mut packet_id_lookup = SimpleVerificator::default();
@@ -237,7 +233,8 @@ fn test_ccsds_server() {
1024,
),
tm_source,
CcsdsPacketParser::new(TCP_SERVER_ID, 1024, tc_sender, packet_id_lookup),
tc_sender,
packet_id_lookup,
ConnectionFinishedHandler::default(),
None,
)
@@ -271,13 +268,8 @@ fn test_ccsds_server() {
.expect("setting reas timeout failed");
// Send ping telecommand.
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::new(0), 0);
let ping_tc = PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
&[],
CreatorConfig::default(),
);
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::ZERO, 0);
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
let tc_0 = ping_tc.to_vec().expect("packet creation failed");
stream
.write_all(&tc_0)