updated STM32F3 RTICv2 example #254
@@ -6,6 +6,7 @@ members = [
 | 
			
		||||
    "satrs-example",
 | 
			
		||||
    "satrs-minisim",
 | 
			
		||||
    "satrs-shared",
 | 
			
		||||
    "embedded-examples/embedded-client",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
exclude = [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								embedded-examples/embedded-client/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								embedded-examples/embedded-client/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "embedded-client"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2024"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
clap = { version = "4", features = ["derive"] }
 | 
			
		||||
serialport = "4"
 | 
			
		||||
toml = "0.9"
 | 
			
		||||
serde = { version = "1", features = ["derive"] }
 | 
			
		||||
satrs-stm32f3-disco-rtic = { path = "../stm32f3-disco-rtic" }
 | 
			
		||||
spacepackets = { 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"
 | 
			
		||||
							
								
								
									
										2
									
								
								embedded-examples/embedded-client/config.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								embedded-examples/embedded-client/config.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
[interface]
 | 
			
		||||
serial_port = "/dev/ttyUSB0"
 | 
			
		||||
							
								
								
									
										107
									
								
								embedded-examples/embedded-client/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								embedded-examples/embedded-client/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
use std::{
 | 
			
		||||
    fs::File,
 | 
			
		||||
    io::Read,
 | 
			
		||||
    path::Path,
 | 
			
		||||
    time::{Duration, SystemTime},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use cobs::CobsDecoderOwned;
 | 
			
		||||
use satrs_stm32f3_disco_rtic::Request;
 | 
			
		||||
use spacepackets::{CcsdsPacketCreatorOwned, CcsdsPacketReader, SpHeader};
 | 
			
		||||
use tmtc_utils::transport::{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()
 | 
			
		||||
}
 | 
			
		||||
@@ -34,4 +34,4 @@ rustflags = [
 | 
			
		||||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
 | 
			
		||||
 | 
			
		||||
[env]
 | 
			
		||||
DEFMT_LOG = "info"
 | 
			
		||||
DEFMT_LOG = "info"
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
/target
 | 
			
		||||
/itm.txt
 | 
			
		||||
/.cargo/config*
 | 
			
		||||
/.cargo/config.toml
 | 
			
		||||
/.vscode
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1212
									
								
								embedded-examples/stm32f3-disco-rtic/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1212
									
								
								embedded-examples/stm32f3-disco-rtic/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -9,49 +9,33 @@ default-run = "satrs-stm32f3-disco-rtic"
 | 
			
		||||
[dependencies]
 | 
			
		||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
 | 
			
		||||
cortex-m-rt = "0.7"
 | 
			
		||||
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"
 | 
			
		||||
defmt = "1"
 | 
			
		||||
defmt-rtt = { version = "1" }
 | 
			
		||||
panic-probe = { version = "1", features = ["print-defmt"] }
 | 
			
		||||
embedded-hal = "1"
 | 
			
		||||
cortex-m-semihosting = "0.5.0"
 | 
			
		||||
embassy-stm32 = { version = "0.4", features = ["defmt", "stm32f303vc", "unstable-pac"] }
 | 
			
		||||
enumset = "1"
 | 
			
		||||
heapless = "0.8"
 | 
			
		||||
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"] }
 | 
			
		||||
 | 
			
		||||
[dependencies.rtic]
 | 
			
		||||
version = "2"
 | 
			
		||||
features = ["thumbv7-backend"]
 | 
			
		||||
rtic = { version = "2", features = ["thumbv7-backend"] }
 | 
			
		||||
rtic-sync = { version = "1" }
 | 
			
		||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
 | 
			
		||||
 | 
			
		||||
[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"]
 | 
			
		||||
#[dependencies.satrs]
 | 
			
		||||
# path = "../../satrs"
 | 
			
		||||
#default-features = false
 | 
			
		||||
# features = ["defmt"]
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
defmt-test = "0.3"
 | 
			
		||||
defmt-test = "0.4"
 | 
			
		||||
 | 
			
		||||
# cargo test
 | 
			
		||||
[profile.test]
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,10 +0,0 @@
 | 
			
		||||
target extended-remote localhost:2331
 | 
			
		||||
 | 
			
		||||
monitor reset
 | 
			
		||||
 | 
			
		||||
# *try* to stop at the user entry point (it might be gone due to inlining)
 | 
			
		||||
break main
 | 
			
		||||
 | 
			
		||||
load
 | 
			
		||||
 | 
			
		||||
continue
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
# Sample OpenOCD configuration for the STM32F3DISCOVERY development board
 | 
			
		||||
 | 
			
		||||
# Depending on the hardware revision you got you'll have to pick ONE of these
 | 
			
		||||
# interfaces. At any time only one interface should be commented out.
 | 
			
		||||
 | 
			
		||||
# Revision C (newer revision)
 | 
			
		||||
source [find interface/stlink.cfg]
 | 
			
		||||
 | 
			
		||||
# Revision A and B (older revisions)
 | 
			
		||||
# source [find interface/stlink-v2.cfg]
 | 
			
		||||
 | 
			
		||||
source [find target/stm32f3x.cfg]
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
target extended-remote :3333
 | 
			
		||||
 | 
			
		||||
# print demangled symbols
 | 
			
		||||
set print asm-demangle on
 | 
			
		||||
 | 
			
		||||
# set backtrace limit to not have infinite backtrace loops
 | 
			
		||||
set backtrace limit 32
 | 
			
		||||
 | 
			
		||||
# detect unhandled exceptions, hard faults and panics
 | 
			
		||||
break DefaultHandler
 | 
			
		||||
break HardFault
 | 
			
		||||
break rust_begin_unwind
 | 
			
		||||
# # run the next few lines so the panic message is printed immediately
 | 
			
		||||
# # the number needs to be adjusted for your panic handler
 | 
			
		||||
# commands $bpnum
 | 
			
		||||
# next 4
 | 
			
		||||
# end
 | 
			
		||||
 | 
			
		||||
# *try* to stop at the user entry point (it might be gone due to inlining)
 | 
			
		||||
break main
 | 
			
		||||
 | 
			
		||||
# monitor arm semihosting enable
 | 
			
		||||
 | 
			
		||||
# # send captured ITM to the file itm.fifo
 | 
			
		||||
# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
 | 
			
		||||
# # 8000000 must match the core clock frequency
 | 
			
		||||
# # 2000000 is the frequency of the SWO pin. This was added for newer
 | 
			
		||||
# openocd versions like v0.12.0.
 | 
			
		||||
# monitor tpiu config internal itm.txt uart off 8000000 2000000
 | 
			
		||||
 | 
			
		||||
# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
 | 
			
		||||
# # 8000000 must match the core clock frequency
 | 
			
		||||
# # 2000000 is the frequency of the SWO pin
 | 
			
		||||
# monitor tpiu config external uart off 8000000 2000000
 | 
			
		||||
 | 
			
		||||
# # enable ITM port 0
 | 
			
		||||
# monitor itm port 0 on
 | 
			
		||||
 | 
			
		||||
load
 | 
			
		||||
 | 
			
		||||
# start the process but immediately halt the processor
 | 
			
		||||
stepi
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
/venv
 | 
			
		||||
/.tmtc-history.txt
 | 
			
		||||
/log
 | 
			
		||||
/.idea/*
 | 
			
		||||
!/.idea/runConfigurations
 | 
			
		||||
 | 
			
		||||
/seqcnt.txt
 | 
			
		||||
/tmtc_conf.json
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "com_if": "serial_cobs",
 | 
			
		||||
    "serial_baudrate": 115200
 | 
			
		||||
}
 | 
			
		||||
@@ -1,305 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
"""Example client for the sat-rs example application"""
 | 
			
		||||
import struct
 | 
			
		||||
import logging
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
from typing import Any, Optional, cast
 | 
			
		||||
from prompt_toolkit.history import FileHistory, History
 | 
			
		||||
from spacepackets.ecss.tm import CdsShortTimestamp
 | 
			
		||||
 | 
			
		||||
import tmtccmd
 | 
			
		||||
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusTm, PusVerificator
 | 
			
		||||
from spacepackets.ecss.pus_17_test import Service17Tm
 | 
			
		||||
from spacepackets.ecss.pus_1_verification import UnpackParams, Service1Tm
 | 
			
		||||
 | 
			
		||||
from tmtccmd import TcHandlerBase, ProcedureParamsWrapper
 | 
			
		||||
from tmtccmd.core.base import BackendRequest
 | 
			
		||||
from tmtccmd.core.ccsds_backend import QueueWrapper
 | 
			
		||||
from tmtccmd.logging import add_colorlog_console_logger
 | 
			
		||||
from tmtccmd.pus import VerificationWrapper
 | 
			
		||||
from tmtccmd.tmtc import CcsdsTmHandler, SpecificApidHandlerBase
 | 
			
		||||
from tmtccmd.com import ComInterface
 | 
			
		||||
from tmtccmd.config import (
 | 
			
		||||
    CmdTreeNode,
 | 
			
		||||
    default_json_path,
 | 
			
		||||
    SetupParams,
 | 
			
		||||
    HookBase,
 | 
			
		||||
    params_to_procedure_conversion,
 | 
			
		||||
)
 | 
			
		||||
from tmtccmd.config.com import SerialCfgWrapper
 | 
			
		||||
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
 | 
			
		||||
from tmtccmd.logging.pus import (
 | 
			
		||||
    RegularTmtcLogWrapper,
 | 
			
		||||
    RawTmtcTimedLogWrapper,
 | 
			
		||||
    TimedLogWhen,
 | 
			
		||||
)
 | 
			
		||||
from tmtccmd.tmtc import (
 | 
			
		||||
    TcQueueEntryType,
 | 
			
		||||
    ProcedureWrapper,
 | 
			
		||||
    TcProcedureType,
 | 
			
		||||
    FeedWrapper,
 | 
			
		||||
    SendCbParams,
 | 
			
		||||
    DefaultPusQueueHelper,
 | 
			
		||||
)
 | 
			
		||||
from tmtccmd.pus.s5_fsfw_event import Service5Tm
 | 
			
		||||
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
 | 
			
		||||
from tmtccmd.util.obj_id import ObjectIdDictT
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger()
 | 
			
		||||
 | 
			
		||||
EXAMPLE_PUS_APID = 0x02
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SatRsConfigHook(HookBase):
 | 
			
		||||
    def __init__(self, json_cfg_path: str):
 | 
			
		||||
        super().__init__(json_cfg_path)
 | 
			
		||||
 | 
			
		||||
    def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
 | 
			
		||||
        from tmtccmd.config.com import (
 | 
			
		||||
            create_com_interface_default,
 | 
			
		||||
            create_com_interface_cfg_default,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        assert self.cfg_path is not None
 | 
			
		||||
        cfg = create_com_interface_cfg_default(
 | 
			
		||||
            com_if_key=com_if_key,
 | 
			
		||||
            json_cfg_path=self.cfg_path,
 | 
			
		||||
            space_packet_ids=None,
 | 
			
		||||
        )
 | 
			
		||||
        if cfg is None:
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                f"No valid configuration could be retrieved for the COM IF with key {com_if_key}"
 | 
			
		||||
            )
 | 
			
		||||
        if cfg.com_if_key == "serial_cobs":
 | 
			
		||||
            cfg = cast(SerialCfgWrapper, cfg)
 | 
			
		||||
            cfg.serial_cfg.serial_timeout = 0.5
 | 
			
		||||
        return create_com_interface_default(cfg)
 | 
			
		||||
 | 
			
		||||
    def get_command_definitions(self) -> CmdTreeNode:
 | 
			
		||||
        """This function should return the root node of the command definition tree."""
 | 
			
		||||
        return create_cmd_definition_tree()
 | 
			
		||||
 | 
			
		||||
    def get_cmd_history(self) -> Optional[History]:
 | 
			
		||||
        """Optionlly return a history class for the past command paths which will be used
 | 
			
		||||
        when prompting a command path from the user in CLI mode."""
 | 
			
		||||
        return FileHistory(".tmtc-history.txt")
 | 
			
		||||
 | 
			
		||||
    def get_object_ids(self) -> ObjectIdDictT:
 | 
			
		||||
        from tmtccmd.config.objects import get_core_object_ids
 | 
			
		||||
 | 
			
		||||
        return get_core_object_ids()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_cmd_definition_tree() -> CmdTreeNode:
 | 
			
		||||
    root_node = CmdTreeNode.root_node()
 | 
			
		||||
    root_node.add_child(CmdTreeNode("ping", "Send PUS ping TC"))
 | 
			
		||||
    root_node.add_child(CmdTreeNode("change_blink_freq", "Change blink frequency"))
 | 
			
		||||
    return root_node
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PusHandler(SpecificApidHandlerBase):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        file_logger: logging.Logger,
 | 
			
		||||
        verif_wrapper: VerificationWrapper,
 | 
			
		||||
        raw_logger: RawTmtcTimedLogWrapper,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__(EXAMPLE_PUS_APID, None)
 | 
			
		||||
        self.file_logger = file_logger
 | 
			
		||||
        self.raw_logger = raw_logger
 | 
			
		||||
        self.verif_wrapper = verif_wrapper
 | 
			
		||||
 | 
			
		||||
    def handle_tm(self, packet: bytes, _user_args: Any):
 | 
			
		||||
        try:
 | 
			
		||||
            pus_tm = PusTm.unpack(
 | 
			
		||||
                packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
 | 
			
		||||
            )
 | 
			
		||||
        except ValueError as e:
 | 
			
		||||
            _LOGGER.warning("Could not generate PUS TM object from raw data")
 | 
			
		||||
            _LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
 | 
			
		||||
            raise e
 | 
			
		||||
        service = pus_tm.service
 | 
			
		||||
        tm_packet = None
 | 
			
		||||
        if service == 1:
 | 
			
		||||
            tm_packet = Service1Tm.unpack(
 | 
			
		||||
                data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 1, 2)
 | 
			
		||||
            )
 | 
			
		||||
            res = self.verif_wrapper.add_tm(tm_packet)
 | 
			
		||||
            if res is None:
 | 
			
		||||
                _LOGGER.info(
 | 
			
		||||
                    f"Received Verification TM[{tm_packet.service}, {tm_packet.subservice}] "
 | 
			
		||||
                    f"with Request ID {tm_packet.tc_req_id.as_u32():#08x}"
 | 
			
		||||
                )
 | 
			
		||||
                _LOGGER.warning(
 | 
			
		||||
                    f"No matching telecommand found for {tm_packet.tc_req_id}"
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                self.verif_wrapper.log_to_console(tm_packet, res)
 | 
			
		||||
                self.verif_wrapper.log_to_file(tm_packet, res)
 | 
			
		||||
        if service == 3:
 | 
			
		||||
            _LOGGER.info("No handling for HK packets implemented")
 | 
			
		||||
            _LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
 | 
			
		||||
            pus_tm = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
 | 
			
		||||
            if pus_tm.subservice == 25:
 | 
			
		||||
                if len(pus_tm.source_data) < 8:
 | 
			
		||||
                    raise ValueError("No addressable ID in HK packet")
 | 
			
		||||
                json_str = pus_tm.source_data[8:]
 | 
			
		||||
                _LOGGER.info("received JSON string: " + json_str.decode("utf-8"))
 | 
			
		||||
        if service == 5:
 | 
			
		||||
            tm_packet = Service5Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
 | 
			
		||||
        if service == 17:
 | 
			
		||||
            tm_packet = Service17Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
 | 
			
		||||
            if tm_packet.subservice == 2:
 | 
			
		||||
                _LOGGER.info("Received Ping Reply TM[17,2]")
 | 
			
		||||
            else:
 | 
			
		||||
                _LOGGER.info(
 | 
			
		||||
                    f"Received Test Packet with unknown subservice {tm_packet.subservice}"
 | 
			
		||||
                )
 | 
			
		||||
        if tm_packet is None:
 | 
			
		||||
            _LOGGER.info(
 | 
			
		||||
                f"The service {service} is not implemented in Telemetry Factory"
 | 
			
		||||
            )
 | 
			
		||||
            tm_packet = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
 | 
			
		||||
        self.raw_logger.log_tm(pus_tm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def make_addressable_id(target_id: int, unique_id: int) -> bytes:
 | 
			
		||||
    byte_string = bytearray(struct.pack("!I", target_id))
 | 
			
		||||
    byte_string.extend(struct.pack("!I", unique_id))
 | 
			
		||||
    return byte_string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TcHandler(TcHandlerBase):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        seq_count_provider: FileSeqCountProvider,
 | 
			
		||||
        verif_wrapper: VerificationWrapper,
 | 
			
		||||
    ):
 | 
			
		||||
        super(TcHandler, self).__init__()
 | 
			
		||||
        self.seq_count_provider = seq_count_provider
 | 
			
		||||
        self.verif_wrapper = verif_wrapper
 | 
			
		||||
        self.queue_helper = DefaultPusQueueHelper(
 | 
			
		||||
            queue_wrapper=QueueWrapper.empty(),
 | 
			
		||||
            tc_sched_timestamp_len=7,
 | 
			
		||||
            seq_cnt_provider=seq_count_provider,
 | 
			
		||||
            pus_verificator=verif_wrapper.pus_verificator,
 | 
			
		||||
            default_pus_apid=EXAMPLE_PUS_APID,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def send_cb(self, send_params: SendCbParams):
 | 
			
		||||
        entry_helper = send_params.entry
 | 
			
		||||
        if entry_helper.is_tc:
 | 
			
		||||
            if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
 | 
			
		||||
                pus_tc_wrapper = entry_helper.to_pus_tc_entry()
 | 
			
		||||
                pus_tc_wrapper.pus_tc.seq_count = (
 | 
			
		||||
                    self.seq_count_provider.get_and_increment()
 | 
			
		||||
                )
 | 
			
		||||
                self.verif_wrapper.add_tc(pus_tc_wrapper.pus_tc)
 | 
			
		||||
                raw_tc = pus_tc_wrapper.pus_tc.pack()
 | 
			
		||||
                _LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
 | 
			
		||||
                send_params.com_if.send(raw_tc)
 | 
			
		||||
        elif entry_helper.entry_type == TcQueueEntryType.LOG:
 | 
			
		||||
            log_entry = entry_helper.to_log_entry()
 | 
			
		||||
            _LOGGER.info(log_entry.log_str)
 | 
			
		||||
 | 
			
		||||
    def queue_finished_cb(self, info: ProcedureWrapper):
 | 
			
		||||
        if info.proc_type == TcProcedureType.TREE_COMMANDING:
 | 
			
		||||
            def_proc = info.to_tree_commanding_procedure()
 | 
			
		||||
            _LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
 | 
			
		||||
 | 
			
		||||
    def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
 | 
			
		||||
        q = self.queue_helper
 | 
			
		||||
        q.queue_wrapper = wrapper.queue_wrapper
 | 
			
		||||
        if info.proc_type == TcProcedureType.TREE_COMMANDING:
 | 
			
		||||
            def_proc = info.to_tree_commanding_procedure()
 | 
			
		||||
            cmd_path = def_proc.cmd_path
 | 
			
		||||
            if cmd_path == "/ping":
 | 
			
		||||
                q.add_log_cmd("Sending PUS ping telecommand")
 | 
			
		||||
                q.add_pus_tc(PusTelecommand(service=17, subservice=1))
 | 
			
		||||
            if cmd_path == "/change_blink_freq":
 | 
			
		||||
                self.create_change_blink_freq_command(q)
 | 
			
		||||
 | 
			
		||||
    def create_change_blink_freq_command(self, q: DefaultPusQueueHelper):
 | 
			
		||||
        q.add_log_cmd("Changing blink frequency")
 | 
			
		||||
        while True:
 | 
			
		||||
            blink_freq = int(
 | 
			
		||||
                input(
 | 
			
		||||
                    "Please specify new blink frequency in ms. Valid Range [2..10000]: "
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            if blink_freq < 2 or blink_freq > 10000:
 | 
			
		||||
                print(
 | 
			
		||||
                    "Invalid blink frequency. Please specify a value between 2 and 10000."
 | 
			
		||||
                )
 | 
			
		||||
                continue
 | 
			
		||||
            break
 | 
			
		||||
        app_data = struct.pack("!I", blink_freq)
 | 
			
		||||
        q.add_pus_tc(PusTelecommand(service=8, subservice=1, app_data=app_data))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    add_colorlog_console_logger(_LOGGER)
 | 
			
		||||
    tmtccmd.init_printout(False)
 | 
			
		||||
    hook_obj = SatRsConfigHook(json_cfg_path=default_json_path())
 | 
			
		||||
    parser_wrapper = PreArgsParsingWrapper()
 | 
			
		||||
    parser_wrapper.create_default_parent_parser()
 | 
			
		||||
    parser_wrapper.create_default_parser()
 | 
			
		||||
    parser_wrapper.add_def_proc_args()
 | 
			
		||||
    params = SetupParams()
 | 
			
		||||
    post_args_wrapper = parser_wrapper.parse(hook_obj, params)
 | 
			
		||||
    proc_wrapper = ProcedureParamsWrapper()
 | 
			
		||||
    if post_args_wrapper.use_gui:
 | 
			
		||||
        post_args_wrapper.set_params_without_prompts(proc_wrapper)
 | 
			
		||||
    else:
 | 
			
		||||
        post_args_wrapper.set_params_with_prompts(proc_wrapper)
 | 
			
		||||
    params.apid = EXAMPLE_PUS_APID
 | 
			
		||||
    setup_args = SetupWrapper(
 | 
			
		||||
        hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_wrapper
 | 
			
		||||
    )
 | 
			
		||||
    # Create console logger helper and file loggers
 | 
			
		||||
    tmtc_logger = RegularTmtcLogWrapper()
 | 
			
		||||
    file_logger = tmtc_logger.logger
 | 
			
		||||
    raw_logger = RawTmtcTimedLogWrapper(when=TimedLogWhen.PER_HOUR, interval=1)
 | 
			
		||||
    verificator = PusVerificator()
 | 
			
		||||
    verification_wrapper = VerificationWrapper(verificator, _LOGGER, file_logger)
 | 
			
		||||
    # Create primary TM handler and add it to the CCSDS Packet Handler
 | 
			
		||||
    tm_handler = PusHandler(file_logger, verification_wrapper, raw_logger)
 | 
			
		||||
    ccsds_handler = CcsdsTmHandler(generic_handler=None)
 | 
			
		||||
    ccsds_handler.add_apid_handler(tm_handler)
 | 
			
		||||
 | 
			
		||||
    # Create TC handler
 | 
			
		||||
    seq_count_provider = PusFileSeqCountProvider()
 | 
			
		||||
    tc_handler = TcHandler(seq_count_provider, verification_wrapper)
 | 
			
		||||
    tmtccmd.setup(setup_args=setup_args)
 | 
			
		||||
    init_proc = params_to_procedure_conversion(setup_args.proc_param_wrapper)
 | 
			
		||||
    tmtc_backend = tmtccmd.create_default_tmtc_backend(
 | 
			
		||||
        setup_wrapper=setup_args,
 | 
			
		||||
        tm_handler=ccsds_handler,
 | 
			
		||||
        tc_handler=tc_handler,
 | 
			
		||||
        init_procedure=init_proc,
 | 
			
		||||
    )
 | 
			
		||||
    tmtccmd.start(tmtc_backend=tmtc_backend, hook_obj=hook_obj)
 | 
			
		||||
    try:
 | 
			
		||||
        while True:
 | 
			
		||||
            state = tmtc_backend.periodic_op(None)
 | 
			
		||||
            if state.request == BackendRequest.TERMINATION_NO_ERROR:
 | 
			
		||||
                sys.exit(0)
 | 
			
		||||
            elif state.request == BackendRequest.DELAY_IDLE:
 | 
			
		||||
                _LOGGER.info("TMTC Client in IDLE mode")
 | 
			
		||||
                time.sleep(3.0)
 | 
			
		||||
            elif state.request == BackendRequest.DELAY_LISTENER:
 | 
			
		||||
                time.sleep(0.8)
 | 
			
		||||
            elif state.request == BackendRequest.DELAY_CUSTOM:
 | 
			
		||||
                if state.next_delay.total_seconds() <= 0.4:
 | 
			
		||||
                    time.sleep(state.next_delay.total_seconds())
 | 
			
		||||
                else:
 | 
			
		||||
                    time.sleep(0.4)
 | 
			
		||||
            elif state.request == BackendRequest.CALL_NEXT:
 | 
			
		||||
                pass
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        sys.exit(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    main()
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
tmtccmd == 8.0.1
 | 
			
		||||
# -e git+https://github.com/robamu-org/tmtccmd.git@main#egg=tmtccmd
 | 
			
		||||
@@ -1,76 +1,61 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
use satrs_stm32f3_disco_rtic as _;
 | 
			
		||||
#![no_std]
 | 
			
		||||
 | 
			
		||||
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};
 | 
			
		||||
use panic_probe as _;
 | 
			
		||||
use rtic::app;
 | 
			
		||||
 | 
			
		||||
#[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);
 | 
			
		||||
#[app(device = embassy_stm32)]
 | 
			
		||||
mod app {
 | 
			
		||||
    use rtic_monotonics::fugit::ExtU32;
 | 
			
		||||
    use rtic_monotonics::Monotonic as _;
 | 
			
		||||
    use satrs_stm32f3_disco_rtic::{Direction, LedPinSet, Leds};
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    rtic_monotonics::systick_monotonic!(Mono, 1000);
 | 
			
		||||
 | 
			
		||||
        //explicit on/off
 | 
			
		||||
        leds.ld4_nw.on().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        leds.ld4_nw.off().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
    #[shared]
 | 
			
		||||
    struct Shared {}
 | 
			
		||||
 | 
			
		||||
        leds.ld5_ne.on().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        leds.ld5_ne.off().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
    #[local]
 | 
			
		||||
    struct Local {
 | 
			
		||||
        leds: Leds,
 | 
			
		||||
        current_dir: Direction,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        leds.ld6_w.on().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        leds.ld6_w.off().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
    #[init]
 | 
			
		||||
    fn init(cx: init::Context) -> (Shared, Local) {
 | 
			
		||||
        let p = embassy_stm32::init(Default::default());
 | 
			
		||||
 | 
			
		||||
        leds.ld7_e.on().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        leds.ld7_e.off().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        defmt::info!("Starting sat-rs demo application for the STM32F3-Discovery using RTICv2");
 | 
			
		||||
 | 
			
		||||
        leds.ld8_sw.on().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        leds.ld8_sw.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.ld9_se.on().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        leds.ld9_se.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.ld10_s.on().ok();
 | 
			
		||||
        delay.delay_ms(delay_ms);
 | 
			
		||||
        leds.ld10_s.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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,51 +1,190 @@
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![no_std]
 | 
			
		||||
 | 
			
		||||
use cortex_m_semihosting::debug;
 | 
			
		||||
use arbitrary_int::u11;
 | 
			
		||||
use core::time::Duration;
 | 
			
		||||
use embassy_stm32::gpio::Output;
 | 
			
		||||
use spacepackets::{
 | 
			
		||||
    ccsds_packet_len_for_user_data_len_with_checksum, CcsdsPacketCreationError,
 | 
			
		||||
    CcsdsPacketCreatorWithReservedData, CcsdsPacketIdAndPsc, SpacePacketHeader,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use defmt_brtt as _; // global logger
 | 
			
		||||
pub const APID: u11 = u11::new(0x02);
 | 
			
		||||
 | 
			
		||||
use stm32f3xx_hal as _; // memory layout
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
#[derive(defmt::Format, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Copy)]
 | 
			
		||||
pub enum Direction {
 | 
			
		||||
    North,
 | 
			
		||||
    NorthEast,
 | 
			
		||||
    East,
 | 
			
		||||
    SouthEast,
 | 
			
		||||
    South,
 | 
			
		||||
    SouthWest,
 | 
			
		||||
    West,
 | 
			
		||||
    NorthWest,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Terminates the application and makes a semihosting-capable debug tool exit
 | 
			
		||||
/// with status code 0.
 | 
			
		||||
pub fn exit() -> ! {
 | 
			
		||||
    loop {
 | 
			
		||||
        debug::exit(debug::EXIT_SUCCESS);
 | 
			
		||||
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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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);
 | 
			
		||||
#[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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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;
 | 
			
		||||
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>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn it_works() {
 | 
			
		||||
        assert!(true)
 | 
			
		||||
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,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,682 +1,349 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
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 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 rtic::app;
 | 
			
		||||
 | 
			
		||||
use heapless::{mpmc::Q8, Vec};
 | 
			
		||||
#[allow(unused_imports)]
 | 
			
		||||
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32};
 | 
			
		||||
use rtic_monotonics::systick::prelude::*;
 | 
			
		||||
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};
 | 
			
		||||
 | 
			
		||||
use crate::app::Mono;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
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>;
 | 
			
		||||
pub const PUS_APID: u11 = u11::new(0x02);
 | 
			
		||||
 | 
			
		||||
// 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_TC_OVERHEAD: usize = (MAX_TC_LEN + 254 - 1) / 254;
 | 
			
		||||
const COBS_TM_OVERHEAD: usize = (MAX_TM_LEN + 254 - 1) / 254;
 | 
			
		||||
const COBS_TM_OVERHEAD: usize = cobs::max_encoding_overhead(MAX_TM_LEN);
 | 
			
		||||
 | 
			
		||||
const TC_BUF_LEN: usize = MAX_TC_LEN + COBS_TC_OVERHEAD;
 | 
			
		||||
const TM_BUF_LEN: usize = MAX_TC_LEN + COBS_TM_OVERHEAD;
 | 
			
		||||
 | 
			
		||||
// This is a static buffer which should ONLY (!) be used as the TX DMA
 | 
			
		||||
// 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];
 | 
			
		||||
const TC_DMA_BUF_LEN: usize = 512;
 | 
			
		||||
 | 
			
		||||
type TmPacket = Vec<u8, MAX_TM_LEN>;
 | 
			
		||||
type TcPacket = Vec<u8, MAX_TC_LEN>;
 | 
			
		||||
type TmPacket = heapless::Vec<u8, MAX_TM_LEN>;
 | 
			
		||||
 | 
			
		||||
static TM_REQUESTS: Q8<TmPacket> = Q8::new();
 | 
			
		||||
static TM_QUEUE: heapless::mpmc::Queue<TmPacket, 16> = heapless::mpmc::Queue::new();
 | 
			
		||||
 | 
			
		||||
use core::sync::atomic::{AtomicU16, Ordering};
 | 
			
		||||
 | 
			
		||||
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)]
 | 
			
		||||
#[derive(Debug, defmt::Format, thiserror::Error)]
 | 
			
		||||
pub enum TmSendError {
 | 
			
		||||
    ByteConversion(ByteConversionError),
 | 
			
		||||
    #[error("packet creation error: {0}")]
 | 
			
		||||
    PacketCreation(#[from] CcsdsPacketCreationError),
 | 
			
		||||
    #[error("queue error")]
 | 
			
		||||
    Queue,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ByteConversionError> for TmSendError {
 | 
			
		||||
    fn from(value: ByteConversionError) -> Self {
 | 
			
		||||
        Self::ByteConversion(value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn send_tm(tm_creator: PusTmCreator) -> Result<(), TmSendError> {
 | 
			
		||||
    if tm_creator.len_written() > MAX_TM_LEN {
 | 
			
		||||
        return Err(ByteConversionError::ToSliceTooSmall {
 | 
			
		||||
            expected: tm_creator.len_written(),
 | 
			
		||||
            found: MAX_TM_LEN,
 | 
			
		||||
        }
 | 
			
		||||
        .into());
 | 
			
		||||
    }
 | 
			
		||||
    let mut tm_vec = TmPacket::new();
 | 
			
		||||
    tm_vec
 | 
			
		||||
        .resize(tm_creator.len_written(), 0)
 | 
			
		||||
        .expect("vec resize failed");
 | 
			
		||||
    tm_creator.write_to_bytes(tm_vec.as_mut_slice())?;
 | 
			
		||||
    defmt::info!(
 | 
			
		||||
        "Sending TM[{},{}] with size {}",
 | 
			
		||||
        tm_creator.service(),
 | 
			
		||||
        tm_creator.subservice(),
 | 
			
		||||
        tm_creator.len_written()
 | 
			
		||||
    );
 | 
			
		||||
    TM_REQUESTS
 | 
			
		||||
        .enqueue(tm_vec)
 | 
			
		||||
        .map_err(|_| TmSendError::Queue)?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn handle_tm_send_error(error: TmSendError) {
 | 
			
		||||
    defmt::warn!("sending tm failed with error {}", error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum UartTxState {
 | 
			
		||||
    // Wrapped in an option because we need an owned type later.
 | 
			
		||||
    Idle(Option<TxIdle>),
 | 
			
		||||
    // Same as above
 | 
			
		||||
    Transmitting(Option<TxDmaTransferType>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct UartTxShared {
 | 
			
		||||
    last_completed: Option<InstantFugit>,
 | 
			
		||||
    state: UartTxState,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct RequestWithToken {
 | 
			
		||||
    token: VerificationToken<TcStateAccepted>,
 | 
			
		||||
    request: Request,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, defmt::Format)]
 | 
			
		||||
pub enum Request {
 | 
			
		||||
    Ping,
 | 
			
		||||
    ChangeBlinkFrequency(u32),
 | 
			
		||||
pub struct RequestWithTcId {
 | 
			
		||||
    pub request: Request,
 | 
			
		||||
    pub tc_id: CcsdsPacketId,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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)]
 | 
			
		||||
#[app(device = embassy_stm32)]
 | 
			
		||||
mod app {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    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 core::time::Duration;
 | 
			
		||||
 | 
			
		||||
    use stm32f3_discovery::switch_hal::OutputSwitch;
 | 
			
		||||
    use stm32f3xx_hal::Switch;
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    systick_monotonic!(Mono, 1000);
 | 
			
		||||
 | 
			
		||||
    embassy_stm32::bind_interrupts!(struct Irqs {
 | 
			
		||||
        USART2 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART2>;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    #[shared]
 | 
			
		||||
    struct Shared {
 | 
			
		||||
        blink_freq: MillisDurationU32,
 | 
			
		||||
        tx_shared: UartTxShared,
 | 
			
		||||
        rx_transfer: Option<RxDmaTransferType>,
 | 
			
		||||
        blink_freq: Duration,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[local]
 | 
			
		||||
    struct Local {
 | 
			
		||||
        verif_reporter: VerificationReportCreator,
 | 
			
		||||
        leds: Leds,
 | 
			
		||||
        last_dir: Direction,
 | 
			
		||||
        curr_dir: Iter<'static, Direction>,
 | 
			
		||||
        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>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[init]
 | 
			
		||||
    fn init(cx: init::Context) -> (Shared, Local) {
 | 
			
		||||
        let mut rcc = cx.device.RCC.constrain();
 | 
			
		||||
        static DMA_BUF: static_cell::ConstStaticCell<[u8; TC_DMA_BUF_LEN]> =
 | 
			
		||||
            static_cell::ConstStaticCell::new([0; TC_DMA_BUF_LEN]);
 | 
			
		||||
 | 
			
		||||
        let p = embassy_stm32::init(Default::default());
 | 
			
		||||
 | 
			
		||||
        let (req_sender, req_receiver) = make_channel!(RequestWithTcId, 16);
 | 
			
		||||
        // Initialize the systick interrupt & obtain the token to prove that we did
 | 
			
		||||
        Mono::start(cx.core.SYST, 8_000_000);
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        // Set up monotonic timer.
 | 
			
		||||
        //let mono_timer = MonoTimer::new(cx.core.DWT, clocks, &mut cx.core.DCB);
 | 
			
		||||
        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();
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
        let (tx, rx) = uart.split();
 | 
			
		||||
        defmt::info!("Spawning tasks");
 | 
			
		||||
        blink::spawn().unwrap();
 | 
			
		||||
        blinky::spawn().unwrap();
 | 
			
		||||
        serial_tx_handler::spawn().unwrap();
 | 
			
		||||
 | 
			
		||||
        let verif_reporter = VerificationReportCreator::new(PUS_APID).unwrap();
 | 
			
		||||
        serial_rx_handler::spawn(req_sender).unwrap();
 | 
			
		||||
        req_handler::spawn(req_receiver).unwrap();
 | 
			
		||||
 | 
			
		||||
        (
 | 
			
		||||
            Shared {
 | 
			
		||||
                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),
 | 
			
		||||
                blink_freq: Duration::from_millis(DEFAULT_BLINK_FREQ_MS as u64),
 | 
			
		||||
            },
 | 
			
		||||
            Local {
 | 
			
		||||
                verif_reporter,
 | 
			
		||||
                leds,
 | 
			
		||||
                last_dir: Direction::North,
 | 
			
		||||
                curr_dir: Direction::iter(),
 | 
			
		||||
                tx,
 | 
			
		||||
                seq_count: u14::new(0),
 | 
			
		||||
                rx: rx.into_ring_buffered(DMA_BUF.take()),
 | 
			
		||||
                current_dir: satrs_stm32f3_disco_rtic::Direction::North,
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[task(local = [leds, curr_dir, last_dir], shared=[blink_freq])]
 | 
			
		||||
    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;
 | 
			
		||||
        };
 | 
			
		||||
    #[task(local = [leds, current_dir], shared=[blink_freq])]
 | 
			
		||||
    async fn blinky(mut cx: blinky::Context) {
 | 
			
		||||
        loop {
 | 
			
		||||
            match curr_dir.next() {
 | 
			
		||||
                Some(dir) => {
 | 
			
		||||
                    toggle_leds(dir);
 | 
			
		||||
                }
 | 
			
		||||
                None => {
 | 
			
		||||
                    *curr_dir = Direction::iter();
 | 
			
		||||
                    toggle_leds(curr_dir.next().unwrap());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            cx.local.leds.blink_next(cx.local.current_dir);
 | 
			
		||||
            let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
 | 
			
		||||
            Mono::delay(current_blink_freq).await;
 | 
			
		||||
            Mono::delay(MillisDurationU32::from_ticks(
 | 
			
		||||
                current_blink_freq.as_millis() as u32,
 | 
			
		||||
            ))
 | 
			
		||||
            .await;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[task(
 | 
			
		||||
        shared = [tx_shared],
 | 
			
		||||
        local = [
 | 
			
		||||
            tx,
 | 
			
		||||
            encoded_buf: [u8; TM_BUF_LEN] = [0; TM_BUF_LEN]
 | 
			
		||||
        ],
 | 
			
		||||
        shared = [],
 | 
			
		||||
    )]
 | 
			
		||||
    async fn serial_tx_handler(mut cx: serial_tx_handler::Context) {
 | 
			
		||||
    async fn serial_tx_handler(cx: serial_tx_handler::Context) {
 | 
			
		||||
        loop {
 | 
			
		||||
            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;
 | 
			
		||||
            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();
 | 
			
		||||
                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 = [
 | 
			
		||||
            verif_reporter,
 | 
			
		||||
            rx,
 | 
			
		||||
            read_buf: [u8; 128] = [0; 128],
 | 
			
		||||
            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(
 | 
			
		||||
        mut cx: serial_rx_handler::Context,
 | 
			
		||||
        received_packet: Vec<u8, MAX_TC_LEN>,
 | 
			
		||||
        cx: serial_rx_handler::Context,
 | 
			
		||||
        mut sender: Sender<'static, RequestWithTcId, 16>,
 | 
			
		||||
    ) {
 | 
			
		||||
        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);
 | 
			
		||||
        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();
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                    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);
 | 
			
		||||
                                        });
 | 
			
		||||
                                    Err(e) => {
 | 
			
		||||
                                        defmt::error!("error unpacking ccsds packet: {}", e);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                handle_completion_verification(
 | 
			
		||||
                                    started_token,
 | 
			
		||||
                                    cx.local.verif_reporter,
 | 
			
		||||
                                    cx.local.src_data_buf,
 | 
			
		||||
                                    cx.local.timestamp,
 | 
			
		||||
                                );
 | 
			
		||||
                            }
 | 
			
		||||
                            Err(e) => {
 | 
			
		||||
                                // TODO: Error handling: Send verification failure based on request error.
 | 
			
		||||
                                defmt::warn!("request error {}", e);
 | 
			
		||||
                                defmt::error!("cobs decoding error: {}", e);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Err(e) => {
 | 
			
		||||
                        defmt::warn!("Error unpacking PUS TC: {}", e);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    defmt::error!("uart read error: {}", e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Err(_) => {
 | 
			
		||||
                defmt::warn!("decoding error, can only process cobs encoded frames")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handle_ping_request(timestamp: &[u8]) {
 | 
			
		||||
        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_start_verification(
 | 
			
		||||
        accepted_token: VerificationToken<TcStateAccepted>,
 | 
			
		||||
        verif_reporter: &mut VerificationReportCreator,
 | 
			
		||||
        src_data_buf: &mut [u8],
 | 
			
		||||
        timestamp: &[u8],
 | 
			
		||||
    ) -> VerificationToken<TcStateStarted> {
 | 
			
		||||
        let (tm_creator, started_token) = verif_reporter
 | 
			
		||||
            .start_success(
 | 
			
		||||
                src_data_buf,
 | 
			
		||||
                accepted_token,
 | 
			
		||||
                SEQ_COUNT_PROVIDER.get(),
 | 
			
		||||
                0,
 | 
			
		||||
                ×tamp,
 | 
			
		||||
            )
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let result = send_tm(tm_creator);
 | 
			
		||||
        if let Err(e) = result {
 | 
			
		||||
            handle_tm_send_error(e);
 | 
			
		||||
        }
 | 
			
		||||
        started_token
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handle_completion_verification(
 | 
			
		||||
        started_token: VerificationToken<TcStateStarted>,
 | 
			
		||||
        verif_reporter: &mut VerificationReportCreator,
 | 
			
		||||
        src_data_buf: &mut [u8],
 | 
			
		||||
        timestamp: &[u8],
 | 
			
		||||
    #[task(shared = [blink_freq], local = [seq_count])]
 | 
			
		||||
    async fn req_handler(
 | 
			
		||||
        mut cx: req_handler::Context,
 | 
			
		||||
        mut receiver: Receiver<'static, RequestWithTcId, 16>,
 | 
			
		||||
    ) {
 | 
			
		||||
        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);
 | 
			
		||||
        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::error!("request receive error"),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[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
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    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(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[task(binds = USART2_EXTI26, shared = [rx_transfer, tx_shared])]
 | 
			
		||||
    fn serial_isr(mut cx: serial_isr::Context) {
 | 
			
		||||
    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()
 | 
			
		||||
        );
 | 
			
		||||
        cx.shared
 | 
			
		||||
            .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));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
            .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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										641
									
								
								embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										641
									
								
								embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -16,16 +16,17 @@ harness = false
 | 
			
		||||
[dependencies]
 | 
			
		||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
 | 
			
		||||
cortex-m-rt = "0.7"
 | 
			
		||||
defmt = "0.3"
 | 
			
		||||
defmt = "1"
 | 
			
		||||
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
 | 
			
		||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
 | 
			
		||||
panic-probe = { version = "1", features = ["print-defmt"] }
 | 
			
		||||
cortex-m-semihosting = "0.5.0"
 | 
			
		||||
# 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.11"
 | 
			
		||||
version = "0.12"
 | 
			
		||||
default-features = false
 | 
			
		||||
features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"]
 | 
			
		||||
 | 
			
		||||
@@ -39,12 +40,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.3"
 | 
			
		||||
defmt-test = "0.4"
 | 
			
		||||
 | 
			
		||||
# cargo build/run
 | 
			
		||||
[profile.dev]
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
 | 
			
		||||
use satrs::spacepackets::SpHeader;
 | 
			
		||||
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
 | 
			
		||||
use std::net::{IpAddr, SocketAddr, UdpSocket};
 | 
			
		||||
@@ -13,8 +13,7 @@ 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)),
 | 
			
		||||
        17,
 | 
			
		||||
        1,
 | 
			
		||||
        MessageTypeId::new(17, 1),
 | 
			
		||||
        &[],
 | 
			
		||||
        CreatorConfig::default(),
 | 
			
		||||
    );
 | 
			
		||||
@@ -35,9 +34,9 @@ fn main() {
 | 
			
		||||
        match res {
 | 
			
		||||
            Ok(_len) => {
 | 
			
		||||
                let pus_tm = PusTmReader::new(&buf, 7).expect("Parsing PUS TM failed");
 | 
			
		||||
                if pus_tm.service() == 17 && pus_tm.subservice() == 2 {
 | 
			
		||||
                if pus_tm.service_type_id() == 17 && pus_tm.message_subtype_id() == 2 {
 | 
			
		||||
                    println!("Received PUS Ping Reply TM[17,2]")
 | 
			
		||||
                } else if pus_tm.service() == 1 {
 | 
			
		||||
                } else if pus_tm.service_type_id() == 1 {
 | 
			
		||||
                    if pus_tm.source_data().is_empty() {
 | 
			
		||||
                        println!("Invalid verification TM, no source data");
 | 
			
		||||
                    }
 | 
			
		||||
@@ -46,28 +45,29 @@ fn main() {
 | 
			
		||||
                        println!("Invalid verification TM source data, less than 4 bytes")
 | 
			
		||||
                    }
 | 
			
		||||
                    let req_id = RequestId::from_bytes(src_data).unwrap();
 | 
			
		||||
                    if pus_tm.subservice() == 1 {
 | 
			
		||||
                    let subtype_id = pus_tm.message_subtype_id();
 | 
			
		||||
                    if subtype_id == 1 {
 | 
			
		||||
                        println!("Received TM[1,1] acceptance success for request ID {req_id}")
 | 
			
		||||
                    } else if pus_tm.subservice() == 2 {
 | 
			
		||||
                    } else if subtype_id == 2 {
 | 
			
		||||
                        println!("Received TM[1,2] acceptance failure for request ID {req_id}")
 | 
			
		||||
                    } else if pus_tm.subservice() == 3 {
 | 
			
		||||
                    } else if subtype_id == 3 {
 | 
			
		||||
                        println!("Received TM[1,3] start success for request ID {req_id}")
 | 
			
		||||
                    } else if pus_tm.subservice() == 4 {
 | 
			
		||||
                    } else if subtype_id == 4 {
 | 
			
		||||
                        println!("Received TM[1,2] start failure for request ID {req_id}")
 | 
			
		||||
                    } else if pus_tm.subservice() == 5 {
 | 
			
		||||
                    } else if subtype_id == 5 {
 | 
			
		||||
                        println!("Received TM[1,5] step success for request ID {req_id}")
 | 
			
		||||
                    } else if pus_tm.subservice() == 6 {
 | 
			
		||||
                    } else if subtype_id == 6 {
 | 
			
		||||
                        println!("Received TM[1,6] step failure for request ID {req_id}")
 | 
			
		||||
                    } else if pus_tm.subservice() == 7 {
 | 
			
		||||
                    } else if subtype_id == 7 {
 | 
			
		||||
                        println!("Received TM[1,7] completion success for request ID {req_id}")
 | 
			
		||||
                    } else if pus_tm.subservice() == 8 {
 | 
			
		||||
                    } else if subtype_id == 8 {
 | 
			
		||||
                        println!("Received TM[1,8] completion failure for request ID {req_id}");
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    println!(
 | 
			
		||||
                        "Received TM[{}, {}] with {} bytes",
 | 
			
		||||
                        pus_tm.service(),
 | 
			
		||||
                        pus_tm.subservice(),
 | 
			
		||||
                        pus_tm.service_type_id(),
 | 
			
		||||
                        pus_tm.message_subtype_id(),
 | 
			
		||||
                        size
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ 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};
 | 
			
		||||
use satrs::spacepackets::ecss::{hk, CreatorConfig, MessageTypeId};
 | 
			
		||||
use satrs::spacepackets::{ByteConversionError, SpHeader};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, new, Copy, Clone)]
 | 
			
		||||
@@ -54,8 +54,12 @@ impl PusHkHelper {
 | 
			
		||||
        hk_data_writer: &mut HkWriter,
 | 
			
		||||
        buf: &'b mut [u8],
 | 
			
		||||
    ) -> Result<PusTmCreator<'a, 'b>, ByteConversionError> {
 | 
			
		||||
        let sec_header =
 | 
			
		||||
            PusTmSecondaryHeader::new(3, hk::Subservice::TmHkPacket as u8, 0, 0, timestamp);
 | 
			
		||||
        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());
 | 
			
		||||
        buf[4..8].copy_from_slice(&set_id.to_be_bytes());
 | 
			
		||||
        let (_, second_half) = buf.split_at_mut(8);
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    use arbitrary_int::traits::Integer as _;
 | 
			
		||||
    use arbitrary_int::u14;
 | 
			
		||||
    use satrs::spacepackets::ecss::CreatorConfig;
 | 
			
		||||
    use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
 | 
			
		||||
    use satrs::{
 | 
			
		||||
        spacepackets::{
 | 
			
		||||
            ecss::{tc::PusTcCreator, WritablePusPacket},
 | 
			
		||||
@@ -182,9 +182,14 @@ 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, 17, 1, &[], CreatorConfig::default())
 | 
			
		||||
            .to_vec()
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        )
 | 
			
		||||
        .to_vec()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed");
 | 
			
		||||
        let client_addr = client.local_addr().unwrap();
 | 
			
		||||
        println!("{}", server_addr);
 | 
			
		||||
 
 | 
			
		||||
@@ -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.subservice();
 | 
			
		||||
        let subservice = tc.message_subtype_id();
 | 
			
		||||
        let user_data = tc.user_data();
 | 
			
		||||
        if user_data.len() < 8 {
 | 
			
		||||
            verif_reporter
 | 
			
		||||
@@ -277,7 +277,7 @@ mod tests {
 | 
			
		||||
    use satrs::pus::verification::test_util::TestVerificationReporter;
 | 
			
		||||
    use satrs::pus::{verification, EcssTcVecCacher};
 | 
			
		||||
    use satrs::request::MessageMetadata;
 | 
			
		||||
    use satrs::spacepackets::ecss::CreatorConfig;
 | 
			
		||||
    use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
 | 
			
		||||
    use satrs::tmtc::PacketAsVec;
 | 
			
		||||
    use satrs::ComponentId;
 | 
			
		||||
    use satrs::{
 | 
			
		||||
@@ -450,7 +450,7 @@ 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(8, 128);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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());
 | 
			
		||||
@@ -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(8, 128);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
 | 
			
		||||
        let action_id = 5_u32;
 | 
			
		||||
        let mut app_data: [u8; 8] = [0; 8];
 | 
			
		||||
        // Invalid ID, routing should fail.
 | 
			
		||||
@@ -518,7 +518,7 @@ mod tests {
 | 
			
		||||
            TEST_COMPONENT_ID_0.raw(),
 | 
			
		||||
            ActionRequestConverter::default(),
 | 
			
		||||
        );
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(8, 128);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
 | 
			
		||||
        let action_id = 5_u32;
 | 
			
		||||
        let mut app_data: [u8; 8] = [0; 8];
 | 
			
		||||
        // Invalid ID, routing should fail.
 | 
			
		||||
@@ -554,7 +554,7 @@ 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(8, 128);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(8, 128));
 | 
			
		||||
        let action_id = 5_u32;
 | 
			
		||||
        let mut app_data: [u8; 16] = [0; 16];
 | 
			
		||||
        // Invalid ID, routing should fail.
 | 
			
		||||
 
 | 
			
		||||
@@ -164,7 +164,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
 | 
			
		||||
                found: 4,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        let subservice = tc.subservice();
 | 
			
		||||
        let subservice = tc.message_subtype_id();
 | 
			
		||||
        let target_id_and_apid = UniqueApidTargetId::from_pus_tc(tc).expect("invalid tc format");
 | 
			
		||||
        let unique_id = u32::from_be_bytes(tc.user_data()[4..8].try_into().unwrap());
 | 
			
		||||
 | 
			
		||||
@@ -308,7 +308,7 @@ mod tests {
 | 
			
		||||
        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;
 | 
			
		||||
    use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
 | 
			
		||||
    use satrs::{
 | 
			
		||||
        hk::HkRequestVariant,
 | 
			
		||||
        pus::test_util::TEST_APID,
 | 
			
		||||
@@ -340,8 +340,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
        let hk_req = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            3,
 | 
			
		||||
            Subservice::TcGenerateOneShotHk as u8,
 | 
			
		||||
            MessageTypeId::new(3, Subservice::TcGenerateOneShotHk as u8),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -380,16 +379,14 @@ mod tests {
 | 
			
		||||
        };
 | 
			
		||||
        let tc0 = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            3,
 | 
			
		||||
            Subservice::TcEnableHkGeneration as u8,
 | 
			
		||||
            MessageTypeId::new(3, Subservice::TcEnableHkGeneration as u8),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        generic_check(&tc0);
 | 
			
		||||
        let tc1 = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            3,
 | 
			
		||||
            Subservice::TcEnableDiagGeneration as u8,
 | 
			
		||||
            MessageTypeId::new(3, Subservice::TcEnableDiagGeneration as u8),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -419,16 +416,14 @@ mod tests {
 | 
			
		||||
        };
 | 
			
		||||
        let tc0 = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            3,
 | 
			
		||||
            Subservice::TcDisableHkGeneration as u8,
 | 
			
		||||
            MessageTypeId::new(3, Subservice::TcDisableHkGeneration as u8),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        generic_check(&tc0);
 | 
			
		||||
        let tc1 = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            3,
 | 
			
		||||
            Subservice::TcDisableDiagGeneration as u8,
 | 
			
		||||
            MessageTypeId::new(3, Subservice::TcDisableDiagGeneration as u8),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -462,16 +457,14 @@ mod tests {
 | 
			
		||||
        };
 | 
			
		||||
        let tc0 = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            3,
 | 
			
		||||
            Subservice::TcModifyHkCollectionInterval as u8,
 | 
			
		||||
            MessageTypeId::new(3, Subservice::TcModifyHkCollectionInterval as u8),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        generic_check(&tc0);
 | 
			
		||||
        let tc1 = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            3,
 | 
			
		||||
            Subservice::TcModifyDiagCollectionInterval as u8,
 | 
			
		||||
            MessageTypeId::new(3, Subservice::TcModifyDiagCollectionInterval as u8),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
        let service = PusServiceId::try_from(pus_tc.service_type_id());
 | 
			
		||||
        let tc_in_memory: TcInMemory = if let Some(store_addr) = addr_opt {
 | 
			
		||||
            PacketInPool::new(sender_id, store_addr).into()
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -535,7 +535,7 @@ pub(crate) mod tests {
 | 
			
		||||
    use satrs::pus::test_util::TEST_COMPONENT_ID_0;
 | 
			
		||||
    use satrs::pus::{MpscTmAsVecSender, PusTmVariant};
 | 
			
		||||
    use satrs::request::RequestId;
 | 
			
		||||
    use satrs::spacepackets::ecss::CreatorConfig;
 | 
			
		||||
    use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
 | 
			
		||||
    use satrs::{
 | 
			
		||||
        pus::{
 | 
			
		||||
            verification::test_util::TestVerificationReporter, ActivePusRequestStd,
 | 
			
		||||
@@ -597,7 +597,7 @@ pub(crate) mod tests {
 | 
			
		||||
            time_stamp: &[u8],
 | 
			
		||||
        ) -> (verification::RequestId, ActivePusRequestStd) {
 | 
			
		||||
            let sp_header = SpHeader::new_from_apid(apid);
 | 
			
		||||
            let sec_header_dummy = PusTcSecondaryHeader::new_simple(0, 0);
 | 
			
		||||
            let sec_header_dummy = PusTcSecondaryHeader::new_simple(MessageTypeId::new(0, 0));
 | 
			
		||||
            let init = self.verif_reporter.start_verification(&PusTcCreator::new(
 | 
			
		||||
                sp_header,
 | 
			
		||||
                sec_header_dummy,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,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;
 | 
			
		||||
use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
 | 
			
		||||
use satrs_example::ids;
 | 
			
		||||
use std::sync::mpsc;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
@@ -81,8 +81,12 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
 | 
			
		||||
                    .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(200, Subservice::TmModeReply as u8, 0, 0, time_stamp);
 | 
			
		||||
                let sec_header = PusTmSecondaryHeader::new(
 | 
			
		||||
                    MessageTypeId::new(200, Subservice::TmModeReply as u8),
 | 
			
		||||
                    0,
 | 
			
		||||
                    0,
 | 
			
		||||
                    time_stamp,
 | 
			
		||||
                );
 | 
			
		||||
                let pus_tm = PusTmCreator::new(
 | 
			
		||||
                    sp_header,
 | 
			
		||||
                    sec_header,
 | 
			
		||||
@@ -154,7 +158,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
 | 
			
		||||
        verif_reporter: &impl VerificationReportingProvider,
 | 
			
		||||
        time_stamp: &[u8],
 | 
			
		||||
    ) -> Result<(ActivePusRequestStd, ModeRequest), Self::Error> {
 | 
			
		||||
        let subservice = tc.subservice();
 | 
			
		||||
        let subservice = tc.message_subtype_id();
 | 
			
		||||
        let user_data = tc.user_data();
 | 
			
		||||
        let not_enough_app_data = |expected: usize| {
 | 
			
		||||
            verif_reporter
 | 
			
		||||
@@ -302,7 +306,7 @@ mod tests {
 | 
			
		||||
    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;
 | 
			
		||||
    use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId};
 | 
			
		||||
    use satrs::{
 | 
			
		||||
        mode::{ModeAndSubmode, ModeReply, ModeRequest},
 | 
			
		||||
        pus::mode::Subservice,
 | 
			
		||||
@@ -326,7 +330,8 @@ mod tests {
 | 
			
		||||
        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(200, Subservice::TcReadMode as u8);
 | 
			
		||||
        let sec_header =
 | 
			
		||||
            PusTcSecondaryHeader::new_simple(MessageTypeId::new(200, Subservice::TcReadMode as u8));
 | 
			
		||||
        let mut app_data: [u8; 4] = [0; 4];
 | 
			
		||||
        app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
 | 
			
		||||
        let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
 | 
			
		||||
@@ -342,7 +347,8 @@ mod tests {
 | 
			
		||||
        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(200, Subservice::TcSetMode as u8);
 | 
			
		||||
        let sec_header =
 | 
			
		||||
            PusTcSecondaryHeader::new_simple(MessageTypeId::new(200, Subservice::TcSetMode as u8));
 | 
			
		||||
        let mut app_data: [u8; 4 + ModeAndSubmode::RAW_LEN] = [0; 4 + ModeAndSubmode::RAW_LEN];
 | 
			
		||||
        let mode_and_submode = ModeAndSubmode::new(2, 1);
 | 
			
		||||
        app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
 | 
			
		||||
@@ -368,7 +374,10 @@ mod tests {
 | 
			
		||||
        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(200, Subservice::TcAnnounceMode as u8);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
 | 
			
		||||
            200,
 | 
			
		||||
            Subservice::TcAnnounceMode as u8,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut app_data: [u8; 4] = [0; 4];
 | 
			
		||||
        app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.as_u32().to_be_bytes());
 | 
			
		||||
        let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
 | 
			
		||||
@@ -384,8 +393,10 @@ mod tests {
 | 
			
		||||
        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(200, Subservice::TcAnnounceModeRecursive as u8);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
 | 
			
		||||
            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());
 | 
			
		||||
        let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
 | 
			
		||||
 
 | 
			
		||||
@@ -122,7 +122,7 @@ impl DirectPusService for TestCustomServiceWrapper {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    let fail_data = [tc.subservice()];
 | 
			
		||||
                    let fail_data = [tc.message_subtype_id()];
 | 
			
		||||
                    self.handler
 | 
			
		||||
                        .service_helper
 | 
			
		||||
                        .verif_reporter()
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ impl GenericRequestRouter {
 | 
			
		||||
    ) {
 | 
			
		||||
        warn!(
 | 
			
		||||
            "Routing request for service {} failed: {error:?}",
 | 
			
		||||
            tc.service()
 | 
			
		||||
            tc.service_type_id()
 | 
			
		||||
        );
 | 
			
		||||
        let accepted_token: VerificationToken<TcStateAccepted> = active_request
 | 
			
		||||
            .token()
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ impl TmFunnelCommon {
 | 
			
		||||
        );
 | 
			
		||||
        let entry = self
 | 
			
		||||
            .msg_counter_map
 | 
			
		||||
            .entry(zero_copy_writer.service())
 | 
			
		||||
            .entry(zero_copy_writer.service_type_id())
 | 
			
		||||
            .or_insert(0);
 | 
			
		||||
        zero_copy_writer.set_msg_count(*entry);
 | 
			
		||||
        if *entry == u16::MAX {
 | 
			
		||||
@@ -76,8 +76,8 @@ impl TmFunnelCommon {
 | 
			
		||||
    fn packet_printout(tm: &PusTmZeroCopyWriter) {
 | 
			
		||||
        info!(
 | 
			
		||||
            "Sending PUS TM[{},{}] with APID {}",
 | 
			
		||||
            tm.service(),
 | 
			
		||||
            tm.subservice(),
 | 
			
		||||
            tm.service_type_id(),
 | 
			
		||||
            tm.message_subtype_id(),
 | 
			
		||||
            tm.apid()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ license = "Apache-2.0"
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
spacepackets = { version = ">=0.14, <=0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false }
 | 
			
		||||
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 }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ pub struct ResultU16 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ResultU16 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn new(group_id: u8, unique_id: u8) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            group_id,
 | 
			
		||||
@@ -21,18 +22,22 @@ impl ResultU16 {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn raw(&self) -> u16 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn raw(&self) -> u16 {
 | 
			
		||||
        ((self.group_id as u16) << 8) | self.unique_id as u16
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn group_id(&self) -> u8 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn group_id(&self) -> u8 {
 | 
			
		||||
        self.group_id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn unique_id(&self) -> u8 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const 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))
 | 
			
		||||
    }
 | 
			
		||||
@@ -51,6 +56,7 @@ impl From<ResultU16> for EcssEnumU16 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UnsignedEnum for ResultU16 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn size(&self) -> usize {
 | 
			
		||||
        core::mem::size_of::<u16>()
 | 
			
		||||
    }
 | 
			
		||||
@@ -67,12 +73,14 @@ impl UnsignedEnum for ResultU16 {
 | 
			
		||||
        Ok(self.size())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn value(&self) -> u64 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn value_raw(&self) -> u64 {
 | 
			
		||||
        self.raw() as u64
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EcssEnumeration for ResultU16 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn pfc(&self) -> u8 {
 | 
			
		||||
        16
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,7 @@ mod tests {
 | 
			
		||||
    use arbitrary_int::{u11, u14};
 | 
			
		||||
    use spacepackets::{
 | 
			
		||||
        CcsdsPacket, PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader,
 | 
			
		||||
        ecss::{CreatorConfig, tc::PusTcCreator},
 | 
			
		||||
        ecss::{CreatorConfig, MessageTypeId, tc::PusTcCreator},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    use crate::{ComponentId, encoding::tests::TcCacher};
 | 
			
		||||
@@ -139,7 +139,12 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_basic() {
 | 
			
		||||
        let sph = SpHeader::new_from_apid(TEST_APID_0);
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        let mut buffer: [u8; 32] = [0; 32];
 | 
			
		||||
        let packet_len = ping_tc
 | 
			
		||||
            .write_to_bytes(&mut buffer)
 | 
			
		||||
@@ -164,8 +169,14 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_multi_packet() {
 | 
			
		||||
        let sph = SpHeader::new_from_apid(TEST_APID_0);
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
        let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], CreatorConfig::default());
 | 
			
		||||
        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 mut buffer: [u8; 32] = [0; 32];
 | 
			
		||||
        let packet_len_ping = ping_tc
 | 
			
		||||
            .write_to_bytes(&mut buffer)
 | 
			
		||||
@@ -199,9 +210,15 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_multi_apid() {
 | 
			
		||||
        let sph = SpHeader::new_from_apid(TEST_APID_0);
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        let sph = SpHeader::new_from_apid(TEST_APID_1);
 | 
			
		||||
        let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], CreatorConfig::default());
 | 
			
		||||
        let action_tc =
 | 
			
		||||
            PusTcCreator::new_simple(sph, MessageTypeId::new(8, 0), &[], CreatorConfig::default());
 | 
			
		||||
        let mut buffer: [u8; 32] = [0; 32];
 | 
			
		||||
        let packet_len_ping = ping_tc
 | 
			
		||||
            .write_to_bytes(&mut buffer)
 | 
			
		||||
@@ -231,15 +248,13 @@ mod tests {
 | 
			
		||||
    fn test_split_packet_multi() {
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_0),
 | 
			
		||||
            17,
 | 
			
		||||
            1,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        let action_tc = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_1),
 | 
			
		||||
            8,
 | 
			
		||||
            0,
 | 
			
		||||
            MessageTypeId::new(8, 0),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -274,8 +289,7 @@ mod tests {
 | 
			
		||||
    fn test_one_split_packet() {
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_0),
 | 
			
		||||
            17,
 | 
			
		||||
            1,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -386,7 +386,7 @@ impl UnsignedEnum for EventU32 {
 | 
			
		||||
        self.base.write_to_bytes(self.raw(), buf, self.size())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn value(&self) -> u64 {
 | 
			
		||||
    fn value_raw(&self) -> u64 {
 | 
			
		||||
        self.raw().into()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -445,7 +445,7 @@ impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> {
 | 
			
		||||
    delegate!(to self.event {
 | 
			
		||||
        fn size(&self) -> usize;
 | 
			
		||||
        fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
 | 
			
		||||
        fn value(&self) -> u64;
 | 
			
		||||
        fn value_raw(&self) -> u64;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -558,7 +558,7 @@ impl UnsignedEnum for EventU16 {
 | 
			
		||||
        self.base.write_to_bytes(self.raw(), buf, self.size())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn value(&self) -> u64 {
 | 
			
		||||
    fn value_raw(&self) -> u64 {
 | 
			
		||||
        self.raw().into()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -611,7 +611,7 @@ impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> {
 | 
			
		||||
    delegate!(to self.event {
 | 
			
		||||
        fn size(&self) -> usize;
 | 
			
		||||
        fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
 | 
			
		||||
        fn value(&self) -> u64;
 | 
			
		||||
        fn value_raw(&self) -> u64;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -241,7 +241,7 @@ mod tests {
 | 
			
		||||
    use hashbrown::HashSet;
 | 
			
		||||
    use spacepackets::{
 | 
			
		||||
        CcsdsPacket, PacketId, SpHeader,
 | 
			
		||||
        ecss::{CreatorConfig, WritablePusPacket, tc::PusTcCreator},
 | 
			
		||||
        ecss::{CreatorConfig, MessageTypeId, WritablePusPacket, tc::PusTcCreator},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    use crate::{
 | 
			
		||||
@@ -334,8 +334,7 @@ mod tests {
 | 
			
		||||
        });
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_0),
 | 
			
		||||
            17,
 | 
			
		||||
            1,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -370,8 +369,7 @@ mod tests {
 | 
			
		||||
        let mut total_tm_len = 0;
 | 
			
		||||
        let verif_tm = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_0),
 | 
			
		||||
            1,
 | 
			
		||||
            1,
 | 
			
		||||
            MessageTypeId::new(1, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -380,8 +378,7 @@ mod tests {
 | 
			
		||||
        tm_source.add_tm(&tm_0);
 | 
			
		||||
        let verif_tm = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_1),
 | 
			
		||||
            1,
 | 
			
		||||
            3,
 | 
			
		||||
            MessageTypeId::new(1, 3),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -431,8 +428,7 @@ mod tests {
 | 
			
		||||
        // Send telecommands
 | 
			
		||||
        let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_0),
 | 
			
		||||
            17,
 | 
			
		||||
            1,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
@@ -442,8 +438,7 @@ mod tests {
 | 
			
		||||
            .expect("writing to TCP server failed");
 | 
			
		||||
        let action_tc = PusTcCreator::new_simple(
 | 
			
		||||
            SpHeader::new_from_apid(TEST_APID_1),
 | 
			
		||||
            8,
 | 
			
		||||
            0,
 | 
			
		||||
            MessageTypeId::new(8, 0),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ use std::vec::Vec;
 | 
			
		||||
/// use satrs::ComponentId;
 | 
			
		||||
/// use satrs::tmtc::PacketHandler;
 | 
			
		||||
/// use spacepackets::SpHeader;
 | 
			
		||||
/// use spacepackets::ecss::tc::{PusTcCreator, CreatorConfig};
 | 
			
		||||
/// use spacepackets::ecss::tc::{MessageTypeId, PusTcCreator, CreatorConfig};
 | 
			
		||||
/// use arbitrary_int::u11;
 | 
			
		||||
///
 | 
			
		||||
/// const UDP_SERVER_ID: ComponentId = 0x05;
 | 
			
		||||
@@ -36,7 +36,7 @@ use std::vec::Vec;
 | 
			
		||||
/// 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, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
/// let pus_tc = PusTcCreator::new_simple(sph, MessageTypeId::new(17, 1), &[], CreatorConfig::default());
 | 
			
		||||
/// // Can not fail.
 | 
			
		||||
/// let ping_tc_raw = pus_tc.to_vec().unwrap();
 | 
			
		||||
///
 | 
			
		||||
@@ -131,8 +131,8 @@ mod tests {
 | 
			
		||||
    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;
 | 
			
		||||
@@ -169,7 +169,12 @@ 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, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
        let pus_tc = PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &[],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        let len = pus_tc
 | 
			
		||||
            .write_to_bytes(&mut buf)
 | 
			
		||||
            .expect("Error writing PUS TC packet");
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -110,8 +111,12 @@ impl EventReportCreator {
 | 
			
		||||
            src_data_len += aux_data.len();
 | 
			
		||||
        }
 | 
			
		||||
        source_buffer_large_enough(src_data_buf.len(), src_data_len)?;
 | 
			
		||||
        let sec_header =
 | 
			
		||||
            PusTmSecondaryHeader::new(5, subservice.into(), 0, self.dest_id, time_stamp);
 | 
			
		||||
        let sec_header = PusTmSecondaryHeader::new(
 | 
			
		||||
            MessageTypeId::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();
 | 
			
		||||
 
 | 
			
		||||
@@ -409,8 +409,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(), 5);
 | 
			
		||||
        assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
 | 
			
		||||
        assert_eq!(tm.service_type_id(), 5);
 | 
			
		||||
        assert_eq!(tm.message_subtype_id(), 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 +437,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(), 5);
 | 
			
		||||
        assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
 | 
			
		||||
        assert_eq!(tm.service_type_id(), 5);
 | 
			
		||||
        assert_eq!(tm.message_subtype_id(), 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());
 | 
			
		||||
 
 | 
			
		||||
@@ -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.subservice();
 | 
			
		||||
        let subservice = tc.message_subtype_id();
 | 
			
		||||
        let srv = Subservice::try_from(subservice);
 | 
			
		||||
        if srv.is_err() {
 | 
			
		||||
            return Ok(DirectPusPacketHandlerResult::CustomSubservice(
 | 
			
		||||
                tc.subservice(),
 | 
			
		||||
                tc.message_subtype_id(),
 | 
			
		||||
                ecss_tc_and_token.token,
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
@@ -121,7 +121,7 @@ impl<
 | 
			
		||||
            | Subservice::TmMediumSeverityReport
 | 
			
		||||
            | Subservice::TmHighSeverityReport => {
 | 
			
		||||
                return Err(PusPacketHandlingError::RequestConversion(
 | 
			
		||||
                    GenericConversionError::WrongService(tc.subservice()),
 | 
			
		||||
                    GenericConversionError::WrongService(tc.message_subtype_id()),
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
            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::{
 | 
			
		||||
@@ -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(5, subservice as u8);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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(5, 200);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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,8 +331,10 @@ 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(5, Subservice::TcEnableEventGeneration as u8);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
 | 
			
		||||
            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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1364,10 +1364,10 @@ pub mod tests {
 | 
			
		||||
            let mut timestamp = [0; 7];
 | 
			
		||||
            timestamp.clone_from_slice(&tm.timestamp()[0..7]);
 | 
			
		||||
            Self {
 | 
			
		||||
                subservice: PusPacket::subservice(tm),
 | 
			
		||||
                subservice: PusPacket::message_subtype_id(tm),
 | 
			
		||||
                apid: tm.apid(),
 | 
			
		||||
                seq_count: tm.seq_count(),
 | 
			
		||||
                msg_counter: tm.msg_counter(),
 | 
			
		||||
                msg_counter: tm.msg_type_counter(),
 | 
			
		||||
                dest_id: tm.dest_id(),
 | 
			
		||||
                timestamp: timestamp.to_vec(),
 | 
			
		||||
            }
 | 
			
		||||
@@ -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(&tm), 1);
 | 
			
		||||
            assert_eq!(PusPacket::subservice(&tm), subservice);
 | 
			
		||||
            assert_eq!(PusPacket::service_type_id(&tm), 1);
 | 
			
		||||
            assert_eq!(PusPacket::message_subtype_id(&tm), subservice);
 | 
			
		||||
            assert_eq!(tm.apid(), TEST_APID);
 | 
			
		||||
            let req_id =
 | 
			
		||||
                RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
 | 
			
		||||
@@ -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(&tm), 1);
 | 
			
		||||
            assert_eq!(PusPacket::subservice(&tm), subservice);
 | 
			
		||||
            assert_eq!(PusPacket::service_type_id(&tm), 1);
 | 
			
		||||
            assert_eq!(PusPacket::message_subtype_id(&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() != SERVICE {
 | 
			
		||||
            if tc.service_type_id() != SERVICE {
 | 
			
		||||
                return Err(PusPacketHandlingError::RequestConversion(
 | 
			
		||||
                    GenericConversionError::WrongService(tc.service()),
 | 
			
		||||
                    GenericConversionError::WrongService(tc.service_type_id()),
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
            Ok(())
 | 
			
		||||
 
 | 
			
		||||
@@ -272,14 +272,16 @@ pub trait PusSchedulerProvider {
 | 
			
		||||
        pus_tc: &(impl IsPusTelecommand + PusPacket + GenericPusTcSecondaryHeader),
 | 
			
		||||
        pool: &mut (impl PoolProvider + ?Sized),
 | 
			
		||||
    ) -> Result<TcInfo, ScheduleError> {
 | 
			
		||||
        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(
 | 
			
		||||
        if PusPacket::service_type_id(pus_tc) != 11 {
 | 
			
		||||
            return Err(ScheduleError::WrongService(PusPacket::service_type_id(
 | 
			
		||||
                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);
 | 
			
		||||
        }
 | 
			
		||||
@@ -299,7 +301,9 @@ pub trait PusSchedulerProvider {
 | 
			
		||||
        pool: &mut (impl PoolProvider + ?Sized),
 | 
			
		||||
    ) -> Result<TcInfo, ScheduleError> {
 | 
			
		||||
        let check_tc = PusTcReader::new(tc)?;
 | 
			
		||||
        if PusPacket::service(&check_tc) == 11 && PusPacket::subservice(&check_tc) == 4 {
 | 
			
		||||
        if PusPacket::service_type_id(&check_tc) == 11
 | 
			
		||||
            && PusPacket::message_subtype_id(&check_tc) == 4
 | 
			
		||||
        {
 | 
			
		||||
            return Err(ScheduleError::NestedScheduledTc);
 | 
			
		||||
        }
 | 
			
		||||
        let req_id = RequestId::from_tc(&check_tc);
 | 
			
		||||
@@ -487,7 +491,9 @@ pub mod alloc_mod {
 | 
			
		||||
            pool: &mut (impl PoolProvider + ?Sized),
 | 
			
		||||
        ) -> Result<TcInfo, ScheduleError> {
 | 
			
		||||
            let check_tc = PusTcReader::new(tc)?;
 | 
			
		||||
            if PusPacket::service(&check_tc) == 11 && PusPacket::subservice(&check_tc) == 4 {
 | 
			
		||||
            if PusPacket::service_type_id(&check_tc) == 11
 | 
			
		||||
                && PusPacket::message_subtype_id(&check_tc) == 4
 | 
			
		||||
            {
 | 
			
		||||
                return Err(ScheduleError::NestedScheduledTc);
 | 
			
		||||
            }
 | 
			
		||||
            let req_id = RequestId::from_tc(&check_tc);
 | 
			
		||||
@@ -865,7 +871,7 @@ mod tests {
 | 
			
		||||
    use arbitrary_int::traits::Integer;
 | 
			
		||||
    use arbitrary_int::{u11, u14};
 | 
			
		||||
    use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
 | 
			
		||||
    use spacepackets::ecss::{CreatorConfig, WritablePusPacket};
 | 
			
		||||
    use spacepackets::ecss::{CreatorConfig, MessageTypeId, WritablePusPacket};
 | 
			
		||||
    use spacepackets::time::{TimeWriter, UnixTime, cds};
 | 
			
		||||
    use spacepackets::{PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader};
 | 
			
		||||
    use std::time::Duration;
 | 
			
		||||
@@ -889,17 +895,32 @@ 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, 11, 4, &buf[..len_app_data], CreatorConfig::default())
 | 
			
		||||
        PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(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, 12, 4, &buf[..len_app_data], CreatorConfig::default())
 | 
			
		||||
        PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(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, 11, 5, &buf[..len_app_data], CreatorConfig::default())
 | 
			
		||||
        PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(11, 5),
 | 
			
		||||
            &buf[..len_app_data],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn double_wrapped_time_tagged_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
 | 
			
		||||
@@ -910,15 +931,18 @@ 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, 11, 4, &[], CreatorConfig::default());
 | 
			
		||||
        let inner_time_tagged_tc = PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(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,
 | 
			
		||||
            11,
 | 
			
		||||
            4,
 | 
			
		||||
            MessageTypeId::new(11, 4),
 | 
			
		||||
            &buf[..len_time_stamp + packet_len],
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        )
 | 
			
		||||
@@ -926,12 +950,22 @@ 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, 11, 4, &[], CreatorConfig::default())
 | 
			
		||||
        PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(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, 17, 1, app_data, CreatorConfig::default())
 | 
			
		||||
        PusTcCreator::new_simple(
 | 
			
		||||
            sph,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn ping_tc_to_store(
 | 
			
		||||
@@ -1096,7 +1130,7 @@ mod tests {
 | 
			
		||||
        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(17, 1);
 | 
			
		||||
        let mut sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
 | 
			
		||||
        sec_header.source_id = src_id_to_set;
 | 
			
		||||
        let ping_tc =
 | 
			
		||||
            PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
 | 
			
		||||
@@ -2047,7 +2081,7 @@ mod tests {
 | 
			
		||||
            PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
 | 
			
		||||
        let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
 | 
			
		||||
        let mut buf: [u8; 64] = [0; 64];
 | 
			
		||||
        let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
 | 
			
		||||
@@ -2069,7 +2103,7 @@ mod tests {
 | 
			
		||||
            PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
 | 
			
		||||
        let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
 | 
			
		||||
        let mut buf: [u8; 16] = [0; 16];
 | 
			
		||||
        let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
 | 
			
		||||
@@ -2098,7 +2132,7 @@ mod tests {
 | 
			
		||||
            PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
 | 
			
		||||
        let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
 | 
			
		||||
        let mut buf: [u8; 64] = [0; 64];
 | 
			
		||||
        generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap();
 | 
			
		||||
 
 | 
			
		||||
@@ -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::subservice(&tc);
 | 
			
		||||
        let subservice = PusPacket::message_subtype_id(&tc);
 | 
			
		||||
        let standard_subservice = scheduling::Subservice::try_from(subservice);
 | 
			
		||||
        if standard_subservice.is_err() {
 | 
			
		||||
            return Ok(DirectPusPacketHandlerResult::CustomSubservice(
 | 
			
		||||
@@ -266,7 +266,7 @@ mod tests {
 | 
			
		||||
    use spacepackets::SpHeader;
 | 
			
		||||
    use spacepackets::ecss::scheduling::Subservice;
 | 
			
		||||
    use spacepackets::ecss::tc::PusTcSecondaryHeader;
 | 
			
		||||
    use spacepackets::ecss::{CreatorConfig, WritablePusPacket};
 | 
			
		||||
    use spacepackets::ecss::{CreatorConfig, MessageTypeId, 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(11, subservice as u8);
 | 
			
		||||
        let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(11, subservice as u8));
 | 
			
		||||
        let enable_scheduling =
 | 
			
		||||
            PusTcCreator::new(reply_header, tc_header, &[0; 7], 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(17, 1);
 | 
			
		||||
        let mut sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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");
 | 
			
		||||
@@ -447,7 +447,10 @@ mod tests {
 | 
			
		||||
        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(11, Subservice::TcInsertActivity as u8);
 | 
			
		||||
        sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(
 | 
			
		||||
            11,
 | 
			
		||||
            Subservice::TcInsertActivity as u8,
 | 
			
		||||
        ));
 | 
			
		||||
        let enable_scheduling = PusTcCreator::new(
 | 
			
		||||
            reply_header,
 | 
			
		||||
            sec_header,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ use arbitrary_int::traits::Integer as _;
 | 
			
		||||
use arbitrary_int::u14;
 | 
			
		||||
use spacepackets::SpHeader;
 | 
			
		||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
 | 
			
		||||
use spacepackets::ecss::{CreatorConfig, PusPacket};
 | 
			
		||||
use spacepackets::ecss::{CreatorConfig, MessageTypeId, PusPacket};
 | 
			
		||||
use std::sync::mpsc;
 | 
			
		||||
 | 
			
		||||
use super::verification::{VerificationReporter, VerificationReportingProvider};
 | 
			
		||||
@@ -59,10 +59,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() != 17 {
 | 
			
		||||
            return Err(GenericConversionError::WrongService(tc.service()).into());
 | 
			
		||||
        if tc.service_type_id() != 17 {
 | 
			
		||||
            return Err(GenericConversionError::WrongService(tc.service_type_id()).into());
 | 
			
		||||
        }
 | 
			
		||||
        if tc.subservice() == 1 {
 | 
			
		||||
        if tc.message_subtype_id() == 1 {
 | 
			
		||||
            let opt_started_token = match self.service_helper.verif_reporter().start_success(
 | 
			
		||||
                &self.service_helper.common.tm_sender,
 | 
			
		||||
                ecss_tc_and_token.token,
 | 
			
		||||
@@ -82,7 +82,7 @@ impl<
 | 
			
		||||
                u14::ZERO,
 | 
			
		||||
                0,
 | 
			
		||||
            );
 | 
			
		||||
            let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp);
 | 
			
		||||
            let tc_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), time_stamp);
 | 
			
		||||
            let ping_reply =
 | 
			
		||||
                PusTmCreator::new(reply_header, tc_header, &[], CreatorConfig::default());
 | 
			
		||||
            if let Err(e) = self
 | 
			
		||||
@@ -104,7 +104,7 @@ impl<
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return Ok(DirectPusPacketHandlerResult::CustomSubservice(
 | 
			
		||||
                tc.subservice(),
 | 
			
		||||
                tc.message_subtype_id(),
 | 
			
		||||
                ecss_tc_and_token.token,
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
@@ -160,7 +160,7 @@ mod tests {
 | 
			
		||||
    use spacepackets::SpHeader;
 | 
			
		||||
    use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
 | 
			
		||||
    use spacepackets::ecss::tm::PusTmReader;
 | 
			
		||||
    use spacepackets::ecss::{CreatorConfig, PusPacket};
 | 
			
		||||
    use spacepackets::ecss::{CreatorConfig, MessageTypeId, PusPacket};
 | 
			
		||||
    use spacepackets::time::{TimeWriter, cds};
 | 
			
		||||
 | 
			
		||||
    use super::PusService17TestHandler;
 | 
			
		||||
@@ -292,7 +292,7 @@ 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(17, 1);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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 +311,8 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
        // Ping reply
 | 
			
		||||
        let tm = test_harness.read_next_tm();
 | 
			
		||||
        assert_eq!(tm.service(), 17);
 | 
			
		||||
        assert_eq!(tm.subservice(), 2);
 | 
			
		||||
        assert_eq!(tm.service_type_id(), 17);
 | 
			
		||||
        assert_eq!(tm.message_subtype_id(), 2);
 | 
			
		||||
        assert!(tm.user_data().is_empty());
 | 
			
		||||
 | 
			
		||||
        // TM completion
 | 
			
		||||
@@ -348,7 +348,7 @@ mod tests {
 | 
			
		||||
    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(3, 1);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(3, 1));
 | 
			
		||||
        let ping_tc =
 | 
			
		||||
            PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
 | 
			
		||||
        let token = test_harness.start_verification(&ping_tc);
 | 
			
		||||
@@ -370,7 +370,7 @@ mod tests {
 | 
			
		||||
    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(17, 200);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 200));
 | 
			
		||||
        let ping_tc =
 | 
			
		||||
            PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
 | 
			
		||||
        let token = test_harness.start_verification(&ping_tc);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
//! use satrs::request::UniqueApidTargetId;
 | 
			
		||||
//! use spacepackets::ecss::PusPacket;
 | 
			
		||||
//! use spacepackets::SpHeader;
 | 
			
		||||
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader, CreatorConfig};
 | 
			
		||||
//! use spacepackets::ecss::tc::{MessageTypeId, PusTcCreator, PusTcSecondaryHeader, CreatorConfig};
 | 
			
		||||
//! use spacepackets::ecss::tm::PusTmReader;
 | 
			
		||||
//! use arbitrary_int::{u11, u21};
 | 
			
		||||
//!
 | 
			
		||||
@@ -41,7 +41,7 @@
 | 
			
		||||
//! let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
 | 
			
		||||
//! let mut  reporter = VerificationReporter::new(TEST_COMPONENT_ID.id(), &cfg);
 | 
			
		||||
//!
 | 
			
		||||
//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
 | 
			
		||||
//! let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
 | 
			
		||||
//! let pus_tc_0 = PusTcCreator::new_no_app_data(
 | 
			
		||||
//!     SpHeader::new_from_apid(TEST_APID),
 | 
			
		||||
//!     tc_header,
 | 
			
		||||
@@ -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.subservice(), 1);
 | 
			
		||||
//!         assert_eq!(pus_tm.message_subtype_id(), 1);
 | 
			
		||||
//!     } else if packet_idx == 1 {
 | 
			
		||||
//!         assert_eq!(pus_tm.subservice(), 3);
 | 
			
		||||
//!         assert_eq!(pus_tm.message_subtype_id(), 3);
 | 
			
		||||
//!     } else if packet_idx == 2 {
 | 
			
		||||
//!         assert_eq!(pus_tm.subservice(), 7);
 | 
			
		||||
//!         assert_eq!(pus_tm.message_subtype_id(), 7);
 | 
			
		||||
//!     }
 | 
			
		||||
//!     packet_idx += 1;
 | 
			
		||||
//! }
 | 
			
		||||
@@ -94,7 +94,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use spacepackets::SpHeader;
 | 
			
		||||
use spacepackets::ecss::tc::IsPusTelecommand;
 | 
			
		||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
 | 
			
		||||
use spacepackets::ecss::{CreatorConfig, EcssEnumeration};
 | 
			
		||||
use spacepackets::ecss::{CreatorConfig, EcssEnumeration, MessageTypeId};
 | 
			
		||||
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl};
 | 
			
		||||
 | 
			
		||||
pub use spacepackets::ecss::verification::*;
 | 
			
		||||
@@ -507,9 +507,7 @@ impl VerificationReportCreator {
 | 
			
		||||
        self.dest_id = dest_id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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 {
 | 
			
		||||
    pub fn read_request_id(&self, pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> RequestId {
 | 
			
		||||
        RequestId::new(pus_tc)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -822,8 +820,12 @@ impl VerificationReportCreator {
 | 
			
		||||
        time_stamp: &'time [u8],
 | 
			
		||||
        source_data_len: usize,
 | 
			
		||||
    ) -> PusTmCreator<'time, 'src_data> {
 | 
			
		||||
        let tm_sec_header =
 | 
			
		||||
            PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, time_stamp);
 | 
			
		||||
        let tm_sec_header = PusTmSecondaryHeader::new(
 | 
			
		||||
            MessageTypeId::new(1, subservice),
 | 
			
		||||
            msg_counter,
 | 
			
		||||
            self.dest_id,
 | 
			
		||||
            time_stamp,
 | 
			
		||||
        );
 | 
			
		||||
        PusTmCreator::new(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            tm_sec_header,
 | 
			
		||||
@@ -897,7 +899,7 @@ pub mod alloc_mod {
 | 
			
		||||
    > {
 | 
			
		||||
        owner_id: ComponentId,
 | 
			
		||||
        source_data_buf: RefCell<alloc::vec::Vec<u8>>,
 | 
			
		||||
        pub reporter_creator: VerificationReportCreator,
 | 
			
		||||
        pub report_creator: VerificationReportCreator,
 | 
			
		||||
        pub tm_hook: VerificationHookInstance,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -913,7 +915,7 @@ pub mod alloc_mod {
 | 
			
		||||
                        + cfg.fail_code_field_width
 | 
			
		||||
                        + cfg.max_fail_data_len
 | 
			
		||||
                ]),
 | 
			
		||||
                reporter_creator: reporter,
 | 
			
		||||
                report_creator: reporter,
 | 
			
		||||
                tm_hook: DummyVerificationHook::default(),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -937,7 +939,7 @@ pub mod alloc_mod {
 | 
			
		||||
                        + cfg.fail_code_field_width
 | 
			
		||||
                        + cfg.max_fail_data_len
 | 
			
		||||
                ]),
 | 
			
		||||
                reporter_creator: reporter,
 | 
			
		||||
                report_creator: reporter,
 | 
			
		||||
                tm_hook,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -946,9 +948,7 @@ pub mod alloc_mod {
 | 
			
		||||
            &self,
 | 
			
		||||
            pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
 | 
			
		||||
        ) -> VerificationToken<TcStateNone> {
 | 
			
		||||
            VerificationToken::<TcStateNone>::new(
 | 
			
		||||
                VerificationReportCreator::read_request_id_from_tc(pus_tc),
 | 
			
		||||
            )
 | 
			
		||||
            VerificationToken::<TcStateNone>::new(self.report_creator.read_request_id(pus_tc))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn start_verification_with_req_id(
 | 
			
		||||
@@ -959,7 +959,7 @@ pub mod alloc_mod {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        delegate!(
 | 
			
		||||
            to self.reporter_creator {
 | 
			
		||||
            to self.report_creator {
 | 
			
		||||
                pub fn set_apid(&mut self, apid: u11);
 | 
			
		||||
                pub fn apid(&self) -> u11;
 | 
			
		||||
                pub fn dest_id(&self) -> u16;
 | 
			
		||||
@@ -976,7 +976,7 @@ pub mod alloc_mod {
 | 
			
		||||
        for VerificationReporter<VerificationHookInstance>
 | 
			
		||||
    {
 | 
			
		||||
        delegate!(
 | 
			
		||||
            to self.reporter_creator {
 | 
			
		||||
            to self.report_creator {
 | 
			
		||||
                fn set_apid(&mut self, apid: Apid);
 | 
			
		||||
                fn apid(&self) -> Apid;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1002,7 +1002,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<VerificationToken<TcStateAccepted>, EcssTmtcError> {
 | 
			
		||||
            let mut source_data_buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .acceptance_success(
 | 
			
		||||
                    source_data_buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
@@ -1025,7 +1025,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<(), EcssTmtcError> {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .acceptance_failure(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
@@ -1050,7 +1050,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<VerificationToken<TcStateStarted>, EcssTmtcError> {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .start_success(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
@@ -1076,7 +1076,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<(), EcssTmtcError> {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .start_failure(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
@@ -1102,7 +1102,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<(), EcssTmtcError> {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .step_success(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
@@ -1129,7 +1129,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<(), EcssTmtcError> {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .step_failure(buf.as_mut_slice(), token, u14::ZERO, 0, params)
 | 
			
		||||
                .map_err(PusError::ByteConversion)?;
 | 
			
		||||
            self.tm_hook.modify_tm(&mut tm_creator);
 | 
			
		||||
@@ -1150,7 +1150,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<(), EcssTmtcError> {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .completion_success(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
@@ -1176,7 +1176,7 @@ pub mod alloc_mod {
 | 
			
		||||
        ) -> Result<(), EcssTmtcError> {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .report_creator
 | 
			
		||||
                .completion_failure(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
@@ -1427,7 +1427,7 @@ pub mod test_util {
 | 
			
		||||
                token.request_id(),
 | 
			
		||||
                VerificationReportInfo::AcceptanceFailure(FailureData {
 | 
			
		||||
                    sender: self.owner_id(),
 | 
			
		||||
                    error_enum: params.failure_code.value(),
 | 
			
		||||
                    error_enum: params.failure_code.value_raw(),
 | 
			
		||||
                    fail_data: params.failure_data.to_vec(),
 | 
			
		||||
                    time_stamp: params.time_stamp.to_vec(),
 | 
			
		||||
                }),
 | 
			
		||||
@@ -1464,7 +1464,7 @@ pub mod test_util {
 | 
			
		||||
                token.request_id(),
 | 
			
		||||
                VerificationReportInfo::StartedFailure(FailureData {
 | 
			
		||||
                    sender: self.owner_id(),
 | 
			
		||||
                    error_enum: params.failure_code.value(),
 | 
			
		||||
                    error_enum: params.failure_code.value_raw(),
 | 
			
		||||
                    fail_data: params.failure_data.to_vec(),
 | 
			
		||||
                    time_stamp: params.time_stamp.to_vec(),
 | 
			
		||||
                }),
 | 
			
		||||
@@ -1486,7 +1486,7 @@ pub mod test_util {
 | 
			
		||||
                        sender: self.owner_id(),
 | 
			
		||||
                        time_stamp: time_stamp.to_vec(),
 | 
			
		||||
                    },
 | 
			
		||||
                    step: step.value() as u16,
 | 
			
		||||
                    step: step.value_raw() as u16,
 | 
			
		||||
                },
 | 
			
		||||
            ));
 | 
			
		||||
            Ok(())
 | 
			
		||||
@@ -1502,7 +1502,7 @@ pub mod test_util {
 | 
			
		||||
                token.request_id(),
 | 
			
		||||
                VerificationReportInfo::StepFailure(FailureData {
 | 
			
		||||
                    sender: self.owner_id(),
 | 
			
		||||
                    error_enum: params.common.failure_code.value(),
 | 
			
		||||
                    error_enum: params.common.failure_code.value_raw(),
 | 
			
		||||
                    fail_data: params.common.failure_data.to_vec(),
 | 
			
		||||
                    time_stamp: params.common.time_stamp.to_vec(),
 | 
			
		||||
                }),
 | 
			
		||||
@@ -1536,7 +1536,7 @@ pub mod test_util {
 | 
			
		||||
                token.request_id(),
 | 
			
		||||
                VerificationReportInfo::CompletionFailure(FailureData {
 | 
			
		||||
                    sender: self.owner_id(),
 | 
			
		||||
                    error_enum: params.failure_code.value(),
 | 
			
		||||
                    error_enum: params.failure_code.value_raw(),
 | 
			
		||||
                    fail_data: params.failure_data.to_vec(),
 | 
			
		||||
                    time_stamp: params.time_stamp.to_vec(),
 | 
			
		||||
                }),
 | 
			
		||||
@@ -1731,8 +1731,8 @@ pub mod tests {
 | 
			
		||||
    use arbitrary_int::{u11, u14};
 | 
			
		||||
    use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
 | 
			
		||||
    use spacepackets::ecss::{
 | 
			
		||||
        CreatorConfig, EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, PusError, PusPacket,
 | 
			
		||||
        WritablePusPacket,
 | 
			
		||||
        CreatorConfig, EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, MessageTypeId,
 | 
			
		||||
        PusError, PusPacket, WritablePusPacket,
 | 
			
		||||
    };
 | 
			
		||||
    use spacepackets::util::UnsignedEnum;
 | 
			
		||||
    use spacepackets::{ByteConversionError, SpHeader};
 | 
			
		||||
@@ -1783,7 +1783,7 @@ pub mod tests {
 | 
			
		||||
                    panic!("TestSender: Can not deal with addresses");
 | 
			
		||||
                }
 | 
			
		||||
                PusTmVariant::Direct(tm) => {
 | 
			
		||||
                    assert_eq!(PusPacket::service(&tm), 1);
 | 
			
		||||
                    assert_eq!(PusPacket::service_type_id(&tm), 1);
 | 
			
		||||
                    assert!(!tm.source_data().is_empty());
 | 
			
		||||
                    let mut time_stamp = [0; 7];
 | 
			
		||||
                    time_stamp.clone_from_slice(&tm.timestamp()[0..7]);
 | 
			
		||||
@@ -2102,7 +2102,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(17, 1);
 | 
			
		||||
        let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
 | 
			
		||||
        PusTcCreator::new(sph, tc_header, &[], CreatorConfig::default())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -501,7 +501,7 @@ mod tests {
 | 
			
		||||
    use spacepackets::{
 | 
			
		||||
        ByteConversionError, SpHeader,
 | 
			
		||||
        ecss::{
 | 
			
		||||
            CreatorConfig,
 | 
			
		||||
            CreatorConfig, MessageTypeId,
 | 
			
		||||
            tc::{PusTcCreator, PusTcSecondaryHeader},
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
@@ -540,8 +540,12 @@ 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, 17, 1, &app_data, CreatorConfig::default());
 | 
			
		||||
        let pus_tc = PusTcCreator::new_simple(
 | 
			
		||||
            sp_header,
 | 
			
		||||
            MessageTypeId::new(17, 1),
 | 
			
		||||
            &app_data,
 | 
			
		||||
            CreatorConfig::default(),
 | 
			
		||||
        );
 | 
			
		||||
        let id = UniqueApidTargetId::from_pus_tc(&pus_tc).unwrap();
 | 
			
		||||
        assert_eq!(id.apid.value(), 0x111);
 | 
			
		||||
        assert_eq!(id.unique_id.value(), 1);
 | 
			
		||||
@@ -550,7 +554,7 @@ mod tests {
 | 
			
		||||
    #[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(17, 1);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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());
 | 
			
		||||
 
 | 
			
		||||
@@ -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,7 +50,10 @@ 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(service, subservice, &self.cds_short_buf);
 | 
			
		||||
        let tc_header = PusTmSecondaryHeader::new_simple(
 | 
			
		||||
            MessageTypeId::new(service, subservice),
 | 
			
		||||
            &self.cds_short_buf,
 | 
			
		||||
        );
 | 
			
		||||
        PusTmCreator::new(
 | 
			
		||||
            reply_header,
 | 
			
		||||
            tc_header,
 | 
			
		||||
@@ -73,8 +76,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(), 17);
 | 
			
		||||
        assert_eq!(tm.subservice(), 1);
 | 
			
		||||
        assert_eq!(tm.service_type_id(), 17);
 | 
			
		||||
        assert_eq!(tm.message_subtype_id(), 1);
 | 
			
		||||
        assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
 | 
			
		||||
        assert_eq!(tm.seq_count().value(), 25);
 | 
			
		||||
        assert_eq!(tm.timestamp(), [64, 0, 0, 0, 0, 0, 0])
 | 
			
		||||
@@ -84,8 +87,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(), 17);
 | 
			
		||||
        assert_eq!(tm.subservice(), 1);
 | 
			
		||||
        assert_eq!(tm.service_type_id(), 17);
 | 
			
		||||
        assert_eq!(tm.message_subtype_id(), 1);
 | 
			
		||||
        assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
 | 
			
		||||
        assert_eq!(tm.seq_count().value(), 25);
 | 
			
		||||
        assert_eq!(tm.timestamp().len(), 7);
 | 
			
		||||
 
 | 
			
		||||
@@ -107,8 +107,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(), 5);
 | 
			
		||||
                    assert_eq!(tm.subservice(), 1);
 | 
			
		||||
                    assert_eq!(tm.service_type_id(), 5);
 | 
			
		||||
                    assert_eq!(tm.message_subtype_id(), 1);
 | 
			
		||||
                    let src_data = tm.source_data();
 | 
			
		||||
                    assert!(!src_data.is_empty());
 | 
			
		||||
                    assert_eq!(src_data.len(), 4);
 | 
			
		||||
@@ -137,8 +137,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(), 5);
 | 
			
		||||
                    assert_eq!(tm.subservice(), 2);
 | 
			
		||||
                    assert_eq!(tm.service_type_id(), 5);
 | 
			
		||||
                    assert_eq!(tm.message_subtype_id(), 2);
 | 
			
		||||
                    let src_data = tm.source_data();
 | 
			
		||||
                    assert!(!src_data.is_empty());
 | 
			
		||||
                    assert_eq!(src_data.len(), 12);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,9 @@ 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, WritablePusPacket};
 | 
			
		||||
    use spacepackets::ecss::{
 | 
			
		||||
        CreatorConfig, EcssEnumU8, EcssEnumU16, MessageTypeId, WritablePusPacket,
 | 
			
		||||
    };
 | 
			
		||||
    use std::sync::RwLock;
 | 
			
		||||
    use std::thread;
 | 
			
		||||
    use std::time::Duration;
 | 
			
		||||
@@ -60,7 +62,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(17, 1);
 | 
			
		||||
            let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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
 | 
			
		||||
@@ -70,7 +72,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(5, 1);
 | 
			
		||||
            let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(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
 | 
			
		||||
@@ -164,11 +166,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.subservice()];
 | 
			
		||||
                    let content = vec![pus_tm.service_type_id()];
 | 
			
		||||
                    verif_map.insert(req_id, content);
 | 
			
		||||
                } else {
 | 
			
		||||
                    let content = verif_map.get_mut(&req_id).unwrap();
 | 
			
		||||
                    content.push(pus_tm.subservice())
 | 
			
		||||
                    content.push(pus_tm.message_subtype_id())
 | 
			
		||||
                }
 | 
			
		||||
                packet_counter += 1;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ use satrs::{
 | 
			
		||||
};
 | 
			
		||||
use spacepackets::{
 | 
			
		||||
    CcsdsPacket, PacketId, SpHeader,
 | 
			
		||||
    ecss::{CreatorConfig, WritablePusPacket, tc::PusTcCreator},
 | 
			
		||||
    ecss::{CreatorConfig, MessageTypeId, WritablePusPacket, tc::PusTcCreator},
 | 
			
		||||
};
 | 
			
		||||
use std::{collections::VecDeque, sync::Arc, vec::Vec};
 | 
			
		||||
 | 
			
		||||
@@ -222,7 +222,8 @@ 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, 1, 1, &[], CreatorConfig::default());
 | 
			
		||||
    let verif_tm =
 | 
			
		||||
        PusTcCreator::new_simple(sph, MessageTypeId::new(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();
 | 
			
		||||
@@ -271,7 +272,12 @@ fn test_ccsds_server() {
 | 
			
		||||
 | 
			
		||||
    // Send ping telecommand.
 | 
			
		||||
    let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::new(0), 0);
 | 
			
		||||
    let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
    let ping_tc = PusTcCreator::new_simple(
 | 
			
		||||
        sph,
 | 
			
		||||
        MessageTypeId::new(17, 1),
 | 
			
		||||
        &[],
 | 
			
		||||
        CreatorConfig::default(),
 | 
			
		||||
    );
 | 
			
		||||
    let tc_0 = ping_tc.to_vec().expect("packet creation failed");
 | 
			
		||||
    stream
 | 
			
		||||
        .write_all(&tc_0)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user