Compare commits

..

30 Commits

Author SHA1 Message Date
2eaa78dfbc this actually works!
All checks were successful
Rust/sat-rs/pipeline/head This commit looks good
2024-05-25 13:46:14 +02:00
a6d9bee5df Merge branch 'sim-mgm-update' into serialization-prototyping
All checks were successful
Rust/sat-rs/pipeline/head This commit looks good
2024-05-25 13:09:25 +02:00
a77bbfa953 Merge remote-tracking branch 'origin/main' into serialization-prototyping 2024-05-25 13:08:54 +02:00
4c67bcdde1 clean up serializatio ntest code 2024-05-25 13:08:32 +02:00
a710b30013 Merge remote-tracking branch 'origin/main' into serialization-prototyping
All checks were successful
Rust/sat-rs/pipeline/head This commit looks good
2024-05-25 12:31:51 +02:00
29783b2b07 introduce new HK helper
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-25 12:29:44 +02:00
2a2a3a3eab
PCDU switch set TM handling
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-22 18:48:46 +02:00
2507469e68
continue PCDU integration
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit
2024-05-22 18:34:37 +02:00
b4febefa33 introduce switch handling for MGM
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-22 16:48:51 +02:00
fe60cb9ccf continue integrating power subsystem
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-19 17:33:37 +02:00
27e88ed7f7
fix tests
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-18 18:45:42 +02:00
295fed9a72
continue PCDU handler
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit
2024-05-18 18:39:25 +02:00
8e89c8dd66
compiles again
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit
2024-05-18 17:58:54 +02:00
cb0a65c4d4 continue PCDU
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit
2024-05-18 14:08:42 +02:00
3db54da3df Merge remote-tracking branch 'origin/main' into sim-mgm-update
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit
2024-05-18 12:49:20 +02:00
15fcb17363 continue PCDU handler
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-16 16:28:22 +02:00
8728c7ebea continued sample PCDU handler
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-12 14:23:42 +02:00
7606767f63 the PCDU handler is already required
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-11 19:11:41 +02:00
37b32a9008 try to make MGM set HK data work
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-10 17:55:11 +02:00
9e096193dd clean up python commander a bit 2024-05-10 17:21:59 +02:00
43bd77eef0
check that MGM data conversion works
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-10 15:33:43 +02:00
a4888bce01
add first MGM device unittests 2024-05-09 21:38:56 +02:00
6e5b70af34
basic tests for SIM client
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-09 13:23:40 +02:00
d1476eb770
added basic tests for pytmtc app
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-09 11:41:11 +02:00
783388aa6f
pytmtc as regular package now
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-09 11:07:08 +02:00
4a8db6b26a
fix tests
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-05-08 21:08:41 +02:00
b86c2eb1d1
added some test stubs
Some checks are pending
Rust/sat-rs/pipeline/head Build started...
2024-05-08 21:02:16 +02:00
fe4126f7e2
first connection success
Some checks failed
Rust/sat-rs/pipeline/head There was a failure building this commit
2024-05-08 20:55:56 +02:00
c20163b10a
start integrating sim in example
Some checks failed
Rust/sat-rs/pipeline/head There was a failure building this commit
2024-05-08 20:38:45 +02:00
b970154488 add serialization prototyping
All checks were successful
Rust/sat-rs/pipeline/head This commit looks good
2024-04-26 10:01:29 +02:00
111 changed files with 7108 additions and 7486 deletions

View File

@ -11,9 +11,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- run: cargo check - run: cargo check --release
# Check example with static pool configuration
- run: cargo check -p satrs-example --no-default-features
test: test:
name: Run Tests name: Run Tests
@ -39,7 +37,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf" targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
- run: cargo check -p satrs --target=${{matrix.target}} --no-default-features - run: cargo check -p satrs --release --target=${{matrix.target}} --no-default-features
fmt: fmt:
name: Check formatting name: Check formatting

View File

@ -10,5 +10,7 @@ members = [
exclude = [ exclude = [
"embedded-examples/stm32f3-disco-rtic", "embedded-examples/stm32f3-disco-rtic",
"embedded-examples/stm32h7-nucleo-rtic", "embedded-examples/stm32h7-rtic",
"serialization-prototyping",
] ]

View File

@ -37,16 +37,13 @@ This project currently contains following crates:
* [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example): * [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example):
Example of a simple example on-board software using various sat-rs components which can be run Example of a simple example on-board software using various sat-rs components which can be run
on a host computer or on any system with a standard runtime like a Raspberry Pi. on a host computer or on any system with a standard runtime like a Raspberry Pi.
* [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim):
Mini-Simulator based on [asynchronix](https://github.com/asynchronics/asynchronix) which
simulates some physical devices for the `satrs-example` application device handlers.
* [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-mib): * [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-mib):
Components to build a mission information base from the on-board software directly. Components to build a mission information base from the on-board software directly.
* [`satrs-stm32f3-disco-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32f3-disco-rtic): * [`satrs-stm32f3-disco-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/satrs-stm32f3-disco-rtic):
Example of a simple example using low-level sat-rs components on a bare-metal system Example of a simple example using low-level sat-rs components on a bare-metal system
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic) with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
framework on the STM32F3-Discovery device. framework on the STM32F3-Discovery device.
* [`satrs-stm32h-nucleo-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32h7-nucleo-rtic): * [`satrs-stm32h-nucleo-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/satrs-stm32h7-nucleo-rtic):
Example of a simple example using sat-rs components on a bare-metal system Example of a simple example using sat-rs components on a bare-metal system
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic) with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
framework on the STM32H743ZIT device. framework on the STM32H743ZIT device.
@ -72,10 +69,6 @@ Currently this library has the following flight heritage:
[flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/). [flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/).
The application is strongly based on the sat-rs example application. You can find the repository The application is strongly based on the sat-rs example application. You can find the repository
of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs). of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs).
- Development and use of a sat-rs-based [demonstration on-board software](https://egit.irs.uni-stuttgart.de/rust/eurosim-obsw)
alongside a Flight System Simulator in the context of a
[Bachelors Thesis](https://www.researchgate.net/publication/380785984_Design_and_Development_of_a_Hardware-in-the-Loop_EuroSim_Demonstrator)
at [Airbus Netherlands](https://www.airbusdefenceandspacenetherlands.nl/).
# Coverage # Coverage

View File

@ -1,3 +0,0 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open

View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]] [[package]]
name = "accelerometer" name = "accelerometer"
@ -12,10 +12,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "autocfg" name = "atomic-polyfill"
version = "1.4.0" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]] [[package]]
name = "bare-metal" name = "bare-metal"
@ -68,7 +77,7 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
dependencies = [ dependencies = [
"rustc_version 0.4.1", "rustc_version 0.4.0",
] ]
[[package]] [[package]]
@ -79,9 +88,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.39" version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [ dependencies = [
"num-traits", "num-traits",
] ]
@ -94,12 +103,8 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
[[package]] [[package]]
name = "cobs" name = "cobs"
version = "0.3.0" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "cortex-m" name = "cortex-m"
@ -116,22 +121,22 @@ dependencies = [
[[package]] [[package]]
name = "cortex-m-rt" name = "cortex-m-rt"
version = "0.7.5" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
dependencies = [ dependencies = [
"cortex-m-rt-macros", "cortex-m-rt-macros",
] ]
[[package]] [[package]]
name = "cortex-m-rt-macros" name = "cortex-m-rt-macros"
version = "0.7.5" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 1.0.109",
] ]
[[package]] [[package]]
@ -160,15 +165,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]] [[package]]
name = "critical-section" name = "critical-section"
version = "1.2.0" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.20.10" version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@ -176,33 +181,33 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.20.10" version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.20.10" version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
name = "defmt" name = "defmt"
version = "0.3.10" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130" checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"defmt-macros", "defmt-macros",
@ -220,22 +225,22 @@ dependencies = [
[[package]] [[package]]
name = "defmt-macros" name = "defmt-macros"
version = "0.4.0" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6" checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
dependencies = [ dependencies = [
"defmt-parser", "defmt-parser",
"proc-macro-error2", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
name = "defmt-parser" name = "defmt-parser"
version = "0.4.1" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3" checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
dependencies = [ dependencies = [
"thiserror", "thiserror",
] ]
@ -260,7 +265,7 @@ checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
@ -282,7 +287,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
@ -310,15 +315,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]]
name = "embedded-hal-async"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
dependencies = [
"embedded-hal 1.0.0",
]
[[package]] [[package]]
name = "embedded-time" name = "embedded-time"
version = "0.12.1" version = "0.12.1"
@ -330,23 +326,23 @@ dependencies = [
[[package]] [[package]]
name = "enumset" name = "enumset"
version = "1.1.5" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
dependencies = [ dependencies = [
"enumset_derive", "enumset_derive",
] ]
[[package]] [[package]]
name = "enumset_derive" name = "enumset_derive"
version = "0.10.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
@ -372,21 +368,21 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.31" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.31" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.31" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-task", "futures-task",
@ -430,9 +426,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]] [[package]]
name = "heapless" name = "heapless"
@ -452,9 +448,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.7.1" version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -559,22 +555,22 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.7.3" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
dependencies = [ dependencies = [
"num_enum_derive", "num_enum_derive",
] ]
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.7.3" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
@ -595,9 +591,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]] [[package]]
name = "pin-utils" name = "pin-utils"
@ -606,47 +602,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "portable-atomic" name = "proc-macro-error"
version = "1.10.0" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [ dependencies = [
"proc-macro-error-attr",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109",
"version_check",
] ]
[[package]] [[package]]
name = "proc-macro-error2" name = "proc-macro-error-attr"
version = "2.0.1" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [ dependencies = [
"proc-macro-error-attr2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "version_check",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.93" version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.38" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -662,14 +654,14 @@ dependencies = [
[[package]] [[package]]
name = "rtic" name = "rtic"
version = "2.1.2" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1" checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
dependencies = [ dependencies = [
"atomic-polyfill",
"bare-metal 1.0.0", "bare-metal 1.0.0",
"cortex-m", "cortex-m",
"critical-section", "critical-section",
"portable-atomic",
"rtic-core", "rtic-core",
"rtic-macros", "rtic-macros",
] ]
@ -691,40 +683,38 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
[[package]] [[package]]
name = "rtic-macros" name = "rtic-macros"
version = "2.1.2" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80" checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"proc-macro-error2", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "2.0.3" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748" checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
dependencies = [ dependencies = [
"atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0",
"fugit", "fugit",
"portable-atomic",
"rtic-time", "rtic-time",
] ]
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "2.0.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614" checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]
@ -740,11 +730,11 @@ dependencies = [
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [ dependencies = [
"semver 1.0.25", "semver 1.0.23",
] ]
[[package]] [[package]]
@ -753,7 +743,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "866fcae3b683ccc37b5ad77982483a0ee01d5dc408dea5aad2117ad404b60fe1" checksum = "866fcae3b683ccc37b5ad77982483a0ee01d5dc408dea5aad2117ad404b60fe1"
dependencies = [ dependencies = [
"cobs 0.2.3", "cobs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"crc", "crc",
"defmt", "defmt",
"delegate", "delegate",
@ -779,7 +769,7 @@ dependencies = [
name = "satrs-stm32f3-disco-rtic" name = "satrs-stm32f3-disco-rtic"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cobs 0.3.0", "cobs 0.2.3 (git+https://github.com/robamu/cobs.rs.git?branch=all_features)",
"cortex-m", "cortex-m",
"cortex-m-rt", "cortex-m-rt",
"cortex-m-semihosting", "cortex-m-semihosting",
@ -808,9 +798,9 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.25" version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]] [[package]]
name = "semver-parser" name = "semver-parser"
@ -932,9 +922,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.96" version = "2.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -943,22 +933,22 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.11" version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.11" version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]
[[package]] [[package]]
@ -969,9 +959,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.16" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]] [[package]]
name = "usb-device" name = "usb-device"
@ -987,9 +977,9 @@ checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "void" name = "void"
@ -1008,9 +998,9 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.7.35" version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"zerocopy-derive", "zerocopy-derive",
@ -1018,11 +1008,11 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.7.35" version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.65",
] ]

View File

@ -22,11 +22,12 @@ version = "2"
features = ["thumbv7-backend"] features = ["thumbv7-backend"]
[dependencies.rtic-monotonics] [dependencies.rtic-monotonics]
version = "2" version = "1"
features = ["cortex-m-systick"] features = ["cortex-m-systick"]
[dependencies.cobs] [dependencies.cobs]
version = "0.3" git = "https://github.com/robamu/cobs.rs.git"
branch = "all_features"
default-features = false default-features = false
[dependencies.stm32f3xx-hal] [dependencies.stm32f3xx-hal]

View File

@ -15,8 +15,8 @@ use rtic::app;
use heapless::{mpmc::Q8, Vec}; use heapless::{mpmc::Q8, Vec};
#[allow(unused_imports)] #[allow(unused_imports)]
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32}; use rtic_monotonics::systick::fugit::{MillisDurationU32, TimerInstantU32};
use rtic_monotonics::systick::prelude::*; use rtic_monotonics::systick::ExtU32;
use satrs::seq_count::SequenceCountProviderCore; use satrs::seq_count::SequenceCountProviderCore;
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket}; use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
use stm32f3xx_hal::dma::dma1; use stm32f3xx_hal::dma::dma1;
@ -245,6 +245,8 @@ pub fn convert_pus_tc_to_request(
mod app { mod app {
use super::*; use super::*;
use core::slice::Iter; use core::slice::Iter;
use rtic_monotonics::systick::Systick;
use rtic_monotonics::Monotonic;
use satrs::pus::verification::{TcStateStarted, VerificationReportCreator}; use satrs::pus::verification::{TcStateStarted, VerificationReportCreator};
use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE}; use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE};
#[allow(unused_imports)] #[allow(unused_imports)]
@ -257,8 +259,6 @@ mod app {
#[allow(dead_code)] #[allow(dead_code)]
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>; type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
systick_monotonic!(Mono, 1000);
#[shared] #[shared]
struct Shared { struct Shared {
blink_freq: MillisDurationU32, blink_freq: MillisDurationU32,
@ -279,7 +279,8 @@ mod app {
let mut rcc = cx.device.RCC.constrain(); let mut rcc = cx.device.RCC.constrain();
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
Mono::start(cx.core.SYST, 8_000_000); let systick_mono_token = rtic_monotonics::create_systick_token!();
Systick::start(cx.core.SYST, 8_000_000, systick_mono_token);
let mut flash = cx.device.FLASH.constrain(); let mut flash = cx.device.FLASH.constrain();
let clocks = rcc let clocks = rcc
@ -393,7 +394,7 @@ mod app {
} }
} }
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current); let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
Mono::delay(current_blink_freq).await; Systick::delay(current_blink_freq).await;
} }
} }
@ -411,14 +412,15 @@ mod app {
if is_idle { if is_idle {
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed); let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
if let Some(last_completed) = last_completed { if let Some(last_completed) = last_completed {
let elapsed_ms = (Mono::now() - last_completed).to_millis(); let elapsed_ms = (Systick::now() - last_completed).to_millis();
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS { if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
Mono::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis()).await; Systick::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis())
.await;
} }
} }
} else { } else {
// Check for completion after 1 ms // Check for completion after 1 ms
Mono::delay(1.millis()).await; Systick::delay(1.millis()).await;
continue; continue;
} }
if let Some(vec) = TM_REQUESTS.dequeue() { if let Some(vec) = TM_REQUESTS.dequeue() {
@ -457,11 +459,11 @@ mod app {
UartTxState::Transmitting(_) => (), UartTxState::Transmitting(_) => (),
}); });
// Check for completion after 1 ms // Check for completion after 1 ms
Mono::delay(1.millis()).await; Systick::delay(1.millis()).await;
continue; continue;
} }
// Nothing to do, and we are idle. // Nothing to do, and we are idle.
Mono::delay(TX_HANDLER_FREQ_MS.millis()).await; Systick::delay(TX_HANDLER_FREQ_MS.millis()).await;
} }
} }
@ -655,7 +657,7 @@ mod app {
tx_shared.state = UartTxState::Idle(Some(TxIdle { tx, dma_channel })); 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 // We cache the last completed time to ensure that there is a minimum delay between consecutive
// transferred packets. // transferred packets.
tx_shared.last_completed = Some(Mono::now()); tx_shared.last_completed = Some(Systick::now());
} }
} }
}); });

View File

@ -1,12 +1,21 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]] [[package]]
name = "bare-metal" name = "bare-metal"
@ -14,7 +23,7 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
dependencies = [ dependencies = [
"rustc_version", "rustc_version 0.2.3",
] ]
[[package]] [[package]]
@ -23,12 +32,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "bitfield" name = "bitfield"
version = "0.13.2" version = "0.13.2"
@ -61,18 +64,8 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cobs" name = "cobs"
version = "0.3.0" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror",
]
[[package]]
name = "const-default"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
[[package]] [[package]]
name = "cortex-m" name = "cortex-m"
@ -89,22 +82,22 @@ dependencies = [
[[package]] [[package]]
name = "cortex-m-rt" name = "cortex-m-rt"
version = "0.7.5" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
dependencies = [ dependencies = [
"cortex-m-rt-macros", "cortex-m-rt-macros",
] ]
[[package]] [[package]]
name = "cortex-m-rt-macros" name = "cortex-m-rt-macros"
version = "0.7.5" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 1.0.109",
] ]
[[package]] [[package]]
@ -133,15 +126,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]] [[package]]
name = "critical-section" name = "critical-section"
version = "1.2.0" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]] [[package]]
name = "defmt" name = "defmt"
version = "0.3.10" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130" checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"defmt-macros", "defmt-macros",
@ -159,22 +152,22 @@ dependencies = [
[[package]] [[package]]
name = "defmt-macros" name = "defmt-macros"
version = "0.4.0" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6" checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
dependencies = [ dependencies = [
"defmt-parser", "defmt-parser",
"proc-macro-error2", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.64",
] ]
[[package]] [[package]]
name = "defmt-parser" name = "defmt-parser"
version = "0.4.1" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3" checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
dependencies = [ dependencies = [
"thiserror", "thiserror",
] ]
@ -199,41 +192,39 @@ checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.64",
] ]
[[package]] [[package]]
name = "delegate" name = "delegate"
version = "0.13.2" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297806318ef30ad066b15792a8372858020ae3ca2e414ee6c2133b1eb9e9e945" checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 1.0.109",
] ]
[[package]] [[package]]
name = "derive-new" name = "derive-new"
version = "0.7.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.64",
] ]
[[package]] [[package]]
name = "embedded-alloc" name = "embedded-alloc"
version = "0.6.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd" checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815"
dependencies = [ dependencies = [
"const-default",
"critical-section", "critical-section",
"linked_list_allocator", "linked_list_allocator",
"rlsf",
] ]
[[package]] [[package]]
@ -309,21 +300,21 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.31" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.31" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.31" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-task", "futures-task",
@ -337,6 +328,15 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "hash32" name = "hash32"
version = "0.3.1" version = "0.3.1"
@ -348,9 +348,22 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32 0.2.1",
"rustc_version 0.4.0",
"spin",
"stable_deref_trait",
]
[[package]] [[package]]
name = "heapless" name = "heapless"
@ -359,32 +372,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [ dependencies = [
"defmt", "defmt",
"hash32", "hash32 0.3.1",
"stable_deref_trait", "stable_deref_trait",
] ]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.7.1" version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
] ]
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]] [[package]]
name = "linked_list_allocator" name = "linked_list_allocator"
version = "0.10.5" version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "managed" name = "managed"
version = "0.8.0" version = "0.8.0"
@ -417,22 +434,22 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.7.3" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
dependencies = [ dependencies = [
"num_enum_derive", "num_enum_derive",
] ]
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.7.3" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.64",
] ]
[[package]] [[package]]
@ -453,9 +470,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]] [[package]]
name = "pin-utils" name = "pin-utils"
@ -465,72 +482,62 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.10.0" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]] [[package]]
name = "proc-macro-error-attr2" name = "proc-macro-error"
version = "2.0.0" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [ dependencies = [
"proc-macro-error-attr",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109",
"version_check",
] ]
[[package]] [[package]]
name = "proc-macro-error2" name = "proc-macro-error-attr"
version = "2.0.1" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [ dependencies = [
"proc-macro-error-attr2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "version_check",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.93" version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.38" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rlsf"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf"
dependencies = [
"cfg-if",
"const-default",
"libc",
"svgbobdoc",
]
[[package]] [[package]]
name = "rtic" name = "rtic"
version = "2.1.2" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1" checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
dependencies = [ dependencies = [
"atomic-polyfill",
"bare-metal 1.0.0", "bare-metal 1.0.0",
"cortex-m", "cortex-m",
"critical-section", "critical-section",
"portable-atomic",
"rtic-core", "rtic-core",
"rtic-macros", "rtic-macros",
] ]
@ -552,27 +559,28 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
[[package]] [[package]]
name = "rtic-macros" name = "rtic-macros"
version = "2.1.2" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80" checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"proc-macro-error2", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.64",
] ]
[[package]] [[package]]
name = "rtic-monotonics" name = "rtic-monotonics"
version = "2.0.3" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748" checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
dependencies = [ dependencies = [
"atomic-polyfill",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
"embedded-hal 1.0.0",
"fugit", "fugit",
"portable-atomic",
"rtic-time", "rtic-time",
] ]
@ -587,21 +595,18 @@ dependencies = [
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-async", "embedded-hal-async",
"embedded-hal-bus", "embedded-hal-bus",
"heapless", "heapless 0.8.0",
"portable-atomic", "portable-atomic",
"rtic-common", "rtic-common",
] ]
[[package]] [[package]]
name = "rtic-time" name = "rtic-time"
version = "2.0.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614" checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"embedded-hal 1.0.0",
"embedded-hal-async",
"fugit",
"futures-util", "futures-util",
"rtic-common", "rtic-common",
] ]
@ -612,7 +617,16 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [ dependencies = [
"semver", "semver 0.9.0",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.23",
] ]
[[package]] [[package]]
@ -624,22 +638,20 @@ dependencies = [
"defmt", "defmt",
"delegate", "delegate",
"derive-new", "derive-new",
"heapless", "heapless 0.7.17",
"num-traits", "num-traits",
"num_enum", "num_enum",
"paste", "paste",
"satrs-shared", "satrs-shared",
"smallvec", "smallvec",
"spacepackets", "spacepackets",
"static_cell",
"thiserror",
] ]
[[package]] [[package]]
name = "satrs-shared" name = "satrs-shared"
version = "0.2.1" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f10b962c131d0bcb0de06fefa5d4716cbd7f836167f90229f7f38405dc573bd3" checksum = "6042477018c2d43fffccaaa5099bc299a58485139b4d31c5b276889311e474f1"
dependencies = [ dependencies = [
"spacepackets", "spacepackets",
] ]
@ -664,6 +676,12 @@ dependencies = [
"stm32h7xx-hal", "stm32h7xx-hal",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "semver" name = "semver"
version = "0.9.0" version = "0.9.0"
@ -673,6 +691,12 @@ dependencies = [
"semver-parser", "semver-parser",
] ]
[[package]]
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]] [[package]]
name = "semver-parser" name = "semver-parser"
version = "0.7.0" version = "0.7.0"
@ -695,41 +719,39 @@ dependencies = [
"byteorder", "byteorder",
"cfg-if", "cfg-if",
"defmt", "defmt",
"heapless", "heapless 0.8.0",
"managed", "managed",
] ]
[[package]] [[package]]
name = "spacepackets" name = "spacepackets"
version = "0.13.0" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "591d42bfa6af2ab308e5efd8f0870e2d0eb5dd19a141bdcb7da731f5ff9cc11c" checksum = "e85574d113a06312010c0ba51aadccd4ba2806231ebe9a49fc6473d0534d8696"
dependencies = [ dependencies = [
"crc", "crc",
"defmt", "defmt",
"delegate", "delegate",
"num-traits", "num-traits",
"num_enum", "num_enum",
"paste",
"thiserror",
"zerocopy", "zerocopy",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_cell"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "stm32h7" name = "stm32h7"
version = "0.15.1" version = "0.15.1"
@ -762,19 +784,6 @@ dependencies = [
"void", "void",
] ]
[[package]]
name = "svgbobdoc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50"
dependencies = [
"base64",
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-width",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -788,9 +797,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.96" version = "2.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -799,35 +808,29 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.11" version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.11" version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.64",
] ]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.16" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]] [[package]]
name = "vcell" name = "vcell"
@ -835,6 +838,12 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "void" name = "void"
version = "1.0.2" version = "1.0.2"
@ -852,20 +861,21 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.14" version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [ dependencies = [
"byteorder",
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.14" version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.64",
] ]

View File

@ -21,11 +21,11 @@ defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
panic-probe = { version = "0.3", features = ["print-defmt"] } panic-probe = { version = "0.3", features = ["print-defmt"] }
cortex-m-semihosting = "0.5.0" cortex-m-semihosting = "0.5.0"
stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] } stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] }
embedded-alloc = "0.6" embedded-alloc = "0.5"
rtic-sync = { version = "1", features = ["defmt-03"] } rtic-sync = { version = "1", features = ["defmt-03"] }
[dependencies.smoltcp] [dependencies.smoltcp]
version = "0.11" version = "0.11.0"
default-features = false default-features = false
features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"] features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"]
@ -34,7 +34,7 @@ version = "2"
features = ["thumbv7-backend"] features = ["thumbv7-backend"]
[dependencies.rtic-monotonics] [dependencies.rtic-monotonics]
version = "2" version = "1"
features = ["cortex-m-systick"] features = ["cortex-m-systick"]
[dependencies.satrs] [dependencies.satrs]

View File

@ -1,4 +1,4 @@
sat-rs example for the STM32H73ZI-Nucleo board sat-rs example for the STM32F3-Discovery board
======= =======
This example application shows how the [sat-rs library](https://egit.irs.uni-stuttgart.de/rust/sat-rs) This example application shows how the [sat-rs library](https://egit.irs.uni-stuttgart.de/rust/sat-rs)

View File

@ -3,7 +3,8 @@
extern crate alloc; extern crate alloc;
use rtic::app; use rtic::app;
use rtic_monotonics::systick::prelude::*; use rtic_monotonics::systick::Systick;
use rtic_monotonics::Monotonic;
use satrs::pool::{PoolAddr, PoolProvider, StaticHeaplessMemoryPool}; use satrs::pool::{PoolAddr, PoolProvider, StaticHeaplessMemoryPool};
use satrs::static_subpool; use satrs::static_subpool;
// global logger + panicking-behavior + memory layout // global logger + panicking-behavior + memory layout
@ -12,7 +13,7 @@ use smoltcp::socket::udp::UdpMetadata;
use smoltcp::socket::{dhcpv4, udp}; use smoltcp::socket::{dhcpv4, udp};
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use embedded_alloc::LlffHeap as Heap; use embedded_alloc::Heap;
use smoltcp::iface::{Config, Interface, SocketHandle, SocketSet, SocketStorage}; use smoltcp::iface::{Config, Interface, SocketHandle, SocketSet, SocketStorage};
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};
use stm32h7xx_hal::ethernet; use stm32h7xx_hal::ethernet;
@ -31,8 +32,6 @@ pub type TcSourceRx = rtic_sync::channel::Receiver<'static, PoolAddr, TC_SOURCE_
#[global_allocator] #[global_allocator]
static HEAP: Heap = Heap::empty(); static HEAP: Heap = Heap::empty();
systick_monotonic!(Mono, 1000);
// We place the memory pool buffers inside the larger AXISRAM. // We place the memory pool buffers inside the larger AXISRAM.
pub const SUBPOOL_SMALL_NUM_BLOCKS: u16 = 32; pub const SUBPOOL_SMALL_NUM_BLOCKS: u16 = 32;
pub const SUBPOOL_SMALL_BLOCK_SIZE: usize = 32; pub const SUBPOOL_SMALL_BLOCK_SIZE: usize = 32;
@ -73,7 +72,7 @@ impl Net {
let mut iface = Interface::new( let mut iface = Interface::new(
config, config,
&mut ethdev, &mut ethdev,
smoltcp::time::Instant::from_millis(Mono::now().duration_since_epoch().to_millis()), smoltcp::time::Instant::from_millis((Systick::now() - Systick::ZERO).to_millis()),
); );
// Create sockets // Create sockets
let dhcp_socket = dhcpv4::Socket::new(); let dhcp_socket = dhcpv4::Socket::new();
@ -92,7 +91,7 @@ impl Net {
/// Polls on the ethernet interface. You should refer to the smoltcp /// Polls on the ethernet interface. You should refer to the smoltcp
/// documentation for poll() to understand how to call poll efficiently /// documentation for poll() to understand how to call poll efficiently
pub fn poll<'a>(&mut self, sockets: &'a mut SocketSet) -> bool { pub fn poll<'a>(&mut self, sockets: &'a mut SocketSet) -> bool {
let uptime = Mono::now().duration_since_epoch(); let uptime = Systick::now() - Systick::ZERO;
let timestamp = smoltcp::time::Instant::from_millis(uptime.to_millis()); let timestamp = smoltcp::time::Instant::from_millis(uptime.to_millis());
self.iface.poll(timestamp, &mut self.ethdev, sockets) self.iface.poll(timestamp, &mut self.ethdev, sockets)
@ -204,7 +203,8 @@ mod app {
use core::ptr::addr_of_mut; use core::ptr::addr_of_mut;
use super::*; use super::*;
use rtic_monotonics::fugit::MillisDurationU32; use rtic_monotonics::systick::fugit::MillisDurationU32;
use rtic_monotonics::systick::Systick;
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use stm32h7xx_hal::ethernet::{EthernetMAC, PHY}; use stm32h7xx_hal::ethernet::{EthernetMAC, PHY};
use stm32h7xx_hal::gpio::{Output, Pin}; use stm32h7xx_hal::gpio::{Output, Pin};
@ -256,7 +256,12 @@ mod app {
.freeze(pwrcfg, &cx.device.SYSCFG); .freeze(pwrcfg, &cx.device.SYSCFG);
// Initialize the systick interrupt & obtain the token to prove that we did // Initialize the systick interrupt & obtain the token to prove that we did
Mono::start(cx.core.SYST, ccdr.clocks.sys_ck().to_Hz()); let systick_mono_token = rtic_monotonics::create_systick_token!();
Systick::start(
cx.core.SYST,
ccdr.clocks.sys_ck().to_Hz(),
systick_mono_token,
);
// Those are used in the smoltcp of the stm32h7xx-hal , I am not fully sure what they are // Those are used in the smoltcp of the stm32h7xx-hal , I am not fully sure what they are
// good for. // good for.
@ -372,24 +377,24 @@ mod app {
shared_pool shared_pool
.grow( .grow(
SUBPOOL_SMALL.get_mut().unwrap(), unsafe { SUBPOOL_SMALL.assume_init_mut() },
SUBPOOL_SMALL_SIZES.get_mut().unwrap(), unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
SUBPOOL_SMALL_NUM_BLOCKS, SUBPOOL_SMALL_NUM_BLOCKS,
true, true,
) )
.expect("growing heapless memory pool failed"); .expect("growing heapless memory pool failed");
shared_pool shared_pool
.grow( .grow(
SUBPOOL_MEDIUM.get_mut().unwrap(), unsafe { SUBPOOL_MEDIUM.assume_init_mut() },
SUBPOOL_MEDIUM_SIZES.get_mut().unwrap(), unsafe { SUBPOOL_MEDIUM_SIZES.assume_init_mut() },
SUBPOOL_MEDIUM_NUM_BLOCKS, SUBPOOL_MEDIUM_NUM_BLOCKS,
true, true,
) )
.expect("growing heapless memory pool failed"); .expect("growing heapless memory pool failed");
shared_pool shared_pool
.grow( .grow(
SUBPOOL_LARGE.get_mut().unwrap(), unsafe { SUBPOOL_LARGE.assume_init_mut() },
SUBPOOL_LARGE_SIZES.get_mut().unwrap(), unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
SUBPOOL_LARGE_NUM_BLOCKS, SUBPOOL_LARGE_NUM_BLOCKS,
true, true,
) )
@ -398,7 +403,7 @@ mod app {
// Set up global allocator. Use AXISRAM for the heap. // Set up global allocator. Use AXISRAM for the heap.
#[link_section = ".axisram"] #[link_section = ".axisram"]
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
eth_link_check::spawn().expect("eth link check failed"); eth_link_check::spawn().expect("eth link check failed");
blinky::spawn().expect("spawning blink task failed"); blinky::spawn().expect("spawning blink task failed");
@ -430,7 +435,7 @@ mod app {
leds.led1.toggle(); leds.led1.toggle();
leds.led2.toggle(); leds.led2.toggle();
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current); let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
Mono::delay(current_blink_freq).await; Systick::delay(current_blink_freq).await;
} }
} }
@ -452,7 +457,7 @@ mod app {
cx.shared.eth_link_up.lock(|link_up| *link_up = false); cx.shared.eth_link_up.lock(|link_up| *link_up = false);
defmt::info!("Ethernet link down"); defmt::info!("Ethernet link down");
} }
Mono::delay(100.millis()).await; Systick::delay(100.millis()).await;
} }
} }
@ -478,7 +483,7 @@ mod app {
cx.local.udp.poll(sockets, pool); cx.local.udp.poll(sockets, pool);
}) })
}); });
Mono::delay(40.millis()).await; Systick::delay(40.millis()).await;
} }
} }

View File

@ -1,260 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.23.2-->
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key for="graphml" id="d7" yfiles.type="resources"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d0" xml:space="preserve"/>
<node id="n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="360.0" width="479.0" x="771.3047672479152" y="458.0"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="237.5" y="178.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="177.64799999999997" width="200.75199999999973" x="1037.5527672479152" y="470.15200000000027"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="67.919921875" x="13.264464667588754" xml:space="preserve" y="8.302185845943427">Simulation<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="-0.5" nodeRatioX="-0.433926114471642" nodeRatioY="-0.45326608886143704" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="84.39999999999986" x="1068.8351781652768" y="508.2800000000002"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.638671875" x="23.380664062499818" xml:space="preserve" y="8.015625">PCDU<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="120.39999999999986" x="1068.8351781652768" y="550.4800000000001"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="92.453125" x="13.973437499999818" xml:space="preserve" y="8.015625">Magnetometer<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n4">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="120.39999999999986" x="1068.8351781652768" y="594.9000000000001"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="88.83203125" x="15.783984374999818" xml:space="preserve" y="8.015625">Magnetorquer<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n5">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="120.39999999999986" x="783.4063563305535" y="545.2800000000002"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="85.931640625" x="17.234179687499932" xml:space="preserve" y="8.015625">SimController<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n6">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="120.39999999999986" x="840.5407126611072" y="677.8000000000002"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="105.05078125" x="7.674609374999932" xml:space="preserve" y="8.015625">UDP TC Receiver<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n7">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="120.39999999999986" x="1005.2814253222144" y="677.8000000000002"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="97.111328125" x="11.644335937499932" xml:space="preserve" y="8.015625">UDP TM Sender<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n8">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="34.0" width="120.39999999999986" x="931.6174253222144" y="775.5920000000002"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="38.740234375" x="40.82988281249993" xml:space="preserve" y="8.015625">Client<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<edge id="e0" source="n5" target="n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="-5.199999999999932"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n5" target="n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="60.19999999999993" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1023.8695890826383" y="562.2800000000002"/>
<y:Point x="1023.8695890826383" y="525.2800000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n5" target="n4">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="48.72964366944643" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1023.8695890826383" y="562.2800000000002"/>
<y:Point x="1023.8695890826383" y="611.9000000000001"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="97.955078125" x="12.686124396959713" xml:space="preserve" y="-22.50440429687478">schedule_event<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="13.519999999999978" distanceToCenter="true" position="left" ratio="0.11621274698385183" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n6" target="n5">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="-5.329643669446341" ty="0.0">
<y:Point x="900.7407126611072" y="628.5400000000002"/>
<y:Point x="838.2767126611071" y="628.5400000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="75.923828125" x="-87.89792405764274" xml:space="preserve" y="-40.606550292968564">SimRequest<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="49.935999999999936" distanceToCenter="true" position="left" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n4" target="n7">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="60.200000000000045" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1223.8814253222142" y="611.9000000000001"/>
<y:Point x="1223.8814253222142" y="694.8000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n3" target="n7">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1223.8814253222142" y="567.4800000000001"/>
<y:Point x="1223.8814253222142" y="694.8000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e6" source="n2" target="n7">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="11.514125426161627" sy="-2.5781798912005343" tx="45.553752843062284" ty="0.0">
<y:Point x="1223.8814253222142" y="522.7018201087997"/>
<y:Point x="1223.8814253222142" y="694.8000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="60.4140625" x="-2.4087265765670054" xml:space="preserve" y="145.1356018470808">SimReply<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="17.97817989120062" distanceToCenter="true" position="right" ratio="0.679561684469248" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n8" target="n6">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-25.212712661107275" sy="0.0" tx="-11.264000000000124" ty="0.0">
<y:Point x="966.6047126611071" y="731.8000000000002"/>
<y:Point x="889.4767126611071" y="731.8000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="119.751953125" x="-132.27600022951788" xml:space="preserve" y="-32.03587548828091">SimRequest in UDP<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="20.73181250000017" distanceToCenter="true" position="left" ratio="0.9386993050513424" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n7" target="n8">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="29.18399999999997" sy="0.0" tx="24.28800000000001" ty="0.0">
<y:Point x="1094.6654253222143" y="731.8000000000002"/>
<y:Point x="1016.1054253222144" y="731.8000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="104.2421875" x="-62.15307370122309" xml:space="preserve" y="34.80927001953137">SimReply in UDP<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="23.81218750000005" distanceToCenter="true" position="left" ratio="0.12769857433808468" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n5" target="n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="23.921741802203996" sy="-3.0501798912007416" tx="0.0" ty="-56.27417989120056">
<y:Point x="867.5280981327575" y="502.70182010879967"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="29.95703125" x="73.38950633588263" xml:space="preserve" y="-62.699758016200235">step<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="11.126187499999986" distanceToCenter="true" position="left" ratio="0.5889387894625147" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>
</data>
</graphml>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

View File

@ -5,8 +5,10 @@ This book is the primary information resource for the [sat-rs library](https://e
in addition to the regular API documentation. It contains the following resources: in addition to the regular API documentation. It contains the following resources:
1. Architecture informations and consideration which would exceeds the scope of the regular API. 1. Architecture informations and consideration which would exceeds the scope of the regular API.
2. General information on how to build on-board Software and how `sat-rs` can help to fulfill 2. General information on how to build On-Board Software and how `sat-rs` can help to fulfill
the unique requirements of writing software for remote systems. the unique requirements of writing software for remote systems.
2. A Getting-Started workshop where a small On-Board Software is built from scratch using
sat-rs components.
# Introduction # Introduction
@ -29,9 +31,7 @@ and [EIVE](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-
The [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example) The [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
provides various practical usage examples of the `sat-rs` framework. If you are more interested in provides various practical usage examples of the `sat-rs` framework. If you are more interested in
the practical application of `sat-rs` inside an application, it is recommended to have a look at the practical application of `sat-rs` inside an application, it is recommended to have a look at
the example application. The [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim) the example application.
applicatin complements the example application and can be used to simulate some physical devices
for the `satrs-example` device handlers.
# Flight Heritage # Flight Heritage
@ -43,7 +43,3 @@ Currently this library has the following flight heritage:
[flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/). [flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/).
The application is strongly based on the sat-rs example application. You can find the repository The application is strongly based on the sat-rs example application. You can find the repository
of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs). of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs).
- Development and use of a sat-rs-based [demonstration on-board software](https://egit.irs.uni-stuttgart.de/rust/eurosim-obsw)
alongside a Flight System Simulator in the context of a
[Bachelors Thesis](https://www.researchgate.net/publication/380785984_Design_and_Development_of_a_Hardware-in-the-Loop_EuroSim_Demonstrator)
at [Airbus Netherlands](https://www.airbusdefenceandspacenetherlands.nl/).

View File

@ -7,13 +7,3 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.1.1] 2024-02-21
satrs v0.2.0-rc.0
satrs-mib v0.1.1
# [v0.1.0] 2024-02-13
satrs v0.1.1
satrs-mib v0.1.0

View File

@ -8,19 +8,18 @@ homepage = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs" repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
[dependencies] [dependencies]
fern = "0.7" fern = "0.6"
chrono = "0.4" chrono = "0.4"
log = "0.4" log = "0.4"
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
delegate = "0.13" delegate = "0.10"
zerocopy = "0.8" zerocopy = "0.6"
csv = "1" csv = "1"
num_enum = "0.7" num_enum = "0.7"
thiserror = "2" thiserror = "1"
lazy_static = "1" lazy_static = "1"
strum = { version = "0.26", features = ["derive"] } strum = { version = "0.26", features = ["derive"] }
derive-new = "0.7" derive-new = "0.5"
cfg-if = "1"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
@ -36,8 +35,8 @@ version = "0.1.1"
path = "../satrs-mib" path = "../satrs-mib"
[features] [features]
heap_tmtc = [] dyn_tmtc = []
default = ["heap_tmtc"] default = ["dyn_tmtc"]
[dev-dependencies] [dev-dependencies]
env_logger = "0.11" env_logger = "0.11"

View File

@ -14,7 +14,7 @@ You can run the application using `cargo run`.
# Features # Features
The example has the `heap_tmtc` feature which is enabled by default. With this feature enabled, The example has the `dyn_tmtc` feature which is enabled by default. With this feature enabled,
TMTC packets are exchanged using the heap as the backing memory instead of pre-allocated static TMTC packets are exchanged using the heap as the backing memory instead of pre-allocated static
stores. stores.
@ -73,22 +73,3 @@ the `simpleclient`:
``` ```
You can also simply call the script without any arguments to view the command tree. You can also simply call the script without any arguments to view the command tree.
## Adding the mini simulator application
This example application features a few device handlers. The
[`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim)
can be used to simulate the physical devices managed by these device handlers.
The example application will attempt communication with the mini simulator on UDP port 7303.
If this works, the device handlers will use communication interfaces dedicated to the communication
with the mini simulator. Otherwise, they will be replaced by dummy interfaces which either
return constant values or behave like ideal devices.
In summary, you can use the following command command to run the mini-simulator first:
```sh
cargo run -p satrs-minisim
```
and then start the example using `cargo run -p satrs-example`.

View File

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

View File

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

View File

@ -1,11 +0,0 @@
from tmtccmd.config import CmdTreeNode
def create_acs_node(mode_node: CmdTreeNode, hk_node: CmdTreeNode) -> CmdTreeNode:
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
mgm_node = CmdTreeNode("mgms", "MGM devices node")
mgm_node.add_child(mode_node)
mgm_node.add_child(hk_node)
acs_node.add_child(mgm_node)
return acs_node

View File

@ -39,10 +39,7 @@ class EventU32:
class AcsId(enum.IntEnum): class AcsId(enum.IntEnum):
SUBSYSTEM = 1 MGM_0 = 0
MGM_ASSEMBLY = 2
MGM_0 = 3
MGM_1 = 4
class AcsHkIds(enum.IntEnum): class AcsHkIds(enum.IntEnum):

View File

@ -12,7 +12,7 @@ from pytmtc.pus_tc import create_cmd_definition_tree
class SatrsConfigHook(HookBase): class SatrsConfigHook(HookBase):
def __init__(self, json_cfg_path: str): def __init__(self, json_cfg_path: str):
super().__init__(json_cfg_path) super().__init__(json_cfg_path=json_cfg_path)
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]: def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
from tmtccmd.config.com import ( from tmtccmd.config.com import (

View File

@ -4,7 +4,7 @@ from spacepackets.ecss.pus_3_hk import Subservice
from spacepackets.ecss import PusTm from spacepackets.ecss import PusTm
from pytmtc.common import AcsId, Apid from pytmtc.common import AcsId, Apid
from pytmtc.acs.mgms import handle_mgm_hk_report from pytmtc.mgms import handle_mgm_hk_report
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -17,9 +17,8 @@ from tmtccmd.tmtc import (
) )
from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd
from pytmtc.acs import create_acs_node
from pytmtc.common import Apid from pytmtc.common import Apid
from pytmtc.acs.mgms import create_mgm_cmds from pytmtc.mgms import create_mgm_cmds
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -68,6 +67,7 @@ class TcHandler(TcHandlerBase):
def create_cmd_definition_tree() -> CmdTreeNode: def create_cmd_definition_tree() -> CmdTreeNode:
root_node = CmdTreeNode.root_node() root_node = CmdTreeNode.root_node()
hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True) hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True)
@ -101,7 +101,15 @@ def create_cmd_definition_tree() -> CmdTreeNode:
) )
) )
root_node.add_child(scheduler_node) root_node.add_child(scheduler_node)
root_node.add_child(create_acs_node(mode_node, hk_node))
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
mgm_node = CmdTreeNode("mgms", "MGM devices node")
mgm_node.add_child(mode_node)
mgm_node.add_child(hk_node)
acs_node.add_child(mgm_node)
root_node.add_child(acs_node)
return root_node return root_node

View File

@ -1 +0,0 @@
// TODO: Write the assembly

View File

@ -1 +0,0 @@
// TODO: Write dummy controller

View File

@ -1,8 +1,7 @@
use derive_new::new; use derive_new::new;
use satrs::hk::{HkRequest, HkRequestVariant}; use satrs::hk::{HkRequest, HkRequestVariant};
use satrs::mode_tree::{ModeChild, ModeNode};
use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender}; use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender};
use satrs_example::config::pus::PUS_MODE_SERVICE; use satrs::queue::{GenericSendError, GenericTargetedMessagingError};
use satrs_example::{DeviceMode, TimestampHelper}; use satrs_example::{DeviceMode, TimestampHelper};
use satrs_minisim::acs::lis3mdl::{ use satrs_minisim::acs::lis3mdl::{
MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR, MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR,
@ -16,18 +15,15 @@ use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
use satrs::mode::{ use satrs::mode::{
ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler, ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequest, ModeRequestHandler,
ModeRequestHandlerMpscBounded,
}; };
use satrs::pus::{EcssTmSender, PusTmVariant}; use satrs::pus::{EcssTmSender, PusTmVariant};
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId}; use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
use satrs_example::config::components::NO_SENDER; use satrs_example::config::components::PUS_MODE_SERVICE;
use crate::hk::PusHkHelper; use crate::hk::PusHkHelper;
use crate::pus::hk::{HkReply, HkReplyVariant}; use crate::pus::hk::{HkReply, HkReplyVariant};
use crate::requests::CompositeRequest; use crate::requests::CompositeRequest;
use crate::spi::SpiInterface;
use crate::tmtc::sender::TmTcSender;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -52,6 +48,11 @@ pub enum TransitionState {
Done, Done,
} }
pub trait SpiInterface {
type Error: Debug;
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error>;
}
#[derive(Default)] #[derive(Default)]
pub struct SpiDummyInterface { pub struct SpiDummyInterface {
pub dummy_values: MgmLis3RawValues, pub dummy_values: MgmLis3RawValues,
@ -128,6 +129,12 @@ pub struct MgmData {
pub z: f32, pub z: f32,
} }
pub struct MpscModeLeafInterface {
pub request_rx: mpsc::Receiver<GenericMessage<ModeRequest>>,
pub reply_to_pus_tx: mpsc::Sender<GenericMessage<ModeReply>>,
pub reply_to_parent_tx: mpsc::SyncSender<GenericMessage<ModeReply>>,
}
#[derive(Default)] #[derive(Default)]
pub struct BufWrapper { pub struct BufWrapper {
tx_buf: [u8; 32], tx_buf: [u8; 32],
@ -158,15 +165,16 @@ impl Default for ModeHelpers {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub struct MgmHandlerLis3Mdl< pub struct MgmHandlerLis3Mdl<
ComInterface: SpiInterface, ComInterface: SpiInterface,
TmSender: EcssTmSender,
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>, SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
> { > {
id: UniqueApidTargetId, id: UniqueApidTargetId,
dev_str: &'static str, dev_str: &'static str,
mode_node: ModeRequestHandlerMpscBounded, mode_interface: MpscModeLeafInterface,
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>, composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
hk_reply_tx: mpsc::SyncSender<GenericMessage<HkReply>>, hk_reply_tx: mpsc::Sender<GenericMessage<HkReply>>,
switch_helper: SwitchHelper, switch_helper: SwitchHelper,
tm_sender: TmTcSender, tm_sender: TmSender,
pub com_interface: ComInterface, pub com_interface: ComInterface,
shared_mgm_set: Arc<Mutex<MgmData>>, shared_mgm_set: Arc<Mutex<MgmData>>,
#[new(value = "PusHkHelper::new(id)")] #[new(value = "PusHkHelper::new(id)")]
@ -181,8 +189,9 @@ pub struct MgmHandlerLis3Mdl<
impl< impl<
ComInterface: SpiInterface, ComInterface: SpiInterface,
TmSender: EcssTmSender,
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>, SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
> MgmHandlerLis3Mdl<ComInterface, SwitchHelper> > MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
{ {
pub fn periodic_operation(&mut self) { pub fn periodic_operation(&mut self) {
self.stamp_helper.update_from_now(); self.stamp_helper.update_from_now();
@ -265,28 +274,25 @@ impl<
pub fn handle_mode_requests(&mut self) { pub fn handle_mode_requests(&mut self) {
loop { loop {
// TODO: Only allow one set mode request per cycle? // TODO: Only allow one set mode request per cycle?
match self.mode_node.try_recv_mode_request() { match self.mode_interface.request_rx.try_recv() {
Ok(opt_msg) => { Ok(msg) => {
if let Some(msg) = opt_msg { let result = self.handle_mode_request(msg);
let result = self.handle_mode_request(msg); // TODO: Trigger event?
// TODO: Trigger event? if result.is_err() {
if result.is_err() { log::warn!(
log::warn!( "{}: mode request failed with error {:?}",
"{}: mode request failed with error {:?}", self.dev_str,
self.dev_str, result.err().unwrap()
result.err().unwrap() );
); }
} }
Err(e) => {
if e != mpsc::TryRecvError::Empty {
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
} else { } else {
break; break;
} }
} }
Err(e) => match e {
satrs::queue::GenericReceiveError::Empty => break,
satrs::queue::GenericReceiveError::TxDisconnected(e) => {
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
}
},
} }
} }
} }
@ -358,8 +364,9 @@ impl<
impl< impl<
ComInterface: SpiInterface, ComInterface: SpiInterface,
TmSender: EcssTmSender,
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>, SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
> ModeProvider for MgmHandlerLis3Mdl<ComInterface, SwitchHelper> > ModeProvider for MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
{ {
fn mode_and_submode(&self) -> ModeAndSubmode { fn mode_and_submode(&self) -> ModeAndSubmode {
self.mode_helpers.current self.mode_helpers.current
@ -368,8 +375,9 @@ impl<
impl< impl<
ComInterface: SpiInterface, ComInterface: SpiInterface,
TmSender: EcssTmSender,
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>, SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
> ModeRequestHandler for MgmHandlerLis3Mdl<ComInterface, SwitchHelper> > ModeRequestHandler for MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
{ {
type Error = ModeError; type Error = ModeError;
@ -377,7 +385,6 @@ impl<
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
_forced: bool,
) -> Result<(), satrs::mode::ModeError> { ) -> Result<(), satrs::mode::ModeError> {
log::info!( log::info!(
"{}: transitioning to mode {:?}", "{}: transitioning to mode {:?}",
@ -414,12 +421,9 @@ impl<
self.mode_helpers.target = None; self.mode_helpers.target = None;
self.announce_mode(requestor, false); self.announce_mode(requestor, false);
if let Some(requestor) = requestor { if let Some(requestor) = requestor {
if requestor.sender_id() == NO_SENDER {
return Ok(());
}
if requestor.sender_id() != PUS_MODE_SERVICE.id() { if requestor.sender_id() != PUS_MODE_SERVICE.id() {
log::warn!( log::warn!(
"can not send back mode reply to sender {:x}", "can not send back mode reply to sender {}",
requestor.sender_id() requestor.sender_id()
); );
} else { } else {
@ -440,9 +444,10 @@ impl<
requestor.sender_id() requestor.sender_id()
); );
} }
self.mode_node self.mode_interface
.send_mode_reply(requestor, reply) .reply_to_pus_tx
.map_err(ModeError::Send)?; .send(GenericMessage::new(requestor, reply))
.map_err(|_| GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected))?;
Ok(()) Ok(())
} }
@ -455,44 +460,17 @@ impl<
} }
} }
impl<
ComInterface: SpiInterface,
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
> ModeNode for MgmHandlerLis3Mdl<ComInterface, SwitchHelper>
{
fn id(&self) -> satrs::ComponentId {
self.id.into()
}
}
impl<
ComInterface: SpiInterface,
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
> ModeChild for MgmHandlerLis3Mdl<ComInterface, SwitchHelper>
{
type Sender = mpsc::SyncSender<GenericMessage<ModeReply>>;
fn add_mode_parent(&mut self, id: satrs::ComponentId, reply_sender: Self::Sender) {
self.mode_node.add_message_target(id, reply_sender);
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{ use std::sync::{mpsc, Arc};
collections::HashMap,
sync::{mpsc, Arc},
};
use satrs::{ use satrs::{
mode::{ModeReply, ModeRequest}, mode::{ModeReply, ModeRequest},
mode_tree::ModeParent,
power::SwitchStateBinary, power::SwitchStateBinary,
request::{GenericMessage, UniqueApidTargetId}, request::{GenericMessage, UniqueApidTargetId},
tmtc::PacketAsVec, tmtc::PacketAsVec,
ComponentId,
}; };
use satrs_example::config::{acs::MGM_ASSEMBLY, components::Apid}; use satrs_example::config::components::Apid;
use satrs_minisim::acs::lis3mdl::MgmLis3RawValues; use satrs_minisim::acs::lis3mdl::MgmLis3RawValues;
use crate::{eps::TestSwitchHelper, pus::hk::HkReply, requests::CompositeRequest}; use crate::{eps::TestSwitchHelper, pus::hk::HkReply, requests::CompositeRequest};
@ -520,88 +498,49 @@ mod tests {
} }
} }
#[allow(dead_code)]
pub struct MgmTestbench { pub struct MgmTestbench {
pub mode_request_tx: mpsc::SyncSender<GenericMessage<ModeRequest>>, pub mode_request_tx: mpsc::Sender<GenericMessage<ModeRequest>>,
pub mode_reply_rx_to_pus: mpsc::Receiver<GenericMessage<ModeReply>>, pub mode_reply_rx_to_pus: mpsc::Receiver<GenericMessage<ModeReply>>,
pub mode_reply_rx_to_parent: mpsc::Receiver<GenericMessage<ModeReply>>, pub mode_reply_rx_to_parent: mpsc::Receiver<GenericMessage<ModeReply>>,
pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>, pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>, pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
pub tm_rx: mpsc::Receiver<PacketAsVec>, pub tm_rx: mpsc::Receiver<PacketAsVec>,
pub handler: MgmHandlerLis3Mdl<TestSpiInterface, TestSwitchHelper>, pub handler:
} MgmHandlerLis3Mdl<TestSpiInterface, mpsc::Sender<PacketAsVec>, TestSwitchHelper>,
#[derive(Default)]
pub struct MgmAssemblyMock(
pub HashMap<ComponentId, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
);
impl ModeNode for MgmAssemblyMock {
fn id(&self) -> satrs::ComponentId {
PUS_MODE_SERVICE.into()
}
}
impl ModeParent for MgmAssemblyMock {
type Sender = mpsc::SyncSender<GenericMessage<ModeRequest>>;
fn add_mode_child(&mut self, id: satrs::ComponentId, request_sender: Self::Sender) {
self.0.insert(id, request_sender);
}
}
#[derive(Default)]
pub struct PusMock {
pub request_sender_map: HashMap<ComponentId, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
}
impl ModeNode for PusMock {
fn id(&self) -> satrs::ComponentId {
PUS_MODE_SERVICE.into()
}
}
impl ModeParent for PusMock {
type Sender = mpsc::SyncSender<GenericMessage<ModeRequest>>;
fn add_mode_child(&mut self, id: satrs::ComponentId, request_sender: Self::Sender) {
self.request_sender_map.insert(id, request_sender);
}
} }
impl MgmTestbench { impl MgmTestbench {
pub fn new() -> Self { pub fn new() -> Self {
let (request_tx, request_rx) = mpsc::sync_channel(5); let (request_tx, request_rx) = mpsc::channel();
let (reply_tx_to_pus, reply_rx_to_pus) = mpsc::sync_channel(5); let (reply_tx_to_pus, reply_rx_to_pus) = mpsc::channel();
let (reply_tx_to_parent, reply_rx_to_parent) = mpsc::sync_channel(5); let (reply_tx_to_parent, reply_rx_to_parent) = mpsc::sync_channel(5);
let id = UniqueApidTargetId::new(Apid::Acs as u16, 1); let mode_interface = MpscModeLeafInterface {
let mode_node = ModeRequestHandlerMpscBounded::new(id.into(), request_rx); request_rx,
reply_to_pus_tx: reply_tx_to_pus,
reply_to_parent_tx: reply_tx_to_parent,
};
let (composite_request_tx, composite_request_rx) = mpsc::channel(); let (composite_request_tx, composite_request_rx) = mpsc::channel();
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10); let (hk_reply_tx, hk_reply_rx) = mpsc::channel();
let (tm_tx, tm_rx) = mpsc::sync_channel(10); let (tm_tx, tm_rx) = mpsc::channel::<PacketAsVec>();
let tm_sender = TmTcSender::Heap(tm_tx);
let shared_mgm_set = Arc::default(); let shared_mgm_set = Arc::default();
let mut handler = MgmHandlerLis3Mdl::new(
id,
"TEST_MGM",
mode_node,
composite_request_rx,
hk_reply_tx,
TestSwitchHelper::default(),
tm_sender,
TestSpiInterface::default(),
shared_mgm_set,
);
handler.add_mode_parent(PUS_MODE_SERVICE.into(), reply_tx_to_pus);
handler.add_mode_parent(MGM_ASSEMBLY.into(), reply_tx_to_parent);
Self { Self {
mode_request_tx: request_tx, mode_request_tx: request_tx,
mode_reply_rx_to_pus: reply_rx_to_pus, mode_reply_rx_to_pus: reply_rx_to_pus,
mode_reply_rx_to_parent: reply_rx_to_parent, mode_reply_rx_to_parent: reply_rx_to_parent,
composite_request_tx, composite_request_tx,
handler,
tm_rx, tm_rx,
hk_reply_rx, hk_reply_rx,
handler: MgmHandlerLis3Mdl::new(
UniqueApidTargetId::new(Apid::Acs as u16, 1),
"test-mgm",
mode_interface,
composite_request_rx,
hk_reply_tx,
TestSwitchHelper::default(),
tm_tx,
TestSpiInterface::default(),
shared_mgm_set,
),
} }
} }
} }
@ -632,10 +571,7 @@ mod tests {
.mode_request_tx .mode_request_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()), MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode { ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)),
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
)) ))
.expect("failed to send mode request"); .expect("failed to send mode request");
testbench.handler.periodic_operation(); testbench.handler.periodic_operation();
@ -693,10 +629,7 @@ mod tests {
.mode_request_tx .mode_request_tx
.send(GenericMessage::new( .send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()), MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode { ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)),
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
)) ))
.expect("failed to send mode request"); .expect("failed to send mode request");
testbench.handler.periodic_operation(); testbench.handler.periodic_operation();

View File

@ -1,4 +1 @@
pub mod assembly;
pub mod ctrl;
pub mod mgm; pub mod mgm;
pub mod subsystem;

View File

@ -1 +0,0 @@
// TODO: Write subsystem

View File

@ -3,7 +3,7 @@
use crossbeam_channel::{bounded, Receiver, Sender}; use crossbeam_channel::{bounded, Receiver, Sender};
use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::atomic::{AtomicU16, Ordering};
use std::thread; use std::thread;
use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16}; use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
trait FieldDataProvider: Send { trait FieldDataProvider: Send {
fn get_data(&self) -> &[u8]; fn get_data(&self) -> &[u8];
@ -35,7 +35,7 @@ struct ExampleMgmSet {
temperature: u16, temperature: u16,
} }
#[derive(FromBytes, IntoBytes, Immutable, Unaligned)] #[derive(FromBytes, AsBytes, Unaligned)]
#[repr(C)] #[repr(C)]
struct ExampleMgmSetZc { struct ExampleMgmSetZc {
mgm_vec: [u8; 12], mgm_vec: [u8; 12],

View File

@ -122,7 +122,7 @@ pub mod mode_err {
} }
pub mod components { pub mod components {
use satrs::{request::UniqueApidTargetId, ComponentId}; use satrs::request::UniqueApidTargetId;
use strum::EnumIter; use strum::EnumIter;
#[derive(Copy, Clone, PartialEq, Eq, EnumIter)] #[derive(Copy, Clone, PartialEq, Eq, EnumIter)]
@ -135,33 +135,6 @@ pub mod components {
Eps = 6, Eps = 6,
} }
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum EpsId {
Pcdu = 0,
Subsystem = 1,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum TmtcId {
UdpServer = 0,
TcpServer = 1,
}
pub const EPS_SUBSYSTEM: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Subsystem as u32);
pub const PCDU_HANDLER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Pcdu as u32);
pub const UDP_SERVER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::UdpServer as u32);
pub const TCP_SERVER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::TcpServer as u32);
pub const NO_SENDER: ComponentId = ComponentId::MAX;
}
pub mod pus {
use super::components::Apid;
use satrs::request::UniqueApidTargetId;
// Component IDs for components with the PUS APID. // Component IDs for components with the PUS APID.
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum PusId { pub enum PusId {
@ -172,6 +145,23 @@ pub mod pus {
PusMode = 4, PusMode = 4,
PusHk = 5, PusHk = 5,
} }
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AcsId {
Mgm0 = 0,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum EpsId {
Pcdu = 0,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum TmtcId {
UdpServer = 0,
TcpServer = 1,
}
pub const PUS_ACTION_SERVICE: UniqueApidTargetId = pub const PUS_ACTION_SERVICE: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusAction as u32); UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusAction as u32);
pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId = pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId =
@ -186,26 +176,14 @@ pub mod pus {
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusHk as u32); UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusHk as u32);
pub const PUS_SCHED_SERVICE: UniqueApidTargetId = pub const PUS_SCHED_SERVICE: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Sched as u16, 0); UniqueApidTargetId::new(Apid::Sched as u16, 0);
}
pub mod acs {
use super::components::Apid;
use satrs::request::UniqueApidTargetId;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AcsId {
Subsystem = 1,
Assembly = 2,
Mgm0 = 3,
Mgm1 = 4,
}
pub const MGM_ASSEMBLY: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Assembly as u32);
pub const MGM_HANDLER_0: UniqueApidTargetId = pub const MGM_HANDLER_0: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32); UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
pub const MGM_HANDLER_1: UniqueApidTargetId = pub const PCDU_HANDLER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32); UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Pcdu as u32);
pub const UDP_SERVER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::UdpServer as u32);
pub const TCP_SERVER: UniqueApidTargetId =
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::TcpServer as u32);
} }
pub mod pool { pub mod pool {

View File

@ -88,7 +88,6 @@ impl PowerSwitcherCommandSender<PcduSwitch> for PowerSwitchHelper {
} }
} }
#[allow(dead_code)]
#[derive(new)] #[derive(new)]
pub struct SwitchRequestInfo { pub struct SwitchRequestInfo {
pub requestor_info: MessageMetadata, pub requestor_info: MessageMetadata,
@ -100,7 +99,6 @@ pub struct SwitchRequestInfo {
pub struct TestSwitchHelper { pub struct TestSwitchHelper {
pub switch_requests: RefCell<VecDeque<SwitchRequestInfo>>, pub switch_requests: RefCell<VecDeque<SwitchRequestInfo>>,
pub switch_info_requests: RefCell<VecDeque<PcduSwitch>>, pub switch_info_requests: RefCell<VecDeque<PcduSwitch>>,
#[allow(dead_code)]
pub switch_delay_request_count: u32, pub switch_delay_request_count: u32,
pub next_switch_delay: Duration, pub next_switch_delay: Duration,
pub switch_map: RefCell<SwitchMapWrapper>, pub switch_map: RefCell<SwitchMapWrapper>,

View File

@ -8,24 +8,14 @@ use derive_new::new;
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use satrs::{ use satrs::{
hk::{HkRequest, HkRequestVariant}, hk::{HkRequest, HkRequestVariant},
mode::{ mode::{ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler},
ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler,
ModeRequestHandlerMpscBounded,
},
mode_tree::{ModeChild, ModeNode},
power::SwitchRequest, power::SwitchRequest,
pus::{EcssTmSender, PusTmVariant}, pus::{EcssTmSender, PusTmVariant},
queue::GenericSendError, queue::{GenericSendError, GenericTargetedMessagingError},
request::{GenericMessage, MessageMetadata, UniqueApidTargetId}, request::{GenericMessage, MessageMetadata, UniqueApidTargetId},
spacepackets::ByteConversionError, spacepackets::ByteConversionError,
}; };
use satrs_example::{ use satrs_example::{config::components::PUS_MODE_SERVICE, DeviceMode, TimestampHelper};
config::{
components::{NO_SENDER, PCDU_HANDLER},
pus::PUS_MODE_SERVICE,
},
DeviceMode, TimestampHelper,
};
use satrs_minisim::{ use satrs_minisim::{
eps::{ eps::{
PcduReply, PcduRequest, PcduSwitch, SwitchMap, SwitchMapBinaryWrapper, SwitchMapWrapper, PcduReply, PcduRequest, PcduSwitch, SwitchMap, SwitchMapBinaryWrapper, SwitchMapWrapper,
@ -35,10 +25,10 @@ use satrs_minisim::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
acs::mgm::MpscModeLeafInterface,
hk::PusHkHelper, hk::PusHkHelper,
pus::hk::{HkReply, HkReplyVariant}, pus::hk::{HkReply, HkReplyVariant},
requests::CompositeRequest, requests::CompositeRequest,
tmtc::sender::TmTcSender,
}; };
pub trait SerialInterface { pub trait SerialInterface {
@ -70,9 +60,9 @@ impl SerialInterface for SerialInterfaceToSim {
type Error = (); type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> { fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
let request: PcduRequest = serde_json::from_slice(data).expect("expected a PCDU request"); let request: SimRequest = serde_json::from_slice(data).unwrap();
self.sim_request_tx self.sim_request_tx
.send(SimRequest::new_with_epoch_time(request)) .send(request)
.expect("failed to send request to simulation"); .expect("failed to send request to simulation");
Ok(()) Ok(())
} }
@ -111,7 +101,9 @@ impl SerialInterface for SerialInterfaceDummy {
type Error = (); type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> { fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
let pcdu_req: PcduRequest = serde_json::from_slice(data).unwrap(); let sim_req: SimRequest = serde_json::from_slice(data).unwrap();
let pcdu_req =
PcduRequest::from_sim_message(&sim_req).expect("PCDU request creation failed");
let switch_map_mut = &mut self.switch_map.borrow_mut().0; let switch_map_mut = &mut self.switch_map.borrow_mut().0;
match pcdu_req { match pcdu_req {
PcduRequest::SwitchDevice { switch, state } => { PcduRequest::SwitchDevice { switch, state } => {
@ -127,7 +119,7 @@ impl SerialInterface for SerialInterfaceDummy {
PcduRequest::RequestSwitchInfo => { PcduRequest::RequestSwitchInfo => {
let mut reply_deque_mut = self.reply_deque.borrow_mut(); let mut reply_deque_mut = self.reply_deque.borrow_mut();
reply_deque_mut.push_back(SimReply::new(&PcduReply::SwitchInfo( reply_deque_mut.push_back(SimReply::new(&PcduReply::SwitchInfo(
switch_map_mut.clone(), self.switch_map.borrow().0.clone(),
))); )));
} }
}; };
@ -138,13 +130,15 @@ impl SerialInterface for SerialInterfaceDummy {
&self, &self,
mut f: ReplyHandler, mut f: ReplyHandler,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
if self.reply_queue_empty() { if self.reply_deque.borrow().is_empty() {
return Ok(()); return Ok(());
} }
loop { loop {
let reply = self.get_next_reply_as_string(); let mut reply_deque_mut = self.reply_deque.borrow_mut();
let next_reply = reply_deque_mut.pop_front().unwrap();
let reply = serde_json::to_string(&next_reply).unwrap();
f(reply.as_bytes()); f(reply.as_bytes());
if self.reply_queue_empty() { if reply_deque_mut.is_empty() {
break; break;
} }
} }
@ -152,18 +146,6 @@ impl SerialInterface for SerialInterfaceDummy {
} }
} }
impl SerialInterfaceDummy {
fn get_next_reply_as_string(&self) -> String {
let mut reply_deque_mut = self.reply_deque.borrow_mut();
let next_reply = reply_deque_mut.pop_front().unwrap();
serde_json::to_string(&next_reply).unwrap()
}
fn reply_queue_empty(&self) -> bool {
self.reply_deque.borrow().is_empty()
}
}
pub enum SerialSimInterfaceWrapper { pub enum SerialSimInterfaceWrapper {
Dummy(SerialInterfaceDummy), Dummy(SerialInterfaceDummy),
Sim(SerialInterfaceToSim), Sim(SerialInterfaceToSim),
@ -207,14 +189,14 @@ pub type SharedSwitchSet = Arc<Mutex<SwitchSet>>;
/// Example PCDU device handler. /// Example PCDU device handler.
#[derive(new)] #[derive(new)]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub struct PcduHandler<ComInterface: SerialInterface> { pub struct PcduHandler<ComInterface: SerialInterface, TmSender: EcssTmSender> {
id: UniqueApidTargetId, id: UniqueApidTargetId,
dev_str: &'static str, dev_str: &'static str,
mode_node: ModeRequestHandlerMpscBounded, mode_interface: MpscModeLeafInterface,
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>, composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
hk_reply_tx: mpsc::SyncSender<GenericMessage<HkReply>>, hk_reply_tx: mpsc::Sender<GenericMessage<HkReply>>,
switch_request_rx: mpsc::Receiver<GenericMessage<SwitchRequest>>, switch_request_rx: mpsc::Receiver<GenericMessage<SwitchRequest>>,
tm_sender: TmTcSender, tm_sender: TmSender,
pub com_interface: ComInterface, pub com_interface: ComInterface,
shared_switch_map: Arc<Mutex<SwitchSet>>, shared_switch_map: Arc<Mutex<SwitchSet>>,
#[new(value = "PusHkHelper::new(id)")] #[new(value = "PusHkHelper::new(id)")]
@ -227,7 +209,7 @@ pub struct PcduHandler<ComInterface: SerialInterface> {
tm_buf: [u8; 256], tm_buf: [u8; 256],
} }
impl<ComInterface: SerialInterface> PcduHandler<ComInterface> { impl<ComInterface: SerialInterface, TmSender: EcssTmSender> PcduHandler<ComInterface, TmSender> {
pub fn periodic_operation(&mut self, op_code: OpCode) { pub fn periodic_operation(&mut self, op_code: OpCode) {
match op_code { match op_code {
OpCode::RegularOp => { OpCode::RegularOp => {
@ -331,30 +313,25 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
pub fn handle_mode_requests(&mut self) { pub fn handle_mode_requests(&mut self) {
loop { loop {
// TODO: Only allow one set mode request per cycle? // TODO: Only allow one set mode request per cycle?
match self.mode_node.try_recv_mode_request() { match self.mode_interface.request_rx.try_recv() {
Ok(opt_msg) => { Ok(msg) => {
if let Some(msg) = opt_msg { let result = self.handle_mode_request(msg);
let result = self.handle_mode_request(msg); // TODO: Trigger event?
// TODO: Trigger event? if result.is_err() {
if result.is_err() { log::warn!(
log::warn!( "{}: mode request failed with error {:?}",
"{}: mode request failed with error {:?}", self.dev_str,
self.dev_str, result.err().unwrap()
result.err().unwrap() );
); }
} }
Err(e) => {
if e != mpsc::TryRecvError::Empty {
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
} else { } else {
break; break;
} }
} }
Err(e) => match e {
satrs::queue::GenericReceiveError::Empty => {
break;
}
satrs::queue::GenericReceiveError::TxDisconnected(_) => {
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
}
},
} }
} }
} }
@ -394,12 +371,10 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
PcduReply::SwitchInfo(switch_info) => { PcduReply::SwitchInfo(switch_info) => {
let switch_map_wrapper = let switch_map_wrapper =
SwitchMapWrapper::from_binary_switch_map_ref(&switch_info); SwitchMapWrapper::from_binary_switch_map_ref(&switch_info);
let mut shared_switch_map = self self.shared_switch_map
.shared_switch_map
.lock() .lock()
.expect("failed to lock switch map"); .expect("failed to lock switch map")
shared_switch_map.switch_map = switch_map_wrapper.0; .switch_map = switch_map_wrapper.0;
shared_switch_map.valid = true;
} }
} }
}) { }) {
@ -408,19 +383,22 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
} }
} }
impl<ComInterface: SerialInterface> ModeProvider for PcduHandler<ComInterface> { impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeProvider
for PcduHandler<ComInterface, TmSender>
{
fn mode_and_submode(&self) -> ModeAndSubmode { fn mode_and_submode(&self) -> ModeAndSubmode {
self.mode_and_submode self.mode_and_submode
} }
} }
impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterface> { impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
for PcduHandler<ComInterface, TmSender>
{
type Error = ModeError; type Error = ModeError;
fn start_transition( fn start_transition(
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
_forced: bool,
) -> Result<(), satrs::mode::ModeError> { ) -> Result<(), satrs::mode::ModeError> {
log::info!( log::info!(
"{}: transitioning to mode {:?}", "{}: transitioning to mode {:?}",
@ -449,9 +427,6 @@ impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterf
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
self.announce_mode(requestor, false); self.announce_mode(requestor, false);
if let Some(requestor) = requestor { if let Some(requestor) = requestor {
if requestor.sender_id() == NO_SENDER {
return Ok(());
}
if requestor.sender_id() != PUS_MODE_SERVICE.id() { if requestor.sender_id() != PUS_MODE_SERVICE.id() {
log::warn!( log::warn!(
"can not send back mode reply to sender {}", "can not send back mode reply to sender {}",
@ -475,9 +450,10 @@ impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterf
requestor.sender_id() requestor.sender_id()
); );
} }
self.mode_node self.mode_interface
.send_mode_reply(requestor, reply) .reply_to_pus_tx
.map_err(|_| GenericSendError::RxDisconnected)?; .send(GenericMessage::new(requestor, reply))
.map_err(|_| GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected))?;
Ok(()) Ok(())
} }
@ -489,265 +465,3 @@ impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterf
Ok(()) Ok(())
} }
} }
impl<ComInterface: SerialInterface> ModeNode for PcduHandler<ComInterface> {
fn id(&self) -> satrs::ComponentId {
PCDU_HANDLER.into()
}
}
impl<ComInterface: SerialInterface> ModeChild for PcduHandler<ComInterface> {
type Sender = mpsc::SyncSender<GenericMessage<ModeReply>>;
fn add_mode_parent(&mut self, id: satrs::ComponentId, reply_sender: Self::Sender) {
self.mode_node.add_message_target(id, reply_sender);
}
}
#[cfg(test)]
mod tests {
use std::sync::mpsc;
use satrs::{
mode::ModeRequest, power::SwitchStateBinary, request::GenericMessage, tmtc::PacketAsVec,
};
use satrs_example::config::{
acs::MGM_HANDLER_0,
components::{Apid, EPS_SUBSYSTEM, PCDU_HANDLER},
};
use satrs_minisim::eps::SwitchMapBinary;
use super::*;
#[derive(Default)]
pub struct SerialInterfaceTest {
pub inner: SerialInterfaceDummy,
pub send_queue: RefCell<VecDeque<Vec<u8>>>,
pub reply_queue: RefCell<VecDeque<String>>,
}
impl SerialInterface for SerialInterfaceTest {
type Error = ();
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
let mut send_queue_mut = self.send_queue.borrow_mut();
send_queue_mut.push_back(data.to_vec());
self.inner.send(data)
}
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
&self,
mut f: ReplyHandler,
) -> Result<(), Self::Error> {
if self.inner.reply_queue_empty() {
return Ok(());
}
loop {
let reply = self.inner.get_next_reply_as_string();
self.reply_queue.borrow_mut().push_back(reply.clone());
f(reply.as_bytes());
if self.inner.reply_queue_empty() {
break;
}
}
Ok(())
}
}
pub struct PcduTestbench {
pub mode_request_tx: mpsc::SyncSender<GenericMessage<ModeRequest>>,
pub mode_reply_rx_to_pus: mpsc::Receiver<GenericMessage<ModeReply>>,
pub mode_reply_rx_to_parent: mpsc::Receiver<GenericMessage<ModeReply>>,
pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
pub tm_rx: mpsc::Receiver<PacketAsVec>,
pub switch_request_tx: mpsc::Sender<GenericMessage<SwitchRequest>>,
pub handler: PcduHandler<SerialInterfaceTest>,
}
impl PcduTestbench {
pub fn new() -> Self {
let (mode_request_tx, mode_request_rx) = mpsc::sync_channel(5);
let (mode_reply_tx_to_pus, mode_reply_rx_to_pus) = mpsc::sync_channel(5);
let (mode_reply_tx_to_parent, mode_reply_rx_to_parent) = mpsc::sync_channel(5);
let mode_node =
ModeRequestHandlerMpscBounded::new(PCDU_HANDLER.into(), mode_request_rx);
let (composite_request_tx, composite_request_rx) = mpsc::channel();
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
let (tm_tx, tm_rx) = mpsc::sync_channel::<PacketAsVec>(5);
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
let mut handler = PcduHandler::new(
UniqueApidTargetId::new(Apid::Eps as u16, 0),
"TEST_PCDU",
mode_node,
composite_request_rx,
hk_reply_tx,
switch_reqest_rx,
TmTcSender::Heap(tm_tx.clone()),
SerialInterfaceTest::default(),
shared_switch_map,
);
handler.add_mode_parent(EPS_SUBSYSTEM.into(), mode_reply_tx_to_parent);
handler.add_mode_parent(PUS_MODE_SERVICE.into(), mode_reply_tx_to_pus);
Self {
mode_request_tx,
mode_reply_rx_to_pus,
mode_reply_rx_to_parent,
composite_request_tx,
hk_reply_rx,
tm_rx,
switch_request_tx,
handler,
}
}
pub fn verify_switch_info_req_was_sent(&self, expected_queue_len: usize) {
// Check that there is now communication happening.
let mut send_queue_mut = self.handler.com_interface.send_queue.borrow_mut();
assert_eq!(send_queue_mut.len(), expected_queue_len);
let packet_sent = send_queue_mut.pop_front().unwrap();
drop(send_queue_mut);
let pcdu_req: PcduRequest = serde_json::from_slice(&packet_sent).unwrap();
assert_eq!(pcdu_req, PcduRequest::RequestSwitchInfo);
}
pub fn verify_switch_req_was_sent(
&self,
expected_queue_len: usize,
switch_id: PcduSwitch,
target_state: SwitchStateBinary,
) {
// Check that there is now communication happening.
let mut send_queue_mut = self.handler.com_interface.send_queue.borrow_mut();
assert_eq!(send_queue_mut.len(), expected_queue_len);
let packet_sent = send_queue_mut.pop_front().unwrap();
drop(send_queue_mut);
let pcdu_req: PcduRequest = serde_json::from_slice(&packet_sent).unwrap();
assert_eq!(
pcdu_req,
PcduRequest::SwitchDevice {
switch: switch_id,
state: target_state
}
)
}
pub fn verify_switch_reply_received(
&self,
expected_queue_len: usize,
expected_map: SwitchMapBinary,
) {
// Check that a switch reply was read back.
let mut reply_received_mut = self.handler.com_interface.reply_queue.borrow_mut();
assert_eq!(reply_received_mut.len(), expected_queue_len);
let reply_received = reply_received_mut.pop_front().unwrap();
let sim_reply: SimReply = serde_json::from_str(&reply_received).unwrap();
let pcdu_reply = PcduReply::from_sim_message(&sim_reply).unwrap();
assert_eq!(pcdu_reply, PcduReply::SwitchInfo(expected_map));
}
}
#[test]
fn test_basic_handler() {
let mut testbench = PcduTestbench::new();
assert_eq!(testbench.handler.com_interface.send_queue.borrow().len(), 0);
assert_eq!(
testbench.handler.com_interface.reply_queue.borrow().len(),
0
);
assert_eq!(
testbench.handler.mode_and_submode().mode(),
DeviceMode::Off as u32
);
assert_eq!(testbench.handler.mode_and_submode().submode(), 0_u16);
testbench.handler.periodic_operation(OpCode::RegularOp);
testbench
.handler
.periodic_operation(OpCode::PollAndRecvReplies);
// Handler is OFF, no changes expected.
assert_eq!(testbench.handler.com_interface.send_queue.borrow().len(), 0);
assert_eq!(
testbench.handler.com_interface.reply_queue.borrow().len(),
0
);
assert_eq!(
testbench.handler.mode_and_submode().mode(),
DeviceMode::Off as u32
);
assert_eq!(testbench.handler.mode_and_submode().submode(), 0_u16);
}
#[test]
fn test_normal_mode() {
let mut testbench = PcduTestbench::new();
testbench
.mode_request_tx
.send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
))
.expect("failed to send mode request");
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
assert!(!switch_map_shared.valid);
drop(switch_map_shared);
testbench.handler.periodic_operation(OpCode::RegularOp);
testbench
.handler
.periodic_operation(OpCode::PollAndRecvReplies);
// Check correctness of mode.
assert_eq!(
testbench.handler.mode_and_submode().mode(),
DeviceMode::Normal as u32
);
assert_eq!(testbench.handler.mode_and_submode().submode(), 0);
testbench.verify_switch_info_req_was_sent(1);
testbench.verify_switch_reply_received(1, SwitchMapBinaryWrapper::default().0);
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
assert!(switch_map_shared.valid);
drop(switch_map_shared);
}
#[test]
fn test_switch_request_handling() {
let mut testbench = PcduTestbench::new();
testbench
.mode_request_tx
.send(GenericMessage::new(
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
forced: false,
},
))
.expect("failed to send mode request");
testbench
.switch_request_tx
.send(GenericMessage::new(
MessageMetadata::new(0, MGM_HANDLER_0.id()),
SwitchRequest::new(0, SwitchStateBinary::On),
))
.expect("failed to send switch request");
testbench.handler.periodic_operation(OpCode::RegularOp);
testbench
.handler
.periodic_operation(OpCode::PollAndRecvReplies);
testbench.verify_switch_req_was_sent(2, PcduSwitch::Mgm, SwitchStateBinary::On);
testbench.verify_switch_info_req_was_sent(1);
let mut switch_map = SwitchMapBinaryWrapper::default().0;
*switch_map
.get_mut(&PcduSwitch::Mgm)
.expect("switch state setting failed") = SwitchStateBinary::On;
testbench.verify_switch_reply_received(1, switch_map);
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
assert!(switch_map_shared.valid);
drop(switch_map_shared);
}
}

View File

@ -16,7 +16,7 @@ use satrs::{
}, },
spacepackets::time::cds::CdsTime, spacepackets::time::cds::CdsTime,
}; };
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT; use satrs_example::config::components::PUS_EVENT_MANAGEMENT;
use crate::update_time; use crate::update_time;

View File

@ -1,20 +1,19 @@
use std::time::Duration; use std::time::Duration;
use std::{ use std::{
collections::{HashSet, VecDeque}, collections::{HashSet, VecDeque},
fmt::Debug,
marker::PhantomData,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use log::{info, warn}; use log::{info, warn};
use satrs::tmtc::StoreAndSendError;
use satrs::{ use satrs::{
encoding::ccsds::{SpValidity, SpacePacketValidator}, encoding::ccsds::{SpValidity, SpacePacketValidator},
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer}, hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
spacepackets::{CcsdsPacket, PacketId}, spacepackets::{CcsdsPacket, PacketId},
tmtc::PacketSource, tmtc::{PacketSenderRaw, PacketSource},
}; };
use crate::tmtc::sender::TmTcSender;
#[derive(Default)] #[derive(Default)]
pub struct ConnectionFinishedHandler {} pub struct ConnectionFinishedHandler {}
@ -112,23 +111,31 @@ pub type TcpServer<ReceivesTc, SendError> = TcpSpacepacketsServer<
SendError, SendError,
>; >;
pub struct TcpTask(pub TcpServer<TmTcSender, StoreAndSendError>); pub struct TcpTask<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>(
pub TcpServer<TcSender, SendError>,
PhantomData<SendError>,
);
impl TcpTask { impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
TcpTask<TcSender, SendError>
{
pub fn new( pub fn new(
cfg: ServerConfig, cfg: ServerConfig,
tm_source: SyncTcpTmSource, tm_source: SyncTcpTmSource,
tc_sender: TmTcSender, tc_sender: TcSender,
valid_ids: HashSet<PacketId>, valid_ids: HashSet<PacketId>,
) -> Result<Self, std::io::Error> { ) -> Result<Self, std::io::Error> {
Ok(Self(TcpSpacepacketsServer::new( Ok(Self(
cfg, TcpSpacepacketsServer::new(
tm_source, cfg,
tc_sender, tm_source,
SimplePacketValidator { valid_ids }, tc_sender,
ConnectionFinishedHandler::default(), SimplePacketValidator { valid_ids },
None, ConnectionFinishedHandler::default(),
)?)) None,
)?,
PhantomData,
))
} }
pub fn periodic_operation(&mut self) { pub fn periodic_operation(&mut self) {

View File

@ -1,16 +1,15 @@
use core::fmt::Debug;
use std::net::{SocketAddr, UdpSocket}; use std::net::{SocketAddr, UdpSocket};
use std::sync::mpsc; use std::sync::mpsc;
use log::{info, warn}; use log::{info, warn};
use satrs::pus::HandlingStatus; use satrs::pus::HandlingStatus;
use satrs::tmtc::{PacketAsVec, PacketInPool, StoreAndSendError}; use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderRaw};
use satrs::{ use satrs::{
hal::std::udp_server::{ReceiveResult, UdpTcServer}, hal::std::udp_server::{ReceiveResult, UdpTcServer},
pool::{PoolProviderWithGuards, SharedStaticMemoryPool}, pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
}; };
use crate::tmtc::sender::TmTcSender;
pub trait UdpTmHandler { pub trait UdpTmHandler {
fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr); fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
} }
@ -66,12 +65,21 @@ impl UdpTmHandler for DynamicUdpTmHandler {
} }
} }
pub struct UdpTmtcServer<TmHandler: UdpTmHandler> { pub struct UdpTmtcServer<
pub udp_tc_server: UdpTcServer<TmTcSender, StoreAndSendError>, TcSender: PacketSenderRaw<Error = SendError>,
TmHandler: UdpTmHandler,
SendError,
> {
pub udp_tc_server: UdpTcServer<TcSender, SendError>,
pub tm_handler: TmHandler, pub tm_handler: TmHandler,
} }
impl<TmHandler: UdpTmHandler> UdpTmtcServer<TmHandler> { impl<
TcSender: PacketSenderRaw<Error = SendError>,
TmHandler: UdpTmHandler,
SendError: Debug + 'static,
> UdpTmtcServer<TcSender, TmHandler, SendError>
{
pub fn periodic_operation(&mut self) { pub fn periodic_operation(&mut self) {
loop { loop {
if self.poll_tc_server() == HandlingStatus::Empty { if self.poll_tc_server() == HandlingStatus::Empty {
@ -107,6 +115,7 @@ impl<TmHandler: UdpTmHandler> UdpTmtcServer<TmHandler> {
mod tests { mod tests {
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::{ use std::{
cell::RefCell,
collections::VecDeque, collections::VecDeque,
net::IpAddr, net::IpAddr,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -117,16 +126,30 @@ mod tests {
ecss::{tc::PusTcCreator, WritablePusPacket}, ecss::{tc::PusTcCreator, WritablePusPacket},
SpHeader, SpHeader,
}, },
tmtc::PacketSenderRaw,
ComponentId, ComponentId,
}; };
use satrs_example::config::{components, OBSW_SERVER_ADDR}; use satrs_example::config::{components, OBSW_SERVER_ADDR};
use crate::tmtc::sender::{MockSender, TmTcSender};
use super::*; use super::*;
const UDP_SERVER_ID: ComponentId = 0x05; const UDP_SERVER_ID: ComponentId = 0x05;
#[derive(Default, Debug)]
pub struct TestSender {
tc_vec: RefCell<VecDeque<PacketAsVec>>,
}
impl PacketSenderRaw for TestSender {
type Error = ();
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
let mut mut_queue = self.tc_vec.borrow_mut();
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
Ok(())
}
}
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub struct TestTmHandler { pub struct TestTmHandler {
addrs_to_send_to: Arc<Mutex<VecDeque<SocketAddr>>>, addrs_to_send_to: Arc<Mutex<VecDeque<SocketAddr>>>,
@ -141,7 +164,8 @@ mod tests {
#[test] #[test]
fn test_basic() { fn test_basic() {
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0); let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0);
let test_receiver = TmTcSender::Mock(MockSender::default()); let test_receiver = TestSender::default();
// let tc_queue = test_receiver.tc_vec.clone();
let udp_tc_server = let udp_tc_server =
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap(); UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
let tm_handler = TestTmHandler::default(); let tm_handler = TestTmHandler::default();
@ -151,13 +175,7 @@ mod tests {
tm_handler, tm_handler,
}; };
udp_dyn_server.periodic_operation(); udp_dyn_server.periodic_operation();
let queue = udp_dyn_server let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
.udp_tc_server
.tc_sender
.get_mock_sender()
.unwrap()
.0
.borrow();
assert!(queue.is_empty()); assert!(queue.is_empty());
assert!(tm_handler_calls.lock().unwrap().is_empty()); assert!(tm_handler_calls.lock().unwrap().is_empty());
} }
@ -165,7 +183,8 @@ mod tests {
#[test] #[test]
fn test_transactions() { fn test_transactions() {
let sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); let sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
let test_receiver = TmTcSender::Mock(MockSender::default()); let test_receiver = TestSender::default();
// let tc_queue = test_receiver.tc_vec.clone();
let udp_tc_server = let udp_tc_server =
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap(); UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
let server_addr = udp_tc_server.socket.local_addr().unwrap(); let server_addr = udp_tc_server.socket.local_addr().unwrap();
@ -185,13 +204,7 @@ mod tests {
client.send_to(&ping_tc, server_addr).unwrap(); client.send_to(&ping_tc, server_addr).unwrap();
udp_dyn_server.periodic_operation(); udp_dyn_server.periodic_operation();
{ {
let mut queue = udp_dyn_server let mut queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow_mut();
.udp_tc_server
.tc_sender
.get_mock_sender()
.unwrap()
.0
.borrow_mut();
assert!(!queue.is_empty()); assert!(!queue.is_empty());
let packet_with_sender = queue.pop_front().unwrap(); let packet_with_sender = queue.pop_front().unwrap();
assert_eq!(packet_with_sender.packet, ping_tc); assert_eq!(packet_with_sender.packet, ping_tc);
@ -206,13 +219,7 @@ mod tests {
assert_eq!(received_addr, client_addr); assert_eq!(received_addr, client_addr);
} }
udp_dyn_server.periodic_operation(); udp_dyn_server.periodic_operation();
let queue = udp_dyn_server let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
.udp_tc_server
.tc_sender
.get_mock_sender()
.unwrap()
.0
.borrow();
assert!(queue.is_empty()); assert!(queue.is_empty());
drop(queue); drop(queue);
// Still tries to send to the same client. // Still tries to send to the same client.

View File

@ -1,73 +1,3 @@
use std::{
net::{IpAddr, SocketAddr},
sync::{mpsc, Arc, Mutex},
thread,
time::Duration,
};
use acs::mgm::{MgmHandlerLis3Mdl, SpiDummyInterface, SpiSimInterface, SpiSimInterfaceWrapper};
use eps::{
pcdu::{PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper},
PowerSwitchHelper,
};
use events::EventHandler;
use interface::{
sim_client_udp::create_sim_client,
tcp::{SyncTcpTmSource, TcpTask},
udp::UdpTmtcServer,
};
use log::info;
use logger::setup_logger;
use pus::{
action::create_action_service,
event::create_event_service,
hk::create_hk_service,
mode::create_mode_service,
scheduler::{create_scheduler_service, TcReleaser},
stack::PusStack,
test::create_test_service,
PusTcDistributor, PusTcMpscRouter,
};
use requests::GenericRequestRouter;
use satrs::{
hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer},
mode::{Mode, ModeAndSubmode, ModeRequest, ModeRequestHandlerMpscBounded},
mode_tree::connect_mode_nodes,
pus::{event_man::EventRequestWithToken, EcssTcInMemConverter, HandlingStatus},
request::{GenericMessage, MessageMetadata},
spacepackets::time::{cds::CdsTime, TimeWriter},
};
use satrs_example::{
config::{
acs::{MGM_HANDLER_0, MGM_HANDLER_1},
components::{NO_SENDER, PCDU_HANDLER, TCP_SERVER, UDP_SERVER},
pool::create_sched_tc_pool,
tasks::{FREQ_MS_AOCS, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS},
OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT,
},
DeviceMode,
};
use tmtc::sender::TmTcSender;
use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink};
cfg_if::cfg_if! {
if #[cfg(feature = "heap_tmtc")] {
use interface::udp::DynamicUdpTmHandler;
use satrs::pus::EcssTcInVecConverter;
use tmtc::{tc_source::TcSourceTaskDynamic, tm_sink::TmSinkDynamic};
} else {
use std::sync::RwLock;
use interface::udp::StaticUdpTmHandler;
use satrs::pus::EcssTcInSharedPoolConverter;
use satrs::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
use satrs_example::config::pool::create_static_pools;
use tmtc::{
tc_source::TcSourceTaskStatic,
tm_sink::TmSinkStatic,
};
}
}
mod acs; mod acs;
mod eps; mod eps;
mod events; mod events;
@ -76,74 +6,103 @@ mod interface;
mod logger; mod logger;
mod pus; mod pus;
mod requests; mod requests;
mod spi;
mod tmtc; mod tmtc;
fn main() { use crate::eps::pcdu::{
setup_logger().expect("setting up logging with fern failed"); PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper,
println!("Running OBSW example"); };
use crate::eps::PowerSwitchHelper;
use crate::events::EventHandler;
use crate::interface::udp::DynamicUdpTmHandler;
use crate::pus::stack::PusStack;
use crate::tmtc::tc_source::{TcSourceTaskDynamic, TcSourceTaskStatic};
use crate::tmtc::tm_sink::{TmSinkDynamic, TmSinkStatic};
use log::info;
use pus::test::create_test_service_dynamic;
use satrs::hal::std::tcp_server::ServerConfig;
use satrs::hal::std::udp_server::UdpTcServer;
use satrs::pus::HandlingStatus;
use satrs::request::GenericMessage;
use satrs::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools};
use satrs_example::config::tasks::{
FREQ_MS_AOCS, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS,
};
use satrs_example::config::{OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT};
cfg_if::cfg_if! { use crate::acs::mgm::{
if #[cfg(not(feature = "heap_tmtc"))] { MgmHandlerLis3Mdl, MpscModeLeafInterface, SpiDummyInterface, SpiSimInterface,
let (tm_pool, tc_pool) = create_static_pools(); SpiSimInterfaceWrapper,
let shared_tm_pool = Arc::new(RwLock::new(tm_pool)); };
let shared_tc_pool = Arc::new(RwLock::new(tc_pool)); use crate::interface::sim_client_udp::create_sim_client;
let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool); use crate::interface::tcp::{SyncTcpTmSource, TcpTask};
let shared_tc_pool_wrapper = SharedPacketPool::new(&shared_tc_pool); use crate::interface::udp::{StaticUdpTmHandler, UdpTmtcServer};
} use crate::logger::setup_logger;
} use crate::pus::action::{create_action_service_dynamic, create_action_service_static};
use crate::pus::event::{create_event_service_dynamic, create_event_service_static};
use crate::pus::hk::{create_hk_service_dynamic, create_hk_service_static};
use crate::pus::mode::{create_mode_service_dynamic, create_mode_service_static};
use crate::pus::scheduler::{create_scheduler_service_dynamic, create_scheduler_service_static};
use crate::pus::test::create_test_service_static;
use crate::pus::{PusTcDistributor, PusTcMpscRouter};
use crate::requests::{CompositeRequest, GenericRequestRouter};
use satrs::mode::ModeRequest;
use satrs::pus::event_man::EventRequestWithToken;
use satrs::spacepackets::{time::cds::CdsTime, time::TimeWriter};
use satrs_example::config::components::{MGM_HANDLER_0, PCDU_HANDLER, TCP_SERVER, UDP_SERVER};
use std::net::{IpAddr, SocketAddr};
use std::sync::{mpsc, Mutex};
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Duration;
#[allow(dead_code)]
fn static_tmtc_pool_main() {
let (tm_pool, tc_pool) = create_static_pools();
let shared_tm_pool = Arc::new(RwLock::new(tm_pool));
let shared_tc_pool = Arc::new(RwLock::new(tc_pool));
let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool);
let shared_tc_pool_wrapper = SharedPacketPool::new(&shared_tc_pool);
let (tc_source_tx, tc_source_rx) = mpsc::sync_channel(50); let (tc_source_tx, tc_source_rx) = mpsc::sync_channel(50);
let (tm_sink_tx, tm_sink_rx) = mpsc::sync_channel(50); let (tm_sink_tx, tm_sink_rx) = mpsc::sync_channel(50);
let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50); let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50);
cfg_if::cfg_if! { let tm_sink_tx_sender =
if #[cfg(not(feature = "heap_tmtc"))] { PacketSenderWithSharedPool::new(tm_sink_tx.clone(), shared_tm_pool_wrapper.clone());
let tm_sender = TmTcSender::Static(
PacketSenderWithSharedPool::new(tm_sink_tx.clone(), shared_tm_pool_wrapper.clone())
);
} else if #[cfg(feature = "heap_tmtc")] {
let tm_sender = TmTcSender::Heap(tm_sink_tx.clone());
}
}
let (sim_request_tx, sim_request_rx) = mpsc::channel(); let (sim_request_tx, sim_request_rx) = mpsc::channel();
let (mgm_0_sim_reply_tx, mgm_0_sim_reply_rx) = mpsc::channel(); let (mgm_sim_reply_tx, mgm_sim_reply_rx) = mpsc::channel();
let (mgm_1_sim_reply_tx, mgm_1_sim_reply_rx) = mpsc::channel();
let (pcdu_sim_reply_tx, pcdu_sim_reply_rx) = mpsc::channel(); let (pcdu_sim_reply_tx, pcdu_sim_reply_rx) = mpsc::channel();
let mut opt_sim_client = create_sim_client(sim_request_rx); let mut opt_sim_client = create_sim_client(sim_request_rx);
let (mgm_0_handler_composite_tx, mgm_0_handler_composite_rx) = mpsc::sync_channel(10); let (mgm_handler_composite_tx, mgm_handler_composite_rx) =
let (mgm_1_handler_composite_tx, mgm_1_handler_composite_rx) = mpsc::sync_channel(10); mpsc::sync_channel::<GenericMessage<CompositeRequest>>(10);
let (pcdu_handler_composite_tx, pcdu_handler_composite_rx) = mpsc::sync_channel(30); let (pcdu_handler_composite_tx, pcdu_handler_composite_rx) =
let (mgm_0_handler_mode_tx, mgm_0_handler_mode_rx) = mpsc::sync_channel(5); mpsc::sync_channel::<GenericMessage<CompositeRequest>>(30);
let (mgm_1_handler_mode_tx, mgm_1_handler_mode_rx) = mpsc::sync_channel(5);
let (pcdu_handler_mode_tx, pcdu_handler_mode_rx) = mpsc::sync_channel(5); let (mgm_handler_mode_tx, mgm_handler_mode_rx) =
mpsc::sync_channel::<GenericMessage<ModeRequest>>(5);
let (pcdu_handler_mode_tx, pcdu_handler_mode_rx) =
mpsc::sync_channel::<GenericMessage<ModeRequest>>(5);
// Some request are targetable. This map is used to retrieve sender handles based on a target ID. // Some request are targetable. This map is used to retrieve sender handles based on a target ID.
let mut request_map = GenericRequestRouter::default(); let mut request_map = GenericRequestRouter::default();
request_map request_map
.composite_router_map .composite_router_map
.insert(MGM_HANDLER_0.id(), mgm_0_handler_composite_tx); .insert(MGM_HANDLER_0.id(), mgm_handler_composite_tx);
request_map request_map
.composite_router_map .mode_router_map
.insert(MGM_HANDLER_0.id(), mgm_1_handler_composite_tx); .insert(MGM_HANDLER_0.id(), mgm_handler_mode_tx);
request_map request_map
.composite_router_map .composite_router_map
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx); .insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
request_map
.mode_router_map
.insert(PCDU_HANDLER.id(), pcdu_handler_mode_tx);
// This helper structure is used by all telecommand providers which need to send telecommands // This helper structure is used by all telecommand providers which need to send telecommands
// to the TC source. // to the TC source.
cfg_if::cfg_if! { let tc_source = PacketSenderWithSharedPool::new(tc_source_tx, shared_tc_pool_wrapper.clone());
if #[cfg(not(feature = "heap_tmtc"))] {
let tc_sender_with_shared_pool =
PacketSenderWithSharedPool::new(tc_source_tx, shared_tc_pool_wrapper.clone());
let tc_in_mem_converter =
EcssTcInMemConverter::Static(EcssTcInSharedPoolConverter::new(shared_tc_pool, 4096));
} else if #[cfg(feature = "heap_tmtc")] {
let tc_in_mem_converter = EcssTcInMemConverter::Heap(EcssTcInVecConverter::default());
}
}
// Create event handling components // Create event handling components
// These sender handles are used to send event requests, for example to enable or disable // These sender handles are used to send event requests, for example to enable or disable
@ -155,24 +114,17 @@ fn main() {
// in the sat-rs documentation. // in the sat-rs documentation.
let mut event_handler = EventHandler::new(tm_sink_tx.clone(), event_rx, event_request_rx); let mut event_handler = EventHandler::new(tm_sink_tx.clone(), event_rx, event_request_rx);
let (pus_test_tx, pus_test_rx) = mpsc::sync_channel(20); let (pus_test_tx, pus_test_rx) = mpsc::channel();
let (pus_event_tx, pus_event_rx) = mpsc::sync_channel(10); let (pus_event_tx, pus_event_rx) = mpsc::channel();
let (pus_sched_tx, pus_sched_rx) = mpsc::sync_channel(50); let (pus_sched_tx, pus_sched_rx) = mpsc::channel();
let (pus_hk_tx, pus_hk_rx) = mpsc::sync_channel(50); let (pus_hk_tx, pus_hk_rx) = mpsc::channel();
let (pus_action_tx, pus_action_rx) = mpsc::sync_channel(50); let (pus_action_tx, pus_action_rx) = mpsc::channel();
let (pus_mode_tx, pus_mode_rx) = mpsc::sync_channel(50); let (pus_mode_tx, pus_mode_rx) = mpsc::channel();
let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel(); let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel();
let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::sync_channel(50); let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::channel();
let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::sync_channel(30); let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel();
cfg_if::cfg_if! {
if #[cfg(not(feature = "heap_tmtc"))] {
let tc_releaser = TcReleaser::Static(tc_sender_with_shared_pool.clone());
} else if #[cfg(feature = "heap_tmtc")] {
let tc_releaser = TcReleaser::Heap(tc_source_tx.clone());
}
}
let pus_router = PusTcMpscRouter { let pus_router = PusTcMpscRouter {
test_tc_sender: pus_test_tx, test_tc_sender: pus_test_tx,
event_tc_sender: pus_event_tx, event_tc_sender: pus_event_tx,
@ -181,42 +133,41 @@ fn main() {
action_tc_sender: pus_action_tx, action_tc_sender: pus_action_tx,
mode_tc_sender: pus_mode_tx, mode_tc_sender: pus_mode_tx,
}; };
let pus_test_service = create_test_service( let pus_test_service = create_test_service_static(
tm_sender.clone(), tm_sink_tx_sender.clone(),
tc_in_mem_converter.clone(), shared_tc_pool.clone(),
event_tx.clone(), event_tx.clone(),
pus_test_rx, pus_test_rx,
); );
let pus_scheduler_service = create_scheduler_service( let pus_scheduler_service = create_scheduler_service_static(
tm_sender.clone(), tm_sink_tx_sender.clone(),
tc_in_mem_converter.clone(), tc_source.clone(),
tc_releaser,
pus_sched_rx, pus_sched_rx,
create_sched_tc_pool(), create_sched_tc_pool(),
); );
let pus_event_service = create_event_service( let pus_event_service = create_event_service_static(
tm_sender.clone(), tm_sink_tx_sender.clone(),
tc_in_mem_converter.clone(), shared_tc_pool.clone(),
pus_event_rx, pus_event_rx,
event_request_tx, event_request_tx,
); );
let pus_action_service = create_action_service( let pus_action_service = create_action_service_static(
tm_sender.clone(), tm_sink_tx_sender.clone(),
tc_in_mem_converter.clone(), shared_tc_pool.clone(),
pus_action_rx, pus_action_rx,
request_map.clone(), request_map.clone(),
pus_action_reply_rx, pus_action_reply_rx,
); );
let pus_hk_service = create_hk_service( let pus_hk_service = create_hk_service_static(
tm_sender.clone(), tm_sink_tx_sender.clone(),
tc_in_mem_converter.clone(), shared_tc_pool.clone(),
pus_hk_rx, pus_hk_rx,
request_map.clone(), request_map.clone(),
pus_hk_reply_rx, pus_hk_reply_rx,
); );
let pus_mode_service = create_mode_service( let pus_mode_service = create_mode_service_static(
tm_sender.clone(), tm_sink_tx_sender.clone(),
tc_in_mem_converter.clone(), shared_tc_pool.clone(),
pus_mode_rx, pus_mode_rx,
request_map, request_map,
pus_mode_reply_rx, pus_mode_reply_rx,
@ -230,36 +181,21 @@ fn main() {
pus_mode_service, pus_mode_service,
); );
cfg_if::cfg_if! { let mut tmtc_task = TcSourceTaskStatic::new(
if #[cfg(not(feature = "heap_tmtc"))] { shared_tc_pool_wrapper.clone(),
let mut tmtc_task = TcSourceTask::Static(TcSourceTaskStatic::new( tc_source_rx,
shared_tc_pool_wrapper.clone(), PusTcDistributor::new(tm_sink_tx_sender, pus_router),
tc_source_rx, );
PusTcDistributor::new(tm_sender.clone(), pus_router),
));
let tc_sender = TmTcSender::Static(tc_sender_with_shared_pool);
let udp_tm_handler = StaticUdpTmHandler {
tm_rx: tm_server_rx,
tm_store: shared_tm_pool.clone(),
};
} else if #[cfg(feature = "heap_tmtc")] {
let mut tmtc_task = TcSourceTask::Heap(TcSourceTaskDynamic::new(
tc_source_rx,
PusTcDistributor::new(tm_sender.clone(), pus_router),
));
let tc_sender = TmTcSender::Heap(tc_source_tx.clone());
let udp_tm_handler = DynamicUdpTmHandler {
tm_rx: tm_server_rx,
};
}
}
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
let udp_tc_server = UdpTcServer::new(UDP_SERVER.id(), sock_addr, 2048, tc_sender.clone()) let udp_tc_server = UdpTcServer::new(UDP_SERVER.id(), sock_addr, 2048, tc_source.clone())
.expect("creating UDP TMTC server failed"); .expect("creating UDP TMTC server failed");
let mut udp_tmtc_server = UdpTmtcServer { let mut udp_tmtc_server = UdpTmtcServer {
udp_tc_server, udp_tc_server,
tm_handler: udp_tm_handler, tm_handler: StaticUdpTmHandler {
tm_rx: tm_server_rx,
tm_store: shared_tm_pool.clone(),
},
}; };
let tcp_server_cfg = ServerConfig::new( let tcp_server_cfg = ServerConfig::new(
@ -273,96 +209,60 @@ fn main() {
let mut tcp_server = TcpTask::new( let mut tcp_server = TcpTask::new(
tcp_server_cfg, tcp_server_cfg,
sync_tm_tcp_source.clone(), sync_tm_tcp_source.clone(),
tc_sender, tc_source.clone(),
PACKET_ID_VALIDATOR.clone(), PACKET_ID_VALIDATOR.clone(),
) )
.expect("tcp server creation failed"); .expect("tcp server creation failed");
cfg_if::cfg_if! { let mut tm_sink = TmSinkStatic::new(
if #[cfg(not(feature = "heap_tmtc"))] { shared_tm_pool_wrapper,
let mut tm_sink = TmSink::Static(TmSinkStatic::new( sync_tm_tcp_source,
shared_tm_pool_wrapper, tm_sink_rx,
sync_tm_tcp_source, tm_server_tx,
tm_sink_rx, );
tm_server_tx,
)); let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
} else if #[cfg(feature = "heap_tmtc")] { mpsc::sync_channel(5);
let mut tm_sink = TmSink::Heap(TmSinkDynamic::new(
sync_tm_tcp_source,
tm_sink_rx,
tm_server_tx,
));
}
}
let shared_switch_set = Arc::new(Mutex::default()); let shared_switch_set = Arc::new(Mutex::default());
let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20); let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20);
let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone()); let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone());
let shared_mgm_0_set = Arc::default(); let shared_mgm_set = Arc::default();
let shared_mgm_1_set = Arc::default(); let mgm_mode_leaf_interface = MpscModeLeafInterface {
let mgm_0_mode_node = request_rx: mgm_handler_mode_rx,
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_0.into(), mgm_0_handler_mode_rx); reply_to_pus_tx: pus_mode_reply_tx.clone(),
let mgm_1_mode_node = reply_to_parent_tx: mgm_handler_mode_reply_to_parent_tx,
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_1.into(), mgm_1_handler_mode_rx); };
let (mgm_0_spi_interface, mgm_1_spi_interface) =
if let Some(sim_client) = opt_sim_client.as_mut() { let mgm_spi_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
sim_client sim_client.add_reply_recipient(satrs_minisim::SimComponent::MgmLis3Mdl, mgm_sim_reply_tx);
.add_reply_recipient(satrs_minisim::SimComponent::Mgm0Lis3Mdl, mgm_0_sim_reply_tx); SpiSimInterfaceWrapper::Sim(SpiSimInterface {
sim_client sim_request_tx: sim_request_tx.clone(),
.add_reply_recipient(satrs_minisim::SimComponent::Mgm1Lis3Mdl, mgm_1_sim_reply_tx); sim_reply_rx: mgm_sim_reply_rx,
( })
SpiSimInterfaceWrapper::Sim(SpiSimInterface { } else {
sim_request_tx: sim_request_tx.clone(), SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default())
sim_reply_rx: mgm_0_sim_reply_rx, };
}), let mut mgm_handler = MgmHandlerLis3Mdl::new(
SpiSimInterfaceWrapper::Sim(SpiSimInterface {
sim_request_tx: sim_request_tx.clone(),
sim_reply_rx: mgm_1_sim_reply_rx,
}),
)
} else {
(
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()),
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()),
)
};
let mut mgm_0_handler = MgmHandlerLis3Mdl::new(
MGM_HANDLER_0, MGM_HANDLER_0,
"MGM_0", "MGM_0",
mgm_0_mode_node, mgm_mode_leaf_interface,
mgm_0_handler_composite_rx, mgm_handler_composite_rx,
pus_hk_reply_tx.clone(), pus_hk_reply_tx.clone(),
switch_helper.clone(), switch_helper.clone(),
tm_sender.clone(), tm_sink_tx.clone(),
mgm_0_spi_interface, mgm_spi_interface,
shared_mgm_0_set, shared_mgm_set,
);
let mut mgm_1_handler = MgmHandlerLis3Mdl::new(
MGM_HANDLER_1,
"MGM_1",
mgm_1_mode_node,
mgm_1_handler_composite_rx,
pus_hk_reply_tx.clone(),
switch_helper.clone(),
tm_sender.clone(),
mgm_1_spi_interface,
shared_mgm_1_set,
);
// Connect PUS service to device handlers.
connect_mode_nodes(
&mut pus_stack.mode_srv,
mgm_0_handler_mode_tx,
&mut mgm_0_handler,
pus_mode_reply_tx.clone(),
);
connect_mode_nodes(
&mut pus_stack.mode_srv,
mgm_1_handler_mode_tx,
&mut mgm_1_handler,
pus_mode_reply_tx.clone(),
); );
let (pcdu_handler_mode_reply_to_parent_tx, _pcdu_handler_mode_reply_to_parent_rx) =
mpsc::sync_channel(10);
let pcdu_mode_leaf_interface = MpscModeLeafInterface {
request_rx: pcdu_handler_mode_rx,
reply_to_pus_tx: pus_mode_reply_tx,
reply_to_parent_tx: pcdu_handler_mode_reply_to_parent_tx,
};
let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() { let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
sim_client.add_reply_recipient(satrs_minisim::SimComponent::Pcdu, pcdu_sim_reply_tx); sim_client.add_reply_recipient(satrs_minisim::SimComponent::Pcdu, pcdu_sim_reply_tx);
SerialSimInterfaceWrapper::Sim(SerialInterfaceToSim::new( SerialSimInterfaceWrapper::Sim(SerialInterfaceToSim::new(
@ -372,36 +272,17 @@ fn main() {
} else { } else {
SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default()) SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default())
}; };
let pcdu_mode_node =
ModeRequestHandlerMpscBounded::new(PCDU_HANDLER.into(), pcdu_handler_mode_rx);
let mut pcdu_handler = PcduHandler::new( let mut pcdu_handler = PcduHandler::new(
PCDU_HANDLER, PCDU_HANDLER,
"PCDU", "PCDU",
pcdu_mode_node, pcdu_mode_leaf_interface,
pcdu_handler_composite_rx, pcdu_handler_composite_rx,
pus_hk_reply_tx, pus_hk_reply_tx,
switch_request_rx, switch_request_rx,
tm_sender.clone(), tm_sink_tx,
pcdu_serial_interface, pcdu_serial_interface,
shared_switch_set, shared_switch_set,
); );
connect_mode_nodes(
&mut pus_stack.mode_srv,
pcdu_handler_mode_tx.clone(),
&mut pcdu_handler,
pus_mode_reply_tx,
);
// The PCDU is a critical component which should be in normal mode immediately.
pcdu_handler_mode_tx
.send(GenericMessage::new(
MessageMetadata::new(0, NO_SENDER),
ModeRequest::SetMode {
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as Mode, 0),
forced: false,
},
))
.expect("sending initial mode request failed");
info!("Starting TMTC and UDP task"); info!("Starting TMTC and UDP task");
let jh_udp_tmtc = thread::Builder::new() let jh_udp_tmtc = thread::Builder::new()
@ -454,8 +335,7 @@ fn main() {
let jh_aocs = thread::Builder::new() let jh_aocs = thread::Builder::new()
.name("sat-rs aocs".to_string()) .name("sat-rs aocs".to_string())
.spawn(move || loop { .spawn(move || loop {
mgm_0_handler.periodic_operation(); mgm_handler.periodic_operation();
mgm_1_handler.periodic_operation();
thread::sleep(Duration::from_millis(FREQ_MS_AOCS)); thread::sleep(Duration::from_millis(FREQ_MS_AOCS));
}) })
.unwrap(); .unwrap();
@ -466,13 +346,11 @@ fn main() {
.spawn(move || loop { .spawn(move || loop {
// TODO: We should introduce something like a fixed timeslot helper to allow a more // TODO: We should introduce something like a fixed timeslot helper to allow a more
// declarative API. It would also be very useful for the AOCS task. // declarative API. It would also be very useful for the AOCS task.
// pcdu_handler.periodic_operation(eps::pcdu::OpCode::RegularOp);
// TODO: The fixed timeslot handler exists.. use it.
pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::RegularOp);
thread::sleep(Duration::from_millis(50)); thread::sleep(Duration::from_millis(50));
pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies); pcdu_handler.periodic_operation(eps::pcdu::OpCode::PollAndRecvReplies);
thread::sleep(Duration::from_millis(50)); thread::sleep(Duration::from_millis(50));
pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies); pcdu_handler.periodic_operation(eps::pcdu::OpCode::PollAndRecvReplies);
thread::sleep(Duration::from_millis(300)); thread::sleep(Duration::from_millis(300));
}) })
.unwrap(); .unwrap();
@ -508,6 +386,315 @@ fn main() {
.expect("Joining PUS handler thread failed"); .expect("Joining PUS handler thread failed");
} }
#[allow(dead_code)]
fn dyn_tmtc_pool_main() {
let (tc_source_tx, tc_source_rx) = mpsc::channel();
let (tm_sink_tx, tm_sink_rx) = mpsc::channel();
let (tm_server_tx, tm_server_rx) = mpsc::channel();
let (sim_request_tx, sim_request_rx) = mpsc::channel();
let (mgm_sim_reply_tx, mgm_sim_reply_rx) = mpsc::channel();
let (pcdu_sim_reply_tx, pcdu_sim_reply_rx) = mpsc::channel();
let mut opt_sim_client = create_sim_client(sim_request_rx);
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
let (mgm_handler_composite_tx, mgm_handler_composite_rx) =
mpsc::sync_channel::<GenericMessage<CompositeRequest>>(5);
let (pcdu_handler_composite_tx, pcdu_handler_composite_rx) =
mpsc::sync_channel::<GenericMessage<CompositeRequest>>(10);
let (mgm_handler_mode_tx, mgm_handler_mode_rx) =
mpsc::sync_channel::<GenericMessage<ModeRequest>>(5);
let (pcdu_handler_mode_tx, pcdu_handler_mode_rx) =
mpsc::sync_channel::<GenericMessage<ModeRequest>>(10);
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
let mut request_map = GenericRequestRouter::default();
request_map
.composite_router_map
.insert(MGM_HANDLER_0.id(), mgm_handler_composite_tx);
request_map
.mode_router_map
.insert(MGM_HANDLER_0.id(), mgm_handler_mode_tx);
request_map
.composite_router_map
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
request_map
.mode_router_map
.insert(PCDU_HANDLER.id(), pcdu_handler_mode_tx);
// Create event handling components
// These sender handles are used to send event requests, for example to enable or disable
// certain events.
let (event_tx, event_rx) = mpsc::sync_channel(100);
let (event_request_tx, event_request_rx) = mpsc::channel::<EventRequestWithToken>();
// The event task is the core handler to perform the event routing and TM handling as specified
// in the sat-rs documentation.
let mut event_handler = EventHandler::new(tm_sink_tx.clone(), event_rx, event_request_rx);
let (pus_test_tx, pus_test_rx) = mpsc::channel();
let (pus_event_tx, pus_event_rx) = mpsc::channel();
let (pus_sched_tx, pus_sched_rx) = mpsc::channel();
let (pus_hk_tx, pus_hk_rx) = mpsc::channel();
let (pus_action_tx, pus_action_rx) = mpsc::channel();
let (pus_mode_tx, pus_mode_rx) = mpsc::channel();
let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel();
let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::channel();
let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel();
let pus_router = PusTcMpscRouter {
test_tc_sender: pus_test_tx,
event_tc_sender: pus_event_tx,
sched_tc_sender: pus_sched_tx,
hk_tc_sender: pus_hk_tx,
action_tc_sender: pus_action_tx,
mode_tc_sender: pus_mode_tx,
};
let pus_test_service =
create_test_service_dynamic(tm_sink_tx.clone(), event_tx.clone(), pus_test_rx);
let pus_scheduler_service = create_scheduler_service_dynamic(
tm_sink_tx.clone(),
tc_source_tx.clone(),
pus_sched_rx,
create_sched_tc_pool(),
);
let pus_event_service =
create_event_service_dynamic(tm_sink_tx.clone(), pus_event_rx, event_request_tx);
let pus_action_service = create_action_service_dynamic(
tm_sink_tx.clone(),
pus_action_rx,
request_map.clone(),
pus_action_reply_rx,
);
let pus_hk_service = create_hk_service_dynamic(
tm_sink_tx.clone(),
pus_hk_rx,
request_map.clone(),
pus_hk_reply_rx,
);
let pus_mode_service = create_mode_service_dynamic(
tm_sink_tx.clone(),
pus_mode_rx,
request_map,
pus_mode_reply_rx,
);
let mut pus_stack = PusStack::new(
pus_test_service,
pus_hk_service,
pus_event_service,
pus_action_service,
pus_scheduler_service,
pus_mode_service,
);
let mut tmtc_task = TcSourceTaskDynamic::new(
tc_source_rx,
PusTcDistributor::new(tm_sink_tx.clone(), pus_router),
);
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
let udp_tc_server = UdpTcServer::new(UDP_SERVER.id(), sock_addr, 2048, tc_source_tx.clone())
.expect("creating UDP TMTC server failed");
let mut udp_tmtc_server = UdpTmtcServer {
udp_tc_server,
tm_handler: DynamicUdpTmHandler {
tm_rx: tm_server_rx,
},
};
let tcp_server_cfg = ServerConfig::new(
TCP_SERVER.id(),
sock_addr,
Duration::from_millis(400),
4096,
8192,
);
let sync_tm_tcp_source = SyncTcpTmSource::new(200);
let mut tcp_server = TcpTask::new(
tcp_server_cfg,
sync_tm_tcp_source.clone(),
tc_source_tx.clone(),
PACKET_ID_VALIDATOR.clone(),
)
.expect("tcp server creation failed");
let mut tm_funnel = TmSinkDynamic::new(sync_tm_tcp_source, tm_sink_rx, tm_server_tx);
let shared_switch_set = Arc::new(Mutex::default());
let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20);
let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone());
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
mpsc::sync_channel(5);
let shared_mgm_set = Arc::default();
let mode_leaf_interface = MpscModeLeafInterface {
request_rx: mgm_handler_mode_rx,
reply_to_pus_tx: pus_mode_reply_tx.clone(),
reply_to_parent_tx: mgm_handler_mode_reply_to_parent_tx,
};
let mgm_spi_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
sim_client.add_reply_recipient(satrs_minisim::SimComponent::MgmLis3Mdl, mgm_sim_reply_tx);
SpiSimInterfaceWrapper::Sim(SpiSimInterface {
sim_request_tx: sim_request_tx.clone(),
sim_reply_rx: mgm_sim_reply_rx,
})
} else {
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default())
};
let mut mgm_handler = MgmHandlerLis3Mdl::new(
MGM_HANDLER_0,
"MGM_0",
mode_leaf_interface,
mgm_handler_composite_rx,
pus_hk_reply_tx.clone(),
switch_helper.clone(),
tm_sink_tx.clone(),
mgm_spi_interface,
shared_mgm_set,
);
let (pcdu_handler_mode_reply_to_parent_tx, _pcdu_handler_mode_reply_to_parent_rx) =
mpsc::sync_channel(10);
let pcdu_mode_leaf_interface = MpscModeLeafInterface {
request_rx: pcdu_handler_mode_rx,
reply_to_pus_tx: pus_mode_reply_tx,
reply_to_parent_tx: pcdu_handler_mode_reply_to_parent_tx,
};
let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
sim_client.add_reply_recipient(satrs_minisim::SimComponent::Pcdu, pcdu_sim_reply_tx);
SerialSimInterfaceWrapper::Sim(SerialInterfaceToSim::new(
sim_request_tx.clone(),
pcdu_sim_reply_rx,
))
} else {
SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default())
};
let mut pcdu_handler = PcduHandler::new(
PCDU_HANDLER,
"PCDU",
pcdu_mode_leaf_interface,
pcdu_handler_composite_rx,
pus_hk_reply_tx,
switch_request_rx,
tm_sink_tx,
pcdu_serial_interface,
shared_switch_set,
);
info!("Starting TMTC and UDP task");
let jh_udp_tmtc = thread::Builder::new()
.name("sat-rs tmtc-udp".to_string())
.spawn(move || {
info!("Running UDP server on port {SERVER_PORT}");
loop {
udp_tmtc_server.periodic_operation();
tmtc_task.periodic_operation();
thread::sleep(Duration::from_millis(FREQ_MS_UDP_TMTC));
}
})
.unwrap();
info!("Starting TCP task");
let jh_tcp = thread::Builder::new()
.name("sat-rs tcp".to_string())
.spawn(move || {
info!("Running TCP server on port {SERVER_PORT}");
loop {
tcp_server.periodic_operation();
}
})
.unwrap();
info!("Starting TM funnel task");
let jh_tm_funnel = thread::Builder::new()
.name("sat-rs tm-sink".to_string())
.spawn(move || loop {
tm_funnel.operation();
})
.unwrap();
let mut opt_jh_sim_client = None;
if let Some(mut sim_client) = opt_sim_client {
info!("Starting UDP sim client task");
opt_jh_sim_client = Some(
thread::Builder::new()
.name("sat-rs sim adapter".to_string())
.spawn(move || loop {
if sim_client.operation() == HandlingStatus::Empty {
std::thread::sleep(Duration::from_millis(SIM_CLIENT_IDLE_DELAY_MS));
}
})
.unwrap(),
);
}
info!("Starting AOCS thread");
let jh_aocs = thread::Builder::new()
.name("sat-rs aocs".to_string())
.spawn(move || loop {
mgm_handler.periodic_operation();
thread::sleep(Duration::from_millis(FREQ_MS_AOCS));
})
.unwrap();
info!("Starting EPS thread");
let jh_eps = thread::Builder::new()
.name("sat-rs eps".to_string())
.spawn(move || loop {
// TODO: We should introduce something like a fixed timeslot helper to allow a more
// declarative API. It would also be very useful for the AOCS task.
pcdu_handler.periodic_operation(eps::pcdu::OpCode::RegularOp);
thread::sleep(Duration::from_millis(50));
pcdu_handler.periodic_operation(eps::pcdu::OpCode::PollAndRecvReplies);
thread::sleep(Duration::from_millis(50));
pcdu_handler.periodic_operation(eps::pcdu::OpCode::PollAndRecvReplies);
thread::sleep(Duration::from_millis(300));
})
.unwrap();
info!("Starting PUS handler thread");
let jh_pus_handler = thread::Builder::new()
.name("sat-rs pus".to_string())
.spawn(move || loop {
pus_stack.periodic_operation();
event_handler.periodic_operation();
thread::sleep(Duration::from_millis(FREQ_MS_PUS_STACK));
})
.unwrap();
jh_udp_tmtc
.join()
.expect("Joining UDP TMTC server thread failed");
jh_tcp
.join()
.expect("Joining TCP TMTC server thread failed");
jh_tm_funnel
.join()
.expect("Joining TM Funnel thread failed");
if let Some(jh_sim_client) = opt_jh_sim_client {
jh_sim_client
.join()
.expect("Joining SIM client thread failed");
}
jh_aocs.join().expect("Joining AOCS thread failed");
jh_eps.join().expect("Joining EPS thread failed");
jh_pus_handler
.join()
.expect("Joining PUS handler thread failed");
}
fn main() {
setup_logger().expect("setting up logging with fern failed");
println!("Running OBSW example");
#[cfg(not(feature = "dyn_tmtc"))]
static_tmtc_pool_main();
#[cfg(feature = "dyn_tmtc")]
dyn_tmtc_pool_main();
}
pub fn update_time(time_provider: &mut CdsTime, timestamp: &mut [u8]) { pub fn update_time(time_provider: &mut CdsTime, timestamp: &mut [u8]) {
time_provider time_provider
.update_from_now() .update_from_now()

View File

@ -1,5 +1,6 @@
use log::warn; use log::warn;
use satrs::action::{ActionRequest, ActionRequestVariant}; use satrs::action::{ActionRequest, ActionRequestVariant};
use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::action::{ use satrs::pus::action::{
ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap, ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap,
}; };
@ -9,20 +10,21 @@ use satrs::pus::verification::{
VerificationReportingProvider, VerificationToken, VerificationReportingProvider, VerificationToken,
}; };
use satrs::pus::{ use satrs::pus::{
ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTmSender, EcssTmtcError, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
GenericConversionError, MpscTcReceiver, PusPacketHandlingError, PusReplyHandler, EcssTcInVecConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver,
PusServiceHelper, PusTcToRequestConverter, MpscTmAsVecSender, PusPacketHandlingError, PusReplyHandler, PusServiceHelper,
PusTcToRequestConverter,
}; };
use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::request::{GenericMessage, UniqueApidTargetId};
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId}; use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId};
use satrs_example::config::pus::PUS_ACTION_SERVICE; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_ACTION_SERVICE;
use satrs_example::config::tmtc_err; use satrs_example::config::tmtc_err;
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use crate::requests::GenericRequestRouter; use crate::requests::GenericRequestRouter;
use crate::tmtc::sender::TmTcSender;
use super::{ use super::{
create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus, create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus,
@ -205,20 +207,20 @@ impl PusTcToRequestConverter<ActivePusActionRequestStd, ActionRequest> for Actio
} }
} }
pub fn create_action_service( pub fn create_action_service_static(
tm_sender: TmTcSender, tm_sender: PacketSenderWithSharedPool,
tc_in_mem_converter: EcssTcInMemConverter, tc_pool: SharedStaticMemoryPool,
pus_action_rx: mpsc::Receiver<EcssTcAndToken>, pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
action_router: GenericRequestRouter, action_router: GenericRequestRouter,
reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>, reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
) -> ActionServiceWrapper { ) -> ActionServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
let action_request_handler = PusTargetedRequestService::new( let action_request_handler = PusTargetedRequestService::new(
PusServiceHelper::new( PusServiceHelper::new(
PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.id(),
pus_action_rx, pus_action_rx,
tm_sender, tm_sender,
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid), create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid),
tc_in_mem_converter, EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
), ),
ActionRequestConverter::default(), ActionRequestConverter::default(),
// TODO: Implementation which does not use run-time allocation? Maybe something like // TODO: Implementation which does not use run-time allocation? Maybe something like
@ -233,9 +235,36 @@ pub fn create_action_service(
} }
} }
pub struct ActionServiceWrapper { pub fn create_action_service_dynamic(
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
action_router: GenericRequestRouter,
reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
) -> ActionServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
let action_request_handler = PusTargetedRequestService::new(
PusServiceHelper::new(
PUS_ACTION_SERVICE.id(),
pus_action_rx,
tm_funnel_tx,
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid),
EcssTcInVecConverter::default(),
),
ActionRequestConverter::default(),
DefaultActiveActionRequestMap::default(),
ActionReplyHandler::default(),
action_router,
reply_receiver,
);
ActionServiceWrapper {
service: action_request_handler,
}
}
pub struct ActionServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
pub(crate) service: PusTargetedRequestService< pub(crate) service: PusTargetedRequestService<
MpscTcReceiver, MpscTcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter, VerificationReporter,
ActionRequestConverter, ActionRequestConverter,
ActionReplyHandler, ActionReplyHandler,
@ -246,7 +275,9 @@ pub struct ActionServiceWrapper {
>, >,
} }
impl TargetedPusService for ActionServiceWrapper { impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
for ActionServiceWrapper<TmSender, TcInMemConverter>
{
const SERVICE_ID: u8 = PusServiceId::Action as u8; const SERVICE_ID: u8 = PusServiceId::Action as u8;
const SERVICE_STR: &'static str = "action"; const SERVICE_STR: &'static str = "action";
@ -272,10 +303,9 @@ mod tests {
use satrs::pus::test_util::{ use satrs::pus::test_util::{
TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1, TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
}; };
use satrs::pus::verification;
use satrs::pus::verification::test_util::TestVerificationReporter; use satrs::pus::verification::test_util::TestVerificationReporter;
use satrs::pus::{verification, EcssTcInVecConverter};
use satrs::request::MessageMetadata; use satrs::request::MessageMetadata;
use satrs::tmtc::PacketAsVec;
use satrs::ComponentId; use satrs::ComponentId;
use satrs::{ use satrs::{
res_code::ResultU16, res_code::ResultU16,
@ -308,7 +338,7 @@ mod tests {
{ {
pub fn new_for_action(owner_id: ComponentId, target_id: ComponentId) -> Self { pub fn new_for_action(owner_id: ComponentId, target_id: ComponentId) -> Self {
let _ = env_logger::builder().is_test(true).try_init(); let _ = env_logger::builder().is_test(true).try_init();
let (tm_funnel_tx, tm_funnel_rx) = mpsc::sync_channel(5); let (tm_funnel_tx, tm_funnel_rx) = mpsc::channel();
let (pus_action_tx, pus_action_rx) = mpsc::channel(); let (pus_action_tx, pus_action_rx) = mpsc::channel();
let (action_reply_tx, action_reply_rx) = mpsc::channel(); let (action_reply_tx, action_reply_rx) = mpsc::channel();
let (action_req_tx, action_req_rx) = mpsc::sync_channel(10); let (action_req_tx, action_req_rx) = mpsc::sync_channel(10);
@ -322,9 +352,9 @@ mod tests {
PusServiceHelper::new( PusServiceHelper::new(
owner_id, owner_id,
pus_action_rx, pus_action_rx,
TmTcSender::Heap(tm_funnel_tx.clone()), tm_funnel_tx.clone(),
verif_reporter, verif_reporter,
EcssTcInMemConverter::Heap(EcssTcInVecConverter::default()), EcssTcInVecConverter::default(),
), ),
ActionRequestConverter::default(), ActionRequestConverter::default(),
DefaultActiveActionRequestMap::default(), DefaultActiveActionRequestMap::default(),

View File

@ -1,32 +1,34 @@
use std::sync::mpsc; use std::sync::mpsc;
use crate::pus::create_verification_reporter; use crate::pus::create_verification_reporter;
use crate::tmtc::sender::TmTcSender; use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::event_man::EventRequestWithToken; use satrs::pus::event_man::EventRequestWithToken;
use satrs::pus::event_srv::PusEventServiceHandler; use satrs::pus::event_srv::PusEventServiceHandler;
use satrs::pus::verification::VerificationReporter; use satrs::pus::verification::VerificationReporter;
use satrs::pus::{ use satrs::pus::{
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver, DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter,
PartialPusHandlingError, PusServiceHelper, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver,
MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper,
}; };
use satrs::spacepackets::ecss::PusServiceId; use satrs::spacepackets::ecss::PusServiceId;
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_EVENT_MANAGEMENT;
use super::{DirectPusService, HandlingStatus}; use super::{DirectPusService, HandlingStatus};
pub fn create_event_service( pub fn create_event_service_static(
tm_sender: TmTcSender, tm_sender: PacketSenderWithSharedPool,
tm_in_pool_converter: EcssTcInMemConverter, tc_pool: SharedStaticMemoryPool,
pus_event_rx: mpsc::Receiver<EcssTcAndToken>, pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
event_request_tx: mpsc::Sender<EventRequestWithToken>, event_request_tx: mpsc::Sender<EventRequestWithToken>,
) -> EventServiceWrapper { ) -> EventServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
let pus_5_handler = PusEventServiceHandler::new( let pus_5_handler = PusEventServiceHandler::new(
PusServiceHelper::new( PusServiceHelper::new(
PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.id(),
pus_event_rx, pus_event_rx,
tm_sender, tm_sender,
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid), create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
tm_in_pool_converter, EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
), ),
event_request_tx, event_request_tx,
); );
@ -35,16 +37,34 @@ pub fn create_event_service(
} }
} }
pub struct EventServiceWrapper { pub fn create_event_service_dynamic(
pub handler: PusEventServiceHandler< tm_funnel_tx: mpsc::Sender<PacketAsVec>,
MpscTcReceiver, pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
TmTcSender, event_request_tx: mpsc::Sender<EventRequestWithToken>,
EcssTcInMemConverter, ) -> EventServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
VerificationReporter, let pus_5_handler = PusEventServiceHandler::new(
>, PusServiceHelper::new(
PUS_EVENT_MANAGEMENT.id(),
pus_event_rx,
tm_funnel_tx,
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
EcssTcInVecConverter::default(),
),
event_request_tx,
);
EventServiceWrapper {
handler: pus_5_handler,
}
} }
impl DirectPusService for EventServiceWrapper { pub struct EventServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
pub handler:
PusEventServiceHandler<MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
}
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
for EventServiceWrapper<TmSender, TcInMemConverter>
{
const SERVICE_ID: u8 = PusServiceId::Event as u8; const SERVICE_ID: u8 = PusServiceId::Event as u8;
const SERVICE_STR: &'static str = "events"; const SERVICE_STR: &'static str = "events";

View File

@ -1,26 +1,28 @@
use derive_new::new; use derive_new::new;
use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId}; use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId};
use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::verification::{ use satrs::pus::verification::{
FailParams, TcStateAccepted, TcStateStarted, VerificationReporter, FailParams, TcStateAccepted, TcStateStarted, VerificationReporter,
VerificationReportingProvider, VerificationToken, VerificationReportingProvider, VerificationToken,
}; };
use satrs::pus::{ use satrs::pus::{
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken, ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken,
EcssTcInMemConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender,
EcssTmtcError, GenericConversionError, MpscTcReceiver, MpscTmAsVecSender,
PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter, PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter,
}; };
use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::request::{GenericMessage, UniqueApidTargetId};
use satrs::res_code::ResultU16; use satrs::res_code::ResultU16;
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId}; use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId};
use satrs_example::config::pus::PUS_HK_SERVICE; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_HK_SERVICE;
use satrs_example::config::{hk_err, tmtc_err}; use satrs_example::config::{hk_err, tmtc_err};
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler}; use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler};
use crate::requests::GenericRequestRouter; use crate::requests::GenericRequestRouter;
use crate::tmtc::sender::TmTcSender;
use super::{HandlingStatus, PusTargetedRequestService, TargetedPusService}; use super::{HandlingStatus, PusTargetedRequestService, TargetedPusService};
@ -31,7 +33,6 @@ pub struct HkReply {
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
#[allow(dead_code)]
pub enum HkReplyVariant { pub enum HkReplyVariant {
Ack, Ack,
Failed(ResultU16), Failed(ResultU16),
@ -240,20 +241,20 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
} }
} }
pub fn create_hk_service( pub fn create_hk_service_static(
tm_sender: TmTcSender, tm_sender: PacketSenderWithSharedPool,
tc_in_mem_converter: EcssTcInMemConverter, tc_pool: SharedStaticMemoryPool,
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>, pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
request_router: GenericRequestRouter, request_router: GenericRequestRouter,
reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>, reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>,
) -> HkServiceWrapper { ) -> HkServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
let pus_3_handler = PusTargetedRequestService::new( let pus_3_handler = PusTargetedRequestService::new(
PusServiceHelper::new( PusServiceHelper::new(
PUS_HK_SERVICE.id(), PUS_HK_SERVICE.id(),
pus_hk_rx, pus_hk_rx,
tm_sender, tm_sender,
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid), create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid),
tc_in_mem_converter, EcssTcInSharedStoreConverter::new(tc_pool, 2048),
), ),
HkRequestConverter::default(), HkRequestConverter::default(),
DefaultActiveRequestMap::default(), DefaultActiveRequestMap::default(),
@ -266,9 +267,36 @@ pub fn create_hk_service(
} }
} }
pub struct HkServiceWrapper { pub fn create_hk_service_dynamic(
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
request_router: GenericRequestRouter,
reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>,
) -> HkServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
let pus_3_handler = PusTargetedRequestService::new(
PusServiceHelper::new(
PUS_HK_SERVICE.id(),
pus_hk_rx,
tm_funnel_tx,
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid),
EcssTcInVecConverter::default(),
),
HkRequestConverter::default(),
DefaultActiveRequestMap::default(),
HkReplyHandler::default(),
request_router,
reply_receiver,
);
HkServiceWrapper {
service: pus_3_handler,
}
}
pub struct HkServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
pub(crate) service: PusTargetedRequestService< pub(crate) service: PusTargetedRequestService<
MpscTcReceiver, MpscTcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter, VerificationReporter,
HkRequestConverter, HkRequestConverter,
HkReplyHandler, HkReplyHandler,
@ -279,7 +307,9 @@ pub struct HkServiceWrapper {
>, >,
} }
impl TargetedPusService for HkServiceWrapper { impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
for HkServiceWrapper<TmSender, TcInMemConverter>
{
const SERVICE_ID: u8 = PusServiceId::Housekeeping as u8; const SERVICE_ID: u8 = PusServiceId::Housekeeping as u8;
const SERVICE_STR: &'static str = "housekeeping"; const SERVICE_STR: &'static str = "housekeeping";

View File

@ -1,5 +1,4 @@
use crate::requests::GenericRequestRouter; use crate::requests::GenericRequestRouter;
use crate::tmtc::sender::TmTcSender;
use log::warn; use log::warn;
use satrs::pool::PoolAddr; use satrs::pool::PoolAddr;
use satrs::pus::verification::{ use satrs::pus::verification::{
@ -7,10 +6,10 @@ use satrs::pus::verification::{
VerificationReporterCfg, VerificationReportingProvider, VerificationToken, VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
}; };
use satrs::pus::{ use satrs::pus::{
ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConversionProvider, ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter,
EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, GenericRoutingError,
GenericRoutingError, HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, PusServiceHelper,
PusServiceHelper, PusTcToRequestConverter, TcInMemory, PusTcToRequestConverter, TcInMemory,
}; };
use satrs::queue::{GenericReceiveError, GenericSendError}; use satrs::queue::{GenericReceiveError, GenericSendError};
use satrs::request::{Apid, GenericMessage, MessageMetadata}; use satrs::request::{Apid, GenericMessage, MessageMetadata};
@ -18,11 +17,11 @@ use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::{PusPacket, PusServiceId}; use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
use satrs::tmtc::{PacketAsVec, PacketInPool}; use satrs::tmtc::{PacketAsVec, PacketInPool};
use satrs::ComponentId; use satrs::ComponentId;
use satrs_example::config::pus::PUS_ROUTING_SERVICE; use satrs_example::config::components::PUS_ROUTING_SERVICE;
use satrs_example::config::{tmtc_err, CustomPusServiceId}; use satrs_example::config::{tmtc_err, CustomPusServiceId};
use satrs_example::TimestampHelper; use satrs_example::TimestampHelper;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::mpsc; use std::sync::mpsc::{self, Sender};
pub mod action; pub mod action;
pub mod event; pub mod event;
@ -41,26 +40,24 @@ pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> Verifi
/// Simple router structure which forwards PUS telecommands to dedicated handlers. /// Simple router structure which forwards PUS telecommands to dedicated handlers.
pub struct PusTcMpscRouter { pub struct PusTcMpscRouter {
pub test_tc_sender: mpsc::SyncSender<EcssTcAndToken>, pub test_tc_sender: Sender<EcssTcAndToken>,
pub event_tc_sender: mpsc::SyncSender<EcssTcAndToken>, pub event_tc_sender: Sender<EcssTcAndToken>,
pub sched_tc_sender: mpsc::SyncSender<EcssTcAndToken>, pub sched_tc_sender: Sender<EcssTcAndToken>,
pub hk_tc_sender: mpsc::SyncSender<EcssTcAndToken>, pub hk_tc_sender: Sender<EcssTcAndToken>,
#[allow(dead_code)] pub action_tc_sender: Sender<EcssTcAndToken>,
pub action_tc_sender: mpsc::SyncSender<EcssTcAndToken>, pub mode_tc_sender: Sender<EcssTcAndToken>,
pub mode_tc_sender: mpsc::SyncSender<EcssTcAndToken>,
} }
pub struct PusTcDistributor { pub struct PusTcDistributor<TmSender: EcssTmSender> {
#[allow(dead_code)]
pub id: ComponentId, pub id: ComponentId,
pub tm_sender: TmTcSender, pub tm_sender: TmSender,
pub verif_reporter: VerificationReporter, pub verif_reporter: VerificationReporter,
pub pus_router: PusTcMpscRouter, pub pus_router: PusTcMpscRouter,
stamp_helper: TimestampHelper, stamp_helper: TimestampHelper,
} }
impl PusTcDistributor { impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
pub fn new(tm_sender: TmTcSender, pus_router: PusTcMpscRouter) -> Self { pub fn new(tm_sender: TmSender, pus_router: PusTcMpscRouter) -> Self {
Self { Self {
id: PUS_ROUTING_SERVICE.raw(), id: PUS_ROUTING_SERVICE.raw(),
tm_sender, tm_sender,
@ -269,6 +266,8 @@ pub trait DirectPusService {
/// 3. [Self::check_for_request_timeouts] which checks for request timeouts, covering step 7. /// 3. [Self::check_for_request_timeouts] which checks for request timeouts, covering step 7.
pub struct PusTargetedRequestService< pub struct PusTargetedRequestService<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>, RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>, ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
@ -278,7 +277,7 @@ pub struct PusTargetedRequestService<
ReplyType, ReplyType,
> { > {
pub service_helper: pub service_helper:
PusServiceHelper<TcReceiver, TmTcSender, EcssTcInMemConverter, VerificationReporter>, PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub request_router: GenericRequestRouter, pub request_router: GenericRequestRouter,
pub request_converter: RequestConverter, pub request_converter: RequestConverter,
pub active_request_map: ActiveRequestMap, pub active_request_map: ActiveRequestMap,
@ -289,6 +288,8 @@ pub struct PusTargetedRequestService<
impl< impl<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>, RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>, ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
@ -299,6 +300,8 @@ impl<
> >
PusTargetedRequestService< PusTargetedRequestService<
TcReceiver, TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter, VerificationReporter,
RequestConverter, RequestConverter,
ReplyHandler, ReplyHandler,
@ -313,8 +316,8 @@ where
pub fn new( pub fn new(
service_helper: PusServiceHelper< service_helper: PusServiceHelper<
TcReceiver, TcReceiver,
TmTcSender, TmSender,
EcssTcInMemConverter, TcInMemConverter,
VerificationReporter, VerificationReporter,
>, >,
request_converter: RequestConverter, request_converter: RequestConverter,
@ -540,7 +543,7 @@ pub(crate) mod tests {
use satrs::{ use satrs::{
pus::{ pus::{
verification::test_util::TestVerificationReporter, ActivePusRequestStd, verification::test_util::TestVerificationReporter, ActivePusRequestStd,
ActiveRequestMapProvider, MpscTcReceiver, ActiveRequestMapProvider, EcssTcInVecConverter, MpscTcReceiver,
}, },
request::UniqueApidTargetId, request::UniqueApidTargetId,
spacepackets::{ spacepackets::{
@ -761,6 +764,8 @@ pub(crate) mod tests {
> { > {
pub service: PusTargetedRequestService< pub service: PusTargetedRequestService<
MpscTcReceiver, MpscTcReceiver,
MpscTmAsVecSender,
EcssTcInVecConverter,
TestVerificationReporter, TestVerificationReporter,
RequestConverter, RequestConverter,
ReplyHandler, ReplyHandler,

View File

@ -1,15 +1,15 @@
use derive_new::new; use derive_new::new;
use satrs::mode_tree::{ModeNode, ModeParent}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::pus::PUS_MODE_SERVICE;
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use crate::requests::GenericRequestRouter; use crate::requests::GenericRequestRouter;
use crate::tmtc::sender::TmTcSender; use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::verification::VerificationReporter; use satrs::pus::verification::VerificationReporter;
use satrs::pus::{ use satrs::pus::{
DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver, DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
PusPacketHandlingError, PusServiceHelper, EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlingError,
PusServiceHelper,
}; };
use satrs::request::GenericMessage; use satrs::request::GenericMessage;
use satrs::{ use satrs::{
@ -34,6 +34,7 @@ use satrs::{
}, },
ComponentId, ComponentId,
}; };
use satrs_example::config::components::PUS_MODE_SERVICE;
use satrs_example::config::{mode_err, tmtc_err, CustomPusServiceId}; use satrs_example::config::{mode_err, tmtc_err, CustomPusServiceId};
use super::{ use super::{
@ -109,7 +110,6 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
), ),
)?; )?;
} }
ModeReply::ModeInfo(_mode_and_submode) => (),
}; };
Ok(true) Ok(true)
} }
@ -190,13 +190,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
} }
let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..]) let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..])
.expect("mode and submode extraction failed"); .expect("mode and submode extraction failed");
Ok(( Ok((active_request, ModeRequest::SetMode(mode_and_submode)))
active_request,
ModeRequest::SetMode {
mode_and_submode,
forced: false,
},
))
} }
Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)), Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)),
Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)), Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)),
@ -208,20 +202,20 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
} }
} }
pub fn create_mode_service( pub fn create_mode_service_static(
tm_sender: TmTcSender, tm_sender: PacketSenderWithSharedPool,
tc_in_mem_converter: EcssTcInMemConverter, tc_pool: SharedStaticMemoryPool,
pus_action_rx: mpsc::Receiver<EcssTcAndToken>, pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
mode_router: GenericRequestRouter, mode_router: GenericRequestRouter,
reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>, reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>,
) -> ModeServiceWrapper { ) -> ModeServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
let mode_request_handler = PusTargetedRequestService::new( let mode_request_handler = PusTargetedRequestService::new(
PusServiceHelper::new( PusServiceHelper::new(
PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.id(),
pus_action_rx, pus_action_rx,
tm_sender, tm_sender,
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid), create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid),
tc_in_mem_converter, EcssTcInSharedStoreConverter::new(tc_pool, 2048),
), ),
ModeRequestConverter::default(), ModeRequestConverter::default(),
DefaultActiveRequestMap::default(), DefaultActiveRequestMap::default(),
@ -234,9 +228,36 @@ pub fn create_mode_service(
} }
} }
pub struct ModeServiceWrapper { pub fn create_mode_service_dynamic(
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
mode_router: GenericRequestRouter,
reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>,
) -> ModeServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
let mode_request_handler = PusTargetedRequestService::new(
PusServiceHelper::new(
PUS_MODE_SERVICE.id(),
pus_action_rx,
tm_funnel_tx,
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid),
EcssTcInVecConverter::default(),
),
ModeRequestConverter::default(),
DefaultActiveRequestMap::default(),
ModeReplyHandler::new(PUS_MODE_SERVICE.id()),
mode_router,
reply_receiver,
);
ModeServiceWrapper {
service: mode_request_handler,
}
}
pub struct ModeServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
pub(crate) service: PusTargetedRequestService< pub(crate) service: PusTargetedRequestService<
MpscTcReceiver, MpscTcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter, VerificationReporter,
ModeRequestConverter, ModeRequestConverter,
ModeReplyHandler, ModeReplyHandler,
@ -247,24 +268,9 @@ pub struct ModeServiceWrapper {
>, >,
} }
impl ModeNode for ModeServiceWrapper { impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
fn id(&self) -> ComponentId { for ModeServiceWrapper<TmSender, TcInMemConverter>
self.service.service_helper.id() {
}
}
impl ModeParent for ModeServiceWrapper {
type Sender = mpsc::SyncSender<GenericMessage<ModeRequest>>;
fn add_mode_child(&mut self, id: ComponentId, request_sender: Self::Sender) {
self.service
.request_router
.mode_router_map
.insert(id, request_sender);
}
}
impl TargetedPusService for ModeServiceWrapper {
const SERVICE_ID: u8 = CustomPusServiceId::Mode as u8; const SERVICE_ID: u8 = CustomPusServiceId::Mode as u8;
const SERVICE_STR: &'static str = "mode"; const SERVICE_STR: &'static str = "mode";
@ -340,13 +346,7 @@ mod tests {
let (_active_req, req) = testbench let (_active_req, req) = testbench
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0) .convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
.expect("conversion has failed"); .expect("conversion has failed");
assert_eq!( assert_eq!(req, ModeRequest::SetMode(mode_and_submode));
req,
ModeRequest::SetMode {
mode_and_submode,
forced: false
}
);
} }
#[test] #[test]

View File

@ -2,28 +2,28 @@ use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use crate::pus::create_verification_reporter; use crate::pus::create_verification_reporter;
use crate::tmtc::sender::TmTcSender;
use log::info; use log::info;
use satrs::pool::{PoolProvider, StaticMemoryPool}; use satrs::pool::{PoolProvider, StaticMemoryPool};
use satrs::pus::scheduler::{PusScheduler, TcInfo}; use satrs::pus::scheduler::{PusScheduler, TcInfo};
use satrs::pus::scheduler_srv::PusSchedServiceHandler; use satrs::pus::scheduler_srv::PusSchedServiceHandler;
use satrs::pus::verification::VerificationReporter; use satrs::pus::verification::VerificationReporter;
use satrs::pus::{ use satrs::pus::{
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver, DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter,
PartialPusHandlingError, PusServiceHelper, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver,
MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper,
}; };
use satrs::spacepackets::ecss::PusServiceId; use satrs::spacepackets::ecss::PusServiceId;
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool}; use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool};
use satrs::ComponentId; use satrs::ComponentId;
use satrs_example::config::pus::PUS_SCHED_SERVICE; use satrs_example::config::components::PUS_SCHED_SERVICE;
use super::{DirectPusService, HandlingStatus}; use super::{DirectPusService, HandlingStatus};
pub trait TcReleaseProvider { pub trait TcReleaser {
fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool; fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool;
} }
impl TcReleaseProvider for PacketSenderWithSharedPool { impl TcReleaser for PacketSenderWithSharedPool {
fn release( fn release(
&mut self, &mut self,
sender_id: ComponentId, sender_id: ComponentId,
@ -48,7 +48,7 @@ impl TcReleaseProvider for PacketSenderWithSharedPool {
} }
} }
impl TcReleaseProvider for mpsc::SyncSender<PacketAsVec> { impl TcReleaser for mpsc::Sender<PacketAsVec> {
fn release( fn release(
&mut self, &mut self,
sender_id: ComponentId, sender_id: ComponentId,
@ -65,35 +65,23 @@ impl TcReleaseProvider for mpsc::SyncSender<PacketAsVec> {
} }
} }
#[allow(dead_code)] pub struct SchedulingServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
pub enum TcReleaser { {
Static(PacketSenderWithSharedPool),
Heap(mpsc::SyncSender<PacketAsVec>),
}
impl TcReleaseProvider for TcReleaser {
fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool {
match self {
TcReleaser::Static(sender) => sender.release(sender_id, enabled, info, tc),
TcReleaser::Heap(sender) => sender.release(sender_id, enabled, info, tc),
}
}
}
pub struct SchedulingServiceWrapper {
pub pus_11_handler: PusSchedServiceHandler< pub pus_11_handler: PusSchedServiceHandler<
MpscTcReceiver, MpscTcReceiver,
TmTcSender, TmSender,
EcssTcInMemConverter, TcInMemConverter,
VerificationReporter, VerificationReporter,
PusScheduler, PusScheduler,
>, >,
pub sched_tc_pool: StaticMemoryPool, pub sched_tc_pool: StaticMemoryPool,
pub releaser_buf: [u8; 4096], pub releaser_buf: [u8; 4096],
pub tc_releaser: TcReleaser, pub tc_releaser: Box<dyn TcReleaser + Send>,
} }
impl DirectPusService for SchedulingServiceWrapper { impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
for SchedulingServiceWrapper<TmSender, TcInMemConverter>
{
const SERVICE_ID: u8 = PusServiceId::Verification as u8; const SERVICE_ID: u8 = PusServiceId::Verification as u8;
const SERVICE_STR: &'static str = "verification"; const SERVICE_STR: &'static str = "verification";
@ -146,7 +134,9 @@ impl DirectPusService for SchedulingServiceWrapper {
} }
} }
impl SchedulingServiceWrapper { impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
SchedulingServiceWrapper<TmSender, TcInMemConverter>
{
pub fn release_tcs(&mut self) { pub fn release_tcs(&mut self) {
let id = self.pus_11_handler.service_helper.id(); let id = self.pus_11_handler.service_helper.id();
let releaser = |enabled: bool, info: &TcInfo, tc: &[u8]| -> bool { let releaser = |enabled: bool, info: &TcInfo, tc: &[u8]| -> bool {
@ -172,13 +162,12 @@ impl SchedulingServiceWrapper {
} }
} }
pub fn create_scheduler_service( pub fn create_scheduler_service_static(
tm_sender: TmTcSender, tm_sender: PacketSenderWithSharedPool,
tc_in_mem_converter: EcssTcInMemConverter, tc_releaser: PacketSenderWithSharedPool,
tc_releaser: TcReleaser,
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>, pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
sched_tc_pool: StaticMemoryPool, sched_tc_pool: StaticMemoryPool,
) -> SchedulingServiceWrapper { ) -> SchedulingServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5)) let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
.expect("Creating PUS Scheduler failed"); .expect("Creating PUS Scheduler failed");
let pus_11_handler = PusSchedServiceHandler::new( let pus_11_handler = PusSchedServiceHandler::new(
@ -187,7 +176,7 @@ pub fn create_scheduler_service(
pus_sched_rx, pus_sched_rx,
tm_sender, tm_sender,
create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid), create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid),
tc_in_mem_converter, EcssTcInSharedStoreConverter::new(tc_releaser.shared_packet_store().0.clone(), 2048),
), ),
scheduler, scheduler,
); );
@ -195,6 +184,34 @@ pub fn create_scheduler_service(
pus_11_handler, pus_11_handler,
sched_tc_pool, sched_tc_pool,
releaser_buf: [0; 4096], releaser_buf: [0; 4096],
tc_releaser, tc_releaser: Box::new(tc_releaser),
}
}
pub fn create_scheduler_service_dynamic(
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
tc_source_sender: mpsc::Sender<PacketAsVec>,
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
sched_tc_pool: StaticMemoryPool,
) -> SchedulingServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
//let sched_srv_receiver =
//MpscTcReceiver::new(PUS_SCHED_SERVICE.raw(), "PUS_11_TC_RECV", pus_sched_rx);
let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
.expect("Creating PUS Scheduler failed");
let pus_11_handler = PusSchedServiceHandler::new(
PusServiceHelper::new(
PUS_SCHED_SERVICE.id(),
pus_sched_rx,
tm_funnel_tx,
create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid),
EcssTcInVecConverter::default(),
),
scheduler,
);
SchedulingServiceWrapper {
pus_11_handler,
sched_tc_pool,
releaser_buf: [0; 4096],
tc_releaser: Box::new(tc_source_sender),
} }
} }

View File

@ -1,6 +1,9 @@
use crate::pus::mode::ModeServiceWrapper; use crate::pus::mode::ModeServiceWrapper;
use derive_new::new; use derive_new::new;
use satrs::spacepackets::time::{cds, TimeWriter}; use satrs::{
pus::{EcssTcInMemConverter, EcssTmSender},
spacepackets::time::{cds, TimeWriter},
};
use super::{ use super::{
action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper, action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper,
@ -8,17 +11,21 @@ use super::{
HandlingStatus, TargetedPusService, HandlingStatus, TargetedPusService,
}; };
// TODO: For better extensibility, we could create 2 vectors: One for direct PUS services and one
// for targeted services..
#[derive(new)] #[derive(new)]
pub struct PusStack { pub struct PusStack<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
pub test_srv: TestCustomServiceWrapper, test_srv: TestCustomServiceWrapper<TmSender, TcInMemConverter>,
pub hk_srv_wrapper: HkServiceWrapper, hk_srv_wrapper: HkServiceWrapper<TmSender, TcInMemConverter>,
pub event_srv: EventServiceWrapper, event_srv: EventServiceWrapper<TmSender, TcInMemConverter>,
pub action_srv_wrapper: ActionServiceWrapper, action_srv_wrapper: ActionServiceWrapper<TmSender, TcInMemConverter>,
pub schedule_srv: SchedulingServiceWrapper, schedule_srv: SchedulingServiceWrapper<TmSender, TcInMemConverter>,
pub mode_srv: ModeServiceWrapper, mode_srv: ModeServiceWrapper<TmSender, TcInMemConverter>,
} }
impl PusStack { impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
PusStack<TmSender, TcInMemConverter>
{
pub fn periodic_operation(&mut self) { pub fn periodic_operation(&mut self) {
// Release all telecommands which reached their release time before calling the service // Release all telecommands which reached their release time before calling the service
// handlers. // handlers.

View File

@ -1,34 +1,35 @@
use crate::pus::create_verification_reporter; use crate::pus::create_verification_reporter;
use crate::tmtc::sender::TmTcSender;
use log::info; use log::info;
use satrs::event_man::{EventMessage, EventMessageU32}; use satrs::event_man::{EventMessage, EventMessageU32};
use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::test::PusService17TestHandler; use satrs::pus::test::PusService17TestHandler;
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider}; use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
use satrs::pus::PartialPusHandlingError;
use satrs::pus::{ use satrs::pus::{
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConversionProvider, DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter,
EcssTcInMemConverter, MpscTcReceiver, PusServiceHelper, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PusServiceHelper,
}; };
use satrs::pus::{EcssTcInSharedStoreConverter, PartialPusHandlingError};
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::{PusPacket, PusServiceId}; use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
use satrs_example::config::pus::PUS_TEST_SERVICE; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_TEST_SERVICE;
use satrs_example::config::{tmtc_err, TEST_EVENT}; use satrs_example::config::{tmtc_err, TEST_EVENT};
use std::sync::mpsc; use std::sync::mpsc;
use super::{DirectPusService, HandlingStatus}; use super::{DirectPusService, HandlingStatus};
pub fn create_test_service( pub fn create_test_service_static(
tm_sender: TmTcSender, tm_sender: PacketSenderWithSharedPool,
tc_in_mem_converter: EcssTcInMemConverter, tc_pool: SharedStaticMemoryPool,
event_sender: mpsc::SyncSender<EventMessageU32>, event_sender: mpsc::SyncSender<EventMessageU32>,
pus_test_rx: mpsc::Receiver<EcssTcAndToken>, pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
) -> TestCustomServiceWrapper { ) -> TestCustomServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new( let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.id(),
pus_test_rx, pus_test_rx,
tm_sender, tm_sender,
create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid), create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid),
tc_in_mem_converter, EcssTcInSharedStoreConverter::new(tc_pool, 2048),
)); ));
TestCustomServiceWrapper { TestCustomServiceWrapper {
handler: pus17_handler, handler: pus17_handler,
@ -36,17 +37,34 @@ pub fn create_test_service(
} }
} }
pub struct TestCustomServiceWrapper { pub fn create_test_service_dynamic(
pub handler: PusService17TestHandler< tm_funnel_tx: mpsc::Sender<PacketAsVec>,
MpscTcReceiver, event_sender: mpsc::SyncSender<EventMessageU32>,
TmTcSender, pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
EcssTcInMemConverter, ) -> TestCustomServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
VerificationReporter, let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
>, PUS_TEST_SERVICE.id(),
pus_test_rx,
tm_funnel_tx,
create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid),
EcssTcInVecConverter::default(),
));
TestCustomServiceWrapper {
handler: pus17_handler,
event_tx: event_sender,
}
}
pub struct TestCustomServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
{
pub handler:
PusService17TestHandler<MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub event_tx: mpsc::SyncSender<EventMessageU32>, pub event_tx: mpsc::SyncSender<EventMessageU32>,
} }
impl DirectPusService for TestCustomServiceWrapper { impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
for TestCustomServiceWrapper<TmSender, TcInMemConverter>
{
const SERVICE_ID: u8 = PusServiceId::Test as u8; const SERVICE_ID: u8 = PusServiceId::Test as u8;
const SERVICE_STR: &'static str = "test"; const SERVICE_STR: &'static str = "test";

View File

@ -14,7 +14,7 @@ use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::PusPacket; use satrs::spacepackets::ecss::PusPacket;
use satrs::ComponentId; use satrs::ComponentId;
use satrs_example::config::pus::PUS_ROUTING_SERVICE; use satrs_example::config::components::PUS_ROUTING_SERVICE;
use satrs_example::config::tmtc_err; use satrs_example::config::tmtc_err;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -26,7 +26,6 @@ pub enum CompositeRequest {
#[derive(Clone)] #[derive(Clone)]
pub struct GenericRequestRouter { pub struct GenericRequestRouter {
#[allow(dead_code)]
pub id: ComponentId, pub id: ComponentId,
// All messages which do not have a dedicated queue. // All messages which do not have a dedicated queue.
pub composite_router_map: pub composite_router_map:

View File

@ -1,6 +0,0 @@
use core::fmt::Debug;
pub trait SpiInterface {
type Error: Debug;
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error>;
}

View File

@ -1,3 +1,2 @@
pub mod sender;
pub mod tc_source; pub mod tc_source;
pub mod tm_sink; pub mod tm_sink;

View File

@ -1,75 +0,0 @@
use std::{cell::RefCell, collections::VecDeque, sync::mpsc};
use satrs::{
pus::EcssTmSender,
queue::GenericSendError,
spacepackets::ecss::WritablePusPacket,
tmtc::{PacketAsVec, PacketSenderRaw, PacketSenderWithSharedPool, StoreAndSendError},
ComponentId,
};
#[derive(Default, Debug, Clone)]
pub struct MockSender(pub RefCell<VecDeque<PacketAsVec>>);
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum TmTcSender {
Static(PacketSenderWithSharedPool),
Heap(mpsc::SyncSender<PacketAsVec>),
Mock(MockSender),
}
impl TmTcSender {
#[allow(dead_code)]
pub fn get_mock_sender(&mut self) -> Option<&mut MockSender> {
match self {
TmTcSender::Mock(sender) => Some(sender),
_ => None,
}
}
}
impl EcssTmSender for TmTcSender {
fn send_tm(
&self,
sender_id: satrs::ComponentId,
tm: satrs::pus::PusTmVariant,
) -> Result<(), satrs::pus::EcssTmtcError> {
match self {
TmTcSender::Static(sync_sender) => sync_sender.send_tm(sender_id, tm),
TmTcSender::Heap(sync_sender) => match tm {
satrs::pus::PusTmVariant::InStore(_) => panic!("can not send TM in store"),
satrs::pus::PusTmVariant::Direct(pus_tm_creator) => sync_sender
.send(PacketAsVec::new(sender_id, pus_tm_creator.to_vec()?))
.map_err(|_| GenericSendError::RxDisconnected.into()),
},
TmTcSender::Mock(_) => Ok(()),
}
}
}
impl PacketSenderRaw for TmTcSender {
type Error = StoreAndSendError;
fn send_packet(&self, sender_id: ComponentId, packet: &[u8]) -> Result<(), Self::Error> {
match self {
TmTcSender::Static(packet_sender_with_shared_pool) => {
packet_sender_with_shared_pool.send_packet(sender_id, packet)
}
TmTcSender::Heap(sync_sender) => sync_sender
.send_packet(sender_id, packet)
.map_err(StoreAndSendError::Send),
TmTcSender::Mock(sender) => sender.send_packet(sender_id, packet),
}
}
}
impl PacketSenderRaw for MockSender {
type Error = StoreAndSendError;
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
let mut mut_queue = self.0.borrow_mut();
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
Ok(())
}
}

View File

@ -1,10 +1,12 @@
use satrs::{ use satrs::{
pool::PoolProvider, pool::PoolProvider,
pus::HandlingStatus, pus::HandlingStatus,
tmtc::{PacketAsVec, PacketInPool, SharedPacketPool}, tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool},
}; };
use std::sync::mpsc::{self, TryRecvError}; use std::sync::mpsc::{self, TryRecvError};
use satrs::pus::MpscTmAsVecSender;
use crate::pus::PusTcDistributor; use crate::pus::PusTcDistributor;
// TC source components where static pools are the backing memory of the received telecommands. // TC source components where static pools are the backing memory of the received telecommands.
@ -12,15 +14,14 @@ pub struct TcSourceTaskStatic {
shared_tc_pool: SharedPacketPool, shared_tc_pool: SharedPacketPool,
tc_receiver: mpsc::Receiver<PacketInPool>, tc_receiver: mpsc::Receiver<PacketInPool>,
tc_buf: [u8; 4096], tc_buf: [u8; 4096],
pus_distributor: PusTcDistributor, pus_distributor: PusTcDistributor<PacketSenderWithSharedPool>,
} }
#[allow(dead_code)]
impl TcSourceTaskStatic { impl TcSourceTaskStatic {
pub fn new( pub fn new(
shared_tc_pool: SharedPacketPool, shared_tc_pool: SharedPacketPool,
tc_receiver: mpsc::Receiver<PacketInPool>, tc_receiver: mpsc::Receiver<PacketInPool>,
pus_receiver: PusTcDistributor, pus_receiver: PusTcDistributor<PacketSenderWithSharedPool>,
) -> Self { ) -> Self {
Self { Self {
shared_tc_pool, shared_tc_pool,
@ -66,12 +67,14 @@ impl TcSourceTaskStatic {
// TC source components where the heap is the backing memory of the received telecommands. // TC source components where the heap is the backing memory of the received telecommands.
pub struct TcSourceTaskDynamic { pub struct TcSourceTaskDynamic {
pub tc_receiver: mpsc::Receiver<PacketAsVec>, pub tc_receiver: mpsc::Receiver<PacketAsVec>,
pus_distributor: PusTcDistributor, pus_distributor: PusTcDistributor<MpscTmAsVecSender>,
} }
#[allow(dead_code)]
impl TcSourceTaskDynamic { impl TcSourceTaskDynamic {
pub fn new(tc_receiver: mpsc::Receiver<PacketAsVec>, pus_receiver: PusTcDistributor) -> Self { pub fn new(
tc_receiver: mpsc::Receiver<PacketAsVec>,
pus_receiver: PusTcDistributor<MpscTmAsVecSender>,
) -> Self {
Self { Self {
tc_receiver, tc_receiver,
pus_distributor: pus_receiver, pus_distributor: pus_receiver,
@ -102,18 +105,3 @@ impl TcSourceTaskDynamic {
} }
} }
} }
#[allow(dead_code)]
pub enum TcSourceTask {
Static(TcSourceTaskStatic),
Heap(TcSourceTaskDynamic),
}
impl TcSourceTask {
pub fn periodic_operation(&mut self) {
match self {
TcSourceTask::Static(task) => task.periodic_operation(),
TcSourceTask::Heap(task) => task.periodic_operation(),
}
}
}

View File

@ -4,19 +4,16 @@ use std::{
}; };
use log::info; use log::info;
use satrs::tmtc::{PacketAsVec, PacketInPool, SharedPacketPool};
use satrs::{ use satrs::{
pool::PoolProvider, pool::PoolProvider,
seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore},
spacepackets::{ spacepackets::{
ecss::{tm::PusTmZeroCopyWriter, PusPacket}, ecss::{tm::PusTmZeroCopyWriter, PusPacket},
seq_count::CcsdsSimpleSeqCountProvider,
time::cds::MIN_CDS_FIELD_LEN, time::cds::MIN_CDS_FIELD_LEN,
CcsdsPacket, CcsdsPacket,
}, },
}; };
use satrs::{
spacepackets::seq_count::SequenceCountProvider,
tmtc::{PacketAsVec, PacketInPool, SharedPacketPool},
};
use crate::interface::tcp::SyncTcpTmSource; use crate::interface::tcp::SyncTcpTmSource;
@ -89,7 +86,6 @@ pub struct TmSinkStatic {
tm_server_tx: mpsc::SyncSender<PacketInPool>, tm_server_tx: mpsc::SyncSender<PacketInPool>,
} }
#[allow(dead_code)]
impl TmSinkStatic { impl TmSinkStatic {
pub fn new( pub fn new(
shared_tm_store: SharedPacketPool, shared_tm_store: SharedPacketPool,
@ -133,15 +129,14 @@ impl TmSinkStatic {
pub struct TmSinkDynamic { pub struct TmSinkDynamic {
common: TmFunnelCommon, common: TmFunnelCommon,
tm_funnel_rx: mpsc::Receiver<PacketAsVec>, tm_funnel_rx: mpsc::Receiver<PacketAsVec>,
tm_server_tx: mpsc::SyncSender<PacketAsVec>, tm_server_tx: mpsc::Sender<PacketAsVec>,
} }
#[allow(dead_code)]
impl TmSinkDynamic { impl TmSinkDynamic {
pub fn new( pub fn new(
sync_tm_tcp_source: SyncTcpTmSource, sync_tm_tcp_source: SyncTcpTmSource,
tm_funnel_rx: mpsc::Receiver<PacketAsVec>, tm_funnel_rx: mpsc::Receiver<PacketAsVec>,
tm_server_tx: mpsc::SyncSender<PacketAsVec>, tm_server_tx: mpsc::Sender<PacketAsVec>,
) -> Self { ) -> Self {
Self { Self {
common: TmFunnelCommon::new(sync_tm_tcp_source), common: TmFunnelCommon::new(sync_tm_tcp_source),
@ -164,18 +159,3 @@ impl TmSinkDynamic {
} }
} }
} }
#[allow(dead_code)]
pub enum TmSink {
Static(TmSinkStatic),
Heap(TmSinkDynamic),
}
impl TmSink {
pub fn operation(&mut self) {
match self {
TmSink::Static(static_sink) => static_sink.operation(),
TmSink::Heap(dynamic_sink) => dynamic_sink.operation(),
}
}
}

View File

@ -23,7 +23,7 @@ version = "1"
optional = true optional = true
[dependencies.satrs-shared] [dependencies.satrs-shared]
version = ">=0.1.3, <=0.2" version = ">=0.1.3, <0.2"
features = ["serde"] features = ["serde"]
[dependencies.satrs-mib-codegen] [dependencies.satrs-mib-codegen]

View File

@ -28,7 +28,7 @@ features = ["full"]
trybuild = { version = "1", features = ["diff"] } trybuild = { version = "1", features = ["diff"] }
[dev-dependencies.satrs-shared] [dev-dependencies.satrs-shared]
version = ">=0.1.3, <=0.2" version = ">=0.1.3, <0.2"
[dev-dependencies.satrs-mib] [dev-dependencies.satrs-mib]
path = ".." path = ".."

View File

@ -1,4 +1,6 @@
#![no_std] #![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
extern crate std; extern crate std;

View File

@ -9,18 +9,20 @@ edition = "2021"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
log = "0.4" log = "0.4"
thiserror = "2" thiserror = "1"
fern = "0.7" fern = "0.5"
strum = { version = "0.26", features = ["derive"] } strum = { version = "0.26", features = ["derive"] }
num_enum = "0.7" num_enum = "0.7"
humantime = "2" humantime = "2"
tai-time = { version = "0.3", features = ["serde"] }
[dependencies.nexosim] [dependencies.asynchronix]
version = "0.3.1" version = "0.2.1"
git = "https://github.com/asynchronics/asynchronix.git"
branch = "main"
features = ["serde"]
[dependencies.satrs] [dependencies.satrs]
path = "../satrs" path = "../satrs"
[dev-dependencies] [dev-dependencies]
delegate = "0.13" delegate = "0.12"

View File

@ -1,32 +0,0 @@
sat-rs minisim
======
This crate contains a mini-simulator based on the open-source discrete-event simulation framework
[asynchronix](https://github.com/asynchronics/asynchronix).
Right now, this crate is primarily used together with the
[`satrs-example` application](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
to simulate the devices connected to the example application.
You can simply run this application using
```sh
cargo run
```
or
```sh
cargo run -p satrs-minisim
```
in the workspace. The mini simulator uses the UDP port 7303 to exchange simulation requests and
simulation replies with any other application.
The simulator was designed in a modular way to be scalable and adaptable to other communication
schemes. This might allow it to serve a mini-simulator for other example applications which
still have similar device handlers.
The following graph shows the high-level architecture of the mini-simulator.
<img src="../images/minisim-arch/minisim-arch.png" alt="Mini simulator architecture" width="500" class="center"/>

View File

@ -1,8 +1,8 @@
use std::{f32::consts::PI, sync::mpsc, time::Duration}; use std::{f32::consts::PI, sync::mpsc, time::Duration};
use nexosim::{ use asynchronix::{
model::{Context, Model}, model::{Model, Output},
ports::Output, time::Scheduler,
}; };
use satrs::power::SwitchStateBinary; use satrs::power::SwitchStateBinary;
use satrs_minisim::{ use satrs_minisim::{
@ -31,7 +31,6 @@ const PHASE_Z: f32 = 0.2;
/// might still be possible and is probably sufficient for many OBSW needs. /// might still be possible and is probably sufficient for many OBSW needs.
pub struct MagnetometerModel<ReplyProvider: MgmReplyProvider> { pub struct MagnetometerModel<ReplyProvider: MgmReplyProvider> {
pub switch_state: SwitchStateBinary, pub switch_state: SwitchStateBinary,
#[allow(dead_code)]
pub periodicity: Duration, pub periodicity: Duration,
pub external_mag_field: Option<MgmSensorValuesMicroTesla>, pub external_mag_field: Option<MgmSensorValuesMicroTesla>,
pub reply_sender: mpsc::Sender<SimReply>, pub reply_sender: mpsc::Sender<SimReply>,
@ -55,7 +54,7 @@ impl<ReplyProvider: MgmReplyProvider> MagnetometerModel<ReplyProvider> {
self.switch_state = switch_state; self.switch_state = switch_state;
} }
pub async fn send_sensor_values(&mut self, _: (), scheduler: &mut Context<Self>) { pub async fn send_sensor_values(&mut self, _: (), scheduler: &Scheduler<Self>) {
self.reply_sender self.reply_sender
.send(ReplyProvider::create_mgm_reply(MgmReplyCommon { .send(ReplyProvider::create_mgm_reply(MgmReplyCommon {
switch_state: self.switch_state, switch_state: self.switch_state,
@ -114,11 +113,11 @@ impl MagnetorquerModel {
pub async fn apply_torque( pub async fn apply_torque(
&mut self, &mut self,
duration_and_dipole: (Duration, MgtDipole), duration_and_dipole: (Duration, MgtDipole),
cx: &mut Context<Self>, scheduler: &Scheduler<Self>,
) { ) {
self.torque_dipole = duration_and_dipole.1; self.torque_dipole = duration_and_dipole.1;
self.torquing = true; self.torquing = true;
if cx if scheduler
.schedule_event(duration_and_dipole.0, Self::clear_torque, ()) .schedule_event(duration_and_dipole.0, Self::clear_torque, ())
.is_err() .is_err()
{ {
@ -138,11 +137,12 @@ impl MagnetorquerModel {
self.generate_magnetic_field(()).await; self.generate_magnetic_field(()).await;
} }
pub async fn request_housekeeping_data(&mut self, _: (), cx: &mut Context<Self>) { pub async fn request_housekeeping_data(&mut self, _: (), scheduler: &Scheduler<Self>) {
if self.switch_state != SwitchStateBinary::On { if self.switch_state != SwitchStateBinary::On {
return; return;
} }
cx.schedule_event(Duration::from_millis(15), Self::send_housekeeping_data, ()) scheduler
.schedule_event(Duration::from_millis(15), Self::send_housekeeping_data, ())
.expect("requesting housekeeping data failed") .expect("requesting housekeeping data failed")
} }
@ -199,11 +199,11 @@ pub mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();
assert_eq!(sim_reply.component(), SimComponent::Mgm0Lis3Mdl); assert_eq!(sim_reply.component(), SimComponent::MgmLis3Mdl);
let reply = MgmLis3MdlReply::from_sim_message(&sim_reply) let reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
assert_eq!(reply.common.switch_state, SwitchStateBinary::Off); assert_eq!(reply.common.switch_state, SwitchStateBinary::Off);
@ -222,21 +222,21 @@ pub mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
let mut sim_reply_res = sim_testbench.try_receive_next_reply(); let mut sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
let mut sim_reply = sim_reply_res.unwrap(); let mut sim_reply = sim_reply_res.unwrap();
assert_eq!(sim_reply.component(), SimComponent::Mgm0Lis3Mdl); assert_eq!(sim_reply.component(), SimComponent::MgmLis3Mdl);
let first_reply = MgmLis3MdlReply::from_sim_message(&sim_reply) let first_reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
sim_testbench.step_until(Duration::from_millis(50)).unwrap(); sim_testbench.step_by(Duration::from_millis(50));
request = SimRequest::new_with_epoch_time(MgmRequestLis3Mdl::RequestSensorData); request = SimRequest::new_with_epoch_time(MgmRequestLis3Mdl::RequestSensorData);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
sim_reply_res = sim_testbench.try_receive_next_reply(); sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
sim_reply = sim_reply_res.unwrap(); sim_reply = sim_reply_res.unwrap();
@ -271,7 +271,7 @@ pub mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_none()); assert!(sim_reply_res.is_none());
} }
@ -286,7 +286,7 @@ pub mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
let sim_reply = sim_reply_res.unwrap(); let sim_reply = sim_reply_res.unwrap();
@ -307,7 +307,7 @@ pub mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
let sim_reply = sim_reply_res.unwrap(); let sim_reply = sim_reply_res.unwrap();
@ -338,7 +338,7 @@ pub mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step_until(Duration::from_millis(5)).unwrap(); sim_testbench.step_by(Duration::from_millis(5));
check_mgt_hk( check_mgt_hk(
&mut sim_testbench, &mut sim_testbench,
@ -347,9 +347,7 @@ pub mod tests {
torquing: true, torquing: true,
}, },
); );
sim_testbench sim_testbench.step_by(Duration::from_millis(100));
.step_until(Duration::from_millis(100))
.unwrap();
check_mgt_hk( check_mgt_hk(
&mut sim_testbench, &mut sim_testbench,
MgtHkSet { MgtHkSet {

View File

@ -1,7 +1,7 @@
use std::{sync::mpsc, time::Duration}; use std::{sync::mpsc, time::Duration};
use nexosim::{ use asynchronix::{
simulation::{Address, Scheduler, Simulation}, simulation::{Address, Simulation},
time::{Clock, MonotonicTime, SystemClock}, time::{Clock, MonotonicTime, SystemClock},
}; };
use satrs_minisim::{ use satrs_minisim::{
@ -16,62 +16,40 @@ use crate::{
eps::PcduModel, eps::PcduModel,
}; };
const WARNING_FOR_STALE_DATA: bool = false; const SIM_CTRL_REQ_WIRETAPPING: bool = true;
const MGM_REQ_WIRETAPPING: bool = true;
const SIM_CTRL_REQ_WIRETAPPING: bool = false; const PCDU_REQ_WIRETAPPING: bool = true;
const MGM_REQ_WIRETAPPING: bool = false; const MGT_REQ_WIRETAPPING: bool = true;
const PCDU_REQ_WIRETAPPING: bool = false;
const MGT_REQ_WIRETAPPING: bool = false;
pub struct ModelAddrWrapper {
mgm_0_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
mgm_1_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
pcdu_addr: Address<PcduModel>,
mgt_addr: Address<MagnetorquerModel>,
}
// The simulation controller processes requests and drives the simulation. // The simulation controller processes requests and drives the simulation.
#[allow(dead_code)]
pub struct SimController { pub struct SimController {
pub sys_clock: SystemClock, pub sys_clock: SystemClock,
pub request_receiver: mpsc::Receiver<SimRequest>, pub request_receiver: mpsc::Receiver<SimRequest>,
pub reply_sender: mpsc::Sender<SimReply>, pub reply_sender: mpsc::Sender<SimReply>,
pub simulation: Simulation, pub simulation: Simulation,
pub scheduler: Scheduler, pub mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
pub addr_wrapper: ModelAddrWrapper, pub pcdu_addr: Address<PcduModel>,
pub mgt_addr: Address<MagnetorquerModel>,
} }
impl ModelAddrWrapper {
pub fn new(
mgm_0_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
mgm_1_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
pcdu_addr: Address<PcduModel>,
mgt_addr: Address<MagnetorquerModel>,
) -> Self {
Self {
mgm_0_addr,
mgm_1_addr,
pcdu_addr,
mgt_addr,
}
}
}
impl SimController { impl SimController {
pub fn new( pub fn new(
sys_clock: SystemClock, sys_clock: SystemClock,
request_receiver: mpsc::Receiver<SimRequest>, request_receiver: mpsc::Receiver<SimRequest>,
reply_sender: mpsc::Sender<SimReply>, reply_sender: mpsc::Sender<SimReply>,
simulation: Simulation, simulation: Simulation,
scheduler: Scheduler, mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
addr_wrapper: ModelAddrWrapper, pcdu_addr: Address<PcduModel>,
mgt_addr: Address<MagnetorquerModel>,
) -> Self { ) -> Self {
Self { Self {
sys_clock, sys_clock,
request_receiver, request_receiver,
reply_sender, reply_sender,
simulation, simulation,
scheduler, mgm_addr,
addr_wrapper, pcdu_addr,
mgt_addr,
} }
} }
@ -82,7 +60,7 @@ impl SimController {
// Check for UDP requests every millisecond. Shift the simulator ahead here to prevent // Check for UDP requests every millisecond. Shift the simulator ahead here to prevent
// replies lying in the past. // replies lying in the past.
t += Duration::from_millis(udp_polling_interval_ms); t += Duration::from_millis(udp_polling_interval_ms);
let _synch_status = self.sys_clock.synchronize(t); self.sys_clock.synchronize(t);
self.handle_sim_requests(t_old); self.handle_sim_requests(t_old);
self.simulation self.simulation
.step_until(t) .step_until(t)
@ -94,13 +72,12 @@ impl SimController {
loop { loop {
match self.request_receiver.try_recv() { match self.request_receiver.try_recv() {
Ok(request) => { Ok(request) => {
if request.timestamp < old_timestamp && WARNING_FOR_STALE_DATA { if request.timestamp < old_timestamp {
log::warn!("stale data with timestamp {:?} received", request.timestamp); log::warn!("stale data with timestamp {:?} received", request.timestamp);
} }
if let Err(e) = match request.component() { if let Err(e) = match request.component() {
SimComponent::SimCtrl => self.handle_ctrl_request(&request), SimComponent::SimCtrl => self.handle_ctrl_request(&request),
SimComponent::Mgm0Lis3Mdl => self.handle_mgm_request(0, &request), SimComponent::MgmLis3Mdl => self.handle_mgm_request(&request),
SimComponent::Mgm1Lis3Mdl => self.handle_mgm_request(1, &request),
SimComponent::Mgt => self.handle_mgt_request(&request), SimComponent::Mgt => self.handle_mgt_request(&request),
SimComponent::Pcdu => self.handle_pcdu_request(&request), SimComponent::Pcdu => self.handle_pcdu_request(&request),
} { } {
@ -132,26 +109,18 @@ impl SimController {
Ok(()) Ok(())
} }
fn handle_mgm_request( fn handle_mgm_request(&mut self, request: &SimRequest) -> Result<(), SimRequestError> {
&mut self,
mgm_idx: usize,
request: &SimRequest,
) -> Result<(), SimRequestError> {
let mgm_request = MgmRequestLis3Mdl::from_sim_message(request)?; let mgm_request = MgmRequestLis3Mdl::from_sim_message(request)?;
if MGM_REQ_WIRETAPPING { if MGM_REQ_WIRETAPPING {
log::info!("received MGM request: {:?}", mgm_request); log::info!("received MGM request: {:?}", mgm_request);
} }
match mgm_request { match mgm_request {
MgmRequestLis3Mdl::RequestSensorData => { MgmRequestLis3Mdl::RequestSensorData => {
let addr = match mgm_idx { self.simulation.send_event(
0 => &self.addr_wrapper.mgm_0_addr, MagnetometerModel::send_sensor_values,
1 => &self.addr_wrapper.mgm_1_addr, (),
&self.mgm_addr,
_ => panic!("invalid mgm index"), );
};
self.simulation
.process_event(MagnetometerModel::send_sensor_values, (), addr)
.expect("event execution error for mgm");
} }
} }
Ok(()) Ok(())
@ -165,21 +134,14 @@ impl SimController {
match pcdu_request { match pcdu_request {
PcduRequest::RequestSwitchInfo => { PcduRequest::RequestSwitchInfo => {
self.simulation self.simulation
.process_event( .send_event(PcduModel::request_switch_info, (), &self.pcdu_addr);
PcduModel::request_switch_info,
(),
&self.addr_wrapper.pcdu_addr,
)
.unwrap();
} }
PcduRequest::SwitchDevice { switch, state } => { PcduRequest::SwitchDevice { switch, state } => {
self.simulation self.simulation.send_event(
.process_event( PcduModel::switch_device,
PcduModel::switch_device, (switch, state),
(switch, state), &self.pcdu_addr,
&self.addr_wrapper.pcdu_addr, );
)
.unwrap();
} }
} }
Ok(()) Ok(())
@ -191,23 +153,17 @@ impl SimController {
log::info!("received MGT request: {:?}", mgt_request); log::info!("received MGT request: {:?}", mgt_request);
} }
match mgt_request { match mgt_request {
MgtRequest::ApplyTorque { duration, dipole } => self MgtRequest::ApplyTorque { duration, dipole } => self.simulation.send_event(
.simulation MagnetorquerModel::apply_torque,
.process_event( (duration, dipole),
MagnetorquerModel::apply_torque, &self.mgt_addr,
(duration, dipole), ),
&self.addr_wrapper.mgt_addr, MgtRequest::RequestHk => self.simulation.send_event(
) MagnetorquerModel::request_housekeeping_data,
.unwrap(), (),
MgtRequest::RequestHk => self &self.mgt_addr,
.simulation ),
.process_event( }
MagnetorquerModel::request_housekeeping_data,
(),
&self.addr_wrapper.mgt_addr,
)
.unwrap(),
};
Ok(()) Ok(())
} }
@ -241,7 +197,7 @@ mod tests {
.send_request(request) .send_request(request)
.expect("sending sim ctrl request failed"); .expect("sending sim ctrl request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();

View File

@ -1,8 +1,8 @@
use std::{sync::mpsc, time::Duration}; use std::{sync::mpsc, time::Duration};
use nexosim::{ use asynchronix::{
model::{Context, Model}, model::{Model, Output},
ports::Output, time::Scheduler,
}; };
use satrs::power::SwitchStateBinary; use satrs::power::SwitchStateBinary;
use satrs_minisim::{ use satrs_minisim::{
@ -14,8 +14,7 @@ pub const SWITCH_INFO_DELAY_MS: u64 = 10;
pub struct PcduModel { pub struct PcduModel {
pub switcher_map: SwitchMapBinaryWrapper, pub switcher_map: SwitchMapBinaryWrapper,
pub mgm_0_switch: Output<SwitchStateBinary>, pub mgm_switch: Output<SwitchStateBinary>,
pub mgm_1_switch: Output<SwitchStateBinary>,
pub mgt_switch: Output<SwitchStateBinary>, pub mgt_switch: Output<SwitchStateBinary>,
pub reply_sender: mpsc::Sender<SimReply>, pub reply_sender: mpsc::Sender<SimReply>,
} }
@ -24,20 +23,20 @@ impl PcduModel {
pub fn new(reply_sender: mpsc::Sender<SimReply>) -> Self { pub fn new(reply_sender: mpsc::Sender<SimReply>) -> Self {
Self { Self {
switcher_map: Default::default(), switcher_map: Default::default(),
mgm_0_switch: Output::new(), mgm_switch: Output::new(),
mgm_1_switch: Output::new(),
mgt_switch: Output::new(), mgt_switch: Output::new(),
reply_sender, reply_sender,
} }
} }
pub async fn request_switch_info(&mut self, _: (), cx: &mut Context<Self>) { pub async fn request_switch_info(&mut self, _: (), scheduler: &Scheduler<Self>) {
cx.schedule_event( scheduler
Duration::from_millis(SWITCH_INFO_DELAY_MS), .schedule_event(
Self::send_switch_info, Duration::from_millis(SWITCH_INFO_DELAY_MS),
(), Self::send_switch_info,
) (),
.expect("requesting switch info failed"); )
.expect("requesting switch info failed");
} }
pub fn send_switch_info(&mut self) { pub fn send_switch_info(&mut self) {
@ -57,7 +56,7 @@ impl PcduModel {
*val = switch_and_target_state.1; *val = switch_and_target_state.1;
match switch_and_target_state.0 { match switch_and_target_state.0 {
PcduSwitch::Mgm => { PcduSwitch::Mgm => {
self.mgm_0_switch.send(switch_and_target_state.1).await; self.mgm_switch.send(switch_and_target_state.1).await;
} }
PcduSwitch::Mgt => { PcduSwitch::Mgt => {
self.mgt_switch.send(switch_and_target_state.1).await; self.mgt_switch.send(switch_and_target_state.1).await;
@ -93,7 +92,7 @@ pub(crate) mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM switch request failed"); .expect("sending MGM switch request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -114,7 +113,7 @@ pub(crate) mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step().unwrap(); sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();
@ -144,12 +143,12 @@ pub(crate) mod tests {
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests_time_agnostic(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step_until(Duration::from_millis(1)).unwrap(); sim_testbench.step_by(Duration::from_millis(1));
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_none()); assert!(sim_reply.is_none());
// Reply takes 20ms // Reply takes 20ms
sim_testbench.step_until(Duration::from_millis(25)).unwrap(); sim_testbench.step_by(Duration::from_millis(25));
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
let sim_reply = sim_reply.unwrap(); let sim_reply = sim_reply.unwrap();

View File

@ -1,12 +1,11 @@
use nexosim::time::MonotonicTime; use asynchronix::time::MonotonicTime;
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub enum SimComponent { pub enum SimComponent {
SimCtrl, SimCtrl,
Mgm0Lis3Mdl, MgmLis3Mdl,
Mgm1Lis3Mdl,
Mgt, Mgt,
Pcdu, Pcdu,
} }
@ -237,7 +236,7 @@ pub mod eps {
RequestSwitchInfo = 1, RequestSwitchInfo = 1,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum PcduRequest { pub enum PcduRequest {
SwitchDevice { SwitchDevice {
switch: PcduSwitch, switch: PcduSwitch,
@ -278,7 +277,7 @@ pub mod acs {
} }
impl SerializableSimMsgPayload<SimRequest> for MgmRequestLis3Mdl { impl SerializableSimMsgPayload<SimRequest> for MgmRequestLis3Mdl {
const TARGET: SimComponent = SimComponent::Mgm0Lis3Mdl; const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
} }
// Normally, small magnetometers generate their output as a signed 16 bit raw format or something // Normally, small magnetometers generate their output as a signed 16 bit raw format or something
@ -369,7 +368,7 @@ pub mod acs {
} }
impl SerializableSimMsgPayload<SimReply> for MgmLis3MdlReply { impl SerializableSimMsgPayload<SimReply> for MgmLis3MdlReply {
const TARGET: SimComponent = SimComponent::Mgm0Lis3Mdl; const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
} }
impl MgmReplyProvider for MgmLis3MdlReply { impl MgmReplyProvider for MgmLis3MdlReply {
@ -419,7 +418,7 @@ pub mod acs {
} }
impl SerializableSimMsgPayload<SimReply> for MgtReply { impl SerializableSimMsgPayload<SimReply> for MgtReply {
const TARGET: SimComponent = SimComponent::Mgm0Lis3Mdl; const TARGET: SimComponent = SimComponent::MgmLis3Mdl;
} }
} }

View File

@ -1,8 +1,8 @@
use acs::{MagnetometerModel, MagnetorquerModel}; use acs::{MagnetometerModel, MagnetorquerModel};
use controller::{ModelAddrWrapper, SimController}; use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, SystemClock};
use controller::SimController;
use eps::PcduModel; use eps::PcduModel;
use nexosim::simulation::{Mailbox, SimInit};
use nexosim::time::{MonotonicTime, SystemClock};
use satrs_minisim::udp::SIM_CTRL_PORT; use satrs_minisim::udp::SIM_CTRL_PORT;
use satrs_minisim::{SimReply, SimRequest}; use satrs_minisim::{SimReply, SimRequest};
use std::sync::mpsc; use std::sync::mpsc;
@ -31,15 +31,11 @@ fn create_sim_controller(
request_receiver: mpsc::Receiver<SimRequest>, request_receiver: mpsc::Receiver<SimRequest>,
) -> SimController { ) -> SimController {
// Instantiate models and their mailboxes. // Instantiate models and their mailboxes.
let mgm_0_model = let mgm_model =
MagnetometerModel::new_for_lis3mdl(Duration::from_millis(50), reply_sender.clone());
let mgm_1_model =
MagnetometerModel::new_for_lis3mdl(Duration::from_millis(50), reply_sender.clone()); MagnetometerModel::new_for_lis3mdl(Duration::from_millis(50), reply_sender.clone());
let mgm_0_mailbox = Mailbox::new(); let mgm_mailbox = Mailbox::new();
let mgm_0_addr = mgm_0_mailbox.address(); let mgm_addr = mgm_mailbox.address();
let mgm_1_mailbox = Mailbox::new();
let mgm_1_addr = mgm_1_mailbox.address();
let pcdu_mailbox = Mailbox::new(); let pcdu_mailbox = Mailbox::new();
let pcdu_addr = pcdu_mailbox.address(); let pcdu_addr = pcdu_mailbox.address();
let mgt_mailbox = Mailbox::new(); let mgt_mailbox = Mailbox::new();
@ -47,11 +43,8 @@ fn create_sim_controller(
let mut pcdu_model = PcduModel::new(reply_sender.clone()); let mut pcdu_model = PcduModel::new(reply_sender.clone());
pcdu_model pcdu_model
.mgm_0_switch .mgm_switch
.connect(MagnetometerModel::switch_device, &mgm_0_addr); .connect(MagnetometerModel::switch_device, &mgm_addr);
pcdu_model
.mgm_1_switch
.connect(MagnetometerModel::switch_device, &mgm_1_addr);
let mut mgt_model = MagnetorquerModel::new(reply_sender.clone()); let mut mgt_model = MagnetorquerModel::new(reply_sender.clone());
// Input connections. // Input connections.
@ -59,14 +52,9 @@ fn create_sim_controller(
.mgt_switch .mgt_switch
.connect(MagnetorquerModel::switch_device, &mgt_addr); .connect(MagnetorquerModel::switch_device, &mgt_addr);
// Output connections. // Output connections.
mgt_model.gen_magnetic_field.connect( mgt_model
MagnetometerModel::apply_external_magnetic_field, .gen_magnetic_field
&mgm_0_addr, .connect(MagnetometerModel::apply_external_magnetic_field, &mgm_addr);
);
mgt_model.gen_magnetic_field.connect(
MagnetometerModel::apply_external_magnetic_field,
&mgm_1_addr,
);
// Instantiate the simulator // Instantiate the simulator
let sys_clock = SystemClock::from_system_time(start_time, SystemTime::now()); let sys_clock = SystemClock::from_system_time(start_time, SystemTime::now());
@ -75,21 +63,19 @@ fn create_sim_controller(
} else { } else {
SimInit::new() SimInit::new()
}; };
let addrs = ModelAddrWrapper::new(mgm_0_addr, mgm_1_addr, pcdu_addr, mgt_addr); let simulation = sim_init
let (simulation, scheduler) = sim_init .add_model(mgm_model, mgm_mailbox)
.add_model(mgm_0_model, mgm_0_mailbox, "MGM 0 model") .add_model(pcdu_model, pcdu_mailbox)
.add_model(mgm_1_model, mgm_1_mailbox, "MGM 1 model") .add_model(mgt_model, mgt_mailbox)
.add_model(pcdu_model, pcdu_mailbox, "PCDU model") .init(start_time);
.add_model(mgt_model, mgt_mailbox, "MGT model")
.init(start_time)
.unwrap();
SimController::new( SimController::new(
sys_clock, sys_clock,
request_receiver, request_receiver,
reply_sender, reply_sender,
simulation, simulation,
scheduler, mgm_addr,
addrs, pcdu_addr,
mgt_addr,
) )
} }

View File

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

View File

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

View File

@ -8,14 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.2.1] 2024-11-15
Increased allowed spacepackets to v0.13
# [v0.2.0] 2024-11-04
Semver bump, due to added features in v0.1.4
# [v0.1.4] 2024-04-24 # [v0.1.4] 2024-04-24
## Added ## Added

View File

@ -1,7 +1,7 @@
[package] [package]
name = "satrs-shared" name = "satrs-shared"
description = "Components shared by multiple sat-rs crates" description = "Components shared by multiple sat-rs crates"
version = "0.2.1" version = "0.1.4"
edition = "2021" edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/" homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
@ -22,13 +22,12 @@ version = "0.3"
optional = true optional = true
[dependencies.spacepackets] [dependencies.spacepackets]
version = ">0.9, <=0.13" version = ">0.9, <=0.11"
default-features = false default-features = false
[features] [features]
serde = ["dep:serde", "spacepackets/serde"] serde = ["dep:serde", "spacepackets/serde"]
defmt = ["dep:defmt", "spacepackets/defmt"] spacepackets = ["dep:defmt", "spacepackets/defmt"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
rustdoc-args = ["--generate-link-to-definition"]

View File

@ -1,3 +0,0 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open

View File

@ -1,23 +0,0 @@
Checklist for new releases
=======
# Pre-Release
1. Make sure any new modules are documented sufficiently enough and check docs by running
`docs.sh`.
2. Bump version specifier in `Cargo.toml`.
3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new
`unreleased` section.
4. Run `cargo test --all-features` or `cargo nextest r --all-features` and `cargo test --doc`.
5. Run `cargo fmt` and `cargo clippy`. Check `cargo msrv` against MSRV in `Cargo.toml`.
6. Wait for CI/CD results for EGit and Github. These also check cross-compilation for bare-metal
targets.
# Release
1. `cargo publish`
# Post-Release
1. Create a new release on `EGit` with the name `satrs-<version>`.

View File

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

View File

@ -8,31 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
`spacepackets` v0.13
## Changed ## Changed
- Renamed `StaticPoolConfig::new` to `StaticPoolConfig::new_from_subpool_cfg_tuples`. The new - Renamed `StaticPoolConfig::new` to `StaticPoolConfig::new_from_subpool_cfg_tuples`. The new
`new` implementation expects a type struct instead of tuples. `new` implementation expects a type struct instead of tuples.
- Moved `cfdp` module to [dedicated crate](https://egit.irs.uni-stuttgart.de/rust/cfdp)
- Moved `seq_count` module to [spacepackets](https://egit.irs.uni-stuttgart.de/rust/spacepackets)
crate
## Added ## Added
- `StaticHeaplessMemoryPool` which can be grown with user-provided static buffers. - `StaticHeaplessMemoryPool` which can be grown with user-provided static buffers.
- Scheduling table for systems with a standard runtime
- Mode Tree Feature which allows building a network of mode components which can send mode
messages to each other.
- Added first helper features like the `SubsystemExecutionHelper` and the
`SubsystemCommandingHelper` which allows to build subsystem components. Subsystem components
are able to execute mode sequences and perform target keeping based on a declarative table
format.
- Added `DevManagerCommandingHelper` which performs some of the boilerplate logik required
by Assembly and Device Management components. This includes forwarding mode requests and
handling mode replies.
- First basic health module with `HealthState`s and the `HealthTableProvider` trait. These
components are important for any FDIR components which get added in the future.
# [v0.2.1] 2024-05-19 # [v0.2.1] 2024-05-19

View File

@ -2,7 +2,7 @@
name = "satrs" name = "satrs"
version = "0.2.1" version = "0.2.1"
edition = "2021" edition = "2021"
rust-version = "1.82.0" rust-version = "1.71.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "A framework to build software for remote systems" description = "A framework to build software for remote systems"
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/" homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
@ -13,36 +13,88 @@ keywords = ["no-std", "space", "aerospace"]
categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"] categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"]
[dependencies] [dependencies]
satrs-shared = ">=0.1.3, <=0.2" delegate = ">0.7, <=0.10"
delegate = ">0.7, <=0.13"
paste = "1" paste = "1"
derive-new = ">=0.6, <=0.7" derive-new = "0.6"
smallvec = "1" smallvec = "1"
crc = "3" crc = "3"
num_enum = { version = ">0.5, <=0.7", default-features = false }
spacepackets = { version = "0.13", default-features = false }
cobs = { version = "0.3", default-features = false }
num-traits = { version = "0.2", default-features = false }
thiserror = { version = "2", default-features = false }
hashbrown = { version = ">=0.14, <=0.15", optional = true } [dependencies.satrs-shared]
static_cell = { version = "2", optional = true } version = ">=0.1.3, <0.2"
dyn-clone = { version = "1", optional = true }
heapless = { version = "0.8", optional = true } [dependencies.num_enum]
downcast-rs = { version = "2", default-features = false, optional = true } version = ">0.5, <=0.7"
bus = { version = "2.2", optional = true } default-features = false
crossbeam-channel = { version = "0.5", default-features = false, optional = true }
serde = { version = "1", default-features = false, optional = true } [dependencies.spacepackets]
socket2 = { version = "0.5", features = ["all"], optional = true } version = "0.11"
mio = { version = "1", features = ["os-poll", "net"], optional = true } default-features = false
defmt = { version = "0.3", optional = true }
[dependencies.cobs]
git = "https://github.com/robamu/cobs.rs.git"
version = "0.2.3"
branch = "all_features"
default-features = false
[dependencies.num-traits]
version = "0.2"
default-features = false
[dependencies.dyn-clone]
version = "1"
optional = true
[dependencies.hashbrown]
version = "0.14"
optional = true
[dependencies.heapless]
version = "0.7"
optional = true
[dependencies.downcast-rs]
version = "1.2"
default-features = false
optional = true
[dependencies.bus]
version = "2.2"
optional = true
[dependencies.crossbeam-channel]
version= "0.5"
default-features = false
optional = true
[dependencies.thiserror]
version = "1"
optional = true
[dependencies.serde]
version = "1"
default-features = false
optional = true
[dependencies.socket2]
version = "0.5.4"
features = ["all"]
optional = true
[dependencies.mio]
version = "0.8"
features = ["os-poll", "net"]
optional = true
[dependencies.defmt]
version = "0.3"
optional = true
[dev-dependencies] [dev-dependencies]
serde = "1" serde = "1"
zerocopy = "0.8" zerocopy = "0.7"
once_cell = "1" once_cell = "1"
serde_json = "1" serde_json = "1"
rand = "0.9" rand = "0.8"
tempfile = "3" tempfile = "3"
[dev-dependencies.postcard] [dev-dependencies.postcard]
@ -54,11 +106,12 @@ std = [
"downcast-rs/std", "downcast-rs/std",
"alloc", "alloc",
"bus", "bus",
"postcard/use-std",
"crossbeam-channel/std", "crossbeam-channel/std",
"serde/std", "serde/std",
"spacepackets/std", "spacepackets/std",
"num_enum/std", "num_enum/std",
"thiserror/std", "thiserror",
"socket2", "socket2",
"mio" "mio"
] ]
@ -71,15 +124,11 @@ alloc = [
] ]
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"] serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
crossbeam = ["crossbeam-channel"] crossbeam = ["crossbeam-channel"]
heapless = ["dep:heapless", "static_cell"] heapless = ["dep:heapless"]
defmt = ["dep:defmt", "spacepackets/defmt"] defmt = ["dep:defmt", "spacepackets/defmt"]
test_util = [] test_util = []
doc-images = []
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
rustdoc-args = ["--generate-link-to-definition"] rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
[[test]]
name = "event_test"
path = "tests/pus_events.rs"
required-features = ["test_util"]

View File

@ -1,3 +0,0 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open

View File

@ -3,7 +3,8 @@ Checklist for new releases
# Pre-Release # Pre-Release
1. Make sure any new modules are documented sufficiently enough and check docs by running `docs.sh`. 1. Make sure any new modules are documented sufficiently enough and check docs with
`cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' --open`.
2. Bump version specifier in `Cargo.toml`. 2. Bump version specifier in `Cargo.toml`.
3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new 3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new
`unreleased` section. `unreleased` section.

1603
satrs/src/cfdp/dest.rs Normal file

File diff suppressed because it is too large Load Diff

769
satrs/src/cfdp/filestore.rs Normal file
View File

@ -0,0 +1,769 @@
use alloc::string::{String, ToString};
use core::fmt::Display;
use crc::{Crc, CRC_32_CKSUM};
use spacepackets::cfdp::ChecksumType;
use spacepackets::ByteConversionError;
#[cfg(feature = "std")]
use std::error::Error;
use std::path::Path;
#[cfg(feature = "std")]
pub use std_mod::*;
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
#[derive(Debug, Clone)]
pub enum FilestoreError {
FileDoesNotExist,
FileAlreadyExists,
DirDoesNotExist,
Permission,
IsNotFile,
IsNotDirectory,
ByteConversion(ByteConversionError),
Io {
raw_errno: Option<i32>,
string: String,
},
ChecksumTypeNotImplemented(ChecksumType),
}
impl From<ByteConversionError> for FilestoreError {
fn from(value: ByteConversionError) -> Self {
Self::ByteConversion(value)
}
}
impl Display for FilestoreError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
FilestoreError::FileDoesNotExist => {
write!(f, "file does not exist")
}
FilestoreError::FileAlreadyExists => {
write!(f, "file already exists")
}
FilestoreError::DirDoesNotExist => {
write!(f, "directory does not exist")
}
FilestoreError::Permission => {
write!(f, "permission error")
}
FilestoreError::IsNotFile => {
write!(f, "is not a file")
}
FilestoreError::IsNotDirectory => {
write!(f, "is not a directory")
}
FilestoreError::ByteConversion(e) => {
write!(f, "filestore error: {e}")
}
FilestoreError::Io { raw_errno, string } => {
write!(
f,
"filestore generic IO error with raw errno {:?}: {}",
raw_errno, string
)
}
FilestoreError::ChecksumTypeNotImplemented(checksum_type) => {
write!(f, "checksum {:?} not implemented", checksum_type)
}
}
}
}
impl Error for FilestoreError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
FilestoreError::ByteConversion(e) => Some(e),
_ => None,
}
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for FilestoreError {
fn from(value: std::io::Error) -> Self {
Self::Io {
raw_errno: value.raw_os_error(),
string: value.to_string(),
}
}
}
pub trait VirtualFilestore {
fn create_file(&self, file_path: &str) -> Result<(), FilestoreError>;
fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError>;
/// Truncating a file means deleting all its data so the resulting file is empty.
/// This can be more efficient than removing and re-creating a file.
fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError>;
fn remove_dir(&self, dir_path: &str, all: bool) -> Result<(), FilestoreError>;
fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError>;
fn read_data(
&self,
file_path: &str,
offset: u64,
read_len: u64,
buf: &mut [u8],
) -> Result<(), FilestoreError>;
fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError>;
fn filename_from_full_path(path: &str) -> Option<&str>
where
Self: Sized,
{
// Convert the path string to a Path
let path = Path::new(path);
// Extract the file name using the file_name() method
path.file_name().and_then(|name| name.to_str())
}
fn is_file(&self, path: &str) -> bool;
fn is_dir(&self, path: &str) -> bool {
!self.is_file(path)
}
fn exists(&self, path: &str) -> bool;
/// This special function is the CFDP specific abstraction to verify the checksum of a file.
/// This allows to keep OS specific details like reading the whole file in the most efficient
/// manner inside the file system abstraction.
///
/// The passed verification buffer argument will be used by the specific implementation as
/// a buffer to read the file into. It is recommended to use common buffer sizes like
/// 4096 or 8192 bytes.
fn checksum_verify(
&self,
file_path: &str,
checksum_type: ChecksumType,
expected_checksum: u32,
verification_buf: &mut [u8],
) -> Result<bool, FilestoreError>;
}
#[cfg(feature = "std")]
pub mod std_mod {
use super::*;
use std::{
fs::{self, File, OpenOptions},
io::{BufReader, Read, Seek, SeekFrom, Write},
};
#[derive(Default)]
pub struct NativeFilestore {}
impl VirtualFilestore for NativeFilestore {
fn create_file(&self, file_path: &str) -> Result<(), FilestoreError> {
if self.exists(file_path) {
return Err(FilestoreError::FileAlreadyExists);
}
File::create(file_path)?;
Ok(())
}
fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError> {
if !self.exists(file_path) {
return Err(FilestoreError::FileDoesNotExist);
}
if !self.is_file(file_path) {
return Err(FilestoreError::IsNotFile);
}
fs::remove_file(file_path)?;
Ok(())
}
fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError> {
if !self.exists(file_path) {
return Err(FilestoreError::FileDoesNotExist);
}
if !self.is_file(file_path) {
return Err(FilestoreError::IsNotFile);
}
OpenOptions::new()
.write(true)
.truncate(true)
.open(file_path)?;
Ok(())
}
fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError> {
fs::create_dir(dir_path).map_err(|e| FilestoreError::Io {
raw_errno: e.raw_os_error(),
string: e.to_string(),
})?;
Ok(())
}
fn remove_dir(&self, dir_path: &str, all: bool) -> Result<(), FilestoreError> {
if !self.exists(dir_path) {
return Err(FilestoreError::DirDoesNotExist);
}
if !self.is_dir(dir_path) {
return Err(FilestoreError::IsNotDirectory);
}
if !all {
fs::remove_dir(dir_path)?;
return Ok(());
}
fs::remove_dir_all(dir_path)?;
Ok(())
}
fn read_data(
&self,
file_name: &str,
offset: u64,
read_len: u64,
buf: &mut [u8],
) -> Result<(), FilestoreError> {
if buf.len() < read_len as usize {
return Err(ByteConversionError::ToSliceTooSmall {
found: buf.len(),
expected: read_len as usize,
}
.into());
}
if !self.exists(file_name) {
return Err(FilestoreError::FileDoesNotExist);
}
if !self.is_file(file_name) {
return Err(FilestoreError::IsNotFile);
}
let mut file = File::open(file_name)?;
file.seek(SeekFrom::Start(offset))?;
file.read_exact(&mut buf[0..read_len as usize])?;
Ok(())
}
fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError> {
if !self.exists(file) {
return Err(FilestoreError::FileDoesNotExist);
}
if !self.is_file(file) {
return Err(FilestoreError::IsNotFile);
}
let mut file = OpenOptions::new().write(true).open(file)?;
file.seek(SeekFrom::Start(offset))?;
file.write_all(buf)?;
Ok(())
}
fn is_file(&self, path: &str) -> bool {
let path = Path::new(path);
path.is_file()
}
fn exists(&self, path: &str) -> bool {
let path = Path::new(path);
if !path.exists() {
return false;
}
true
}
fn checksum_verify(
&self,
file_path: &str,
checksum_type: ChecksumType,
expected_checksum: u32,
verification_buf: &mut [u8],
) -> Result<bool, FilestoreError> {
match checksum_type {
ChecksumType::Modular => {
if self.calc_modular_checksum(file_path)? == expected_checksum {
return Ok(true);
}
Ok(false)
}
ChecksumType::Crc32 => {
let mut digest = CRC_32.digest();
let file_to_check = File::open(file_path)?;
let mut buf_reader = BufReader::new(file_to_check);
loop {
let bytes_read = buf_reader.read(verification_buf)?;
if bytes_read == 0 {
break;
}
digest.update(&verification_buf[0..bytes_read]);
}
if digest.finalize() == expected_checksum {
return Ok(true);
}
Ok(false)
}
ChecksumType::NullChecksum => Ok(true),
_ => Err(FilestoreError::ChecksumTypeNotImplemented(checksum_type)),
}
}
}
impl NativeFilestore {
pub fn calc_modular_checksum(&self, file_path: &str) -> Result<u32, FilestoreError> {
let mut checksum: u32 = 0;
let file = File::open(file_path)?;
let mut buf_reader = BufReader::new(file);
let mut buffer = [0; 4];
loop {
let bytes_read = buf_reader.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
// Perform padding directly in the buffer
(bytes_read..4).for_each(|i| {
buffer[i] = 0;
});
checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
}
Ok(checksum)
}
}
}
#[cfg(test)]
mod tests {
use std::{fs, path::Path, println};
use super::*;
use alloc::format;
use tempfile::tempdir;
const EXAMPLE_DATA_CFDP: [u8; 15] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
];
const NATIVE_FS: NativeFilestore = NativeFilestore {};
#[test]
fn test_basic_native_filestore_create() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
let result =
NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
assert!(result.is_ok());
let path = Path::new(&file_path);
assert!(path.exists());
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
}
#[test]
fn test_basic_native_fs_file_exists() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.unwrap();
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
}
#[test]
fn test_basic_native_fs_dir_exists() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let dir_path = tmpdir.path().join("testdir");
assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
NATIVE_FS
.create_dir(dir_path.to_str().expect("getting str for file failed"))
.unwrap();
assert!(NATIVE_FS.exists(dir_path.to_str().unwrap()));
assert!(NATIVE_FS.is_dir(dir_path.as_path().to_str().unwrap()));
}
#[test]
fn test_basic_native_fs_remove_file() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.expect("creating file failed");
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
NATIVE_FS
.remove_file(file_path.to_str().unwrap())
.expect("removing file failed");
assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
}
#[test]
fn test_basic_native_fs_write() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.unwrap();
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
println!("{}", file_path.to_str().unwrap());
let write_data = "hello world\n";
NATIVE_FS
.write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
.expect("writing to file failed");
let read_back = fs::read_to_string(file_path).expect("reading back data failed");
assert_eq!(read_back, write_data);
}
#[test]
fn test_basic_native_fs_read() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.unwrap();
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
println!("{}", file_path.to_str().unwrap());
let write_data = "hello world\n";
NATIVE_FS
.write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
.expect("writing to file failed");
let read_back = fs::read_to_string(file_path).expect("reading back data failed");
assert_eq!(read_back, write_data);
}
#[test]
fn test_truncate_file() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.expect("creating file failed");
fs::write(file_path.clone(), [1, 2, 3, 4]).unwrap();
assert_eq!(fs::read(file_path.clone()).unwrap(), [1, 2, 3, 4]);
NATIVE_FS
.truncate_file(file_path.to_str().unwrap())
.unwrap();
assert_eq!(fs::read(file_path.clone()).unwrap(), []);
}
#[test]
fn test_remove_dir() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let dir_path = tmpdir.path().join("testdir");
assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
NATIVE_FS
.create_dir(dir_path.to_str().expect("getting str for file failed"))
.unwrap();
assert!(NATIVE_FS.exists(dir_path.to_str().unwrap()));
NATIVE_FS
.remove_dir(dir_path.to_str().unwrap(), false)
.unwrap();
assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
}
#[test]
fn test_read_file() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.expect("creating file failed");
fs::write(file_path.clone(), [1, 2, 3, 4]).unwrap();
let read_buf: &mut [u8] = &mut [0; 4];
NATIVE_FS
.read_data(file_path.to_str().unwrap(), 0, 4, read_buf)
.unwrap();
assert_eq!([1, 2, 3, 4], read_buf);
NATIVE_FS
.write_data(file_path.to_str().unwrap(), 4, &[5, 6, 7, 8])
.expect("writing to file failed");
NATIVE_FS
.read_data(file_path.to_str().unwrap(), 2, 4, read_buf)
.unwrap();
assert_eq!([3, 4, 5, 6], read_buf);
}
#[test]
fn test_remove_which_does_not_exist() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
let result = NATIVE_FS.read_data(file_path.to_str().unwrap(), 0, 4, &mut [0; 4]);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::FileDoesNotExist = error {
assert_eq!(error.to_string(), "file does not exist");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_file_already_exists() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
let result =
NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
assert!(result.is_ok());
let result =
NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::FileAlreadyExists = error {
assert_eq!(error.to_string(), "file already exists");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_remove_file_with_dir_api() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.unwrap();
let result = NATIVE_FS.remove_dir(file_path.to_str().unwrap(), true);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::IsNotDirectory = error {
assert_eq!(error.to_string(), "is not a directory");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_remove_dir_remove_all() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let dir_path = tmpdir.path().join("test");
NATIVE_FS
.create_dir(dir_path.to_str().expect("getting str for file failed"))
.unwrap();
let file_path = dir_path.as_path().join("test.txt");
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.unwrap();
let result = NATIVE_FS.remove_dir(dir_path.to_str().unwrap(), true);
assert!(result.is_ok());
assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()));
}
#[test]
fn test_remove_dir_with_file_api() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test");
NATIVE_FS
.create_dir(file_path.to_str().expect("getting str for file failed"))
.unwrap();
let result = NATIVE_FS.remove_file(file_path.to_str().unwrap());
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::IsNotFile = error {
assert_eq!(error.to_string(), "is not a file");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_remove_dir_which_does_not_exist() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test");
let result = NATIVE_FS.remove_dir(file_path.to_str().unwrap(), true);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::DirDoesNotExist = error {
assert_eq!(error.to_string(), "directory does not exist");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_remove_file_which_does_not_exist() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
let result = NATIVE_FS.remove_file(file_path.to_str().unwrap());
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::FileDoesNotExist = error {
assert_eq!(error.to_string(), "file does not exist");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_truncate_file_which_does_not_exist() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
let result = NATIVE_FS.truncate_file(file_path.to_str().unwrap());
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::FileDoesNotExist = error {
assert_eq!(error.to_string(), "file does not exist");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_truncate_file_on_directory() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test");
NATIVE_FS.create_dir(file_path.to_str().unwrap()).unwrap();
let result = NATIVE_FS.truncate_file(file_path.to_str().unwrap());
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::IsNotFile = error {
assert_eq!(error.to_string(), "is not a file");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_byte_conversion_error_when_reading() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.unwrap();
let result = NATIVE_FS.read_data(file_path.to_str().unwrap(), 0, 2, &mut []);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::ByteConversion(byte_conv_error) = error {
if let ByteConversionError::ToSliceTooSmall { found, expected } = byte_conv_error {
assert_eq!(found, 0);
assert_eq!(expected, 2);
} else {
panic!("unexpected error");
}
assert_eq!(
error.to_string(),
format!("filestore error: {}", byte_conv_error)
);
} else {
panic!("unexpected error");
}
}
#[test]
fn test_read_file_on_dir() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let dir_path = tmpdir.path().join("test");
NATIVE_FS
.create_dir(dir_path.to_str().expect("getting str for file failed"))
.unwrap();
let result = NATIVE_FS.read_data(dir_path.to_str().unwrap(), 0, 4, &mut [0; 4]);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::IsNotFile = error {
assert_eq!(error.to_string(), "is not a file");
} else {
panic!("unexpected error");
}
}
#[test]
fn test_write_file_non_existing() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
let result = NATIVE_FS.write_data(file_path.to_str().unwrap(), 0, &[]);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::FileDoesNotExist = error {
} else {
panic!("unexpected error");
}
}
#[test]
fn test_write_file_on_dir() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test");
NATIVE_FS.create_dir(file_path.to_str().unwrap()).unwrap();
let result = NATIVE_FS.write_data(file_path.to_str().unwrap(), 0, &[]);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::IsNotFile = error {
} else {
panic!("unexpected error");
}
}
#[test]
fn test_filename_extraction() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("test.txt");
NATIVE_FS
.create_file(file_path.to_str().expect("getting str for file failed"))
.unwrap();
NativeFilestore::filename_from_full_path(file_path.to_str().unwrap());
}
#[test]
fn test_modular_checksum() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("mod-crc.bin");
fs::write(file_path.as_path(), EXAMPLE_DATA_CFDP).expect("writing test file failed");
// Kind of re-writing the modular checksum impl here which we are trying to test, but the
// numbers/correctness were verified manually using calculators, so this is okay.
let mut checksum: u32 = 0;
let mut buffer: [u8; 4] = [0; 4];
for i in 0..3 {
buffer = EXAMPLE_DATA_CFDP[i * 4..(i + 1) * 4].try_into().unwrap();
checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
}
buffer[0..3].copy_from_slice(&EXAMPLE_DATA_CFDP[12..15]);
buffer[3] = 0;
checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
let mut verif_buf: [u8; 32] = [0; 32];
let result = NATIVE_FS.checksum_verify(
file_path.to_str().unwrap(),
ChecksumType::Modular,
checksum,
&mut verif_buf,
);
assert!(result.is_ok());
}
#[test]
fn test_null_checksum_impl() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("mod-crc.bin");
// The file to check does not even need to exist, and the verification buffer can be
// empty: the null checksum is always yields the same result.
let result = NATIVE_FS.checksum_verify(
file_path.to_str().unwrap(),
ChecksumType::NullChecksum,
0,
&mut [],
);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_checksum_not_implemented() {
let tmpdir = tempdir().expect("creating tmpdir failed");
let file_path = tmpdir.path().join("mod-crc.bin");
// The file to check does not even need to exist, and the verification buffer can be
// empty: the null checksum is always yields the same result.
let result = NATIVE_FS.checksum_verify(
file_path.to_str().unwrap(),
ChecksumType::Crc32Proximity1,
0,
&mut [],
);
assert!(result.is_err());
let error = result.unwrap_err();
if let FilestoreError::ChecksumTypeNotImplemented(cksum_type) = error {
assert_eq!(
error.to_string(),
format!("checksum {:?} not implemented", cksum_type)
);
} else {
panic!("unexpected error");
}
}
}

668
satrs/src/cfdp/mod.rs Normal file
View File

@ -0,0 +1,668 @@
//! This module contains the implementation of the CFDP high level classes as specified in the
//! CCSDS 727.0-B-5.
use core::{cell::RefCell, fmt::Debug, hash::Hash};
use crc::{Crc, CRC_32_CKSUM};
use hashbrown::HashMap;
use spacepackets::{
cfdp::{
pdu::{FileDirectiveType, PduError, PduHeader},
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
},
util::{UnsignedByteField, UnsignedEnum},
};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::time::CountdownProvider;
#[cfg(feature = "std")]
pub mod dest;
#[cfg(feature = "alloc")]
pub mod filestore;
#[cfg(feature = "std")]
pub mod source;
pub mod user;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityType {
Sending,
Receiving,
}
pub enum TimerContext {
CheckLimit {
local_id: UnsignedByteField,
remote_id: UnsignedByteField,
entity_type: EntityType,
},
NakActivity {
expiry_time_seconds: f32,
},
PositiveAck {
expiry_time_seconds: f32,
},
}
/// A generic trait which allows CFDP entities to create check timers which are required to
/// implement special procedures in unacknowledged transmission mode, as specified in 4.6.3.2
/// and 4.6.3.3.
///
/// This trait also allows the creation of different check timers depending on context and purpose
/// of the timer, the runtime environment (e.g. standard clock timer vs. timer using a RTC) or
/// other factors.
///
/// The countdown timer is used by 3 mechanisms of the CFDP protocol.
///
/// ## 1. Check limit handling
///
/// The first mechanism is the check limit handling for unacknowledged transfers as specified
/// in 4.6.3.2 and 4.6.3.3 of the CFDP standard.
/// For this mechanism, the timer has different functionality depending on whether
/// the using entity is the sending entity or the receiving entity for the unacknowledged
/// transmission mode.
///
/// For the sending entity, this timer determines the expiry period for declaring a check limit
/// fault after sending an EOF PDU with requested closure. This allows a timeout of the transfer.
/// Also see 4.6.3.2 of the CFDP standard.
///
/// For the receiving entity, this timer determines the expiry period for incrementing a check
/// counter after an EOF PDU is received for an incomplete file transfer. This allows out-of-order
/// reception of file data PDUs and EOF PDUs. Also see 4.6.3.3 of the CFDP standard.
///
/// ## 2. NAK activity limit
///
/// The timer will be used to perform the NAK activity check as specified in 4.6.4.7 of the CFDP
/// standard. The expiration period will be provided by the NAK timer expiration limit of the
/// remote entity configuration.
///
/// ## 3. Positive ACK procedures
///
/// The timer will be used to perform the Positive Acknowledgement Procedures as specified in
/// 4.7. 1of the CFDP standard. The expiration period will be provided by the Positive ACK timer
/// interval of the remote entity configuration.
#[cfg(feature = "alloc")]
pub trait CheckTimerCreator {
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CountdownProvider>;
}
/// Simple implementation of the [CheckTimerCreator] trait assuming a standard runtime.
/// It also assumes that a second accuracy of the check timer period is sufficient.
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct StdCheckTimer {
expiry_time_seconds: u64,
start_time: std::time::Instant,
}
#[cfg(feature = "std")]
impl StdCheckTimer {
pub fn new(expiry_time_seconds: u64) -> Self {
Self {
expiry_time_seconds,
start_time: std::time::Instant::now(),
}
}
}
#[cfg(feature = "std")]
impl CountdownProvider for StdCheckTimer {
fn has_expired(&self) -> bool {
let elapsed_time = self.start_time.elapsed();
if elapsed_time.as_secs() > self.expiry_time_seconds {
return true;
}
false
}
fn reset(&mut self) {
self.start_time = std::time::Instant::now();
}
}
/// This structure models the remote entity configuration information as specified in chapter 8.3
/// of the CFDP standard.
/// Some of the fields which were not considered necessary for the Rust implementation
/// were omitted. Some other fields which are not contained inside the standard but are considered
/// necessary for the Rust implementation are included.
///
/// ## Notes on Positive Acknowledgment Procedures
///
/// The `positive_ack_timer_interval_seconds` and `positive_ack_timer_expiration_limit` will
/// be used for positive acknowledgement procedures as specified in CFDP chapter 4.7. The sending
/// entity will start the timer for any PDUs where an acknowledgment is required (e.g. EOF PDU).
/// Once the expected ACK response has not been received for that interval, as counter will be
/// incremented and the timer will be reset. Once the counter exceeds the
/// `positive_ack_timer_expiration_limit`, a Positive ACK Limit Reached fault will be declared.
///
/// ## Notes on Deferred Lost Segment Procedures
///
/// This procedure will be active if an EOF (No Error) PDU is received in acknowledged mode. After
/// issuing the NAK sequence which has the whole file scope, a timer will be started. The timer is
/// reset when missing segments or missing metadata is received. The timer will be deactivated if
/// all missing data is received. If the timer expires, a new NAK sequence will be issued and a
/// counter will be incremented, which can lead to a NAK Limit Reached fault being declared.
///
/// ## Fields
///
/// * `entity_id` - The ID of the remote entity.
/// * `max_packet_len` - This determines of all PDUs generated for that remote entity in addition
/// to the `max_file_segment_len` attribute which also determines the size of file data PDUs.
/// * `max_file_segment_len` The maximum file segment length which determines the maximum size
/// of file data PDUs in addition to the `max_packet_len` attribute. If this field is set
/// to None, the maximum file segment length will be derived from the maximum packet length.
/// If this has some value which is smaller than the segment value derived from
/// `max_packet_len`, this value will be picked.
/// * `closure_requested_by_default` - If the closure requested field is not supplied as part of
/// the Put Request, it will be determined from this field in the remote configuration.
/// * `crc_on_transmission_by_default` - If the CRC option is not supplied as part of the Put
/// Request, it will be determined from this field in the remote configuration.
/// * `default_transmission_mode` - If the transmission mode is not supplied as part of the
/// Put Request, it will be determined from this field in the remote configuration.
/// * `disposition_on_cancellation` - Determines whether an incomplete received file is discard on
/// transaction cancellation. Defaults to False.
/// * `default_crc_type` - Default checksum type used to calculate for all file transmissions to
/// this remote entity.
/// * `check_limit` - This timer determines the expiry period for incrementing a check counter
/// after an EOF PDU is received for an incomplete file transfer. This allows out-of-order
/// reception of file data PDUs and EOF PDUs. Also see 4.6.3.3 of the CFDP standard. Defaults to
/// 2, so the check limit timer may expire twice.
/// * `positive_ack_timer_interval_seconds`- See the notes on the Positive Acknowledgment
/// Procedures inside the class documentation. Expected as floating point seconds. Defaults to
/// 10 seconds.
/// * `positive_ack_timer_expiration_limit` - See the notes on the Positive Acknowledgment
/// Procedures inside the class documentation. Defaults to 2, so the timer may expire twice.
/// * `immediate_nak_mode` - Specifies whether a NAK sequence should be issued immediately when a
/// file data gap or lost metadata is detected in the acknowledged mode. Defaults to True.
/// * `nak_timer_interval_seconds` - See the notes on the Deferred Lost Segment Procedure inside
/// the class documentation. Expected as floating point seconds. Defaults to 10 seconds.
/// * `nak_timer_expiration_limit` - See the notes on the Deferred Lost Segment Procedure inside
/// the class documentation. Defaults to 2, so the timer may expire two times.
#[derive(Debug, Copy, Clone)]
pub struct RemoteEntityConfig {
pub entity_id: UnsignedByteField,
pub max_packet_len: usize,
pub max_file_segment_len: usize,
pub closure_requested_by_default: bool,
pub crc_on_transmission_by_default: bool,
pub default_transmission_mode: TransmissionMode,
pub default_crc_type: ChecksumType,
pub positive_ack_timer_interval_seconds: f32,
pub positive_ack_timer_expiration_limit: u32,
pub check_limit: u32,
pub disposition_on_cancellation: bool,
pub immediate_nak_mode: bool,
pub nak_timer_interval_seconds: f32,
pub nak_timer_expiration_limit: u32,
}
impl RemoteEntityConfig {
pub fn new_with_default_values(
entity_id: UnsignedByteField,
max_file_segment_len: usize,
max_packet_len: usize,
closure_requested_by_default: bool,
crc_on_transmission_by_default: bool,
default_transmission_mode: TransmissionMode,
default_crc_type: ChecksumType,
) -> Self {
Self {
entity_id,
max_file_segment_len,
max_packet_len,
closure_requested_by_default,
crc_on_transmission_by_default,
default_transmission_mode,
default_crc_type,
check_limit: 2,
positive_ack_timer_interval_seconds: 10.0,
positive_ack_timer_expiration_limit: 2,
disposition_on_cancellation: false,
immediate_nak_mode: true,
nak_timer_interval_seconds: 10.0,
nak_timer_expiration_limit: 2,
}
}
}
pub trait RemoteEntityConfigProvider {
/// Retrieve the remote entity configuration for the given remote ID.
fn get_remote_config(&self, remote_id: u64) -> Option<&RemoteEntityConfig>;
fn get_remote_config_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig>;
/// Add a new remote configuration. Return [true] if the configuration was
/// inserted successfully, and [false] if a configuration already exists.
fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool;
/// Remote a configuration. Returns [true] if the configuration was removed successfully,
/// and [false] if no configuration exists for the given remote ID.
fn remove_config(&mut self, remote_id: u64) -> bool;
}
#[cfg(feature = "std")]
#[derive(Default)]
pub struct StdRemoteEntityConfigProvider {
remote_cfg_table: HashMap<u64, RemoteEntityConfig>,
}
#[cfg(feature = "std")]
impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
fn get_remote_config(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
self.remote_cfg_table.get(&remote_id)
}
fn get_remote_config_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
self.remote_cfg_table.get_mut(&remote_id)
}
fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool {
self.remote_cfg_table
.insert(cfg.entity_id.value(), *cfg)
.is_some()
}
fn remove_config(&mut self, remote_id: u64) -> bool {
self.remote_cfg_table.remove(&remote_id).is_some()
}
}
/// This trait introduces some callbacks which will be called when a particular CFDP fault
/// handler is called.
///
/// It is passed into the CFDP handlers as part of the [DefaultFaultHandler] and the local entity
/// configuration and provides a way to specify custom user error handlers. This allows to
/// implement some CFDP features like fault handler logging, which would not be possible
/// generically otherwise.
///
/// For each error reported by the [DefaultFaultHandler], the appropriate fault handler callback
/// will be called depending on the [FaultHandlerCode].
pub trait UserFaultHandler {
fn notice_of_suspension_cb(
&mut self,
transaction_id: TransactionId,
cond: ConditionCode,
progress: u64,
);
fn notice_of_cancellation_cb(
&mut self,
transaction_id: TransactionId,
cond: ConditionCode,
progress: u64,
);
fn abandoned_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
}
/// This structure is used to implement the fault handling as specified in chapter 4.8 of the CFDP
/// standard.
///
/// It does so by mapping each applicable [spacepackets::cfdp::ConditionCode] to a fault handler
/// which is denoted by the four [spacepackets::cfdp::FaultHandlerCode]s. This code is used
/// to select the error handling inside the CFDP handler itself in addition to dispatching to a
/// user-provided callback function provided by the [UserFaultHandler].
///
/// Some note on the provided default settings:
///
/// - Checksum failures will be ignored by default. This is because for unacknowledged transfers,
/// cancelling the transfer immediately would interfere with the check limit mechanism specified
/// in chapter 4.6.3.3.
/// - Unsupported checksum types will also be ignored by default. Even if the checksum type is
/// not supported the file transfer might still have worked properly.
///
/// For all other faults, the default fault handling operation will be to cancel the transaction.
/// These defaults can be overriden by using the [Self::set_fault_handler] method.
/// Please note that in any case, fault handler overrides can be specified by the sending CFDP
/// entity.
pub struct DefaultFaultHandler {
handler_array: [FaultHandlerCode; 10],
// Could also change the user fault handler trait to have non mutable methods, but that limits
// flexbility on the user side..
user_fault_handler: RefCell<Box<dyn UserFaultHandler + Send>>,
}
impl DefaultFaultHandler {
fn condition_code_to_array_index(conditon_code: ConditionCode) -> Option<usize> {
Some(match conditon_code {
ConditionCode::PositiveAckLimitReached => 0,
ConditionCode::KeepAliveLimitReached => 1,
ConditionCode::InvalidTransmissionMode => 2,
ConditionCode::FilestoreRejection => 3,
ConditionCode::FileChecksumFailure => 4,
ConditionCode::FileSizeError => 5,
ConditionCode::NakLimitReached => 6,
ConditionCode::InactivityDetected => 7,
ConditionCode::CheckLimitReached => 8,
ConditionCode::UnsupportedChecksumType => 9,
_ => return None,
})
}
pub fn set_fault_handler(
&mut self,
condition_code: ConditionCode,
fault_handler: FaultHandlerCode,
) {
let array_idx = Self::condition_code_to_array_index(condition_code);
if array_idx.is_none() {
return;
}
self.handler_array[array_idx.unwrap()] = fault_handler;
}
pub fn new(user_fault_handler: Box<dyn UserFaultHandler + Send>) -> Self {
let mut init_array = [FaultHandlerCode::NoticeOfCancellation; 10];
init_array
[Self::condition_code_to_array_index(ConditionCode::FileChecksumFailure).unwrap()] =
FaultHandlerCode::IgnoreError;
init_array[Self::condition_code_to_array_index(ConditionCode::UnsupportedChecksumType)
.unwrap()] = FaultHandlerCode::IgnoreError;
Self {
handler_array: init_array,
user_fault_handler: RefCell::new(user_fault_handler),
}
}
pub fn get_fault_handler(&self, condition_code: ConditionCode) -> FaultHandlerCode {
let array_idx = Self::condition_code_to_array_index(condition_code);
if array_idx.is_none() {
return FaultHandlerCode::IgnoreError;
}
self.handler_array[array_idx.unwrap()]
}
pub fn report_fault(
&self,
transaction_id: TransactionId,
condition: ConditionCode,
progress: u64,
) -> FaultHandlerCode {
let array_idx = Self::condition_code_to_array_index(condition);
if array_idx.is_none() {
return FaultHandlerCode::IgnoreError;
}
let fh_code = self.handler_array[array_idx.unwrap()];
let mut handler_mut = self.user_fault_handler.borrow_mut();
match fh_code {
FaultHandlerCode::NoticeOfCancellation => {
handler_mut.notice_of_cancellation_cb(transaction_id, condition, progress);
}
FaultHandlerCode::NoticeOfSuspension => {
handler_mut.notice_of_suspension_cb(transaction_id, condition, progress);
}
FaultHandlerCode::IgnoreError => {
handler_mut.ignore_cb(transaction_id, condition, progress);
}
FaultHandlerCode::AbandonTransaction => {
handler_mut.abandoned_cb(transaction_id, condition, progress);
}
}
fh_code
}
}
pub struct IndicationConfig {
pub eof_sent: bool,
pub eof_recv: bool,
pub file_segment_recv: bool,
pub transaction_finished: bool,
pub suspended: bool,
pub resumed: bool,
}
impl Default for IndicationConfig {
fn default() -> Self {
Self {
eof_sent: true,
eof_recv: true,
file_segment_recv: true,
transaction_finished: true,
suspended: true,
resumed: true,
}
}
}
pub struct LocalEntityConfig {
pub id: UnsignedByteField,
pub indication_cfg: IndicationConfig,
pub default_fault_handler: DefaultFaultHandler,
}
/// The CFDP transaction ID of a CFDP transaction consists of the source entity ID and the sequence
/// number of that transfer which is also determined by the CFDP source entity.
#[derive(Debug, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TransactionId {
source_id: UnsignedByteField,
seq_num: UnsignedByteField,
}
impl TransactionId {
pub fn new(source_id: UnsignedByteField, seq_num: UnsignedByteField) -> Self {
Self { source_id, seq_num }
}
pub fn source_id(&self) -> &UnsignedByteField {
&self.source_id
}
pub fn seq_num(&self) -> &UnsignedByteField {
&self.seq_num
}
}
impl Hash for TransactionId {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.source_id.value().hash(state);
self.seq_num.value().hash(state);
}
}
impl PartialEq for TransactionId {
fn eq(&self, other: &Self) -> bool {
self.source_id.value() == other.source_id.value()
&& self.seq_num.value() == other.seq_num.value()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TransactionStep {
Idle = 0,
TransactionStart = 1,
ReceivingFileDataPdus = 2,
ReceivingFileDataPdusWithCheckLimitHandling = 3,
SendingAckPdu = 4,
TransferCompletion = 5,
SendingFinishedPdu = 6,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum State {
Idle = 0,
Busy = 1,
Suspended = 2,
}
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PacketTarget {
SourceEntity,
DestEntity,
}
/// This is a helper struct which contains base information about a particular PDU packet.
/// This is also necessary information for CFDP packet routing. For example, some packet types
/// like file data PDUs can only be used by CFDP source entities.
pub struct PacketInfo<'raw_packet> {
pdu_type: PduType,
pdu_directive: Option<FileDirectiveType>,
target: PacketTarget,
raw_packet: &'raw_packet [u8],
}
impl<'raw> PacketInfo<'raw> {
pub fn new(raw_packet: &'raw [u8]) -> Result<Self, PduError> {
let (pdu_header, header_len) = PduHeader::from_bytes(raw_packet)?;
if pdu_header.pdu_type() == PduType::FileData {
return Ok(Self {
pdu_type: pdu_header.pdu_type(),
pdu_directive: None,
target: PacketTarget::DestEntity,
raw_packet,
});
}
if pdu_header.pdu_datafield_len() < 1 {
return Err(PduError::FormatError);
}
// Route depending on PDU type and directive type if applicable. Retrieve directive type
// from the raw stream for better performance (with sanity and directive code check).
// The routing is based on section 4.5 of the CFDP standard which specifies the PDU forwarding
// procedure.
let directive = FileDirectiveType::try_from(raw_packet[header_len]).map_err(|_| {
PduError::InvalidDirectiveType {
found: raw_packet[header_len],
expected: None,
}
})?;
let packet_target = match directive {
// Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a.
// the source handler
FileDirectiveType::NakPdu
| FileDirectiveType::FinishedPdu
| FileDirectiveType::KeepAlivePdu => PacketTarget::SourceEntity,
// Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a.
// the destination handler
FileDirectiveType::MetadataPdu
| FileDirectiveType::EofPdu
| FileDirectiveType::PromptPdu => PacketTarget::DestEntity,
// Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply
// extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to
// the source handler, for a Finished PDU, it is passed to the destination handler.
FileDirectiveType::AckPdu => {
let acked_directive = FileDirectiveType::try_from(raw_packet[header_len + 1])
.map_err(|_| PduError::InvalidDirectiveType {
found: raw_packet[header_len],
expected: None,
})?;
if acked_directive == FileDirectiveType::EofPdu {
PacketTarget::SourceEntity
} else if acked_directive == FileDirectiveType::FinishedPdu {
PacketTarget::DestEntity
} else {
// TODO: Maybe a better error? This might be confusing..
return Err(PduError::InvalidDirectiveType {
found: raw_packet[header_len + 1],
expected: None,
});
}
}
};
Ok(Self {
pdu_type: pdu_header.pdu_type(),
pdu_directive: Some(directive),
target: packet_target,
raw_packet,
})
}
pub fn pdu_type(&self) -> PduType {
self.pdu_type
}
pub fn pdu_directive(&self) -> Option<FileDirectiveType> {
self.pdu_directive
}
pub fn target(&self) -> PacketTarget {
self.target
}
pub fn raw_packet(&self) -> &[u8] {
self.raw_packet
}
}
#[cfg(test)]
mod tests {
use spacepackets::cfdp::{
lv::Lv,
pdu::{
eof::EofPdu,
file_data::FileDataPdu,
metadata::{MetadataGenericParams, MetadataPduCreator},
CommonPduConfig, FileDirectiveType, PduHeader, WritablePduPacket,
},
PduType,
};
use crate::cfdp::PacketTarget;
use super::PacketInfo;
fn generic_pdu_header() -> PduHeader {
let pdu_conf = CommonPduConfig::default();
PduHeader::new_no_file_data(pdu_conf, 0)
}
#[test]
fn test_metadata_pdu_info() {
let mut buf: [u8; 128] = [0; 128];
let pdu_header = generic_pdu_header();
let metadata_params = MetadataGenericParams::default();
let src_file_name = "hello.txt";
let dest_file_name = "hello-dest.txt";
let src_lv = Lv::new_from_str(src_file_name).unwrap();
let dest_lv = Lv::new_from_str(dest_file_name).unwrap();
let metadata_pdu =
MetadataPduCreator::new_no_opts(pdu_header, metadata_params, src_lv, dest_lv);
metadata_pdu
.write_to_bytes(&mut buf)
.expect("writing metadata PDU failed");
let packet_info = PacketInfo::new(&buf).expect("creating packet info failed");
assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
assert!(packet_info.pdu_directive().is_some());
assert_eq!(
packet_info.pdu_directive().unwrap(),
FileDirectiveType::MetadataPdu
);
assert_eq!(packet_info.target(), PacketTarget::DestEntity);
}
#[test]
fn test_filedata_pdu_info() {
let mut buf: [u8; 128] = [0; 128];
let pdu_header = generic_pdu_header();
let file_data_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 0, &[]);
file_data_pdu
.write_to_bytes(&mut buf)
.expect("writing file data PDU failed");
let packet_info = PacketInfo::new(&buf).expect("creating packet info failed");
assert_eq!(packet_info.pdu_type(), PduType::FileData);
assert!(packet_info.pdu_directive().is_none());
assert_eq!(packet_info.target(), PacketTarget::DestEntity);
}
#[test]
fn test_eof_pdu_info() {
let mut buf: [u8; 128] = [0; 128];
let pdu_header = generic_pdu_header();
let eof_pdu = EofPdu::new_no_error(pdu_header, 0, 0);
eof_pdu
.write_to_bytes(&mut buf)
.expect("writing file data PDU failed");
let packet_info = PacketInfo::new(&buf).expect("creating packet info failed");
assert_eq!(packet_info.pdu_type(), PduType::FileDirective);
assert!(packet_info.pdu_directive().is_some());
assert_eq!(
packet_info.pdu_directive().unwrap(),
FileDirectiveType::EofPdu
);
}
}

15
satrs/src/cfdp/source.rs Normal file
View File

@ -0,0 +1,15 @@
#![allow(dead_code)]
use spacepackets::util::UnsignedByteField;
pub struct SourceHandler {
id: UnsignedByteField,
}
impl SourceHandler {
pub fn new(id: impl Into<UnsignedByteField>) -> Self {
Self { id: id.into() }
}
}
#[cfg(test)]
mod tests {}

96
satrs/src/cfdp/user.rs Normal file
View File

@ -0,0 +1,96 @@
use spacepackets::{
cfdp::{
pdu::{
file_data::SegmentMetadata,
finished::{DeliveryCode, FileStatus},
},
tlv::{msg_to_user::MsgToUserTlv, WritableTlv},
ConditionCode,
},
util::UnsignedByteField,
};
use super::TransactionId;
#[derive(Debug, Copy, Clone)]
pub struct TransactionFinishedParams {
pub id: TransactionId,
pub condition_code: ConditionCode,
pub delivery_code: DeliveryCode,
pub file_status: FileStatus,
}
#[derive(Debug)]
pub struct MetadataReceivedParams<'src_file, 'dest_file, 'msgs_to_user> {
pub id: TransactionId,
pub source_id: UnsignedByteField,
pub file_size: u64,
pub src_file_name: &'src_file str,
pub dest_file_name: &'dest_file str,
pub msgs_to_user: &'msgs_to_user [MsgToUserTlv<'msgs_to_user>],
}
#[cfg(feature = "alloc")]
#[derive(Debug)]
pub struct OwnedMetadataRecvdParams {
pub id: TransactionId,
pub source_id: UnsignedByteField,
pub file_size: u64,
pub src_file_name: alloc::string::String,
pub dest_file_name: alloc::string::String,
pub msgs_to_user: alloc::vec::Vec<alloc::vec::Vec<u8>>,
}
#[cfg(feature = "alloc")]
impl From<MetadataReceivedParams<'_, '_, '_>> for OwnedMetadataRecvdParams {
fn from(value: MetadataReceivedParams) -> Self {
Self::from(&value)
}
}
#[cfg(feature = "alloc")]
impl From<&MetadataReceivedParams<'_, '_, '_>> for OwnedMetadataRecvdParams {
fn from(value: &MetadataReceivedParams) -> Self {
Self {
id: value.id,
source_id: value.source_id,
file_size: value.file_size,
src_file_name: value.src_file_name.into(),
dest_file_name: value.dest_file_name.into(),
msgs_to_user: value.msgs_to_user.iter().map(|tlv| tlv.to_vec()).collect(),
}
}
}
#[derive(Debug)]
pub struct FileSegmentRecvdParams<'seg_meta> {
pub id: TransactionId,
pub offset: u64,
pub length: usize,
pub segment_metadata: Option<&'seg_meta SegmentMetadata<'seg_meta>>,
}
pub trait CfdpUser {
fn transaction_indication(&mut self, id: &TransactionId);
fn eof_sent_indication(&mut self, id: &TransactionId);
fn transaction_finished_indication(&mut self, finished_params: &TransactionFinishedParams);
fn metadata_recvd_indication(&mut self, md_recvd_params: &MetadataReceivedParams);
fn file_segment_recvd_indication(&mut self, segment_recvd_params: &FileSegmentRecvdParams);
// TODO: The standard does not strictly specify how the report information looks..
fn report_indication(&mut self, id: &TransactionId);
fn suspended_indication(&mut self, id: &TransactionId, condition_code: ConditionCode);
fn resumed_indication(&mut self, id: &TransactionId, progress: u64);
fn fault_indication(
&mut self,
id: &TransactionId,
condition_code: ConditionCode,
progress: u64,
);
fn abandoned_indication(
&mut self,
id: &TransactionId,
condition_code: ConditionCode,
progress: u64,
);
fn eof_recvd_indication(&mut self, id: &TransactionId);
}

View File

@ -1,448 +0,0 @@
use crate::{
mode::{ModeAndSubmode, ModeReply, ModeRequest, ModeRequestSender},
mode_tree::{ModeStoreProvider, ModeStoreVec},
queue::{GenericSendError, GenericTargetedMessagingError},
request::{GenericMessage, RequestId},
ComponentId,
};
use core::fmt::Debug;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ActiveModeCommandContext {
pub target_mode: ModeAndSubmode,
pub active_request_id: RequestId,
}
#[derive(Debug, Default, PartialEq, Eq)]
pub enum DevManagerHelperResult {
#[default]
Idle,
Busy,
ModeCommandingDone(ActiveModeCommandContext),
}
#[derive(Debug)]
pub enum DevManagerHelperError {
ChildNotInStore,
}
pub trait DevManagerUserHook: Debug {
fn send_mode_cmd_to_child(
&self,
request_id: RequestId,
target_id: ComponentId,
mode: ModeAndSubmode,
forced: bool,
children_mode_store: &mut ModeStoreVec,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericSendError>;
fn send_mode_cmds_to_children(
&self,
request_id: RequestId,
commanded_parent_mode: ModeAndSubmode,
forced: bool,
children_mode_store: &mut ModeStoreVec,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericSendError>;
}
#[derive(Debug, Default)]
pub struct TransparentDevManagerHook {}
impl DevManagerUserHook for TransparentDevManagerHook {
fn send_mode_cmds_to_children(
&self,
request_id: RequestId,
commanded_parent_mode: ModeAndSubmode,
forced: bool,
children_mode_store: &mut ModeStoreVec,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericSendError> {
for child in children_mode_store {
mode_req_sender.send_mode_request(
request_id,
child.id(),
ModeRequest::SetMode {
mode_and_submode: commanded_parent_mode,
forced,
},
)?;
child.awaiting_reply = true;
}
Ok(())
}
fn send_mode_cmd_to_child(
&self,
request_id: RequestId,
target_id: ComponentId,
mode: ModeAndSubmode,
forced: bool,
children_mode_store: &mut ModeStoreVec,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericSendError> {
let mut_val = children_mode_store
.get_mut(target_id)
.ok_or(GenericSendError::TargetDoesNotExist(target_id))?;
mut_val.awaiting_reply = true;
mode_req_sender.send_mode_request(
request_id,
target_id,
ModeRequest::SetMode {
mode_and_submode: mode,
forced,
},
)?;
Ok(())
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum DevManagerCommandingState {
#[default]
Idle,
AwaitingReplies(ActiveModeCommandContext),
}
impl DevManagerCommandingState {
fn new_active_cmd(mode_and_submode: ModeAndSubmode, active_request_id: RequestId) -> Self {
DevManagerCommandingState::AwaitingReplies(ActiveModeCommandContext {
target_mode: mode_and_submode,
active_request_id,
})
}
}
/// A generic helper for manager components which manage child components in a mode tree.
///
/// Mode commands are usually forwarded to all children components transparently.
/// For example, this could be used in an Assembly component which manages multiple redundant
/// child components. It can also be used inside a manager component which only manages one device.
#[derive(Debug, Default)]
pub struct DevManagerCommandingHelper<UserHook: DevManagerUserHook> {
/// The IDs, modes and reply awaition status of all children are tracked in this data
/// structure.
pub children_mode_store: ModeStoreVec,
pub user_hook: UserHook,
pub state: DevManagerCommandingState,
}
impl<UserHook: DevManagerUserHook> DevManagerCommandingHelper<UserHook> {
pub fn new(user_hook: UserHook) -> Self {
Self {
children_mode_store: Default::default(),
user_hook,
state: Default::default(),
}
}
pub fn send_mode_cmd_to_one_child(
&mut self,
request_id: RequestId,
target_id: ComponentId,
mode_and_submode: ModeAndSubmode,
forced: bool,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericSendError> {
self.state = DevManagerCommandingState::new_active_cmd(mode_and_submode, request_id);
self.user_hook.send_mode_cmd_to_child(
request_id,
target_id,
mode_and_submode,
forced,
&mut self.children_mode_store,
mode_req_sender,
)?;
Ok(())
}
pub fn send_mode_cmd_to_all_children(
&mut self,
request_id: RequestId,
mode_and_submode: ModeAndSubmode,
forced: bool,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericSendError> {
self.state = DevManagerCommandingState::new_active_cmd(mode_and_submode, request_id);
self.user_hook.send_mode_cmds_to_children(
request_id,
mode_and_submode,
forced,
&mut self.children_mode_store,
mode_req_sender,
)?;
Ok(())
}
pub fn target_mode(&self) -> Option<ModeAndSubmode> {
match self.state {
DevManagerCommandingState::Idle => None,
DevManagerCommandingState::AwaitingReplies(context) => Some(context.target_mode),
}
}
pub fn state(&self) -> DevManagerCommandingState {
self.state
}
pub fn send_announce_mode_cmd_to_children(
&self,
request_id: RequestId,
mode_req_sender: &impl ModeRequestSender,
recursive: bool,
) -> Result<(), GenericTargetedMessagingError> {
let mut request = ModeRequest::AnnounceMode;
if recursive {
request = ModeRequest::AnnounceModeRecursive;
}
for child in self.children_mode_store.0.iter() {
mode_req_sender.send_mode_request(request_id, child.id(), request)?;
}
Ok(())
}
pub fn add_mode_child(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.children_mode_store.add_component(target_id, mode);
}
/// Helper method which counts the number of children which have a certain mode.
pub fn count_number_of_children_with_mode(&self, mode_and_submode: ModeAndSubmode) -> usize {
let mut children_in_target_mode = 0;
for child in &self.children_mode_store {
if child.mode_and_submode() == mode_and_submode {
children_in_target_mode += 1;
}
}
children_in_target_mode
}
pub fn handle_mode_reply(
&mut self,
mode_reply: &GenericMessage<ModeReply>,
) -> Result<DevManagerHelperResult, DevManagerHelperError> {
let context = match self.state {
DevManagerCommandingState::Idle => return Ok(DevManagerHelperResult::Idle),
DevManagerCommandingState::AwaitingReplies(active_mode_command_context) => {
Some(active_mode_command_context)
}
};
if !self
.children_mode_store
.has_component(mode_reply.sender_id())
{
return Err(DevManagerHelperError::ChildNotInStore);
}
let mut generic_mode_reply_handler = |mode_and_submode: Option<ModeAndSubmode>| {
// Tying the reply awaition to the request ID ensures that something like replies
// belonging to older requests do not interfere with the completion handling of
// the mode commanding. This is important for forced mode commands.
let mut handle_awaition = false;
if let DevManagerCommandingState::AwaitingReplies { .. } = self.state {
handle_awaition = true;
}
let still_awating_replies = self.children_mode_store.mode_reply_handler(
mode_reply.sender_id(),
mode_and_submode,
handle_awaition,
);
// It is okay to unwrap: If awaition should be handled, the returned value should
// always be some valid value.
if handle_awaition && !still_awating_replies.unwrap() {
self.state = DevManagerCommandingState::Idle;
return Ok(DevManagerHelperResult::ModeCommandingDone(context.unwrap()));
}
Ok(DevManagerHelperResult::Busy)
};
match mode_reply.message {
ModeReply::ModeInfo(mode_and_submode) | ModeReply::ModeReply(mode_and_submode) => {
generic_mode_reply_handler(Some(mode_and_submode))
}
ModeReply::CantReachMode(_result_u16) => generic_mode_reply_handler(None),
ModeReply::WrongMode {
expected: _,
reached,
} => generic_mode_reply_handler(Some(reached)),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
mode::{tests::ModeReqSenderMock, UNKNOWN_MODE},
request::MessageMetadata,
};
use super::*;
pub enum ExampleId {
Id1 = 1,
Id2 = 2,
}
pub enum ExampleMode {
Mode1 = 1,
Mode2 = 2,
}
#[test]
fn test_basic() {
let assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
assert_eq!(assy_helper.state(), DevManagerCommandingState::Idle);
}
#[test]
fn test_mode_announce() {
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
assy_helper
.send_announce_mode_cmd_to_children(1, &mode_req_sender, false)
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceMode);
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id2 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceMode);
}
#[test]
fn test_mode_announce_recursive() {
let mut assy_helper = DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
assy_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
assy_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
assy_helper
.send_announce_mode_cmd_to_children(1, &mode_req_sender, true)
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
let mut req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id2 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(req.request, ModeRequest::AnnounceModeRecursive);
}
#[test]
fn test_mode_commanding_one_child() {
let mut dev_mgmt_helper =
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode1 as u32, 0);
dev_mgmt_helper
.send_mode_cmd_to_one_child(
1,
ExampleId::Id1 as u64,
expected_mode,
false,
&mode_req_sender,
)
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 1);
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(
req.request,
ModeRequest::SetMode {
mode_and_submode: expected_mode,
forced: false
}
);
matches!(
dev_mgmt_helper.state(),
DevManagerCommandingState::AwaitingReplies { .. }
);
if let DevManagerCommandingState::AwaitingReplies(ctx) = dev_mgmt_helper.state() {
assert_eq!(ctx.target_mode, expected_mode);
assert_eq!(ctx.active_request_id, 1);
}
let reply = GenericMessage::new(
MessageMetadata::new(1, ExampleId::Id1 as u64),
ModeReply::ModeReply(expected_mode),
);
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {
target_mode,
active_request_id,
}) = dev_mgmt_helper.handle_mode_reply(&reply).unwrap()
{
assert_eq!(target_mode, expected_mode);
assert_eq!(active_request_id, 1);
}
matches!(dev_mgmt_helper.state(), DevManagerCommandingState::Idle);
}
#[test]
fn test_mode_commanding_multi_child() {
let mut dev_mgmt_helper =
DevManagerCommandingHelper::new(TransparentDevManagerHook::default());
let mode_req_sender = ModeReqSenderMock::default();
dev_mgmt_helper.add_mode_child(ExampleId::Id1 as u64, UNKNOWN_MODE);
dev_mgmt_helper.add_mode_child(ExampleId::Id2 as u64, UNKNOWN_MODE);
let expected_mode = ModeAndSubmode::new(ExampleMode::Mode2 as u32, 0);
dev_mgmt_helper
.send_mode_cmd_to_all_children(1, expected_mode, false, &mode_req_sender)
.unwrap();
assert_eq!(mode_req_sender.requests.borrow().len(), 2);
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id1 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(
req.request,
ModeRequest::SetMode {
mode_and_submode: expected_mode,
forced: false
}
);
let req = mode_req_sender.requests.borrow_mut().pop_front().unwrap();
assert_eq!(req.target_id, ExampleId::Id2 as u64);
assert_eq!(req.request_id, 1);
assert_eq!(
req.request,
ModeRequest::SetMode {
mode_and_submode: expected_mode,
forced: false
}
);
matches!(
dev_mgmt_helper.state(),
DevManagerCommandingState::AwaitingReplies { .. }
);
if let DevManagerCommandingState::AwaitingReplies(ctx) = dev_mgmt_helper.state() {
assert_eq!(ctx.target_mode, expected_mode);
assert_eq!(ctx.active_request_id, 1);
}
let reply = GenericMessage::new(
MessageMetadata::new(1, ExampleId::Id1 as u64),
ModeReply::ModeReply(expected_mode),
);
assert_eq!(
dev_mgmt_helper.handle_mode_reply(&reply).unwrap(),
DevManagerHelperResult::Busy
);
let reply = GenericMessage::new(
MessageMetadata::new(1, ExampleId::Id2 as u64),
ModeReply::ModeReply(expected_mode),
);
if let DevManagerHelperResult::ModeCommandingDone(ActiveModeCommandContext {
target_mode,
active_request_id,
}) = dev_mgmt_helper.handle_mode_reply(&reply).unwrap()
{
assert_eq!(target_mode, expected_mode);
assert_eq!(active_request_id, 1);
}
matches!(dev_mgmt_helper.state(), DevManagerCommandingState::Idle);
}
}

View File

@ -15,24 +15,20 @@ pub enum OpResult {
TerminationRequested, TerminationRequested,
} }
#[derive(Debug)]
pub enum ExecutionType { pub enum ExecutionType {
Infinite, Infinite,
Cycles(u32), Cycles(u32),
OneShot, OneShot,
} }
pub trait Executable { pub trait Executable: Send {
type Error; type Error;
fn exec_type(&self) -> ExecutionType;
fn task_name(&self) -> &'static str; fn task_name(&self) -> &'static str;
fn periodic_op(&mut self, op_code: i32) -> Result<OpResult, Self::Error>; fn periodic_op(&mut self, op_code: i32) -> Result<OpResult, Self::Error>;
} }
pub trait ExecutableWithType: Executable {
fn exec_type(&self) -> ExecutionType;
}
/// This function allows executing one task which implements the [Executable] trait /// This function allows executing one task which implements the [Executable] trait
/// ///
/// # Arguments /// # Arguments
@ -43,10 +39,7 @@ pub trait ExecutableWithType: Executable {
/// * `op_code`: Operation code which is passed to the executable task /// * `op_code`: Operation code which is passed to the executable task
/// [operation call][Executable::periodic_op] /// [operation call][Executable::periodic_op]
/// * `termination`: Optional termination handler which can cancel threads with a broadcast /// * `termination`: Optional termination handler which can cancel threads with a broadcast
pub fn exec_sched_single< pub fn exec_sched_single<T: Executable<Error = E> + Send + 'static + ?Sized, E: Send + 'static>(
T: ExecutableWithType<Error = E> + Send + 'static + ?Sized,
E: Send + 'static,
>(
mut executable: Box<T>, mut executable: Box<T>,
task_freq: Option<Duration>, task_freq: Option<Duration>,
op_code: i32, op_code: i32,
@ -95,10 +88,7 @@ pub fn exec_sched_single<
/// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks /// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks
/// * `op_code`: Operation code which is passed to the executable task [operation call][Executable::periodic_op] /// * `op_code`: Operation code which is passed to the executable task [operation call][Executable::periodic_op]
/// * `termination`: Optional termination handler which can cancel threads with a broadcast /// * `termination`: Optional termination handler which can cancel threads with a broadcast
pub fn exec_sched_multi< pub fn exec_sched_multi<T: Executable<Error = E> + Send + 'static + ?Sized, E: Send + 'static>(
T: ExecutableWithType<Error = E> + Send + 'static + ?Sized,
E: Send + 'static,
>(
task_name: &'static str, task_name: &'static str,
mut executable_vec: Vec<Box<T>>, mut executable_vec: Vec<Box<T>>,
task_freq: Option<Duration>, task_freq: Option<Duration>,
@ -152,10 +142,7 @@ pub fn exec_sched_multi<
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult};
exec_sched_multi, exec_sched_single, Executable, ExecutableWithType, ExecutionType,
OpResult,
};
use bus::Bus; use bus::Bus;
use std::boxed::Box; use std::boxed::Box;
use std::error::Error; use std::error::Error;
@ -221,6 +208,10 @@ mod tests {
impl Executable for OneShotTask { impl Executable for OneShotTask {
type Error = ExampleError; type Error = ExampleError;
fn exec_type(&self) -> ExecutionType {
ExecutionType::OneShot
}
fn task_name(&self) -> &'static str { fn task_name(&self) -> &'static str {
ONE_SHOT_TASK_NAME ONE_SHOT_TASK_NAME
} }
@ -238,17 +229,15 @@ mod tests {
} }
} }
impl ExecutableWithType for OneShotTask {
fn exec_type(&self) -> ExecutionType {
ExecutionType::OneShot
}
}
const CYCLE_TASK_NAME: &str = "Fixed Cycles Task"; const CYCLE_TASK_NAME: &str = "Fixed Cycles Task";
impl Executable for FixedCyclesTask { impl Executable for FixedCyclesTask {
type Error = ExampleError; type Error = ExampleError;
fn exec_type(&self) -> ExecutionType {
ExecutionType::Cycles(self.cycles)
}
fn task_name(&self) -> &'static str { fn task_name(&self) -> &'static str {
CYCLE_TASK_NAME CYCLE_TASK_NAME
} }
@ -266,17 +255,15 @@ mod tests {
} }
} }
impl ExecutableWithType for FixedCyclesTask {
fn exec_type(&self) -> ExecutionType {
ExecutionType::Cycles(self.cycles)
}
}
const PERIODIC_TASK_NAME: &str = "Periodic Task"; const PERIODIC_TASK_NAME: &str = "Periodic Task";
impl Executable for PeriodicTask { impl Executable for PeriodicTask {
type Error = ExampleError; type Error = ExampleError;
fn exec_type(&self) -> ExecutionType {
ExecutionType::Infinite
}
fn task_name(&self) -> &'static str { fn task_name(&self) -> &'static str {
PERIODIC_TASK_NAME PERIODIC_TASK_NAME
} }
@ -294,12 +281,6 @@ mod tests {
} }
} }
impl ExecutableWithType for PeriodicTask {
fn exec_type(&self) -> ExecutionType {
ExecutionType::Infinite
}
}
#[test] #[test]
fn test_simple_one_shot() { fn test_simple_one_shot() {
let expected_op_code = 42; let expected_op_code = 42;
@ -442,7 +423,7 @@ mod tests {
}); });
assert_eq!(cycled_task_0.task_name(), CYCLE_TASK_NAME); assert_eq!(cycled_task_0.task_name(), CYCLE_TASK_NAME);
assert_eq!(one_shot_task.task_name(), ONE_SHOT_TASK_NAME); assert_eq!(one_shot_task.task_name(), ONE_SHOT_TASK_NAME);
let task_vec: Vec<Box<dyn ExecutableWithType<Error = ExampleError> + Send>> = let task_vec: Vec<Box<dyn Executable<Error = ExampleError>>> =
vec![one_shot_task, cycled_task_0, cycled_task_1]; vec![one_shot_task, cycled_task_0, cycled_task_1];
let jh = exec_sched_multi( let jh = exec_sched_multi(
"multi-task-name", "multi-task-name",
@ -512,7 +493,7 @@ mod tests {
}); });
assert_eq!(periodic_task_0.task_name(), PERIODIC_TASK_NAME); assert_eq!(periodic_task_0.task_name(), PERIODIC_TASK_NAME);
assert_eq!(periodic_task_1.task_name(), PERIODIC_TASK_NAME); assert_eq!(periodic_task_1.task_name(), PERIODIC_TASK_NAME);
let task_vec: Vec<Box<dyn ExecutableWithType<Error = ExampleError> + Send>> = let task_vec: Vec<Box<dyn Executable<Error = ExampleError>>> =
vec![cycled_task, periodic_task_0, periodic_task_1]; vec![cycled_task, periodic_task_0, periodic_task_1];
let jh = exec_sched_multi( let jh = exec_sched_multi(
"multi-task-name", "multi-task-name",

View File

@ -1,39 +0,0 @@
use crate::ComponentId;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HealthState {
Healthy = 1,
Faulty = 2,
PermanentFaulty = 3,
ExternalControl = 4,
NeedsRecovery = 5,
}
pub trait HealthTableProvider {
fn health(&self, id: ComponentId) -> Option<HealthState>;
fn set_health(&mut self, id: ComponentId, health: HealthState);
}
#[cfg(feature = "std")]
#[derive(Debug, Clone)]
pub struct HealthTableMapSync(
std::sync::Arc<std::sync::Mutex<hashbrown::HashMap<ComponentId, HealthState>>>,
);
#[cfg(feature = "std")]
impl HealthTableMapSync {
pub fn new(health_table: hashbrown::HashMap<ComponentId, HealthState>) -> Self {
Self(std::sync::Arc::new(std::sync::Mutex::new(health_table)))
}
}
#[cfg(feature = "std")]
impl HealthTableProvider for HealthTableMapSync {
fn health(&self, id: ComponentId) -> Option<HealthState> {
self.0.lock().unwrap().get(&id).copied()
}
fn set_health(&mut self, id: ComponentId, health: HealthState) {
self.0.lock().unwrap().insert(id, health);
}
}

View File

@ -1,4 +1,4 @@
//! # sat-rs: A library to build on-board software for remote systems //! # sat-rs: A framework to build on-board software for remote systems
//! //!
//! You can find more information about the sat-rs framework on the //! You can find more information about the sat-rs framework on the
//! [homepage](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/). //! [homepage](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/).
@ -14,7 +14,7 @@
//! - The [pus] module which provides special support for projects using //! - The [pus] module which provides special support for projects using
//! the [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/). //! the [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
#![no_std] #![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docs_rs, feature(doc_auto_cfg))]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
extern crate alloc; extern crate alloc;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
@ -22,34 +22,31 @@ extern crate downcast_rs;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
extern crate std; extern crate std;
pub mod action;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod dev_mgmt; pub mod cfdp;
pub mod encoding; pub mod encoding;
pub mod event_man; pub mod event_man;
pub mod events; pub mod events;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub mod executable; pub mod executable;
pub mod hal; pub mod hal;
pub mod health;
pub mod hk;
pub mod mode;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub mod mode_tree; pub mod mode_tree;
pub mod params;
pub mod pool; pub mod pool;
pub mod power; pub mod power;
pub mod pus; pub mod pus;
pub mod queue; pub mod queue;
pub mod request; pub mod request;
pub mod res_code; pub mod res_code;
#[cfg(feature = "alloc")] pub mod seq_count;
pub mod scheduling;
#[cfg(feature = "alloc")]
pub mod subsystem;
pub mod time; pub mod time;
pub mod tmtc; pub mod tmtc;
pub mod action;
pub mod hk;
pub mod mode;
pub mod params;
pub use spacepackets; pub use spacepackets;
use spacepackets::PacketId; use spacepackets::PacketId;

View File

@ -11,10 +11,8 @@ pub use alloc_mod::*;
pub use std_mod::*; pub use std_mod::*;
use crate::{ use crate::{
queue::{GenericReceiveError, GenericSendError}, queue::GenericTargetedMessagingError,
request::{ request::{GenericMessage, MessageMetadata, MessageReceiver, MessageReceiverWithId, RequestId},
GenericMessage, MessageMetadata, MessageReceiverProvider, MessageReceiverWithId, RequestId,
},
ComponentId, ComponentId,
}; };
@ -28,11 +26,6 @@ pub struct ModeAndSubmode {
submode: Submode, submode: Submode,
} }
pub const INVALID_MODE_VAL: Mode = Mode::MAX;
pub const UNKNOWN_MODE_VAL: Mode = Mode::MAX - 1;
pub const INVALID_MODE: ModeAndSubmode = ModeAndSubmode::new(INVALID_MODE_VAL, 0);
pub const UNKNOWN_MODE: ModeAndSubmode = ModeAndSubmode::new(UNKNOWN_MODE_VAL, 0);
impl ModeAndSubmode { impl ModeAndSubmode {
pub const RAW_LEN: usize = size_of::<Mode>() + size_of::<Submode>(); pub const RAW_LEN: usize = size_of::<Mode>() + size_of::<Submode>();
@ -118,10 +111,7 @@ impl TargetedModeCommand {
pub enum ModeRequest { pub enum ModeRequest {
/// Mode information. Can be used to notify other components of changed modes. /// Mode information. Can be used to notify other components of changed modes.
ModeInfo(ModeAndSubmode), ModeInfo(ModeAndSubmode),
SetMode { SetMode(ModeAndSubmode),
mode_and_submode: ModeAndSubmode,
forced: bool,
},
ReadMode, ReadMode,
AnnounceMode, AnnounceMode,
AnnounceModeRecursive, AnnounceModeRecursive,
@ -137,8 +127,6 @@ pub struct TargetedModeRequest {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModeReply { pub enum ModeReply {
/// Mode information. Can be used to notify other components of changed modes.
ModeInfo(ModeAndSubmode),
/// Reply to a mode request to confirm the commanded mode was reached. /// Reply to a mode request to confirm the commanded mode was reached.
ModeReply(ModeAndSubmode), ModeReply(ModeAndSubmode),
// Can not reach the commanded mode. Contains a reason as a [ResultU16]. // Can not reach the commanded mode. Contains a reason as a [ResultU16].
@ -159,33 +147,34 @@ pub trait ModeRequestSender {
request_id: RequestId, request_id: RequestId,
target_id: ComponentId, target_id: ComponentId,
request: ModeRequest, request: ModeRequest,
) -> Result<(), GenericSendError>; ) -> Result<(), GenericTargetedMessagingError>;
} }
pub trait ModeRequestReceiver { pub trait ModeRequestReceiver {
fn try_recv_mode_request( fn try_recv_mode_request(
&self, &self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError>; ) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError>;
} }
impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver impl<R: MessageReceiver<ModeRequest>> ModeRequestReceiver
for MessageReceiverWithId<ModeRequest, R> for MessageReceiverWithId<ModeRequest, R>
{ {
fn try_recv_mode_request( fn try_recv_mode_request(
&self, &self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.try_recv_message() self.try_recv_message()
} }
} }
#[derive(Debug, Clone, thiserror::Error)] #[derive(Debug, Clone)]
pub enum ModeError { pub enum ModeError {
#[error("Messaging send error: {0}")] Messaging(GenericTargetedMessagingError),
Send(#[from] GenericSendError), }
#[error("Messaging receive error: {0}")]
Receive(#[from] GenericReceiveError), impl From<GenericTargetedMessagingError> for ModeError {
#[error("busy with other mode request")] fn from(value: GenericTargetedMessagingError) -> Self {
Busy, Self::Messaging(value)
}
} }
pub trait ModeProvider { pub trait ModeProvider {
@ -207,7 +196,6 @@ pub trait ModeRequestHandler: ModeProvider {
&mut self, &mut self,
requestor: MessageMetadata, requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool,
) -> Result<(), Self::Error>; ) -> Result<(), Self::Error>;
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool); fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool);
@ -234,10 +222,9 @@ pub trait ModeRequestHandler: ModeProvider {
request: GenericMessage<ModeRequest>, request: GenericMessage<ModeRequest>,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
match request.message { match request.message {
ModeRequest::SetMode { ModeRequest::SetMode(mode_and_submode) => {
mode_and_submode, self.start_transition(request.requestor_info, mode_and_submode)
forced, }
} => self.start_transition(request.requestor_info, mode_and_submode, forced),
ModeRequest::ReadMode => self.send_mode_reply( ModeRequest::ReadMode => self.send_mode_reply(
request.requestor_info, request.requestor_info,
ModeReply::ModeReply(self.mode_and_submode()), ModeReply::ModeReply(self.mode_and_submode()),
@ -256,16 +243,15 @@ pub trait ModeRequestHandler: ModeProvider {
} }
pub trait ModeReplyReceiver { pub trait ModeReplyReceiver {
fn try_recv_mode_reply(&self)
-> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError>;
}
impl<R: MessageReceiverProvider<ModeReply>> ModeReplyReceiver
for MessageReceiverWithId<ModeReply, R>
{
fn try_recv_mode_reply( fn try_recv_mode_reply(
&self, &self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError>;
}
impl<R: MessageReceiver<ModeReply>> ModeReplyReceiver for MessageReceiverWithId<ModeReply, R> {
fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.try_recv_message() self.try_recv_message()
} }
} }
@ -278,28 +264,24 @@ pub trait ModeReplySender {
&self, &self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
reply: ModeReply, reply: ModeReply,
) -> Result<(), GenericSendError>; ) -> Result<(), GenericTargetedMessagingError>;
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod alloc_mod { pub mod alloc_mod {
use crate::{ use crate::request::{
queue::{GenericReceiveError, GenericSendError}, MessageSender, MessageSenderAndReceiver, MessageSenderMap, RequestAndReplySenderAndReceiver,
request::{
MessageSenderAndReceiver, MessageSenderMap, MessageSenderProvider,
MessageSenderStoreProvider, RequestAndReplySenderAndReceiver,
},
}; };
use super::*; use super::*;
impl<S: MessageSenderProvider<ModeReply>> MessageSenderMap<ModeReply, S> { impl<S: MessageSender<ModeReply>> MessageSenderMap<ModeReply, S> {
pub fn send_mode_reply( pub fn send_mode_reply(
&self, &self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
target_id: ComponentId, target_id: ComponentId,
request: ModeReply, request: ModeReply,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.send_message(requestor_info, target_id, request) self.send_message(requestor_info, target_id, request)
} }
@ -308,13 +290,8 @@ pub mod alloc_mod {
} }
} }
impl< impl<FROM, S: MessageSender<ModeReply>, R: MessageReceiver<FROM>> ModeReplySender
From, for MessageSenderAndReceiver<ModeReply, FROM, S, R>
Sender: MessageSenderProvider<ModeReply>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
> ModeReplySender
for MessageSenderAndReceiver<ModeReply, From, Sender, Receiver, SenderStore>
{ {
fn local_channel_id(&self) -> ComponentId { fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic() self.local_channel_id_generic()
@ -324,8 +301,8 @@ pub mod alloc_mod {
&self, &self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
request: ModeReply, request: ModeReply,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_store.send_message( self.message_sender_map.send_mode_reply(
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()), MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
requestor_info.sender_id(), requestor_info.sender_id(),
request, request,
@ -333,67 +310,37 @@ pub mod alloc_mod {
} }
} }
impl< impl<TO, S: MessageSender<TO>, R: MessageReceiver<ModeReply>> ModeReplyReceiver
To, for MessageSenderAndReceiver<TO, ModeReply, S, R>
Sender: MessageSenderProvider<To>,
Receiver: MessageReceiverProvider<ModeReply>,
SenderStore: MessageSenderStoreProvider<To, Sender>,
> ModeReplyReceiver
for MessageSenderAndReceiver<To, ModeReply, Sender, Receiver, SenderStore>
{ {
fn try_recv_mode_reply( fn try_recv_mode_reply(
&self, &self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.message_receiver.try_recv_message() self.message_receiver.try_recv_message()
} }
} }
impl< impl<
Request, REQUEST,
ReqSender: MessageSenderProvider<Request>, S0: MessageSender<REQUEST>,
ReqReceiver: MessageReceiverProvider<Request>, R0: MessageReceiver<ModeReply>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>, S1: MessageSender<ModeReply>,
Reply, R1: MessageReceiver<REQUEST>,
ReplySender: MessageSenderProvider<Reply>, > RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
>
RequestAndReplySenderAndReceiver<
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{ {
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: ReplySender) { pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: S1) {
self.reply_sender_store self.reply_sender_map
.add_message_target(target_id, reply_sender) .add_message_target(target_id, reply_sender)
} }
} }
impl< impl<
Request, REQUEST,
ReqSender: MessageSenderProvider<Request>, S0: MessageSender<REQUEST>,
ReqReceiver: MessageReceiverProvider<Request>, R0: MessageReceiver<ModeReply>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>, S1: MessageSender<ModeReply>,
ReplySender: MessageSenderProvider<ModeReply>, R1: MessageReceiver<REQUEST>,
ReplyReceiver: MessageReceiverProvider<ModeReply>, > ModeReplySender for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
> ModeReplySender
for RequestAndReplySenderAndReceiver<
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
ModeReply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{ {
fn local_channel_id(&self) -> ComponentId { fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic() self.local_channel_id_generic()
@ -402,56 +349,42 @@ pub mod alloc_mod {
fn send_mode_reply( fn send_mode_reply(
&self, &self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
reply: ModeReply, request: ModeReply,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.reply_sender_store.send_message( self.reply_sender_map.send_mode_reply(
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()), MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
requestor_info.sender_id(), requestor_info.sender_id(),
reply, request,
) )
} }
} }
impl< impl<
Request, REQUEST,
ReqSender: MessageSenderProvider<Request>, S0: MessageSender<REQUEST>,
ReqReceiver: MessageReceiverProvider<Request>, R0: MessageReceiver<ModeReply>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>, S1: MessageSender<ModeReply>,
ReplySender: MessageSenderProvider<ModeReply>, R1: MessageReceiver<REQUEST>,
ReplyReceiver: MessageReceiverProvider<ModeReply>,
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
> ModeReplyReceiver > ModeReplyReceiver
for RequestAndReplySenderAndReceiver< for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
ModeReply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{ {
fn try_recv_mode_reply( fn try_recv_mode_reply(
&self, &self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.reply_receiver.try_recv_message() self.reply_receiver.try_recv_message()
} }
} }
/// Helper type definition for a mode handler which can handle mode requests. /// Helper type definition for a mode handler which can handle mode requests.
pub type ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore> = pub type ModeRequestHandlerInterface<S, R> =
MessageSenderAndReceiver<ModeReply, ModeRequest, Sender, Receiver, ReplySenderStore>; MessageSenderAndReceiver<ModeReply, ModeRequest, S, R>;
impl< impl<S: MessageSender<ModeReply>, R: MessageReceiver<ModeRequest>>
Sender: MessageSenderProvider<ModeReply>, ModeRequestHandlerInterface<S, R>
Receiver: MessageReceiverProvider<ModeRequest>,
ReplySenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
> ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore>
{ {
pub fn try_recv_mode_request( pub fn try_recv_mode_request(
&self, &self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.try_recv_message() self.try_recv_message()
} }
@ -459,7 +392,7 @@ pub mod alloc_mod {
&self, &self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
reply: ModeReply, reply: ModeReply,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.send_message( self.send_message(
requestor_info.request_id(), requestor_info.request_id(),
requestor_info.sender_id(), requestor_info.sender_id(),
@ -470,18 +403,12 @@ pub mod alloc_mod {
/// Helper type defintion for a mode handler object which can send mode requests and receive /// Helper type defintion for a mode handler object which can send mode requests and receive
/// mode replies. /// mode replies.
pub type ModeRequestorInterface<Sender, Receiver, RequestSenderStore> = pub type ModeRequestorInterface<S, R> = MessageSenderAndReceiver<ModeRequest, ModeReply, S, R>;
MessageSenderAndReceiver<ModeRequest, ModeReply, Sender, Receiver, RequestSenderStore>;
impl< impl<S: MessageSender<ModeRequest>, R: MessageReceiver<ModeReply>> ModeRequestorInterface<S, R> {
Sender: MessageSenderProvider<ModeRequest>,
Receiver: MessageReceiverProvider<ModeReply>,
RequestSenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
> ModeRequestorInterface<Sender, Receiver, RequestSenderStore>
{
pub fn try_recv_mode_reply( pub fn try_recv_mode_reply(
&self, &self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.try_recv_message() self.try_recv_message()
} }
@ -490,38 +417,23 @@ pub mod alloc_mod {
request_id: RequestId, request_id: RequestId,
target_id: ComponentId, target_id: ComponentId,
reply: ModeRequest, reply: ModeRequest,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, reply) self.send_message(request_id, target_id, reply)
} }
} }
/// Helper type defintion for a mode handler object which can both send mode requests and /// Helper type defintion for a mode handler object which can both send mode requests and
/// process mode requests. /// process mode requests.
pub type ModeInterface< pub type ModeInterface<S0, R0, S1, R1> =
ReqSender, RequestAndReplySenderAndReceiver<ModeRequest, ModeReply, S0, R0, S1, R1>;
ReqReceiver,
ReqSenderStore,
ReplySender,
ReplyReceiver,
ReplySenderStore,
> = RequestAndReplySenderAndReceiver<
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
ModeReply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>;
impl<S: MessageSenderProvider<ModeRequest>> MessageSenderMap<ModeRequest, S> { impl<S: MessageSender<ModeRequest>> MessageSenderMap<ModeRequest, S> {
pub fn send_mode_request( pub fn send_mode_request(
&self, &self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
target_id: ComponentId, target_id: ComponentId,
request: ModeRequest, request: ModeRequest,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.send_message(requestor_info, target_id, request) self.send_message(requestor_info, target_id, request)
} }
@ -530,28 +442,35 @@ pub mod alloc_mod {
} }
} }
impl< /*
To, impl<S: MessageSender<ModeRequest>> ModeRequestSender for MessageSenderMapWithId<ModeRequest, S> {
Sender: MessageSenderProvider<To>, fn local_channel_id(&self) -> ComponentId {
Receiver: MessageReceiverProvider<ModeRequest>, self.local_channel_id
SenderStore: MessageSenderStoreProvider<To, Sender>, }
> ModeRequestReceiver
for MessageSenderAndReceiver<To, ModeRequest, Sender, Receiver, SenderStore> fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, request)
}
}
*/
impl<TO, S: MessageSender<TO>, R: MessageReceiver<ModeRequest>> ModeRequestReceiver
for MessageSenderAndReceiver<TO, ModeRequest, S, R>
{ {
fn try_recv_mode_request( fn try_recv_mode_request(
&self, &self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.message_receiver.try_recv_message() self.message_receiver.try_recv_message()
} }
} }
impl< impl<FROM, S: MessageSender<ModeRequest>, R: MessageReceiver<FROM>> ModeRequestSender
From, for MessageSenderAndReceiver<ModeRequest, FROM, S, R>
Sender: MessageSenderProvider<ModeRequest>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
> ModeRequestSender
for MessageSenderAndReceiver<ModeRequest, From, Sender, Receiver, SenderStore>
{ {
fn local_channel_id(&self) -> ComponentId { fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic() self.local_channel_id_generic()
@ -562,8 +481,8 @@ pub mod alloc_mod {
request_id: RequestId, request_id: RequestId,
target_id: ComponentId, target_id: ComponentId,
request: ModeRequest, request: ModeRequest,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_store.send_message( self.message_sender_map.send_mode_request(
MessageMetadata::new(request_id, self.local_channel_id()), MessageMetadata::new(request_id, self.local_channel_id()),
target_id, target_id,
request, request,
@ -572,50 +491,27 @@ pub mod alloc_mod {
} }
impl< impl<
ReqSender: MessageSenderProvider<ModeRequest>, REPLY,
ReqReceiver: MessageReceiverProvider<ModeRequest>, S0: MessageSender<ModeRequest>,
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>, R0: MessageReceiver<REPLY>,
Reply, S1: MessageSender<REPLY>,
ReplySender: MessageSenderProvider<Reply>, R1: MessageReceiver<ModeRequest>,
ReplyReceiver: MessageReceiverProvider<Reply>, > RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
>
RequestAndReplySenderAndReceiver<
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{ {
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: ReqSender) { pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S0) {
self.request_sender_store self.request_sender_map
.add_message_target(target_id, request_sender) .add_message_target(target_id, request_sender)
} }
} }
impl< impl<
ReqSender: MessageSenderProvider<ModeRequest>, REPLY,
ReqReceiver: MessageReceiverProvider<ModeRequest>, S0: MessageSender<ModeRequest>,
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>, R0: MessageReceiver<REPLY>,
Reply, S1: MessageSender<REPLY>,
ReplySender: MessageSenderProvider<Reply>, R1: MessageReceiver<ModeRequest>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
> ModeRequestSender > ModeRequestSender
for RequestAndReplySenderAndReceiver< for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{ {
fn local_channel_id(&self) -> ComponentId { fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic() self.local_channel_id_generic()
@ -626,8 +522,8 @@ pub mod alloc_mod {
request_id: RequestId, request_id: RequestId,
target_id: ComponentId, target_id: ComponentId,
request: ModeRequest, request: ModeRequest,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.request_sender_store.send_message( self.request_sender_map.send_mode_request(
MessageMetadata::new(request_id, self.local_channel_id()), MessageMetadata::new(request_id, self.local_channel_id()),
target_id, target_id,
request, request,
@ -636,28 +532,17 @@ pub mod alloc_mod {
} }
impl< impl<
ReqSender: MessageSenderProvider<ModeRequest>, REPLY,
ReqReceiver: MessageReceiverProvider<ModeRequest>, S0: MessageSender<ModeRequest>,
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>, R0: MessageReceiver<REPLY>,
Reply, S1: MessageSender<REPLY>,
ReplySender: MessageSenderProvider<Reply>, R1: MessageReceiver<ModeRequest>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
> ModeRequestReceiver > ModeRequestReceiver
for RequestAndReplySenderAndReceiver< for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
ModeRequest,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{ {
fn try_recv_mode_request( fn try_recv_mode_request(
&self, &self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.request_receiver.try_recv_message() self.request_receiver.try_recv_message()
} }
} }
@ -667,97 +552,39 @@ pub mod alloc_mod {
pub mod std_mod { pub mod std_mod {
use std::sync::mpsc; use std::sync::mpsc;
use crate::request::{MessageSenderList, OneMessageSender};
use super::*; use super::*;
pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface< pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface<
mpsc::Sender<GenericMessage<ModeReply>>, mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>, mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
>; >;
pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface< pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface<
mpsc::SyncSender<GenericMessage<ModeReply>>, mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>, mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
>; >;
pub type ModeRequestorOneChildMpsc = ModeRequestorInterface< pub type ModeRequestorMpsc = ModeRequestorInterface<
mpsc::Sender<GenericMessage<ModeRequest>>, mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>, mpsc::Receiver<GenericMessage<ModeReply>>,
OneMessageSender<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
>; >;
pub type ModeRequestorOneChildBoundedMpsc = ModeRequestorInterface< pub type ModeRequestorBoundedMpsc = ModeRequestorInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>, mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>, mpsc::Receiver<GenericMessage<ModeReply>>,
OneMessageSender<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
>;
pub type ModeRequestorChildListMpsc = ModeRequestorInterface<
mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
>;
pub type ModeRequestorChildListBoundedMpsc = ModeRequestorInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
>; >;
pub type ModeRequestorAndHandlerMpsc = ModeInterface< pub type ModeRequestorAndHandlerMpsc = ModeInterface<
mpsc::Sender<GenericMessage<ModeRequest>>, mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeReply>>, mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>, mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>; >;
pub type ModeRequestorAndHandlerMpscBounded = ModeInterface< pub type ModeRequestorAndHandlerMpscBounded = ModeInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>, mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeReply>>, mpsc::Receiver<GenericMessage<ModeReply>>,
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>, mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>; >;
} }
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { mod tests {}
use core::cell::RefCell;
use std::collections::VecDeque;
use crate::{request::RequestId, ComponentId};
use super::*;
pub struct ModeReqWrapper {
pub request_id: RequestId,
pub target_id: ComponentId,
pub request: ModeRequest,
}
#[derive(Default)]
pub struct ModeReqSenderMock {
pub requests: RefCell<VecDeque<ModeReqWrapper>>,
}
impl ModeRequestSender for ModeReqSenderMock {
fn local_channel_id(&self) -> crate::ComponentId {
0
}
fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericSendError> {
self.requests.borrow_mut().push_back(ModeReqWrapper {
request_id,
target_id,
request,
});
Ok(())
}
}
}

View File

@ -2,57 +2,10 @@ use alloc::vec::Vec;
use hashbrown::HashMap; use hashbrown::HashMap;
use crate::{ use crate::{
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, Submode}, mode::{Mode, ModeAndSubmode, Submode},
request::MessageSenderProvider,
ComponentId, ComponentId,
}; };
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
/// Common trait for node modes which can have mode parents or mode children.
pub trait ModeNode {
fn id(&self) -> ComponentId;
}
/// Trait which denotes that an object is a parent in a mode tree.
///
/// A mode parent is capable of sending mode requests to child objects and has a unique component
/// ID.
pub trait ModeParent: ModeNode {
type Sender: MessageSenderProvider<ModeRequest>;
fn add_mode_child(&mut self, id: ComponentId, request_sender: Self::Sender);
}
/// Trait which denotes that an object is a child in a mode tree.
///
/// A child is capable of sending mode replies to parent objects and has a unique component ID.
pub trait ModeChild: ModeNode {
type Sender: MessageSenderProvider<ModeReply>;
fn add_mode_parent(&mut self, id: ComponentId, reply_sender: Self::Sender);
}
/// Utility method which connects a mode tree parent object to a child object by calling
/// [ModeParent::add_mode_child] on the [parent][ModeParent] and calling
/// [ModeChild::add_mode_parent] on the [child][ModeChild].
///
/// # Arguments
///
/// * `parent` - The parent object which implements [ModeParent].
/// * `request_sender` - Sender object to send mode requests to the child.
/// * `child` - The child object which implements [ModeChild].
/// * `reply_sender` - Sender object to send mode replies to the parent.
pub fn connect_mode_nodes<ReqSender, ReplySender>(
parent: &mut impl ModeParent<Sender = ReqSender>,
request_sender: ReqSender,
child: &mut impl ModeChild<Sender = ReplySender>,
reply_sender: ReplySender,
) {
parent.add_mode_child(child.id(), request_sender);
child.add_mode_parent(parent.id(), reply_sender);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TableEntryType { pub enum TableEntryType {
/// Target table containing information of the expected children modes for given mode. /// Target table containing information of the expected children modes for given mode.
@ -62,553 +15,23 @@ pub enum TableEntryType {
Sequence, Sequence,
} }
/// Common fields required for both target and sequence table entries. pub struct ModeTableEntry {
///
/// The most important parameters here are the target ID which this entry belongs to, and the mode
/// and submode the entry either will be commanded to for sequence table entries or which will be
/// monitored for target table entries.
#[derive(Debug, Copy, Clone)]
pub struct ModeTableEntryCommon {
/// Name of respective table entry. /// Name of respective table entry.
pub name: &'static str, pub name: &'static str,
/// Target component ID. /// Target channel ID.
pub target_id: ComponentId, pub channel_id: ComponentId,
/// Has a different meaning depending on whether this is a sequence table or a target table.
///
/// - For sequence tables, this denotes the mode which will be commanded
/// - For target tables, this is the mode which the target children should have and which
/// might be monitored depending on configuration.
pub mode_submode: ModeAndSubmode, pub mode_submode: ModeAndSubmode,
/// This mask allows to specify multiple allowed submodes for a given mode.
pub allowed_submode_mask: Option<Submode>, pub allowed_submode_mask: Option<Submode>,
}
impl ModeTableEntryCommon {
pub fn set_allowed_submode_mask(&mut self, mask: Submode) {
self.allowed_submode_mask = Some(mask);
}
pub fn allowed_submode_mask(&self) -> Option<Submode> {
self.allowed_submode_mask
}
}
/// An entry for the target tables.
#[derive(Debug)]
pub struct TargetTableEntry {
pub common: ModeTableEntryCommon,
pub monitor_state: bool,
}
impl TargetTableEntry {
pub fn new(
name: &'static str,
target_id: ComponentId,
mode_submode: ModeAndSubmode,
allowed_submode_mask: Option<Submode>,
) -> Self {
Self {
common: ModeTableEntryCommon {
name,
target_id,
mode_submode,
allowed_submode_mask,
},
monitor_state: true,
}
}
pub fn new_with_precise_submode(
name: &'static str,
target_id: ComponentId,
mode_submode: ModeAndSubmode,
) -> Self {
Self {
common: ModeTableEntryCommon {
name,
target_id,
mode_submode,
allowed_submode_mask: None,
},
monitor_state: true,
}
}
delegate::delegate! {
to self.common {
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
pub fn allowed_submode_mask(&self) -> Option<Submode>;
}
}
}
/// An entry for the sequence tables.
///
/// The [Self::check_success] field specifies that a mode sequence executor should check that the
/// target mode was actually reached before executing the next sequence.
#[derive(Debug)]
pub struct SequenceTableEntry {
pub common: ModeTableEntryCommon,
pub check_success: bool, pub check_success: bool,
} }
impl SequenceTableEntry { pub struct ModeTableMapValue {
pub fn new( /// Name for a given mode table entry.
name: &'static str, pub name: &'static str,
target_id: ComponentId, pub entries: Vec<ModeTableEntry>,
mode_submode: ModeAndSubmode,
check_success: bool,
) -> Self {
Self {
common: ModeTableEntryCommon {
name,
target_id,
mode_submode,
allowed_submode_mask: None,
},
check_success,
}
}
delegate::delegate! {
to self.common {
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
pub fn allowed_submode_mask(&self) -> Option<Submode>;
}
}
} }
#[derive(Debug, thiserror::Error)] pub type ModeTable = HashMap<Mode, ModeTableMapValue>;
#[error("target {0} not in mode store")]
pub struct TargetNotInModeStoreError(pub ComponentId);
/// Mode store value type.
#[derive(Debug, Copy, Clone)]
pub struct ModeStoreValue {
/// ID of the mode component.
id: ComponentId,
/// Current mode and submode of the component.
pub mode_and_submode: ModeAndSubmode,
/// State information to track whether a reply should be awaited for the mode component.
pub awaiting_reply: bool,
}
impl ModeStoreValue {
pub fn new(id: ComponentId, mode_and_submode: ModeAndSubmode) -> Self {
Self {
id,
mode_and_submode,
awaiting_reply: false,
}
}
pub fn id(&self) -> ComponentId {
self.id
}
pub fn mode_and_submode(&self) -> ModeAndSubmode {
self.mode_and_submode
}
}
pub trait ModeStoreProvider {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode);
fn has_component(&self, target_id: ComponentId) -> bool;
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue>;
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue>;
/// Generic handler for mode replies received from child components.
///
/// Implementation should clear the awaition flag if the `handle_reply_awaition` argument is
/// true and returns whether any children are still awaiting replies. If the flag is not set
fn mode_reply_handler_with_reply_awaition(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
) -> bool {
self.mode_reply_handler(sender_id, reported_mode_and_submode, true)
.unwrap_or(false)
}
fn mode_reply_handler_without_reply_awaition(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
) {
self.mode_reply_handler(sender_id, reported_mode_and_submode, false);
}
fn mode_reply_handler(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
with_reply_awaition: bool,
) -> Option<bool>;
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use super::*;
#[derive(Debug)]
pub struct TargetTablesMapValue {
/// Name for a given mode table entry.
pub name: &'static str,
/// Optional fallback mode if the target mode can not be kept.
pub fallback_mode: Option<Mode>,
/// These are the rows of the a target table.
pub entries: Vec<TargetTableEntry>,
}
impl TargetTablesMapValue {
pub fn new(name: &'static str, fallback_mode: Option<Mode>) -> Self {
Self {
name,
fallback_mode,
entries: Default::default(),
}
}
pub fn add_entry(&mut self, entry: TargetTableEntry) {
self.entries.push(entry);
}
}
/// One sequence of a [SequenceTablesMapValue] in a [SequenceModeTables].
///
/// It contains all mode requests which need to be executed for a sequence step and it also
/// associates a [Self::name] with the sequence.
#[derive(Debug)]
pub struct SequenceTableMapTable {
/// Name for a given mode sequence.
pub name: &'static str,
/// These are the rows of the a sequence table.
pub entries: Vec<SequenceTableEntry>,
}
impl SequenceTableMapTable {
pub fn new(name: &'static str) -> Self {
Self {
name,
entries: Default::default(),
}
}
pub fn add_entry(&mut self, entry: SequenceTableEntry) {
self.entries.push(entry);
}
}
/// A sequence table entry.
///
/// This is simply a list of [SequenceTableMapTable]s which also associates a [Self::name]
/// with the sequence. The order of sub-tables in the list also specifies the execution order
/// in the mode sequence.
#[derive(Debug)]
pub struct SequenceTablesMapValue {
/// Name for a given mode sequence.
pub name: &'static str,
/// Each sequence can consists of multiple sequences that are executed consecutively.
pub entries: Vec<SequenceTableMapTable>,
}
impl SequenceTablesMapValue {
pub fn new(name: &'static str) -> Self {
Self {
name,
entries: Default::default(),
}
}
pub fn add_sequence_table(&mut self, entry: SequenceTableMapTable) {
self.entries.push(entry);
}
}
#[derive(Debug, Default)]
pub struct TargetModeTables(pub HashMap<Mode, TargetTablesMapValue>);
impl TargetModeTables {
pub fn name(&self, mode: Mode) -> Option<&'static str> {
self.0.get(&mode).map(|value| value.name)
}
}
impl SequenceModeTables {
pub fn name(&self, mode: Mode) -> Option<&'static str> {
self.0.get(&mode).map(|value| value.name)
}
pub fn name_of_sequence(&self, mode: Mode, seq_idx: usize) -> Option<&'static str> {
self.0
.get(&mode)
.map(|value| value.entries.get(seq_idx).map(|v| v.name))?
}
}
/// This is the core data structure used to store mode sequence tables.
///
/// A mode sequence table specifies which commands have to be sent in which order
/// to reach a certain [Mode]. Therefore, it simply maps a [Mode] to a [SequenceTablesMapValue].
#[derive(Debug, Default)]
pub struct SequenceModeTables(pub HashMap<Mode, SequenceTablesMapValue>);
/// Mode store which tracks the [mode information][ModeStoreValue] inside a [Vec]
#[derive(Debug, Default)]
pub struct ModeStoreVec(pub alloc::vec::Vec<ModeStoreValue>);
impl<'a> IntoIterator for &'a ModeStoreVec {
type Item = &'a ModeStoreValue;
type IntoIter = std::slice::Iter<'a, ModeStoreValue>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a> IntoIterator for &'a mut ModeStoreVec {
type Item = &'a mut ModeStoreValue;
type IntoIter = std::slice::IterMut<'a, ModeStoreValue>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
/// Mode store which tracks the mode information inside a [hashbrown::HashMap]
#[derive(Debug, Default)]
pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeStoreValue>);
impl<'a> IntoIterator for &'a ModeStoreMap {
type Item = (&'a ComponentId, &'a ModeStoreValue);
type IntoIter = hashbrown::hash_map::Iter<'a, ComponentId, ModeStoreValue>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl ModeStoreProvider for ModeStoreVec {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.0.push(ModeStoreValue::new(target_id, mode));
}
fn has_component(&self, target_id: ComponentId) -> bool {
self.0.iter().any(|val| val.id == target_id)
}
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
self.0.iter().find(|val| val.id == target_id)
}
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
self.0.iter_mut().find(|val| val.id == target_id)
}
fn mode_reply_handler(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
handle_reply_awaition: bool,
) -> Option<bool> {
let mut still_awating_replies = None;
if handle_reply_awaition {
still_awating_replies = Some(false);
}
self.0.iter_mut().for_each(|val| {
if val.id() == sender_id {
if let Some(mode_and_submode) = reported_mode_and_submode {
val.mode_and_submode = mode_and_submode;
}
if handle_reply_awaition {
val.awaiting_reply = false;
}
}
if handle_reply_awaition && val.awaiting_reply {
still_awating_replies = Some(true);
}
});
still_awating_replies
}
}
impl ModeStoreProvider for ModeStoreMap {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.0
.insert(target_id, ModeStoreValue::new(target_id, mode));
}
fn has_component(&self, target_id: ComponentId) -> bool {
self.0.contains_key(&target_id)
}
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue> {
self.0.get(&target_id)
}
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue> {
self.0.get_mut(&target_id)
}
fn mode_reply_handler(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
handle_reply_awaition: bool,
) -> Option<bool> {
let mut still_awating_replies = None;
if handle_reply_awaition {
still_awating_replies = Some(false);
}
for val in self.0.values_mut() {
if val.id() == sender_id {
if let Some(mode_and_submode) = reported_mode_and_submode {
val.mode_and_submode = mode_and_submode;
}
if handle_reply_awaition {
val.awaiting_reply = false;
}
}
if handle_reply_awaition && val.awaiting_reply {
still_awating_replies = Some(true);
}
}
still_awating_replies
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {}
use super::*;
fn generic_test(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
assert!(mode_store.has_component(1));
assert!(mode_store.has_component(2));
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(0, 0)
);
assert!(!mode_store.get(1).unwrap().awaiting_reply);
assert!(!mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(mode_store.get(1).unwrap().id, 1);
assert_eq!(mode_store.get(2).unwrap().id, 2);
assert!(mode_store.get(3).is_none());
assert!(mode_store.get_mut(3).is_none());
}
fn generic_reply_handling_with_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
mode_store.get_mut(1).unwrap().awaiting_reply = true;
mode_store.get_mut(2).unwrap().awaiting_reply = true;
let mut reply_awation_pending =
mode_store.mode_reply_handler_with_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
assert!(reply_awation_pending);
reply_awation_pending = mode_store.mode_reply_handler_with_reply_awaition(2, None);
assert!(!reply_awation_pending);
assert!(!mode_store.get(1).unwrap().awaiting_reply);
assert!(!mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(2, 0)
);
assert_eq!(
mode_store.get(2).unwrap().mode_and_submode(),
ModeAndSubmode::new(1, 0)
);
}
fn generic_reply_handling_test_no_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
mode_store.get_mut(1).unwrap().awaiting_reply = true;
mode_store.get_mut(2).unwrap().awaiting_reply = true;
mode_store.mode_reply_handler_without_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
mode_store.mode_reply_handler_without_reply_awaition(2, None);
assert!(mode_store.get(1).unwrap().awaiting_reply);
assert!(mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(2, 0)
);
assert_eq!(
mode_store.get(2).unwrap().mode_and_submode(),
ModeAndSubmode::new(1, 0)
);
}
fn generic_reply_handling_with_reply_awaition_2(mode_store: &mut impl ModeStoreProvider) {
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
mode_store.get_mut(1).unwrap().awaiting_reply = true;
mode_store.get_mut(2).unwrap().awaiting_reply = true;
let mut reply_awation_pending =
mode_store.mode_reply_handler(1, Some(ModeAndSubmode::new(2, 0)), true);
assert!(reply_awation_pending.unwrap());
reply_awation_pending = mode_store.mode_reply_handler(2, None, true);
assert!(!reply_awation_pending.unwrap());
assert!(!mode_store.get(1).unwrap().awaiting_reply);
assert!(!mode_store.get(2).unwrap().awaiting_reply);
assert_eq!(
mode_store.get(1).unwrap().mode_and_submode(),
ModeAndSubmode::new(2, 0)
);
assert_eq!(
mode_store.get(2).unwrap().mode_and_submode(),
ModeAndSubmode::new(1, 0)
);
}
#[test]
fn test_vec_mode_store() {
let mut mode_store = ModeStoreVec::default();
generic_test(&mut mode_store);
}
#[test]
fn test_map_mode_store() {
let mut mode_store = ModeStoreMap::default();
generic_test(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_vec_with_reply_awaition() {
let mut mode_store = ModeStoreVec::default();
generic_reply_handling_with_reply_awaition(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_vec_with_reply_awaition_2() {
let mut mode_store = ModeStoreVec::default();
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_map_with_reply_awaition() {
let mut mode_store = ModeStoreMap::default();
generic_reply_handling_with_reply_awaition(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_map_with_reply_awaition_2() {
let mut mode_store = ModeStoreMap::default();
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_vec_no_reply_awaition() {
let mut mode_store = ModeStoreVec::default();
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
}
#[test]
fn test_generic_reply_handler_map_no_reply_awaition() {
let mut mode_store = ModeStoreMap::default();
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
}
}

View File

@ -387,18 +387,18 @@ pub mod heapless_mod {
#[macro_export] #[macro_export]
macro_rules! static_subpool { macro_rules! static_subpool {
($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr) => { ($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr) => {
static $pool_name: static_cell::ConstStaticCell<[u8; $num_blocks * $block_size]> = static mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
static_cell::ConstStaticCell::new([0; $num_blocks * $block_size]); core::mem::MaybeUninit::new([0; $num_blocks * $block_size]);
static $sizes_list_name: static_cell::ConstStaticCell<[usize; $num_blocks]> = static mut $sizes_list_name: core::mem::MaybeUninit<[usize; $num_blocks]> =
static_cell::ConstStaticCell::new([$crate::pool::STORE_FREE; $num_blocks]); core::mem::MaybeUninit::new([$crate::pool::STORE_FREE; $num_blocks]);
}; };
($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr, $meta_data: meta) => { ($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr, $meta_data: meta) => {
#[$meta_data] #[$meta_data]
static $pool_name: static_cell::ConstStaticCell<[u8; $num_blocks * $block_size]> = static mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
static_cell::ConstStaticCell::new([0; $num_blocks * $block_size]); core::mem::MaybeUninit::new([0; $num_blocks * $block_size]);
#[$meta_data] #[$meta_data]
static $sizes_list_name: static_cell::ConstStaticCell<[usize; $num_blocks]> = static mut $sizes_list_name: core::mem::MaybeUninit<[usize; $num_blocks]> =
static_cell::ConstStaticCell::new([$crate::pool::STORE_FREE; $num_blocks]); core::mem::MaybeUninit::new([$crate::pool::STORE_FREE; $num_blocks]);
}; };
} }
@ -435,17 +435,17 @@ pub mod heapless_mod {
/// ///
/// let mut mem_pool: StaticHeaplessMemoryPool<2> = StaticHeaplessMemoryPool::new(true); /// let mut mem_pool: StaticHeaplessMemoryPool<2> = StaticHeaplessMemoryPool::new(true);
/// mem_pool.grow( /// mem_pool.grow(
/// SUBPOOL_SMALL.take(), /// unsafe { SUBPOOL_SMALL.assume_init_mut() },
/// SUBPOOL_SMALL_SIZES.take(), /// unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
/// SUBPOOL_SMALL_NUM_BLOCKS, /// SUBPOOL_SMALL_NUM_BLOCKS,
/// false /// false
/// ).unwrap(); /// );
/// mem_pool.grow( /// mem_pool.grow(
/// SUBPOOL_LARGE.take(), /// unsafe { SUBPOOL_LARGE.assume_init_mut() },
/// SUBPOOL_LARGE_SIZES.take(), /// unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
/// SUBPOOL_LARGE_NUM_BLOCKS, /// SUBPOOL_LARGE_NUM_BLOCKS,
/// false /// false
/// ).unwrap(); /// );
/// ///
/// let mut read_buf: [u8; 16] = [0; 16]; /// let mut read_buf: [u8; 16] = [0; 16];
/// let mut addr; /// let mut addr;
@ -522,14 +522,12 @@ pub mod heapless_mod {
num_blocks: NumBlocks, num_blocks: NumBlocks,
set_sizes_list_to_all_free: bool, set_sizes_list_to_all_free: bool,
) -> Result<(), PoolIsFull> { ) -> Result<(), PoolIsFull> {
assert_eq!( assert!(
(subpool_memory.len() % num_blocks as usize), (subpool_memory.len() % num_blocks as usize) == 0,
0,
"pool slice length must be multiple of number of blocks" "pool slice length must be multiple of number of blocks"
); );
assert_eq!( assert!(
num_blocks as usize, num_blocks as usize == sizes_list.len(),
sizes_list.len(),
"used block size list slice must be of same length as number of blocks" "used block size list slice must be of same length as number of blocks"
); );
let subpool_config = SubpoolConfig { let subpool_config = SubpoolConfig {
@ -775,7 +773,7 @@ mod alloc_mod {
/// if the next fitting subpool is full. This is useful to ensure the pool remains useful /// if the next fitting subpool is full. This is useful to ensure the pool remains useful
/// for all data sizes as long as possible. However, an undesirable side-effect might be /// for all data sizes as long as possible. However, an undesirable side-effect might be
/// the chocking of larger subpools by underdimensioned smaller subpools. /// the chocking of larger subpools by underdimensioned smaller subpools.
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct StaticPoolConfig { pub struct StaticPoolConfig {
cfg: Vec<SubpoolConfig>, cfg: Vec<SubpoolConfig>,
spill_to_higher_subpools: bool, spill_to_higher_subpools: bool,
@ -834,7 +832,6 @@ mod alloc_mod {
/// [address][PoolAddr] type. Adding any data to the pool will yield a store address. /// [address][PoolAddr] type. Adding any data to the pool will yield a store address.
/// Modification and read operations are done using a reference to a store address. Deletion /// Modification and read operations are done using a reference to a store address. Deletion
/// will consume the store address. /// will consume the store address.
#[derive(Debug)]
pub struct StaticMemoryPool { pub struct StaticMemoryPool {
pool_cfg: StaticPoolConfig, pool_cfg: StaticPoolConfig,
pool: Vec<Vec<u8>>, pool: Vec<Vec<u8>>,
@ -1587,33 +1584,23 @@ mod tests {
mod heapless_tests { mod heapless_tests {
use super::*; use super::*;
use crate::static_subpool; use crate::static_subpool;
use std::cell::UnsafeCell; use core::mem::MaybeUninit;
use std::sync::Mutex;
const SUBPOOL_1_BLOCK_SIZE: usize = 4; const SUBPOOL_1_BLOCK_SIZE: usize = 4;
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4; const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
static mut SUBPOOL_1: MaybeUninit<
static SUBPOOL_1: static_cell::ConstStaticCell<
[u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE], [u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
> = static_cell::ConstStaticCell::new( > = MaybeUninit::new([0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE]);
[0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE], static mut SUBPOOL_1_SIZES: MaybeUninit<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]> =
); MaybeUninit::new([STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize]);
static SUBPOOL_1_SIZES: Mutex<UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>> =
Mutex::new(UnsafeCell::new(
[STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize],
));
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2; const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
const SUBPOOL_2_BLOCK_SIZE: usize = 8; const SUBPOOL_2_BLOCK_SIZE: usize = 8;
static SUBPOOL_2: static_cell::ConstStaticCell< static mut SUBPOOL_2: MaybeUninit<
[u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE], [u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
> = static_cell::ConstStaticCell::new( > = MaybeUninit::new([0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE]);
[0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE], static mut SUBPOOL_2_SIZES: MaybeUninit<[usize; SUBPOOL_2_NUM_ELEMENTS as usize]> =
); MaybeUninit::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
static SUBPOOL_2_SIZES: static_cell::ConstStaticCell<
[usize; SUBPOOL_2_NUM_ELEMENTS as usize],
> = static_cell::ConstStaticCell::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1; const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
const SUBPOOL_3_BLOCK_SIZE: usize = 16; const SUBPOOL_3_BLOCK_SIZE: usize = 16;
@ -1656,26 +1643,26 @@ mod tests {
StaticHeaplessMemoryPool::new(false); StaticHeaplessMemoryPool::new(false);
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_1.take(), unsafe { SUBPOOL_1.assume_init_mut() },
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() }, unsafe { SUBPOOL_1_SIZES.assume_init_mut() },
SUBPOOL_1_NUM_ELEMENTS, SUBPOOL_1_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_2.take(), unsafe { SUBPOOL_2.assume_init_mut() },
SUBPOOL_2_SIZES.take(), unsafe { SUBPOOL_2_SIZES.assume_init_mut() },
SUBPOOL_2_NUM_ELEMENTS, SUBPOOL_2_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_3.take(), unsafe { SUBPOOL_3.assume_init_mut() },
SUBPOOL_3_SIZES.take(), unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS, SUBPOOL_3_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
heapless_pool heapless_pool
@ -1795,18 +1782,18 @@ mod tests {
StaticHeaplessMemoryPool::new(true); StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_2.take(), unsafe { SUBPOOL_2.assume_init_mut() },
SUBPOOL_2_SIZES.take(), unsafe { SUBPOOL_2_SIZES.assume_init_mut() },
SUBPOOL_2_NUM_ELEMENTS, SUBPOOL_2_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_4.take(), unsafe { SUBPOOL_4.assume_init_mut() },
SUBPOOL_4_SIZES.take(), unsafe { SUBPOOL_4_SIZES.assume_init_mut() },
SUBPOOL_4_NUM_ELEMENTS, SUBPOOL_4_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
generic_test_spills_to_higher_subpools(&mut heapless_pool); generic_test_spills_to_higher_subpools(&mut heapless_pool);
@ -1818,18 +1805,18 @@ mod tests {
StaticHeaplessMemoryPool::new(true); StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_5.take(), unsafe { SUBPOOL_5.assume_init_mut() },
SUBPOOL_5_SIZES.take(), unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
SUBPOOL_5_NUM_ELEMENTS, SUBPOOL_5_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_3.take(), unsafe { SUBPOOL_3.assume_init_mut() },
SUBPOOL_3_SIZES.take(), unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS, SUBPOOL_3_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
generic_test_spillage_fails_as_well(&mut heapless_pool); generic_test_spillage_fails_as_well(&mut heapless_pool);
@ -1841,26 +1828,26 @@ mod tests {
StaticHeaplessMemoryPool::new(true); StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_5.take(), unsafe { SUBPOOL_5.assume_init_mut() },
SUBPOOL_5_SIZES.take(), unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
SUBPOOL_5_NUM_ELEMENTS, SUBPOOL_5_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_6.take(), unsafe { SUBPOOL_6.assume_init_mut() },
SUBPOOL_6_SIZES.take(), unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
SUBPOOL_6_NUM_ELEMENTS, SUBPOOL_6_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_3.take(), unsafe { SUBPOOL_3.assume_init_mut() },
SUBPOOL_3_SIZES.take(), unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS, SUBPOOL_3_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
generic_test_spillage_works_across_multiple_subpools(&mut heapless_pool); generic_test_spillage_works_across_multiple_subpools(&mut heapless_pool);
@ -1872,26 +1859,26 @@ mod tests {
StaticHeaplessMemoryPool::new(true); StaticHeaplessMemoryPool::new(true);
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_5.take(), unsafe { SUBPOOL_5.assume_init_mut() },
SUBPOOL_5_SIZES.take(), unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
SUBPOOL_5_NUM_ELEMENTS, SUBPOOL_5_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_6.take(), unsafe { SUBPOOL_6.assume_init_mut() },
SUBPOOL_6_SIZES.take(), unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
SUBPOOL_6_NUM_ELEMENTS, SUBPOOL_6_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
assert!(heapless_pool assert!(heapless_pool
.grow( .grow(
SUBPOOL_3.take(), unsafe { SUBPOOL_3.assume_init_mut() },
SUBPOOL_3_SIZES.take(), unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
SUBPOOL_3_NUM_ELEMENTS, SUBPOOL_3_NUM_ELEMENTS,
true false
) )
.is_ok()); .is_ok());
generic_test_spillage_fails_across_multiple_subpools(&mut heapless_pool); generic_test_spillage_fails_across_multiple_subpools(&mut heapless_pool);

View File

@ -66,10 +66,9 @@ impl GenericActionReplyPus {
pub mod alloc_mod { pub mod alloc_mod {
use crate::{ use crate::{
action::ActionRequest, action::ActionRequest,
queue::{GenericReceiveError, GenericSendError}, queue::GenericTargetedMessagingError,
request::{ request::{
GenericMessage, MessageReceiverProvider, MessageSenderAndReceiver, GenericMessage, MessageReceiver, MessageSender, MessageSenderAndReceiver, RequestId,
MessageSenderProvider, MessageSenderStoreProvider, RequestId,
}, },
ComponentId, ComponentId,
}; };
@ -77,18 +76,15 @@ pub mod alloc_mod {
use super::ActionReplyPus; use super::ActionReplyPus;
/// Helper type definition for a mode handler which can handle mode requests. /// Helper type definition for a mode handler which can handle mode requests.
pub type ActionRequestHandlerInterface<Sender, Receiver, ReplySenderStore> = pub type ActionRequestHandlerInterface<S, R> =
MessageSenderAndReceiver<ActionReplyPus, ActionRequest, Sender, Receiver, ReplySenderStore>; MessageSenderAndReceiver<ActionReplyPus, ActionRequest, S, R>;
impl< impl<S: MessageSender<ActionReplyPus>, R: MessageReceiver<ActionRequest>>
Sender: MessageSenderProvider<ActionReplyPus>, ActionRequestHandlerInterface<S, R>
Receiver: MessageReceiverProvider<ActionRequest>,
ReplySender: MessageSenderStoreProvider<ActionReplyPus, Sender>,
> ActionRequestHandlerInterface<Sender, Receiver, ReplySender>
{ {
pub fn try_recv_action_request( pub fn try_recv_action_request(
&self, &self,
) -> Result<Option<GenericMessage<ActionRequest>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ActionRequest>>, GenericTargetedMessagingError> {
self.try_recv_message() self.try_recv_message()
} }
@ -97,31 +93,22 @@ pub mod alloc_mod {
request_id: RequestId, request_id: RequestId,
target_id: ComponentId, target_id: ComponentId,
reply: ActionReplyPus, reply: ActionReplyPus,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, reply) self.send_message(request_id, target_id, reply)
} }
} }
/// Helper type defintion for a mode handler object which can send mode requests and receive /// Helper type defintion for a mode handler object which can send mode requests and receive
/// mode replies. /// mode replies.
pub type ActionRequestorInterface<Sender, Receiver, RequestSenderStore> = pub type ActionRequestorInterface<S, R> =
MessageSenderAndReceiver< MessageSenderAndReceiver<ActionRequest, ActionReplyPus, S, R>;
ActionRequest,
ActionReplyPus,
Sender,
Receiver,
RequestSenderStore,
>;
impl< impl<S: MessageSender<ActionRequest>, R: MessageReceiver<ActionReplyPus>>
Sender: MessageSenderProvider<ActionRequest>, ActionRequestorInterface<S, R>
Receiver: MessageReceiverProvider<ActionReplyPus>,
RequestSenderStore: MessageSenderStoreProvider<ActionRequest, Sender>,
> ActionRequestorInterface<Sender, Receiver, RequestSenderStore>
{ {
pub fn try_recv_action_reply( pub fn try_recv_action_reply(
&self, &self,
) -> Result<Option<GenericMessage<ActionReplyPus>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<ActionReplyPus>>, GenericTargetedMessagingError> {
self.try_recv_message() self.try_recv_message()
} }
@ -130,7 +117,7 @@ pub mod alloc_mod {
request_id: RequestId, request_id: RequestId,
target_id: ComponentId, target_id: ComponentId,
request: ActionRequest, request: ActionRequest,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, request) self.send_message(request_id, target_id, request)
} }
} }
@ -145,7 +132,6 @@ pub mod std_mod {
verification::{self, TcStateToken}, verification::{self, TcStateToken},
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap,
}, },
request::{MessageSenderMap, OneMessageSender},
ComponentId, ComponentId,
}; };
@ -188,38 +174,22 @@ pub mod std_mod {
} }
pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActivePusActionRequestStd>; pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActivePusActionRequestStd>;
pub type ActionRequestHandlerOneSenderMpsc = ActionRequestHandlerInterface< pub type ActionRequestHandlerMpsc = ActionRequestHandlerInterface<
mpsc::Sender<GenericMessage<ActionReplyPus>>, mpsc::Sender<GenericMessage<ActionReplyPus>>,
mpsc::Receiver<GenericMessage<ActionRequest>>, mpsc::Receiver<GenericMessage<ActionRequest>>,
OneMessageSender<
GenericMessage<ActionReplyPus>,
mpsc::Sender<GenericMessage<ActionReplyPus>>,
>,
>; >;
pub type ActionRequestHandlerOneSenderMpscBounded = ActionRequestHandlerInterface< pub type ActionRequestHandlerMpscBounded = ActionRequestHandlerInterface<
mpsc::SyncSender<GenericMessage<ActionReplyPus>>, mpsc::SyncSender<GenericMessage<ActionReplyPus>>,
mpsc::Receiver<GenericMessage<ActionRequest>>, mpsc::Receiver<GenericMessage<ActionRequest>>,
OneMessageSender<
GenericMessage<ActionReplyPus>,
mpsc::SyncSender<GenericMessage<ActionReplyPus>>,
>,
>; >;
pub type ActionRequestorWithSenderMapMpsc = ActionRequestorInterface< pub type ActionRequestorMpsc = ActionRequestorInterface<
mpsc::Sender<GenericMessage<ActionRequest>>, mpsc::Sender<GenericMessage<ActionRequest>>,
mpsc::Receiver<GenericMessage<ActionReplyPus>>, mpsc::Receiver<GenericMessage<ActionReplyPus>>,
MessageSenderMap<
GenericMessage<ActionRequest>,
mpsc::Sender<GenericMessage<ActionRequest>>,
>,
>; >;
pub type ActionRequestorWithSenderMapBoundedMpsc = ActionRequestorInterface< pub type ActionRequestorBoundedMpsc = ActionRequestorInterface<
mpsc::SyncSender<GenericMessage<ActionRequest>>, mpsc::SyncSender<GenericMessage<ActionRequest>>,
mpsc::Receiver<GenericMessage<ActionReplyPus>>, mpsc::Receiver<GenericMessage<ActionReplyPus>>,
MessageSenderMap<
GenericMessage<ActionRequest>,
mpsc::SyncSender<GenericMessage<ActionRequest>>,
>,
>; >;
} }

View File

@ -9,14 +9,14 @@ use std::sync::mpsc::Sender;
use super::verification::VerificationReportingProvider; use super::verification::VerificationReportingProvider;
use super::{ use super::{
EcssTcInMemConversionProvider, EcssTcReceiver, EcssTmSender, GenericConversionError, EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, GenericConversionError,
GenericRoutingError, HandlingStatus, PusServiceHelper, GenericRoutingError, HandlingStatus, PusServiceHelper,
}; };
pub struct PusEventServiceHandler< pub struct PusEventServiceHandler<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
> { > {
pub service_helper: pub service_helper:
@ -27,7 +27,7 @@ pub struct PusEventServiceHandler<
impl< impl<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
> PusEventServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter> > PusEventServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
{ {
@ -170,7 +170,7 @@ mod tests {
event_man::EventRequestWithToken, event_man::EventRequestWithToken,
tests::PusServiceHandlerWithSharedStoreCommon, tests::PusServiceHandlerWithSharedStoreCommon,
verification::{TcStateAccepted, VerificationToken}, verification::{TcStateAccepted, VerificationToken},
DirectPusPacketHandlerResult, EcssTcInSharedPoolConverter, PusPacketHandlingError, DirectPusPacketHandlerResult, EcssTcInSharedStoreConverter, PusPacketHandlingError,
}, },
}; };
@ -183,7 +183,7 @@ mod tests {
handler: PusEventServiceHandler< handler: PusEventServiceHandler<
MpscTcReceiver, MpscTcReceiver,
PacketSenderWithSharedPool, PacketSenderWithSharedPool,
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
VerificationReporter, VerificationReporter,
>, >,
} }

View File

@ -947,7 +947,7 @@ pub mod std_mod {
} }
} }
pub trait EcssTcInMemConversionProvider { pub trait EcssTcInMemConverter {
fn cache(&mut self, possible_packet: &TcInMemory) -> Result<(), PusTcFromMemError>; fn cache(&mut self, possible_packet: &TcInMemory) -> Result<(), PusTcFromMemError>;
fn tc_slice_raw(&self) -> &[u8]; fn tc_slice_raw(&self) -> &[u8];
@ -980,7 +980,7 @@ pub mod std_mod {
pub pus_tc_raw: Option<Vec<u8>>, pub pus_tc_raw: Option<Vec<u8>>,
} }
impl EcssTcInMemConversionProvider for EcssTcInVecConverter { impl EcssTcInMemConverter for EcssTcInVecConverter {
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> { fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
self.pus_tc_raw = None; self.pus_tc_raw = None;
match tc_in_memory { match tc_in_memory {
@ -1011,25 +1011,24 @@ pub mod std_mod {
/// [SharedStaticMemoryPool] structure. This is useful if run-time allocation for these /// [SharedStaticMemoryPool] structure. This is useful if run-time allocation for these
/// packets should be avoided. Please note that this structure is not able to convert TCs which /// packets should be avoided. Please note that this structure is not able to convert TCs which
/// are stored as a `Vec<u8>`. /// are stored as a `Vec<u8>`.
#[derive(Clone)] pub struct EcssTcInSharedStoreConverter {
pub struct EcssTcInSharedPoolConverter {
sender_id: Option<ComponentId>, sender_id: Option<ComponentId>,
shared_tc_pool: SharedStaticMemoryPool, shared_tc_store: SharedStaticMemoryPool,
pus_buf: Vec<u8>, pus_buf: Vec<u8>,
} }
impl EcssTcInSharedPoolConverter { impl EcssTcInSharedStoreConverter {
pub fn new(shared_tc_store: SharedStaticMemoryPool, max_expected_tc_size: usize) -> Self { pub fn new(shared_tc_store: SharedStaticMemoryPool, max_expected_tc_size: usize) -> Self {
Self { Self {
sender_id: None, sender_id: None,
shared_tc_pool: shared_tc_store, shared_tc_store,
pus_buf: alloc::vec![0; max_expected_tc_size], pus_buf: alloc::vec![0; max_expected_tc_size],
} }
} }
pub fn copy_tc_to_buf(&mut self, addr: PoolAddr) -> Result<(), PusTcFromMemError> { pub fn copy_tc_to_buf(&mut self, addr: PoolAddr) -> Result<(), PusTcFromMemError> {
// Keep locked section as short as possible. // Keep locked section as short as possible.
let mut tc_pool = self.shared_tc_pool.write().map_err(|_| { let mut tc_pool = self.shared_tc_store.write().map_err(|_| {
PusTcFromMemError::EcssTmtc(EcssTmtcError::Store(PoolError::LockError)) PusTcFromMemError::EcssTmtc(EcssTmtcError::Store(PoolError::LockError))
})?; })?;
let tc_size = tc_pool.len_of_data(&addr).map_err(EcssTmtcError::Store)?; let tc_size = tc_pool.len_of_data(&addr).map_err(EcssTmtcError::Store)?;
@ -1049,7 +1048,7 @@ pub mod std_mod {
} }
} }
impl EcssTcInMemConversionProvider for EcssTcInSharedPoolConverter { impl EcssTcInMemConverter for EcssTcInSharedStoreConverter {
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> { fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
match tc_in_memory { match tc_in_memory {
super::TcInMemory::Pool(packet_in_pool) => { super::TcInMemory::Pool(packet_in_pool) => {
@ -1072,44 +1071,6 @@ pub mod std_mod {
} }
} }
// TODO: alloc feature flag?
#[derive(Clone)]
pub enum EcssTcInMemConverter {
Static(EcssTcInSharedPoolConverter),
Heap(EcssTcInVecConverter),
}
impl EcssTcInMemConverter {
pub fn new_static(static_store_converter: EcssTcInSharedPoolConverter) -> Self {
EcssTcInMemConverter::Static(static_store_converter)
}
pub fn new_heap(heap_converter: EcssTcInVecConverter) -> Self {
EcssTcInMemConverter::Heap(heap_converter)
}
}
impl EcssTcInMemConversionProvider for EcssTcInMemConverter {
fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
match self {
EcssTcInMemConverter::Static(converter) => converter.cache(tc_in_memory),
EcssTcInMemConverter::Heap(converter) => converter.cache(tc_in_memory),
}
}
fn tc_slice_raw(&self) -> &[u8] {
match self {
EcssTcInMemConverter::Static(converter) => converter.tc_slice_raw(),
EcssTcInMemConverter::Heap(converter) => converter.tc_slice_raw(),
}
}
fn sender_id(&self) -> Option<ComponentId> {
match self {
EcssTcInMemConverter::Static(converter) => converter.sender_id(),
EcssTcInMemConverter::Heap(converter) => converter.sender_id(),
}
}
}
pub struct PusServiceBase< pub struct PusServiceBase<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
@ -1133,7 +1094,7 @@ pub mod std_mod {
pub struct PusServiceHelper< pub struct PusServiceHelper<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
> { > {
pub common: PusServiceBase<TcReceiver, TmSender, VerificationReporter>, pub common: PusServiceBase<TcReceiver, TmSender, VerificationReporter>,
@ -1143,7 +1104,7 @@ pub mod std_mod {
impl< impl<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
> PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter> > PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
{ {
@ -1260,9 +1221,8 @@ pub(crate) fn source_buffer_large_enough(
#[cfg(any(feature = "test_util", test))] #[cfg(any(feature = "test_util", test))]
pub mod test_util { pub mod test_util {
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
use crate::request::UniqueApidTargetId; use crate::request::UniqueApidTargetId;
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
use super::{ use super::{
verification::{self, TcStateAccepted, VerificationToken}, verification::{self, TcStateAccepted, VerificationToken},
@ -1272,7 +1232,6 @@ pub mod test_util {
pub const TEST_APID: u16 = 0x101; pub const TEST_APID: u16 = 0x101;
pub const TEST_UNIQUE_ID_0: u32 = 0x05; pub const TEST_UNIQUE_ID_0: u32 = 0x05;
pub const TEST_UNIQUE_ID_1: u32 = 0x06; pub const TEST_UNIQUE_ID_1: u32 = 0x06;
pub const TEST_COMPONENT_ID_0: UniqueApidTargetId = pub const TEST_COMPONENT_ID_0: UniqueApidTargetId =
UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0); UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0);
pub const TEST_COMPONENT_ID_1: UniqueApidTargetId = pub const TEST_COMPONENT_ID_1: UniqueApidTargetId =
@ -1309,13 +1268,14 @@ pub mod tests {
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader}; use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
use spacepackets::ecss::{PusPacket, WritablePusPacket}; use spacepackets::ecss::{PusPacket, WritablePusPacket};
use spacepackets::CcsdsPacket; use spacepackets::CcsdsPacket;
use test_util::{TEST_APID, TEST_COMPONENT_ID_0};
use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig}; use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
use crate::pus::verification::{RequestId, VerificationReporter}; use crate::pus::verification::{RequestId, VerificationReporter};
use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool}; use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool};
use crate::ComponentId; use crate::ComponentId;
use super::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
use super::verification::test_util::TestVerificationReporter; use super::verification::test_util::TestVerificationReporter;
use super::verification::{ use super::verification::{
TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken, TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
@ -1386,7 +1346,7 @@ pub mod tests {
pub type PusServiceHelperStatic = PusServiceHelper< pub type PusServiceHelperStatic = PusServiceHelper<
MpscTcReceiver, MpscTcReceiver,
PacketSenderWithSharedPool, PacketSenderWithSharedPool,
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
VerificationReporter, VerificationReporter,
>; >;
@ -1413,7 +1373,8 @@ pub mod tests {
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg); VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
let test_srv_tm_sender = let test_srv_tm_sender =
PacketSenderWithSharedPool::new(tm_tx, shared_tm_pool_wrapper.clone()); PacketSenderWithSharedPool::new(tm_tx, shared_tm_pool_wrapper.clone());
let in_store_converter = EcssTcInSharedPoolConverter::new(shared_tc_pool.clone(), 2048); let in_store_converter =
EcssTcInSharedStoreConverter::new(shared_tc_pool.clone(), 2048);
( (
Self { Self {
pus_buf: RefCell::new([0; 2048]), pus_buf: RefCell::new([0; 2048]),

View File

@ -39,7 +39,7 @@ mod tests {
use crate::{ use crate::{
mode::{ mode::{
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender, ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
ModeRequestorAndHandlerMpsc, ModeRequestorOneChildMpsc, ModeRequestorAndHandlerMpsc, ModeRequestorMpsc,
}, },
request::{GenericMessage, MessageMetadata}, request::{GenericMessage, MessageMetadata},
}; };
@ -52,8 +52,7 @@ mod tests {
fn test_simple_mode_requestor() { fn test_simple_mode_requestor() {
let (reply_sender, reply_receiver) = mpsc::channel(); let (reply_sender, reply_receiver) = mpsc::channel();
let (request_sender, request_receiver) = mpsc::channel(); let (request_sender, request_receiver) = mpsc::channel();
let mut mode_requestor = let mut mode_requestor = ModeRequestorMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
ModeRequestorOneChildMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
mode_requestor.add_message_target(TEST_COMPONENT_ID_1, request_sender); mode_requestor.add_message_target(TEST_COMPONENT_ID_1, request_sender);
// Send a request and verify it arrives at the receiver. // Send a request and verify it arrives at the receiver.

View File

@ -1,7 +1,7 @@
use super::scheduler::PusSchedulerProvider; use super::scheduler::PusSchedulerProvider;
use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::verification::{VerificationReporter, VerificationReportingProvider};
use super::{ use super::{
DirectPusPacketHandlerResult, EcssTcInMemConversionProvider, EcssTcInSharedPoolConverter, DirectPusPacketHandlerResult, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTcInVecConverter, EcssTcReceiver, EcssTmSender, HandlingStatus, MpscTcReceiver, EcssTcInVecConverter, EcssTcReceiver, EcssTmSender, HandlingStatus, MpscTcReceiver,
PartialPusHandlingError, PusServiceHelper, PartialPusHandlingError, PusServiceHelper,
}; };
@ -24,7 +24,7 @@ use std::sync::mpsc;
pub struct PusSchedServiceHandler< pub struct PusSchedServiceHandler<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
PusScheduler: PusSchedulerProvider, PusScheduler: PusSchedulerProvider,
> { > {
@ -36,7 +36,7 @@ pub struct PusSchedServiceHandler<
impl< impl<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
Scheduler: PusSchedulerProvider, Scheduler: PusSchedulerProvider,
> >
@ -229,7 +229,7 @@ pub type PusService11SchedHandlerDynWithBoundedMpsc<PusScheduler> = PusSchedServ
pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusSchedServiceHandler< pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusSchedServiceHandler<
MpscTcReceiver, MpscTcReceiver,
PacketSenderWithSharedPool, PacketSenderWithSharedPool,
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
VerificationReporter, VerificationReporter,
PusScheduler, PusScheduler,
>; >;
@ -238,7 +238,7 @@ pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusSchedServiceH
pub type PusService11SchedHandlerStaticWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler< pub type PusService11SchedHandlerStaticWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler<
MpscTcReceiver, MpscTcReceiver,
PacketSenderWithSharedPool, PacketSenderWithSharedPool,
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
VerificationReporter, VerificationReporter,
PusScheduler, PusScheduler,
>; >;
@ -253,7 +253,7 @@ mod tests {
scheduler::{self, PusSchedulerProvider, TcInfo}, scheduler::{self, PusSchedulerProvider, TcInfo},
tests::PusServiceHandlerWithSharedStoreCommon, tests::PusServiceHandlerWithSharedStoreCommon,
verification::{RequestId, TcStateAccepted, VerificationToken}, verification::{RequestId, TcStateAccepted, VerificationToken},
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
}; };
use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError}; use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError};
use crate::tmtc::PacketSenderWithSharedPool; use crate::tmtc::PacketSenderWithSharedPool;
@ -276,7 +276,7 @@ mod tests {
handler: PusSchedServiceHandler< handler: PusSchedServiceHandler<
MpscTcReceiver, MpscTcReceiver,
PacketSenderWithSharedPool, PacketSenderWithSharedPool,
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
VerificationReporter, VerificationReporter,
TestScheduler, TestScheduler,
>, >,

View File

@ -9,9 +9,8 @@ use std::sync::mpsc;
use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::verification::{VerificationReporter, VerificationReportingProvider};
use super::{ use super::{
EcssTcInMemConversionProvider, EcssTcInSharedPoolConverter, EcssTcInVecConverter, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiver,
EcssTcReceiver, EcssTmSender, GenericConversionError, HandlingStatus, MpscTcReceiver, EcssTmSender, GenericConversionError, HandlingStatus, MpscTcReceiver, PusServiceHelper,
PusServiceHelper,
}; };
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets. /// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
@ -19,7 +18,7 @@ use super::{
pub struct PusService17TestHandler< pub struct PusService17TestHandler<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
> { > {
pub service_helper: pub service_helper:
@ -29,7 +28,7 @@ pub struct PusService17TestHandler<
impl< impl<
TcReceiver: EcssTcReceiver, TcReceiver: EcssTcReceiver,
TmSender: EcssTmSender, TmSender: EcssTmSender,
TcInMemConverter: EcssTcInMemConversionProvider, TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider, VerificationReporter: VerificationReportingProvider,
> PusService17TestHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter> > PusService17TestHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
{ {
@ -128,7 +127,7 @@ pub type PusService17TestHandlerDynWithBoundedMpsc = PusService17TestHandler<
pub type PusService17TestHandlerStaticWithBoundedMpsc = PusService17TestHandler< pub type PusService17TestHandlerStaticWithBoundedMpsc = PusService17TestHandler<
MpscTcReceiver, MpscTcReceiver,
PacketSenderWithSharedPool, PacketSenderWithSharedPool,
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
VerificationReporter, VerificationReporter,
>; >;
@ -143,7 +142,7 @@ mod tests {
}; };
use crate::pus::verification::{TcStateAccepted, VerificationToken}; use crate::pus::verification::{TcStateAccepted, VerificationToken};
use crate::pus::{ use crate::pus::{
DirectPusPacketHandlerResult, EcssTcInSharedPoolConverter, EcssTcInVecConverter, DirectPusPacketHandlerResult, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
GenericConversionError, HandlingStatus, MpscTcReceiver, MpscTmAsVecSender, GenericConversionError, HandlingStatus, MpscTcReceiver, MpscTmAsVecSender,
PartialPusHandlingError, PusPacketHandlingError, PartialPusHandlingError, PusPacketHandlingError,
}; };
@ -163,7 +162,7 @@ mod tests {
handler: PusService17TestHandler< handler: PusService17TestHandler<
MpscTcReceiver, MpscTcReceiver,
PacketSenderWithSharedPool, PacketSenderWithSharedPool,
EcssTcInSharedPoolConverter, EcssTcInSharedStoreConverter,
VerificationReporter, VerificationReporter,
>, >,
} }

View File

@ -20,7 +20,7 @@
//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporter //! VerificationReportingProvider, VerificationReporterCfg, VerificationReporter
//! }; //! };
//! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool}; //! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool};
//! use satrs::spacepackets::seq_count::SeqCountProviderSimple; //! use satrs::seq_count::SeqCountProviderSimple;
//! use satrs::request::UniqueApidTargetId; //! use satrs::request::UniqueApidTargetId;
//! use spacepackets::ecss::PusPacket; //! use spacepackets::ecss::PusPacket;
//! use spacepackets::SpHeader; //! use spacepackets::SpHeader;
@ -97,8 +97,8 @@ use spacepackets::ecss::EcssEnumeration;
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceCtrl}; use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceCtrl};
use spacepackets::{SpHeader, MAX_APID}; use spacepackets::{SpHeader, MAX_APID};
pub use crate::seq_count::SeqCountProviderSimple;
pub use spacepackets::ecss::verification::*; pub use spacepackets::ecss::verification::*;
pub use spacepackets::seq_count::SeqCountProviderSimple;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub use alloc_mod::*; pub use alloc_mod::*;
@ -1702,7 +1702,7 @@ pub mod tests {
}; };
use crate::pus::{ChannelWithId, PusTmVariant}; use crate::pus::{ChannelWithId, PusTmVariant};
use crate::request::MessageMetadata; use crate::request::MessageMetadata;
use crate::spacepackets::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProvider}; use crate::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore};
use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool}; use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
use crate::ComponentId; use crate::ComponentId;
use alloc::format; use alloc::format;

View File

@ -1,3 +1,6 @@
use core::fmt::{Display, Formatter};
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::sync::mpsc; use std::sync::mpsc;
@ -7,31 +10,89 @@ use crate::ComponentId;
pub type ChannelId = u32; pub type ChannelId = u32;
/// Generic error type for sending something via a message queue. /// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GenericSendError { pub enum GenericSendError {
#[error("rx side has disconnected")]
RxDisconnected, RxDisconnected,
#[error("queue with max capacity of {0:?} is full")]
QueueFull(Option<u32>), QueueFull(Option<u32>),
#[error("target queue with ID {0} does not exist")]
TargetDoesNotExist(ComponentId), TargetDoesNotExist(ComponentId),
} }
impl Display for GenericSendError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
GenericSendError::RxDisconnected => {
write!(f, "rx side has disconnected")
}
GenericSendError::QueueFull(max_cap) => {
write!(f, "queue with max capacity of {max_cap:?} is full")
}
GenericSendError::TargetDoesNotExist(target) => {
write!(f, "target queue with ID {target} does not exist")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericSendError {}
/// Generic error type for sending something via a message queue. /// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GenericReceiveError { pub enum GenericReceiveError {
#[error("nothing to receive")]
Empty, Empty,
#[error("tx side with id {0:?} has disconnected")]
TxDisconnected(Option<ComponentId>), TxDisconnected(Option<ComponentId>),
} }
#[derive(Debug, Clone, thiserror::Error)] impl Display for GenericReceiveError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::TxDisconnected(channel_id) => {
write!(f, "tx side with id {channel_id:?} has disconnected")
}
Self::Empty => {
write!(f, "nothing to receive")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericReceiveError {}
#[derive(Debug, Clone)]
pub enum GenericTargetedMessagingError { pub enum GenericTargetedMessagingError {
#[error("generic targeted messaging send error: {0}")] Send(GenericSendError),
Send(#[from] GenericSendError), Receive(GenericReceiveError),
#[error("generic targeted messaging receive error: {0}")] }
Receive(#[from] GenericReceiveError), impl From<GenericSendError> for GenericTargetedMessagingError {
fn from(value: GenericSendError) -> Self {
Self::Send(value)
}
}
impl From<GenericReceiveError> for GenericTargetedMessagingError {
fn from(value: GenericReceiveError) -> Self {
Self::Receive(value)
}
}
impl Display for GenericTargetedMessagingError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::Send(err) => write!(f, "generic targeted messaging error: {}", err),
Self::Receive(err) => write!(f, "generic targeted messaging error: {}", err),
}
}
}
#[cfg(feature = "std")]
impl Error for GenericTargetedMessagingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
GenericTargetedMessagingError::Send(send) => Some(send),
GenericTargetedMessagingError::Receive(receive) => Some(receive),
}
}
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]

View File

@ -13,10 +13,7 @@ use spacepackets::{
ByteConversionError, ByteConversionError,
}; };
use crate::{ use crate::{queue::GenericTargetedMessagingError, ComponentId};
queue::{GenericReceiveError, GenericSendError},
ComponentId,
};
/// Generic request ID type. Requests can be associated with an ID to have a unique identifier /// Generic request ID type. Requests can be associated with an ID to have a unique identifier
/// for them. This can be useful for tasks like tracking their progress. /// for them. This can be useful for tasks like tracking their progress.
@ -143,38 +140,37 @@ impl<Message> GenericMessage<Message> {
} }
/// Generic trait for objects which can send targeted messages. /// Generic trait for objects which can send targeted messages.
pub trait MessageSenderProvider<MSG>: Send { pub trait MessageSender<MSG>: Send {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericSendError>; fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError>;
} }
// Generic trait for objects which can receive targeted messages. // Generic trait for objects which can receive targeted messages.
pub trait MessageReceiverProvider<MSG> { pub trait MessageReceiver<MSG> {
fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericReceiveError>; fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError>;
} }
pub struct MessageWithSenderIdReceiver<Msg, Receiver: MessageReceiverProvider<Msg>>( pub struct MessageWithSenderIdReceiver<MSG, R: MessageReceiver<MSG>>(pub R, PhantomData<MSG>);
pub Receiver,
PhantomData<Msg>,
);
impl<MSG, R: MessageReceiverProvider<MSG>> From<R> for MessageWithSenderIdReceiver<MSG, R> { impl<MSG, R: MessageReceiver<MSG>> From<R> for MessageWithSenderIdReceiver<MSG, R> {
fn from(receiver: R) -> Self { fn from(receiver: R) -> Self {
MessageWithSenderIdReceiver(receiver, PhantomData) MessageWithSenderIdReceiver(receiver, PhantomData)
} }
} }
impl<MSG, R: MessageReceiverProvider<MSG>> MessageWithSenderIdReceiver<MSG, R> { impl<MSG, R: MessageReceiver<MSG>> MessageWithSenderIdReceiver<MSG, R> {
pub fn try_recv_message(&self) -> Result<Option<GenericMessage<MSG>>, GenericReceiveError> { pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
self.0.try_recv() self.0.try_recv()
} }
} }
pub struct MessageReceiverWithId<MSG, R: MessageReceiverProvider<MSG>> { pub struct MessageReceiverWithId<MSG, R: MessageReceiver<MSG>> {
local_channel_id: ComponentId, local_channel_id: ComponentId,
reply_receiver: MessageWithSenderIdReceiver<MSG, R>, reply_receiver: MessageWithSenderIdReceiver<MSG, R>,
} }
impl<MSG, R: MessageReceiverProvider<MSG>> MessageReceiverWithId<MSG, R> { impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
pub fn new(local_channel_id: ComponentId, reply_receiver: R) -> Self { pub fn new(local_channel_id: ComponentId, reply_receiver: R) -> Self {
Self { Self {
local_channel_id, local_channel_id,
@ -187,129 +183,43 @@ impl<MSG, R: MessageReceiverProvider<MSG>> MessageReceiverWithId<MSG, R> {
} }
} }
impl<MSG, R: MessageReceiverProvider<MSG>> MessageReceiverWithId<MSG, R> { impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
pub fn try_recv_message(&self) -> Result<Option<GenericMessage<MSG>>, GenericReceiveError> { pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
self.reply_receiver.0.try_recv() self.reply_receiver.0.try_recv()
} }
} }
pub trait MessageSenderStoreProvider<Message, Sender>: Default {
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender);
fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: Message,
) -> Result<(), GenericSendError>;
}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod alloc_mod { pub mod alloc_mod {
use crate::queue::GenericSendError; use crate::queue::GenericSendError;
use std::convert::From;
use super::*; use super::*;
use hashbrown::HashMap; use hashbrown::HashMap;
pub struct OneMessageSender<Msg, S: MessageSenderProvider<Msg>> { pub struct MessageSenderMap<MSG, S: MessageSender<MSG>>(
pub id_and_sender: Option<(ComponentId, S)>,
pub(crate) phantom: PhantomData<Msg>,
}
impl<Msg, S: MessageSenderProvider<Msg>> Default for OneMessageSender<Msg, S> {
fn default() -> Self {
Self {
id_and_sender: Default::default(),
phantom: Default::default(),
}
}
}
impl<Msg, Sender: MessageSenderProvider<Msg>> MessageSenderStoreProvider<Msg, Sender>
for OneMessageSender<Msg, Sender>
{
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
if self.id_and_sender.is_some() {
return;
}
self.id_and_sender = Some((target_id, message_sender));
}
fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: Msg,
) -> Result<(), GenericSendError> {
if let Some((current_id, sender)) = &self.id_and_sender {
if *current_id == target_channel_id {
sender.send(GenericMessage::new(requestor_info, message))?;
return Ok(());
}
}
Err(GenericSendError::TargetDoesNotExist(target_channel_id))
}
}
pub struct MessageSenderList<MSG, S: MessageSenderProvider<MSG>>(
pub alloc::vec::Vec<(ComponentId, S)>,
pub(crate) PhantomData<MSG>,
);
impl<MSG, S: MessageSenderProvider<MSG>> Default for MessageSenderList<MSG, S> {
fn default() -> Self {
Self(Default::default(), PhantomData)
}
}
impl<Msg, Sender: MessageSenderProvider<Msg>> MessageSenderStoreProvider<Msg, Sender>
for MessageSenderList<Msg, Sender>
{
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
self.0.push((target_id, message_sender));
}
fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: Msg,
) -> Result<(), GenericSendError> {
for (current_id, sender) in &self.0 {
if *current_id == target_channel_id {
sender.send(GenericMessage::new(requestor_info, message))?;
return Ok(());
}
}
Err(GenericSendError::TargetDoesNotExist(target_channel_id))
}
}
pub struct MessageSenderMap<MSG, S: MessageSenderProvider<MSG>>(
pub HashMap<ComponentId, S>, pub HashMap<ComponentId, S>,
pub(crate) PhantomData<MSG>, pub(crate) PhantomData<MSG>,
); );
impl<MSG, S: MessageSenderProvider<MSG>> Default for MessageSenderMap<MSG, S> { impl<MSG, S: MessageSender<MSG>> Default for MessageSenderMap<MSG, S> {
fn default() -> Self { fn default() -> Self {
Self(Default::default(), PhantomData) Self(Default::default(), PhantomData)
} }
} }
impl<Msg, Sender: MessageSenderProvider<Msg>> MessageSenderStoreProvider<Msg, Sender> impl<MSG, S: MessageSender<MSG>> MessageSenderMap<MSG, S> {
for MessageSenderMap<Msg, Sender> pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
{
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
self.0.insert(target_id, message_sender); self.0.insert(target_id, message_sender);
} }
fn send_message( pub fn send_message(
&self, &self,
requestor_info: MessageMetadata, requestor_info: MessageMetadata,
target_channel_id: ComponentId, target_channel_id: ComponentId,
message: Msg, message: MSG,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
if self.0.contains_key(&target_channel_id) { if self.0.contains_key(&target_channel_id) {
return self return self
.0 .0
@ -317,42 +227,29 @@ pub mod alloc_mod {
.unwrap() .unwrap()
.send(GenericMessage::new(requestor_info, message)); .send(GenericMessage::new(requestor_info, message));
} }
Err(GenericSendError::TargetDoesNotExist(target_channel_id)) Err(GenericSendError::TargetDoesNotExist(target_channel_id).into())
} }
} }
pub struct MessageSenderAndReceiver< pub struct MessageSenderAndReceiver<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>> {
To,
From,
Sender: MessageSenderProvider<To>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<To, Sender>,
> {
pub local_channel_id: ComponentId, pub local_channel_id: ComponentId,
pub message_sender_store: SenderStore, pub message_sender_map: MessageSenderMap<TO, S>,
pub message_receiver: MessageWithSenderIdReceiver<From, Receiver>, pub message_receiver: MessageWithSenderIdReceiver<FROM, R>,
pub(crate) phantom: PhantomData<(To, Sender)>,
} }
impl< impl<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>>
To, MessageSenderAndReceiver<TO, FROM, S, R>
From,
Sender: MessageSenderProvider<To>,
Receiver: MessageReceiverProvider<From>,
SenderStore: MessageSenderStoreProvider<To, Sender>,
> MessageSenderAndReceiver<To, From, Sender, Receiver, SenderStore>
{ {
pub fn new(local_channel_id: ComponentId, message_receiver: Receiver) -> Self { pub fn new(local_channel_id: ComponentId, message_receiver: R) -> Self {
Self { Self {
local_channel_id, local_channel_id,
message_sender_store: Default::default(), message_sender_map: Default::default(),
message_receiver: MessageWithSenderIdReceiver::from(message_receiver), message_receiver: MessageWithSenderIdReceiver::from(message_receiver),
phantom: PhantomData,
} }
} }
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) { pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
self.message_sender_store self.message_sender_map
.add_message_target(target_id, message_sender) .add_message_target(target_id, message_sender)
} }
@ -365,9 +262,9 @@ pub mod alloc_mod {
&self, &self,
request_id: RequestId, request_id: RequestId,
target_id: ComponentId, target_id: ComponentId,
message: To, message: TO,
) -> Result<(), GenericSendError> { ) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_store.send_message( self.message_sender_map.send_message(
MessageMetadata::new(request_id, self.local_channel_id_generic()), MessageMetadata::new(request_id, self.local_channel_id_generic()),
target_id, target_id,
message, message,
@ -377,64 +274,48 @@ pub mod alloc_mod {
/// Try to receive a message, which can be a reply or a request, depending on the generics. /// Try to receive a message, which can be a reply or a request, depending on the generics.
pub fn try_recv_message( pub fn try_recv_message(
&self, &self,
) -> Result<Option<GenericMessage<From>>, GenericReceiveError> { ) -> Result<Option<GenericMessage<FROM>>, GenericTargetedMessagingError> {
self.message_receiver.try_recv_message() self.message_receiver.try_recv_message()
} }
} }
pub struct RequestAndReplySenderAndReceiver< pub struct RequestAndReplySenderAndReceiver<
Request, REQUEST,
ReqSender: MessageSenderProvider<Request>, REPLY,
ReqReceiver: MessageReceiverProvider<Request>, S0: MessageSender<REQUEST>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>, R0: MessageReceiver<REPLY>,
Reply, S1: MessageSender<REPLY>,
ReplySender: MessageSenderProvider<Reply>, R1: MessageReceiver<REQUEST>,
ReplyReceiver: MessageReceiverProvider<Reply>,
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
> { > {
pub local_channel_id: ComponentId, pub local_channel_id: ComponentId,
// These 2 are a functional group. // These 2 are a functional group.
pub request_sender_store: ReqSenderStore, pub request_sender_map: MessageSenderMap<REQUEST, S0>,
pub reply_receiver: MessageWithSenderIdReceiver<Reply, ReplyReceiver>, pub reply_receiver: MessageWithSenderIdReceiver<REPLY, R0>,
// These 2 are a functional group. // These 2 are a functional group.
pub request_receiver: MessageWithSenderIdReceiver<Request, ReqReceiver>, pub request_receiver: MessageWithSenderIdReceiver<REQUEST, R1>,
pub reply_sender_store: ReplySenderStore, pub reply_sender_map: MessageSenderMap<REPLY, S1>,
phantom: PhantomData<(ReqSender, ReplySender)>,
} }
impl< impl<
Request, REQUEST,
ReqSender: MessageSenderProvider<Request>, REPLY,
ReqReceiver: MessageReceiverProvider<Request>, S0: MessageSender<REQUEST>,
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>, R0: MessageReceiver<REPLY>,
Reply, S1: MessageSender<REPLY>,
ReplySender: MessageSenderProvider<Reply>, R1: MessageReceiver<REQUEST>,
ReplyReceiver: MessageReceiverProvider<Reply>, > RequestAndReplySenderAndReceiver<REQUEST, REPLY, S0, R0, S1, R1>
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
>
RequestAndReplySenderAndReceiver<
Request,
ReqSender,
ReqReceiver,
ReqSenderStore,
Reply,
ReplySender,
ReplyReceiver,
ReplySenderStore,
>
{ {
pub fn new( pub fn new(
local_channel_id: ComponentId, local_channel_id: ComponentId,
request_receiver: ReqReceiver, request_receiver: R1,
reply_receiver: ReplyReceiver, reply_receiver: R0,
) -> Self { ) -> Self {
Self { Self {
local_channel_id, local_channel_id,
request_receiver: request_receiver.into(), request_receiver: request_receiver.into(),
reply_receiver: reply_receiver.into(), reply_receiver: reply_receiver.into(),
request_sender_store: Default::default(), request_sender_map: Default::default(),
reply_sender_store: Default::default(), reply_sender_map: Default::default(),
phantom: PhantomData,
} }
} }
@ -452,19 +333,21 @@ pub mod std_mod {
use crate::queue::{GenericReceiveError, GenericSendError}; use crate::queue::{GenericReceiveError, GenericSendError};
impl<MSG: Send> MessageSenderProvider<MSG> for mpsc::Sender<GenericMessage<MSG>> { impl<MSG: Send> MessageSender<MSG> for mpsc::Sender<GenericMessage<MSG>> {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericSendError> { fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError> {
self.send(message) self.send(message)
.map_err(|_| GenericSendError::RxDisconnected)?; .map_err(|_| GenericSendError::RxDisconnected)?;
Ok(()) Ok(())
} }
} }
impl<MSG: Send> MessageSenderProvider<MSG> for mpsc::SyncSender<GenericMessage<MSG>> { impl<MSG: Send> MessageSender<MSG> for mpsc::SyncSender<GenericMessage<MSG>> {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericSendError> { fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError> {
if let Err(e) = self.try_send(message) { if let Err(e) = self.try_send(message) {
return match e { return match e {
mpsc::TrySendError::Full(_) => Err(GenericSendError::QueueFull(None)), mpsc::TrySendError::Full(_) => Err(GenericSendError::QueueFull(None).into()),
mpsc::TrySendError::Disconnected(_) => Err(GenericSendError::RxDisconnected), mpsc::TrySendError::Disconnected(_) => {
Err(GenericSendError::RxDisconnected.into())
}
}; };
} }
Ok(()) Ok(())
@ -474,14 +357,14 @@ pub mod std_mod {
pub type MessageSenderMapMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::Sender<MSG>>; pub type MessageSenderMapMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::Sender<MSG>>;
pub type MessageSenderMapBoundedMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::SyncSender<MSG>>; pub type MessageSenderMapBoundedMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::SyncSender<MSG>>;
impl<MSG> MessageReceiverProvider<MSG> for mpsc::Receiver<GenericMessage<MSG>> { impl<MSG> MessageReceiver<MSG> for mpsc::Receiver<GenericMessage<MSG>> {
fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericReceiveError> { fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
match self.try_recv() { match self.try_recv() {
Ok(msg) => Ok(Some(msg)), Ok(msg) => Ok(Some(msg)),
Err(e) => match e { Err(e) => match e {
mpsc::TryRecvError::Empty => Ok(None), mpsc::TryRecvError::Empty => Ok(None),
mpsc::TryRecvError::Disconnected => { mpsc::TryRecvError::Disconnected => {
Err(GenericReceiveError::TxDisconnected(None)) Err(GenericReceiveError::TxDisconnected(None).into())
} }
}, },
} }
@ -502,8 +385,8 @@ mod tests {
}; };
use crate::{ use crate::{
queue::{GenericReceiveError, GenericSendError}, queue::{GenericReceiveError, GenericSendError, GenericTargetedMessagingError},
request::{MessageMetadata, MessageSenderMap, MessageSenderStoreProvider}, request::{MessageMetadata, MessageSenderMap},
}; };
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId}; use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
@ -595,7 +478,9 @@ mod tests {
let reply = receiver.try_recv_message(); let reply = receiver.try_recv_message();
assert!(reply.is_err()); assert!(reply.is_err());
let error = reply.unwrap_err(); let error = reply.unwrap_err();
if let GenericReceiveError::TxDisconnected(None) = error { if let GenericTargetedMessagingError::Receive(GenericReceiveError::TxDisconnected(None)) =
error
{
} else { } else {
panic!("unexpected error type"); panic!("unexpected error type");
} }
@ -644,7 +529,9 @@ mod tests {
); );
assert!(result.is_err()); assert!(result.is_err());
let error = result.unwrap_err(); let error = result.unwrap_err();
if let GenericSendError::TargetDoesNotExist(target) = error { if let GenericTargetedMessagingError::Send(GenericSendError::TargetDoesNotExist(target)) =
error
{
assert_eq!(target, TEST_CHANNEL_ID_2); assert_eq!(target, TEST_CHANNEL_ID_2);
} else { } else {
panic!("Unexpected error type"); panic!("Unexpected error type");
@ -669,7 +556,7 @@ mod tests {
); );
assert!(result.is_err()); assert!(result.is_err());
let error = result.unwrap_err(); let error = result.unwrap_err();
if let GenericSendError::QueueFull(capacity) = error { if let GenericTargetedMessagingError::Send(GenericSendError::QueueFull(capacity)) = error {
assert!(capacity.is_none()); assert!(capacity.is_none());
} else { } else {
panic!("Unexpected error type {}", error); panic!("Unexpected error type {}", error);
@ -689,7 +576,7 @@ mod tests {
); );
assert!(result.is_err()); assert!(result.is_err());
let error = result.unwrap_err(); let error = result.unwrap_err();
if let GenericSendError::RxDisconnected = error { if let GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected) = error {
} else { } else {
panic!("Unexpected error type {}", error); panic!("Unexpected error type {}", error);
} }

View File

@ -1,416 +0,0 @@
use core::{convert::Infallible, fmt::Debug, time::Duration};
use std::time::Instant;
use thiserror::Error;
use crate::executable::Executable;
#[cfg(feature = "std")]
pub use std_mod::*;
#[derive(Debug, Default)]
pub struct SchedulingTable {
execution_frequency: Duration,
pub table: alloc::vec::Vec<u32>,
}
#[derive(Debug, Error)]
pub enum InvalidSlotError {
#[error("slot time is larger than the execution frequency")]
SlotTimeLargerThanFrequency,
#[error("slot time is smaller than previous slot")]
SmallerThanPreviousSlot {
slot_time_ms: u32,
prev_slot_time_ms: u32,
},
}
impl SchedulingTable {
pub fn new(execution_frequency: Duration) -> Self {
Self {
execution_frequency,
table: Default::default(),
}
}
pub fn add_slot(&mut self, relative_execution_time_ms: u32) -> Result<(), InvalidSlotError> {
if relative_execution_time_ms > self.execution_frequency.as_millis() as u32 {
return Err(InvalidSlotError::SlotTimeLargerThanFrequency);
}
if !self.table.is_empty() {
let prev_slot_ms = *self.table.last().unwrap();
if relative_execution_time_ms < prev_slot_ms {
return Err(InvalidSlotError::SmallerThanPreviousSlot {
slot_time_ms: relative_execution_time_ms,
prev_slot_time_ms: *self.table.last().unwrap(),
});
}
}
self.table.push(relative_execution_time_ms);
Ok(())
}
}
#[derive(Debug, Error)]
pub enum TaskWithSchedulingTableError {
#[error("scheudlig table error: {0}")]
InvalidSlot(#[from] InvalidSlotError),
#[error("task lock error")]
LockError,
#[error("task borrow error")]
BorrowError,
}
pub trait DeadlineMissedHandler {
fn deadline_missed_callback(&mut self, task_name: &'static str, op_code: i32);
}
pub trait TaskExecutor {
fn with_task<F: FnOnce(&mut dyn Executable<Error = Infallible>)>(&self, f: F);
}
#[cfg(feature = "std")]
pub mod std_mod {
use core::cell::RefCell;
use std::{
rc::Rc,
sync::{Arc, Mutex},
vec::Vec,
};
use super::*;
impl TaskExecutor for Arc<Mutex<dyn Executable<Error = Infallible> + Send>> {
fn with_task<F: FnOnce(&mut dyn Executable<Error = Infallible>)>(&self, f: F) {
let mut task = self.lock().unwrap();
f(&mut *task);
}
}
impl TaskExecutor for Rc<RefCell<dyn Executable<Error = Infallible>>> {
fn with_task<F: FnOnce(&mut dyn Executable<Error = Infallible>)>(&self, f: F) {
let mut task = self.borrow_mut();
f(&mut *task);
}
}
pub struct TaskWithOpCode<T: TaskExecutor> {
task: T,
op_code: i32,
}
pub struct TaskWithSchedulingTable<T: TaskExecutor> {
start_of_slot: Instant,
end_of_slot: Instant,
deadline_missed_ms_count: u32,
table: SchedulingTable,
tasks: Vec<TaskWithOpCode<T>>,
}
impl TaskWithSchedulingTable<Rc<RefCell<dyn Executable<Error = Infallible>>>> {
/// Add a new task to the scheduling table
///
/// The task needs to be wrapped inside [Rc] and [RefCell]. The task is not sendable and
/// needs to be created inside the target thread.
pub fn add_task(
&mut self,
relative_execution_time_ms: u32,
task: Rc<RefCell<dyn Executable<Error = Infallible>>>,
op_code: i32,
) -> Result<(), TaskWithSchedulingTableError> {
self.table.add_slot(relative_execution_time_ms)?;
self.tasks.push(TaskWithOpCode { task, op_code });
Ok(())
}
}
impl TaskWithSchedulingTable<Arc<Mutex<dyn Executable<Error = Infallible> + Send>>> {
/// Add a new task to the scheduling table
///
/// The task needs to be wrapped inside [Arc] and [Mutex], but the task can be sent to
/// a different thread.
pub fn add_task_sendable(
&mut self,
relative_execution_time_ms: u32,
task: Arc<Mutex<dyn Executable<Error = Infallible> + Send>>,
op_code: i32,
) -> Result<(), TaskWithSchedulingTableError> {
self.table.add_slot(relative_execution_time_ms)?;
self.tasks.push(TaskWithOpCode { task, op_code });
Ok(())
}
}
impl<T: TaskExecutor> TaskWithSchedulingTable<T> {
pub fn new(execution_frequency: Duration) -> Self {
Self {
start_of_slot: Instant::now(),
end_of_slot: Instant::now(),
deadline_missed_ms_count: 10,
table: SchedulingTable::new(execution_frequency),
tasks: Default::default(),
}
}
/// Can be used to set the start of the slot to the current time. This is useful if a custom
/// runner implementation is used instead of the [Self::run_one_task_cycle] method.
pub fn init_start_of_slot(&mut self) {
self.start_of_slot = Instant::now();
}
pub fn run_one_task_cycle(
&mut self,
deadline_missed_cb: &mut impl DeadlineMissedHandler,
) -> Result<(), TaskWithSchedulingTableError> {
self.end_of_slot = self.start_of_slot + self.table.execution_frequency;
for (&relative_execution_time_ms, task_with_op_code) in
self.table.table.iter().zip(self.tasks.iter_mut())
{
let scheduled_execution_time = self.start_of_slot
+ core::time::Duration::from_millis(relative_execution_time_ms as u64);
let now = Instant::now();
if now < scheduled_execution_time {
std::thread::sleep(scheduled_execution_time - now);
} else if (now - scheduled_execution_time).as_millis()
> self.deadline_missed_ms_count.into()
{
task_with_op_code.task.with_task(|task| {
deadline_missed_cb
.deadline_missed_callback(task.task_name(), task_with_op_code.op_code);
});
}
task_with_op_code.task.with_task(|task| {
// Unwrapping is okay here because we constrain the tasks to be infallible.
task.periodic_op(task_with_op_code.op_code).unwrap();
});
}
let now = Instant::now();
if now <= self.end_of_slot {
let diff = self.end_of_slot - now;
std::thread::sleep(diff);
self.start_of_slot = self.end_of_slot;
} else if now > self.end_of_slot + self.table.execution_frequency {
// We're getting strongly out of sync. Set the new start timt to now.
self.start_of_slot = now;
}
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use core::{cell::RefCell, convert::Infallible, time::Duration};
use std::{
println,
rc::Rc,
sync::{
mpsc::{self, TryRecvError},
Arc, Mutex,
},
time::Instant,
};
use crate::executable::{Executable, OpResult};
use super::{DeadlineMissedHandler, TaskWithSchedulingTable};
#[derive(Debug)]
pub struct CallInfo {
time: std::time::Instant,
op_code: i32,
}
pub struct Task1 {
called_queue: mpsc::Sender<CallInfo>,
}
impl Executable for Task1 {
type Error = Infallible;
fn task_name(&self) -> &'static str {
"Task1"
}
fn periodic_op(&mut self, op_code: i32) -> Result<OpResult, Self::Error> {
self.called_queue
.send(CallInfo {
time: Instant::now(),
op_code,
})
.unwrap();
Ok(OpResult::Ok)
}
}
pub struct Task2 {
called_queue: mpsc::Sender<CallInfo>,
}
impl Executable for Task2 {
type Error = Infallible;
fn task_name(&self) -> &'static str {
"Task2"
}
fn periodic_op(&mut self, op_code: i32) -> Result<OpResult, Self::Error> {
self.called_queue
.send(CallInfo {
time: Instant::now(),
op_code,
})
.unwrap();
Ok(OpResult::Ok)
}
}
#[derive(Default)]
pub struct DeadlineMissed {
call_count: u32,
}
impl DeadlineMissedHandler for DeadlineMissed {
fn deadline_missed_callback(&mut self, task_name: &'static str, _op_code: i32) {
println!("task name {task_name} missed the deadline");
self.call_count += 1;
}
}
#[test]
pub fn basic_test() {
let (tx_t1, rx_t1) = mpsc::channel();
let (tx_t2, rx_t2) = mpsc::channel();
let t1 = Task1 {
called_queue: tx_t1,
};
let t2 = Task2 {
called_queue: tx_t2,
};
let mut deadline_missed_cb = DeadlineMissed::default();
let mut exec_task = TaskWithSchedulingTable::new(Duration::from_millis(200));
let t1_first_slot = Rc::new(RefCell::new(t1));
let t1_second_slot = t1_first_slot.clone();
let t2_first_slot = Rc::new(RefCell::new(t2));
let t2_second_slot = t2_first_slot.clone();
exec_task.add_task(0, t1_first_slot, 0).unwrap();
exec_task.add_task(50, t1_second_slot, -1).unwrap();
exec_task.add_task(100, t2_first_slot, 1).unwrap();
exec_task.add_task(150, t2_second_slot, 2).unwrap();
let now = Instant::now();
exec_task.init_start_of_slot();
exec_task
.run_one_task_cycle(&mut deadline_missed_cb)
.unwrap();
let mut call_info = rx_t1.try_recv().unwrap();
assert_eq!(call_info.op_code, 0);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 30);
call_info = rx_t1.try_recv().unwrap();
assert_eq!(call_info.op_code, -1);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 80);
assert!(diff_call_to_start.as_millis() >= 50);
matches!(rx_t1.try_recv().unwrap_err(), TryRecvError::Empty);
call_info = rx_t2.try_recv().unwrap();
assert_eq!(call_info.op_code, 1);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 120);
assert!(diff_call_to_start.as_millis() >= 100);
call_info = rx_t2.try_recv().unwrap();
assert_eq!(call_info.op_code, 2);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 180);
assert!(diff_call_to_start.as_millis() >= 150);
matches!(rx_t2.try_recv().unwrap_err(), TryRecvError::Empty);
assert_eq!(deadline_missed_cb.call_count, 0);
}
#[test]
pub fn basic_test_with_arc_mutex() {
let (tx_t1, rx_t1) = mpsc::channel();
let (tx_t2, rx_t2) = mpsc::channel();
let t1 = Task1 {
called_queue: tx_t1,
};
let t2 = Task2 {
called_queue: tx_t2,
};
let mut deadline_missed_cb = DeadlineMissed::default();
let mut exec_task = TaskWithSchedulingTable::new(Duration::from_millis(200));
let t1_first_slot = Arc::new(Mutex::new(t1));
let t1_second_slot = t1_first_slot.clone();
let t2_first_slot = Arc::new(Mutex::new(t2));
let t2_second_slot = t2_first_slot.clone();
exec_task.add_task_sendable(0, t1_first_slot, 0).unwrap();
exec_task.add_task_sendable(50, t1_second_slot, -1).unwrap();
exec_task.add_task_sendable(100, t2_first_slot, 1).unwrap();
exec_task.add_task_sendable(150, t2_second_slot, 2).unwrap();
let now = Instant::now();
exec_task.init_start_of_slot();
exec_task
.run_one_task_cycle(&mut deadline_missed_cb)
.unwrap();
let mut call_info = rx_t1.try_recv().unwrap();
assert_eq!(call_info.op_code, 0);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 30);
call_info = rx_t1.try_recv().unwrap();
assert_eq!(call_info.op_code, -1);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 80);
assert!(diff_call_to_start.as_millis() >= 50);
matches!(rx_t1.try_recv().unwrap_err(), TryRecvError::Empty);
call_info = rx_t2.try_recv().unwrap();
assert_eq!(call_info.op_code, 1);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 120);
assert!(diff_call_to_start.as_millis() >= 100);
call_info = rx_t2.try_recv().unwrap();
assert_eq!(call_info.op_code, 2);
let diff_call_to_start = call_info.time - now;
assert!(diff_call_to_start.as_millis() < 180);
assert!(diff_call_to_start.as_millis() >= 150);
matches!(rx_t2.try_recv().unwrap_err(), TryRecvError::Empty);
assert_eq!(deadline_missed_cb.call_count, 0);
}
#[test]
pub fn basic_test_in_thread() {
let mut deadline_missed_cb = DeadlineMissed::default();
std::thread::spawn(move || {
let (tx_t1, _rx_t1) = mpsc::channel();
let t1 = Task1 {
called_queue: tx_t1,
};
// Need to construct this in the thread, the task table in not [Send]
let mut exec_task = TaskWithSchedulingTable::new(Duration::from_millis(200));
let t1_wrapper = Rc::new(RefCell::new(t1));
exec_task.add_task(0, t1_wrapper, 0).unwrap();
exec_task
.run_one_task_cycle(&mut deadline_missed_cb)
.unwrap();
});
let mut deadline_missed_cb = DeadlineMissed::default();
let (tx_t1, _rx_t1) = mpsc::channel();
let t1 = Task1 {
called_queue: tx_t1,
};
let mut exec_task_sendable = TaskWithSchedulingTable::new(Duration::from_millis(200));
exec_task_sendable
.add_task_sendable(0, Arc::new(Mutex::new(t1)), 1)
.unwrap();
std::thread::spawn(move || {
exec_task_sendable
.run_one_task_cycle(&mut deadline_missed_cb)
.unwrap();
});
}
}

250
satrs/src/seq_count.rs Normal file
View File

@ -0,0 +1,250 @@
use core::cell::Cell;
#[cfg(feature = "alloc")]
use dyn_clone::DynClone;
use paste::paste;
use spacepackets::MAX_SEQ_COUNT;
#[cfg(feature = "std")]
pub use stdmod::*;
/// Core trait for objects which can provide a sequence count.
///
/// The core functions are not mutable on purpose to allow easier usage with
/// static structs when using the interior mutability pattern. This can be achieved by using
/// [Cell], [core::cell::RefCell] or atomic types.
pub trait SequenceCountProviderCore<Raw> {
fn get(&self) -> Raw;
fn increment(&self);
fn get_and_increment(&self) -> Raw {
let val = self.get();
self.increment();
val
}
}
/// Extension trait which allows cloning a sequence count provider after it was turned into
/// a trait object.
#[cfg(feature = "alloc")]
pub trait SequenceCountProvider<Raw>: SequenceCountProviderCore<Raw> + DynClone {}
#[cfg(feature = "alloc")]
dyn_clone::clone_trait_object!(SequenceCountProvider<u16>);
#[cfg(feature = "alloc")]
impl<T, Raw> SequenceCountProvider<Raw> for T where T: SequenceCountProviderCore<Raw> + Clone {}
#[derive(Clone)]
pub struct SeqCountProviderSimple<T: Copy> {
seq_count: Cell<T>,
max_val: T,
}
macro_rules! impl_for_primitives {
($($ty: ident,)+) => {
$(
paste! {
impl SeqCountProviderSimple<$ty> {
pub fn [<new_custom_max_val_ $ty>](max_val: $ty) -> Self {
Self {
seq_count: Cell::new(0),
max_val,
}
}
pub fn [<new_ $ty>]() -> Self {
Self {
seq_count: Cell::new(0),
max_val: $ty::MAX
}
}
}
impl Default for SeqCountProviderSimple<$ty> {
fn default() -> Self {
Self::[<new_ $ty>]()
}
}
impl SequenceCountProviderCore<$ty> for SeqCountProviderSimple<$ty> {
fn get(&self) -> $ty {
self.seq_count.get()
}
fn increment(&self) {
self.get_and_increment();
}
fn get_and_increment(&self) -> $ty {
let curr_count = self.seq_count.get();
if curr_count == self.max_val {
self.seq_count.set(0);
} else {
self.seq_count.set(curr_count + 1);
}
curr_count
}
}
}
)+
}
}
impl_for_primitives!(u8, u16, u32, u64,);
/// This is a sequence count provider which wraps around at [MAX_SEQ_COUNT].
#[derive(Clone)]
pub struct CcsdsSimpleSeqCountProvider {
provider: SeqCountProviderSimple<u16>,
}
impl Default for CcsdsSimpleSeqCountProvider {
fn default() -> Self {
Self {
provider: SeqCountProviderSimple::new_custom_max_val_u16(MAX_SEQ_COUNT),
}
}
}
impl SequenceCountProviderCore<u16> for CcsdsSimpleSeqCountProvider {
delegate::delegate! {
to self.provider {
fn get(&self) -> u16;
fn increment(&self);
fn get_and_increment(&self) -> u16;
}
}
}
#[cfg(feature = "std")]
pub mod stdmod {
use super::*;
use std::sync::{Arc, Mutex};
macro_rules! sync_clonable_seq_counter_impl {
($($ty: ident,)+) => {
$(paste! {
/// These sequence counters can be shared between threads and can also be
/// configured to wrap around at specified maximum values. Please note that
/// that the API provided by this class will not panic und [Mutex] lock errors,
/// but it will yield 0 for the getter functions.
#[derive(Clone, Default)]
pub struct [<SeqCountProviderSync $ty:upper>] {
seq_count: Arc<Mutex<$ty>>,
max_val: $ty
}
impl [<SeqCountProviderSync $ty:upper>] {
pub fn new() -> Self {
Self::new_with_max_val($ty::MAX)
}
pub fn new_with_max_val(max_val: $ty) -> Self {
Self {
seq_count: Arc::default(),
max_val
}
}
}
impl SequenceCountProviderCore<$ty> for [<SeqCountProviderSync $ty:upper>] {
fn get(&self) -> $ty {
match self.seq_count.lock() {
Ok(counter) => *counter,
Err(_) => 0
}
}
fn increment(&self) {
self.get_and_increment();
}
fn get_and_increment(&self) -> $ty {
match self.seq_count.lock() {
Ok(mut counter) => {
let val = *counter;
if val == self.max_val {
*counter = 0;
} else {
*counter += 1;
}
val
}
Err(_) => 0,
}
}
}
})+
}
}
sync_clonable_seq_counter_impl!(u8, u16, u32, u64,);
}
#[cfg(test)]
mod tests {
use crate::seq_count::{
CcsdsSimpleSeqCountProvider, SeqCountProviderSimple, SeqCountProviderSyncU8,
SequenceCountProviderCore,
};
use spacepackets::MAX_SEQ_COUNT;
#[test]
fn test_u8_counter() {
let u8_counter = SeqCountProviderSimple::<u8>::default();
assert_eq!(u8_counter.get(), 0);
assert_eq!(u8_counter.get_and_increment(), 0);
assert_eq!(u8_counter.get_and_increment(), 1);
assert_eq!(u8_counter.get(), 2);
}
#[test]
fn test_u8_counter_overflow() {
let u8_counter = SeqCountProviderSimple::new_u8();
for _ in 0..256 {
u8_counter.increment();
}
assert_eq!(u8_counter.get(), 0);
}
#[test]
fn test_ccsds_counter() {
let ccsds_counter = CcsdsSimpleSeqCountProvider::default();
assert_eq!(ccsds_counter.get(), 0);
assert_eq!(ccsds_counter.get_and_increment(), 0);
assert_eq!(ccsds_counter.get_and_increment(), 1);
assert_eq!(ccsds_counter.get(), 2);
}
#[test]
fn test_ccsds_counter_overflow() {
let ccsds_counter = CcsdsSimpleSeqCountProvider::default();
for _ in 0..MAX_SEQ_COUNT + 1 {
ccsds_counter.increment();
}
assert_eq!(ccsds_counter.get(), 0);
}
#[test]
fn test_atomic_ref_counters() {
let sync_u8_counter = SeqCountProviderSyncU8::new();
assert_eq!(sync_u8_counter.get(), 0);
assert_eq!(sync_u8_counter.get_and_increment(), 0);
assert_eq!(sync_u8_counter.get_and_increment(), 1);
assert_eq!(sync_u8_counter.get(), 2);
}
#[test]
fn test_atomic_ref_counters_overflow() {
let sync_u8_counter = SeqCountProviderSyncU8::new();
for _ in 0..u8::MAX as u16 + 1 {
sync_u8_counter.increment();
}
assert_eq!(sync_u8_counter.get(), 0);
}
#[test]
fn test_atomic_ref_counters_overflow_custom_max_val() {
let sync_u8_counter = SeqCountProviderSyncU8::new_with_max_val(128);
for _ in 0..129 {
sync_u8_counter.increment();
}
assert_eq!(sync_u8_counter.get(), 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@ use crate::{
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use alloc_mod::*; pub use alloc_mod::*;
use core::fmt::Debug;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
use spacepackets::{ use spacepackets::{
@ -171,7 +170,7 @@ where
} }
/// Helper trait for any generic (static) store which allows storing raw or CCSDS packets. /// Helper trait for any generic (static) store which allows storing raw or CCSDS packets.
pub trait CcsdsPacketPool: Debug { pub trait CcsdsPacketPool {
fn add_ccsds_tc(&mut self, _: &SpHeader, tc_raw: &[u8]) -> Result<PoolAddr, PoolError> { fn add_ccsds_tc(&mut self, _: &SpHeader, tc_raw: &[u8]) -> Result<PoolAddr, PoolError> {
self.add_raw_tc(tc_raw) self.add_raw_tc(tc_raw)
} }
@ -191,7 +190,7 @@ pub trait PusTmPool {
} }
/// Generic trait for any sender component able to send packets stored inside a pool structure. /// Generic trait for any sender component able to send packets stored inside a pool structure.
pub trait PacketInPoolSender: Debug + Send { pub trait PacketInPoolSender: Send {
fn send_packet( fn send_packet(
&self, &self,
sender_id: ComponentId, sender_id: ComponentId,
@ -236,7 +235,7 @@ pub mod std_mod {
/// Newtype wrapper around the [SharedStaticMemoryPool] to enable extension helper traits on /// Newtype wrapper around the [SharedStaticMemoryPool] to enable extension helper traits on
/// top of the regular shared memory pool API. /// top of the regular shared memory pool API.
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct SharedPacketPool(pub SharedStaticMemoryPool); pub struct SharedPacketPool(pub SharedStaticMemoryPool);
impl SharedPacketPool { impl SharedPacketPool {
@ -288,6 +287,7 @@ pub mod std_mod {
} }
} }
#[cfg(feature = "std")]
impl PacketSenderRaw for mpsc::Sender<PacketAsVec> { impl PacketSenderRaw for mpsc::Sender<PacketAsVec> {
type Error = GenericSendError; type Error = GenericSendError;
@ -297,6 +297,7 @@ pub mod std_mod {
} }
} }
#[cfg(feature = "std")]
impl PacketSenderRaw for mpsc::SyncSender<PacketAsVec> { impl PacketSenderRaw for mpsc::SyncSender<PacketAsVec> {
type Error = GenericSendError; type Error = GenericSendError;
@ -361,7 +362,7 @@ pub mod std_mod {
/// This is the primary structure used to send packets stored in a dedicated memory pool /// This is the primary structure used to send packets stored in a dedicated memory pool
/// structure. /// structure.
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct PacketSenderWithSharedPool< pub struct PacketSenderWithSharedPool<
Sender: PacketInPoolSender = mpsc::SyncSender<PacketInPool>, Sender: PacketInPoolSender = mpsc::SyncSender<PacketInPool>,
PacketPool: CcsdsPacketPool = SharedPacketPool, PacketPool: CcsdsPacketPool = SharedPacketPool,

Some files were not shown because too many files have changed in this diff Show More