Compare commits
30 Commits
018c2593f8
...
serializat
Author | SHA1 | Date | |
---|---|---|---|
2eaa78dfbc | |||
a6d9bee5df | |||
a77bbfa953 | |||
4c67bcdde1 | |||
a710b30013 | |||
29783b2b07 | |||
2a2a3a3eab
|
|||
2507469e68
|
|||
b4febefa33 | |||
fe60cb9ccf | |||
27e88ed7f7
|
|||
295fed9a72
|
|||
8e89c8dd66
|
|||
cb0a65c4d4 | |||
3db54da3df | |||
15fcb17363 | |||
8728c7ebea | |||
7606767f63 | |||
37b32a9008 | |||
9e096193dd | |||
43bd77eef0
|
|||
a4888bce01
|
|||
6e5b70af34
|
|||
d1476eb770
|
|||
783388aa6f
|
|||
4a8db6b26a
|
|||
b86c2eb1d1
|
|||
fe4126f7e2
|
|||
c20163b10a
|
|||
b970154488 |
@ -10,5 +10,7 @@ members = [
|
||||
|
||||
exclude = [
|
||||
"embedded-examples/stm32f3-disco-rtic",
|
||||
"embedded-examples/stm32h7-nucleo-rtic",
|
||||
"embedded-examples/stm32h7-rtic",
|
||||
"serialization-prototyping",
|
||||
]
|
||||
|
||||
|
11
README.md
11
README.md
@ -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):
|
||||
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.
|
||||
* [`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):
|
||||
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
|
||||
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
|
||||
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
|
||||
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
|
||||
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/).
|
||||
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).
|
||||
- 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
|
||||
|
||||
|
3
docs.sh
3
docs.sh
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --all-features --open
|
240
embedded-examples/stm32f3-disco-rtic/Cargo.lock
generated
240
embedded-examples/stm32f3-disco-rtic/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "accelerometer"
|
||||
@ -12,10 +12,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
name = "atomic-polyfill"
|
||||
version = "1.0.3"
|
||||
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]]
|
||||
name = "bare-metal"
|
||||
@ -68,7 +77,7 @@ version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
|
||||
dependencies = [
|
||||
"rustc_version 0.4.1",
|
||||
"rustc_version 0.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -79,9 +88,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.39"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@ -94,12 +103,8 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
version = "0.2.3"
|
||||
source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
@ -116,22 +121,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.7.5"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
||||
checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.7.5"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
||||
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -160,15 +165,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@ -176,33 +181,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.10"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
||||
checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"defmt-macros",
|
||||
@ -220,22 +225,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "0.4.0"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
||||
checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error2",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "0.4.1"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
||||
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@ -260,7 +265,7 @@ checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -282,7 +287,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -310,15 +315,6 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "embedded-time"
|
||||
version = "0.12.1"
|
||||
@ -330,23 +326,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "enumset"
|
||||
version = "1.1.5"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293"
|
||||
checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
|
||||
dependencies = [
|
||||
"enumset_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumset_derive"
|
||||
version = "0.10.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242"
|
||||
checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -372,21 +368,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@ -430,9 +426,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
@ -452,9 +448,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
version = "2.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -559,22 +555,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.3"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
||||
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.3"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -595,9 +591,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@ -606,47 +602,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.10.0"
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -662,14 +654,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rtic"
|
||||
version = "2.1.2"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1"
|
||||
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"bare-metal 1.0.0",
|
||||
"cortex-m",
|
||||
"critical-section",
|
||||
"portable-atomic",
|
||||
"rtic-core",
|
||||
"rtic-macros",
|
||||
]
|
||||
@ -691,40 +683,38 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
||||
|
||||
[[package]]
|
||||
name = "rtic-macros"
|
||||
version = "2.1.2"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80"
|
||||
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro-error2",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-monotonics"
|
||||
version = "2.0.3"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748"
|
||||
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"cfg-if",
|
||||
"cortex-m",
|
||||
"embedded-hal 1.0.0",
|
||||
"fugit",
|
||||
"portable-atomic",
|
||||
"rtic-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-time"
|
||||
version = "2.0.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614"
|
||||
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"fugit",
|
||||
"futures-util",
|
||||
"rtic-common",
|
||||
]
|
||||
@ -740,11 +730,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -753,7 +743,7 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "866fcae3b683ccc37b5ad77982483a0ee01d5dc408dea5aad2117ad404b60fe1"
|
||||
dependencies = [
|
||||
"cobs 0.2.3",
|
||||
"cobs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crc",
|
||||
"defmt",
|
||||
"delegate",
|
||||
@ -779,7 +769,7 @@ dependencies = [
|
||||
name = "satrs-stm32f3-disco-rtic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cobs 0.3.0",
|
||||
"cobs 0.2.3 (git+https://github.com/robamu/cobs.rs.git?branch=all_features)",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"cortex-m-semihosting",
|
||||
@ -808,9 +798,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.25"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
@ -932,9 +922,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
version = "2.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -943,22 +933,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -969,9 +959,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "usb-device"
|
||||
@ -987,9 +977,9 @@ checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
@ -1008,9 +998,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
@ -1018,11 +1008,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
@ -22,11 +22,12 @@ version = "2"
|
||||
features = ["thumbv7-backend"]
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "2"
|
||||
version = "1"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[dependencies.cobs]
|
||||
version = "0.3"
|
||||
git = "https://github.com/robamu/cobs.rs.git"
|
||||
branch = "all_features"
|
||||
default-features = false
|
||||
|
||||
[dependencies.stm32f3xx-hal]
|
||||
|
@ -15,8 +15,8 @@ use rtic::app;
|
||||
|
||||
use heapless::{mpmc::Q8, Vec};
|
||||
#[allow(unused_imports)]
|
||||
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32};
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use rtic_monotonics::systick::fugit::{MillisDurationU32, TimerInstantU32};
|
||||
use rtic_monotonics::systick::ExtU32;
|
||||
use satrs::seq_count::SequenceCountProviderCore;
|
||||
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
|
||||
use stm32f3xx_hal::dma::dma1;
|
||||
@ -245,6 +245,8 @@ pub fn convert_pus_tc_to_request(
|
||||
mod app {
|
||||
use super::*;
|
||||
use core::slice::Iter;
|
||||
use rtic_monotonics::systick::Systick;
|
||||
use rtic_monotonics::Monotonic;
|
||||
use satrs::pus::verification::{TcStateStarted, VerificationReportCreator};
|
||||
use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE};
|
||||
#[allow(unused_imports)]
|
||||
@ -257,8 +259,6 @@ mod app {
|
||||
#[allow(dead_code)]
|
||||
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
|
||||
|
||||
systick_monotonic!(Mono, 1000);
|
||||
|
||||
#[shared]
|
||||
struct Shared {
|
||||
blink_freq: MillisDurationU32,
|
||||
@ -279,7 +279,8 @@ mod app {
|
||||
let mut rcc = cx.device.RCC.constrain();
|
||||
|
||||
// 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 clocks = rcc
|
||||
@ -393,7 +394,7 @@ mod app {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
|
||||
if let Some(last_completed) = last_completed {
|
||||
let elapsed_ms = (Mono::now() - last_completed).to_millis();
|
||||
let elapsed_ms = (Systick::now() - last_completed).to_millis();
|
||||
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
|
||||
Mono::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis()).await;
|
||||
Systick::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis())
|
||||
.await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check for completion after 1 ms
|
||||
Mono::delay(1.millis()).await;
|
||||
Systick::delay(1.millis()).await;
|
||||
continue;
|
||||
}
|
||||
if let Some(vec) = TM_REQUESTS.dequeue() {
|
||||
@ -457,11 +459,11 @@ mod app {
|
||||
UartTxState::Transmitting(_) => (),
|
||||
});
|
||||
// Check for completion after 1 ms
|
||||
Mono::delay(1.millis()).await;
|
||||
Systick::delay(1.millis()).await;
|
||||
continue;
|
||||
}
|
||||
// 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 }));
|
||||
// We cache the last completed time to ensure that there is a minimum delay between consecutive
|
||||
// transferred packets.
|
||||
tx_shared.last_completed = Some(Mono::now());
|
||||
tx_shared.last_completed = Some(Systick::now());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
334
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
334
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
@ -1,12 +1,21 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# 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]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
@ -14,7 +23,7 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
"rustc_version 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -23,12 +32,6 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitfield"
|
||||
version = "0.13.2"
|
||||
@ -61,18 +64,8 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-default"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
|
||||
version = "0.2.3"
|
||||
source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
@ -89,22 +82,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.7.5"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
||||
checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.7.5"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
||||
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -133,15 +126,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.10"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
||||
checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"defmt-macros",
|
||||
@ -159,22 +152,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "0.4.0"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
||||
checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error2",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "0.4.1"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
||||
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@ -199,41 +192,39 @@ checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "delegate"
|
||||
version = "0.13.2"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "297806318ef30ad066b15792a8372858020ae3ca2e414ee6c2133b1eb9e9e945"
|
||||
checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-new"
|
||||
version = "0.7.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
|
||||
checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-alloc"
|
||||
version = "0.6.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd"
|
||||
checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815"
|
||||
dependencies = [
|
||||
"const-default",
|
||||
"critical-section",
|
||||
"linked_list_allocator",
|
||||
"rlsf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -309,21 +300,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@ -337,6 +328,15 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
@ -348,9 +348,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
version = "0.14.5"
|
||||
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]]
|
||||
name = "heapless"
|
||||
@ -359,32 +372,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"defmt",
|
||||
"hash32",
|
||||
"hash32 0.3.1",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
version = "2.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "linked_list_allocator"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "managed"
|
||||
version = "0.8.0"
|
||||
@ -417,22 +434,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.3"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
||||
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.3"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -453,9 +470,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@ -465,72 +482,62 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.10.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"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]]
|
||||
name = "rtic"
|
||||
version = "2.1.2"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1"
|
||||
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"bare-metal 1.0.0",
|
||||
"cortex-m",
|
||||
"critical-section",
|
||||
"portable-atomic",
|
||||
"rtic-core",
|
||||
"rtic-macros",
|
||||
]
|
||||
@ -552,27 +559,28 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
||||
|
||||
[[package]]
|
||||
name = "rtic-macros"
|
||||
version = "2.1.2"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80"
|
||||
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro-error2",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-monotonics"
|
||||
version = "2.0.3"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748"
|
||||
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"cfg-if",
|
||||
"cortex-m",
|
||||
"embedded-hal 1.0.0",
|
||||
"fugit",
|
||||
"portable-atomic",
|
||||
"rtic-time",
|
||||
]
|
||||
|
||||
@ -587,21 +595,18 @@ dependencies = [
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"embedded-hal-bus",
|
||||
"heapless",
|
||||
"heapless 0.8.0",
|
||||
"portable-atomic",
|
||||
"rtic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-time"
|
||||
version = "2.0.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614"
|
||||
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"fugit",
|
||||
"futures-util",
|
||||
"rtic-common",
|
||||
]
|
||||
@ -612,7 +617,16 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
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]]
|
||||
@ -624,21 +638,20 @@ dependencies = [
|
||||
"defmt",
|
||||
"delegate",
|
||||
"derive-new",
|
||||
"heapless",
|
||||
"heapless 0.7.17",
|
||||
"num-traits",
|
||||
"num_enum",
|
||||
"paste",
|
||||
"satrs-shared",
|
||||
"smallvec",
|
||||
"spacepackets",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "satrs-shared"
|
||||
version = "0.2.1"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f10b962c131d0bcb0de06fefa5d4716cbd7f836167f90229f7f38405dc573bd3"
|
||||
checksum = "6042477018c2d43fffccaaa5099bc299a58485139b4d31c5b276889311e474f1"
|
||||
dependencies = [
|
||||
"spacepackets",
|
||||
]
|
||||
@ -663,6 +676,12 @@ dependencies = [
|
||||
"stm32h7xx-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
@ -672,6 +691,12 @@ dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
@ -694,26 +719,33 @@ dependencies = [
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"defmt",
|
||||
"heapless",
|
||||
"heapless 0.8.0",
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spacepackets"
|
||||
version = "0.13.0"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "591d42bfa6af2ab308e5efd8f0870e2d0eb5dd19a141bdcb7da731f5ff9cc11c"
|
||||
checksum = "e85574d113a06312010c0ba51aadccd4ba2806231ebe9a49fc6473d0534d8696"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"defmt",
|
||||
"delegate",
|
||||
"num-traits",
|
||||
"num_enum",
|
||||
"paste",
|
||||
"thiserror",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@ -752,19 +784,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@ -778,9 +797,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
version = "2.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -789,35 +808,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
@ -825,6 +838,12 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
@ -842,20 +861,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.14"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.14"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
"syn 2.0.64",
|
||||
]
|
||||
|
@ -21,11 +21,11 @@ defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
cortex-m-semihosting = "0.5.0"
|
||||
stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] }
|
||||
embedded-alloc = "0.6"
|
||||
embedded-alloc = "0.5"
|
||||
rtic-sync = { version = "1", features = ["defmt-03"] }
|
||||
|
||||
[dependencies.smoltcp]
|
||||
version = "0.11"
|
||||
version = "0.11.0"
|
||||
default-features = false
|
||||
features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"]
|
||||
|
||||
@ -34,7 +34,7 @@ version = "2"
|
||||
features = ["thumbv7-backend"]
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "2"
|
||||
version = "1"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[dependencies.satrs]
|
||||
|
@ -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)
|
||||
|
@ -3,8 +3,8 @@
|
||||
extern crate alloc;
|
||||
|
||||
use rtic::app;
|
||||
use rtic_monotonics::fugit::TimerInstantU32;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use rtic_monotonics::systick::Systick;
|
||||
use rtic_monotonics::Monotonic;
|
||||
use satrs::pool::{PoolAddr, PoolProvider, StaticHeaplessMemoryPool};
|
||||
use satrs::static_subpool;
|
||||
// global logger + panicking-behavior + memory layout
|
||||
@ -13,7 +13,7 @@ use smoltcp::socket::udp::UdpMetadata;
|
||||
use smoltcp::socket::{dhcpv4, udp};
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use embedded_alloc::LlffHeap as Heap;
|
||||
use embedded_alloc::Heap;
|
||||
use smoltcp::iface::{Config, Interface, SocketHandle, SocketSet, SocketStorage};
|
||||
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};
|
||||
use stm32h7xx_hal::ethernet;
|
||||
@ -32,8 +32,6 @@ pub type TcSourceRx = rtic_sync::channel::Receiver<'static, PoolAddr, TC_SOURCE_
|
||||
#[global_allocator]
|
||||
static HEAP: Heap = Heap::empty();
|
||||
|
||||
systick_monotonic!(Mono, 1000);
|
||||
|
||||
// We place the memory pool buffers inside the larger AXISRAM.
|
||||
pub const SUBPOOL_SMALL_NUM_BLOCKS: u16 = 32;
|
||||
pub const SUBPOOL_SMALL_BLOCK_SIZE: usize = 32;
|
||||
@ -74,9 +72,7 @@ impl Net {
|
||||
let mut iface = Interface::new(
|
||||
config,
|
||||
&mut ethdev,
|
||||
smoltcp::time::Instant::from_millis(
|
||||
(Mono::now() - TimerInstantU32::<1000>::from_ticks(0)).to_millis(),
|
||||
),
|
||||
smoltcp::time::Instant::from_millis((Systick::now() - Systick::ZERO).to_millis()),
|
||||
);
|
||||
// Create sockets
|
||||
let dhcp_socket = dhcpv4::Socket::new();
|
||||
@ -95,7 +91,7 @@ impl Net {
|
||||
/// Polls on the ethernet interface. You should refer to the smoltcp
|
||||
/// documentation for poll() to understand how to call poll efficiently
|
||||
pub fn poll<'a>(&mut self, sockets: &'a mut SocketSet) -> bool {
|
||||
let uptime = Mono::now() - TimerInstantU32::<1000>::from_ticks(0);
|
||||
let uptime = Systick::now() - Systick::ZERO;
|
||||
let timestamp = smoltcp::time::Instant::from_millis(uptime.to_millis());
|
||||
|
||||
self.iface.poll(timestamp, &mut self.ethdev, sockets)
|
||||
@ -207,7 +203,8 @@ mod app {
|
||||
use core::ptr::addr_of_mut;
|
||||
|
||||
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 stm32h7xx_hal::ethernet::{EthernetMAC, PHY};
|
||||
use stm32h7xx_hal::gpio::{Output, Pin};
|
||||
@ -259,7 +256,12 @@ mod app {
|
||||
.freeze(pwrcfg, &cx.device.SYSCFG);
|
||||
|
||||
// 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
|
||||
// good for.
|
||||
@ -375,24 +377,24 @@ mod app {
|
||||
|
||||
shared_pool
|
||||
.grow(
|
||||
SUBPOOL_SMALL.get_mut().unwrap(),
|
||||
SUBPOOL_SMALL_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_SMALL.assume_init_mut() },
|
||||
unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
|
||||
SUBPOOL_SMALL_NUM_BLOCKS,
|
||||
true,
|
||||
)
|
||||
.expect("growing heapless memory pool failed");
|
||||
shared_pool
|
||||
.grow(
|
||||
SUBPOOL_MEDIUM.get_mut().unwrap(),
|
||||
SUBPOOL_MEDIUM_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_MEDIUM.assume_init_mut() },
|
||||
unsafe { SUBPOOL_MEDIUM_SIZES.assume_init_mut() },
|
||||
SUBPOOL_MEDIUM_NUM_BLOCKS,
|
||||
true,
|
||||
)
|
||||
.expect("growing heapless memory pool failed");
|
||||
shared_pool
|
||||
.grow(
|
||||
SUBPOOL_LARGE.get_mut().unwrap(),
|
||||
SUBPOOL_LARGE_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_LARGE.assume_init_mut() },
|
||||
unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
|
||||
SUBPOOL_LARGE_NUM_BLOCKS,
|
||||
true,
|
||||
)
|
||||
@ -401,7 +403,7 @@ mod app {
|
||||
// Set up global allocator. Use AXISRAM for the heap.
|
||||
#[link_section = ".axisram"]
|
||||
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");
|
||||
blinky::spawn().expect("spawning blink task failed");
|
||||
@ -433,7 +435,7 @@ mod app {
|
||||
leds.led1.toggle();
|
||||
leds.led2.toggle();
|
||||
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
||||
Mono::delay(current_blink_freq).await;
|
||||
Systick::delay(current_blink_freq).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,7 +457,7 @@ mod app {
|
||||
cx.shared.eth_link_up.lock(|link_up| *link_up = false);
|
||||
defmt::info!("Ethernet link down");
|
||||
}
|
||||
Mono::delay(100.millis()).await;
|
||||
Systick::delay(100.millis()).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,7 +483,7 @@ mod app {
|
||||
cx.local.udp.poll(sockets, pool);
|
||||
})
|
||||
});
|
||||
Mono::delay(40.millis()).await;
|
||||
Systick::delay(40.millis()).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 |
@ -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:
|
||||
|
||||
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.
|
||||
2. A Getting-Started workshop where a small On-Board Software is built from scratch using
|
||||
sat-rs components.
|
||||
|
||||
# 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)
|
||||
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 example application. The [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim)
|
||||
applicatin complements the example application and can be used to simulate some physical devices
|
||||
for the `satrs-example` device handlers.
|
||||
the example application.
|
||||
|
||||
# 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/).
|
||||
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).
|
||||
- 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/).
|
||||
|
@ -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/).
|
||||
|
||||
# [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
|
||||
|
@ -8,18 +8,18 @@ homepage = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
||||
repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
||||
|
||||
[dependencies]
|
||||
fern = "0.7"
|
||||
fern = "0.6"
|
||||
chrono = "0.4"
|
||||
log = "0.4"
|
||||
crossbeam-channel = "0.5"
|
||||
delegate = "0.13"
|
||||
zerocopy = "0.8"
|
||||
delegate = "0.10"
|
||||
zerocopy = "0.6"
|
||||
csv = "1"
|
||||
num_enum = "0.7"
|
||||
thiserror = "2"
|
||||
thiserror = "1"
|
||||
lazy_static = "1"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
derive-new = "0.7"
|
||||
derive-new = "0.5"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
|
@ -73,22 +73,3 @@ the `simpleclient`:
|
||||
```
|
||||
|
||||
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`.
|
||||
|
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Example client for the sat-rs example application"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
@ -12,7 +12,7 @@ authors = [
|
||||
{name = "Robin Mueller", email = "robin.mueller.m@gmail.com"},
|
||||
]
|
||||
dependencies = [
|
||||
"tmtccmd~=8.1",
|
||||
"tmtccmd~=8.0",
|
||||
"pydantic~=2.7"
|
||||
]
|
||||
|
||||
|
@ -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
|
@ -12,7 +12,7 @@ from pytmtc.pus_tc import create_cmd_definition_tree
|
||||
|
||||
class SatrsConfigHook(HookBase):
|
||||
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]:
|
||||
from tmtccmd.config.com import (
|
||||
|
@ -4,7 +4,7 @@ from spacepackets.ecss.pus_3_hk import Subservice
|
||||
from spacepackets.ecss import PusTm
|
||||
|
||||
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__)
|
||||
|
@ -17,9 +17,8 @@ from tmtccmd.tmtc import (
|
||||
)
|
||||
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.acs.mgms import create_mgm_cmds
|
||||
from pytmtc.mgms import create_mgm_cmds
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -68,6 +67,7 @@ class TcHandler(TcHandlerBase):
|
||||
|
||||
|
||||
def create_cmd_definition_tree() -> CmdTreeNode:
|
||||
|
||||
root_node = CmdTreeNode.root_node()
|
||||
|
||||
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(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
|
||||
|
||||
|
||||
|
@ -19,7 +19,7 @@ use satrs::mode::{
|
||||
};
|
||||
use satrs::pus::{EcssTmSender, PusTmVariant};
|
||||
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||
use satrs_example::config::components::{NO_SENDER, PUS_MODE_SERVICE};
|
||||
use satrs_example::config::components::PUS_MODE_SERVICE;
|
||||
|
||||
use crate::hk::PusHkHelper;
|
||||
use crate::pus::hk::{HkReply, HkReplyVariant};
|
||||
@ -132,7 +132,6 @@ pub struct MgmData {
|
||||
pub struct MpscModeLeafInterface {
|
||||
pub request_rx: mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
pub reply_to_pus_tx: mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
#[allow(dead_code)]
|
||||
pub reply_to_parent_tx: mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
}
|
||||
|
||||
@ -386,7 +385,6 @@ impl<
|
||||
&mut self,
|
||||
requestor: MessageMetadata,
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
_forced: bool,
|
||||
) -> Result<(), satrs::mode::ModeError> {
|
||||
log::info!(
|
||||
"{}: transitioning to mode {:?}",
|
||||
@ -423,12 +421,9 @@ impl<
|
||||
self.mode_helpers.target = None;
|
||||
self.announce_mode(requestor, false);
|
||||
if let Some(requestor) = requestor {
|
||||
if requestor.sender_id() == NO_SENDER {
|
||||
return Ok(());
|
||||
}
|
||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
||||
log::warn!(
|
||||
"can not send back mode reply to sender {:x}",
|
||||
"can not send back mode reply to sender {}",
|
||||
requestor.sender_id()
|
||||
);
|
||||
} else {
|
||||
@ -537,7 +532,7 @@ mod tests {
|
||||
hk_reply_rx,
|
||||
handler: MgmHandlerLis3Mdl::new(
|
||||
UniqueApidTargetId::new(Apid::Acs as u16, 1),
|
||||
"TEST_MGM",
|
||||
"test-mgm",
|
||||
mode_interface,
|
||||
composite_request_rx,
|
||||
hk_reply_tx,
|
||||
@ -576,10 +571,7 @@ mod tests {
|
||||
.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,
|
||||
},
|
||||
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)),
|
||||
))
|
||||
.expect("failed to send mode request");
|
||||
testbench.handler.periodic_operation();
|
||||
@ -637,10 +629,7 @@ mod tests {
|
||||
.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,
|
||||
},
|
||||
ModeRequest::SetMode(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)),
|
||||
))
|
||||
.expect("failed to send mode request");
|
||||
testbench.handler.periodic_operation();
|
||||
|
@ -3,7 +3,7 @@
|
||||
use crossbeam_channel::{bounded, Receiver, Sender};
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
use std::thread;
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16};
|
||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||
|
||||
trait FieldDataProvider: Send {
|
||||
fn get_data(&self) -> &[u8];
|
||||
@ -35,7 +35,7 @@ struct ExampleMgmSet {
|
||||
temperature: u16,
|
||||
}
|
||||
|
||||
#[derive(FromBytes, IntoBytes, Immutable, Unaligned)]
|
||||
#[derive(FromBytes, AsBytes, Unaligned)]
|
||||
#[repr(C)]
|
||||
struct ExampleMgmSetZc {
|
||||
mgm_vec: [u8; 12],
|
||||
|
@ -122,7 +122,7 @@ pub mod mode_err {
|
||||
}
|
||||
|
||||
pub mod components {
|
||||
use satrs::{request::UniqueApidTargetId, ComponentId};
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
use strum::EnumIter;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, EnumIter)]
|
||||
@ -184,7 +184,6 @@ pub mod components {
|
||||
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 pool {
|
||||
|
@ -88,7 +88,6 @@ impl PowerSwitcherCommandSender<PcduSwitch> for PowerSwitchHelper {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(new)]
|
||||
pub struct SwitchRequestInfo {
|
||||
pub requestor_info: MessageMetadata,
|
||||
@ -100,7 +99,6 @@ pub struct SwitchRequestInfo {
|
||||
pub struct TestSwitchHelper {
|
||||
pub switch_requests: RefCell<VecDeque<SwitchRequestInfo>>,
|
||||
pub switch_info_requests: RefCell<VecDeque<PcduSwitch>>,
|
||||
#[allow(dead_code)]
|
||||
pub switch_delay_request_count: u32,
|
||||
pub next_switch_delay: Duration,
|
||||
pub switch_map: RefCell<SwitchMapWrapper>,
|
||||
|
@ -15,10 +15,7 @@ use satrs::{
|
||||
request::{GenericMessage, MessageMetadata, UniqueApidTargetId},
|
||||
spacepackets::ByteConversionError,
|
||||
};
|
||||
use satrs_example::{
|
||||
config::components::{NO_SENDER, PUS_MODE_SERVICE},
|
||||
DeviceMode, TimestampHelper,
|
||||
};
|
||||
use satrs_example::{config::components::PUS_MODE_SERVICE, DeviceMode, TimestampHelper};
|
||||
use satrs_minisim::{
|
||||
eps::{
|
||||
PcduReply, PcduRequest, PcduSwitch, SwitchMap, SwitchMapBinaryWrapper, SwitchMapWrapper,
|
||||
@ -63,9 +60,9 @@ impl SerialInterface for SerialInterfaceToSim {
|
||||
type 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
|
||||
.send(SimRequest::new_with_epoch_time(request))
|
||||
.send(request)
|
||||
.expect("failed to send request to simulation");
|
||||
Ok(())
|
||||
}
|
||||
@ -104,7 +101,9 @@ impl SerialInterface for SerialInterfaceDummy {
|
||||
type 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;
|
||||
match pcdu_req {
|
||||
PcduRequest::SwitchDevice { switch, state } => {
|
||||
@ -120,7 +119,7 @@ impl SerialInterface for SerialInterfaceDummy {
|
||||
PcduRequest::RequestSwitchInfo => {
|
||||
let mut reply_deque_mut = self.reply_deque.borrow_mut();
|
||||
reply_deque_mut.push_back(SimReply::new(&PcduReply::SwitchInfo(
|
||||
switch_map_mut.clone(),
|
||||
self.switch_map.borrow().0.clone(),
|
||||
)));
|
||||
}
|
||||
};
|
||||
@ -131,13 +130,15 @@ impl SerialInterface for SerialInterfaceDummy {
|
||||
&self,
|
||||
mut f: ReplyHandler,
|
||||
) -> Result<(), Self::Error> {
|
||||
if self.reply_queue_empty() {
|
||||
if self.reply_deque.borrow().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
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());
|
||||
if self.reply_queue_empty() {
|
||||
if reply_deque_mut.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -145,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 {
|
||||
Dummy(SerialInterfaceDummy),
|
||||
Sim(SerialInterfaceToSim),
|
||||
@ -382,12 +371,10 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> PcduHandler<ComInter
|
||||
PcduReply::SwitchInfo(switch_info) => {
|
||||
let switch_map_wrapper =
|
||||
SwitchMapWrapper::from_binary_switch_map_ref(&switch_info);
|
||||
let mut shared_switch_map = self
|
||||
.shared_switch_map
|
||||
self.shared_switch_map
|
||||
.lock()
|
||||
.expect("failed to lock switch map");
|
||||
shared_switch_map.switch_map = switch_map_wrapper.0;
|
||||
shared_switch_map.valid = true;
|
||||
.expect("failed to lock switch map")
|
||||
.switch_map = switch_map_wrapper.0;
|
||||
}
|
||||
}
|
||||
}) {
|
||||
@ -412,7 +399,6 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
|
||||
&mut self,
|
||||
requestor: MessageMetadata,
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
_forced: bool,
|
||||
) -> Result<(), satrs::mode::ModeError> {
|
||||
log::info!(
|
||||
"{}: transitioning to mode {:?}",
|
||||
@ -441,9 +427,6 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
|
||||
) -> Result<(), Self::Error> {
|
||||
self.announce_mode(requestor, false);
|
||||
if let Some(requestor) = requestor {
|
||||
if requestor.sender_id() == NO_SENDER {
|
||||
return Ok(());
|
||||
}
|
||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
||||
log::warn!(
|
||||
"can not send back mode reply to sender {}",
|
||||
@ -482,248 +465,3 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSender> ModeRequestHandler
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::mpsc;
|
||||
|
||||
use satrs::{
|
||||
mode::ModeRequest, power::SwitchStateBinary, request::GenericMessage, tmtc::PacketAsVec,
|
||||
};
|
||||
use satrs_example::config::components::{Apid, MGM_HANDLER_0};
|
||||
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::Sender<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, mpsc::Sender<PacketAsVec>>,
|
||||
}
|
||||
|
||||
impl PcduTestbench {
|
||||
pub fn new() -> Self {
|
||||
let (mode_request_tx, mode_request_rx) = mpsc::channel();
|
||||
let (mode_reply_tx_to_pus, mode_reply_rx_to_pus) = mpsc::channel();
|
||||
let (mode_reply_tx_to_parent, mode_reply_rx_to_parent) = mpsc::sync_channel(5);
|
||||
let mode_interface = MpscModeLeafInterface {
|
||||
request_rx: mode_request_rx,
|
||||
reply_to_pus_tx: mode_reply_tx_to_pus,
|
||||
reply_to_parent_tx: mode_reply_tx_to_parent,
|
||||
};
|
||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||
let (hk_reply_tx, hk_reply_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel::<PacketAsVec>();
|
||||
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
|
||||
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
|
||||
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: PcduHandler::new(
|
||||
UniqueApidTargetId::new(Apid::Eps as u16, 0),
|
||||
"TEST_PCDU",
|
||||
mode_interface,
|
||||
composite_request_rx,
|
||||
hk_reply_tx,
|
||||
switch_reqest_rx,
|
||||
tm_tx,
|
||||
SerialInterfaceTest::default(),
|
||||
shared_switch_map,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,13 @@ 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, MessageMetadata};
|
||||
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};
|
||||
use satrs_example::DeviceMode;
|
||||
|
||||
use crate::acs::mgm::{
|
||||
MgmHandlerLis3Mdl, MpscModeLeafInterface, SpiDummyInterface, SpiSimInterface,
|
||||
@ -47,12 +46,10 @@ use crate::pus::scheduler::{create_scheduler_service_dynamic, create_scheduler_s
|
||||
use crate::pus::test::create_test_service_static;
|
||||
use crate::pus::{PusTcDistributor, PusTcMpscRouter};
|
||||
use crate::requests::{CompositeRequest, GenericRequestRouter};
|
||||
use satrs::mode::{Mode, ModeAndSubmode, ModeRequest};
|
||||
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, NO_SENDER, PCDU_HANDLER, TCP_SERVER, UDP_SERVER,
|
||||
};
|
||||
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};
|
||||
@ -101,7 +98,7 @@ fn static_tmtc_pool_main() {
|
||||
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
|
||||
request_map
|
||||
.mode_router_map
|
||||
.insert(PCDU_HANDLER.id(), pcdu_handler_mode_tx.clone());
|
||||
.insert(PCDU_HANDLER.id(), pcdu_handler_mode_tx);
|
||||
|
||||
// This helper structure is used by all telecommand providers which need to send telecommands
|
||||
// to the TC source.
|
||||
@ -275,7 +272,6 @@ fn static_tmtc_pool_main() {
|
||||
} else {
|
||||
SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default())
|
||||
};
|
||||
|
||||
let mut pcdu_handler = PcduHandler::new(
|
||||
PCDU_HANDLER,
|
||||
"PCDU",
|
||||
@ -287,16 +283,6 @@ fn static_tmtc_pool_main() {
|
||||
pcdu_serial_interface,
|
||||
shared_switch_set,
|
||||
);
|
||||
// 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");
|
||||
let jh_udp_tmtc = thread::Builder::new()
|
||||
@ -434,7 +420,7 @@ fn dyn_tmtc_pool_main() {
|
||||
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
|
||||
request_map
|
||||
.mode_router_map
|
||||
.insert(PCDU_HANDLER.id(), pcdu_handler_mode_tx.clone());
|
||||
.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
|
||||
@ -597,16 +583,6 @@ fn dyn_tmtc_pool_main() {
|
||||
pcdu_serial_interface,
|
||||
shared_switch_set,
|
||||
);
|
||||
// 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");
|
||||
let jh_udp_tmtc = thread::Builder::new()
|
||||
|
@ -33,7 +33,6 @@ pub struct HkReply {
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum HkReplyVariant {
|
||||
Ack,
|
||||
Failed(ResultU16),
|
||||
|
@ -44,13 +44,11 @@ pub struct PusTcMpscRouter {
|
||||
pub event_tc_sender: Sender<EcssTcAndToken>,
|
||||
pub sched_tc_sender: Sender<EcssTcAndToken>,
|
||||
pub hk_tc_sender: Sender<EcssTcAndToken>,
|
||||
#[allow(dead_code)]
|
||||
pub action_tc_sender: Sender<EcssTcAndToken>,
|
||||
pub mode_tc_sender: Sender<EcssTcAndToken>,
|
||||
}
|
||||
|
||||
pub struct PusTcDistributor<TmSender: EcssTmSender> {
|
||||
#[allow(dead_code)]
|
||||
pub id: ComponentId,
|
||||
pub tm_sender: TmSender,
|
||||
pub verif_reporter: VerificationReporter,
|
||||
|
@ -110,7 +110,6 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
|
||||
),
|
||||
)?;
|
||||
}
|
||||
ModeReply::ModeInfo(_mode_and_submode) => (),
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
@ -191,13 +190,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
|
||||
}
|
||||
let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..])
|
||||
.expect("mode and submode extraction failed");
|
||||
Ok((
|
||||
active_request,
|
||||
ModeRequest::SetMode {
|
||||
mode_and_submode,
|
||||
forced: false,
|
||||
},
|
||||
))
|
||||
Ok((active_request, ModeRequest::SetMode(mode_and_submode)))
|
||||
}
|
||||
Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)),
|
||||
Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)),
|
||||
@ -353,13 +346,7 @@ mod tests {
|
||||
let (_active_req, req) = testbench
|
||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||
.expect("conversion has failed");
|
||||
assert_eq!(
|
||||
req,
|
||||
ModeRequest::SetMode {
|
||||
mode_and_submode,
|
||||
forced: false
|
||||
}
|
||||
);
|
||||
assert_eq!(req, ModeRequest::SetMode(mode_and_submode));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -26,7 +26,6 @@ pub enum CompositeRequest {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenericRequestRouter {
|
||||
#[allow(dead_code)]
|
||||
pub id: ComponentId,
|
||||
// All messages which do not have a dedicated queue.
|
||||
pub composite_router_map:
|
||||
|
@ -4,19 +4,16 @@ use std::{
|
||||
};
|
||||
|
||||
use log::info;
|
||||
use satrs::tmtc::{PacketAsVec, PacketInPool, SharedPacketPool};
|
||||
use satrs::{
|
||||
pool::PoolProvider,
|
||||
seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore},
|
||||
spacepackets::{
|
||||
ecss::{tm::PusTmZeroCopyWriter, PusPacket},
|
||||
seq_count::CcsdsSimpleSeqCountProvider,
|
||||
time::cds::MIN_CDS_FIELD_LEN,
|
||||
CcsdsPacket,
|
||||
},
|
||||
};
|
||||
use satrs::{
|
||||
spacepackets::seq_count::SequenceCountProvider,
|
||||
tmtc::{PacketAsVec, PacketInPool, SharedPacketPool},
|
||||
};
|
||||
|
||||
use crate::interface::tcp::SyncTcpTmSource;
|
||||
|
||||
|
@ -23,7 +23,7 @@ version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.satrs-shared]
|
||||
version = ">=0.1.3, <=0.2"
|
||||
version = ">=0.1.3, <0.2"
|
||||
features = ["serde"]
|
||||
|
||||
[dependencies.satrs-mib-codegen]
|
||||
|
@ -28,7 +28,7 @@ features = ["full"]
|
||||
trybuild = { version = "1", features = ["diff"] }
|
||||
|
||||
[dev-dependencies.satrs-shared]
|
||||
version = ">=0.1.3, <=0.2"
|
||||
version = ">=0.1.3, <0.2"
|
||||
|
||||
[dev-dependencies.satrs-mib]
|
||||
path = ".."
|
||||
|
@ -1,4 +1,6 @@
|
||||
#![no_std]
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate std;
|
||||
|
||||
|
@ -9,18 +9,20 @@ edition = "2021"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
log = "0.4"
|
||||
thiserror = "2"
|
||||
fern = "0.7"
|
||||
thiserror = "1"
|
||||
fern = "0.5"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
num_enum = "0.7"
|
||||
humantime = "2"
|
||||
tai-time = { version = "0.3", features = ["serde"] }
|
||||
|
||||
[dependencies.nexosim]
|
||||
version = "0.3.1"
|
||||
[dependencies.asynchronix]
|
||||
version = "0.2.1"
|
||||
git = "https://github.com/asynchronics/asynchronix.git"
|
||||
branch = "main"
|
||||
features = ["serde"]
|
||||
|
||||
[dependencies.satrs]
|
||||
path = "../satrs"
|
||||
|
||||
[dev-dependencies]
|
||||
delegate = "0.13"
|
||||
delegate = "0.12"
|
||||
|
@ -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"/>
|
@ -1,8 +1,8 @@
|
||||
use std::{f32::consts::PI, sync::mpsc, time::Duration};
|
||||
|
||||
use nexosim::{
|
||||
model::{Context, Model},
|
||||
ports::Output,
|
||||
use asynchronix::{
|
||||
model::{Model, Output},
|
||||
time::Scheduler,
|
||||
};
|
||||
use satrs::power::SwitchStateBinary;
|
||||
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.
|
||||
pub struct MagnetometerModel<ReplyProvider: MgmReplyProvider> {
|
||||
pub switch_state: SwitchStateBinary,
|
||||
#[allow(dead_code)]
|
||||
pub periodicity: Duration,
|
||||
pub external_mag_field: Option<MgmSensorValuesMicroTesla>,
|
||||
pub reply_sender: mpsc::Sender<SimReply>,
|
||||
@ -55,7 +54,7 @@ impl<ReplyProvider: MgmReplyProvider> MagnetometerModel<ReplyProvider> {
|
||||
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
|
||||
.send(ReplyProvider::create_mgm_reply(MgmReplyCommon {
|
||||
switch_state: self.switch_state,
|
||||
@ -114,11 +113,11 @@ impl MagnetorquerModel {
|
||||
pub async fn apply_torque(
|
||||
&mut self,
|
||||
duration_and_dipole: (Duration, MgtDipole),
|
||||
cx: &mut Context<Self>,
|
||||
scheduler: &Scheduler<Self>,
|
||||
) {
|
||||
self.torque_dipole = duration_and_dipole.1;
|
||||
self.torquing = true;
|
||||
if cx
|
||||
if scheduler
|
||||
.schedule_event(duration_and_dipole.0, Self::clear_torque, ())
|
||||
.is_err()
|
||||
{
|
||||
@ -138,11 +137,12 @@ impl MagnetorquerModel {
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
|
||||
@ -199,7 +199,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
@ -222,21 +222,21 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
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();
|
||||
assert!(sim_reply_res.is_some());
|
||||
let mut sim_reply = sim_reply_res.unwrap();
|
||||
assert_eq!(sim_reply.component(), SimComponent::MgmLis3Mdl);
|
||||
let first_reply = MgmLis3MdlReply::from_sim_message(&sim_reply)
|
||||
.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);
|
||||
sim_testbench
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_some());
|
||||
sim_reply = sim_reply_res.unwrap();
|
||||
@ -271,7 +271,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
let sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_none());
|
||||
}
|
||||
@ -286,7 +286,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
let sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_some());
|
||||
let sim_reply = sim_reply_res.unwrap();
|
||||
@ -307,7 +307,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
let sim_reply_res = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply_res.is_some());
|
||||
let sim_reply = sim_reply_res.unwrap();
|
||||
@ -338,7 +338,7 @@ pub mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
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(
|
||||
&mut sim_testbench,
|
||||
@ -347,9 +347,7 @@ pub mod tests {
|
||||
torquing: true,
|
||||
},
|
||||
);
|
||||
sim_testbench
|
||||
.step_until(Duration::from_millis(100))
|
||||
.unwrap();
|
||||
sim_testbench.step_by(Duration::from_millis(100));
|
||||
check_mgt_hk(
|
||||
&mut sim_testbench,
|
||||
MgtHkSet {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
|
||||
use nexosim::{
|
||||
simulation::{Address, Scheduler, Simulation},
|
||||
use asynchronix::{
|
||||
simulation::{Address, Simulation},
|
||||
time::{Clock, MonotonicTime, SystemClock},
|
||||
};
|
||||
use satrs_minisim::{
|
||||
@ -16,59 +16,40 @@ use crate::{
|
||||
eps::PcduModel,
|
||||
};
|
||||
|
||||
const WARNING_FOR_STALE_DATA: bool = false;
|
||||
|
||||
const SIM_CTRL_REQ_WIRETAPPING: bool = false;
|
||||
const MGM_REQ_WIRETAPPING: bool = false;
|
||||
const PCDU_REQ_WIRETAPPING: bool = false;
|
||||
const MGT_REQ_WIRETAPPING: bool = false;
|
||||
|
||||
pub struct ModelAddrWrapper {
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
}
|
||||
const SIM_CTRL_REQ_WIRETAPPING: bool = true;
|
||||
const MGM_REQ_WIRETAPPING: bool = true;
|
||||
const PCDU_REQ_WIRETAPPING: bool = true;
|
||||
const MGT_REQ_WIRETAPPING: bool = true;
|
||||
|
||||
// The simulation controller processes requests and drives the simulation.
|
||||
#[allow(dead_code)]
|
||||
pub struct SimController {
|
||||
pub sys_clock: SystemClock,
|
||||
pub request_receiver: mpsc::Receiver<SimRequest>,
|
||||
pub reply_sender: mpsc::Sender<SimReply>,
|
||||
pub simulation: Simulation,
|
||||
pub scheduler: Scheduler,
|
||||
pub addr_wrapper: ModelAddrWrapper,
|
||||
pub mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pub pcdu_addr: Address<PcduModel>,
|
||||
pub mgt_addr: Address<MagnetorquerModel>,
|
||||
}
|
||||
|
||||
impl ModelAddrWrapper {
|
||||
pub fn new(
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
) -> Self {
|
||||
Self {
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SimController {
|
||||
pub fn new(
|
||||
sys_clock: SystemClock,
|
||||
request_receiver: mpsc::Receiver<SimRequest>,
|
||||
reply_sender: mpsc::Sender<SimReply>,
|
||||
simulation: Simulation,
|
||||
scheduler: Scheduler,
|
||||
addr_wrapper: ModelAddrWrapper,
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sys_clock,
|
||||
request_receiver,
|
||||
reply_sender,
|
||||
simulation,
|
||||
scheduler,
|
||||
addr_wrapper,
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +60,7 @@ impl SimController {
|
||||
// Check for UDP requests every millisecond. Shift the simulator ahead here to prevent
|
||||
// replies lying in the past.
|
||||
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.simulation
|
||||
.step_until(t)
|
||||
@ -91,7 +72,7 @@ impl SimController {
|
||||
loop {
|
||||
match self.request_receiver.try_recv() {
|
||||
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);
|
||||
}
|
||||
if let Err(e) = match request.component() {
|
||||
@ -135,13 +116,11 @@ impl SimController {
|
||||
}
|
||||
match mgm_request {
|
||||
MgmRequestLis3Mdl::RequestSensorData => {
|
||||
self.simulation
|
||||
.process_event(
|
||||
MagnetometerModel::send_sensor_values,
|
||||
(),
|
||||
&self.addr_wrapper.mgm_addr,
|
||||
)
|
||||
.expect("event execution error for mgm");
|
||||
self.simulation.send_event(
|
||||
MagnetometerModel::send_sensor_values,
|
||||
(),
|
||||
&self.mgm_addr,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -155,21 +134,14 @@ impl SimController {
|
||||
match pcdu_request {
|
||||
PcduRequest::RequestSwitchInfo => {
|
||||
self.simulation
|
||||
.process_event(
|
||||
PcduModel::request_switch_info,
|
||||
(),
|
||||
&self.addr_wrapper.pcdu_addr,
|
||||
)
|
||||
.unwrap();
|
||||
.send_event(PcduModel::request_switch_info, (), &self.pcdu_addr);
|
||||
}
|
||||
PcduRequest::SwitchDevice { switch, state } => {
|
||||
self.simulation
|
||||
.process_event(
|
||||
PcduModel::switch_device,
|
||||
(switch, state),
|
||||
&self.addr_wrapper.pcdu_addr,
|
||||
)
|
||||
.unwrap();
|
||||
self.simulation.send_event(
|
||||
PcduModel::switch_device,
|
||||
(switch, state),
|
||||
&self.pcdu_addr,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -181,23 +153,17 @@ impl SimController {
|
||||
log::info!("received MGT request: {:?}", mgt_request);
|
||||
}
|
||||
match mgt_request {
|
||||
MgtRequest::ApplyTorque { duration, dipole } => self
|
||||
.simulation
|
||||
.process_event(
|
||||
MagnetorquerModel::apply_torque,
|
||||
(duration, dipole),
|
||||
&self.addr_wrapper.mgt_addr,
|
||||
)
|
||||
.unwrap(),
|
||||
MgtRequest::RequestHk => self
|
||||
.simulation
|
||||
.process_event(
|
||||
MagnetorquerModel::request_housekeeping_data,
|
||||
(),
|
||||
&self.addr_wrapper.mgt_addr,
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
MgtRequest::ApplyTorque { duration, dipole } => self.simulation.send_event(
|
||||
MagnetorquerModel::apply_torque,
|
||||
(duration, dipole),
|
||||
&self.mgt_addr,
|
||||
),
|
||||
MgtRequest::RequestHk => self.simulation.send_event(
|
||||
MagnetorquerModel::request_housekeeping_data,
|
||||
(),
|
||||
&self.mgt_addr,
|
||||
),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -231,7 +197,7 @@ mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending sim ctrl request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
|
||||
use nexosim::{
|
||||
model::{Context, Model},
|
||||
ports::Output,
|
||||
use asynchronix::{
|
||||
model::{Model, Output},
|
||||
time::Scheduler,
|
||||
};
|
||||
use satrs::power::SwitchStateBinary;
|
||||
use satrs_minisim::{
|
||||
@ -29,13 +29,14 @@ impl PcduModel {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn request_switch_info(&mut self, _: (), cx: &mut Context<Self>) {
|
||||
cx.schedule_event(
|
||||
Duration::from_millis(SWITCH_INFO_DELAY_MS),
|
||||
Self::send_switch_info,
|
||||
(),
|
||||
)
|
||||
.expect("requesting switch info failed");
|
||||
pub async fn request_switch_info(&mut self, _: (), scheduler: &Scheduler<Self>) {
|
||||
scheduler
|
||||
.schedule_event(
|
||||
Duration::from_millis(SWITCH_INFO_DELAY_MS),
|
||||
Self::send_switch_info,
|
||||
(),
|
||||
)
|
||||
.expect("requesting switch info failed");
|
||||
}
|
||||
|
||||
pub fn send_switch_info(&mut self) {
|
||||
@ -91,7 +92,7 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM switch request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -112,7 +113,7 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step().unwrap();
|
||||
sim_testbench.step();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
@ -142,12 +143,12 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
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();
|
||||
assert!(sim_reply.is_none());
|
||||
// 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();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
|
@ -1,4 +1,4 @@
|
||||
use nexosim::time::MonotonicTime;
|
||||
use asynchronix::time::MonotonicTime;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
@ -236,7 +236,7 @@ pub mod eps {
|
||||
RequestSwitchInfo = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum PcduRequest {
|
||||
SwitchDevice {
|
||||
switch: PcduSwitch,
|
||||
|
@ -1,8 +1,8 @@
|
||||
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 nexosim::simulation::{Mailbox, SimInit};
|
||||
use nexosim::time::{MonotonicTime, SystemClock};
|
||||
use satrs_minisim::udp::SIM_CTRL_PORT;
|
||||
use satrs_minisim::{SimReply, SimRequest};
|
||||
use std::sync::mpsc;
|
||||
@ -63,20 +63,19 @@ fn create_sim_controller(
|
||||
} else {
|
||||
SimInit::new()
|
||||
};
|
||||
let addrs = ModelAddrWrapper::new(mgm_addr, pcdu_addr, mgt_addr);
|
||||
let (simulation, scheduler) = sim_init
|
||||
.add_model(mgm_model, mgm_mailbox, "MGM model")
|
||||
.add_model(pcdu_model, pcdu_mailbox, "PCDU model")
|
||||
.add_model(mgt_model, mgt_mailbox, "MGT model")
|
||||
.init(start_time)
|
||||
.unwrap();
|
||||
let simulation = sim_init
|
||||
.add_model(mgm_model, mgm_mailbox)
|
||||
.add_model(pcdu_model, pcdu_mailbox)
|
||||
.add_model(mgt_model, mgt_mailbox)
|
||||
.init(start_time);
|
||||
SimController::new(
|
||||
sys_clock,
|
||||
request_receiver,
|
||||
reply_sender,
|
||||
simulation,
|
||||
scheduler,
|
||||
addrs,
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
use delegate::delegate;
|
||||
use std::sync::mpsc;
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
|
||||
use nexosim::{
|
||||
simulation::ExecutionError,
|
||||
time::{Deadline, MonotonicTime},
|
||||
};
|
||||
use asynchronix::time::MonotonicTime;
|
||||
use satrs_minisim::{SimReply, SimRequest};
|
||||
|
||||
use crate::{controller::SimController, create_sim_controller, ThreadingModel};
|
||||
@ -38,8 +35,8 @@ impl SimTestbench {
|
||||
pub fn handle_sim_requests(&mut self, old_timestamp: MonotonicTime);
|
||||
}
|
||||
to self.sim_controller.simulation {
|
||||
pub fn step(&mut self) -> Result<(), ExecutionError>;
|
||||
pub fn step_until(&mut self, duration: impl Deadline) -> Result<(), ExecutionError>;
|
||||
pub fn step(&mut self);
|
||||
pub fn step_by(&mut self, duration: Duration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use nexosim::time::MonotonicTime;
|
||||
use asynchronix::time::MonotonicTime;
|
||||
|
||||
pub fn current_millis(time: MonotonicTime) -> u64 {
|
||||
(time.as_secs() as u64 * 1000) + (time.subsec_nanos() as u64 / 1_000_000)
|
||||
|
@ -8,14 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [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
|
||||
|
||||
## Added
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "satrs-shared"
|
||||
description = "Components shared by multiple sat-rs crates"
|
||||
version = "0.2.1"
|
||||
version = "0.1.4"
|
||||
edition = "2021"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
||||
@ -22,13 +22,12 @@ version = "0.3"
|
||||
optional = true
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = ">0.9, <=0.13"
|
||||
version = ">0.9, <=0.11"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
serde = ["dep:serde", "spacepackets/serde"]
|
||||
defmt = ["dep:defmt", "spacepackets/defmt"]
|
||||
spacepackets = ["dep:defmt", "spacepackets/defmt"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
|
||||
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --all-features --open
|
@ -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>`.
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! This crates contains modules shared among other sat-rs framework crates.
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
|
||||
pub mod res_code;
|
||||
|
@ -12,14 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
- Renamed `StaticPoolConfig::new` to `StaticPoolConfig::new_from_subpool_cfg_tuples`. The new
|
||||
`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
|
||||
|
||||
- `StaticHeaplessMemoryPool` which can be grown with user-provided static buffers.
|
||||
- Scheduling table for systems with a standard runtime
|
||||
|
||||
# [v0.2.1] 2024-05-19
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "satrs"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.82.0"
|
||||
rust-version = "1.71.1"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
description = "A framework to build software for remote systems"
|
||||
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
||||
@ -13,25 +13,27 @@ keywords = ["no-std", "space", "aerospace"]
|
||||
categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"]
|
||||
|
||||
[dependencies]
|
||||
delegate = ">0.7, <=0.13"
|
||||
delegate = ">0.7, <=0.10"
|
||||
paste = "1"
|
||||
derive-new = ">=0.6, <=0.7"
|
||||
derive-new = "0.6"
|
||||
smallvec = "1"
|
||||
crc = "3"
|
||||
|
||||
[dependencies.satrs-shared]
|
||||
version = ">=0.1.3, <=0.2"
|
||||
version = ">=0.1.3, <0.2"
|
||||
|
||||
[dependencies.num_enum]
|
||||
version = ">0.5, <=0.7"
|
||||
default-features = false
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = "0.13"
|
||||
version = "0.11"
|
||||
default-features = false
|
||||
|
||||
[dependencies.cobs]
|
||||
version = "0.3"
|
||||
git = "https://github.com/robamu/cobs.rs.git"
|
||||
version = "0.2.3"
|
||||
branch = "all_features"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num-traits]
|
||||
@ -43,15 +45,15 @@ version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.hashbrown]
|
||||
version = ">=0.14, <=0.15"
|
||||
version = "0.14"
|
||||
optional = true
|
||||
|
||||
[dependencies.heapless]
|
||||
version = "0.8"
|
||||
version = "0.7"
|
||||
optional = true
|
||||
|
||||
[dependencies.downcast-rs]
|
||||
version = "2"
|
||||
version = "1.2"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
@ -65,8 +67,8 @@ default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "2"
|
||||
default-features = false
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
@ -79,7 +81,7 @@ features = ["all"]
|
||||
optional = true
|
||||
|
||||
[dependencies.mio]
|
||||
version = "1"
|
||||
version = "0.8"
|
||||
features = ["os-poll", "net"]
|
||||
optional = true
|
||||
|
||||
@ -89,10 +91,10 @@ optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
serde = "1"
|
||||
zerocopy = "0.8"
|
||||
zerocopy = "0.7"
|
||||
once_cell = "1"
|
||||
serde_json = "1"
|
||||
rand = "0.9"
|
||||
rand = "0.8"
|
||||
tempfile = "3"
|
||||
|
||||
[dev-dependencies.postcard]
|
||||
@ -104,11 +106,12 @@ std = [
|
||||
"downcast-rs/std",
|
||||
"alloc",
|
||||
"bus",
|
||||
"postcard/use-std",
|
||||
"crossbeam-channel/std",
|
||||
"serde/std",
|
||||
"spacepackets/std",
|
||||
"num_enum/std",
|
||||
"thiserror/std",
|
||||
"thiserror",
|
||||
"socket2",
|
||||
"mio"
|
||||
]
|
||||
@ -124,12 +127,8 @@ crossbeam = ["crossbeam-channel"]
|
||||
heapless = ["dep:heapless"]
|
||||
defmt = ["dep:defmt", "spacepackets/defmt"]
|
||||
test_util = []
|
||||
doc-images = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
||||
[[test]]
|
||||
name = "event_test"
|
||||
path = "tests/pus_events.rs"
|
||||
required-features = ["test_util"]
|
||||
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
|
||||
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --all-features --open
|
@ -3,7 +3,8 @@ Checklist for new releases
|
||||
|
||||
# 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`.
|
||||
3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new
|
||||
`unreleased` section.
|
||||
|
1603
satrs/src/cfdp/dest.rs
Normal file
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
769
satrs/src/cfdp/filestore.rs
Normal 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
668
satrs/src/cfdp/mod.rs
Normal 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
15
satrs/src/cfdp/source.rs
Normal 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
96
satrs/src/cfdp/user.rs
Normal 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);
|
||||
}
|
@ -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<(), GenericTargetedMessagingError>;
|
||||
|
||||
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<(), GenericTargetedMessagingError>;
|
||||
}
|
||||
|
||||
#[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<(), GenericTargetedMessagingError> {
|
||||
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<(), GenericTargetedMessagingError> {
|
||||
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<(), GenericTargetedMessagingError> {
|
||||
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<(), GenericTargetedMessagingError> {
|
||||
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);
|
||||
}
|
||||
}
|
@ -15,24 +15,20 @@ pub enum OpResult {
|
||||
TerminationRequested,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExecutionType {
|
||||
Infinite,
|
||||
Cycles(u32),
|
||||
OneShot,
|
||||
}
|
||||
|
||||
pub trait Executable {
|
||||
pub trait Executable: Send {
|
||||
type Error;
|
||||
|
||||
fn exec_type(&self) -> ExecutionType;
|
||||
fn task_name(&self) -> &'static str;
|
||||
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
|
||||
///
|
||||
/// # Arguments
|
||||
@ -43,10 +39,7 @@ pub trait ExecutableWithType: Executable {
|
||||
/// * `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
|
||||
pub fn exec_sched_single<
|
||||
T: ExecutableWithType<Error = E> + Send + 'static + ?Sized,
|
||||
E: Send + 'static,
|
||||
>(
|
||||
pub fn exec_sched_single<T: Executable<Error = E> + Send + 'static + ?Sized, E: Send + 'static>(
|
||||
mut executable: Box<T>,
|
||||
task_freq: Option<Duration>,
|
||||
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
|
||||
/// * `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
|
||||
pub fn exec_sched_multi<
|
||||
T: ExecutableWithType<Error = E> + Send + 'static + ?Sized,
|
||||
E: Send + 'static,
|
||||
>(
|
||||
pub fn exec_sched_multi<T: Executable<Error = E> + Send + 'static + ?Sized, E: Send + 'static>(
|
||||
task_name: &'static str,
|
||||
mut executable_vec: Vec<Box<T>>,
|
||||
task_freq: Option<Duration>,
|
||||
@ -152,10 +142,7 @@ pub fn exec_sched_multi<
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
exec_sched_multi, exec_sched_single, Executable, ExecutableWithType, ExecutionType,
|
||||
OpResult,
|
||||
};
|
||||
use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult};
|
||||
use bus::Bus;
|
||||
use std::boxed::Box;
|
||||
use std::error::Error;
|
||||
@ -221,6 +208,10 @@ mod tests {
|
||||
impl Executable for OneShotTask {
|
||||
type Error = ExampleError;
|
||||
|
||||
fn exec_type(&self) -> ExecutionType {
|
||||
ExecutionType::OneShot
|
||||
}
|
||||
|
||||
fn task_name(&self) -> &'static str {
|
||||
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";
|
||||
|
||||
impl Executable for FixedCyclesTask {
|
||||
type Error = ExampleError;
|
||||
|
||||
fn exec_type(&self) -> ExecutionType {
|
||||
ExecutionType::Cycles(self.cycles)
|
||||
}
|
||||
|
||||
fn task_name(&self) -> &'static str {
|
||||
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";
|
||||
|
||||
impl Executable for PeriodicTask {
|
||||
type Error = ExampleError;
|
||||
|
||||
fn exec_type(&self) -> ExecutionType {
|
||||
ExecutionType::Infinite
|
||||
}
|
||||
|
||||
fn task_name(&self) -> &'static str {
|
||||
PERIODIC_TASK_NAME
|
||||
}
|
||||
@ -294,12 +281,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecutableWithType for PeriodicTask {
|
||||
fn exec_type(&self) -> ExecutionType {
|
||||
ExecutionType::Infinite
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_one_shot() {
|
||||
let expected_op_code = 42;
|
||||
@ -442,7 +423,7 @@ mod tests {
|
||||
});
|
||||
assert_eq!(cycled_task_0.task_name(), CYCLE_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];
|
||||
let jh = exec_sched_multi(
|
||||
"multi-task-name",
|
||||
@ -512,7 +493,7 @@ mod tests {
|
||||
});
|
||||
assert_eq!(periodic_task_0.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];
|
||||
let jh = exec_sched_multi(
|
||||
"multi-task-name",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
//! [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 [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "alloc")]
|
||||
@ -22,34 +22,31 @@ extern crate downcast_rs;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate std;
|
||||
|
||||
pub mod action;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod dev_mgmt;
|
||||
pub mod cfdp;
|
||||
pub mod encoding;
|
||||
pub mod event_man;
|
||||
pub mod events;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod executable;
|
||||
pub mod hal;
|
||||
pub mod health;
|
||||
pub mod hk;
|
||||
pub mod mode;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod mode_tree;
|
||||
pub mod params;
|
||||
pub mod pool;
|
||||
pub mod power;
|
||||
pub mod pus;
|
||||
pub mod queue;
|
||||
pub mod request;
|
||||
pub mod res_code;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod scheduling;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod subsystem;
|
||||
pub mod seq_count;
|
||||
pub mod time;
|
||||
pub mod tmtc;
|
||||
|
||||
pub mod action;
|
||||
pub mod hk;
|
||||
pub mod mode;
|
||||
pub mod params;
|
||||
|
||||
pub use spacepackets;
|
||||
|
||||
use spacepackets::PacketId;
|
||||
|
@ -12,9 +12,7 @@ pub use std_mod::*;
|
||||
|
||||
use crate::{
|
||||
queue::GenericTargetedMessagingError,
|
||||
request::{
|
||||
GenericMessage, MessageMetadata, MessageReceiverProvider, MessageReceiverWithId, RequestId,
|
||||
},
|
||||
request::{GenericMessage, MessageMetadata, MessageReceiver, MessageReceiverWithId, RequestId},
|
||||
ComponentId,
|
||||
};
|
||||
|
||||
@ -28,11 +26,6 @@ pub struct ModeAndSubmode {
|
||||
submode: Submode,
|
||||
}
|
||||
|
||||
pub const INVALID_MODE_VAL: Mode = Mode::MAX;
|
||||
pub const UNKNOWN_MODE_VAL: Mode = Mode::MAX - 1;
|
||||
pub const INVALID_MODE: ModeAndSubmode = ModeAndSubmode::new(INVALID_MODE_VAL, 0);
|
||||
pub const UNKNOWN_MODE: ModeAndSubmode = ModeAndSubmode::new(UNKNOWN_MODE_VAL, 0);
|
||||
|
||||
impl ModeAndSubmode {
|
||||
pub const RAW_LEN: usize = size_of::<Mode>() + size_of::<Submode>();
|
||||
|
||||
@ -118,10 +111,7 @@ impl TargetedModeCommand {
|
||||
pub enum ModeRequest {
|
||||
/// Mode information. Can be used to notify other components of changed modes.
|
||||
ModeInfo(ModeAndSubmode),
|
||||
SetMode {
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
forced: bool,
|
||||
},
|
||||
SetMode(ModeAndSubmode),
|
||||
ReadMode,
|
||||
AnnounceMode,
|
||||
AnnounceModeRecursive,
|
||||
@ -137,8 +127,6 @@ pub struct TargetedModeRequest {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ModeReply {
|
||||
/// Mode information. Can be used to notify other components of changed modes.
|
||||
ModeInfo(ModeAndSubmode),
|
||||
/// Reply to a mode request to confirm the commanded mode was reached.
|
||||
ModeReply(ModeAndSubmode),
|
||||
// Can not reach the commanded mode. Contains a reason as a [ResultU16].
|
||||
@ -168,7 +156,7 @@ pub trait ModeRequestReceiver {
|
||||
) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError>;
|
||||
}
|
||||
|
||||
impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver
|
||||
impl<R: MessageReceiver<ModeRequest>> ModeRequestReceiver
|
||||
for MessageReceiverWithId<ModeRequest, R>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
@ -178,12 +166,15 @@ impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ModeError {
|
||||
#[error("Messaging error: {0}")]
|
||||
Messaging(#[from] GenericTargetedMessagingError),
|
||||
#[error("busy with other mode request")]
|
||||
Busy,
|
||||
Messaging(GenericTargetedMessagingError),
|
||||
}
|
||||
|
||||
impl From<GenericTargetedMessagingError> for ModeError {
|
||||
fn from(value: GenericTargetedMessagingError) -> Self {
|
||||
Self::Messaging(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeProvider {
|
||||
@ -205,7 +196,6 @@ pub trait ModeRequestHandler: ModeProvider {
|
||||
&mut self,
|
||||
requestor: MessageMetadata,
|
||||
mode_and_submode: ModeAndSubmode,
|
||||
forced: bool,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool);
|
||||
@ -232,10 +222,9 @@ pub trait ModeRequestHandler: ModeProvider {
|
||||
request: GenericMessage<ModeRequest>,
|
||||
) -> Result<(), Self::Error> {
|
||||
match request.message {
|
||||
ModeRequest::SetMode {
|
||||
mode_and_submode,
|
||||
forced,
|
||||
} => self.start_transition(request.requestor_info, mode_and_submode, forced),
|
||||
ModeRequest::SetMode(mode_and_submode) => {
|
||||
self.start_transition(request.requestor_info, mode_and_submode)
|
||||
}
|
||||
ModeRequest::ReadMode => self.send_mode_reply(
|
||||
request.requestor_info,
|
||||
ModeReply::ModeReply(self.mode_and_submode()),
|
||||
@ -259,9 +248,7 @@ pub trait ModeReplyReceiver {
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError>;
|
||||
}
|
||||
|
||||
impl<R: MessageReceiverProvider<ModeReply>> ModeReplyReceiver
|
||||
for MessageReceiverWithId<ModeReply, R>
|
||||
{
|
||||
impl<R: MessageReceiver<ModeReply>> ModeReplyReceiver for MessageReceiverWithId<ModeReply, R> {
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
|
||||
@ -283,13 +270,12 @@ pub trait ModeReplySender {
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use crate::request::{
|
||||
MessageSenderAndReceiver, MessageSenderMap, MessageSenderProvider,
|
||||
MessageSenderStoreProvider, RequestAndReplySenderAndReceiver,
|
||||
MessageSender, MessageSenderAndReceiver, MessageSenderMap, RequestAndReplySenderAndReceiver,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<S: MessageSenderProvider<ModeReply>> MessageSenderMap<ModeReply, S> {
|
||||
impl<S: MessageSender<ModeReply>> MessageSenderMap<ModeReply, S> {
|
||||
pub fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
@ -304,13 +290,8 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
From,
|
||||
Sender: MessageSenderProvider<ModeReply>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||
> ModeReplySender
|
||||
for MessageSenderAndReceiver<ModeReply, From, Sender, Receiver, SenderStore>
|
||||
impl<FROM, S: MessageSender<ModeReply>, R: MessageReceiver<FROM>> ModeReplySender
|
||||
for MessageSenderAndReceiver<ModeReply, FROM, S, R>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
@ -321,7 +302,7 @@ pub mod alloc_mod {
|
||||
requestor_info: MessageMetadata,
|
||||
request: ModeReply,
|
||||
) -> 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()),
|
||||
requestor_info.sender_id(),
|
||||
request,
|
||||
@ -329,13 +310,8 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
To,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<ModeReply>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> ModeReplyReceiver
|
||||
for MessageSenderAndReceiver<To, ModeReply, Sender, Receiver, SenderStore>
|
||||
impl<TO, S: MessageSender<TO>, R: MessageReceiver<ModeReply>> ModeReplyReceiver
|
||||
for MessageSenderAndReceiver<TO, ModeReply, S, R>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
@ -345,51 +321,26 @@ pub mod alloc_mod {
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
>
|
||||
RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
REQUEST,
|
||||
S0: MessageSender<REQUEST>,
|
||||
R0: MessageReceiver<ModeReply>,
|
||||
S1: MessageSender<ModeReply>,
|
||||
R1: MessageReceiver<REQUEST>,
|
||||
> RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
|
||||
{
|
||||
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: ReplySender) {
|
||||
self.reply_sender_store
|
||||
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: S1) {
|
||||
self.reply_sender_map
|
||||
.add_message_target(target_id, reply_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
ReplySender: MessageSenderProvider<ModeReply>,
|
||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||
> ModeReplySender
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
REQUEST,
|
||||
S0: MessageSender<REQUEST>,
|
||||
R0: MessageReceiver<ModeReply>,
|
||||
S1: MessageSender<ModeReply>,
|
||||
R1: MessageReceiver<REQUEST>,
|
||||
> ModeReplySender for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
@ -398,35 +349,24 @@ pub mod alloc_mod {
|
||||
fn send_mode_reply(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
reply: ModeReply,
|
||||
request: ModeReply,
|
||||
) -> 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()),
|
||||
requestor_info.sender_id(),
|
||||
reply,
|
||||
request,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
ReplySender: MessageSenderProvider<ModeReply>,
|
||||
ReplyReceiver: MessageReceiverProvider<ModeReply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, ReplySender>,
|
||||
REQUEST,
|
||||
S0: MessageSender<REQUEST>,
|
||||
R0: MessageReceiver<ModeReply>,
|
||||
S1: MessageSender<ModeReply>,
|
||||
R1: MessageReceiver<REQUEST>,
|
||||
> ModeReplyReceiver
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
|
||||
{
|
||||
fn try_recv_mode_reply(
|
||||
&self,
|
||||
@ -436,14 +376,11 @@ pub mod alloc_mod {
|
||||
}
|
||||
|
||||
/// Helper type definition for a mode handler which can handle mode requests.
|
||||
pub type ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore> =
|
||||
MessageSenderAndReceiver<ModeReply, ModeRequest, Sender, Receiver, ReplySenderStore>;
|
||||
pub type ModeRequestHandlerInterface<S, R> =
|
||||
MessageSenderAndReceiver<ModeReply, ModeRequest, S, R>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ModeReply>,
|
||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<ModeReply, Sender>,
|
||||
> ModeRequestHandlerInterface<Sender, Receiver, ReplySenderStore>
|
||||
impl<S: MessageSender<ModeReply>, R: MessageReceiver<ModeRequest>>
|
||||
ModeRequestHandlerInterface<S, R>
|
||||
{
|
||||
pub fn try_recv_mode_request(
|
||||
&self,
|
||||
@ -466,15 +403,9 @@ pub mod alloc_mod {
|
||||
|
||||
/// Helper type defintion for a mode handler object which can send mode requests and receive
|
||||
/// mode replies.
|
||||
pub type ModeRequestorInterface<Sender, Receiver, RequestSenderStore> =
|
||||
MessageSenderAndReceiver<ModeRequest, ModeReply, Sender, Receiver, RequestSenderStore>;
|
||||
pub type ModeRequestorInterface<S, R> = MessageSenderAndReceiver<ModeRequest, ModeReply, S, R>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ModeRequest>,
|
||||
Receiver: MessageReceiverProvider<ModeReply>,
|
||||
RequestSenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||
> ModeRequestorInterface<Sender, Receiver, RequestSenderStore>
|
||||
{
|
||||
impl<S: MessageSender<ModeRequest>, R: MessageReceiver<ModeReply>> ModeRequestorInterface<S, R> {
|
||||
pub fn try_recv_mode_reply(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
|
||||
@ -493,25 +424,10 @@ pub mod alloc_mod {
|
||||
|
||||
/// Helper type defintion for a mode handler object which can both send mode requests and
|
||||
/// process mode requests.
|
||||
pub type ModeInterface<
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
> = RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
ModeReply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>;
|
||||
pub type ModeInterface<S0, R0, S1, R1> =
|
||||
RequestAndReplySenderAndReceiver<ModeRequest, ModeReply, S0, R0, S1, R1>;
|
||||
|
||||
impl<S: MessageSenderProvider<ModeRequest>> MessageSenderMap<ModeRequest, S> {
|
||||
impl<S: MessageSender<ModeRequest>> MessageSenderMap<ModeRequest, S> {
|
||||
pub fn send_mode_request(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
@ -526,13 +442,25 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
To,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<ModeRequest>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> ModeRequestReceiver
|
||||
for MessageSenderAndReceiver<To, ModeRequest, Sender, Receiver, SenderStore>
|
||||
/*
|
||||
impl<S: MessageSender<ModeRequest>> ModeRequestSender for MessageSenderMapWithId<ModeRequest, S> {
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id
|
||||
}
|
||||
|
||||
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(
|
||||
&self,
|
||||
@ -541,13 +469,8 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
From,
|
||||
Sender: MessageSenderProvider<ModeRequest>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<ModeRequest, Sender>,
|
||||
> ModeRequestSender
|
||||
for MessageSenderAndReceiver<ModeRequest, From, Sender, Receiver, SenderStore>
|
||||
impl<FROM, S: MessageSender<ModeRequest>, R: MessageReceiver<FROM>> ModeRequestSender
|
||||
for MessageSenderAndReceiver<ModeRequest, FROM, S, R>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
@ -559,7 +482,7 @@ pub mod alloc_mod {
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericTargetedMessagingError> {
|
||||
self.message_sender_store.send_message(
|
||||
self.message_sender_map.send_mode_request(
|
||||
MessageMetadata::new(request_id, self.local_channel_id()),
|
||||
target_id,
|
||||
request,
|
||||
@ -568,50 +491,27 @@ pub mod alloc_mod {
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
>
|
||||
RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
REPLY,
|
||||
S0: MessageSender<ModeRequest>,
|
||||
R0: MessageReceiver<REPLY>,
|
||||
S1: MessageSender<REPLY>,
|
||||
R1: MessageReceiver<ModeRequest>,
|
||||
> RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
|
||||
{
|
||||
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: ReqSender) {
|
||||
self.request_sender_store
|
||||
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S0) {
|
||||
self.request_sender_map
|
||||
.add_message_target(target_id, request_sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
REPLY,
|
||||
S0: MessageSender<ModeRequest>,
|
||||
R0: MessageReceiver<REPLY>,
|
||||
S1: MessageSender<REPLY>,
|
||||
R1: MessageReceiver<ModeRequest>,
|
||||
> ModeRequestSender
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
|
||||
{
|
||||
fn local_channel_id(&self) -> ComponentId {
|
||||
self.local_channel_id_generic()
|
||||
@ -623,7 +523,7 @@ pub mod alloc_mod {
|
||||
target_id: ComponentId,
|
||||
request: ModeRequest,
|
||||
) -> Result<(), GenericTargetedMessagingError> {
|
||||
self.request_sender_store.send_message(
|
||||
self.request_sender_map.send_mode_request(
|
||||
MessageMetadata::new(request_id, self.local_channel_id()),
|
||||
target_id,
|
||||
request,
|
||||
@ -632,24 +532,13 @@ pub mod alloc_mod {
|
||||
}
|
||||
|
||||
impl<
|
||||
ReqSender: MessageSenderProvider<ModeRequest>,
|
||||
ReqReceiver: MessageReceiverProvider<ModeRequest>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<ModeRequest, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
REPLY,
|
||||
S0: MessageSender<ModeRequest>,
|
||||
R0: MessageReceiver<REPLY>,
|
||||
S1: MessageSender<REPLY>,
|
||||
R1: MessageReceiver<ModeRequest>,
|
||||
> ModeRequestReceiver
|
||||
for RequestAndReplySenderAndReceiver<
|
||||
ModeRequest,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
|
||||
{
|
||||
fn try_recv_mode_request(
|
||||
&self,
|
||||
@ -663,97 +552,39 @@ pub mod alloc_mod {
|
||||
pub mod std_mod {
|
||||
use std::sync::mpsc;
|
||||
|
||||
use crate::request::{MessageSenderList, OneMessageSender};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface<
|
||||
mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
|
||||
>;
|
||||
|
||||
pub type ModeRequestorOneChildMpsc = ModeRequestorInterface<
|
||||
pub type ModeRequestorMpsc = ModeRequestorInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
OneMessageSender<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorOneChildBoundedMpsc = ModeRequestorInterface<
|
||||
pub type ModeRequestorBoundedMpsc = ModeRequestorInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
OneMessageSender<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorChildListMpsc = ModeRequestorInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
pub type ModeRequestorChildListBoundedMpsc = ModeRequestorInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
>;
|
||||
|
||||
pub type ModeRequestorAndHandlerMpsc = ModeInterface<
|
||||
mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeRequest, mpsc::Sender<GenericMessage<ModeRequest>>>,
|
||||
mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeReply, mpsc::Sender<GenericMessage<ModeReply>>>,
|
||||
mpsc::Sender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
>;
|
||||
pub type ModeRequestorAndHandlerMpscBounded = ModeInterface<
|
||||
mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
MessageSenderList<ModeRequest, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
||||
mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||
MessageSenderList<ModeReply, mpsc::SyncSender<GenericMessage<ModeReply>>>,
|
||||
mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||
mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||
>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) 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<(), GenericTargetedMessagingError> {
|
||||
self.requests.borrow_mut().push_back(ModeReqWrapper {
|
||||
request_id,
|
||||
target_id,
|
||||
request,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
mod tests {}
|
||||
|
@ -2,57 +2,10 @@ use alloc::vec::Vec;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::{
|
||||
mode::{Mode, ModeAndSubmode, ModeReply, ModeRequest, Submode},
|
||||
request::MessageSenderProvider,
|
||||
mode::{Mode, ModeAndSubmode, Submode},
|
||||
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)]
|
||||
pub enum TableEntryType {
|
||||
/// Target table containing information of the expected children modes for given mode.
|
||||
@ -62,553 +15,23 @@ pub enum TableEntryType {
|
||||
Sequence,
|
||||
}
|
||||
|
||||
/// Common fields required for both target and sequence table entries.
|
||||
///
|
||||
/// The most important parameters here are the target ID which this entry belongs to, and the mode
|
||||
/// and submode the entry either will be commanded to for sequence table entries or which will be
|
||||
/// monitored for target table entries.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ModeTableEntryCommon {
|
||||
pub struct ModeTableEntry {
|
||||
/// Name of respective table entry.
|
||||
pub name: &'static str,
|
||||
/// Target component ID.
|
||||
pub target_id: ComponentId,
|
||||
/// Has a different meaning depending on whether this is a sequence table or a target table.
|
||||
///
|
||||
/// - For sequence tables, this denotes the mode which will be commanded
|
||||
/// - For target tables, this is the mode which the target children should have and which
|
||||
/// might be monitored depending on configuration.
|
||||
/// Target channel ID.
|
||||
pub channel_id: ComponentId,
|
||||
pub mode_submode: ModeAndSubmode,
|
||||
/// This mask allows to specify multiple allowed submodes for a given mode.
|
||||
pub allowed_submode_mask: Option<Submode>,
|
||||
}
|
||||
|
||||
impl ModeTableEntryCommon {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode) {
|
||||
self.allowed_submode_mask = Some(mask);
|
||||
}
|
||||
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode> {
|
||||
self.allowed_submode_mask
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry for the target tables.
|
||||
#[derive(Debug)]
|
||||
pub struct TargetTableEntry {
|
||||
pub common: ModeTableEntryCommon,
|
||||
pub monitor_state: bool,
|
||||
}
|
||||
|
||||
impl TargetTableEntry {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
allowed_submode_mask: Option<Submode>,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask,
|
||||
},
|
||||
monitor_state: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_precise_submode(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask: None,
|
||||
},
|
||||
monitor_state: true,
|
||||
}
|
||||
}
|
||||
|
||||
delegate::delegate! {
|
||||
to self.common {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry for the sequence tables.
|
||||
///
|
||||
/// The [Self::check_success] field specifies that a mode sequence executor should check that the
|
||||
/// target mode was actually reached before executing the next sequence.
|
||||
#[derive(Debug)]
|
||||
pub struct SequenceTableEntry {
|
||||
pub common: ModeTableEntryCommon,
|
||||
pub check_success: bool,
|
||||
}
|
||||
|
||||
impl SequenceTableEntry {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
target_id: ComponentId,
|
||||
mode_submode: ModeAndSubmode,
|
||||
check_success: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: ModeTableEntryCommon {
|
||||
name,
|
||||
target_id,
|
||||
mode_submode,
|
||||
allowed_submode_mask: None,
|
||||
},
|
||||
check_success,
|
||||
}
|
||||
}
|
||||
|
||||
delegate::delegate! {
|
||||
to self.common {
|
||||
pub fn set_allowed_submode_mask(&mut self, mask: Submode);
|
||||
pub fn allowed_submode_mask(&self) -> Option<Submode>;
|
||||
}
|
||||
}
|
||||
pub struct ModeTableMapValue {
|
||||
/// Name for a given mode table entry.
|
||||
pub name: &'static str,
|
||||
pub entries: Vec<ModeTableEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("target {0} not in mode store")]
|
||||
pub struct TargetNotInModeStoreError(pub ComponentId);
|
||||
|
||||
/// Mode store value type.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ModeStoreValue {
|
||||
/// ID of the mode component.
|
||||
id: ComponentId,
|
||||
/// Current mode and submode of the component.
|
||||
pub mode_and_submode: ModeAndSubmode,
|
||||
/// State information to track whether a reply should be awaited for the mode component.
|
||||
pub awaiting_reply: bool,
|
||||
}
|
||||
|
||||
impl ModeStoreValue {
|
||||
pub fn new(id: ComponentId, mode_and_submode: ModeAndSubmode) -> Self {
|
||||
Self {
|
||||
id,
|
||||
mode_and_submode,
|
||||
awaiting_reply: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ComponentId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||
self.mode_and_submode
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ModeStoreProvider {
|
||||
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode);
|
||||
|
||||
fn has_component(&self, target_id: ComponentId) -> bool;
|
||||
|
||||
fn get(&self, target_id: ComponentId) -> Option<&ModeStoreValue>;
|
||||
|
||||
fn get_mut(&mut self, target_id: ComponentId) -> Option<&mut ModeStoreValue>;
|
||||
|
||||
/// Generic handler for mode replies received from child components.
|
||||
///
|
||||
/// Implementation should clear the awaition flag if the `handle_reply_awaition` argument is
|
||||
/// true and returns whether any children are still awaiting replies. If the flag is not set
|
||||
fn mode_reply_handler_with_reply_awaition(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
) -> bool {
|
||||
self.mode_reply_handler(sender_id, reported_mode_and_submode, true)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn mode_reply_handler_without_reply_awaition(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
) {
|
||||
self.mode_reply_handler(sender_id, reported_mode_and_submode, false);
|
||||
}
|
||||
|
||||
fn mode_reply_handler(
|
||||
&mut self,
|
||||
sender_id: ComponentId,
|
||||
reported_mode_and_submode: Option<ModeAndSubmode>,
|
||||
with_reply_awaition: bool,
|
||||
) -> Option<bool>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use 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
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type ModeTable = HashMap<Mode, ModeTableMapValue>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn generic_test(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
assert!(mode_store.has_component(1));
|
||||
assert!(mode_store.has_component(2));
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(0, 0)
|
||||
);
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(mode_store.get(1).unwrap().id, 1);
|
||||
assert_eq!(mode_store.get(2).unwrap().id, 2);
|
||||
assert!(mode_store.get(3).is_none());
|
||||
assert!(mode_store.get_mut(3).is_none());
|
||||
}
|
||||
|
||||
fn generic_reply_handling_with_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
let mut reply_awation_pending =
|
||||
mode_store.mode_reply_handler_with_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
|
||||
assert!(reply_awation_pending);
|
||||
reply_awation_pending = mode_store.mode_reply_handler_with_reply_awaition(2, None);
|
||||
assert!(!reply_awation_pending);
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
fn generic_reply_handling_test_no_reply_awaition(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
mode_store.mode_reply_handler_without_reply_awaition(1, Some(ModeAndSubmode::new(2, 0)));
|
||||
mode_store.mode_reply_handler_without_reply_awaition(2, None);
|
||||
assert!(mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
fn generic_reply_handling_with_reply_awaition_2(mode_store: &mut impl ModeStoreProvider) {
|
||||
mode_store.add_component(1, ModeAndSubmode::new(0, 0));
|
||||
mode_store.add_component(2, ModeAndSubmode::new(1, 0));
|
||||
mode_store.get_mut(1).unwrap().awaiting_reply = true;
|
||||
mode_store.get_mut(2).unwrap().awaiting_reply = true;
|
||||
let mut reply_awation_pending =
|
||||
mode_store.mode_reply_handler(1, Some(ModeAndSubmode::new(2, 0)), true);
|
||||
assert!(reply_awation_pending.unwrap());
|
||||
reply_awation_pending = mode_store.mode_reply_handler(2, None, true);
|
||||
assert!(!reply_awation_pending.unwrap());
|
||||
assert!(!mode_store.get(1).unwrap().awaiting_reply);
|
||||
assert!(!mode_store.get(2).unwrap().awaiting_reply);
|
||||
assert_eq!(
|
||||
mode_store.get(1).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
mode_store.get(2).unwrap().mode_and_submode(),
|
||||
ModeAndSubmode::new(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_mode_store() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_test(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_mode_store() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_test(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_vec_with_reply_awaition() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_reply_handling_with_reply_awaition(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_vec_with_reply_awaition_2() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_map_with_reply_awaition() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_reply_handling_with_reply_awaition(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_map_with_reply_awaition_2() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_reply_handling_with_reply_awaition_2(&mut mode_store);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_reply_handler_vec_no_reply_awaition() {
|
||||
let mut mode_store = ModeStoreVec::default();
|
||||
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
|
||||
}
|
||||
#[test]
|
||||
fn test_generic_reply_handler_map_no_reply_awaition() {
|
||||
let mut mode_store = ModeStoreMap::default();
|
||||
generic_reply_handling_test_no_reply_awaition(&mut mode_store);
|
||||
}
|
||||
}
|
||||
mod tests {}
|
||||
|
@ -378,74 +378,27 @@ pub struct SubpoolConfig {
|
||||
#[cfg(feature = "heapless")]
|
||||
pub mod heapless_mod {
|
||||
use super::*;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PoolIsFull;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnsafeCellBufWrapper<T> {
|
||||
val: UnsafeCell<T>,
|
||||
once: AtomicBool,
|
||||
}
|
||||
// `Sync` is required because `UnsafeCell` is not `Sync` by default.
|
||||
// This is safe as long as access is manually synchronized.
|
||||
unsafe impl<T> Sync for UnsafeCellBufWrapper<T> {}
|
||||
|
||||
impl<T: Sync> UnsafeCellBufWrapper<T> {
|
||||
/// Creates a new wrapper around an arbitrary value which should be [Sync].
|
||||
pub const fn new(v: T) -> Self {
|
||||
unsafe { Self::new_unchecked(v) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UnsafeCellBufWrapper<T> {
|
||||
/// Creates a new wrapper around a buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Currently, the [Sync] trait is implemented for all T and ignores the usual [Sync] bound
|
||||
/// on T. This API should only be called for declaring byte buffers statically or if T is
|
||||
/// known to be [Sync]. You can use [new] to let the compiler do the [Sync] check.
|
||||
pub const unsafe fn new_unchecked(v: T) -> Self {
|
||||
Self {
|
||||
val: UnsafeCell::new(v),
|
||||
once: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves a mutable reference to the internal value once.
|
||||
///
|
||||
/// All subsequent calls return None.
|
||||
pub fn get_mut(&self) -> Option<&mut T> {
|
||||
if self.once.load(Ordering::Relaxed) {
|
||||
return None;
|
||||
}
|
||||
// Safety: We ensure that this is only done once with an [AtomicBool].
|
||||
let mut_ref = unsafe { &mut *self.val.get() };
|
||||
self.once.store(true, Ordering::Relaxed);
|
||||
Some(mut_ref)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper macro to generate static buffers for the [crate::pool::StaticHeaplessMemoryPool].
|
||||
#[macro_export]
|
||||
macro_rules! static_subpool {
|
||||
($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr) => {
|
||||
static $pool_name: $crate::pool::UnsafeCellBufWrapper<[u8; $num_blocks * $block_size]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([0; $num_blocks * $block_size]);
|
||||
static $sizes_list_name: $crate::pool::UnsafeCellBufWrapper<[usize; $num_blocks]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
static mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
|
||||
core::mem::MaybeUninit::new([0; $num_blocks * $block_size]);
|
||||
static mut $sizes_list_name: core::mem::MaybeUninit<[usize; $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) => {
|
||||
#[$meta_data]
|
||||
static $pool_name: $crate::pool::UnsafeCellBufWrapper<[u8; $num_blocks * $block_size]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([0; $num_blocks * $block_size]);
|
||||
static mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
|
||||
core::mem::MaybeUninit::new([0; $num_blocks * $block_size]);
|
||||
#[$meta_data]
|
||||
static $sizes_list_name: $crate::pool::UnsafeCellBufWrapper<[usize; $num_blocks]> =
|
||||
$crate::pool::UnsafeCellBufWrapper::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
static mut $sizes_list_name: core::mem::MaybeUninit<[usize; $num_blocks]> =
|
||||
core::mem::MaybeUninit::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
};
|
||||
}
|
||||
|
||||
@ -482,17 +435,17 @@ pub mod heapless_mod {
|
||||
///
|
||||
/// let mut mem_pool: StaticHeaplessMemoryPool<2> = StaticHeaplessMemoryPool::new(true);
|
||||
/// mem_pool.grow(
|
||||
/// SUBPOOL_SMALL.get_mut().unwrap(),
|
||||
/// SUBPOOL_SMALL_SIZES.get_mut().unwrap(),
|
||||
/// unsafe { SUBPOOL_SMALL.assume_init_mut() },
|
||||
/// unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
|
||||
/// SUBPOOL_SMALL_NUM_BLOCKS,
|
||||
/// false
|
||||
/// ).unwrap();
|
||||
/// );
|
||||
/// mem_pool.grow(
|
||||
/// SUBPOOL_LARGE.get_mut().unwrap(),
|
||||
/// SUBPOOL_LARGE_SIZES.get_mut().unwrap(),
|
||||
/// unsafe { SUBPOOL_LARGE.assume_init_mut() },
|
||||
/// unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
|
||||
/// SUBPOOL_LARGE_NUM_BLOCKS,
|
||||
/// false
|
||||
/// ).unwrap();
|
||||
/// );
|
||||
///
|
||||
/// let mut read_buf: [u8; 16] = [0; 16];
|
||||
/// let mut addr;
|
||||
@ -569,14 +522,12 @@ pub mod heapless_mod {
|
||||
num_blocks: NumBlocks,
|
||||
set_sizes_list_to_all_free: bool,
|
||||
) -> Result<(), PoolIsFull> {
|
||||
assert_eq!(
|
||||
(subpool_memory.len() % num_blocks as usize),
|
||||
0,
|
||||
assert!(
|
||||
(subpool_memory.len() % num_blocks as usize) == 0,
|
||||
"pool slice length must be multiple of number of blocks"
|
||||
);
|
||||
assert_eq!(
|
||||
num_blocks as usize,
|
||||
sizes_list.len(),
|
||||
assert!(
|
||||
num_blocks as usize == sizes_list.len(),
|
||||
"used block size list slice must be of same length as number of blocks"
|
||||
);
|
||||
let subpool_config = SubpoolConfig {
|
||||
@ -1633,28 +1584,23 @@ mod tests {
|
||||
mod heapless_tests {
|
||||
use super::*;
|
||||
use crate::static_subpool;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::Mutex;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
const SUBPOOL_1_BLOCK_SIZE: usize = 4;
|
||||
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
|
||||
|
||||
static SUBPOOL_1: UnsafeCellBufWrapper<
|
||||
static mut SUBPOOL_1: MaybeUninit<
|
||||
[u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
||||
> = UnsafeCellBufWrapper::new([0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE]);
|
||||
|
||||
static SUBPOOL_1_SIZES: Mutex<UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>> =
|
||||
Mutex::new(UnsafeCell::new(
|
||||
[STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize],
|
||||
));
|
||||
> = MaybeUninit::new([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]);
|
||||
|
||||
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
|
||||
const SUBPOOL_2_BLOCK_SIZE: usize = 8;
|
||||
static SUBPOOL_2: UnsafeCellBufWrapper<
|
||||
static mut SUBPOOL_2: MaybeUninit<
|
||||
[u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
||||
> = UnsafeCellBufWrapper::new([0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE]);
|
||||
static SUBPOOL_2_SIZES: UnsafeCellBufWrapper<[usize; SUBPOOL_2_NUM_ELEMENTS as usize]> =
|
||||
UnsafeCellBufWrapper::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
|
||||
> = MaybeUninit::new([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]);
|
||||
|
||||
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
|
||||
const SUBPOOL_3_BLOCK_SIZE: usize = 16;
|
||||
@ -1697,26 +1643,26 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(false);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_1.get_mut().unwrap(),
|
||||
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
|
||||
unsafe { SUBPOOL_1.assume_init_mut() },
|
||||
unsafe { SUBPOOL_1_SIZES.assume_init_mut() },
|
||||
SUBPOOL_1_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_2.get_mut().unwrap(),
|
||||
SUBPOOL_2_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_2.assume_init_mut() },
|
||||
unsafe { SUBPOOL_2_SIZES.assume_init_mut() },
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
heapless_pool
|
||||
@ -1836,18 +1782,18 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_2.get_mut().unwrap(),
|
||||
SUBPOOL_2_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_2.assume_init_mut() },
|
||||
unsafe { SUBPOOL_2_SIZES.assume_init_mut() },
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_4.get_mut().unwrap(),
|
||||
SUBPOOL_4_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_4.assume_init_mut() },
|
||||
unsafe { SUBPOOL_4_SIZES.assume_init_mut() },
|
||||
SUBPOOL_4_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spills_to_higher_subpools(&mut heapless_pool);
|
||||
@ -1859,18 +1805,18 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spillage_fails_as_well(&mut heapless_pool);
|
||||
@ -1882,26 +1828,26 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_6.get_mut().unwrap(),
|
||||
SUBPOOL_6_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_6.assume_init_mut() },
|
||||
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spillage_works_across_multiple_subpools(&mut heapless_pool);
|
||||
@ -1913,26 +1859,26 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_5.get_mut().unwrap(),
|
||||
SUBPOOL_5_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_6.get_mut().unwrap(),
|
||||
SUBPOOL_6_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_6.assume_init_mut() },
|
||||
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.get_mut().unwrap(),
|
||||
SUBPOOL_3_SIZES.get_mut().unwrap(),
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
false
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spillage_fails_across_multiple_subpools(&mut heapless_pool);
|
||||
|
@ -68,8 +68,7 @@ pub mod alloc_mod {
|
||||
action::ActionRequest,
|
||||
queue::GenericTargetedMessagingError,
|
||||
request::{
|
||||
GenericMessage, MessageReceiverProvider, MessageSenderAndReceiver,
|
||||
MessageSenderProvider, MessageSenderStoreProvider, RequestId,
|
||||
GenericMessage, MessageReceiver, MessageSender, MessageSenderAndReceiver, RequestId,
|
||||
},
|
||||
ComponentId,
|
||||
};
|
||||
@ -77,14 +76,11 @@ pub mod alloc_mod {
|
||||
use super::ActionReplyPus;
|
||||
|
||||
/// Helper type definition for a mode handler which can handle mode requests.
|
||||
pub type ActionRequestHandlerInterface<Sender, Receiver, ReplySenderStore> =
|
||||
MessageSenderAndReceiver<ActionReplyPus, ActionRequest, Sender, Receiver, ReplySenderStore>;
|
||||
pub type ActionRequestHandlerInterface<S, R> =
|
||||
MessageSenderAndReceiver<ActionReplyPus, ActionRequest, S, R>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ActionReplyPus>,
|
||||
Receiver: MessageReceiverProvider<ActionRequest>,
|
||||
ReplySender: MessageSenderStoreProvider<ActionReplyPus, Sender>,
|
||||
> ActionRequestHandlerInterface<Sender, Receiver, ReplySender>
|
||||
impl<S: MessageSender<ActionReplyPus>, R: MessageReceiver<ActionRequest>>
|
||||
ActionRequestHandlerInterface<S, R>
|
||||
{
|
||||
pub fn try_recv_action_request(
|
||||
&self,
|
||||
@ -104,20 +100,11 @@ pub mod alloc_mod {
|
||||
|
||||
/// Helper type defintion for a mode handler object which can send mode requests and receive
|
||||
/// mode replies.
|
||||
pub type ActionRequestorInterface<Sender, Receiver, RequestSenderStore> =
|
||||
MessageSenderAndReceiver<
|
||||
ActionRequest,
|
||||
ActionReplyPus,
|
||||
Sender,
|
||||
Receiver,
|
||||
RequestSenderStore,
|
||||
>;
|
||||
pub type ActionRequestorInterface<S, R> =
|
||||
MessageSenderAndReceiver<ActionRequest, ActionReplyPus, S, R>;
|
||||
|
||||
impl<
|
||||
Sender: MessageSenderProvider<ActionRequest>,
|
||||
Receiver: MessageReceiverProvider<ActionReplyPus>,
|
||||
RequestSenderStore: MessageSenderStoreProvider<ActionRequest, Sender>,
|
||||
> ActionRequestorInterface<Sender, Receiver, RequestSenderStore>
|
||||
impl<S: MessageSender<ActionRequest>, R: MessageReceiver<ActionReplyPus>>
|
||||
ActionRequestorInterface<S, R>
|
||||
{
|
||||
pub fn try_recv_action_reply(
|
||||
&self,
|
||||
@ -145,7 +132,6 @@ pub mod std_mod {
|
||||
verification::{self, TcStateToken},
|
||||
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap,
|
||||
},
|
||||
request::{MessageSenderMap, OneMessageSender},
|
||||
ComponentId,
|
||||
};
|
||||
|
||||
@ -188,38 +174,22 @@ pub mod std_mod {
|
||||
}
|
||||
pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActivePusActionRequestStd>;
|
||||
|
||||
pub type ActionRequestHandlerOneSenderMpsc = ActionRequestHandlerInterface<
|
||||
pub type ActionRequestHandlerMpsc = ActionRequestHandlerInterface<
|
||||
mpsc::Sender<GenericMessage<ActionReplyPus>>,
|
||||
mpsc::Receiver<GenericMessage<ActionRequest>>,
|
||||
OneMessageSender<
|
||||
GenericMessage<ActionReplyPus>,
|
||||
mpsc::Sender<GenericMessage<ActionReplyPus>>,
|
||||
>,
|
||||
>;
|
||||
pub type ActionRequestHandlerOneSenderMpscBounded = ActionRequestHandlerInterface<
|
||||
pub type ActionRequestHandlerMpscBounded = ActionRequestHandlerInterface<
|
||||
mpsc::SyncSender<GenericMessage<ActionReplyPus>>,
|
||||
mpsc::Receiver<GenericMessage<ActionRequest>>,
|
||||
OneMessageSender<
|
||||
GenericMessage<ActionReplyPus>,
|
||||
mpsc::SyncSender<GenericMessage<ActionReplyPus>>,
|
||||
>,
|
||||
>;
|
||||
|
||||
pub type ActionRequestorWithSenderMapMpsc = ActionRequestorInterface<
|
||||
pub type ActionRequestorMpsc = ActionRequestorInterface<
|
||||
mpsc::Sender<GenericMessage<ActionRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ActionReplyPus>>,
|
||||
MessageSenderMap<
|
||||
GenericMessage<ActionRequest>,
|
||||
mpsc::Sender<GenericMessage<ActionRequest>>,
|
||||
>,
|
||||
>;
|
||||
pub type ActionRequestorWithSenderMapBoundedMpsc = ActionRequestorInterface<
|
||||
pub type ActionRequestorBoundedMpsc = ActionRequestorInterface<
|
||||
mpsc::SyncSender<GenericMessage<ActionRequest>>,
|
||||
mpsc::Receiver<GenericMessage<ActionReplyPus>>,
|
||||
MessageSenderMap<
|
||||
GenericMessage<ActionRequest>,
|
||||
mpsc::SyncSender<GenericMessage<ActionRequest>>,
|
||||
>,
|
||||
>;
|
||||
}
|
||||
|
||||
|
@ -1221,9 +1221,8 @@ pub(crate) fn source_buffer_large_enough(
|
||||
|
||||
#[cfg(any(feature = "test_util", test))]
|
||||
pub mod test_util {
|
||||
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
|
||||
|
||||
use crate::request::UniqueApidTargetId;
|
||||
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
|
||||
|
||||
use super::{
|
||||
verification::{self, TcStateAccepted, VerificationToken},
|
||||
@ -1233,7 +1232,6 @@ pub mod test_util {
|
||||
pub const TEST_APID: u16 = 0x101;
|
||||
pub const TEST_UNIQUE_ID_0: u32 = 0x05;
|
||||
pub const TEST_UNIQUE_ID_1: u32 = 0x06;
|
||||
|
||||
pub const TEST_COMPONENT_ID_0: UniqueApidTargetId =
|
||||
UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0);
|
||||
pub const TEST_COMPONENT_ID_1: UniqueApidTargetId =
|
||||
@ -1270,13 +1268,14 @@ pub mod tests {
|
||||
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
|
||||
use spacepackets::ecss::{PusPacket, WritablePusPacket};
|
||||
use spacepackets::CcsdsPacket;
|
||||
use test_util::{TEST_APID, TEST_COMPONENT_ID_0};
|
||||
|
||||
use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
|
||||
use crate::pus::verification::{RequestId, VerificationReporter};
|
||||
use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool};
|
||||
use crate::ComponentId;
|
||||
|
||||
use super::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
|
||||
|
||||
use super::verification::test_util::TestVerificationReporter;
|
||||
use super::verification::{
|
||||
TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
|
||||
|
@ -39,7 +39,7 @@ mod tests {
|
||||
use crate::{
|
||||
mode::{
|
||||
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
|
||||
ModeRequestorAndHandlerMpsc, ModeRequestorOneChildMpsc,
|
||||
ModeRequestorAndHandlerMpsc, ModeRequestorMpsc,
|
||||
},
|
||||
request::{GenericMessage, MessageMetadata},
|
||||
};
|
||||
@ -52,8 +52,7 @@ mod tests {
|
||||
fn test_simple_mode_requestor() {
|
||||
let (reply_sender, reply_receiver) = mpsc::channel();
|
||||
let (request_sender, request_receiver) = mpsc::channel();
|
||||
let mut mode_requestor =
|
||||
ModeRequestorOneChildMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
|
||||
let mut mode_requestor = ModeRequestorMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
|
||||
mode_requestor.add_message_target(TEST_COMPONENT_ID_1, request_sender);
|
||||
|
||||
// Send a request and verify it arrives at the receiver.
|
||||
|
@ -20,7 +20,7 @@
|
||||
//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporter
|
||||
//! };
|
||||
//! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool};
|
||||
//! use satrs::spacepackets::seq_count::SeqCountProviderSimple;
|
||||
//! use satrs::seq_count::SeqCountProviderSimple;
|
||||
//! use satrs::request::UniqueApidTargetId;
|
||||
//! use spacepackets::ecss::PusPacket;
|
||||
//! use spacepackets::SpHeader;
|
||||
@ -97,8 +97,8 @@ use spacepackets::ecss::EcssEnumeration;
|
||||
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceCtrl};
|
||||
use spacepackets::{SpHeader, MAX_APID};
|
||||
|
||||
pub use crate::seq_count::SeqCountProviderSimple;
|
||||
pub use spacepackets::ecss::verification::*;
|
||||
pub use spacepackets::seq_count::SeqCountProviderSimple;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
@ -1702,7 +1702,7 @@ pub mod tests {
|
||||
};
|
||||
use crate::pus::{ChannelWithId, PusTmVariant};
|
||||
use crate::request::MessageMetadata;
|
||||
use crate::spacepackets::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProvider};
|
||||
use crate::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore};
|
||||
use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
||||
use crate::ComponentId;
|
||||
use alloc::format;
|
||||
|
@ -1,3 +1,6 @@
|
||||
use core::fmt::{Display, Formatter};
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::mpsc;
|
||||
|
||||
@ -7,31 +10,89 @@ use crate::ComponentId;
|
||||
pub type ChannelId = u32;
|
||||
|
||||
/// 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 {
|
||||
#[error("rx side has disconnected")]
|
||||
RxDisconnected,
|
||||
#[error("queue with max capacity of {0:?} is full")]
|
||||
QueueFull(Option<u32>),
|
||||
#[error("target queue with ID {0} does not exist")]
|
||||
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.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum GenericReceiveError {
|
||||
#[error("nothing to receive")]
|
||||
Empty,
|
||||
#[error("tx side with id {0:?} has disconnected")]
|
||||
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 {
|
||||
#[error("generic targeted messaging send error: {0}")]
|
||||
Send(#[from] GenericSendError),
|
||||
#[error("generic targeted messaging receive error: {0}")]
|
||||
Receive(#[from] GenericReceiveError),
|
||||
Send(GenericSendError),
|
||||
Receive(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")]
|
||||
|
@ -140,27 +140,24 @@ impl<Message> GenericMessage<Message> {
|
||||
}
|
||||
|
||||
/// 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<(), GenericTargetedMessagingError>;
|
||||
}
|
||||
|
||||
// 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>>, GenericTargetedMessagingError>;
|
||||
}
|
||||
|
||||
pub struct MessageWithSenderIdReceiver<Msg, Receiver: MessageReceiverProvider<Msg>>(
|
||||
pub Receiver,
|
||||
PhantomData<Msg>,
|
||||
);
|
||||
pub struct MessageWithSenderIdReceiver<MSG, R: MessageReceiver<MSG>>(pub R, 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 {
|
||||
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>>, GenericTargetedMessagingError> {
|
||||
@ -168,12 +165,12 @@ impl<MSG, R: MessageReceiverProvider<MSG>> MessageWithSenderIdReceiver<MSG, R> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MessageReceiverWithId<MSG, R: MessageReceiverProvider<MSG>> {
|
||||
pub struct MessageReceiverWithId<MSG, R: MessageReceiver<MSG>> {
|
||||
local_channel_id: ComponentId,
|
||||
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 {
|
||||
Self {
|
||||
local_channel_id,
|
||||
@ -186,7 +183,7 @@ 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>>, GenericTargetedMessagingError> {
|
||||
@ -194,122 +191,34 @@ impl<MSG, R: MessageReceiverProvider<MSG>> MessageReceiverWithId<MSG, R> {
|
||||
}
|
||||
}
|
||||
|
||||
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<(), GenericTargetedMessagingError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use crate::queue::GenericSendError;
|
||||
use std::convert::From;
|
||||
|
||||
use super::*;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
pub struct OneMessageSender<Msg, S: MessageSenderProvider<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<(), GenericTargetedMessagingError> {
|
||||
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).into())
|
||||
}
|
||||
}
|
||||
|
||||
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<(), GenericTargetedMessagingError> {
|
||||
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).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MessageSenderMap<MSG, S: MessageSenderProvider<MSG>>(
|
||||
pub struct MessageSenderMap<MSG, S: MessageSender<MSG>>(
|
||||
pub HashMap<ComponentId, S>,
|
||||
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 {
|
||||
Self(Default::default(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Msg, Sender: MessageSenderProvider<Msg>> MessageSenderStoreProvider<Msg, Sender>
|
||||
for MessageSenderMap<Msg, Sender>
|
||||
{
|
||||
fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
|
||||
impl<MSG, S: MessageSender<MSG>> MessageSenderMap<MSG, S> {
|
||||
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
|
||||
self.0.insert(target_id, message_sender);
|
||||
}
|
||||
|
||||
fn send_message(
|
||||
pub fn send_message(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
target_channel_id: ComponentId,
|
||||
message: Msg,
|
||||
message: MSG,
|
||||
) -> Result<(), GenericTargetedMessagingError> {
|
||||
if self.0.contains_key(&target_channel_id) {
|
||||
return self
|
||||
@ -322,38 +231,25 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MessageSenderAndReceiver<
|
||||
To,
|
||||
From,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> {
|
||||
pub struct MessageSenderAndReceiver<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>> {
|
||||
pub local_channel_id: ComponentId,
|
||||
pub message_sender_store: SenderStore,
|
||||
pub message_receiver: MessageWithSenderIdReceiver<From, Receiver>,
|
||||
pub(crate) phantom: PhantomData<(To, Sender)>,
|
||||
pub message_sender_map: MessageSenderMap<TO, S>,
|
||||
pub message_receiver: MessageWithSenderIdReceiver<FROM, R>,
|
||||
}
|
||||
|
||||
impl<
|
||||
To,
|
||||
From,
|
||||
Sender: MessageSenderProvider<To>,
|
||||
Receiver: MessageReceiverProvider<From>,
|
||||
SenderStore: MessageSenderStoreProvider<To, Sender>,
|
||||
> MessageSenderAndReceiver<To, From, Sender, Receiver, SenderStore>
|
||||
impl<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>>
|
||||
MessageSenderAndReceiver<TO, FROM, S, R>
|
||||
{
|
||||
pub fn new(local_channel_id: ComponentId, message_receiver: Receiver) -> Self {
|
||||
pub fn new(local_channel_id: ComponentId, message_receiver: R) -> Self {
|
||||
Self {
|
||||
local_channel_id,
|
||||
message_sender_store: Default::default(),
|
||||
message_sender_map: Default::default(),
|
||||
message_receiver: MessageWithSenderIdReceiver::from(message_receiver),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: Sender) {
|
||||
self.message_sender_store
|
||||
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
|
||||
self.message_sender_map
|
||||
.add_message_target(target_id, message_sender)
|
||||
}
|
||||
|
||||
@ -366,9 +262,9 @@ pub mod alloc_mod {
|
||||
&self,
|
||||
request_id: RequestId,
|
||||
target_id: ComponentId,
|
||||
message: To,
|
||||
message: TO,
|
||||
) -> Result<(), GenericTargetedMessagingError> {
|
||||
self.message_sender_store.send_message(
|
||||
self.message_sender_map.send_message(
|
||||
MessageMetadata::new(request_id, self.local_channel_id_generic()),
|
||||
target_id,
|
||||
message,
|
||||
@ -378,64 +274,48 @@ pub mod alloc_mod {
|
||||
/// Try to receive a message, which can be a reply or a request, depending on the generics.
|
||||
pub fn try_recv_message(
|
||||
&self,
|
||||
) -> Result<Option<GenericMessage<From>>, GenericTargetedMessagingError> {
|
||||
) -> Result<Option<GenericMessage<FROM>>, GenericTargetedMessagingError> {
|
||||
self.message_receiver.try_recv_message()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
REQUEST,
|
||||
REPLY,
|
||||
S0: MessageSender<REQUEST>,
|
||||
R0: MessageReceiver<REPLY>,
|
||||
S1: MessageSender<REPLY>,
|
||||
R1: MessageReceiver<REQUEST>,
|
||||
> {
|
||||
pub local_channel_id: ComponentId,
|
||||
// These 2 are a functional group.
|
||||
pub request_sender_store: ReqSenderStore,
|
||||
pub reply_receiver: MessageWithSenderIdReceiver<Reply, ReplyReceiver>,
|
||||
pub request_sender_map: MessageSenderMap<REQUEST, S0>,
|
||||
pub reply_receiver: MessageWithSenderIdReceiver<REPLY, R0>,
|
||||
// These 2 are a functional group.
|
||||
pub request_receiver: MessageWithSenderIdReceiver<Request, ReqReceiver>,
|
||||
pub reply_sender_store: ReplySenderStore,
|
||||
phantom: PhantomData<(ReqSender, ReplySender)>,
|
||||
pub request_receiver: MessageWithSenderIdReceiver<REQUEST, R1>,
|
||||
pub reply_sender_map: MessageSenderMap<REPLY, S1>,
|
||||
}
|
||||
|
||||
impl<
|
||||
Request,
|
||||
ReqSender: MessageSenderProvider<Request>,
|
||||
ReqReceiver: MessageReceiverProvider<Request>,
|
||||
ReqSenderStore: MessageSenderStoreProvider<Request, ReqSender>,
|
||||
Reply,
|
||||
ReplySender: MessageSenderProvider<Reply>,
|
||||
ReplyReceiver: MessageReceiverProvider<Reply>,
|
||||
ReplySenderStore: MessageSenderStoreProvider<Reply, ReplySender>,
|
||||
>
|
||||
RequestAndReplySenderAndReceiver<
|
||||
Request,
|
||||
ReqSender,
|
||||
ReqReceiver,
|
||||
ReqSenderStore,
|
||||
Reply,
|
||||
ReplySender,
|
||||
ReplyReceiver,
|
||||
ReplySenderStore,
|
||||
>
|
||||
REQUEST,
|
||||
REPLY,
|
||||
S0: MessageSender<REQUEST>,
|
||||
R0: MessageReceiver<REPLY>,
|
||||
S1: MessageSender<REPLY>,
|
||||
R1: MessageReceiver<REQUEST>,
|
||||
> RequestAndReplySenderAndReceiver<REQUEST, REPLY, S0, R0, S1, R1>
|
||||
{
|
||||
pub fn new(
|
||||
local_channel_id: ComponentId,
|
||||
request_receiver: ReqReceiver,
|
||||
reply_receiver: ReplyReceiver,
|
||||
request_receiver: R1,
|
||||
reply_receiver: R0,
|
||||
) -> Self {
|
||||
Self {
|
||||
local_channel_id,
|
||||
request_receiver: request_receiver.into(),
|
||||
reply_receiver: reply_receiver.into(),
|
||||
request_sender_store: Default::default(),
|
||||
reply_sender_store: Default::default(),
|
||||
phantom: PhantomData,
|
||||
request_sender_map: Default::default(),
|
||||
reply_sender_map: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,14 +333,14 @@ pub mod std_mod {
|
||||
|
||||
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<(), GenericTargetedMessagingError> {
|
||||
self.send(message)
|
||||
.map_err(|_| GenericSendError::RxDisconnected)?;
|
||||
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<(), GenericTargetedMessagingError> {
|
||||
if let Err(e) = self.try_send(message) {
|
||||
return match e {
|
||||
@ -477,7 +357,7 @@ pub mod std_mod {
|
||||
pub type MessageSenderMapMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::Sender<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>>, GenericTargetedMessagingError> {
|
||||
match self.try_recv() {
|
||||
Ok(msg) => Ok(Some(msg)),
|
||||
@ -506,7 +386,7 @@ mod tests {
|
||||
|
||||
use crate::{
|
||||
queue::{GenericReceiveError, GenericSendError, GenericTargetedMessagingError},
|
||||
request::{MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
|
||||
request::{MessageMetadata, MessageSenderMap},
|
||||
};
|
||||
|
||||
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
|
||||
|
@ -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
250
satrs/src/seq_count.rs
Normal 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
@ -17,37 +17,37 @@ impl PusTmWithCdsShortHelper {
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn create_pus_tm_timestamp_now<'data>(
|
||||
&mut self,
|
||||
pub fn create_pus_tm_timestamp_now<'a>(
|
||||
&'a mut self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
source_data: &'a [u8],
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
) -> PusTmCreator {
|
||||
let time_stamp = CdsTime::now_with_u16_days().unwrap();
|
||||
time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||
self.create_pus_tm_common(service, subservice, source_data, seq_count)
|
||||
}
|
||||
|
||||
pub fn create_pus_tm_with_stamper<'data>(
|
||||
&mut self,
|
||||
pub fn create_pus_tm_with_stamper<'a>(
|
||||
&'a mut self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
source_data: &'a [u8],
|
||||
stamper: &CdsTime,
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
) -> PusTmCreator {
|
||||
stamper.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||
self.create_pus_tm_common(service, subservice, source_data, seq_count)
|
||||
}
|
||||
|
||||
fn create_pus_tm_common<'data>(
|
||||
&self,
|
||||
fn create_pus_tm_common<'a>(
|
||||
&'a self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
source_data: &'a [u8],
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
) -> PusTmCreator {
|
||||
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
|
||||
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
|
||||
PusTmCreator::new(reply_header, tc_header, source_data, true)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@ use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
|
||||
use satrs::params::U32Pair;
|
||||
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
|
||||
use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap};
|
||||
use satrs::pus::test_util::TEST_COMPONENT_ID_0;
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
use satrs::tmtc::PacketAsVec;
|
||||
use spacepackets::ecss::tm::PusTmReader;
|
||||
@ -99,7 +100,10 @@ fn test_threaded_usage() {
|
||||
// Event sender and TM checker thread
|
||||
let jh1 = thread::spawn(move || {
|
||||
event_tx
|
||||
.send(EventMessage::new(TEST_ID.id(), INFO_EVENT.into()))
|
||||
.send(EventMessage::new(
|
||||
TEST_COMPONENT_ID_0.id(),
|
||||
INFO_EVENT.into(),
|
||||
))
|
||||
.expect("Sending info event failed");
|
||||
loop {
|
||||
match event_packet_rx.try_recv() {
|
||||
@ -126,7 +130,7 @@ fn test_threaded_usage() {
|
||||
}
|
||||
event_tx
|
||||
.send(EventMessage::new_with_params(
|
||||
TEST_ID.id(),
|
||||
TEST_COMPONENT_ID_0.id(),
|
||||
LOW_SEV_EVENT,
|
||||
&Params::Heapless((2_u32, 3_u32).into()),
|
||||
))
|
||||
|
1
serialization-prototyping/.gitignore
vendored
Normal file
1
serialization-prototyping/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
1242
serialization-prototyping/Cargo.lock
generated
Normal file
1242
serialization-prototyping/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
serialization-prototyping/Cargo.toml
Normal file
14
serialization-prototyping/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "msg-pack-test"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
satrs-minisim = { version = "0.1", path = "../satrs-minisim" }
|
||||
satrs = { version = "0.2", path = "../satrs" }
|
||||
rmp-serde = "1"
|
||||
rmpv = { version = "1", features = ["with-serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
1
serialization-prototyping/python-test/.gitignore
vendored
Normal file
1
serialization-prototyping/python-test/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/venv
|
51
serialization-prototyping/python-test/main.py
Executable file
51
serialization-prototyping/python-test/main.py
Executable file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
import enum
|
||||
from socket import AF_INET, SOCK_DGRAM, socket
|
||||
from pydantic import BaseModel
|
||||
import msgpack
|
||||
|
||||
|
||||
TEST_PERSON = {
|
||||
"age": 24,
|
||||
"name": "Nadine",
|
||||
}
|
||||
|
||||
|
||||
class Devices(str, enum.Enum):
|
||||
MGT = "Mgt"
|
||||
MGM = "Mgm"
|
||||
|
||||
|
||||
class SwitchState(str, enum.Enum):
|
||||
OFF = "Off"
|
||||
ON = "On"
|
||||
UNKNOWN = "Unknown"
|
||||
FAULTY = "Faulty"
|
||||
|
||||
|
||||
class SwitchMap(BaseModel):
|
||||
valid: bool
|
||||
switch_map: dict[Devices, SwitchState]
|
||||
|
||||
|
||||
def msg_pack_unloading(recv_back: bytes):
|
||||
unpacked = msgpack.unpackb(recv_back)
|
||||
print(f"unpacked: {unpacked}")
|
||||
loaded_back = msgpack.loads(recv_back)
|
||||
print(loaded_back)
|
||||
|
||||
|
||||
def main():
|
||||
server_socket = socket(AF_INET, SOCK_DGRAM)
|
||||
target_address = "localhost", 7301
|
||||
msg_pack_stuff = msgpack.packb(TEST_PERSON)
|
||||
assert msg_pack_stuff is not None
|
||||
_ = server_socket.sendto(msg_pack_stuff, target_address)
|
||||
recv_back = server_socket.recv(4096)
|
||||
print(f"recv back: {recv_back}")
|
||||
switch_map = SwitchMap.model_validate_json(recv_back)
|
||||
print(f"switch map: {switch_map}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
2
serialization-prototyping/python-test/requirements.txt
Normal file
2
serialization-prototyping/python-test/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
msgpack==1.0.8
|
||||
pydantic==2.7
|
88
serialization-prototyping/src/archive.rs
Normal file
88
serialization-prototyping/src/archive.rs
Normal file
@ -0,0 +1,88 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
|
||||
pub enum Color {
|
||||
Red = 0,
|
||||
Green = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
struct Human {
|
||||
age: u16,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
struct HumanAdvanced {
|
||||
id: u32,
|
||||
age: u16,
|
||||
name: String,
|
||||
fav_color: Color,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
struct HumanGroup {
|
||||
humans: Vec<HumanAdvanced>,
|
||||
bank: HashMap<u32, usize>,
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn random_testing() {
|
||||
let mut buf = Vec::new();
|
||||
let john = HumanAdvanced {
|
||||
id: 0,
|
||||
age: 42,
|
||||
name: "John".into(),
|
||||
fav_color: Color::Green,
|
||||
};
|
||||
|
||||
john.serialize(&mut Serializer::new(&mut buf)).unwrap();
|
||||
|
||||
println!("{:?}", buf);
|
||||
let new_val: HumanAdvanced = rmp_serde::from_slice(&buf).expect("deserialization failed");
|
||||
let rmpv_val: rmpv::Value = rmp_serde::from_slice(&buf).expect("serialization into val failed");
|
||||
println!("RMPV value: {:?}", rmpv_val);
|
||||
let json_str = serde_json::to_string(&rmpv_val).expect("creating json failed");
|
||||
assert_eq!(john, new_val);
|
||||
println!("JSON str: {}", json_str);
|
||||
let val_test: HumanAdvanced = serde_json::from_str(&json_str).expect("wild");
|
||||
println!("val test: {:?}", val_test);
|
||||
|
||||
let nadine = HumanAdvanced {
|
||||
id: 1,
|
||||
age: 24,
|
||||
name: "Nadine".into(),
|
||||
fav_color: Color::Red,
|
||||
};
|
||||
let mut bank = HashMap::default();
|
||||
bank.insert(john.id, 1000000);
|
||||
bank.insert(nadine.id, 1);
|
||||
|
||||
let human_group = HumanGroup {
|
||||
humans: vec![john, nadine.clone()],
|
||||
bank,
|
||||
};
|
||||
let json_str = serde_json::to_string(&nadine).unwrap();
|
||||
println!("Nadine as JSON: {}", json_str);
|
||||
|
||||
let nadine_is_back: HumanAdvanced = serde_json::from_str(&json_str).unwrap();
|
||||
println!("nadine deserialized: {:?}", nadine_is_back);
|
||||
|
||||
let human_group_json = serde_json::to_string(&human_group).unwrap();
|
||||
println!("human group: {}", human_group_json);
|
||||
println!("human group json size: {}", human_group_json.len());
|
||||
|
||||
let human_group_rmp_vec = rmp_serde::to_vec_named(&human_group_json).unwrap();
|
||||
println!("human group msg pack size: {:?}", human_group_rmp_vec.len());
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn send_back_weird_stuff(buf: &[u8], received: usize, socket: &UdpSocket, src: SocketAddr) {
|
||||
let human_from_python: rmpv::Value = rmp_serde::from_slice(&buf[..received]).expect("blablah");
|
||||
let human_attempt_2: Human = rmp_serde::from_slice(&buf[..received]).expect("blhfwhfw");
|
||||
println!("human from python: {}", human_from_python);
|
||||
println!("human 2 from python: {:?}", human_attempt_2);
|
||||
let send_back_human = rmp_serde::to_vec_named(&human_attempt_2).expect("k32k323k2");
|
||||
socket
|
||||
.send_to(&send_back_human, src)
|
||||
.expect("sending back failed");
|
||||
}
|
61
serialization-prototyping/src/main.rs
Normal file
61
serialization-prototyping/src/main.rs
Normal file
@ -0,0 +1,61 @@
|
||||
#![allow(unused_imports)]
|
||||
use rmp_serde::{Deserializer, Serializer};
|
||||
use satrs_minisim::eps::{SwitchMap, SwitchMapWrapper};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::{SocketAddr, UdpSocket},
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct SwitchSet {
|
||||
pub valid: bool,
|
||||
pub switch_map: SwitchMap,
|
||||
}
|
||||
|
||||
pub struct UdpServer {
|
||||
socket: UdpSocket,
|
||||
last_sender: Option<SocketAddr>,
|
||||
}
|
||||
|
||||
impl Default for UdpServer {
|
||||
fn default() -> Self {
|
||||
UdpServer {
|
||||
socket: UdpSocket::bind("127.0.0.1:7301").expect("binding UDP socket failed"),
|
||||
last_sender: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UdpServer {
|
||||
pub fn send_back_reply(&self, reply: &[u8]) {
|
||||
self.socket
|
||||
.send_to(reply, self.last_sender.expect("last sender not set"))
|
||||
.expect("sending back failed");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut udp_server = UdpServer::default();
|
||||
|
||||
loop {
|
||||
// Receives a single datagram message on the socket. If `buf` is too small to hold
|
||||
// the message, it will be cut off.
|
||||
let mut buf = [0; 4096];
|
||||
let (received, src) = udp_server
|
||||
.socket
|
||||
.recv_from(&mut buf)
|
||||
.expect("receive call failed");
|
||||
udp_server.last_sender = Some(src);
|
||||
println!("received {} bytes from {:?}", received, src);
|
||||
let switch_map_off = SwitchMapWrapper::default();
|
||||
let switch_set = SwitchSet {
|
||||
valid: true,
|
||||
switch_map: switch_map_off.0.clone(),
|
||||
};
|
||||
let switch_map_off_json =
|
||||
serde_json::to_string(&switch_set).expect("json serialization failed");
|
||||
println!("sending back reply: {}", switch_map_off_json);
|
||||
udp_server.send_back_reply(switch_map_off_json.as_bytes());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user