Compare commits
35 Commits
satrs-shar
...
3268b07ad9
Author | SHA1 | Date | |
---|---|---|---|
3268b07ad9 | |||
1a1d330814 | |||
ef7b5d66fe
|
|||
b3497e0592 | |||
73286e36c2
|
|||
ed266a11f6 | |||
af972e174f
|
|||
3d12083c16
|
|||
e8d2c020fa | |||
69e172b633 | |||
e1dda751bc
|
|||
b01628d8ef | |||
31844e4fe2 | |||
738872f421 | |||
309e39999f
|
|||
1c43c3adf9 | |||
d9e0abffa7 | |||
abec9dd448
|
|||
c18fbb59ad | |||
c91ddcd658
|
|||
c5fa1955d7 | |||
c9708810e6
|
|||
79d0c2e222 | |||
8e87875c0e | |||
1ac6c02c06
|
|||
6e7907522e | |||
f747a5efdc
|
|||
6ffd55cec2 | |||
5e51b3de42
|
|||
bd059a2541 | |||
1a4d764f25 | |||
b38c617fae | |||
edcd5491f1 | |||
b4cb034b73 | |||
47c86aea5c |
@@ -10,6 +10,5 @@ members = [
|
||||
|
||||
exclude = [
|
||||
"embedded-examples/stm32f3-disco-rtic",
|
||||
"embedded-examples/stm32h7-rtic",
|
||||
"embedded-examples/stm32h7-nucleo-rtic",
|
||||
]
|
||||
|
||||
|
238
embedded-examples/stm32f3-disco-rtic/Cargo.lock
generated
238
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 = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "accelerometer"
|
||||
@@ -11,20 +11,11 @@ dependencies = [
|
||||
"micromath",
|
||||
]
|
||||
|
||||
[[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.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
@@ -77,7 +68,7 @@ version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
|
||||
dependencies = [
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -88,9 +79,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -103,8 +94,12 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.2.3"
|
||||
source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
@@ -121,22 +116,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
|
||||
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.7.0"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -165,15 +160,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.1.2"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.9"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@@ -181,33 +176,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.9"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.9"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.8"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
|
||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"defmt-macros",
|
||||
@@ -225,22 +220,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "0.3.9"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
|
||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "0.3.4"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@@ -265,7 +260,7 @@ checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -287,7 +282,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -315,6 +310,15 @@ 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"
|
||||
@@ -326,23 +330,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "enumset"
|
||||
version = "1.1.3"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
|
||||
checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293"
|
||||
dependencies = [
|
||||
"enumset_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumset_derive"
|
||||
version = "0.8.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"
|
||||
checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -368,21 +372,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -426,9 +430,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
@@ -448,9 +452,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -555,22 +559,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -591,9 +595,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@@ -602,43 +606,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
name = "portable-atomic"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.83"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -654,14 +662,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rtic"
|
||||
version = "2.1.1"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"bare-metal 1.0.0",
|
||||
"cortex-m",
|
||||
"critical-section",
|
||||
"portable-atomic",
|
||||
"rtic-core",
|
||||
"rtic-macros",
|
||||
]
|
||||
@@ -683,38 +691,40 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
||||
|
||||
[[package]]
|
||||
name = "rtic-macros"
|
||||
version = "2.1.0"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro-error",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-monotonics"
|
||||
version = "1.5.0"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"cfg-if",
|
||||
"cortex-m",
|
||||
"embedded-hal 1.0.0",
|
||||
"fugit",
|
||||
"portable-atomic",
|
||||
"rtic-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-time"
|
||||
version = "1.3.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"fugit",
|
||||
"futures-util",
|
||||
"rtic-common",
|
||||
]
|
||||
@@ -730,11 +740,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver 1.0.23",
|
||||
"semver 1.0.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -743,7 +753,7 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "866fcae3b683ccc37b5ad77982483a0ee01d5dc408dea5aad2117ad404b60fe1"
|
||||
dependencies = [
|
||||
"cobs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cobs 0.2.3",
|
||||
"crc",
|
||||
"defmt",
|
||||
"delegate",
|
||||
@@ -769,7 +779,7 @@ dependencies = [
|
||||
name = "satrs-stm32f3-disco-rtic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cobs 0.2.3 (git+https://github.com/robamu/cobs.rs.git?branch=all_features)",
|
||||
"cobs 0.3.0",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"cortex-m-semihosting",
|
||||
@@ -798,9 +808,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
@@ -922,9 +932,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.65"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -933,22 +943,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -959,9 +969,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "usb-device"
|
||||
@@ -977,9 +987,9 @@ checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
@@ -998,9 +1008,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
@@ -1008,11 +1018,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.34"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
@@ -22,12 +22,11 @@ version = "2"
|
||||
features = ["thumbv7-backend"]
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "1"
|
||||
version = "2"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[dependencies.cobs]
|
||||
git = "https://github.com/robamu/cobs.rs.git"
|
||||
branch = "all_features"
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.stm32f3xx-hal]
|
||||
|
@@ -15,8 +15,8 @@ use rtic::app;
|
||||
|
||||
use heapless::{mpmc::Q8, Vec};
|
||||
#[allow(unused_imports)]
|
||||
use rtic_monotonics::systick::fugit::{MillisDurationU32, TimerInstantU32};
|
||||
use rtic_monotonics::systick::ExtU32;
|
||||
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32};
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use satrs::seq_count::SequenceCountProviderCore;
|
||||
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
|
||||
use stm32f3xx_hal::dma::dma1;
|
||||
@@ -245,8 +245,6 @@ 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)]
|
||||
@@ -259,6 +257,8 @@ 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,8 +279,7 @@ mod app {
|
||||
let mut rcc = cx.device.RCC.constrain();
|
||||
|
||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||
Systick::start(cx.core.SYST, 8_000_000, systick_mono_token);
|
||||
Mono::start(cx.core.SYST, 8_000_000);
|
||||
|
||||
let mut flash = cx.device.FLASH.constrain();
|
||||
let clocks = rcc
|
||||
@@ -394,7 +393,7 @@ mod app {
|
||||
}
|
||||
}
|
||||
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
||||
Systick::delay(current_blink_freq).await;
|
||||
Mono::delay(current_blink_freq).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,15 +411,14 @@ 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 = (Systick::now() - last_completed).to_millis();
|
||||
let elapsed_ms = (Mono::now() - last_completed).to_millis();
|
||||
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
|
||||
Systick::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis())
|
||||
.await;
|
||||
Mono::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis()).await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check for completion after 1 ms
|
||||
Systick::delay(1.millis()).await;
|
||||
Mono::delay(1.millis()).await;
|
||||
continue;
|
||||
}
|
||||
if let Some(vec) = TM_REQUESTS.dequeue() {
|
||||
@@ -459,11 +457,11 @@ mod app {
|
||||
UartTxState::Transmitting(_) => (),
|
||||
});
|
||||
// Check for completion after 1 ms
|
||||
Systick::delay(1.millis()).await;
|
||||
Mono::delay(1.millis()).await;
|
||||
continue;
|
||||
}
|
||||
// Nothing to do, and we are idle.
|
||||
Systick::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
||||
Mono::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,7 +655,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(Systick::now());
|
||||
tx_shared.last_completed = Some(Mono::now());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
346
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
346
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
@@ -1,21 +1,12 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
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",
|
||||
]
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
@@ -23,7 +14,7 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -32,6 +23,12 @@ 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"
|
||||
@@ -64,8 +61,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.2.3"
|
||||
source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
|
||||
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"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
@@ -82,22 +89,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
|
||||
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.7.0"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -126,15 +133,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.1.2"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.8"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
|
||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"defmt-macros",
|
||||
@@ -152,22 +159,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "0.3.9"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
|
||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.64",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "0.3.4"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@@ -192,39 +199,41 @@ checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.64",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "delegate"
|
||||
version = "0.10.0"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1"
|
||||
checksum = "297806318ef30ad066b15792a8372858020ae3ca2e414ee6c2133b1eb9e9e945"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-new"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
|
||||
checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.64",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-alloc"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815"
|
||||
checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd"
|
||||
dependencies = [
|
||||
"const-default",
|
||||
"critical-section",
|
||||
"linked_list_allocator",
|
||||
"rlsf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -300,21 +309,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -328,15 +337,6 @@ 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,22 +348,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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",
|
||||
]
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
@@ -372,36 +359,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"defmt",
|
||||
"hash32 0.3.1",
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
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"
|
||||
@@ -434,22 +417,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.64",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -470,9 +453,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@@ -482,62 +465,72 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.6.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.82"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic"
|
||||
version = "2.1.1"
|
||||
name = "rlsf"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||
checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"const-default",
|
||||
"libc",
|
||||
"svgbobdoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"bare-metal 1.0.0",
|
||||
"cortex-m",
|
||||
"critical-section",
|
||||
"portable-atomic",
|
||||
"rtic-core",
|
||||
"rtic-macros",
|
||||
]
|
||||
@@ -559,28 +552,27 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
||||
|
||||
[[package]]
|
||||
name = "rtic-macros"
|
||||
version = "2.1.0"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro-error",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.64",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-monotonics"
|
||||
version = "1.5.0"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"cfg-if",
|
||||
"cortex-m",
|
||||
"embedded-hal 1.0.0",
|
||||
"fugit",
|
||||
"portable-atomic",
|
||||
"rtic-time",
|
||||
]
|
||||
|
||||
@@ -595,18 +587,21 @@ dependencies = [
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"embedded-hal-bus",
|
||||
"heapless 0.8.0",
|
||||
"heapless",
|
||||
"portable-atomic",
|
||||
"rtic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtic-time"
|
||||
version = "1.3.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"fugit",
|
||||
"futures-util",
|
||||
"rtic-common",
|
||||
]
|
||||
@@ -617,16 +612,7 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"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",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -638,20 +624,22 @@ dependencies = [
|
||||
"defmt",
|
||||
"delegate",
|
||||
"derive-new",
|
||||
"heapless 0.7.17",
|
||||
"heapless",
|
||||
"num-traits",
|
||||
"num_enum",
|
||||
"paste",
|
||||
"satrs-shared",
|
||||
"smallvec",
|
||||
"spacepackets",
|
||||
"static_cell",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "satrs-shared"
|
||||
version = "0.1.4"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6042477018c2d43fffccaaa5099bc299a58485139b4d31c5b276889311e474f1"
|
||||
checksum = "f10b962c131d0bcb0de06fefa5d4716cbd7f836167f90229f7f38405dc573bd3"
|
||||
dependencies = [
|
||||
"spacepackets",
|
||||
]
|
||||
@@ -676,12 +664,6 @@ 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"
|
||||
@@ -691,12 +673,6 @@ 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"
|
||||
@@ -719,39 +695,41 @@ dependencies = [
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"defmt",
|
||||
"heapless 0.8.0",
|
||||
"heapless",
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spacepackets"
|
||||
version = "0.11.2"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e85574d113a06312010c0ba51aadccd4ba2806231ebe9a49fc6473d0534d8696"
|
||||
checksum = "591d42bfa6af2ab308e5efd8f0870e2d0eb5dd19a141bdcb7da731f5ff9cc11c"
|
||||
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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_cell"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32h7"
|
||||
version = "0.15.1"
|
||||
@@ -784,6 +762,19 @@ 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"
|
||||
@@ -797,9 +788,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.64"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -808,29 +799,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.64",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
@@ -838,12 +835,6 @@ 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"
|
||||
@@ -861,21 +852,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.34"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.64",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
@@ -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.5"
|
||||
embedded-alloc = "0.6"
|
||||
rtic-sync = { version = "1", features = ["defmt-03"] }
|
||||
|
||||
[dependencies.smoltcp]
|
||||
version = "0.11.0"
|
||||
version = "0.11"
|
||||
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 = "1"
|
||||
version = "2"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[dependencies.satrs]
|
||||
|
@@ -3,8 +3,8 @@
|
||||
extern crate alloc;
|
||||
|
||||
use rtic::app;
|
||||
use rtic_monotonics::systick::Systick;
|
||||
use rtic_monotonics::Monotonic;
|
||||
use rtic_monotonics::fugit::TimerInstantU32;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
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::Heap;
|
||||
use embedded_alloc::LlffHeap as Heap;
|
||||
use smoltcp::iface::{Config, Interface, SocketHandle, SocketSet, SocketStorage};
|
||||
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};
|
||||
use stm32h7xx_hal::ethernet;
|
||||
@@ -32,6 +32,8 @@ 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;
|
||||
@@ -72,7 +74,9 @@ impl Net {
|
||||
let mut iface = Interface::new(
|
||||
config,
|
||||
&mut ethdev,
|
||||
smoltcp::time::Instant::from_millis((Systick::now() - Systick::ZERO).to_millis()),
|
||||
smoltcp::time::Instant::from_millis(
|
||||
(Mono::now() - TimerInstantU32::<1000>::from_ticks(0)).to_millis(),
|
||||
),
|
||||
);
|
||||
// Create sockets
|
||||
let dhcp_socket = dhcpv4::Socket::new();
|
||||
@@ -91,7 +95,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 = Systick::now() - Systick::ZERO;
|
||||
let uptime = Mono::now() - TimerInstantU32::<1000>::from_ticks(0);
|
||||
let timestamp = smoltcp::time::Instant::from_millis(uptime.to_millis());
|
||||
|
||||
self.iface.poll(timestamp, &mut self.ethdev, sockets)
|
||||
@@ -203,8 +207,7 @@ mod app {
|
||||
use core::ptr::addr_of_mut;
|
||||
|
||||
use super::*;
|
||||
use rtic_monotonics::systick::fugit::MillisDurationU32;
|
||||
use rtic_monotonics::systick::Systick;
|
||||
use rtic_monotonics::fugit::MillisDurationU32;
|
||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||
use stm32h7xx_hal::ethernet::{EthernetMAC, PHY};
|
||||
use stm32h7xx_hal::gpio::{Output, Pin};
|
||||
@@ -256,12 +259,7 @@ mod app {
|
||||
.freeze(pwrcfg, &cx.device.SYSCFG);
|
||||
|
||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||
Systick::start(
|
||||
cx.core.SYST,
|
||||
ccdr.clocks.sys_ck().to_Hz(),
|
||||
systick_mono_token,
|
||||
);
|
||||
Mono::start(cx.core.SYST, ccdr.clocks.sys_ck().to_Hz());
|
||||
|
||||
// Those are used in the smoltcp of the stm32h7xx-hal , I am not fully sure what they are
|
||||
// good for.
|
||||
@@ -377,24 +375,24 @@ mod app {
|
||||
|
||||
shared_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_SMALL.assume_init_mut() },
|
||||
unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
|
||||
SUBPOOL_SMALL.get_mut().unwrap(),
|
||||
SUBPOOL_SMALL_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_SMALL_NUM_BLOCKS,
|
||||
true,
|
||||
)
|
||||
.expect("growing heapless memory pool failed");
|
||||
shared_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_MEDIUM.assume_init_mut() },
|
||||
unsafe { SUBPOOL_MEDIUM_SIZES.assume_init_mut() },
|
||||
SUBPOOL_MEDIUM.get_mut().unwrap(),
|
||||
SUBPOOL_MEDIUM_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_MEDIUM_NUM_BLOCKS,
|
||||
true,
|
||||
)
|
||||
.expect("growing heapless memory pool failed");
|
||||
shared_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_LARGE.assume_init_mut() },
|
||||
unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
|
||||
SUBPOOL_LARGE.get_mut().unwrap(),
|
||||
SUBPOOL_LARGE_SIZES.get_mut().unwrap(),
|
||||
SUBPOOL_LARGE_NUM_BLOCKS,
|
||||
true,
|
||||
)
|
||||
@@ -403,7 +401,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(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
|
||||
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
|
||||
|
||||
eth_link_check::spawn().expect("eth link check failed");
|
||||
blinky::spawn().expect("spawning blink task failed");
|
||||
@@ -435,7 +433,7 @@ mod app {
|
||||
leds.led1.toggle();
|
||||
leds.led2.toggle();
|
||||
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
||||
Systick::delay(current_blink_freq).await;
|
||||
Mono::delay(current_blink_freq).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,7 +455,7 @@ mod app {
|
||||
cx.shared.eth_link_up.lock(|link_up| *link_up = false);
|
||||
defmt::info!("Ethernet link down");
|
||||
}
|
||||
Systick::delay(100.millis()).await;
|
||||
Mono::delay(100.millis()).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +481,7 @@ mod app {
|
||||
cx.local.udp.poll(sockets, pool);
|
||||
})
|
||||
});
|
||||
Systick::delay(40.millis()).await;
|
||||
Mono::delay(40.millis()).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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.6"
|
||||
fern = "0.7"
|
||||
chrono = "0.4"
|
||||
log = "0.4"
|
||||
crossbeam-channel = "0.5"
|
||||
delegate = "0.10"
|
||||
zerocopy = "0.6"
|
||||
delegate = "0.13"
|
||||
zerocopy = "0.8"
|
||||
csv = "1"
|
||||
num_enum = "0.7"
|
||||
thiserror = "1"
|
||||
thiserror = "2"
|
||||
lazy_static = "1"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
derive-new = "0.5"
|
||||
derive-new = "0.7"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#!/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.0",
|
||||
"tmtccmd~=8.1",
|
||||
"pydantic~=2.7"
|
||||
]
|
||||
|
||||
|
11
satrs-example/pytmtc/pytmtc/acs/__init__.py
Normal file
11
satrs-example/pytmtc/pytmtc/acs/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
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=json_cfg_path)
|
||||
super().__init__(json_cfg_path)
|
||||
|
||||
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
||||
from tmtccmd.config.com import (
|
||||
|
0
satrs-example/pytmtc/pytmtc/eps/__init__.py
Normal file
0
satrs-example/pytmtc/pytmtc/eps/__init__.py
Normal file
0
satrs-example/pytmtc/pytmtc/eps/pcdu.py
Normal file
0
satrs-example/pytmtc/pytmtc/eps/pcdu.py
Normal file
@@ -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.mgms import handle_mgm_hk_report
|
||||
from pytmtc.acs.mgms import handle_mgm_hk_report
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@@ -17,8 +17,9 @@ 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.mgms import create_mgm_cmds
|
||||
from pytmtc.acs.mgms import create_mgm_cmds
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -67,7 +68,6 @@ 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,15 +101,7 @@ def create_cmd_definition_tree() -> CmdTreeNode:
|
||||
)
|
||||
)
|
||||
root_node.add_child(scheduler_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)
|
||||
|
||||
root_node.add_child(create_acs_node(mode_node, hk_node))
|
||||
return root_node
|
||||
|
||||
|
||||
|
@@ -132,6 +132,7 @@ 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>>,
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
use crossbeam_channel::{bounded, Receiver, Sender};
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
use std::thread;
|
||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16};
|
||||
|
||||
trait FieldDataProvider: Send {
|
||||
fn get_data(&self) -> &[u8];
|
||||
@@ -35,7 +35,7 @@ struct ExampleMgmSet {
|
||||
temperature: u16,
|
||||
}
|
||||
|
||||
#[derive(FromBytes, AsBytes, Unaligned)]
|
||||
#[derive(FromBytes, IntoBytes, Immutable, Unaligned)]
|
||||
#[repr(C)]
|
||||
struct ExampleMgmSetZc {
|
||||
mgm_vec: [u8; 12],
|
||||
|
@@ -88,6 +88,7 @@ impl PowerSwitcherCommandSender<PcduSwitch> for PowerSwitchHelper {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(new)]
|
||||
pub struct SwitchRequestInfo {
|
||||
pub requestor_info: MessageMetadata,
|
||||
@@ -99,6 +100,7 @@ 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>,
|
||||
|
@@ -44,11 +44,13 @@ 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,
|
||||
|
@@ -26,6 +26,7 @@ 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,16 +4,19 @@ 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,6 +1,4 @@
|
||||
#![no_std]
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate std;
|
||||
|
||||
|
@@ -9,20 +9,18 @@ edition = "2021"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
log = "0.4"
|
||||
thiserror = "1"
|
||||
fern = "0.5"
|
||||
thiserror = "2"
|
||||
fern = "0.7"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
num_enum = "0.7"
|
||||
humantime = "2"
|
||||
tai-time = { version = "0.3", features = ["serde"] }
|
||||
|
||||
[dependencies.asynchronix]
|
||||
version = "0.2.2"
|
||||
# git = "https://github.com/asynchronics/asynchronix.git"
|
||||
# branch = "main"
|
||||
features = ["serde"]
|
||||
[dependencies.nexosim]
|
||||
version = "0.3.1"
|
||||
|
||||
[dependencies.satrs]
|
||||
path = "../satrs"
|
||||
|
||||
[dev-dependencies]
|
||||
delegate = "0.12"
|
||||
delegate = "0.13"
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use std::{f32::consts::PI, sync::mpsc, time::Duration};
|
||||
|
||||
use asynchronix::{
|
||||
model::{Model, Output},
|
||||
time::Scheduler,
|
||||
use nexosim::{
|
||||
model::{Context, Model},
|
||||
ports::Output,
|
||||
};
|
||||
use satrs::power::SwitchStateBinary;
|
||||
use satrs_minisim::{
|
||||
@@ -31,6 +31,7 @@ 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>,
|
||||
@@ -54,7 +55,7 @@ impl<ReplyProvider: MgmReplyProvider> MagnetometerModel<ReplyProvider> {
|
||||
self.switch_state = switch_state;
|
||||
}
|
||||
|
||||
pub async fn send_sensor_values(&mut self, _: (), scheduler: &Scheduler<Self>) {
|
||||
pub async fn send_sensor_values(&mut self, _: (), scheduler: &mut Context<Self>) {
|
||||
self.reply_sender
|
||||
.send(ReplyProvider::create_mgm_reply(MgmReplyCommon {
|
||||
switch_state: self.switch_state,
|
||||
@@ -113,11 +114,11 @@ impl MagnetorquerModel {
|
||||
pub async fn apply_torque(
|
||||
&mut self,
|
||||
duration_and_dipole: (Duration, MgtDipole),
|
||||
scheduler: &Scheduler<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.torque_dipole = duration_and_dipole.1;
|
||||
self.torquing = true;
|
||||
if scheduler
|
||||
if cx
|
||||
.schedule_event(duration_and_dipole.0, Self::clear_torque, ())
|
||||
.is_err()
|
||||
{
|
||||
@@ -137,12 +138,11 @@ impl MagnetorquerModel {
|
||||
self.generate_magnetic_field(()).await;
|
||||
}
|
||||
|
||||
pub async fn request_housekeeping_data(&mut self, _: (), scheduler: &Scheduler<Self>) {
|
||||
pub async fn request_housekeeping_data(&mut self, _: (), cx: &mut Context<Self>) {
|
||||
if self.switch_state != SwitchStateBinary::On {
|
||||
return;
|
||||
}
|
||||
scheduler
|
||||
.schedule_event(Duration::from_millis(15), Self::send_housekeeping_data, ())
|
||||
cx.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();
|
||||
sim_testbench.step().unwrap();
|
||||
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();
|
||||
sim_testbench.step().unwrap();
|
||||
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_by(Duration::from_millis(50));
|
||||
sim_testbench.step_until(Duration::from_millis(50)).unwrap();
|
||||
|
||||
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();
|
||||
sim_testbench.step().unwrap();
|
||||
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();
|
||||
sim_testbench.step().unwrap();
|
||||
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();
|
||||
sim_testbench.step().unwrap();
|
||||
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();
|
||||
sim_testbench.step().unwrap();
|
||||
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_by(Duration::from_millis(5));
|
||||
sim_testbench.step_until(Duration::from_millis(5)).unwrap();
|
||||
|
||||
check_mgt_hk(
|
||||
&mut sim_testbench,
|
||||
@@ -347,7 +347,9 @@ pub mod tests {
|
||||
torquing: true,
|
||||
},
|
||||
);
|
||||
sim_testbench.step_by(Duration::from_millis(100));
|
||||
sim_testbench
|
||||
.step_until(Duration::from_millis(100))
|
||||
.unwrap();
|
||||
check_mgt_hk(
|
||||
&mut sim_testbench,
|
||||
MgtHkSet {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
|
||||
use asynchronix::{
|
||||
simulation::{Address, Simulation},
|
||||
use nexosim::{
|
||||
simulation::{Address, Scheduler, Simulation},
|
||||
time::{Clock, MonotonicTime, SystemClock},
|
||||
};
|
||||
use satrs_minisim::{
|
||||
@@ -23,35 +23,52 @@ 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>,
|
||||
}
|
||||
|
||||
// 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 mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pub pcdu_addr: Address<PcduModel>,
|
||||
pub mgt_addr: Address<MagnetorquerModel>,
|
||||
pub scheduler: Scheduler,
|
||||
pub addr_wrapper: ModelAddrWrapper,
|
||||
}
|
||||
|
||||
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,
|
||||
mgm_addr: Address<MagnetometerModel<MgmLis3MdlReply>>,
|
||||
pcdu_addr: Address<PcduModel>,
|
||||
mgt_addr: Address<MagnetorquerModel>,
|
||||
scheduler: Scheduler,
|
||||
addr_wrapper: ModelAddrWrapper,
|
||||
) -> Self {
|
||||
Self {
|
||||
sys_clock,
|
||||
request_receiver,
|
||||
reply_sender,
|
||||
simulation,
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
scheduler,
|
||||
addr_wrapper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +79,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);
|
||||
self.sys_clock.synchronize(t);
|
||||
let _synch_status = self.sys_clock.synchronize(t);
|
||||
self.handle_sim_requests(t_old);
|
||||
self.simulation
|
||||
.step_until(t)
|
||||
@@ -118,11 +135,13 @@ impl SimController {
|
||||
}
|
||||
match mgm_request {
|
||||
MgmRequestLis3Mdl::RequestSensorData => {
|
||||
self.simulation.send_event(
|
||||
MagnetometerModel::send_sensor_values,
|
||||
(),
|
||||
&self.mgm_addr,
|
||||
);
|
||||
self.simulation
|
||||
.process_event(
|
||||
MagnetometerModel::send_sensor_values,
|
||||
(),
|
||||
&self.addr_wrapper.mgm_addr,
|
||||
)
|
||||
.expect("event execution error for mgm");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -136,14 +155,21 @@ impl SimController {
|
||||
match pcdu_request {
|
||||
PcduRequest::RequestSwitchInfo => {
|
||||
self.simulation
|
||||
.send_event(PcduModel::request_switch_info, (), &self.pcdu_addr);
|
||||
.process_event(
|
||||
PcduModel::request_switch_info,
|
||||
(),
|
||||
&self.addr_wrapper.pcdu_addr,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
PcduRequest::SwitchDevice { switch, state } => {
|
||||
self.simulation.send_event(
|
||||
PcduModel::switch_device,
|
||||
(switch, state),
|
||||
&self.pcdu_addr,
|
||||
);
|
||||
self.simulation
|
||||
.process_event(
|
||||
PcduModel::switch_device,
|
||||
(switch, state),
|
||||
&self.addr_wrapper.pcdu_addr,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -155,17 +181,23 @@ impl SimController {
|
||||
log::info!("received MGT request: {:?}", mgt_request);
|
||||
}
|
||||
match mgt_request {
|
||||
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,
|
||||
),
|
||||
}
|
||||
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(),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -199,7 +231,7 @@ mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending sim ctrl request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
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 asynchronix::{
|
||||
model::{Model, Output},
|
||||
time::Scheduler,
|
||||
use nexosim::{
|
||||
model::{Context, Model},
|
||||
ports::Output,
|
||||
};
|
||||
use satrs::power::SwitchStateBinary;
|
||||
use satrs_minisim::{
|
||||
@@ -29,14 +29,13 @@ impl PcduModel {
|
||||
}
|
||||
}
|
||||
|
||||
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 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 fn send_switch_info(&mut self) {
|
||||
@@ -92,7 +91,7 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM switch request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -113,7 +112,7 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step();
|
||||
sim_testbench.step().unwrap();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
@@ -143,12 +142,12 @@ pub(crate) mod tests {
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests_time_agnostic();
|
||||
sim_testbench.step_by(Duration::from_millis(1));
|
||||
sim_testbench.step_until(Duration::from_millis(1)).unwrap();
|
||||
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_none());
|
||||
// Reply takes 20ms
|
||||
sim_testbench.step_by(Duration::from_millis(25));
|
||||
sim_testbench.step_until(Duration::from_millis(25)).unwrap();
|
||||
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 asynchronix::time::MonotonicTime;
|
||||
use nexosim::time::MonotonicTime;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use acs::{MagnetometerModel, MagnetorquerModel};
|
||||
use asynchronix::simulation::{Mailbox, SimInit};
|
||||
use asynchronix::time::{MonotonicTime, SystemClock};
|
||||
use controller::SimController;
|
||||
use controller::{ModelAddrWrapper, 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,19 +63,20 @@ fn create_sim_controller(
|
||||
} else {
|
||||
SimInit::new()
|
||||
};
|
||||
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);
|
||||
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();
|
||||
SimController::new(
|
||||
sys_clock,
|
||||
request_receiver,
|
||||
reply_sender,
|
||||
simulation,
|
||||
mgm_addr,
|
||||
pcdu_addr,
|
||||
mgt_addr,
|
||||
scheduler,
|
||||
addrs,
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,10 @@
|
||||
use delegate::delegate;
|
||||
use std::{sync::mpsc, time::Duration};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use asynchronix::time::MonotonicTime;
|
||||
use nexosim::{
|
||||
simulation::ExecutionError,
|
||||
time::{Deadline, MonotonicTime},
|
||||
};
|
||||
use satrs_minisim::{SimReply, SimRequest};
|
||||
|
||||
use crate::{controller::SimController, create_sim_controller, ThreadingModel};
|
||||
@@ -35,8 +38,8 @@ impl SimTestbench {
|
||||
pub fn handle_sim_requests(&mut self, old_timestamp: MonotonicTime);
|
||||
}
|
||||
to self.sim_controller.simulation {
|
||||
pub fn step(&mut self);
|
||||
pub fn step_by(&mut self, duration: Duration);
|
||||
pub fn step(&mut self) -> Result<(), ExecutionError>;
|
||||
pub fn step_until(&mut self, duration: impl Deadline) -> Result<(), ExecutionError>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use asynchronix::time::MonotonicTime;
|
||||
use nexosim::time::MonotonicTime;
|
||||
|
||||
pub fn current_millis(time: MonotonicTime) -> u64 {
|
||||
(time.as_secs() as u64 * 1000) + (time.subsec_nanos() as u64 / 1_000_000)
|
||||
|
@@ -8,6 +8,10 @@ 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
|
||||
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "satrs-shared"
|
||||
description = "Components shared by multiple sat-rs crates"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
|
||||
@@ -22,7 +22,7 @@ version = "0.3"
|
||||
optional = true
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = ">0.9, <=0.12"
|
||||
version = ">0.9, <=0.13"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
|
@@ -12,10 +12,14 @@ 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
|
||||
|
||||
|
107
satrs/Cargo.toml
107
satrs/Cargo.toml
@@ -13,88 +13,36 @@ keywords = ["no-std", "space", "aerospace"]
|
||||
categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"]
|
||||
|
||||
[dependencies]
|
||||
delegate = ">0.7, <=0.10"
|
||||
satrs-shared = ">=0.1.3, <=0.2"
|
||||
delegate = ">0.7, <=0.13"
|
||||
paste = "1"
|
||||
derive-new = "0.6"
|
||||
derive-new = ">=0.6, <=0.7"
|
||||
smallvec = "1"
|
||||
crc = "3"
|
||||
num_enum = { version = ">0.5, <=0.7", default-features = false }
|
||||
spacepackets = { version = "0.13", default-features = false }
|
||||
cobs = { version = "0.3", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
thiserror = { version = "2", default-features = false }
|
||||
|
||||
[dependencies.satrs-shared]
|
||||
version = ">=0.1.3, <=0.2"
|
||||
|
||||
[dependencies.num_enum]
|
||||
version = ">0.5, <=0.7"
|
||||
default-features = false
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = "0.12"
|
||||
default-features = false
|
||||
|
||||
[dependencies.cobs]
|
||||
git = "https://github.com/robamu/cobs.rs.git"
|
||||
version = "0.2.3"
|
||||
branch = "all_features"
|
||||
default-features = false
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.dyn-clone]
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.hashbrown]
|
||||
version = "0.14"
|
||||
optional = true
|
||||
|
||||
[dependencies.heapless]
|
||||
version = "0.7"
|
||||
optional = true
|
||||
|
||||
[dependencies.downcast-rs]
|
||||
version = "1.2"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.bus]
|
||||
version = "2.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.crossbeam-channel]
|
||||
version= "0.5"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.socket2]
|
||||
version = "0.5.4"
|
||||
features = ["all"]
|
||||
optional = true
|
||||
|
||||
[dependencies.mio]
|
||||
version = "0.8"
|
||||
features = ["os-poll", "net"]
|
||||
optional = true
|
||||
|
||||
[dependencies.defmt]
|
||||
version = "0.3"
|
||||
optional = true
|
||||
hashbrown = { version = ">=0.14, <=0.15", optional = true }
|
||||
static_cell = { version = "2", optional = true }
|
||||
dyn-clone = { version = "1", optional = true }
|
||||
heapless = { version = "0.8", optional = true }
|
||||
downcast-rs = { version = "2", default-features = false, optional = true }
|
||||
bus = { version = "2.2", optional = true }
|
||||
crossbeam-channel = { version = "0.5", default-features = false, optional = true }
|
||||
serde = { version = "1", default-features = false, optional = true }
|
||||
socket2 = { version = "0.5", features = ["all"], optional = true }
|
||||
mio = { version = "1", features = ["os-poll", "net"], optional = true }
|
||||
defmt = { version = "0.3", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde = "1"
|
||||
zerocopy = "0.7"
|
||||
zerocopy = "0.8"
|
||||
once_cell = "1"
|
||||
serde_json = "1"
|
||||
rand = "0.8"
|
||||
rand = "0.9"
|
||||
tempfile = "3"
|
||||
|
||||
[dev-dependencies.postcard]
|
||||
@@ -106,12 +54,11 @@ std = [
|
||||
"downcast-rs/std",
|
||||
"alloc",
|
||||
"bus",
|
||||
"postcard/use-std",
|
||||
"crossbeam-channel/std",
|
||||
"serde/std",
|
||||
"spacepackets/std",
|
||||
"num_enum/std",
|
||||
"thiserror",
|
||||
"thiserror/std",
|
||||
"socket2",
|
||||
"mio"
|
||||
]
|
||||
@@ -124,11 +71,15 @@ alloc = [
|
||||
]
|
||||
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
|
||||
crossbeam = ["crossbeam-channel"]
|
||||
heapless = ["dep:heapless"]
|
||||
heapless = ["dep:heapless", "static_cell"]
|
||||
defmt = ["dep:defmt", "spacepackets/defmt"]
|
||||
test_util = []
|
||||
doc-images = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
||||
[[test]]
|
||||
name = "event_test"
|
||||
path = "tests/pus_events.rs"
|
||||
required-features = ["test_util"]
|
||||
|
3
satrs/docs.sh
Executable file
3
satrs/docs.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --all-features --open
|
@@ -3,8 +3,7 @@ Checklist for new releases
|
||||
|
||||
# Pre-Release
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,769 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,668 +0,0 @@
|
||||
//! 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
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
#![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 {}
|
@@ -1,96 +0,0 @@
|
||||
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);
|
||||
}
|
@@ -15,20 +15,24 @@ pub enum OpResult {
|
||||
TerminationRequested,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExecutionType {
|
||||
Infinite,
|
||||
Cycles(u32),
|
||||
OneShot,
|
||||
}
|
||||
|
||||
pub trait Executable: Send {
|
||||
pub trait Executable {
|
||||
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
|
||||
@@ -39,7 +43,10 @@ pub trait Executable: Send {
|
||||
/// * `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: Executable<Error = E> + Send + 'static + ?Sized, E: Send + 'static>(
|
||||
pub fn exec_sched_single<
|
||||
T: ExecutableWithType<Error = E> + Send + 'static + ?Sized,
|
||||
E: Send + 'static,
|
||||
>(
|
||||
mut executable: Box<T>,
|
||||
task_freq: Option<Duration>,
|
||||
op_code: i32,
|
||||
@@ -88,7 +95,10 @@ pub fn exec_sched_single<T: Executable<Error = E> + Send + 'static + ?Sized, E:
|
||||
/// * `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: Executable<Error = E> + Send + 'static + ?Sized, E: Send + 'static>(
|
||||
pub fn exec_sched_multi<
|
||||
T: ExecutableWithType<Error = E> + Send + 'static + ?Sized,
|
||||
E: Send + 'static,
|
||||
>(
|
||||
task_name: &'static str,
|
||||
mut executable_vec: Vec<Box<T>>,
|
||||
task_freq: Option<Duration>,
|
||||
@@ -142,7 +152,10 @@ pub fn exec_sched_multi<T: Executable<Error = E> + Send + 'static + ?Sized, E: S
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult};
|
||||
use super::{
|
||||
exec_sched_multi, exec_sched_single, Executable, ExecutableWithType, ExecutionType,
|
||||
OpResult,
|
||||
};
|
||||
use bus::Bus;
|
||||
use std::boxed::Box;
|
||||
use std::error::Error;
|
||||
@@ -208,10 +221,6 @@ 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
|
||||
}
|
||||
@@ -229,15 +238,17 @@ 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
|
||||
}
|
||||
@@ -255,15 +266,17 @@ 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
|
||||
}
|
||||
@@ -281,6 +294,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecutableWithType for PeriodicTask {
|
||||
fn exec_type(&self) -> ExecutionType {
|
||||
ExecutionType::Infinite
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_one_shot() {
|
||||
let expected_op_code = 42;
|
||||
@@ -423,7 +442,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 Executable<Error = ExampleError>>> =
|
||||
let task_vec: Vec<Box<dyn ExecutableWithType<Error = ExampleError> + Send>> =
|
||||
vec![one_shot_task, cycled_task_0, cycled_task_1];
|
||||
let jh = exec_sched_multi(
|
||||
"multi-task-name",
|
||||
@@ -493,7 +512,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 Executable<Error = ExampleError>>> =
|
||||
let task_vec: Vec<Box<dyn ExecutableWithType<Error = ExampleError> + Send>> =
|
||||
vec![cycled_task, periodic_task_0, periodic_task_1];
|
||||
let jh = exec_sched_multi(
|
||||
"multi-task-name",
|
||||
|
@@ -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(docs_rs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "alloc")]
|
||||
@@ -22,8 +22,6 @@ extern crate downcast_rs;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod cfdp;
|
||||
pub mod encoding;
|
||||
pub mod event_man;
|
||||
pub mod events;
|
||||
@@ -38,7 +36,8 @@ pub mod pus;
|
||||
pub mod queue;
|
||||
pub mod request;
|
||||
pub mod res_code;
|
||||
pub mod seq_count;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod scheduling;
|
||||
pub mod time;
|
||||
pub mod tmtc;
|
||||
|
||||
|
@@ -387,18 +387,18 @@ pub mod heapless_mod {
|
||||
#[macro_export]
|
||||
macro_rules! static_subpool {
|
||||
($pool_name: ident, $sizes_list_name: ident, $num_blocks: expr, $block_size: expr) => {
|
||||
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]);
|
||||
static $pool_name: static_cell::ConstStaticCell<[u8; $num_blocks * $block_size]> =
|
||||
static_cell::ConstStaticCell::new([0; $num_blocks * $block_size]);
|
||||
static $sizes_list_name: static_cell::ConstStaticCell<[usize; $num_blocks]> =
|
||||
static_cell::ConstStaticCell::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 mut $pool_name: core::mem::MaybeUninit<[u8; $num_blocks * $block_size]> =
|
||||
core::mem::MaybeUninit::new([0; $num_blocks * $block_size]);
|
||||
static $pool_name: static_cell::ConstStaticCell<[u8; $num_blocks * $block_size]> =
|
||||
static_cell::ConstStaticCell::new([0; $num_blocks * $block_size]);
|
||||
#[$meta_data]
|
||||
static mut $sizes_list_name: core::mem::MaybeUninit<[usize; $num_blocks]> =
|
||||
core::mem::MaybeUninit::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
static $sizes_list_name: static_cell::ConstStaticCell<[usize; $num_blocks]> =
|
||||
static_cell::ConstStaticCell::new([$crate::pool::STORE_FREE; $num_blocks]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -435,17 +435,17 @@ pub mod heapless_mod {
|
||||
///
|
||||
/// let mut mem_pool: StaticHeaplessMemoryPool<2> = StaticHeaplessMemoryPool::new(true);
|
||||
/// mem_pool.grow(
|
||||
/// unsafe { SUBPOOL_SMALL.assume_init_mut() },
|
||||
/// unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
|
||||
/// SUBPOOL_SMALL.take(),
|
||||
/// SUBPOOL_SMALL_SIZES.take(),
|
||||
/// SUBPOOL_SMALL_NUM_BLOCKS,
|
||||
/// false
|
||||
/// );
|
||||
/// ).unwrap();
|
||||
/// mem_pool.grow(
|
||||
/// unsafe { SUBPOOL_LARGE.assume_init_mut() },
|
||||
/// unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
|
||||
/// SUBPOOL_LARGE.take(),
|
||||
/// SUBPOOL_LARGE_SIZES.take(),
|
||||
/// SUBPOOL_LARGE_NUM_BLOCKS,
|
||||
/// false
|
||||
/// );
|
||||
/// ).unwrap();
|
||||
///
|
||||
/// let mut read_buf: [u8; 16] = [0; 16];
|
||||
/// let mut addr;
|
||||
@@ -522,12 +522,14 @@ pub mod heapless_mod {
|
||||
num_blocks: NumBlocks,
|
||||
set_sizes_list_to_all_free: bool,
|
||||
) -> Result<(), PoolIsFull> {
|
||||
assert!(
|
||||
(subpool_memory.len() % num_blocks as usize) == 0,
|
||||
assert_eq!(
|
||||
(subpool_memory.len() % num_blocks as usize),
|
||||
0,
|
||||
"pool slice length must be multiple of number of blocks"
|
||||
);
|
||||
assert!(
|
||||
num_blocks as usize == sizes_list.len(),
|
||||
assert_eq!(
|
||||
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 {
|
||||
@@ -1584,23 +1586,33 @@ mod tests {
|
||||
mod heapless_tests {
|
||||
use super::*;
|
||||
use crate::static_subpool;
|
||||
use core::mem::MaybeUninit;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::sync::Mutex;
|
||||
|
||||
const SUBPOOL_1_BLOCK_SIZE: usize = 4;
|
||||
const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
|
||||
static mut SUBPOOL_1: MaybeUninit<
|
||||
|
||||
static SUBPOOL_1: static_cell::ConstStaticCell<
|
||||
[u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
||||
> = 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]);
|
||||
> = static_cell::ConstStaticCell::new(
|
||||
[0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
|
||||
);
|
||||
|
||||
static SUBPOOL_1_SIZES: Mutex<UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>> =
|
||||
Mutex::new(UnsafeCell::new(
|
||||
[STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize],
|
||||
));
|
||||
|
||||
const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
|
||||
const SUBPOOL_2_BLOCK_SIZE: usize = 8;
|
||||
static mut SUBPOOL_2: MaybeUninit<
|
||||
static SUBPOOL_2: static_cell::ConstStaticCell<
|
||||
[u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
||||
> = 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]);
|
||||
> = static_cell::ConstStaticCell::new(
|
||||
[0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
|
||||
);
|
||||
static SUBPOOL_2_SIZES: static_cell::ConstStaticCell<
|
||||
[usize; SUBPOOL_2_NUM_ELEMENTS as usize],
|
||||
> = static_cell::ConstStaticCell::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
|
||||
|
||||
const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
|
||||
const SUBPOOL_3_BLOCK_SIZE: usize = 16;
|
||||
@@ -1643,26 +1655,26 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(false);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_1.assume_init_mut() },
|
||||
unsafe { SUBPOOL_1_SIZES.assume_init_mut() },
|
||||
SUBPOOL_1.take(),
|
||||
unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
|
||||
SUBPOOL_1_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_2.assume_init_mut() },
|
||||
unsafe { SUBPOOL_2_SIZES.assume_init_mut() },
|
||||
SUBPOOL_2.take(),
|
||||
SUBPOOL_2_SIZES.take(),
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
heapless_pool
|
||||
@@ -1782,18 +1794,18 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_2.assume_init_mut() },
|
||||
unsafe { SUBPOOL_2_SIZES.assume_init_mut() },
|
||||
SUBPOOL_2.take(),
|
||||
SUBPOOL_2_SIZES.take(),
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_4.assume_init_mut() },
|
||||
unsafe { SUBPOOL_4_SIZES.assume_init_mut() },
|
||||
SUBPOOL_4.take(),
|
||||
SUBPOOL_4_SIZES.take(),
|
||||
SUBPOOL_4_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spills_to_higher_subpools(&mut heapless_pool);
|
||||
@@ -1805,18 +1817,18 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5.take(),
|
||||
SUBPOOL_5_SIZES.take(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spillage_fails_as_well(&mut heapless_pool);
|
||||
@@ -1828,26 +1840,26 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5.take(),
|
||||
SUBPOOL_5_SIZES.take(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_6.assume_init_mut() },
|
||||
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
|
||||
SUBPOOL_6.take(),
|
||||
SUBPOOL_6_SIZES.take(),
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spillage_works_across_multiple_subpools(&mut heapless_pool);
|
||||
@@ -1859,26 +1871,26 @@ mod tests {
|
||||
StaticHeaplessMemoryPool::new(true);
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_5.assume_init_mut() },
|
||||
unsafe { SUBPOOL_5_SIZES.assume_init_mut() },
|
||||
SUBPOOL_5.take(),
|
||||
SUBPOOL_5_SIZES.take(),
|
||||
SUBPOOL_5_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_6.assume_init_mut() },
|
||||
unsafe { SUBPOOL_6_SIZES.assume_init_mut() },
|
||||
SUBPOOL_6.take(),
|
||||
SUBPOOL_6_SIZES.take(),
|
||||
SUBPOOL_6_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
assert!(heapless_pool
|
||||
.grow(
|
||||
unsafe { SUBPOOL_3.assume_init_mut() },
|
||||
unsafe { SUBPOOL_3_SIZES.assume_init_mut() },
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
false
|
||||
true
|
||||
)
|
||||
.is_ok());
|
||||
generic_test_spillage_fails_across_multiple_subpools(&mut heapless_pool);
|
||||
|
@@ -20,7 +20,7 @@
|
||||
//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporter
|
||||
//! };
|
||||
//! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool};
|
||||
//! use satrs::seq_count::SeqCountProviderSimple;
|
||||
//! use satrs::spacepackets::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::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore};
|
||||
use crate::spacepackets::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProvider};
|
||||
use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
||||
use crate::ComponentId;
|
||||
use alloc::format;
|
||||
|
416
satrs/src/scheduling.rs
Normal file
416
satrs/src/scheduling.rs
Normal file
@@ -0,0 +1,416 @@
|
||||
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::start] 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();
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,250 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
@@ -17,37 +17,37 @@ impl PusTmWithCdsShortHelper {
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn create_pus_tm_timestamp_now<'a>(
|
||||
&'a mut self,
|
||||
pub fn create_pus_tm_timestamp_now<'data>(
|
||||
&mut self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'a [u8],
|
||||
source_data: &'data [u8],
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator {
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
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<'a>(
|
||||
&'a mut self,
|
||||
pub fn create_pus_tm_with_stamper<'data>(
|
||||
&mut self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'a [u8],
|
||||
source_data: &'data [u8],
|
||||
stamper: &CdsTime,
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator {
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
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<'a>(
|
||||
&'a self,
|
||||
fn create_pus_tm_common<'data>(
|
||||
&self,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'a [u8],
|
||||
source_data: &'data [u8],
|
||||
seq_count: u16,
|
||||
) -> PusTmCreator {
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
|
||||
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
|
||||
PusTmCreator::new(reply_header, tc_header, source_data, true)
|
||||
|
Reference in New Issue
Block a user