diff --git a/.gitignore b/.gitignore index 91f35d4..6a5b49d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ target/ +output.log /Cargo.lock output.log diff --git a/README.md b/README.md index c20b996..b1e76c9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

+

[![sat-rs website](https://img.shields.io/badge/sat--rs-website-darkgreen?style=flat)](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/) [![sat-rs book](https://img.shields.io/badge/sat--rs-book-darkgreen?style=flat)](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/) @@ -8,17 +8,27 @@ sat-rs ========= -This is the repository of the sat-rs framework. Its primary goal is to provide re-usable components +This is the repository of the sat-rs library. Its primary goal is to provide re-usable components to write on-board software for remote systems like rovers or satellites. It is specifically written for the special requirements for these systems. You can find an overview of the project and the link to the [more high-level sat-rs book](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/) at the [IRS software projects website](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/). +This is early-stage software. Important features are missing. New releases +with breaking changes are released regularly, with all changes documented inside respective +changelog files. You should only use this library if your are willing to work in this +environment. + A lot of the architecture and general design considerations are based on the [FSFW](https://egit.irs.uni-stuttgart.de/fsfw/fsfw) C++ framework which has flight heritage through the 2 missions [FLP](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/flying-laptop/) and [EIVE](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/EIVE/). +This framework is in the early stages of development. Important features are missing. New releases +with breaking changes are released regularly, with all changes documented inside respective +changelog files. You should only use this framework if your are willing to work in this +environment. + # Overview This project currently contains following crates: @@ -35,7 +45,7 @@ This project currently contains following crates: * [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-mib): Components to build a mission information base from the on-board software directly. * [`satrs-example-stm32f3-disco`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example-stm32f3-disco): - Example of a simple example on-board software using sat-rs components on a bare-metal system + Example of a simple example using low-level sat-rs components on a bare-metal system with constrained resources. Each project has its own `CHANGELOG.md`. diff --git a/coverage.py b/coverage.py index 3b1c3c4..c932d9a 100755 --- a/coverage.py +++ b/coverage.py @@ -47,7 +47,7 @@ def main(): parser.add_argument( "-p", "--package", - choices=["satrs", "satrs-minisim"], + choices=["satrs", "satrs-minisim", "satrs-example"], default="satrs", help="Choose project to generate coverage for", ) diff --git a/misc/satrs-logo-v2.png b/misc/satrs-logo-v2.png new file mode 100644 index 0000000..76859f8 Binary files /dev/null and b/misc/satrs-logo-v2.png differ diff --git a/satrs-book/src/actions.md b/satrs-book/src/actions.md index 0e092d9..bed9fdb 100644 --- a/satrs-book/src/actions.md +++ b/satrs-book/src/actions.md @@ -15,7 +15,7 @@ action commanding could look like. 2. Target ID and Action String based. The target ID is the same as in the first proposal, but the unique action is identified by a string. -The framework provides an `ActionRequest` abstraction to model both of these cases. +The library provides an `ActionRequest` abstraction to model both of these cases. ## Commanding with ECSS PUS 8 diff --git a/satrs-book/src/communication.md b/satrs-book/src/communication.md index 9e7a4ac..f102f6b 100644 --- a/satrs-book/src/communication.md +++ b/satrs-book/src/communication.md @@ -20,7 +20,7 @@ components. 1. [UDP TMTC Server](https://docs.rs/satrs/latest/satrs/hal/host/udp_server/index.html). UDP is already packet based which makes it an excellent fit for exchanging space packets. 2. [TCP TMTC Server Components](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/index.html). - TCP is a stream based protocol, so the framework provides building blocks to parse telemetry + TCP is a stream based protocol, so the library provides building blocks to parse telemetry from an arbitrary bytestream. Two concrete implementations are provided: - [TCP spacepackets server](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/struct.TcpSpacepacketsServer.html) to parse tightly packed CCSDS Spacepackets. diff --git a/satrs-book/src/design.md b/satrs-book/src/design.md index 9ec7317..ef14250 100644 --- a/satrs-book/src/design.md +++ b/satrs-book/src/design.md @@ -1,13 +1,14 @@ -# Framework Design +# Library Design Satellites and space systems in general are complex systems with a wide range of requirements for -both the hardware and the software. Consequently, the general design of the framework is centered +both the hardware and the software. Consequently, the general design of the library is centered around many light-weight components which try to impose as few restrictions as possible on how to -solve certain problems. +solve certain problems. This is also the reason why sat-rs is explicitely called a library +instead of a framework. There are still a lot of common patterns and architectures across these systems where guidance of how to solve a problem and a common structure would still be extremely useful to avoid pitfalls -which were already solved and to avoid boilerplate code. This framework tries to provide this +which were already solved and to avoid boilerplate code. This library tries to provide this structure and guidance the following way: 1. Providing this book which explains the architecture and design patterns in respect to common @@ -18,7 +19,7 @@ structure and guidance the following way: 3. Providing a good test suite. This includes both unittests and integration tests. The integration tests can also serve as smaller usage examples than the large `satrs-example` application. -This framework has special support for standards used in the space industry. This especially +This library has special support for standards used in the space industry. This especially includes standards provided by Consultative Committee for Space Data Systems (CCSDS) and European Cooperation for Space Standardization (ECSS). It does not enforce using any of those standards, but it is always recommended to use some sort of standard for interoperability. @@ -30,10 +31,10 @@ Flying Laptop Project by the University of Stuttgart with Airbus Defence and Spa It has flight heritage through the 2 mssions [FLP](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/flying-laptop/) and [EIVE](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/EIVE/). Therefore, a lot of the design concepts were ported more or less unchanged to the `sat-rs` -framework. +library. FLP is a medium-size small satellite with a higher budget and longer development time than EIVE, which allowed to build a highly reliable system while EIVE is a smaller 6U+ cubesat which had a -shorter development cycle and was built using cheaper COTS components. This framework also tries +shorter development cycle and was built using cheaper COTS components. This library also tries to accumulate the knowledge of developing the OBSW and operating the satellite for both these different systems and provide a solution for a wider range of small satellite systems. diff --git a/satrs-book/src/example.md b/satrs-book/src/example.md index 12fec83..4e1f590 100644 --- a/satrs-book/src/example.md +++ b/satrs-book/src/example.md @@ -1,6 +1,6 @@ # sat-rs Example Application -The `sat-rs` framework includes a monolithic example application which can be found inside +The `sat-rs` library includes a monolithic example application which can be found inside the [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example) subdirectory of the repository. The primary purpose of this example application is to show how the various components of the sat-rs framework could be used as part of a larger on-board diff --git a/satrs-book/src/introduction.md b/satrs-book/src/introduction.md index f448441..babd8ca 100644 --- a/satrs-book/src/introduction.md +++ b/satrs-book/src/introduction.md @@ -1,7 +1,7 @@ The sat-rs book ====== -This book is the primary information resource for the [sat-rs framework](https://egit.irs.uni-stuttgart.de/rust/sat-rs) +This book is the primary information resource for the [sat-rs library](https://egit.irs.uni-stuttgart.de/rust/sat-rs) in addition to the regular API documentation. It contains the following resources: 1. Architecture informations and consideration which would exceeds the scope of the regular API. @@ -12,10 +12,15 @@ in addition to the regular API documentation. It contains the following resource # Introduction -The primary goal of the sat-rs framework is to provide re-usable components +The primary goal of the sat-rs library is to provide re-usable components to write on-board software for remote systems like rovers or satellites. It is specifically written for the special requirements for these systems. +It should be noted that sat-rs is early-stage software. Important features are missing. New releases +with breaking changes are released regularly, with all changes documented inside respective +changelog files. You should only use this library if your are willing to work in this +environment. + A lot of the architecture and general design considerations are based on the [FSFW](https://egit.irs.uni-stuttgart.de/fsfw/fsfw) C++ framework which has flight heritage through the 2 missions [FLP](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/flying-laptop/) diff --git a/satrs-example-stm32f3-disco/.cargo/def_config.toml b/satrs-example-stm32f3-disco/.cargo/def_config.toml index 4150986..4598c09 100644 --- a/satrs-example-stm32f3-disco/.cargo/def_config.toml +++ b/satrs-example-stm32f3-disco/.cargo/def_config.toml @@ -5,11 +5,17 @@ # runner = "arm-none-eabi-gdb -q -x openocd.gdb" # runner = "gdb-multiarch -q -x openocd.gdb" # runner = "gdb -q -x openocd.gdb" -# runner = "probe-run --chip STM32F303VCTx --connect-under-reset" +runner = "probe-rs run --chip STM32F303VCTx" rustflags = [ + "-C", "linker=flip-link", # LLD (shipped with the Rust toolchain) is used as the default linker "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", # if you run into problems with LLD switch to the GNU linker by commenting out # this line @@ -26,3 +32,6 @@ rustflags = [ [build] # comment out the following line if you intend to run unit tests on host machine target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[env] +DEFMT_LOG = "info" \ No newline at end of file diff --git a/satrs-example-stm32f3-disco/Cargo.lock b/satrs-example-stm32f3-disco/Cargo.lock index 9043d7a..fffb020 100644 --- a/satrs-example-stm32f3-disco/Cargo.lock +++ b/satrs-example-stm32f3-disco/Cargo.lock @@ -22,9 +22,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "bare-metal" @@ -88,19 +88,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "num-traits", ] -[[package]] -name = "cobs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" - [[package]] name = "cobs" version = "0.2.3" @@ -139,6 +133,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cortex-m-semihosting" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0" +dependencies = [ + "cortex-m", +] + [[package]] name = "crc" version = "3.0.1" @@ -180,7 +183,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] @@ -191,7 +194,72 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.53", + "syn 2.0.58", +] + +[[package]] +name = "defmt" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3939552907426de152b3c2c6f51ed53f98f448babd26f28694c95f5906194595" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-brtt" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f0ac3635d0c89d12b8101fcb44a7625f5f030a1c0491124b74467eb5a58a78" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "defmt-macros" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18bdc7a7b92ac413e19e95240e75d3a73a8d8e78aa24a594c22cbb4d44b4bbda" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "defmt-parser" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-test" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290966e8c38f94b11884877242de876280d0eab934900e9642d58868e77c5df1" +dependencies = [ + "cortex-m-rt", + "cortex-m-semihosting", + "defmt", + "defmt-test-macros", +] + +[[package]] +name = "defmt-test-macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -257,7 +325,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] @@ -371,21 +439,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "itm_logger" -version = "0.1.3-alpha.0" -source = "git+https://github.com/robamu/itm_logger.rs.git?branch=all_features#83ee7a6c57f525a70d0cc5bb7e65826d0ce938a0" -dependencies = [ - "cortex-m", - "log", -] - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - [[package]] name = "lsm303dlhc" version = "0.2.0" @@ -500,16 +553,17 @@ checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] -name = "panic-itm" -version = "0.4.2" +name = "panic-probe" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d577d97d1b31268087b6dddf2470e6794ef5eee87d9dca7fcd0481695391a4c" +checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9" dependencies = [ "cortex-m", + "defmt", ] [[package]] @@ -520,9 +574,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -574,9 +628,9 @@ dependencies = [ [[package]] name = "rtcc" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fbd0d5bed2b76e27a7ef872568b34072c1af94c277cd52c17a89d54673b3fe" +checksum = "95973c3a0274adc4f3c5b70d2b5b85618d6de9559a6737d3293ecae9a2fc0839" dependencies = [ "chrono", ] @@ -620,7 +674,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] @@ -669,10 +723,8 @@ dependencies = [ [[package]] name = "satrs" version = "0.2.0-rc.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cb19cba46a45047ff0879ebfbf9d6ae1c5b2e0e38b2e08760b10a441d4dae6" dependencies = [ - "cobs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cobs", "crc", "delegate", "num-traits", @@ -687,14 +739,17 @@ dependencies = [ name = "satrs-example-stm32f3-disco" version = "0.1.0" dependencies = [ - "cobs 0.2.3 (git+https://github.com/robamu/cobs.rs.git?branch=all_features)", + "cobs", "cortex-m", "cortex-m-rt", + "cortex-m-semihosting", + "defmt", + "defmt-brtt", + "defmt-test", "embedded-hal 0.2.7", "enumset", "heapless", - "itm_logger", - "panic-itm", + "panic-probe", "rtic", "rtic-monotonics", "satrs", @@ -704,9 +759,7 @@ dependencies = [ [[package]] name = "satrs-shared" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a402ba556a7f5eef707035b45e64a3259b09674311e98697f3dd0508a1bf51" +version = "0.1.3" dependencies = [ "spacepackets", ] @@ -746,12 +799,12 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "spacepackets" -version = "0.10.0" +version = "0.11.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28246ae2451af240c3e3ff3c51363c7b6ad565ca6aa9bad23b8c725687c485e1" +checksum = "c2cfd5f9a4c7f10714d21f9bc61f2d176cb7ae092cdd687e7ade2d4e6f7d7125" dependencies = [ - "chrono", "crc", + "defmt", "delegate", "num-traits", "num_enum", @@ -846,15 +899,35 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "typenum" version = "1.17.0" @@ -918,5 +991,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] diff --git a/satrs-example-stm32f3-disco/Cargo.toml b/satrs-example-stm32f3-disco/Cargo.toml index 4337919..c149dd4 100644 --- a/satrs-example-stm32f3-disco/Cargo.toml +++ b/satrs-example-stm32f3-disco/Cargo.toml @@ -2,13 +2,18 @@ name = "satrs-example-stm32f3-disco" version = "0.1.0" edition = "2021" +default-run = "satrs-example-stm32f3-disco" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" +defmt = "0.3" +defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] } +panic-probe = { version = "0.3", features = ["print-defmt"] } embedded-hal = "0.2.7" +cortex-m-semihosting = "0.5.0" enumset = "1" heapless = "0.8" @@ -25,14 +30,6 @@ git = "https://github.com/robamu/cobs.rs.git" branch = "all_features" default-features = false -[dependencies.panic-itm] -version = "0.4" - -[dependencies.itm_logger] -git = "https://github.com/robamu/itm_logger.rs.git" -branch = "all_features" -version = "0.1.3-alpha.0" - [dependencies.stm32f3xx-hal] git = "https://github.com/robamu/stm32f3xx-hal" version = "0.11.0-alpha.0" @@ -49,17 +46,38 @@ branch = "complete-dma-update-hal" # path = "../stm32f3-discovery" [dependencies.satrs] -# git = "https://egit.irs.uni-stuttgart.de/rust/satrs-core.git" -version = "0.2.0-rc.0" +path = "../satrs" default-features = false +features = ["defmt"] -# this lets you use `cargo fix`! -# [[bin]] -# name = "stm32f3-blinky" -# test = false -# bench = false +[dev-dependencies] +defmt-test = "0.3" +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = "s" # <- +overflow-checks = true # <- + +# cargo build/run --release [profile.release] -codegen-units = 1 # better optimizations -debug = true # symbols are nice and they don't increase the size on Flash -lto = true # better optimizations +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = "s" # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = "s" # <- +overflow-checks = false # <- diff --git a/satrs-example-stm32f3-disco/README.md b/satrs-example-stm32f3-disco/README.md index 9d502fa..090faea 100644 --- a/satrs-example-stm32f3-disco/README.md +++ b/satrs-example-stm32f3-disco/README.md @@ -2,26 +2,24 @@ sat-rs example for the STM32F3-Discovery board ======= This example application shows how the [sat-rs framework](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad) -can be used on an embedded target. It also shows how a relatively simple OBSW could be built when no -standard runtime is available. It uses [RTIC](https://rtic.rs/1/book/en/) as the concurrency -framework. +can be used on an embedded target. +It also shows how a relatively simple OBSW could be built when no standard runtime is available. +It uses [RTIC](https://rtic.rs/1/book/en/) as the concurrency framework and the +[defmt](https://defmt.ferrous-systems.com/) framework for logging. The STM32F3-Discovery device was picked because it is a cheap Cortex-M4 based device which is also used by the [Rust Embedded Book](https://docs.rust-embedded.org/book/intro/hardware.html) and the [Rust Discovery](https://docs.rust-embedded.org/discovery/f3discovery/) book as an introduction to embedded Rust. -If you would like to access the ITM log output, you need to connect the PB3 pin to the CN3 pin -of the SWD header like [shown here](https://docs.rust-embedded.org/discovery/f3discovery/06-hello-world/index.html). - ## Pre-Requisites Make sure the following tools are installed: -1. `openocd`: This is the debug server used to debug the STM32F3. You can install this from - [`xPacks`](https://xpack.github.io/dev-tools/openocd/install/). You can also use the one provided - by a STM32Cube installation. -2. A debugger like `arm-none-eabi-gdb` or `gdb-multiarch`. +1. [`probe-rs`](https://probe.rs/): Application used to flash and debug the MCU. +2. Optional and recommended: [VS Code](https://code.visualstudio.com/) with + [probe-rs plugin](https://marketplace.visualstudio.com/items?itemName=probe-rs.probe-rs-debugger) + for debugging. ## Preparing Rust and the repository @@ -52,23 +50,19 @@ you can simply build the application with cargo build ``` -## Flashing and Debugging from the command line +## Flashing from the command line -Make sure you have `openocd` and `itmdump` installed first. +You can flash the application from the command line using `probe-rs`: -1. Configure a runner inside your `.cargo/config.toml` file by uncommenting an appropriate line - depending on the application you want to use for debugging -2. Start `openocd` inside the project folder. This will start `openocd` with the provided - `openocd.cfg` configuration file. -3. Use `cargo run` to flash and debug the application in your terminal -4. Use `itmdump -F -f itm.txt` to print the logs received from the STM32F3 device. Please note - that the PB3 and CN3 pin of the SWD header need to be connected for this to work. +```sh +probe-rs run --chip STM32F303VCTx +``` ## Debugging with VS Code The STM32F3-Discovery comes with an on-board ST-Link so all that is required to flash and debug -the board is a Mini-USB cable. The code in this repository was debugged using `openocd` -and the VS Code [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug). +the board is a Mini-USB cable. The code in this repository was debugged using [`probe-rs`](https://probe.rs/docs/tools/debuggerA) +and the VS Code [`probe-rs` plugin](https://marketplace.visualstudio.com/items?itemName=probe-rs.probe-rs-debugger). Make sure to install this plugin first. Sample configuration files are provided inside the `vscode` folder. @@ -80,19 +74,11 @@ to automatically rebuild and flash your application. The `tasks.json` and `launch.json` files are generic and you can use them immediately by opening the folder in VS code or adding it to a workspace. -If you would like to use a custom GDB application, you can specify the gdb binary in the following -configuration variables in your `settings.json`: - -- `"cortex-debug.gdbPath"` -- `"cortex-debug.gdbPath.linux"` -- `"cortex-debug.gdbPath.windows"` -- `"cortex-debug.gdbPath.osx"` - ## Commanding with Python When the SW is running on the Discovery board, you can command the MCU via a serial interface, using COBS encoded PUS packets. - + It is recommended to use a virtual environment to do this. To set up one in the command line, you can use `python3 -m venv venv` on Unix systems or `py -m venv venv` on Windows systems. After doing this, you can check the [venv tutorial](https://docs.python.org/3/tutorial/venv.html) @@ -111,3 +97,18 @@ A default configuration file for the python application is provided and can be u ```sh cp def_tmtc_conf.json tmtc_conf.json ``` + +After that, you can for example send a ping to the MCU using the following command + +```sh +./main.py -p /ping +``` + +You can configure the blinky frequency using + +```sh +./main.py -p /change_blink_freq +``` + +All these commands will package a PUS telecommand which will be sent to the MCU using the COBS +format as the packet framing format. diff --git a/satrs-example-stm32f3-disco/STM32F303.svd b/satrs-example-stm32f3-disco/STM32F303.svd new file mode 100644 index 0000000..63fb761 --- /dev/null +++ b/satrs-example-stm32f3-disco/STM32F303.svd @@ -0,0 +1,38601 @@ + + + + STM32F303 + 1.8 + STM32F303 + + CM4 + r1p0 + little + true + true + 3 + false + + 8 + 32 + 0x20 + 0x0 + 0xFFFFFFFF + + + GPIOA + General-purpose I/Os + GPIO + 0x48000000 + + 0x0 + 0x400 + registers + + + + MODER + MODER + GPIO port mode register + 0x0 + 0x20 + read-write + 0x28000000 + + + MODER15 + Port x configuration bits (y = + 0..15) + 30 + 2 + + + MODER14 + Port x configuration bits (y = + 0..15) + 28 + 2 + + + MODER13 + Port x configuration bits (y = + 0..15) + 26 + 2 + + + MODER12 + Port x configuration bits (y = + 0..15) + 24 + 2 + + + MODER11 + Port x configuration bits (y = + 0..15) + 22 + 2 + + + MODER10 + Port x configuration bits (y = + 0..15) + 20 + 2 + + + MODER9 + Port x configuration bits (y = + 0..15) + 18 + 2 + + + MODER8 + Port x configuration bits (y = + 0..15) + 16 + 2 + + + MODER7 + Port x configuration bits (y = + 0..15) + 14 + 2 + + + MODER6 + Port x configuration bits (y = + 0..15) + 12 + 2 + + + MODER5 + Port x configuration bits (y = + 0..15) + 10 + 2 + + + MODER4 + Port x configuration bits (y = + 0..15) + 8 + 2 + + + MODER3 + Port x configuration bits (y = + 0..15) + 6 + 2 + + + MODER2 + Port x configuration bits (y = + 0..15) + 4 + 2 + + + MODER1 + Port x configuration bits (y = + 0..15) + 2 + 2 + + + MODER0 + Port x configuration bits (y = + 0..15) + 0 + 2 + + + + + OTYPER + OTYPER + GPIO port output type register + 0x4 + 0x20 + read-write + 0x00000000 + + + OT15 + Port x configuration bits (y = + 0..15) + 15 + 1 + + + OT14 + Port x configuration bits (y = + 0..15) + 14 + 1 + + + OT13 + Port x configuration bits (y = + 0..15) + 13 + 1 + + + OT12 + Port x configuration bits (y = + 0..15) + 12 + 1 + + + OT11 + Port x configuration bits (y = + 0..15) + 11 + 1 + + + OT10 + Port x configuration bits (y = + 0..15) + 10 + 1 + + + OT9 + Port x configuration bits (y = + 0..15) + 9 + 1 + + + OT8 + Port x configuration bits (y = + 0..15) + 8 + 1 + + + OT7 + Port x configuration bits (y = + 0..15) + 7 + 1 + + + OT6 + Port x configuration bits (y = + 0..15) + 6 + 1 + + + OT5 + Port x configuration bits (y = + 0..15) + 5 + 1 + + + OT4 + Port x configuration bits (y = + 0..15) + 4 + 1 + + + OT3 + Port x configuration bits (y = + 0..15) + 3 + 1 + + + OT2 + Port x configuration bits (y = + 0..15) + 2 + 1 + + + OT1 + Port x configuration bits (y = + 0..15) + 1 + 1 + + + OT0 + Port x configuration bits (y = + 0..15) + 0 + 1 + + + + + OSPEEDR + OSPEEDR + GPIO port output speed + register + 0x8 + 0x20 + read-write + 0x00000000 + + + OSPEEDR15 + Port x configuration bits (y = + 0..15) + 30 + 2 + + + OSPEEDR14 + Port x configuration bits (y = + 0..15) + 28 + 2 + + + OSPEEDR13 + Port x configuration bits (y = + 0..15) + 26 + 2 + + + OSPEEDR12 + Port x configuration bits (y = + 0..15) + 24 + 2 + + + OSPEEDR11 + Port x configuration bits (y = + 0..15) + 22 + 2 + + + OSPEEDR10 + Port x configuration bits (y = + 0..15) + 20 + 2 + + + OSPEEDR9 + Port x configuration bits (y = + 0..15) + 18 + 2 + + + OSPEEDR8 + Port x configuration bits (y = + 0..15) + 16 + 2 + + + OSPEEDR7 + Port x configuration bits (y = + 0..15) + 14 + 2 + + + OSPEEDR6 + Port x configuration bits (y = + 0..15) + 12 + 2 + + + OSPEEDR5 + Port x configuration bits (y = + 0..15) + 10 + 2 + + + OSPEEDR4 + Port x configuration bits (y = + 0..15) + 8 + 2 + + + OSPEEDR3 + Port x configuration bits (y = + 0..15) + 6 + 2 + + + OSPEEDR2 + Port x configuration bits (y = + 0..15) + 4 + 2 + + + OSPEEDR1 + Port x configuration bits (y = + 0..15) + 2 + 2 + + + OSPEEDR0 + Port x configuration bits (y = + 0..15) + 0 + 2 + + + + + PUPDR + PUPDR + GPIO port pull-up/pull-down + register + 0xC + 0x20 + read-write + 0x24000000 + + + PUPDR15 + Port x configuration bits (y = + 0..15) + 30 + 2 + + + PUPDR14 + Port x configuration bits (y = + 0..15) + 28 + 2 + + + PUPDR13 + Port x configuration bits (y = + 0..15) + 26 + 2 + + + PUPDR12 + Port x configuration bits (y = + 0..15) + 24 + 2 + + + PUPDR11 + Port x configuration bits (y = + 0..15) + 22 + 2 + + + PUPDR10 + Port x configuration bits (y = + 0..15) + 20 + 2 + + + PUPDR9 + Port x configuration bits (y = + 0..15) + 18 + 2 + + + PUPDR8 + Port x configuration bits (y = + 0..15) + 16 + 2 + + + PUPDR7 + Port x configuration bits (y = + 0..15) + 14 + 2 + + + PUPDR6 + Port x configuration bits (y = + 0..15) + 12 + 2 + + + PUPDR5 + Port x configuration bits (y = + 0..15) + 10 + 2 + + + PUPDR4 + Port x configuration bits (y = + 0..15) + 8 + 2 + + + PUPDR3 + Port x configuration bits (y = + 0..15) + 6 + 2 + + + PUPDR2 + Port x configuration bits (y = + 0..15) + 4 + 2 + + + PUPDR1 + Port x configuration bits (y = + 0..15) + 2 + 2 + + + PUPDR0 + Port x configuration bits (y = + 0..15) + 0 + 2 + + + + + IDR + IDR + GPIO port input data register + 0x10 + 0x20 + read-only + 0x00000000 + + + IDR15 + Port input data (y = + 0..15) + 15 + 1 + + + IDR14 + Port input data (y = + 0..15) + 14 + 1 + + + IDR13 + Port input data (y = + 0..15) + 13 + 1 + + + IDR12 + Port input data (y = + 0..15) + 12 + 1 + + + IDR11 + Port input data (y = + 0..15) + 11 + 1 + + + IDR10 + Port input data (y = + 0..15) + 10 + 1 + + + IDR9 + Port input data (y = + 0..15) + 9 + 1 + + + IDR8 + Port input data (y = + 0..15) + 8 + 1 + + + IDR7 + Port input data (y = + 0..15) + 7 + 1 + + + IDR6 + Port input data (y = + 0..15) + 6 + 1 + + + IDR5 + Port input data (y = + 0..15) + 5 + 1 + + + IDR4 + Port input data (y = + 0..15) + 4 + 1 + + + IDR3 + Port input data (y = + 0..15) + 3 + 1 + + + IDR2 + Port input data (y = + 0..15) + 2 + 1 + + + IDR1 + Port input data (y = + 0..15) + 1 + 1 + + + IDR0 + Port input data (y = + 0..15) + 0 + 1 + + + + + ODR + ODR + GPIO port output data register + 0x14 + 0x20 + read-write + 0x00000000 + + + ODR15 + Port output data (y = + 0..15) + 15 + 1 + + + ODR14 + Port output data (y = + 0..15) + 14 + 1 + + + ODR13 + Port output data (y = + 0..15) + 13 + 1 + + + ODR12 + Port output data (y = + 0..15) + 12 + 1 + + + ODR11 + Port output data (y = + 0..15) + 11 + 1 + + + ODR10 + Port output data (y = + 0..15) + 10 + 1 + + + ODR9 + Port output data (y = + 0..15) + 9 + 1 + + + ODR8 + Port output data (y = + 0..15) + 8 + 1 + + + ODR7 + Port output data (y = + 0..15) + 7 + 1 + + + ODR6 + Port output data (y = + 0..15) + 6 + 1 + + + ODR5 + Port output data (y = + 0..15) + 5 + 1 + + + ODR4 + Port output data (y = + 0..15) + 4 + 1 + + + ODR3 + Port output data (y = + 0..15) + 3 + 1 + + + ODR2 + Port output data (y = + 0..15) + 2 + 1 + + + ODR1 + Port output data (y = + 0..15) + 1 + 1 + + + ODR0 + Port output data (y = + 0..15) + 0 + 1 + + + + + BSRR + BSRR + GPIO port bit set/reset + register + 0x18 + 0x20 + write-only + 0x00000000 + + + BR15 + Port x reset bit y (y = + 0..15) + 31 + 1 + + + BR14 + Port x reset bit y (y = + 0..15) + 30 + 1 + + + BR13 + Port x reset bit y (y = + 0..15) + 29 + 1 + + + BR12 + Port x reset bit y (y = + 0..15) + 28 + 1 + + + BR11 + Port x reset bit y (y = + 0..15) + 27 + 1 + + + BR10 + Port x reset bit y (y = + 0..15) + 26 + 1 + + + BR9 + Port x reset bit y (y = + 0..15) + 25 + 1 + + + BR8 + Port x reset bit y (y = + 0..15) + 24 + 1 + + + BR7 + Port x reset bit y (y = + 0..15) + 23 + 1 + + + BR6 + Port x reset bit y (y = + 0..15) + 22 + 1 + + + BR5 + Port x reset bit y (y = + 0..15) + 21 + 1 + + + BR4 + Port x reset bit y (y = + 0..15) + 20 + 1 + + + BR3 + Port x reset bit y (y = + 0..15) + 19 + 1 + + + BR2 + Port x reset bit y (y = + 0..15) + 18 + 1 + + + BR1 + Port x reset bit y (y = + 0..15) + 17 + 1 + + + BR0 + Port x set bit y (y= + 0..15) + 16 + 1 + + + BS15 + Port x set bit y (y= + 0..15) + 15 + 1 + + + BS14 + Port x set bit y (y= + 0..15) + 14 + 1 + + + BS13 + Port x set bit y (y= + 0..15) + 13 + 1 + + + BS12 + Port x set bit y (y= + 0..15) + 12 + 1 + + + BS11 + Port x set bit y (y= + 0..15) + 11 + 1 + + + BS10 + Port x set bit y (y= + 0..15) + 10 + 1 + + + BS9 + Port x set bit y (y= + 0..15) + 9 + 1 + + + BS8 + Port x set bit y (y= + 0..15) + 8 + 1 + + + BS7 + Port x set bit y (y= + 0..15) + 7 + 1 + + + BS6 + Port x set bit y (y= + 0..15) + 6 + 1 + + + BS5 + Port x set bit y (y= + 0..15) + 5 + 1 + + + BS4 + Port x set bit y (y= + 0..15) + 4 + 1 + + + BS3 + Port x set bit y (y= + 0..15) + 3 + 1 + + + BS2 + Port x set bit y (y= + 0..15) + 2 + 1 + + + BS1 + Port x set bit y (y= + 0..15) + 1 + 1 + + + BS0 + Port x set bit y (y= + 0..15) + 0 + 1 + + + + + LCKR + LCKR + GPIO port configuration lock + register + 0x1C + 0x20 + read-write + 0x00000000 + + + LCKK + Lok Key + 16 + 1 + + + LCK15 + Port x lock bit y (y= + 0..15) + 15 + 1 + + + LCK14 + Port x lock bit y (y= + 0..15) + 14 + 1 + + + LCK13 + Port x lock bit y (y= + 0..15) + 13 + 1 + + + LCK12 + Port x lock bit y (y= + 0..15) + 12 + 1 + + + LCK11 + Port x lock bit y (y= + 0..15) + 11 + 1 + + + LCK10 + Port x lock bit y (y= + 0..15) + 10 + 1 + + + LCK9 + Port x lock bit y (y= + 0..15) + 9 + 1 + + + LCK8 + Port x lock bit y (y= + 0..15) + 8 + 1 + + + LCK7 + Port x lock bit y (y= + 0..15) + 7 + 1 + + + LCK6 + Port x lock bit y (y= + 0..15) + 6 + 1 + + + LCK5 + Port x lock bit y (y= + 0..15) + 5 + 1 + + + LCK4 + Port x lock bit y (y= + 0..15) + 4 + 1 + + + LCK3 + Port x lock bit y (y= + 0..15) + 3 + 1 + + + LCK2 + Port x lock bit y (y= + 0..15) + 2 + 1 + + + LCK1 + Port x lock bit y (y= + 0..15) + 1 + 1 + + + LCK0 + Port x lock bit y (y= + 0..15) + 0 + 1 + + + + + AFRL + AFRL + GPIO alternate function low + register + 0x20 + 0x20 + read-write + 0x00000000 + + + AFRL7 + Alternate function selection for port x + bit y (y = 0..7) + 28 + 4 + + + AFRL6 + Alternate function selection for port x + bit y (y = 0..7) + 24 + 4 + + + AFRL5 + Alternate function selection for port x + bit y (y = 0..7) + 20 + 4 + + + AFRL4 + Alternate function selection for port x + bit y (y = 0..7) + 16 + 4 + + + AFRL3 + Alternate function selection for port x + bit y (y = 0..7) + 12 + 4 + + + AFRL2 + Alternate function selection for port x + bit y (y = 0..7) + 8 + 4 + + + AFRL1 + Alternate function selection for port x + bit y (y = 0..7) + 4 + 4 + + + AFRL0 + Alternate function selection for port x + bit y (y = 0..7) + 0 + 4 + + + + + AFRH + AFRH + GPIO alternate function high + register + 0x24 + 0x20 + read-write + 0x00000000 + + + AFRH15 + Alternate function selection for port x + bit y (y = 8..15) + 28 + 4 + + + AFRH14 + Alternate function selection for port x + bit y (y = 8..15) + 24 + 4 + + + AFRH13 + Alternate function selection for port x + bit y (y = 8..15) + 20 + 4 + + + AFRH12 + Alternate function selection for port x + bit y (y = 8..15) + 16 + 4 + + + AFRH11 + Alternate function selection for port x + bit y (y = 8..15) + 12 + 4 + + + AFRH10 + Alternate function selection for port x + bit y (y = 8..15) + 8 + 4 + + + AFRH9 + Alternate function selection for port x + bit y (y = 8..15) + 4 + 4 + + + AFRH8 + Alternate function selection for port x + bit y (y = 8..15) + 0 + 4 + + + + + BRR + BRR + Port bit reset register + 0x28 + 0x20 + write-only + 0x00000000 + + + BR0 + Port x Reset bit y + 0 + 1 + + + BR1 + Port x Reset bit y + 1 + 1 + + + BR2 + Port x Reset bit y + 2 + 1 + + + BR3 + Port x Reset bit y + 3 + 1 + + + BR4 + Port x Reset bit y + 4 + 1 + + + BR5 + Port x Reset bit y + 5 + 1 + + + BR6 + Port x Reset bit y + 6 + 1 + + + BR7 + Port x Reset bit y + 7 + 1 + + + BR8 + Port x Reset bit y + 8 + 1 + + + BR9 + Port x Reset bit y + 9 + 1 + + + BR10 + Port x Reset bit y + 10 + 1 + + + BR11 + Port x Reset bit y + 11 + 1 + + + BR12 + Port x Reset bit y + 12 + 1 + + + BR13 + Port x Reset bit y + 13 + 1 + + + BR14 + Port x Reset bit y + 14 + 1 + + + BR15 + Port x Reset bit y + 15 + 1 + + + + + + + GPIOB + General-purpose I/Os + GPIO + 0x48000400 + + 0x0 + 0x400 + registers + + + + MODER + MODER + GPIO port mode register + 0x0 + 0x20 + read-write + 0x00000000 + + + MODER15 + Port x configuration bits (y = + 0..15) + 30 + 2 + + + MODER14 + Port x configuration bits (y = + 0..15) + 28 + 2 + + + MODER13 + Port x configuration bits (y = + 0..15) + 26 + 2 + + + MODER12 + Port x configuration bits (y = + 0..15) + 24 + 2 + + + MODER11 + Port x configuration bits (y = + 0..15) + 22 + 2 + + + MODER10 + Port x configuration bits (y = + 0..15) + 20 + 2 + + + MODER9 + Port x configuration bits (y = + 0..15) + 18 + 2 + + + MODER8 + Port x configuration bits (y = + 0..15) + 16 + 2 + + + MODER7 + Port x configuration bits (y = + 0..15) + 14 + 2 + + + MODER6 + Port x configuration bits (y = + 0..15) + 12 + 2 + + + MODER5 + Port x configuration bits (y = + 0..15) + 10 + 2 + + + MODER4 + Port x configuration bits (y = + 0..15) + 8 + 2 + + + MODER3 + Port x configuration bits (y = + 0..15) + 6 + 2 + + + MODER2 + Port x configuration bits (y = + 0..15) + 4 + 2 + + + MODER1 + Port x configuration bits (y = + 0..15) + 2 + 2 + + + MODER0 + Port x configuration bits (y = + 0..15) + 0 + 2 + + + + + OTYPER + OTYPER + GPIO port output type register + 0x4 + 0x20 + read-write + 0x00000000 + + + OT15 + Port x configuration bit + 15 + 15 + 1 + + + OT14 + Port x configuration bit + 14 + 14 + 1 + + + OT13 + Port x configuration bit + 13 + 13 + 1 + + + OT12 + Port x configuration bit + 12 + 12 + 1 + + + OT11 + Port x configuration bit + 11 + 11 + 1 + + + OT10 + Port x configuration bit + 10 + 10 + 1 + + + OT9 + Port x configuration bit 9 + 9 + 1 + + + OT8 + Port x configuration bit 8 + 8 + 1 + + + OT7 + Port x configuration bit 7 + 7 + 1 + + + OT6 + Port x configuration bit 6 + 6 + 1 + + + OT5 + Port x configuration bit 5 + 5 + 1 + + + OT4 + Port x configuration bit 4 + 4 + 1 + + + OT3 + Port x configuration bit 3 + 3 + 1 + + + OT2 + Port x configuration bit 2 + 2 + 1 + + + OT1 + Port x configuration bit 1 + 1 + 1 + + + OT0 + Port x configuration bit 0 + 0 + 1 + + + + + OSPEEDR + OSPEEDR + GPIO port output speed + register + 0x8 + 0x20 + read-write + 0x00000000 + + + OSPEEDR15 + Port x configuration bits (y = + 0..15) + 30 + 2 + + + OSPEEDR14 + Port x configuration bits (y = + 0..15) + 28 + 2 + + + OSPEEDR13 + Port x configuration bits (y = + 0..15) + 26 + 2 + + + OSPEEDR12 + Port x configuration bits (y = + 0..15) + 24 + 2 + + + OSPEEDR11 + Port x configuration bits (y = + 0..15) + 22 + 2 + + + OSPEEDR10 + Port x configuration bits (y = + 0..15) + 20 + 2 + + + OSPEEDR9 + Port x configuration bits (y = + 0..15) + 18 + 2 + + + OSPEEDR8 + Port x configuration bits (y = + 0..15) + 16 + 2 + + + OSPEEDR7 + Port x configuration bits (y = + 0..15) + 14 + 2 + + + OSPEEDR6 + Port x configuration bits (y = + 0..15) + 12 + 2 + + + OSPEEDR5 + Port x configuration bits (y = + 0..15) + 10 + 2 + + + OSPEEDR4 + Port x configuration bits (y = + 0..15) + 8 + 2 + + + OSPEEDR3 + Port x configuration bits (y = + 0..15) + 6 + 2 + + + OSPEEDR2 + Port x configuration bits (y = + 0..15) + 4 + 2 + + + OSPEEDR1 + Port x configuration bits (y = + 0..15) + 2 + 2 + + + OSPEEDR0 + Port x configuration bits (y = + 0..15) + 0 + 2 + + + + + PUPDR + PUPDR + GPIO port pull-up/pull-down + register + 0xC + 0x20 + read-write + 0x00000000 + + + PUPDR15 + Port x configuration bits (y = + 0..15) + 30 + 2 + + + PUPDR14 + Port x configuration bits (y = + 0..15) + 28 + 2 + + + PUPDR13 + Port x configuration bits (y = + 0..15) + 26 + 2 + + + PUPDR12 + Port x configuration bits (y = + 0..15) + 24 + 2 + + + PUPDR11 + Port x configuration bits (y = + 0..15) + 22 + 2 + + + PUPDR10 + Port x configuration bits (y = + 0..15) + 20 + 2 + + + PUPDR9 + Port x configuration bits (y = + 0..15) + 18 + 2 + + + PUPDR8 + Port x configuration bits (y = + 0..15) + 16 + 2 + + + PUPDR7 + Port x configuration bits (y = + 0..15) + 14 + 2 + + + PUPDR6 + Port x configuration bits (y = + 0..15) + 12 + 2 + + + PUPDR5 + Port x configuration bits (y = + 0..15) + 10 + 2 + + + PUPDR4 + Port x configuration bits (y = + 0..15) + 8 + 2 + + + PUPDR3 + Port x configuration bits (y = + 0..15) + 6 + 2 + + + PUPDR2 + Port x configuration bits (y = + 0..15) + 4 + 2 + + + PUPDR1 + Port x configuration bits (y = + 0..15) + 2 + 2 + + + PUPDR0 + Port x configuration bits (y = + 0..15) + 0 + 2 + + + + + IDR + IDR + GPIO port input data register + 0x10 + 0x20 + read-only + 0x00000000 + + + IDR15 + Port input data (y = + 0..15) + 15 + 1 + + + IDR14 + Port input data (y = + 0..15) + 14 + 1 + + + IDR13 + Port input data (y = + 0..15) + 13 + 1 + + + IDR12 + Port input data (y = + 0..15) + 12 + 1 + + + IDR11 + Port input data (y = + 0..15) + 11 + 1 + + + IDR10 + Port input data (y = + 0..15) + 10 + 1 + + + IDR9 + Port input data (y = + 0..15) + 9 + 1 + + + IDR8 + Port input data (y = + 0..15) + 8 + 1 + + + IDR7 + Port input data (y = + 0..15) + 7 + 1 + + + IDR6 + Port input data (y = + 0..15) + 6 + 1 + + + IDR5 + Port input data (y = + 0..15) + 5 + 1 + + + IDR4 + Port input data (y = + 0..15) + 4 + 1 + + + IDR3 + Port input data (y = + 0..15) + 3 + 1 + + + IDR2 + Port input data (y = + 0..15) + 2 + 1 + + + IDR1 + Port input data (y = + 0..15) + 1 + 1 + + + IDR0 + Port input data (y = + 0..15) + 0 + 1 + + + + + ODR + ODR + GPIO port output data register + 0x14 + 0x20 + read-write + 0x00000000 + + + ODR15 + Port output data (y = + 0..15) + 15 + 1 + + + ODR14 + Port output data (y = + 0..15) + 14 + 1 + + + ODR13 + Port output data (y = + 0..15) + 13 + 1 + + + ODR12 + Port output data (y = + 0..15) + 12 + 1 + + + ODR11 + Port output data (y = + 0..15) + 11 + 1 + + + ODR10 + Port output data (y = + 0..15) + 10 + 1 + + + ODR9 + Port output data (y = + 0..15) + 9 + 1 + + + ODR8 + Port output data (y = + 0..15) + 8 + 1 + + + ODR7 + Port output data (y = + 0..15) + 7 + 1 + + + ODR6 + Port output data (y = + 0..15) + 6 + 1 + + + ODR5 + Port output data (y = + 0..15) + 5 + 1 + + + ODR4 + Port output data (y = + 0..15) + 4 + 1 + + + ODR3 + Port output data (y = + 0..15) + 3 + 1 + + + ODR2 + Port output data (y = + 0..15) + 2 + 1 + + + ODR1 + Port output data (y = + 0..15) + 1 + 1 + + + ODR0 + Port output data (y = + 0..15) + 0 + 1 + + + + + BSRR + BSRR + GPIO port bit set/reset + register + 0x18 + 0x20 + write-only + 0x00000000 + + + BR15 + Port x reset bit y (y = + 0..15) + 31 + 1 + + + BR14 + Port x reset bit y (y = + 0..15) + 30 + 1 + + + BR13 + Port x reset bit y (y = + 0..15) + 29 + 1 + + + BR12 + Port x reset bit y (y = + 0..15) + 28 + 1 + + + BR11 + Port x reset bit y (y = + 0..15) + 27 + 1 + + + BR10 + Port x reset bit y (y = + 0..15) + 26 + 1 + + + BR9 + Port x reset bit y (y = + 0..15) + 25 + 1 + + + BR8 + Port x reset bit y (y = + 0..15) + 24 + 1 + + + BR7 + Port x reset bit y (y = + 0..15) + 23 + 1 + + + BR6 + Port x reset bit y (y = + 0..15) + 22 + 1 + + + BR5 + Port x reset bit y (y = + 0..15) + 21 + 1 + + + BR4 + Port x reset bit y (y = + 0..15) + 20 + 1 + + + BR3 + Port x reset bit y (y = + 0..15) + 19 + 1 + + + BR2 + Port x reset bit y (y = + 0..15) + 18 + 1 + + + BR1 + Port x reset bit y (y = + 0..15) + 17 + 1 + + + BR0 + Port x set bit y (y= + 0..15) + 16 + 1 + + + BS15 + Port x set bit y (y= + 0..15) + 15 + 1 + + + BS14 + Port x set bit y (y= + 0..15) + 14 + 1 + + + BS13 + Port x set bit y (y= + 0..15) + 13 + 1 + + + BS12 + Port x set bit y (y= + 0..15) + 12 + 1 + + + BS11 + Port x set bit y (y= + 0..15) + 11 + 1 + + + BS10 + Port x set bit y (y= + 0..15) + 10 + 1 + + + BS9 + Port x set bit y (y= + 0..15) + 9 + 1 + + + BS8 + Port x set bit y (y= + 0..15) + 8 + 1 + + + BS7 + Port x set bit y (y= + 0..15) + 7 + 1 + + + BS6 + Port x set bit y (y= + 0..15) + 6 + 1 + + + BS5 + Port x set bit y (y= + 0..15) + 5 + 1 + + + BS4 + Port x set bit y (y= + 0..15) + 4 + 1 + + + BS3 + Port x set bit y (y= + 0..15) + 3 + 1 + + + BS2 + Port x set bit y (y= + 0..15) + 2 + 1 + + + BS1 + Port x set bit y (y= + 0..15) + 1 + 1 + + + BS0 + Port x set bit y (y= + 0..15) + 0 + 1 + + + + + LCKR + LCKR + GPIO port configuration lock + register + 0x1C + 0x20 + read-write + 0x00000000 + + + LCKK + Lok Key + 16 + 1 + + + LCK15 + Port x lock bit y (y= + 0..15) + 15 + 1 + + + LCK14 + Port x lock bit y (y= + 0..15) + 14 + 1 + + + LCK13 + Port x lock bit y (y= + 0..15) + 13 + 1 + + + LCK12 + Port x lock bit y (y= + 0..15) + 12 + 1 + + + LCK11 + Port x lock bit y (y= + 0..15) + 11 + 1 + + + LCK10 + Port x lock bit y (y= + 0..15) + 10 + 1 + + + LCK9 + Port x lock bit y (y= + 0..15) + 9 + 1 + + + LCK8 + Port x lock bit y (y= + 0..15) + 8 + 1 + + + LCK7 + Port x lock bit y (y= + 0..15) + 7 + 1 + + + LCK6 + Port x lock bit y (y= + 0..15) + 6 + 1 + + + LCK5 + Port x lock bit y (y= + 0..15) + 5 + 1 + + + LCK4 + Port x lock bit y (y= + 0..15) + 4 + 1 + + + LCK3 + Port x lock bit y (y= + 0..15) + 3 + 1 + + + LCK2 + Port x lock bit y (y= + 0..15) + 2 + 1 + + + LCK1 + Port x lock bit y (y= + 0..15) + 1 + 1 + + + LCK0 + Port x lock bit y (y= + 0..15) + 0 + 1 + + + + + AFRL + AFRL + GPIO alternate function low + register + 0x20 + 0x20 + read-write + 0x00000000 + + + AFRL7 + Alternate function selection for port x + bit y (y = 0..7) + 28 + 4 + + + AFRL6 + Alternate function selection for port x + bit y (y = 0..7) + 24 + 4 + + + AFRL5 + Alternate function selection for port x + bit y (y = 0..7) + 20 + 4 + + + AFRL4 + Alternate function selection for port x + bit y (y = 0..7) + 16 + 4 + + + AFRL3 + Alternate function selection for port x + bit y (y = 0..7) + 12 + 4 + + + AFRL2 + Alternate function selection for port x + bit y (y = 0..7) + 8 + 4 + + + AFRL1 + Alternate function selection for port x + bit y (y = 0..7) + 4 + 4 + + + AFRL0 + Alternate function selection for port x + bit y (y = 0..7) + 0 + 4 + + + + + AFRH + AFRH + GPIO alternate function high + register + 0x24 + 0x20 + read-write + 0x00000000 + + + AFRH15 + Alternate function selection for port x + bit y (y = 8..15) + 28 + 4 + + + AFRH14 + Alternate function selection for port x + bit y (y = 8..15) + 24 + 4 + + + AFRH13 + Alternate function selection for port x + bit y (y = 8..15) + 20 + 4 + + + AFRH12 + Alternate function selection for port x + bit y (y = 8..15) + 16 + 4 + + + AFRH11 + Alternate function selection for port x + bit y (y = 8..15) + 12 + 4 + + + AFRH10 + Alternate function selection for port x + bit y (y = 8..15) + 8 + 4 + + + AFRH9 + Alternate function selection for port x + bit y (y = 8..15) + 4 + 4 + + + AFRH8 + Alternate function selection for port x + bit y (y = 8..15) + 0 + 4 + + + + + BRR + BRR + Port bit reset register + 0x28 + 0x20 + write-only + 0x00000000 + + + BR0 + Port x Reset bit y + 0 + 1 + + + BR1 + Port x Reset bit y + 1 + 1 + + + BR2 + Port x Reset bit y + 2 + 1 + + + BR3 + Port x Reset bit y + 3 + 1 + + + BR4 + Port x Reset bit y + 4 + 1 + + + BR5 + Port x Reset bit y + 5 + 1 + + + BR6 + Port x Reset bit y + 6 + 1 + + + BR7 + Port x Reset bit y + 7 + 1 + + + BR8 + Port x Reset bit y + 8 + 1 + + + BR9 + Port x Reset bit y + 9 + 1 + + + BR10 + Port x Reset bit y + 10 + 1 + + + BR11 + Port x Reset bit y + 11 + 1 + + + BR12 + Port x Reset bit y + 12 + 1 + + + BR13 + Port x Reset bit y + 13 + 1 + + + BR14 + Port x Reset bit y + 14 + 1 + + + BR15 + Port x Reset bit y + 15 + 1 + + + + + + + GPIOC + 0x48000800 + + + GPIOD + 0x48000C00 + + + GPIOE + 0x48001000 + + + GPIOF + 0x48001400 + + + GPIOG + 0x48001800 + + + GPIOH + 0x48001C00 + + + TSC + Touch sensing controller + TSC + 0x40024000 + + 0x0 + 0x400 + registers + + + + CR + CR + control register + 0x0 + 0x20 + read-write + 0x00000000 + + + CTPH + Charge transfer pulse high + 28 + 4 + + + CTPL + Charge transfer pulse low + 24 + 4 + + + SSD + Spread spectrum deviation + 17 + 7 + + + SSE + Spread spectrum enable + 16 + 1 + + + SSPSC + Spread spectrum prescaler + 15 + 1 + + + PGPSC + pulse generator prescaler + 12 + 3 + + + MCV + Max count value + 5 + 3 + + + IODEF + I/O Default mode + 4 + 1 + + + SYNCPOL + Synchronization pin + polarity + 3 + 1 + + + AM + Acquisition mode + 2 + 1 + + + START + Start a new acquisition + 1 + 1 + + + TSCE + Touch sensing controller + enable + 0 + 1 + + + + + IER + IER + interrupt enable register + 0x4 + 0x20 + read-write + 0x00000000 + + + MCEIE + Max count error interrupt + enable + 1 + 1 + + + EOAIE + End of acquisition interrupt + enable + 0 + 1 + + + + + ICR + ICR + interrupt clear register + 0x8 + 0x20 + read-write + 0x00000000 + + + MCEIC + Max count error interrupt + clear + 1 + 1 + + + EOAIC + End of acquisition interrupt + clear + 0 + 1 + + + + + ISR + ISR + interrupt status register + 0xC + 0x20 + read-write + 0x00000000 + + + MCEF + Max count error flag + 1 + 1 + + + EOAF + End of acquisition flag + 0 + 1 + + + + + IOHCR + IOHCR + I/O hysteresis control + register + 0x10 + 0x20 + read-write + 0xFFFFFFFF + + + G1_IO1 + G1_IO1 Schmitt trigger hysteresis + mode + 0 + 1 + + + G1_IO2 + G1_IO2 Schmitt trigger hysteresis + mode + 1 + 1 + + + G1_IO3 + G1_IO3 Schmitt trigger hysteresis + mode + 2 + 1 + + + G1_IO4 + G1_IO4 Schmitt trigger hysteresis + mode + 3 + 1 + + + G2_IO1 + G2_IO1 Schmitt trigger hysteresis + mode + 4 + 1 + + + G2_IO2 + G2_IO2 Schmitt trigger hysteresis + mode + 5 + 1 + + + G2_IO3 + G2_IO3 Schmitt trigger hysteresis + mode + 6 + 1 + + + G2_IO4 + G2_IO4 Schmitt trigger hysteresis + mode + 7 + 1 + + + G3_IO1 + G3_IO1 Schmitt trigger hysteresis + mode + 8 + 1 + + + G3_IO2 + G3_IO2 Schmitt trigger hysteresis + mode + 9 + 1 + + + G3_IO3 + G3_IO3 Schmitt trigger hysteresis + mode + 10 + 1 + + + G3_IO4 + G3_IO4 Schmitt trigger hysteresis + mode + 11 + 1 + + + G4_IO1 + G4_IO1 Schmitt trigger hysteresis + mode + 12 + 1 + + + G4_IO2 + G4_IO2 Schmitt trigger hysteresis + mode + 13 + 1 + + + G4_IO3 + G4_IO3 Schmitt trigger hysteresis + mode + 14 + 1 + + + G4_IO4 + G4_IO4 Schmitt trigger hysteresis + mode + 15 + 1 + + + G5_IO1 + G5_IO1 Schmitt trigger hysteresis + mode + 16 + 1 + + + G5_IO2 + G5_IO2 Schmitt trigger hysteresis + mode + 17 + 1 + + + G5_IO3 + G5_IO3 Schmitt trigger hysteresis + mode + 18 + 1 + + + G5_IO4 + G5_IO4 Schmitt trigger hysteresis + mode + 19 + 1 + + + G6_IO1 + G6_IO1 Schmitt trigger hysteresis + mode + 20 + 1 + + + G6_IO2 + G6_IO2 Schmitt trigger hysteresis + mode + 21 + 1 + + + G6_IO3 + G6_IO3 Schmitt trigger hysteresis + mode + 22 + 1 + + + G6_IO4 + G6_IO4 Schmitt trigger hysteresis + mode + 23 + 1 + + + G7_IO1 + G7_IO1 Schmitt trigger hysteresis + mode + 24 + 1 + + + G7_IO2 + G7_IO2 Schmitt trigger hysteresis + mode + 25 + 1 + + + G7_IO3 + G7_IO3 Schmitt trigger hysteresis + mode + 26 + 1 + + + G7_IO4 + G7_IO4 Schmitt trigger hysteresis + mode + 27 + 1 + + + G8_IO1 + G8_IO1 Schmitt trigger hysteresis + mode + 28 + 1 + + + G8_IO2 + G8_IO2 Schmitt trigger hysteresis + mode + 29 + 1 + + + G8_IO3 + G8_IO3 Schmitt trigger hysteresis + mode + 30 + 1 + + + G8_IO4 + G8_IO4 Schmitt trigger hysteresis + mode + 31 + 1 + + + + + IOASCR + IOASCR + I/O analog switch control + register + 0x18 + 0x20 + read-write + 0x00000000 + + + G1_IO1 + G1_IO1 analog switch + enable + 0 + 1 + + + G1_IO2 + G1_IO2 analog switch + enable + 1 + 1 + + + G1_IO3 + G1_IO3 analog switch + enable + 2 + 1 + + + G1_IO4 + G1_IO4 analog switch + enable + 3 + 1 + + + G2_IO1 + G2_IO1 analog switch + enable + 4 + 1 + + + G2_IO2 + G2_IO2 analog switch + enable + 5 + 1 + + + G2_IO3 + G2_IO3 analog switch + enable + 6 + 1 + + + G2_IO4 + G2_IO4 analog switch + enable + 7 + 1 + + + G3_IO1 + G3_IO1 analog switch + enable + 8 + 1 + + + G3_IO2 + G3_IO2 analog switch + enable + 9 + 1 + + + G3_IO3 + G3_IO3 analog switch + enable + 10 + 1 + + + G3_IO4 + G3_IO4 analog switch + enable + 11 + 1 + + + G4_IO1 + G4_IO1 analog switch + enable + 12 + 1 + + + G4_IO2 + G4_IO2 analog switch + enable + 13 + 1 + + + G4_IO3 + G4_IO3 analog switch + enable + 14 + 1 + + + G4_IO4 + G4_IO4 analog switch + enable + 15 + 1 + + + G5_IO1 + G5_IO1 analog switch + enable + 16 + 1 + + + G5_IO2 + G5_IO2 analog switch + enable + 17 + 1 + + + G5_IO3 + G5_IO3 analog switch + enable + 18 + 1 + + + G5_IO4 + G5_IO4 analog switch + enable + 19 + 1 + + + G6_IO1 + G6_IO1 analog switch + enable + 20 + 1 + + + G6_IO2 + G6_IO2 analog switch + enable + 21 + 1 + + + G6_IO3 + G6_IO3 analog switch + enable + 22 + 1 + + + G6_IO4 + G6_IO4 analog switch + enable + 23 + 1 + + + G7_IO1 + G7_IO1 analog switch + enable + 24 + 1 + + + G7_IO2 + G7_IO2 analog switch + enable + 25 + 1 + + + G7_IO3 + G7_IO3 analog switch + enable + 26 + 1 + + + G7_IO4 + G7_IO4 analog switch + enable + 27 + 1 + + + G8_IO1 + G8_IO1 analog switch + enable + 28 + 1 + + + G8_IO2 + G8_IO2 analog switch + enable + 29 + 1 + + + G8_IO3 + G8_IO3 analog switch + enable + 30 + 1 + + + G8_IO4 + G8_IO4 analog switch + enable + 31 + 1 + + + + + IOSCR + IOSCR + I/O sampling control register + 0x20 + 0x20 + read-write + 0x00000000 + + + G1_IO1 + G1_IO1 sampling mode + 0 + 1 + + + G1_IO2 + G1_IO2 sampling mode + 1 + 1 + + + G1_IO3 + G1_IO3 sampling mode + 2 + 1 + + + G1_IO4 + G1_IO4 sampling mode + 3 + 1 + + + G2_IO1 + G2_IO1 sampling mode + 4 + 1 + + + G2_IO2 + G2_IO2 sampling mode + 5 + 1 + + + G2_IO3 + G2_IO3 sampling mode + 6 + 1 + + + G2_IO4 + G2_IO4 sampling mode + 7 + 1 + + + G3_IO1 + G3_IO1 sampling mode + 8 + 1 + + + G3_IO2 + G3_IO2 sampling mode + 9 + 1 + + + G3_IO3 + G3_IO3 sampling mode + 10 + 1 + + + G3_IO4 + G3_IO4 sampling mode + 11 + 1 + + + G4_IO1 + G4_IO1 sampling mode + 12 + 1 + + + G4_IO2 + G4_IO2 sampling mode + 13 + 1 + + + G4_IO3 + G4_IO3 sampling mode + 14 + 1 + + + G4_IO4 + G4_IO4 sampling mode + 15 + 1 + + + G5_IO1 + G5_IO1 sampling mode + 16 + 1 + + + G5_IO2 + G5_IO2 sampling mode + 17 + 1 + + + G5_IO3 + G5_IO3 sampling mode + 18 + 1 + + + G5_IO4 + G5_IO4 sampling mode + 19 + 1 + + + G6_IO1 + G6_IO1 sampling mode + 20 + 1 + + + G6_IO2 + G6_IO2 sampling mode + 21 + 1 + + + G6_IO3 + G6_IO3 sampling mode + 22 + 1 + + + G6_IO4 + G6_IO4 sampling mode + 23 + 1 + + + G7_IO1 + G7_IO1 sampling mode + 24 + 1 + + + G7_IO2 + G7_IO2 sampling mode + 25 + 1 + + + G7_IO3 + G7_IO3 sampling mode + 26 + 1 + + + G7_IO4 + G7_IO4 sampling mode + 27 + 1 + + + G8_IO1 + G8_IO1 sampling mode + 28 + 1 + + + G8_IO2 + G8_IO2 sampling mode + 29 + 1 + + + G8_IO3 + G8_IO3 sampling mode + 30 + 1 + + + G8_IO4 + G8_IO4 sampling mode + 31 + 1 + + + + + IOCCR + IOCCR + I/O channel control register + 0x28 + 0x20 + read-write + 0x00000000 + + + G1_IO1 + G1_IO1 channel mode + 0 + 1 + + + G1_IO2 + G1_IO2 channel mode + 1 + 1 + + + G1_IO3 + G1_IO3 channel mode + 2 + 1 + + + G1_IO4 + G1_IO4 channel mode + 3 + 1 + + + G2_IO1 + G2_IO1 channel mode + 4 + 1 + + + G2_IO2 + G2_IO2 channel mode + 5 + 1 + + + G2_IO3 + G2_IO3 channel mode + 6 + 1 + + + G2_IO4 + G2_IO4 channel mode + 7 + 1 + + + G3_IO1 + G3_IO1 channel mode + 8 + 1 + + + G3_IO2 + G3_IO2 channel mode + 9 + 1 + + + G3_IO3 + G3_IO3 channel mode + 10 + 1 + + + G3_IO4 + G3_IO4 channel mode + 11 + 1 + + + G4_IO1 + G4_IO1 channel mode + 12 + 1 + + + G4_IO2 + G4_IO2 channel mode + 13 + 1 + + + G4_IO3 + G4_IO3 channel mode + 14 + 1 + + + G4_IO4 + G4_IO4 channel mode + 15 + 1 + + + G5_IO1 + G5_IO1 channel mode + 16 + 1 + + + G5_IO2 + G5_IO2 channel mode + 17 + 1 + + + G5_IO3 + G5_IO3 channel mode + 18 + 1 + + + G5_IO4 + G5_IO4 channel mode + 19 + 1 + + + G6_IO1 + G6_IO1 channel mode + 20 + 1 + + + G6_IO2 + G6_IO2 channel mode + 21 + 1 + + + G6_IO3 + G6_IO3 channel mode + 22 + 1 + + + G6_IO4 + G6_IO4 channel mode + 23 + 1 + + + G7_IO1 + G7_IO1 channel mode + 24 + 1 + + + G7_IO2 + G7_IO2 channel mode + 25 + 1 + + + G7_IO3 + G7_IO3 channel mode + 26 + 1 + + + G7_IO4 + G7_IO4 channel mode + 27 + 1 + + + G8_IO1 + G8_IO1 channel mode + 28 + 1 + + + G8_IO2 + G8_IO2 channel mode + 29 + 1 + + + G8_IO3 + G8_IO3 channel mode + 30 + 1 + + + G8_IO4 + G8_IO4 channel mode + 31 + 1 + + + + + IOGCSR + IOGCSR + I/O group control status + register + 0x30 + 0x20 + 0x00000000 + + + G8S + Analog I/O group x status + 23 + 1 + read-write + + + G7S + Analog I/O group x status + 22 + 1 + read-write + + + G6S + Analog I/O group x status + 21 + 1 + read-only + + + G5S + Analog I/O group x status + 20 + 1 + read-only + + + G4S + Analog I/O group x status + 19 + 1 + read-only + + + G3S + Analog I/O group x status + 18 + 1 + read-only + + + G2S + Analog I/O group x status + 17 + 1 + read-only + + + G1S + Analog I/O group x status + 16 + 1 + read-only + + + G8E + Analog I/O group x enable + 7 + 1 + read-write + + + G7E + Analog I/O group x enable + 6 + 1 + read-write + + + G6E + Analog I/O group x enable + 5 + 1 + read-write + + + G5E + Analog I/O group x enable + 4 + 1 + read-write + + + G4E + Analog I/O group x enable + 3 + 1 + read-write + + + G3E + Analog I/O group x enable + 2 + 1 + read-write + + + G2E + Analog I/O group x enable + 1 + 1 + read-write + + + G1E + Analog I/O group x enable + 0 + 1 + read-write + + + + + IOG1CR + IOG1CR + I/O group x counter register + 0x34 + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + IOG2CR + IOG2CR + I/O group x counter register + 0x38 + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + IOG3CR + IOG3CR + I/O group x counter register + 0x3C + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + IOG4CR + IOG4CR + I/O group x counter register + 0x40 + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + IOG5CR + IOG5CR + I/O group x counter register + 0x44 + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + IOG6CR + IOG6CR + I/O group x counter register + 0x48 + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + IOG7CR + IOG7CR + I/O group x counter register + 0x4C + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + IOG8CR + IOG8CR + I/O group x counter register + 0x50 + 0x20 + read-only + 0x00000000 + + + CNT + Counter value + 0 + 14 + + + + + + + CRC + cyclic redundancy check calculation + unit + CRC + 0x40023000 + + 0x0 + 0x400 + registers + + + + DR + DR + Data register + 0x0 + 0x20 + read-write + 0xFFFFFFFF + + + DR + Data register bits + 0 + 32 + + + + + IDR + IDR + Independent data register + 0x4 + 0x20 + read-write + 0x00000000 + + + IDR + General-purpose 8-bit data register + bits + 0 + 8 + + + + + CR + CR + Control register + 0x8 + 0x20 + read-write + 0x00000000 + + + RESET + reset bit + 0 + 1 + + + POLYSIZE + Polynomial size + 3 + 2 + + + REV_IN + Reverse input data + 5 + 2 + + + REV_OUT + Reverse output data + 7 + 1 + + + + + INIT + INIT + Initial CRC value + 0x10 + 0x20 + read-write + 0xFFFFFFFF + + + INIT + Programmable initial CRC + value + 0 + 32 + + + + + POL + POL + CRC polynomial + 0x14 + 0x20 + read-write + 0x04C11DB7 + + + POL + Programmable polynomial + 0 + 32 + + + + + + + Flash + Flash + Flash + 0x40022000 + + 0x0 + 0x400 + registers + + + FLASH + Flash global interrupt + 4 + + + + ACR + ACR + Flash access control register + 0x0 + 0x20 + 0x00000030 + + + LATENCY + LATENCY + 0 + 3 + read-write + + + PRFTBE + PRFTBE + 4 + 1 + read-write + + + PRFTBS + PRFTBS + 5 + 1 + read-only + + + + + KEYR + KEYR + Flash key register + 0x4 + 0x20 + write-only + 0x00000000 + + + FKEYR + Flash Key + 0 + 32 + + + + + OPTKEYR + OPTKEYR + Flash option key register + 0x8 + 0x20 + write-only + 0x00000000 + + + OPTKEYR + Option byte key + 0 + 32 + + + + + SR + SR + Flash status register + 0xC + 0x20 + 0x00000000 + + + EOP + End of operation + 5 + 1 + read-write + + + WRPRT + Write protection error + 4 + 1 + read-write + + + PGERR + Programming error + 2 + 1 + read-write + + + BSY + Busy + 0 + 1 + read-only + + + + + CR + CR + Flash control register + 0x10 + 0x20 + read-write + 0x00000080 + + + FORCE_OPTLOAD + Force option byte loading + 13 + 1 + + + EOPIE + End of operation interrupt + enable + 12 + 1 + + + ERRIE + Error interrupt enable + 10 + 1 + + + OPTWRE + Option bytes write enable + 9 + 1 + + + LOCK + Lock + 7 + 1 + + + STRT + Start + 6 + 1 + + + OPTER + Option byte erase + 5 + 1 + + + OPTPG + Option byte programming + 4 + 1 + + + MER + Mass erase + 2 + 1 + + + PER + Page erase + 1 + 1 + + + PG + Programming + 0 + 1 + + + + + AR + AR + Flash address register + 0x14 + 0x20 + write-only + 0x00000000 + + + FAR + Flash address + 0 + 32 + + + + + OBR + OBR + Option byte register + 0x1C + 0x20 + read-only + 0xFFFFFF02 + + + OPTERR + Option byte error + 0 + 1 + + + LEVEL1_PROT + Level 1 protection status + 1 + 1 + + + LEVEL2_PROT + Level 2 protection status + 2 + 1 + + + WDG_SW + WDG_SW + 8 + 1 + + + nRST_STOP + nRST_STOP + 9 + 1 + + + nRST_STDBY + nRST_STDBY + 10 + 1 + + + BOOT1 + BOOT1 + 12 + 1 + + + VDDA_MONITOR + VDDA_MONITOR + 13 + 1 + + + SRAM_PARITY_CHECK + SRAM_PARITY_CHECK + 14 + 1 + + + Data0 + Data0 + 16 + 8 + + + Data1 + Data1 + 24 + 8 + + + + + WRPR + WRPR + Write protection register + 0x20 + 0x20 + read-only + 0xFFFFFFFF + + + WRP + Write protect + 0 + 32 + + + + + + + RCC + Reset and clock control + RCC + 0x40021000 + + 0x0 + 0x400 + registers + + + RCC + RCC global interrupt + 5 + + + + CR + CR + Clock control register + 0x0 + 0x20 + 0x00000083 + + + HSION + Internal High Speed clock + enable + 0 + 1 + read-write + + + HSIRDY + Internal High Speed clock ready + flag + 1 + 1 + read-only + + + HSITRIM + Internal High Speed clock + trimming + 3 + 5 + read-write + + + HSICAL + Internal High Speed clock + Calibration + 8 + 8 + read-only + + + HSEON + External High Speed clock + enable + 16 + 1 + read-write + + + HSERDY + External High Speed clock ready + flag + 17 + 1 + read-only + + + HSEBYP + External High Speed clock + Bypass + 18 + 1 + read-write + + + CSSON + Clock Security System + enable + 19 + 1 + read-write + + + PLLON + PLL enable + 24 + 1 + read-write + + + PLLRDY + PLL clock ready flag + 25 + 1 + read-only + + + + + CFGR + CFGR + Clock configuration register + (RCC_CFGR) + 0x4 + 0x20 + 0x00000000 + + + SW + System clock Switch + 0 + 2 + read-write + + + SWS + System Clock Switch Status + 2 + 2 + read-only + + + HPRE + AHB prescaler + 4 + 4 + read-write + + + PPRE1 + APB Low speed prescaler + (APB1) + 8 + 3 + read-write + + + PPRE2 + APB high speed prescaler + (APB2) + 11 + 3 + read-write + + + PLLSRC + PLL entry clock source + 15 + 2 + read-write + + + PLLXTPRE + HSE divider for PLL entry + 17 + 1 + read-write + + + PLLMUL + PLL Multiplication Factor + 18 + 4 + read-write + + + USBPRES + USB prescaler + 22 + 1 + read-write + + + MCO + Microcontroller clock + output + 24 + 3 + read-write + + + MCOF + Microcontroller Clock Output + Flag + 28 + 1 + read-only + + + I2SSRC + I2S external clock source + selection + 23 + 1 + read-write + + + + + CIR + CIR + Clock interrupt register + (RCC_CIR) + 0x8 + 0x20 + 0x00000000 + + + LSIRDYF + LSI Ready Interrupt flag + 0 + 1 + read-only + + + LSERDYF + LSE Ready Interrupt flag + 1 + 1 + read-only + + + HSIRDYF + HSI Ready Interrupt flag + 2 + 1 + read-only + + + HSERDYF + HSE Ready Interrupt flag + 3 + 1 + read-only + + + PLLRDYF + PLL Ready Interrupt flag + 4 + 1 + read-only + + + CSSF + Clock Security System Interrupt + flag + 7 + 1 + read-only + + + LSIRDYIE + LSI Ready Interrupt Enable + 8 + 1 + read-write + + + LSERDYIE + LSE Ready Interrupt Enable + 9 + 1 + read-write + + + HSIRDYIE + HSI Ready Interrupt Enable + 10 + 1 + read-write + + + HSERDYIE + HSE Ready Interrupt Enable + 11 + 1 + read-write + + + PLLRDYIE + PLL Ready Interrupt Enable + 12 + 1 + read-write + + + LSIRDYC + LSI Ready Interrupt Clear + 16 + 1 + write-only + + + LSERDYC + LSE Ready Interrupt Clear + 17 + 1 + write-only + + + HSIRDYC + HSI Ready Interrupt Clear + 18 + 1 + write-only + + + HSERDYC + HSE Ready Interrupt Clear + 19 + 1 + write-only + + + PLLRDYC + PLL Ready Interrupt Clear + 20 + 1 + write-only + + + CSSC + Clock security system interrupt + clear + 23 + 1 + write-only + + + + + APB2RSTR + APB2RSTR + APB2 peripheral reset register + (RCC_APB2RSTR) + 0xC + 0x20 + read-write + 0x00000000 + + + SYSCFGRST + SYSCFG and COMP reset + 0 + 1 + + + TIM1RST + TIM1 timer reset + 11 + 1 + + + SPI1RST + SPI 1 reset + 12 + 1 + + + TIM8RST + TIM8 timer reset + 13 + 1 + + + USART1RST + USART1 reset + 14 + 1 + + + TIM15RST + TIM15 timer reset + 16 + 1 + + + TIM16RST + TIM16 timer reset + 17 + 1 + + + TIM17RST + TIM17 timer reset + 18 + 1 + + + + + APB1RSTR + APB1RSTR + APB1 peripheral reset register + (RCC_APB1RSTR) + 0x10 + 0x20 + read-write + 0x00000000 + + + TIM2RST + Timer 2 reset + 0 + 1 + + + TIM3RST + Timer 3 reset + 1 + 1 + + + TIM4RST + Timer 14 reset + 2 + 1 + + + TIM6RST + Timer 6 reset + 4 + 1 + + + TIM7RST + Timer 7 reset + 5 + 1 + + + WWDGRST + Window watchdog reset + 11 + 1 + + + SPI2RST + SPI2 reset + 14 + 1 + + + SPI3RST + SPI3 reset + 15 + 1 + + + USART2RST + USART 2 reset + 17 + 1 + + + USART3RST + USART3 reset + 18 + 1 + + + UART4RST + UART 4 reset + 19 + 1 + + + UART5RST + UART 5 reset + 20 + 1 + + + I2C1RST + I2C1 reset + 21 + 1 + + + I2C2RST + I2C2 reset + 22 + 1 + + + USBRST + USB reset + 23 + 1 + + + CANRST + CAN reset + 25 + 1 + + + PWRRST + Power interface reset + 28 + 1 + + + DACRST + DAC interface reset + 29 + 1 + + + I2C3RST + I2C3 reset + 30 + 1 + + + + + AHBENR + AHBENR + AHB Peripheral Clock enable register + (RCC_AHBENR) + 0x14 + 0x20 + read-write + 0x00000014 + + + DMAEN + DMA1 clock enable + 0 + 1 + + + DMA2EN + DMA2 clock enable + 1 + 1 + + + SRAMEN + SRAM interface clock + enable + 2 + 1 + + + FLITFEN + FLITF clock enable + 4 + 1 + + + FMCEN + FMC clock enable + 5 + 1 + + + CRCEN + CRC clock enable + 6 + 1 + + + IOPHEN + IO port H clock enable + 16 + 1 + + + IOPAEN + I/O port A clock enable + 17 + 1 + + + IOPBEN + I/O port B clock enable + 18 + 1 + + + IOPCEN + I/O port C clock enable + 19 + 1 + + + IOPDEN + I/O port D clock enable + 20 + 1 + + + IOPEEN + I/O port E clock enable + 21 + 1 + + + IOPFEN + I/O port F clock enable + 22 + 1 + + + IOPGEN + I/O port G clock enable + 23 + 1 + + + TSCEN + Touch sensing controller clock + enable + 24 + 1 + + + ADC12EN + ADC1 and ADC2 clock enable + 28 + 1 + + + ADC34EN + ADC3 and ADC4 clock enable + 29 + 1 + + + + + APB2ENR + APB2ENR + APB2 peripheral clock enable register + (RCC_APB2ENR) + 0x18 + 0x20 + read-write + 0x00000000 + + + SYSCFGEN + SYSCFG clock enable + 0 + 1 + + + TIM1EN + TIM1 Timer clock enable + 11 + 1 + + + SPI1EN + SPI 1 clock enable + 12 + 1 + + + TIM8EN + TIM8 Timer clock enable + 13 + 1 + + + USART1EN + USART1 clock enable + 14 + 1 + + + TIM15EN + TIM15 timer clock enable + 16 + 1 + + + TIM16EN + TIM16 timer clock enable + 17 + 1 + + + TIM17EN + TIM17 timer clock enable + 18 + 1 + + + + + APB1ENR + APB1ENR + APB1 peripheral clock enable register + (RCC_APB1ENR) + 0x1C + 0x20 + read-write + 0x00000000 + + + TIM2EN + Timer 2 clock enable + 0 + 1 + + + TIM3EN + Timer 3 clock enable + 1 + 1 + + + TIM4EN + Timer 4 clock enable + 2 + 1 + + + TIM6EN + Timer 6 clock enable + 4 + 1 + + + TIM7EN + Timer 7 clock enable + 5 + 1 + + + WWDGEN + Window watchdog clock + enable + 11 + 1 + + + SPI2EN + SPI 2 clock enable + 14 + 1 + + + SPI3EN + SPI 3 clock enable + 15 + 1 + + + USART2EN + USART 2 clock enable + 17 + 1 + + + USART3EN + USART 3 clock enable + 18 + 1 + + + USART4EN + USART 4 clock enable + 19 + 1 + + + USART5EN + USART 5 clock enable + 20 + 1 + + + I2C1EN + I2C 1 clock enable + 21 + 1 + + + I2C2EN + I2C 2 clock enable + 22 + 1 + + + USBEN + USB clock enable + 23 + 1 + + + CANEN + CAN clock enable + 25 + 1 + + + DAC2EN + DAC2 interface clock + enable + 26 + 1 + + + PWREN + Power interface clock + enable + 28 + 1 + + + DACEN + DAC interface clock enable + 29 + 1 + + + I2C3EN + I2C3 clock enable + 30 + 1 + + + + + BDCR + BDCR + Backup domain control register + (RCC_BDCR) + 0x20 + 0x20 + 0x00000000 + + + LSEON + External Low Speed oscillator + enable + 0 + 1 + read-write + + + LSERDY + External Low Speed oscillator + ready + 1 + 1 + read-only + + + LSEBYP + External Low Speed oscillator + bypass + 2 + 1 + read-write + + + LSEDRV + LSE oscillator drive + capability + 3 + 2 + read-write + + + RTCSEL + RTC clock source selection + 8 + 2 + read-write + + + RTCEN + RTC clock enable + 15 + 1 + read-write + + + BDRST + Backup domain software + reset + 16 + 1 + read-write + + + + + CSR + CSR + Control/status register + (RCC_CSR) + 0x24 + 0x20 + 0x0C000000 + + + LSION + Internal low speed oscillator + enable + 0 + 1 + read-write + + + LSIRDY + Internal low speed oscillator + ready + 1 + 1 + read-only + + + RMVF + Remove reset flag + 24 + 1 + read-write + + + OBLRSTF + Option byte loader reset + flag + 25 + 1 + read-write + + + PINRSTF + PIN reset flag + 26 + 1 + read-write + + + PORRSTF + POR/PDR reset flag + 27 + 1 + read-write + + + SFTRSTF + Software reset flag + 28 + 1 + read-write + + + IWDGRSTF + Independent watchdog reset + flag + 29 + 1 + read-write + + + WWDGRSTF + Window watchdog reset flag + 30 + 1 + read-write + + + LPWRRSTF + Low-power reset flag + 31 + 1 + read-write + + + + + AHBRSTR + AHBRSTR + AHB peripheral reset register + 0x28 + 0x20 + read-write + 0x00000000 + + + FMCRST + FMC reset + 5 + 1 + + + IOPHRST + I/O port H reset + 16 + 1 + + + IOPARST + I/O port A reset + 17 + 1 + + + IOPBRST + I/O port B reset + 18 + 1 + + + IOPCRST + I/O port C reset + 19 + 1 + + + IOPDRST + I/O port D reset + 20 + 1 + + + IOPERST + I/O port E reset + 21 + 1 + + + IOPFRST + I/O port F reset + 22 + 1 + + + IOPGRST + Touch sensing controller + reset + 23 + 1 + + + TSCRST + Touch sensing controller + reset + 24 + 1 + + + ADC12RST + ADC1 and ADC2 reset + 28 + 1 + + + ADC34RST + ADC3 and ADC4 reset + 29 + 1 + + + + + CFGR2 + CFGR2 + Clock configuration register 2 + 0x2C + 0x20 + read-write + 0x00000000 + + + PREDIV + PREDIV division factor + 0 + 4 + + + ADC12PRES + ADC1 and ADC2 prescaler + 4 + 5 + + + ADC34PRES + ADC3 and ADC4 prescaler + 9 + 5 + + + + + CFGR3 + CFGR3 + Clock configuration register 3 + 0x30 + 0x20 + read-write + 0x00000000 + + + USART1SW + USART1 clock source + selection + 0 + 2 + + + I2C1SW + I2C1 clock source + selection + 4 + 1 + + + I2C2SW + I2C2 clock source + selection + 5 + 1 + + + I2C3SW + I2C3 clock source + selection + 6 + 1 + + + USART2SW + USART2 clock source + selection + 16 + 2 + + + USART3SW + USART3 clock source + selection + 18 + 2 + + + TIM1SW + Timer1 clock source + selection + 8 + 1 + + + TIM8SW + Timer8 clock source + selection + 9 + 1 + + + UART4SW + UART4 clock source + selection + 20 + 2 + + + UART5SW + UART5 clock source + selection + 22 + 2 + + + + + + + DMA1 + DMA controller 1 + DMA + 0x40020000 + + 0x0 + 0x400 + registers + + + DMA1_CH1 + DMA1 channel 1 interrupt + 11 + + + DMA1_CH2 + DMA1 channel 2 interrupt + 12 + + + DMA1_CH3 + DMA1 channel 3 interrupt + 13 + + + DMA1_CH4 + DMA1 channel 4 interrupt + 14 + + + DMA1_CH5 + DMA1 channel 5 interrupt + 15 + + + DMA1_CH6 + DMA1 channel 6 interrupt + 16 + + + DMA1_CH7 + DMA1 channel 7interrupt + 17 + + + + ISR + ISR + DMA interrupt status register + (DMA_ISR) + 0x0 + 0x20 + read-only + 0x00000000 + + + GIF1 + Channel 1 Global interrupt + flag + 0 + 1 + + + TCIF1 + Channel 1 Transfer Complete + flag + 1 + 1 + + + HTIF1 + Channel 1 Half Transfer Complete + flag + 2 + 1 + + + TEIF1 + Channel 1 Transfer Error + flag + 3 + 1 + + + GIF2 + Channel 2 Global interrupt + flag + 4 + 1 + + + TCIF2 + Channel 2 Transfer Complete + flag + 5 + 1 + + + HTIF2 + Channel 2 Half Transfer Complete + flag + 6 + 1 + + + TEIF2 + Channel 2 Transfer Error + flag + 7 + 1 + + + GIF3 + Channel 3 Global interrupt + flag + 8 + 1 + + + TCIF3 + Channel 3 Transfer Complete + flag + 9 + 1 + + + HTIF3 + Channel 3 Half Transfer Complete + flag + 10 + 1 + + + TEIF3 + Channel 3 Transfer Error + flag + 11 + 1 + + + GIF4 + Channel 4 Global interrupt + flag + 12 + 1 + + + TCIF4 + Channel 4 Transfer Complete + flag + 13 + 1 + + + HTIF4 + Channel 4 Half Transfer Complete + flag + 14 + 1 + + + TEIF4 + Channel 4 Transfer Error + flag + 15 + 1 + + + GIF5 + Channel 5 Global interrupt + flag + 16 + 1 + + + TCIF5 + Channel 5 Transfer Complete + flag + 17 + 1 + + + HTIF5 + Channel 5 Half Transfer Complete + flag + 18 + 1 + + + TEIF5 + Channel 5 Transfer Error + flag + 19 + 1 + + + GIF6 + Channel 6 Global interrupt + flag + 20 + 1 + + + TCIF6 + Channel 6 Transfer Complete + flag + 21 + 1 + + + HTIF6 + Channel 6 Half Transfer Complete + flag + 22 + 1 + + + TEIF6 + Channel 6 Transfer Error + flag + 23 + 1 + + + GIF7 + Channel 7 Global interrupt + flag + 24 + 1 + + + TCIF7 + Channel 7 Transfer Complete + flag + 25 + 1 + + + HTIF7 + Channel 7 Half Transfer Complete + flag + 26 + 1 + + + TEIF7 + Channel 7 Transfer Error + flag + 27 + 1 + + + + + IFCR + IFCR + DMA interrupt flag clear register + (DMA_IFCR) + 0x4 + 0x20 + write-only + 0x00000000 + + + CGIF1 + Channel 1 Global interrupt + clear + 0 + 1 + + + CTCIF1 + Channel 1 Transfer Complete + clear + 1 + 1 + + + CHTIF1 + Channel 1 Half Transfer + clear + 2 + 1 + + + CTEIF1 + Channel 1 Transfer Error + clear + 3 + 1 + + + CGIF2 + Channel 2 Global interrupt + clear + 4 + 1 + + + CTCIF2 + Channel 2 Transfer Complete + clear + 5 + 1 + + + CHTIF2 + Channel 2 Half Transfer + clear + 6 + 1 + + + CTEIF2 + Channel 2 Transfer Error + clear + 7 + 1 + + + CGIF3 + Channel 3 Global interrupt + clear + 8 + 1 + + + CTCIF3 + Channel 3 Transfer Complete + clear + 9 + 1 + + + CHTIF3 + Channel 3 Half Transfer + clear + 10 + 1 + + + CTEIF3 + Channel 3 Transfer Error + clear + 11 + 1 + + + CGIF4 + Channel 4 Global interrupt + clear + 12 + 1 + + + CTCIF4 + Channel 4 Transfer Complete + clear + 13 + 1 + + + CHTIF4 + Channel 4 Half Transfer + clear + 14 + 1 + + + CTEIF4 + Channel 4 Transfer Error + clear + 15 + 1 + + + CGIF5 + Channel 5 Global interrupt + clear + 16 + 1 + + + CTCIF5 + Channel 5 Transfer Complete + clear + 17 + 1 + + + CHTIF5 + Channel 5 Half Transfer + clear + 18 + 1 + + + CTEIF5 + Channel 5 Transfer Error + clear + 19 + 1 + + + CGIF6 + Channel 6 Global interrupt + clear + 20 + 1 + + + CTCIF6 + Channel 6 Transfer Complete + clear + 21 + 1 + + + CHTIF6 + Channel 6 Half Transfer + clear + 22 + 1 + + + CTEIF6 + Channel 6 Transfer Error + clear + 23 + 1 + + + CGIF7 + Channel 7 Global interrupt + clear + 24 + 1 + + + CTCIF7 + Channel 7 Transfer Complete + clear + 25 + 1 + + + CHTIF7 + Channel 7 Half Transfer + clear + 26 + 1 + + + CTEIF7 + Channel 7 Transfer Error + clear + 27 + 1 + + + + + CCR1 + CCR1 + DMA channel configuration register + (DMA_CCR) + 0x8 + 0x20 + read-write + 0x00000000 + + + EN + Channel enable + 0 + 1 + + + TCIE + Transfer complete interrupt + enable + 1 + 1 + + + HTIE + Half Transfer interrupt + enable + 2 + 1 + + + TEIE + Transfer error interrupt + enable + 3 + 1 + + + DIR + Data transfer direction + 4 + 1 + + + CIRC + Circular mode + 5 + 1 + + + PINC + Peripheral increment mode + 6 + 1 + + + MINC + Memory increment mode + 7 + 1 + + + PSIZE + Peripheral size + 8 + 2 + + + MSIZE + Memory size + 10 + 2 + + + PL + Channel Priority level + 12 + 2 + + + MEM2MEM + Memory to memory mode + 14 + 1 + + + + + CNDTR1 + CNDTR1 + DMA channel 1 number of data + register + 0xC + 0x20 + read-write + 0x00000000 + + + NDT + Number of data to transfer + 0 + 16 + + + + + CPAR1 + CPAR1 + DMA channel 1 peripheral address + register + 0x10 + 0x20 + read-write + 0x00000000 + + + PA + Peripheral address + 0 + 32 + + + + + CMAR1 + CMAR1 + DMA channel 1 memory address + register + 0x14 + 0x20 + read-write + 0x00000000 + + + MA + Memory address + 0 + 32 + + + + + CCR2 + CCR2 + DMA channel configuration register + (DMA_CCR) + 0x1C + 0x20 + read-write + 0x00000000 + + + EN + Channel enable + 0 + 1 + + + TCIE + Transfer complete interrupt + enable + 1 + 1 + + + HTIE + Half Transfer interrupt + enable + 2 + 1 + + + TEIE + Transfer error interrupt + enable + 3 + 1 + + + DIR + Data transfer direction + 4 + 1 + + + CIRC + Circular mode + 5 + 1 + + + PINC + Peripheral increment mode + 6 + 1 + + + MINC + Memory increment mode + 7 + 1 + + + PSIZE + Peripheral size + 8 + 2 + + + MSIZE + Memory size + 10 + 2 + + + PL + Channel Priority level + 12 + 2 + + + MEM2MEM + Memory to memory mode + 14 + 1 + + + + + CNDTR2 + CNDTR2 + DMA channel 2 number of data + register + 0x20 + 0x20 + read-write + 0x00000000 + + + NDT + Number of data to transfer + 0 + 16 + + + + + CPAR2 + CPAR2 + DMA channel 2 peripheral address + register + 0x24 + 0x20 + read-write + 0x00000000 + + + PA + Peripheral address + 0 + 32 + + + + + CMAR2 + CMAR2 + DMA channel 2 memory address + register + 0x28 + 0x20 + read-write + 0x00000000 + + + MA + Memory address + 0 + 32 + + + + + CCR3 + CCR3 + DMA channel configuration register + (DMA_CCR) + 0x30 + 0x20 + read-write + 0x00000000 + + + EN + Channel enable + 0 + 1 + + + TCIE + Transfer complete interrupt + enable + 1 + 1 + + + HTIE + Half Transfer interrupt + enable + 2 + 1 + + + TEIE + Transfer error interrupt + enable + 3 + 1 + + + DIR + Data transfer direction + 4 + 1 + + + CIRC + Circular mode + 5 + 1 + + + PINC + Peripheral increment mode + 6 + 1 + + + MINC + Memory increment mode + 7 + 1 + + + PSIZE + Peripheral size + 8 + 2 + + + MSIZE + Memory size + 10 + 2 + + + PL + Channel Priority level + 12 + 2 + + + MEM2MEM + Memory to memory mode + 14 + 1 + + + + + CNDTR3 + CNDTR3 + DMA channel 3 number of data + register + 0x34 + 0x20 + read-write + 0x00000000 + + + NDT + Number of data to transfer + 0 + 16 + + + + + CPAR3 + CPAR3 + DMA channel 3 peripheral address + register + 0x38 + 0x20 + read-write + 0x00000000 + + + PA + Peripheral address + 0 + 32 + + + + + CMAR3 + CMAR3 + DMA channel 3 memory address + register + 0x3C + 0x20 + read-write + 0x00000000 + + + MA + Memory address + 0 + 32 + + + + + CCR4 + CCR4 + DMA channel configuration register + (DMA_CCR) + 0x44 + 0x20 + read-write + 0x00000000 + + + EN + Channel enable + 0 + 1 + + + TCIE + Transfer complete interrupt + enable + 1 + 1 + + + HTIE + Half Transfer interrupt + enable + 2 + 1 + + + TEIE + Transfer error interrupt + enable + 3 + 1 + + + DIR + Data transfer direction + 4 + 1 + + + CIRC + Circular mode + 5 + 1 + + + PINC + Peripheral increment mode + 6 + 1 + + + MINC + Memory increment mode + 7 + 1 + + + PSIZE + Peripheral size + 8 + 2 + + + MSIZE + Memory size + 10 + 2 + + + PL + Channel Priority level + 12 + 2 + + + MEM2MEM + Memory to memory mode + 14 + 1 + + + + + CNDTR4 + CNDTR4 + DMA channel 4 number of data + register + 0x48 + 0x20 + read-write + 0x00000000 + + + NDT + Number of data to transfer + 0 + 16 + + + + + CPAR4 + CPAR4 + DMA channel 4 peripheral address + register + 0x4C + 0x20 + read-write + 0x00000000 + + + PA + Peripheral address + 0 + 32 + + + + + CMAR4 + CMAR4 + DMA channel 4 memory address + register + 0x50 + 0x20 + read-write + 0x00000000 + + + MA + Memory address + 0 + 32 + + + + + CCR5 + CCR5 + DMA channel configuration register + (DMA_CCR) + 0x58 + 0x20 + read-write + 0x00000000 + + + EN + Channel enable + 0 + 1 + + + TCIE + Transfer complete interrupt + enable + 1 + 1 + + + HTIE + Half Transfer interrupt + enable + 2 + 1 + + + TEIE + Transfer error interrupt + enable + 3 + 1 + + + DIR + Data transfer direction + 4 + 1 + + + CIRC + Circular mode + 5 + 1 + + + PINC + Peripheral increment mode + 6 + 1 + + + MINC + Memory increment mode + 7 + 1 + + + PSIZE + Peripheral size + 8 + 2 + + + MSIZE + Memory size + 10 + 2 + + + PL + Channel Priority level + 12 + 2 + + + MEM2MEM + Memory to memory mode + 14 + 1 + + + + + CNDTR5 + CNDTR5 + DMA channel 5 number of data + register + 0x5C + 0x20 + read-write + 0x00000000 + + + NDT + Number of data to transfer + 0 + 16 + + + + + CPAR5 + CPAR5 + DMA channel 5 peripheral address + register + 0x60 + 0x20 + read-write + 0x00000000 + + + PA + Peripheral address + 0 + 32 + + + + + CMAR5 + CMAR5 + DMA channel 5 memory address + register + 0x64 + 0x20 + read-write + 0x00000000 + + + MA + Memory address + 0 + 32 + + + + + CCR6 + CCR6 + DMA channel configuration register + (DMA_CCR) + 0x6C + 0x20 + read-write + 0x00000000 + + + EN + Channel enable + 0 + 1 + + + TCIE + Transfer complete interrupt + enable + 1 + 1 + + + HTIE + Half Transfer interrupt + enable + 2 + 1 + + + TEIE + Transfer error interrupt + enable + 3 + 1 + + + DIR + Data transfer direction + 4 + 1 + + + CIRC + Circular mode + 5 + 1 + + + PINC + Peripheral increment mode + 6 + 1 + + + MINC + Memory increment mode + 7 + 1 + + + PSIZE + Peripheral size + 8 + 2 + + + MSIZE + Memory size + 10 + 2 + + + PL + Channel Priority level + 12 + 2 + + + MEM2MEM + Memory to memory mode + 14 + 1 + + + + + CNDTR6 + CNDTR6 + DMA channel 6 number of data + register + 0x70 + 0x20 + read-write + 0x00000000 + + + NDT + Number of data to transfer + 0 + 16 + + + + + CPAR6 + CPAR6 + DMA channel 6 peripheral address + register + 0x74 + 0x20 + read-write + 0x00000000 + + + PA + Peripheral address + 0 + 32 + + + + + CMAR6 + CMAR6 + DMA channel 6 memory address + register + 0x78 + 0x20 + read-write + 0x00000000 + + + MA + Memory address + 0 + 32 + + + + + CCR7 + CCR7 + DMA channel configuration register + (DMA_CCR) + 0x80 + 0x20 + read-write + 0x00000000 + + + EN + Channel enable + 0 + 1 + + + TCIE + Transfer complete interrupt + enable + 1 + 1 + + + HTIE + Half Transfer interrupt + enable + 2 + 1 + + + TEIE + Transfer error interrupt + enable + 3 + 1 + + + DIR + Data transfer direction + 4 + 1 + + + CIRC + Circular mode + 5 + 1 + + + PINC + Peripheral increment mode + 6 + 1 + + + MINC + Memory increment mode + 7 + 1 + + + PSIZE + Peripheral size + 8 + 2 + + + MSIZE + Memory size + 10 + 2 + + + PL + Channel Priority level + 12 + 2 + + + MEM2MEM + Memory to memory mode + 14 + 1 + + + + + CNDTR7 + CNDTR7 + DMA channel 7 number of data + register + 0x84 + 0x20 + read-write + 0x00000000 + + + NDT + Number of data to transfer + 0 + 16 + + + + + CPAR7 + CPAR7 + DMA channel 7 peripheral address + register + 0x88 + 0x20 + read-write + 0x00000000 + + + PA + Peripheral address + 0 + 32 + + + + + CMAR7 + CMAR7 + DMA channel 7 memory address + register + 0x8C + 0x20 + read-write + 0x00000000 + + + MA + Memory address + 0 + 32 + + + + + + + DMA2 + 0x40020400 + + DMA2_CH1 + DMA2 channel1 global interrupt + 56 + + + DMA2_CH2 + DMA2 channel2 global interrupt + 57 + + + DMA2_CH3 + DMA2 channel3 global interrupt + 58 + + + DMA2_CH4 + DMA2 channel4 global interrupt + 59 + + + DMA2_CH5 + DMA2 channel5 global interrupt + 60 + + + + TIM2 + General purpose timer + TIMs + 0x40000000 + + 0x0 + 0x400 + registers + + + TIM2 + TIM2 global interrupt + 28 + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + CEN + Counter enable + 0 + 1 + + + UDIS + Update disable + 1 + 1 + + + URS + Update request source + 2 + 1 + + + OPM + One-pulse mode + 3 + 1 + + + DIR + Direction + 4 + 1 + + + CMS + Center-aligned mode + selection + 5 + 2 + + + ARPE + Auto-reload preload enable + 7 + 1 + + + CKD + Clock division + 8 + 2 + + + UIFREMAP + UIF status bit remapping + 11 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + TI1S + TI1 selection + 7 + 1 + + + MMS + Master mode selection + 4 + 3 + + + CCDS + Capture/compare DMA + selection + 3 + 1 + + + + + SMCR + SMCR + slave mode control register + 0x8 + 0x20 + read-write + 0x0000 + + + SMS + Slave mode selection + 0 + 3 + + + OCCS + OCREF clear selection + 3 + 1 + + + TS + Trigger selection + 4 + 3 + + + MSM + Master/Slave mode + 7 + 1 + + + ETF + External trigger filter + 8 + 4 + + + ETPS + External trigger prescaler + 12 + 2 + + + ECE + External clock enable + 14 + 1 + + + ETP + External trigger polarity + 15 + 1 + + + SMS_3 + Slave mode selection bit3 + 16 + 1 + + + + + DIER + DIER + DMA/Interrupt enable register + 0xC + 0x20 + read-write + 0x0000 + + + TDE + Trigger DMA request enable + 14 + 1 + + + CC4DE + Capture/Compare 4 DMA request + enable + 12 + 1 + + + CC3DE + Capture/Compare 3 DMA request + enable + 11 + 1 + + + CC2DE + Capture/Compare 2 DMA request + enable + 10 + 1 + + + CC1DE + Capture/Compare 1 DMA request + enable + 9 + 1 + + + UDE + Update DMA request enable + 8 + 1 + + + TIE + Trigger interrupt enable + 6 + 1 + + + CC4IE + Capture/Compare 4 interrupt + enable + 4 + 1 + + + CC3IE + Capture/Compare 3 interrupt + enable + 3 + 1 + + + CC2IE + Capture/Compare 2 interrupt + enable + 2 + 1 + + + CC1IE + Capture/Compare 1 interrupt + enable + 1 + 1 + + + UIE + Update interrupt enable + 0 + 1 + + + + + SR + SR + status register + 0x10 + 0x20 + read-write + 0x0000 + + + CC4OF + Capture/Compare 4 overcapture + flag + 12 + 1 + + + CC3OF + Capture/Compare 3 overcapture + flag + 11 + 1 + + + CC2OF + Capture/compare 2 overcapture + flag + 10 + 1 + + + CC1OF + Capture/Compare 1 overcapture + flag + 9 + 1 + + + TIF + Trigger interrupt flag + 6 + 1 + + + CC4IF + Capture/Compare 4 interrupt + flag + 4 + 1 + + + CC3IF + Capture/Compare 3 interrupt + flag + 3 + 1 + + + CC2IF + Capture/Compare 2 interrupt + flag + 2 + 1 + + + CC1IF + Capture/compare 1 interrupt + flag + 1 + 1 + + + UIF + Update interrupt flag + 0 + 1 + + + + + EGR + EGR + event generation register + 0x14 + 0x20 + write-only + 0x0000 + + + TG + Trigger generation + 6 + 1 + + + CC4G + Capture/compare 4 + generation + 4 + 1 + + + CC3G + Capture/compare 3 + generation + 3 + 1 + + + CC2G + Capture/compare 2 + generation + 2 + 1 + + + CC1G + Capture/compare 1 + generation + 1 + 1 + + + UG + Update generation + 0 + 1 + + + + + CCMR1_Output + CCMR1_Output + capture/compare mode register 1 (output + mode) + 0x18 + 0x20 + read-write + 0x00000000 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + OC1FE + Output compare 1 fast + enable + 2 + 1 + + + OC1PE + Output compare 1 preload + enable + 3 + 1 + + + OC1M + Output compare 1 mode + 4 + 3 + + + OC1CE + Output compare 1 clear + enable + 7 + 1 + + + CC2S + Capture/Compare 2 + selection + 8 + 2 + + + OC2FE + Output compare 2 fast + enable + 10 + 1 + + + OC2PE + Output compare 2 preload + enable + 11 + 1 + + + OC2M + Output compare 2 mode + 12 + 3 + + + OC2CE + Output compare 2 clear + enable + 15 + 1 + + + OC1M_3 + Output compare 1 mode bit + 3 + 16 + 1 + + + OC2M_3 + Output compare 2 mode bit + 3 + 24 + 1 + + + + + CCMR1_Input + CCMR1_Input + capture/compare mode register 1 (input + mode) + CCMR1_Output + 0x18 + 0x20 + read-write + 0x00000000 + + + IC2F + Input capture 2 filter + 12 + 4 + + + IC2PSC + Input capture 2 prescaler + 10 + 2 + + + CC2S + Capture/compare 2 + selection + 8 + 2 + + + IC1F + Input capture 1 filter + 4 + 4 + + + IC1PSC + Input capture 1 prescaler + 2 + 2 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + + + CCMR2_Output + CCMR2_Output + capture/compare mode register 2 (output + mode) + 0x1C + 0x20 + read-write + 0x00000000 + + + CC3S + Capture/Compare 3 + selection + 0 + 2 + + + OC3FE + Output compare 3 fast + enable + 2 + 1 + + + OC3PE + Output compare 3 preload + enable + 3 + 1 + + + OC3M + Output compare 3 mode + 4 + 3 + + + OC3CE + Output compare 3 clear + enable + 7 + 1 + + + CC4S + Capture/Compare 4 + selection + 8 + 2 + + + OC4FE + Output compare 4 fast + enable + 10 + 1 + + + OC4PE + Output compare 4 preload + enable + 11 + 1 + + + OC4M + Output compare 4 mode + 12 + 3 + + + O24CE + Output compare 4 clear + enable + 15 + 1 + + + OC3M_3 + Output compare 3 mode bit3 + 16 + 1 + + + OC4M_3 + Output compare 4 mode bit3 + 24 + 1 + + + + + CCMR2_Input + CCMR2_Input + capture/compare mode register 2 (input + mode) + CCMR2_Output + 0x1C + 0x20 + read-write + 0x00000000 + + + IC4F + Input capture 4 filter + 12 + 4 + + + IC4PSC + Input capture 4 prescaler + 10 + 2 + + + CC4S + Capture/Compare 4 + selection + 8 + 2 + + + IC3F + Input capture 3 filter + 4 + 4 + + + IC3PSC + Input capture 3 prescaler + 2 + 2 + + + CC3S + Capture/Compare 3 + selection + 0 + 2 + + + + + CCER + CCER + capture/compare enable + register + 0x20 + 0x20 + read-write + 0x0000 + + + CC1E + Capture/Compare 1 output + enable + 0 + 1 + + + CC1P + Capture/Compare 1 output + Polarity + 1 + 1 + + + CC1NP + Capture/Compare 1 output + Polarity + 3 + 1 + + + CC2E + Capture/Compare 2 output + enable + 4 + 1 + + + CC2P + Capture/Compare 2 output + Polarity + 5 + 1 + + + CC2NP + Capture/Compare 2 output + Polarity + 7 + 1 + + + CC3E + Capture/Compare 3 output + enable + 8 + 1 + + + CC3P + Capture/Compare 3 output + Polarity + 9 + 1 + + + CC3NP + Capture/Compare 3 output + Polarity + 11 + 1 + + + CC4E + Capture/Compare 4 output + enable + 12 + 1 + + + CC4P + Capture/Compare 3 output + Polarity + 13 + 1 + + + CC4NP + Capture/Compare 3 output + Polarity + 15 + 1 + + + + + CNT + CNT + counter + 0x24 + 0x20 + read-write + 0x00000000 + + + CNTL + Low counter value + 0 + 16 + + + CNTH + High counter value + 16 + 15 + + + CNT_or_UIFCPY + if IUFREMAP=0 than CNT with read write + access else UIFCPY with read only + access + 31 + 1 + + + + + PSC + PSC + prescaler + 0x28 + 0x20 + read-write + 0x0000 + + + PSC + Prescaler value + 0 + 16 + + + + + ARR + ARR + auto-reload register + 0x2C + 0x20 + read-write + 0x00000000 + + + ARRL + Low Auto-reload value + 0 + 16 + + + ARRH + High Auto-reload value + 16 + 16 + + + + + CCR1 + CCR1 + capture/compare register 1 + 0x34 + 0x20 + read-write + 0x00000000 + + + CCR1L + Low Capture/Compare 1 + value + 0 + 16 + + + CCR1H + High Capture/Compare 1 value (on + TIM2) + 16 + 16 + + + + + CCR2 + CCR2 + capture/compare register 2 + 0x38 + 0x20 + read-write + 0x00000000 + + + CCR2L + Low Capture/Compare 2 + value + 0 + 16 + + + CCR2H + High Capture/Compare 2 value (on + TIM2) + 16 + 16 + + + + + CCR3 + CCR3 + capture/compare register 3 + 0x3C + 0x20 + read-write + 0x00000000 + + + CCR3L + Low Capture/Compare value + 0 + 16 + + + CCR3H + High Capture/Compare value (on + TIM2) + 16 + 16 + + + + + CCR4 + CCR4 + capture/compare register 4 + 0x40 + 0x20 + read-write + 0x00000000 + + + CCR4L + Low Capture/Compare value + 0 + 16 + + + CCR4H + High Capture/Compare value (on + TIM2) + 16 + 16 + + + + + DCR + DCR + DMA control register + 0x48 + 0x20 + read-write + 0x0000 + + + DBL + DMA burst length + 8 + 5 + + + DBA + DMA base address + 0 + 5 + + + + + DMAR + DMAR + DMA address for full transfer + 0x4C + 0x20 + read-write + 0x0000 + + + DMAB + DMA register for burst + accesses + 0 + 16 + + + + + + + TIM3 + 0x40000400 + + TIM3 + TIM3 global interrupt + 29 + + + + TIM4 + 0x40000800 + + TIM4 + TIM4 global interrupt + 30 + + + + TIM15 + General purpose timers + TIMs + 0x40014000 + + 0x0 + 0x400 + registers + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + CEN + Counter enable + 0 + 1 + + + UDIS + Update disable + 1 + 1 + + + URS + Update request source + 2 + 1 + + + OPM + One-pulse mode + 3 + 1 + + + ARPE + Auto-reload preload enable + 7 + 1 + + + CKD + Clock division + 8 + 2 + + + UIFREMAP + UIF status bit remapping + 11 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + CCPC + Capture/compare preloaded + control + 0 + 1 + + + CCUS + Capture/compare control update + selection + 2 + 1 + + + CCDS + Capture/compare DMA + selection + 3 + 1 + + + MMS + Master mode selection + 4 + 3 + + + TI1S + TI1 selection + 7 + 1 + + + OIS1 + Output Idle state 1 + 8 + 1 + + + OIS1N + Output Idle state 1 + 9 + 1 + + + OIS2 + Output Idle state 2 + 10 + 1 + + + + + SMCR + SMCR + slave mode control register + 0x8 + 0x20 + read-write + 0x0000 + + + SMS + Slave mode selection + 0 + 3 + + + TS + Trigger selection + 4 + 3 + + + MSM + Master/Slave mode + 7 + 1 + + + SMS_3 + Slave mode selection bit 3 + 16 + 1 + + + + + DIER + DIER + DMA/Interrupt enable register + 0xC + 0x20 + read-write + 0x0000 + + + UIE + Update interrupt enable + 0 + 1 + + + CC1IE + Capture/Compare 1 interrupt + enable + 1 + 1 + + + CC2IE + Capture/Compare 2 interrupt + enable + 2 + 1 + + + COMIE + COM interrupt enable + 5 + 1 + + + TIE + Trigger interrupt enable + 6 + 1 + + + BIE + Break interrupt enable + 7 + 1 + + + UDE + Update DMA request enable + 8 + 1 + + + CC1DE + Capture/Compare 1 DMA request + enable + 9 + 1 + + + CC2DE + Capture/Compare 2 DMA request + enable + 10 + 1 + + + COMDE + COM DMA request enable + 13 + 1 + + + TDE + Trigger DMA request enable + 14 + 1 + + + + + SR + SR + status register + 0x10 + 0x20 + read-write + 0x0000 + + + CC2OF + Capture/compare 2 overcapture + flag + 10 + 1 + + + CC1OF + Capture/Compare 1 overcapture + flag + 9 + 1 + + + BIF + Break interrupt flag + 7 + 1 + + + TIF + Trigger interrupt flag + 6 + 1 + + + COMIF + COM interrupt flag + 5 + 1 + + + CC2IF + Capture/Compare 2 interrupt + flag + 2 + 1 + + + CC1IF + Capture/compare 1 interrupt + flag + 1 + 1 + + + UIF + Update interrupt flag + 0 + 1 + + + + + EGR + EGR + event generation register + 0x14 + 0x20 + write-only + 0x0000 + + + BG + Break generation + 7 + 1 + + + TG + Trigger generation + 6 + 1 + + + COMG + Capture/Compare control update + generation + 5 + 1 + + + CC2G + Capture/compare 2 + generation + 2 + 1 + + + CC1G + Capture/compare 1 + generation + 1 + 1 + + + UG + Update generation + 0 + 1 + + + + + CCMR1_Output + CCMR1_Output + capture/compare mode register (output + mode) + 0x18 + 0x20 + read-write + 0x00000000 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + OC1FE + Output Compare 1 fast + enable + 2 + 1 + + + OC1PE + Output Compare 1 preload + enable + 3 + 1 + + + OC1M + Output Compare 1 mode + 4 + 3 + + + CC2S + Capture/Compare 2 + selection + 8 + 2 + + + OC2FE + Output Compare 2 fast + enable + 10 + 1 + + + OC2PE + Output Compare 2 preload + enable + 11 + 1 + + + OC2M + Output Compare 2 mode + 12 + 3 + + + OC1M_3 + Output Compare 1 mode bit + 3 + 16 + 1 + + + OC2M_3 + Output Compare 2 mode bit + 3 + 24 + 1 + + + + + CCMR1_Input + CCMR1_Input + capture/compare mode register 1 (input + mode) + CCMR1_Output + 0x18 + 0x20 + read-write + 0x00000000 + + + IC2F + Input capture 2 filter + 12 + 4 + + + IC2PSC + Input capture 2 prescaler + 10 + 2 + + + CC2S + Capture/Compare 2 + selection + 8 + 2 + + + IC1F + Input capture 1 filter + 4 + 4 + + + IC1PSC + Input capture 1 prescaler + 2 + 2 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + + + CCER + CCER + capture/compare enable + register + 0x20 + 0x20 + read-write + 0x0000 + + + CC2NP + Capture/Compare 2 output + Polarity + 7 + 1 + + + CC2P + Capture/Compare 2 output + Polarity + 5 + 1 + + + CC2E + Capture/Compare 2 output + enable + 4 + 1 + + + CC1NP + Capture/Compare 1 output + Polarity + 3 + 1 + + + CC1NE + Capture/Compare 1 complementary output + enable + 2 + 1 + + + CC1P + Capture/Compare 1 output + Polarity + 1 + 1 + + + CC1E + Capture/Compare 1 output + enable + 0 + 1 + + + + + CNT + CNT + counter + 0x24 + 0x20 + 0x00000000 + + + CNT + counter value + 0 + 16 + read-write + + + UIFCPY + UIF copy + 31 + 1 + read-only + + + + + PSC + PSC + prescaler + 0x28 + 0x20 + read-write + 0x0000 + + + PSC + Prescaler value + 0 + 16 + + + + + ARR + ARR + auto-reload register + 0x2C + 0x20 + read-write + 0x00000000 + + + ARR + Auto-reload value + 0 + 16 + + + + + RCR + RCR + repetition counter register + 0x30 + 0x20 + read-write + 0x0000 + + + REP + Repetition counter value + 0 + 8 + + + + + CCR1 + CCR1 + capture/compare register 1 + 0x34 + 0x20 + read-write + 0x00000000 + + + CCR1 + Capture/Compare 1 value + 0 + 16 + + + + + CCR2 + CCR2 + capture/compare register 2 + 0x38 + 0x20 + read-write + 0x00000000 + + + CCR2 + Capture/Compare 2 value + 0 + 16 + + + + + BDTR + BDTR + break and dead-time register + 0x44 + 0x20 + read-write + 0x0000 + + + MOE + Main output enable + 15 + 1 + + + AOE + Automatic output enable + 14 + 1 + + + BKP + Break polarity + 13 + 1 + + + BKE + Break enable + 12 + 1 + + + OSSR + Off-state selection for Run + mode + 11 + 1 + + + OSSI + Off-state selection for Idle + mode + 10 + 1 + + + LOCK + Lock configuration + 8 + 2 + + + DTG + Dead-time generator setup + 0 + 8 + + + BKF + Break filter + 16 + 4 + + + + + DCR + DCR + DMA control register + 0x48 + 0x20 + read-write + 0x0000 + + + DBL + DMA burst length + 8 + 5 + + + DBA + DMA base address + 0 + 5 + + + + + DMAR + DMAR + DMA address for full transfer + 0x4C + 0x20 + read-write + 0x0000 + + + DMAB + DMA register for burst + accesses + 0 + 16 + + + + + + + TIM16 + General-purpose-timers + TIMs + 0x40014400 + + 0x0 + 0x400 + registers + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + CEN + Counter enable + 0 + 1 + + + UDIS + Update disable + 1 + 1 + + + URS + Update request source + 2 + 1 + + + OPM + One-pulse mode + 3 + 1 + + + ARPE + Auto-reload preload enable + 7 + 1 + + + CKD + Clock division + 8 + 2 + + + UIFREMAP + UIF status bit remapping + 11 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + OIS1N + Output Idle state 1 + 9 + 1 + + + OIS1 + Output Idle state 1 + 8 + 1 + + + CCDS + Capture/compare DMA + selection + 3 + 1 + + + CCUS + Capture/compare control update + selection + 2 + 1 + + + CCPC + Capture/compare preloaded + control + 0 + 1 + + + + + DIER + DIER + DMA/Interrupt enable register + 0xC + 0x20 + read-write + 0x0000 + + + UIE + Update interrupt enable + 0 + 1 + + + CC1IE + Capture/Compare 1 interrupt + enable + 1 + 1 + + + COMIE + COM interrupt enable + 5 + 1 + + + TIE + Trigger interrupt enable + 6 + 1 + + + BIE + Break interrupt enable + 7 + 1 + + + UDE + Update DMA request enable + 8 + 1 + + + CC1DE + Capture/Compare 1 DMA request + enable + 9 + 1 + + + COMDE + COM DMA request enable + 13 + 1 + + + TDE + Trigger DMA request enable + 14 + 1 + + + + + SR + SR + status register + 0x10 + 0x20 + read-write + 0x0000 + + + CC1OF + Capture/Compare 1 overcapture + flag + 9 + 1 + + + BIF + Break interrupt flag + 7 + 1 + + + TIF + Trigger interrupt flag + 6 + 1 + + + COMIF + COM interrupt flag + 5 + 1 + + + CC1IF + Capture/compare 1 interrupt + flag + 1 + 1 + + + UIF + Update interrupt flag + 0 + 1 + + + + + EGR + EGR + event generation register + 0x14 + 0x20 + write-only + 0x0000 + + + BG + Break generation + 7 + 1 + + + TG + Trigger generation + 6 + 1 + + + COMG + Capture/Compare control update + generation + 5 + 1 + + + CC1G + Capture/compare 1 + generation + 1 + 1 + + + UG + Update generation + 0 + 1 + + + + + CCMR1_Output + CCMR1_Output + capture/compare mode register (output + mode) + 0x18 + 0x20 + read-write + 0x00000000 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + OC1FE + Output Compare 1 fast + enable + 2 + 1 + + + OC1PE + Output Compare 1 preload + enable + 3 + 1 + + + OC1M + Output Compare 1 mode + 4 + 3 + + + OC1M_3 + Output Compare 1 mode + 16 + 1 + + + + + CCMR1_Input + CCMR1_Input + capture/compare mode register 1 (input + mode) + CCMR1_Output + 0x18 + 0x20 + read-write + 0x00000000 + + + IC1F + Input capture 1 filter + 4 + 4 + + + IC1PSC + Input capture 1 prescaler + 2 + 2 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + + + CCER + CCER + capture/compare enable + register + 0x20 + 0x20 + read-write + 0x0000 + + + CC1NP + Capture/Compare 1 output + Polarity + 3 + 1 + + + CC1NE + Capture/Compare 1 complementary output + enable + 2 + 1 + + + CC1P + Capture/Compare 1 output + Polarity + 1 + 1 + + + CC1E + Capture/Compare 1 output + enable + 0 + 1 + + + + + CNT + CNT + counter + 0x24 + 0x20 + 0x00000000 + + + CNT + counter value + 0 + 16 + read-write + + + UIFCPY + UIF Copy + 31 + 1 + read-only + + + + + PSC + PSC + prescaler + 0x28 + 0x20 + read-write + 0x0000 + + + PSC + Prescaler value + 0 + 16 + + + + + ARR + ARR + auto-reload register + 0x2C + 0x20 + read-write + 0x00000000 + + + ARR + Auto-reload value + 0 + 16 + + + + + RCR + RCR + repetition counter register + 0x30 + 0x20 + read-write + 0x0000 + + + REP + Repetition counter value + 0 + 8 + + + + + CCR1 + CCR1 + capture/compare register 1 + 0x34 + 0x20 + read-write + 0x00000000 + + + CCR1 + Capture/Compare 1 value + 0 + 16 + + + + + BDTR + BDTR + break and dead-time register + 0x44 + 0x20 + read-write + 0x0000 + + + DTG + Dead-time generator setup + 0 + 8 + + + LOCK + Lock configuration + 8 + 2 + + + OSSI + Off-state selection for Idle + mode + 10 + 1 + + + OSSR + Off-state selection for Run + mode + 11 + 1 + + + BKE + Break enable + 12 + 1 + + + BKP + Break polarity + 13 + 1 + + + AOE + Automatic output enable + 14 + 1 + + + MOE + Main output enable + 15 + 1 + + + BKF + Break filter + 16 + 4 + + + + + DCR + DCR + DMA control register + 0x48 + 0x20 + read-write + 0x0000 + + + DBL + DMA burst length + 8 + 5 + + + DBA + DMA base address + 0 + 5 + + + + + DMAR + DMAR + DMA address for full transfer + 0x4C + 0x20 + read-write + 0x0000 + + + DMAB + DMA register for burst + accesses + 0 + 16 + + + + + OR + OR + option register + 0x50 + 0x20 + read-write + 0x0000 + + + + + TIM17 + General purpose timer + TIMs + 0x40014800 + + 0x0 + 0x400 + registers + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + CEN + Counter enable + 0 + 1 + + + UDIS + Update disable + 1 + 1 + + + URS + Update request source + 2 + 1 + + + OPM + One-pulse mode + 3 + 1 + + + ARPE + Auto-reload preload enable + 7 + 1 + + + CKD + Clock division + 8 + 2 + + + UIFREMAP + UIF status bit remapping + 11 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + OIS1N + Output Idle state 1 + 9 + 1 + + + OIS1 + Output Idle state 1 + 8 + 1 + + + CCDS + Capture/compare DMA + selection + 3 + 1 + + + CCUS + Capture/compare control update + selection + 2 + 1 + + + CCPC + Capture/compare preloaded + control + 0 + 1 + + + + + DIER + DIER + DMA/Interrupt enable register + 0xC + 0x20 + read-write + 0x0000 + + + UIE + Update interrupt enable + 0 + 1 + + + CC1IE + Capture/Compare 1 interrupt + enable + 1 + 1 + + + COMIE + COM interrupt enable + 5 + 1 + + + TIE + Trigger interrupt enable + 6 + 1 + + + BIE + Break interrupt enable + 7 + 1 + + + UDE + Update DMA request enable + 8 + 1 + + + CC1DE + Capture/Compare 1 DMA request + enable + 9 + 1 + + + COMDE + COM DMA request enable + 13 + 1 + + + TDE + Trigger DMA request enable + 14 + 1 + + + + + SR + SR + status register + 0x10 + 0x20 + read-write + 0x0000 + + + CC1OF + Capture/Compare 1 overcapture + flag + 9 + 1 + + + BIF + Break interrupt flag + 7 + 1 + + + TIF + Trigger interrupt flag + 6 + 1 + + + COMIF + COM interrupt flag + 5 + 1 + + + CC1IF + Capture/compare 1 interrupt + flag + 1 + 1 + + + UIF + Update interrupt flag + 0 + 1 + + + + + EGR + EGR + event generation register + 0x14 + 0x20 + write-only + 0x0000 + + + BG + Break generation + 7 + 1 + + + TG + Trigger generation + 6 + 1 + + + COMG + Capture/Compare control update + generation + 5 + 1 + + + CC1G + Capture/compare 1 + generation + 1 + 1 + + + UG + Update generation + 0 + 1 + + + + + CCMR1_Output + CCMR1_Output + capture/compare mode register (output + mode) + 0x18 + 0x20 + read-write + 0x00000000 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + OC1FE + Output Compare 1 fast + enable + 2 + 1 + + + OC1PE + Output Compare 1 preload + enable + 3 + 1 + + + OC1M + Output Compare 1 mode + 4 + 3 + + + OC1M_3 + Output Compare 1 mode + 16 + 1 + + + + + CCMR1_Input + CCMR1_Input + capture/compare mode register 1 (input + mode) + CCMR1_Output + 0x18 + 0x20 + read-write + 0x00000000 + + + IC1F + Input capture 1 filter + 4 + 4 + + + IC1PSC + Input capture 1 prescaler + 2 + 2 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + + + CCER + CCER + capture/compare enable + register + 0x20 + 0x20 + read-write + 0x0000 + + + CC1NP + Capture/Compare 1 output + Polarity + 3 + 1 + + + CC1NE + Capture/Compare 1 complementary output + enable + 2 + 1 + + + CC1P + Capture/Compare 1 output + Polarity + 1 + 1 + + + CC1E + Capture/Compare 1 output + enable + 0 + 1 + + + + + CNT + CNT + counter + 0x24 + 0x20 + 0x00000000 + + + CNT + counter value + 0 + 16 + read-write + + + UIFCPY + UIF Copy + 31 + 1 + read-only + + + + + PSC + PSC + prescaler + 0x28 + 0x20 + read-write + 0x0000 + + + PSC + Prescaler value + 0 + 16 + + + + + ARR + ARR + auto-reload register + 0x2C + 0x20 + read-write + 0x00000000 + + + ARR + Auto-reload value + 0 + 16 + + + + + RCR + RCR + repetition counter register + 0x30 + 0x20 + read-write + 0x0000 + + + REP + Repetition counter value + 0 + 8 + + + + + CCR1 + CCR1 + capture/compare register 1 + 0x34 + 0x20 + read-write + 0x00000000 + + + CCR1 + Capture/Compare 1 value + 0 + 16 + + + + + BDTR + BDTR + break and dead-time register + 0x44 + 0x20 + read-write + 0x0000 + + + DTG + Dead-time generator setup + 0 + 8 + + + LOCK + Lock configuration + 8 + 2 + + + OSSI + Off-state selection for Idle + mode + 10 + 1 + + + OSSR + Off-state selection for Run + mode + 11 + 1 + + + BKE + Break enable + 12 + 1 + + + BKP + Break polarity + 13 + 1 + + + AOE + Automatic output enable + 14 + 1 + + + MOE + Main output enable + 15 + 1 + + + BKF + Break filter + 16 + 4 + + + + + DCR + DCR + DMA control register + 0x48 + 0x20 + read-write + 0x0000 + + + DBL + DMA burst length + 8 + 5 + + + DBA + DMA base address + 0 + 5 + + + + + DMAR + DMAR + DMA address for full transfer + 0x4C + 0x20 + read-write + 0x0000 + + + DMAB + DMA register for burst + accesses + 0 + 16 + + + + + + + USART1 + Universal synchronous asynchronous receiver + transmitter + USART + 0x40013800 + + 0x0 + 0x400 + registers + + + USART1_EXTI25 + USART1 global interrupt and EXTI Line 25 + interrupt + 37 + + + + CR1 + CR1 + Control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + EOBIE + End of Block interrupt + enable + 27 + 1 + + + RTOIE + Receiver timeout interrupt + enable + 26 + 1 + + + DEAT + Driver Enable assertion + time + 21 + 5 + + + DEDT + Driver Enable deassertion + time + 16 + 5 + + + OVER8 + Oversampling mode + 15 + 1 + + + CMIE + Character match interrupt + enable + 14 + 1 + + + MME + Mute mode enable + 13 + 1 + + + M + Word length + 12 + 1 + + + WAKE + Receiver wakeup method + 11 + 1 + + + PCE + Parity control enable + 10 + 1 + + + PS + Parity selection + 9 + 1 + + + PEIE + PE interrupt enable + 8 + 1 + + + TXEIE + interrupt enable + 7 + 1 + + + TCIE + Transmission complete interrupt + enable + 6 + 1 + + + RXNEIE + RXNE interrupt enable + 5 + 1 + + + IDLEIE + IDLE interrupt enable + 4 + 1 + + + TE + Transmitter enable + 3 + 1 + + + RE + Receiver enable + 2 + 1 + + + UESM + USART enable in Stop mode + 1 + 1 + + + UE + USART enable + 0 + 1 + + + + + CR2 + CR2 + Control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + ADD4 + Address of the USART node + 28 + 4 + + + ADD0 + Address of the USART node + 24 + 4 + + + RTOEN + Receiver timeout enable + 23 + 1 + + + ABRMOD + Auto baud rate mode + 21 + 2 + + + ABREN + Auto baud rate enable + 20 + 1 + + + MSBFIRST + Most significant bit first + 19 + 1 + + + DATAINV + Binary data inversion + 18 + 1 + + + TXINV + TX pin active level + inversion + 17 + 1 + + + RXINV + RX pin active level + inversion + 16 + 1 + + + SWAP + Swap TX/RX pins + 15 + 1 + + + LINEN + LIN mode enable + 14 + 1 + + + STOP + STOP bits + 12 + 2 + + + CLKEN + Clock enable + 11 + 1 + + + CPOL + Clock polarity + 10 + 1 + + + CPHA + Clock phase + 9 + 1 + + + LBCL + Last bit clock pulse + 8 + 1 + + + LBDIE + LIN break detection interrupt + enable + 6 + 1 + + + LBDL + LIN break detection length + 5 + 1 + + + ADDM7 + 7-bit Address Detection/4-bit Address + Detection + 4 + 1 + + + + + CR3 + CR3 + Control register 3 + 0x8 + 0x20 + read-write + 0x0000 + + + WUFIE + Wakeup from Stop mode interrupt + enable + 22 + 1 + + + WUS + Wakeup from Stop mode interrupt flag + selection + 20 + 2 + + + SCARCNT + Smartcard auto-retry count + 17 + 3 + + + DEP + Driver enable polarity + selection + 15 + 1 + + + DEM + Driver enable mode + 14 + 1 + + + DDRE + DMA Disable on Reception + Error + 13 + 1 + + + OVRDIS + Overrun Disable + 12 + 1 + + + ONEBIT + One sample bit method + enable + 11 + 1 + + + CTSIE + CTS interrupt enable + 10 + 1 + + + CTSE + CTS enable + 9 + 1 + + + RTSE + RTS enable + 8 + 1 + + + DMAT + DMA enable transmitter + 7 + 1 + + + DMAR + DMA enable receiver + 6 + 1 + + + SCEN + Smartcard mode enable + 5 + 1 + + + NACK + Smartcard NACK enable + 4 + 1 + + + HDSEL + Half-duplex selection + 3 + 1 + + + IRLP + IrDA low-power + 2 + 1 + + + IREN + IrDA mode enable + 1 + 1 + + + EIE + Error interrupt enable + 0 + 1 + + + + + BRR + BRR + Baud rate register + 0xC + 0x20 + read-write + 0x0000 + + + DIV_Mantissa + mantissa of USARTDIV + 4 + 12 + + + DIV_Fraction + fraction of USARTDIV + 0 + 4 + + + + + GTPR + GTPR + Guard time and prescaler + register + 0x10 + 0x20 + read-write + 0x0000 + + + GT + Guard time value + 8 + 8 + + + PSC + Prescaler value + 0 + 8 + + + + + RTOR + RTOR + Receiver timeout register + 0x14 + 0x20 + read-write + 0x0000 + + + BLEN + Block Length + 24 + 8 + + + RTO + Receiver timeout value + 0 + 24 + + + + + RQR + RQR + Request register + 0x18 + 0x20 + read-write + 0x0000 + + + TXFRQ + Transmit data flush + request + 4 + 1 + + + RXFRQ + Receive data flush request + 3 + 1 + + + MMRQ + Mute mode request + 2 + 1 + + + SBKRQ + Send break request + 1 + 1 + + + ABRRQ + Auto baud rate request + 0 + 1 + + + + + ISR + ISR + Interrupt & status + register + 0x1C + 0x20 + read-only + 0x00C0 + + + REACK + Receive enable acknowledge + flag + 22 + 1 + + + TEACK + Transmit enable acknowledge + flag + 21 + 1 + + + WUF + Wakeup from Stop mode flag + 20 + 1 + + + RWU + Receiver wakeup from Mute + mode + 19 + 1 + + + SBKF + Send break flag + 18 + 1 + + + CMF + character match flag + 17 + 1 + + + BUSY + Busy flag + 16 + 1 + + + ABRF + Auto baud rate flag + 15 + 1 + + + ABRE + Auto baud rate error + 14 + 1 + + + EOBF + End of block flag + 12 + 1 + + + RTOF + Receiver timeout + 11 + 1 + + + CTS + CTS flag + 10 + 1 + + + CTSIF + CTS interrupt flag + 9 + 1 + + + LBDF + LIN break detection flag + 8 + 1 + + + TXE + Transmit data register + empty + 7 + 1 + + + TC + Transmission complete + 6 + 1 + + + RXNE + Read data register not + empty + 5 + 1 + + + IDLE + Idle line detected + 4 + 1 + + + ORE + Overrun error + 3 + 1 + + + NF + Noise detected flag + 2 + 1 + + + FE + Framing error + 1 + 1 + + + PE + Parity error + 0 + 1 + + + + + ICR + ICR + Interrupt flag clear register + 0x20 + 0x20 + read-write + 0x0000 + + + WUCF + Wakeup from Stop mode clear + flag + 20 + 1 + + + CMCF + Character match clear flag + 17 + 1 + + + EOBCF + End of timeout clear flag + 12 + 1 + + + RTOCF + Receiver timeout clear + flag + 11 + 1 + + + CTSCF + CTS clear flag + 9 + 1 + + + LBDCF + LIN break detection clear + flag + 8 + 1 + + + TCCF + Transmission complete clear + flag + 6 + 1 + + + IDLECF + Idle line detected clear + flag + 4 + 1 + + + ORECF + Overrun error clear flag + 3 + 1 + + + NCF + Noise detected clear flag + 2 + 1 + + + FECF + Framing error clear flag + 1 + 1 + + + PECF + Parity error clear flag + 0 + 1 + + + + + RDR + RDR + Receive data register + 0x24 + 0x20 + read-only + 0x0000 + + + RDR + Receive data value + 0 + 9 + + + + + TDR + TDR + Transmit data register + 0x28 + 0x20 + read-write + 0x0000 + + + TDR + Transmit data value + 0 + 9 + + + + + + + USART2 + 0x40004400 + + USART2_EXTI26 + USART2 global interrupt and EXTI Line 26 + interrupt + 38 + + + + USART3 + 0x40004800 + + USART3_EXTI28 + USART3 global interrupt and EXTI Line 28 + interrupt + 39 + + + + UART4 + 0x40004C00 + + UART4_EXTI34 + UART4 global and EXTI Line 34 + interrupts + 52 + + + + UART5 + 0x40005000 + + UART5_EXTI35 + UART5 global and EXTI Line 35 + interrupts + 53 + + + + SPI1 + Serial peripheral interface/Inter-IC + sound + SPI + 0x40013000 + + 0x0 + 0x400 + registers + + + SPI1 + SPI1 global interrupt + 35 + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x00000000 + + + BIDIMODE + Bidirectional data mode + enable + 15 + 1 + + + BIDIOE + Output enable in bidirectional + mode + 14 + 1 + + + CRCEN + Hardware CRC calculation + enable + 13 + 1 + + + CRCNEXT + CRC transfer next + 12 + 1 + + + CRCL + CRC length + 11 + 1 + + + RXONLY + Receive only + 10 + 1 + + + SSM + Software slave management + 9 + 1 + + + SSI + Internal slave select + 8 + 1 + + + LSBFIRST + Frame format + 7 + 1 + + + SPE + SPI enable + 6 + 1 + + + BR + Baud rate control + 3 + 3 + + + MSTR + Master selection + 2 + 1 + + + CPOL + Clock polarity + 1 + 1 + + + CPHA + Clock phase + 0 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x00000700 + + + RXDMAEN + Rx buffer DMA enable + 0 + 1 + + + TXDMAEN + Tx buffer DMA enable + 1 + 1 + + + SSOE + SS output enable + 2 + 1 + + + NSSP + NSS pulse management + 3 + 1 + + + FRF + Frame format + 4 + 1 + + + ERRIE + Error interrupt enable + 5 + 1 + + + RXNEIE + RX buffer not empty interrupt + enable + 6 + 1 + + + TXEIE + Tx buffer empty interrupt + enable + 7 + 1 + + + DS + Data size + 8 + 4 + + + FRXTH + FIFO reception threshold + 12 + 1 + + + LDMA_RX + Last DMA transfer for + reception + 13 + 1 + + + LDMA_TX + Last DMA transfer for + transmission + 14 + 1 + + + + + SR + SR + status register + 0x8 + 0x20 + 0x00000002 + + + RXNE + Receive buffer not empty + 0 + 1 + read-only + + + TXE + Transmit buffer empty + 1 + 1 + read-only + + + CHSIDE + Channel side + 2 + 1 + read-only + + + UDR + Underrun flag + 3 + 1 + read-only + + + CRCERR + CRC error flag + 4 + 1 + read-write + + + MODF + Mode fault + 5 + 1 + read-only + + + OVR + Overrun flag + 6 + 1 + read-only + + + BSY + Busy flag + 7 + 1 + read-only + + + TIFRFE + TI frame format error + 8 + 1 + read-only + + + FRLVL + FIFO reception level + 9 + 2 + read-only + + + FTLVL + FIFO transmission level + 11 + 2 + read-only + + + + + DR + DR + data register + 0xC + 0x20 + read-write + 0x00000000 + + + DR + Data register + 0 + 16 + + + + + CRCPR + CRCPR + CRC polynomial register + 0x10 + 0x20 + read-write + 0x00000007 + + + CRCPOLY + CRC polynomial register + 0 + 16 + + + + + RXCRCR + RXCRCR + RX CRC register + 0x14 + 0x20 + read-only + 0x00000000 + + + RxCRC + Rx CRC register + 0 + 16 + + + + + TXCRCR + TXCRCR + TX CRC register + 0x18 + 0x20 + read-only + 0x00000000 + + + TxCRC + Tx CRC register + 0 + 16 + + + + + I2SCFGR + I2SCFGR + I2S configuration register + 0x1C + 0x20 + read-write + 0x00000000 + + + I2SMOD + I2S mode selection + 11 + 1 + + + I2SE + I2S Enable + 10 + 1 + + + I2SCFG + I2S configuration mode + 8 + 2 + + + PCMSYNC + PCM frame synchronization + 7 + 1 + + + I2SSTD + I2S standard selection + 4 + 2 + + + CKPOL + Steady state clock + polarity + 3 + 1 + + + DATLEN + Data length to be + transferred + 1 + 2 + + + CHLEN + Channel length (number of bits per audio + channel) + 0 + 1 + + + + + I2SPR + I2SPR + I2S prescaler register + 0x20 + 0x20 + read-write + 0x00000010 + + + MCKOE + Master clock output enable + 9 + 1 + + + ODD + Odd factor for the + prescaler + 8 + 1 + + + I2SDIV + I2S Linear prescaler + 0 + 8 + + + + + + + SPI2 + 0x40003800 + + SPI2 + SPI2 global interrupt + 36 + + + + SPI3 + 0x40003C00 + + SPI3 + SPI3 global interrupt + 51 + + + + SPI4 + 0x40013C00 + + SPI4 + SPI4 Global interrupt + 84 + + + + I2S2ext + Serial peripheral interface/Inter-IC + sound + SPI + 0x40003400 + + 0x0 + 0x400 + registers + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x00000000 + + + BIDIMODE + Bidirectional data mode + enable + 15 + 1 + + + BIDIOE + Output enable in bidirectional + mode + 14 + 1 + + + CRCEN + Hardware CRC calculation + enable + 13 + 1 + + + CRCNEXT + CRC transfer next + 12 + 1 + + + CRCL + CRC length + 11 + 1 + + + RXONLY + Receive only + 10 + 1 + + + SSM + Software slave management + 9 + 1 + + + SSI + Internal slave select + 8 + 1 + + + LSBFIRST + Frame format + 7 + 1 + + + SPE + SPI enable + 6 + 1 + + + BR + Baud rate control + 3 + 3 + + + MSTR + Master selection + 2 + 1 + + + CPOL + Clock polarity + 1 + 1 + + + CPHA + Clock phase + 0 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x00000700 + + + RXDMAEN + Rx buffer DMA enable + 0 + 1 + + + TXDMAEN + Tx buffer DMA enable + 1 + 1 + + + SSOE + SS output enable + 2 + 1 + + + NSSP + NSS pulse management + 3 + 1 + + + FRF + Frame format + 4 + 1 + + + ERRIE + Error interrupt enable + 5 + 1 + + + RXNEIE + RX buffer not empty interrupt + enable + 6 + 1 + + + TXEIE + Tx buffer empty interrupt + enable + 7 + 1 + + + DS + Data size + 8 + 4 + + + FRXTH + FIFO reception threshold + 12 + 1 + + + LDMA_RX + Last DMA transfer for + reception + 13 + 1 + + + LDMA_TX + Last DMA transfer for + transmission + 14 + 1 + + + + + SR + SR + status register + 0x8 + 0x20 + 0x00000002 + + + RXNE + Receive buffer not empty + 0 + 1 + read-only + + + TXE + Transmit buffer empty + 1 + 1 + read-only + + + CHSIDE + Channel side + 2 + 1 + read-only + + + UDR + Underrun flag + 3 + 1 + read-only + + + CRCERR + CRC error flag + 4 + 1 + read-write + + + MODF + Mode fault + 5 + 1 + read-only + + + OVR + Overrun flag + 6 + 1 + read-only + + + BSY + Busy flag + 7 + 1 + read-only + + + TIFRFE + TI frame format error + 8 + 1 + read-only + + + FRLVL + FIFO reception level + 9 + 2 + read-only + + + FTLVL + FIFO transmission level + 11 + 2 + read-only + + + + + DR + DR + data register + 0xC + 0x20 + read-write + 0x00000000 + + + DR + Data register + 0 + 16 + + + + + CRCPR + CRCPR + CRC polynomial register + 0x10 + 0x20 + read-write + 0x00000007 + + + CRCPOLY + CRC polynomial register + 0 + 16 + + + + + RXCRCR + RXCRCR + RX CRC register + 0x14 + 0x20 + read-only + 0x00000000 + + + RxCRC + Rx CRC register + 0 + 16 + + + + + TXCRCR + TXCRCR + TX CRC register + 0x18 + 0x20 + read-only + 0x00000000 + + + TxCRC + Tx CRC register + 0 + 16 + + + + + I2SCFGR + I2SCFGR + I2S configuration register + 0x1C + 0x20 + read-write + 0x00000000 + + + I2SMOD + I2S mode selection + 11 + 1 + + + I2SE + I2S Enable + 10 + 1 + + + I2SCFG + I2S configuration mode + 8 + 2 + + + PCMSYNC + PCM frame synchronization + 7 + 1 + + + I2SSTD + I2S standard selection + 4 + 2 + + + CKPOL + Steady state clock + polarity + 3 + 1 + + + DATLEN + Data length to be + transferred + 1 + 2 + + + CHLEN + Channel length (number of bits per audio + channel) + 0 + 1 + + + + + I2SPR + I2SPR + I2S prescaler register + 0x20 + 0x20 + read-write + 0x00000002 + + + MCKOE + Master clock output enable + 9 + 1 + + + ODD + Odd factor for the + prescaler + 8 + 1 + + + I2SDIV + I2S Linear prescaler + 0 + 8 + + + + + + + I2S3ext + 0x40004000 + + + EXTI + External interrupt/event + controller + EXTI + 0x40010400 + + 0x0 + 0x400 + registers + + + TAMP_STAMP + Tamper and TimeStamp interrupts + 2 + + + EXTI0 + EXTI Line0 interrupt + 6 + + + EXTI1 + EXTI Line3 interrupt + 7 + + + EXTI2_TSC + EXTI Line2 and Touch sensing + interrupts + 8 + + + EXTI3 + EXTI Line3 interrupt + 9 + + + EXTI4 + EXTI Line4 interrupt + 10 + + + EXTI9_5 + EXTI Line5 to Line9 interrupts + 23 + + + EXTI15_10 + EXTI Line15 to Line10 interrupts + 40 + + + USB_WKUP_EXTI + USB wakeup from Suspend and EXTI Line + 18 + 76 + + + + IMR1 + IMR1 + Interrupt mask register + 0x0 + 0x20 + read-write + 0x1F800000 + + + MR0 + Interrupt Mask on line 0 + 0 + 1 + + + MR1 + Interrupt Mask on line 1 + 1 + 1 + + + MR2 + Interrupt Mask on line 2 + 2 + 1 + + + MR3 + Interrupt Mask on line 3 + 3 + 1 + + + MR4 + Interrupt Mask on line 4 + 4 + 1 + + + MR5 + Interrupt Mask on line 5 + 5 + 1 + + + MR6 + Interrupt Mask on line 6 + 6 + 1 + + + MR7 + Interrupt Mask on line 7 + 7 + 1 + + + MR8 + Interrupt Mask on line 8 + 8 + 1 + + + MR9 + Interrupt Mask on line 9 + 9 + 1 + + + MR10 + Interrupt Mask on line 10 + 10 + 1 + + + MR11 + Interrupt Mask on line 11 + 11 + 1 + + + MR12 + Interrupt Mask on line 12 + 12 + 1 + + + MR13 + Interrupt Mask on line 13 + 13 + 1 + + + MR14 + Interrupt Mask on line 14 + 14 + 1 + + + MR15 + Interrupt Mask on line 15 + 15 + 1 + + + MR16 + Interrupt Mask on line 16 + 16 + 1 + + + MR17 + Interrupt Mask on line 17 + 17 + 1 + + + MR18 + Interrupt Mask on line 18 + 18 + 1 + + + MR19 + Interrupt Mask on line 19 + 19 + 1 + + + MR20 + Interrupt Mask on line 20 + 20 + 1 + + + MR21 + Interrupt Mask on line 21 + 21 + 1 + + + MR22 + Interrupt Mask on line 22 + 22 + 1 + + + MR23 + Interrupt Mask on line 23 + 23 + 1 + + + MR24 + Interrupt Mask on line 24 + 24 + 1 + + + MR25 + Interrupt Mask on line 25 + 25 + 1 + + + MR26 + Interrupt Mask on line 26 + 26 + 1 + + + MR27 + Interrupt Mask on line 27 + 27 + 1 + + + MR28 + Interrupt Mask on line 28 + 28 + 1 + + + MR29 + Interrupt Mask on line 29 + 29 + 1 + + + MR30 + Interrupt Mask on line 30 + 30 + 1 + + + MR31 + Interrupt Mask on line 31 + 31 + 1 + + + + + EMR1 + EMR1 + Event mask register + 0x4 + 0x20 + read-write + 0x00000000 + + + MR0 + Event Mask on line 0 + 0 + 1 + + + MR1 + Event Mask on line 1 + 1 + 1 + + + MR2 + Event Mask on line 2 + 2 + 1 + + + MR3 + Event Mask on line 3 + 3 + 1 + + + MR4 + Event Mask on line 4 + 4 + 1 + + + MR5 + Event Mask on line 5 + 5 + 1 + + + MR6 + Event Mask on line 6 + 6 + 1 + + + MR7 + Event Mask on line 7 + 7 + 1 + + + MR8 + Event Mask on line 8 + 8 + 1 + + + MR9 + Event Mask on line 9 + 9 + 1 + + + MR10 + Event Mask on line 10 + 10 + 1 + + + MR11 + Event Mask on line 11 + 11 + 1 + + + MR12 + Event Mask on line 12 + 12 + 1 + + + MR13 + Event Mask on line 13 + 13 + 1 + + + MR14 + Event Mask on line 14 + 14 + 1 + + + MR15 + Event Mask on line 15 + 15 + 1 + + + MR16 + Event Mask on line 16 + 16 + 1 + + + MR17 + Event Mask on line 17 + 17 + 1 + + + MR18 + Event Mask on line 18 + 18 + 1 + + + MR19 + Event Mask on line 19 + 19 + 1 + + + MR20 + Event Mask on line 20 + 20 + 1 + + + MR21 + Event Mask on line 21 + 21 + 1 + + + MR22 + Event Mask on line 22 + 22 + 1 + + + MR23 + Event Mask on line 23 + 23 + 1 + + + MR24 + Event Mask on line 24 + 24 + 1 + + + MR25 + Event Mask on line 25 + 25 + 1 + + + MR26 + Event Mask on line 26 + 26 + 1 + + + MR27 + Event Mask on line 27 + 27 + 1 + + + MR28 + Event Mask on line 28 + 28 + 1 + + + MR29 + Event Mask on line 29 + 29 + 1 + + + MR30 + Event Mask on line 30 + 30 + 1 + + + MR31 + Event Mask on line 31 + 31 + 1 + + + + + RTSR1 + RTSR1 + Rising Trigger selection + register + 0x8 + 0x20 + read-write + 0x00000000 + + + TR0 + Rising trigger event configuration of + line 0 + 0 + 1 + + + TR1 + Rising trigger event configuration of + line 1 + 1 + 1 + + + TR2 + Rising trigger event configuration of + line 2 + 2 + 1 + + + TR3 + Rising trigger event configuration of + line 3 + 3 + 1 + + + TR4 + Rising trigger event configuration of + line 4 + 4 + 1 + + + TR5 + Rising trigger event configuration of + line 5 + 5 + 1 + + + TR6 + Rising trigger event configuration of + line 6 + 6 + 1 + + + TR7 + Rising trigger event configuration of + line 7 + 7 + 1 + + + TR8 + Rising trigger event configuration of + line 8 + 8 + 1 + + + TR9 + Rising trigger event configuration of + line 9 + 9 + 1 + + + TR10 + Rising trigger event configuration of + line 10 + 10 + 1 + + + TR11 + Rising trigger event configuration of + line 11 + 11 + 1 + + + TR12 + Rising trigger event configuration of + line 12 + 12 + 1 + + + TR13 + Rising trigger event configuration of + line 13 + 13 + 1 + + + TR14 + Rising trigger event configuration of + line 14 + 14 + 1 + + + TR15 + Rising trigger event configuration of + line 15 + 15 + 1 + + + TR16 + Rising trigger event configuration of + line 16 + 16 + 1 + + + TR17 + Rising trigger event configuration of + line 17 + 17 + 1 + + + TR18 + Rising trigger event configuration of + line 18 + 18 + 1 + + + TR19 + Rising trigger event configuration of + line 19 + 19 + 1 + + + TR20 + Rising trigger event configuration of + line 20 + 20 + 1 + + + TR21 + Rising trigger event configuration of + line 21 + 21 + 1 + + + TR22 + Rising trigger event configuration of + line 22 + 22 + 1 + + + TR29 + Rising trigger event configuration of + line 29 + 29 + 1 + + + TR30 + Rising trigger event configuration of + line 30 + 30 + 1 + + + TR31 + Rising trigger event configuration of + line 31 + 31 + 1 + + + + + FTSR1 + FTSR1 + Falling Trigger selection + register + 0xC + 0x20 + read-write + 0x00000000 + + + TR0 + Falling trigger event configuration of + line 0 + 0 + 1 + + + TR1 + Falling trigger event configuration of + line 1 + 1 + 1 + + + TR2 + Falling trigger event configuration of + line 2 + 2 + 1 + + + TR3 + Falling trigger event configuration of + line 3 + 3 + 1 + + + TR4 + Falling trigger event configuration of + line 4 + 4 + 1 + + + TR5 + Falling trigger event configuration of + line 5 + 5 + 1 + + + TR6 + Falling trigger event configuration of + line 6 + 6 + 1 + + + TR7 + Falling trigger event configuration of + line 7 + 7 + 1 + + + TR8 + Falling trigger event configuration of + line 8 + 8 + 1 + + + TR9 + Falling trigger event configuration of + line 9 + 9 + 1 + + + TR10 + Falling trigger event configuration of + line 10 + 10 + 1 + + + TR11 + Falling trigger event configuration of + line 11 + 11 + 1 + + + TR12 + Falling trigger event configuration of + line 12 + 12 + 1 + + + TR13 + Falling trigger event configuration of + line 13 + 13 + 1 + + + TR14 + Falling trigger event configuration of + line 14 + 14 + 1 + + + TR15 + Falling trigger event configuration of + line 15 + 15 + 1 + + + TR16 + Falling trigger event configuration of + line 16 + 16 + 1 + + + TR17 + Falling trigger event configuration of + line 17 + 17 + 1 + + + TR18 + Falling trigger event configuration of + line 18 + 18 + 1 + + + TR19 + Falling trigger event configuration of + line 19 + 19 + 1 + + + TR20 + Falling trigger event configuration of + line 20 + 20 + 1 + + + TR21 + Falling trigger event configuration of + line 21 + 21 + 1 + + + TR22 + Falling trigger event configuration of + line 22 + 22 + 1 + + + TR29 + Falling trigger event configuration of + line 29 + 29 + 1 + + + TR30 + Falling trigger event configuration of + line 30. + 30 + 1 + + + TR31 + Falling trigger event configuration of + line 31 + 31 + 1 + + + + + SWIER1 + SWIER1 + Software interrupt event + register + 0x10 + 0x20 + read-write + 0x00000000 + + + SWIER0 + Software Interrupt on line + 0 + 0 + 1 + + + SWIER1 + Software Interrupt on line + 1 + 1 + 1 + + + SWIER2 + Software Interrupt on line + 2 + 2 + 1 + + + SWIER3 + Software Interrupt on line + 3 + 3 + 1 + + + SWIER4 + Software Interrupt on line + 4 + 4 + 1 + + + SWIER5 + Software Interrupt on line + 5 + 5 + 1 + + + SWIER6 + Software Interrupt on line + 6 + 6 + 1 + + + SWIER7 + Software Interrupt on line + 7 + 7 + 1 + + + SWIER8 + Software Interrupt on line + 8 + 8 + 1 + + + SWIER9 + Software Interrupt on line + 9 + 9 + 1 + + + SWIER10 + Software Interrupt on line + 10 + 10 + 1 + + + SWIER11 + Software Interrupt on line + 11 + 11 + 1 + + + SWIER12 + Software Interrupt on line + 12 + 12 + 1 + + + SWIER13 + Software Interrupt on line + 13 + 13 + 1 + + + SWIER14 + Software Interrupt on line + 14 + 14 + 1 + + + SWIER15 + Software Interrupt on line + 15 + 15 + 1 + + + SWIER16 + Software Interrupt on line + 16 + 16 + 1 + + + SWIER17 + Software Interrupt on line + 17 + 17 + 1 + + + SWIER18 + Software Interrupt on line + 18 + 18 + 1 + + + SWIER19 + Software Interrupt on line + 19 + 19 + 1 + + + SWIER20 + Software Interrupt on line + 20 + 20 + 1 + + + SWIER21 + Software Interrupt on line + 21 + 21 + 1 + + + SWIER22 + Software Interrupt on line + 22 + 22 + 1 + + + SWIER29 + Software Interrupt on line + 29 + 29 + 1 + + + SWIER30 + Software Interrupt on line + 309 + 30 + 1 + + + SWIER31 + Software Interrupt on line + 319 + 31 + 1 + + + + + PR1 + PR1 + Pending register + 0x14 + 0x20 + read-write + 0x00000000 + + + PR0 + Pending bit 0 + 0 + 1 + + + PR1 + Pending bit 1 + 1 + 1 + + + PR2 + Pending bit 2 + 2 + 1 + + + PR3 + Pending bit 3 + 3 + 1 + + + PR4 + Pending bit 4 + 4 + 1 + + + PR5 + Pending bit 5 + 5 + 1 + + + PR6 + Pending bit 6 + 6 + 1 + + + PR7 + Pending bit 7 + 7 + 1 + + + PR8 + Pending bit 8 + 8 + 1 + + + PR9 + Pending bit 9 + 9 + 1 + + + PR10 + Pending bit 10 + 10 + 1 + + + PR11 + Pending bit 11 + 11 + 1 + + + PR12 + Pending bit 12 + 12 + 1 + + + PR13 + Pending bit 13 + 13 + 1 + + + PR14 + Pending bit 14 + 14 + 1 + + + PR15 + Pending bit 15 + 15 + 1 + + + PR16 + Pending bit 16 + 16 + 1 + + + PR17 + Pending bit 17 + 17 + 1 + + + PR18 + Pending bit 18 + 18 + 1 + + + PR19 + Pending bit 19 + 19 + 1 + + + PR20 + Pending bit 20 + 20 + 1 + + + PR21 + Pending bit 21 + 21 + 1 + + + PR22 + Pending bit 22 + 22 + 1 + + + PR29 + Pending bit 29 + 29 + 1 + + + PR30 + Pending bit 30 + 30 + 1 + + + PR31 + Pending bit 31 + 31 + 1 + + + + + IMR2 + IMR2 + Interrupt mask register + 0x18 + 0x20 + read-write + 0xFFFFFFFC + + + MR32 + Interrupt Mask on external/internal line + 32 + 0 + 1 + + + MR33 + Interrupt Mask on external/internal line + 33 + 1 + 1 + + + MR34 + Interrupt Mask on external/internal line + 34 + 2 + 1 + + + MR35 + Interrupt Mask on external/internal line + 35 + 3 + 1 + + + + + EMR2 + EMR2 + Event mask register + 0x1C + 0x20 + read-write + 0x00000000 + + + MR32 + Event mask on external/internal line + 32 + 0 + 1 + + + MR33 + Event mask on external/internal line + 33 + 1 + 1 + + + MR34 + Event mask on external/internal line + 34 + 2 + 1 + + + MR35 + Event mask on external/internal line + 35 + 3 + 1 + + + + + RTSR2 + RTSR2 + Rising Trigger selection + register + 0x20 + 0x20 + read-write + 0x00000000 + + + TR32 + Rising trigger event configuration bit + of line 32 + 0 + 1 + + + TR33 + Rising trigger event configuration bit + of line 33 + 1 + 1 + + + + + FTSR2 + FTSR2 + Falling Trigger selection + register + 0x24 + 0x20 + read-write + 0x00000000 + + + TR32 + Falling trigger event configuration bit + of line 32 + 0 + 1 + + + TR33 + Falling trigger event configuration bit + of line 33 + 1 + 1 + + + + + SWIER2 + SWIER2 + Software interrupt event + register + 0x28 + 0x20 + read-write + 0x00000000 + + + SWIER32 + Software interrupt on line + 32 + 0 + 1 + + + SWIER33 + Software interrupt on line + 33 + 1 + 1 + + + + + PR2 + PR2 + Pending register + 0x2C + 0x20 + read-write + 0x00000000 + + + PR32 + Pending bit on line 32 + 0 + 1 + + + PR33 + Pending bit on line 33 + 1 + 1 + + + + + + + PWR + Power control + PWR + 0x40007000 + + 0x0 + 0x400 + registers + + + PVD + PVD through EXTI line detection + interrupt + 1 + + + + CR + CR + power control register + 0x0 + 0x20 + read-write + 0x00000000 + + + LPDS + Low-power deep sleep + 0 + 1 + + + PDDS + Power down deepsleep + 1 + 1 + + + CWUF + Clear wakeup flag + 2 + 1 + + + CSBF + Clear standby flag + 3 + 1 + + + PVDE + Power voltage detector + enable + 4 + 1 + + + PLS + PVD level selection + 5 + 3 + + + DBP + Disable backup domain write + protection + 8 + 1 + + + + + CSR + CSR + power control/status register + 0x4 + 0x20 + 0x00000000 + + + WUF + Wakeup flag + 0 + 1 + read-only + + + SBF + Standby flag + 1 + 1 + read-only + + + PVDO + PVD output + 2 + 1 + read-only + + + EWUP1 + Enable WKUP1 pin + 8 + 1 + read-write + + + EWUP2 + Enable WKUP2 pin + 9 + 1 + read-write + + + + + + + CAN + Controller area network + CAN + 0x40006400 + + 0x0 + 0x400 + registers + + + USB_HP_CAN_TX + USB High Priority/CAN_TX + interrupts + 19 + + + USB_LP_CAN_RX0 + USB Low Priority/CAN_RX0 + interrupts + 20 + + + CAN_RX1 + CAN_RX1 interrupt + 21 + + + CAN_SCE + CAN_SCE interrupt + 22 + + + + MCR + MCR + master control register + 0x0 + 0x20 + read-write + 0x00010002 + + + DBF + DBF + 16 + 1 + + + RESET + RESET + 15 + 1 + + + TTCM + TTCM + 7 + 1 + + + ABOM + ABOM + 6 + 1 + + + AWUM + AWUM + 5 + 1 + + + NART + NART + 4 + 1 + + + RFLM + RFLM + 3 + 1 + + + TXFP + TXFP + 2 + 1 + + + SLEEP + SLEEP + 1 + 1 + + + INRQ + INRQ + 0 + 1 + + + + + MSR + MSR + master status register + 0x4 + 0x20 + 0x00000C02 + + + RX + RX + 11 + 1 + read-only + + + SAMP + SAMP + 10 + 1 + read-only + + + RXM + RXM + 9 + 1 + read-only + + + TXM + TXM + 8 + 1 + read-only + + + SLAKI + SLAKI + 4 + 1 + read-write + + + WKUI + WKUI + 3 + 1 + read-write + + + ERRI + ERRI + 2 + 1 + read-write + + + SLAK + SLAK + 1 + 1 + read-only + + + INAK + INAK + 0 + 1 + read-only + + + + + TSR + TSR + transmit status register + 0x8 + 0x20 + 0x1C000000 + + + LOW2 + Lowest priority flag for mailbox + 2 + 31 + 1 + read-only + + + LOW1 + Lowest priority flag for mailbox + 1 + 30 + 1 + read-only + + + LOW0 + Lowest priority flag for mailbox + 0 + 29 + 1 + read-only + + + TME2 + Lowest priority flag for mailbox + 2 + 28 + 1 + read-only + + + TME1 + Lowest priority flag for mailbox + 1 + 27 + 1 + read-only + + + TME0 + Lowest priority flag for mailbox + 0 + 26 + 1 + read-only + + + CODE + CODE + 24 + 2 + read-only + + + ABRQ2 + ABRQ2 + 23 + 1 + read-write + + + TERR2 + TERR2 + 19 + 1 + read-write + + + ALST2 + ALST2 + 18 + 1 + read-write + + + TXOK2 + TXOK2 + 17 + 1 + read-write + + + RQCP2 + RQCP2 + 16 + 1 + read-write + + + ABRQ1 + ABRQ1 + 15 + 1 + read-write + + + TERR1 + TERR1 + 11 + 1 + read-write + + + ALST1 + ALST1 + 10 + 1 + read-write + + + TXOK1 + TXOK1 + 9 + 1 + read-write + + + RQCP1 + RQCP1 + 8 + 1 + read-write + + + ABRQ0 + ABRQ0 + 7 + 1 + read-write + + + TERR0 + TERR0 + 3 + 1 + read-write + + + ALST0 + ALST0 + 2 + 1 + read-write + + + TXOK0 + TXOK0 + 1 + 1 + read-write + + + RQCP0 + RQCP0 + 0 + 1 + read-write + + + + + RF0R + RF0R + receive FIFO 0 register + 0xC + 0x20 + 0x00000000 + + + RFOM0 + RFOM0 + 5 + 1 + read-write + + + FOVR0 + FOVR0 + 4 + 1 + read-write + + + FULL0 + FULL0 + 3 + 1 + read-write + + + FMP0 + FMP0 + 0 + 2 + read-only + + + + + RF1R + RF1R + receive FIFO 1 register + 0x10 + 0x20 + 0x00000000 + + + RFOM1 + RFOM1 + 5 + 1 + read-write + + + FOVR1 + FOVR1 + 4 + 1 + read-write + + + FULL1 + FULL1 + 3 + 1 + read-write + + + FMP1 + FMP1 + 0 + 2 + read-only + + + + + IER + IER + interrupt enable register + 0x14 + 0x20 + read-write + 0x00000000 + + + SLKIE + SLKIE + 17 + 1 + + + WKUIE + WKUIE + 16 + 1 + + + ERRIE + ERRIE + 15 + 1 + + + LECIE + LECIE + 11 + 1 + + + BOFIE + BOFIE + 10 + 1 + + + EPVIE + EPVIE + 9 + 1 + + + EWGIE + EWGIE + 8 + 1 + + + FOVIE1 + FOVIE1 + 6 + 1 + + + FFIE1 + FFIE1 + 5 + 1 + + + FMPIE1 + FMPIE1 + 4 + 1 + + + FOVIE0 + FOVIE0 + 3 + 1 + + + FFIE0 + FFIE0 + 2 + 1 + + + FMPIE0 + FMPIE0 + 1 + 1 + + + TMEIE + TMEIE + 0 + 1 + + + + + ESR + ESR + error status register + 0x18 + 0x20 + 0x00000000 + + + REC + REC + 24 + 8 + read-only + + + TEC + TEC + 16 + 8 + read-only + + + LEC + LEC + 4 + 3 + read-write + + + BOFF + BOFF + 2 + 1 + read-only + + + EPVF + EPVF + 1 + 1 + read-only + + + EWGF + EWGF + 0 + 1 + read-only + + + + + BTR + BTR + bit timing register + 0x1C + 0x20 + read-write + 0x01230000 + + + SILM + SILM + 31 + 1 + + + LBKM + LBKM + 30 + 1 + + + SJW + SJW + 24 + 2 + + + TS2 + TS2 + 20 + 3 + + + TS1 + TS1 + 16 + 4 + + + BRP + BRP + 0 + 10 + + + + + TI0R + TI0R + TX mailbox identifier register + 0x180 + 0x20 + read-write + 0x00000000 + + + STID + STID + 21 + 11 + + + EXID + EXID + 3 + 18 + + + IDE + IDE + 2 + 1 + + + RTR + RTR + 1 + 1 + + + TXRQ + TXRQ + 0 + 1 + + + + + TDT0R + TDT0R + mailbox data length control and time stamp + register + 0x184 + 0x20 + read-write + 0x00000000 + + + TIME + TIME + 16 + 16 + + + TGT + TGT + 8 + 1 + + + DLC + DLC + 0 + 4 + + + + + TDL0R + TDL0R + mailbox data low register + 0x188 + 0x20 + read-write + 0x00000000 + + + DATA3 + DATA3 + 24 + 8 + + + DATA2 + DATA2 + 16 + 8 + + + DATA1 + DATA1 + 8 + 8 + + + DATA0 + DATA0 + 0 + 8 + + + + + TDH0R + TDH0R + mailbox data high register + 0x18C + 0x20 + read-write + 0x00000000 + + + DATA7 + DATA7 + 24 + 8 + + + DATA6 + DATA6 + 16 + 8 + + + DATA5 + DATA5 + 8 + 8 + + + DATA4 + DATA4 + 0 + 8 + + + + + TI1R + TI1R + TX mailbox identifier register + 0x190 + 0x20 + read-write + 0x00000000 + + + STID + STID + 21 + 11 + + + EXID + EXID + 3 + 18 + + + IDE + IDE + 2 + 1 + + + RTR + RTR + 1 + 1 + + + TXRQ + TXRQ + 0 + 1 + + + + + TDT1R + TDT1R + mailbox data length control and time stamp + register + 0x194 + 0x20 + read-write + 0x00000000 + + + TIME + TIME + 16 + 16 + + + TGT + TGT + 8 + 1 + + + DLC + DLC + 0 + 4 + + + + + TDL1R + TDL1R + mailbox data low register + 0x198 + 0x20 + read-write + 0x00000000 + + + DATA3 + DATA3 + 24 + 8 + + + DATA2 + DATA2 + 16 + 8 + + + DATA1 + DATA1 + 8 + 8 + + + DATA0 + DATA0 + 0 + 8 + + + + + TDH1R + TDH1R + mailbox data high register + 0x19C + 0x20 + read-write + 0x00000000 + + + DATA7 + DATA7 + 24 + 8 + + + DATA6 + DATA6 + 16 + 8 + + + DATA5 + DATA5 + 8 + 8 + + + DATA4 + DATA4 + 0 + 8 + + + + + TI2R + TI2R + TX mailbox identifier register + 0x1A0 + 0x20 + read-write + 0x00000000 + + + STID + STID + 21 + 11 + + + EXID + EXID + 3 + 18 + + + IDE + IDE + 2 + 1 + + + RTR + RTR + 1 + 1 + + + TXRQ + TXRQ + 0 + 1 + + + + + TDT2R + TDT2R + mailbox data length control and time stamp + register + 0x1A4 + 0x20 + read-write + 0x00000000 + + + TIME + TIME + 16 + 16 + + + TGT + TGT + 8 + 1 + + + DLC + DLC + 0 + 4 + + + + + TDL2R + TDL2R + mailbox data low register + 0x1A8 + 0x20 + read-write + 0x00000000 + + + DATA3 + DATA3 + 24 + 8 + + + DATA2 + DATA2 + 16 + 8 + + + DATA1 + DATA1 + 8 + 8 + + + DATA0 + DATA0 + 0 + 8 + + + + + TDH2R + TDH2R + mailbox data high register + 0x1AC + 0x20 + read-write + 0x00000000 + + + DATA7 + DATA7 + 24 + 8 + + + DATA6 + DATA6 + 16 + 8 + + + DATA5 + DATA5 + 8 + 8 + + + DATA4 + DATA4 + 0 + 8 + + + + + RI0R + RI0R + receive FIFO mailbox identifier + register + 0x1B0 + 0x20 + read-only + 0x00000000 + + + STID + STID + 21 + 11 + + + EXID + EXID + 3 + 18 + + + IDE + IDE + 2 + 1 + + + RTR + RTR + 1 + 1 + + + + + RDT0R + RDT0R + receive FIFO mailbox data length control and + time stamp register + 0x1B4 + 0x20 + read-only + 0x00000000 + + + TIME + TIME + 16 + 16 + + + FMI + FMI + 8 + 8 + + + DLC + DLC + 0 + 4 + + + + + RDL0R + RDL0R + receive FIFO mailbox data low + register + 0x1B8 + 0x20 + read-only + 0x00000000 + + + DATA3 + DATA3 + 24 + 8 + + + DATA2 + DATA2 + 16 + 8 + + + DATA1 + DATA1 + 8 + 8 + + + DATA0 + DATA0 + 0 + 8 + + + + + RDH0R + RDH0R + receive FIFO mailbox data high + register + 0x1BC + 0x20 + read-only + 0x00000000 + + + DATA7 + DATA7 + 24 + 8 + + + DATA6 + DATA6 + 16 + 8 + + + DATA5 + DATA5 + 8 + 8 + + + DATA4 + DATA4 + 0 + 8 + + + + + RI1R + RI1R + receive FIFO mailbox identifier + register + 0x1C0 + 0x20 + read-only + 0x00000000 + + + STID + STID + 21 + 11 + + + EXID + EXID + 3 + 18 + + + IDE + IDE + 2 + 1 + + + RTR + RTR + 1 + 1 + + + + + RDT1R + RDT1R + receive FIFO mailbox data length control and + time stamp register + 0x1C4 + 0x20 + read-only + 0x00000000 + + + TIME + TIME + 16 + 16 + + + FMI + FMI + 8 + 8 + + + DLC + DLC + 0 + 4 + + + + + RDL1R + RDL1R + receive FIFO mailbox data low + register + 0x1C8 + 0x20 + read-only + 0x00000000 + + + DATA3 + DATA3 + 24 + 8 + + + DATA2 + DATA2 + 16 + 8 + + + DATA1 + DATA1 + 8 + 8 + + + DATA0 + DATA0 + 0 + 8 + + + + + RDH1R + RDH1R + receive FIFO mailbox data high + register + 0x1CC + 0x20 + read-only + 0x00000000 + + + DATA7 + DATA7 + 24 + 8 + + + DATA6 + DATA6 + 16 + 8 + + + DATA5 + DATA5 + 8 + 8 + + + DATA4 + DATA4 + 0 + 8 + + + + + FMR + FMR + filter master register + 0x200 + 0x20 + read-write + 0x2A1C0E01 + + + CAN2SB + CAN2 start bank + 8 + 6 + + + FINIT + Filter init mode + 0 + 1 + + + + + FM1R + FM1R + filter mode register + 0x204 + 0x20 + read-write + 0x00000000 + + + FBM0 + Filter mode + 0 + 1 + + + FBM1 + Filter mode + 1 + 1 + + + FBM2 + Filter mode + 2 + 1 + + + FBM3 + Filter mode + 3 + 1 + + + FBM4 + Filter mode + 4 + 1 + + + FBM5 + Filter mode + 5 + 1 + + + FBM6 + Filter mode + 6 + 1 + + + FBM7 + Filter mode + 7 + 1 + + + FBM8 + Filter mode + 8 + 1 + + + FBM9 + Filter mode + 9 + 1 + + + FBM10 + Filter mode + 10 + 1 + + + FBM11 + Filter mode + 11 + 1 + + + FBM12 + Filter mode + 12 + 1 + + + FBM13 + Filter mode + 13 + 1 + + + FBM14 + Filter mode + 14 + 1 + + + FBM15 + Filter mode + 15 + 1 + + + FBM16 + Filter mode + 16 + 1 + + + FBM17 + Filter mode + 17 + 1 + + + FBM18 + Filter mode + 18 + 1 + + + FBM19 + Filter mode + 19 + 1 + + + FBM20 + Filter mode + 20 + 1 + + + FBM21 + Filter mode + 21 + 1 + + + FBM22 + Filter mode + 22 + 1 + + + FBM23 + Filter mode + 23 + 1 + + + FBM24 + Filter mode + 24 + 1 + + + FBM25 + Filter mode + 25 + 1 + + + FBM26 + Filter mode + 26 + 1 + + + FBM27 + Filter mode + 27 + 1 + + + + + FS1R + FS1R + filter scale register + 0x20C + 0x20 + read-write + 0x00000000 + + + FSC0 + Filter scale configuration + 0 + 1 + + + FSC1 + Filter scale configuration + 1 + 1 + + + FSC2 + Filter scale configuration + 2 + 1 + + + FSC3 + Filter scale configuration + 3 + 1 + + + FSC4 + Filter scale configuration + 4 + 1 + + + FSC5 + Filter scale configuration + 5 + 1 + + + FSC6 + Filter scale configuration + 6 + 1 + + + FSC7 + Filter scale configuration + 7 + 1 + + + FSC8 + Filter scale configuration + 8 + 1 + + + FSC9 + Filter scale configuration + 9 + 1 + + + FSC10 + Filter scale configuration + 10 + 1 + + + FSC11 + Filter scale configuration + 11 + 1 + + + FSC12 + Filter scale configuration + 12 + 1 + + + FSC13 + Filter scale configuration + 13 + 1 + + + FSC14 + Filter scale configuration + 14 + 1 + + + FSC15 + Filter scale configuration + 15 + 1 + + + FSC16 + Filter scale configuration + 16 + 1 + + + FSC17 + Filter scale configuration + 17 + 1 + + + FSC18 + Filter scale configuration + 18 + 1 + + + FSC19 + Filter scale configuration + 19 + 1 + + + FSC20 + Filter scale configuration + 20 + 1 + + + FSC21 + Filter scale configuration + 21 + 1 + + + FSC22 + Filter scale configuration + 22 + 1 + + + FSC23 + Filter scale configuration + 23 + 1 + + + FSC24 + Filter scale configuration + 24 + 1 + + + FSC25 + Filter scale configuration + 25 + 1 + + + FSC26 + Filter scale configuration + 26 + 1 + + + FSC27 + Filter scale configuration + 27 + 1 + + + + + FFA1R + FFA1R + filter FIFO assignment + register + 0x214 + 0x20 + read-write + 0x00000000 + + + FFA0 + Filter FIFO assignment for filter + 0 + 0 + 1 + + + FFA1 + Filter FIFO assignment for filter + 1 + 1 + 1 + + + FFA2 + Filter FIFO assignment for filter + 2 + 2 + 1 + + + FFA3 + Filter FIFO assignment for filter + 3 + 3 + 1 + + + FFA4 + Filter FIFO assignment for filter + 4 + 4 + 1 + + + FFA5 + Filter FIFO assignment for filter + 5 + 5 + 1 + + + FFA6 + Filter FIFO assignment for filter + 6 + 6 + 1 + + + FFA7 + Filter FIFO assignment for filter + 7 + 7 + 1 + + + FFA8 + Filter FIFO assignment for filter + 8 + 8 + 1 + + + FFA9 + Filter FIFO assignment for filter + 9 + 9 + 1 + + + FFA10 + Filter FIFO assignment for filter + 10 + 10 + 1 + + + FFA11 + Filter FIFO assignment for filter + 11 + 11 + 1 + + + FFA12 + Filter FIFO assignment for filter + 12 + 12 + 1 + + + FFA13 + Filter FIFO assignment for filter + 13 + 13 + 1 + + + FFA14 + Filter FIFO assignment for filter + 14 + 14 + 1 + + + FFA15 + Filter FIFO assignment for filter + 15 + 15 + 1 + + + FFA16 + Filter FIFO assignment for filter + 16 + 16 + 1 + + + FFA17 + Filter FIFO assignment for filter + 17 + 17 + 1 + + + FFA18 + Filter FIFO assignment for filter + 18 + 18 + 1 + + + FFA19 + Filter FIFO assignment for filter + 19 + 19 + 1 + + + FFA20 + Filter FIFO assignment for filter + 20 + 20 + 1 + + + FFA21 + Filter FIFO assignment for filter + 21 + 21 + 1 + + + FFA22 + Filter FIFO assignment for filter + 22 + 22 + 1 + + + FFA23 + Filter FIFO assignment for filter + 23 + 23 + 1 + + + FFA24 + Filter FIFO assignment for filter + 24 + 24 + 1 + + + FFA25 + Filter FIFO assignment for filter + 25 + 25 + 1 + + + FFA26 + Filter FIFO assignment for filter + 26 + 26 + 1 + + + FFA27 + Filter FIFO assignment for filter + 27 + 27 + 1 + + + + + FA1R + FA1R + CAN filter activation register + 0x21C + 0x20 + read-write + 0x00000000 + + + FACT0 + Filter active + 0 + 1 + + + FACT1 + Filter active + 1 + 1 + + + FACT2 + Filter active + 2 + 1 + + + FACT3 + Filter active + 3 + 1 + + + FACT4 + Filter active + 4 + 1 + + + FACT5 + Filter active + 5 + 1 + + + FACT6 + Filter active + 6 + 1 + + + FACT7 + Filter active + 7 + 1 + + + FACT8 + Filter active + 8 + 1 + + + FACT9 + Filter active + 9 + 1 + + + FACT10 + Filter active + 10 + 1 + + + FACT11 + Filter active + 11 + 1 + + + FACT12 + Filter active + 12 + 1 + + + FACT13 + Filter active + 13 + 1 + + + FACT14 + Filter active + 14 + 1 + + + FACT15 + Filter active + 15 + 1 + + + FACT16 + Filter active + 16 + 1 + + + FACT17 + Filter active + 17 + 1 + + + FACT18 + Filter active + 18 + 1 + + + FACT19 + Filter active + 19 + 1 + + + FACT20 + Filter active + 20 + 1 + + + FACT21 + Filter active + 21 + 1 + + + FACT22 + Filter active + 22 + 1 + + + FACT23 + Filter active + 23 + 1 + + + FACT24 + Filter active + 24 + 1 + + + FACT25 + Filter active + 25 + 1 + + + FACT26 + Filter active + 26 + 1 + + + FACT27 + Filter active + 27 + 1 + + + + + F0R1 + F0R1 + Filter bank 0 register 1 + 0x240 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F0R2 + F0R2 + Filter bank 0 register 2 + 0x244 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F1R1 + F1R1 + Filter bank 1 register 1 + 0x248 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F1R2 + F1R2 + Filter bank 1 register 2 + 0x24C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F2R1 + F2R1 + Filter bank 2 register 1 + 0x250 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F2R2 + F2R2 + Filter bank 2 register 2 + 0x254 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F3R1 + F3R1 + Filter bank 3 register 1 + 0x258 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F3R2 + F3R2 + Filter bank 3 register 2 + 0x25C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F4R1 + F4R1 + Filter bank 4 register 1 + 0x260 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F4R2 + F4R2 + Filter bank 4 register 2 + 0x264 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F5R1 + F5R1 + Filter bank 5 register 1 + 0x268 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F5R2 + F5R2 + Filter bank 5 register 2 + 0x26C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F6R1 + F6R1 + Filter bank 6 register 1 + 0x270 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F6R2 + F6R2 + Filter bank 6 register 2 + 0x274 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F7R1 + F7R1 + Filter bank 7 register 1 + 0x278 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F7R2 + F7R2 + Filter bank 7 register 2 + 0x27C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F8R1 + F8R1 + Filter bank 8 register 1 + 0x280 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F8R2 + F8R2 + Filter bank 8 register 2 + 0x284 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F9R1 + F9R1 + Filter bank 9 register 1 + 0x288 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F9R2 + F9R2 + Filter bank 9 register 2 + 0x28C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F10R1 + F10R1 + Filter bank 10 register 1 + 0x290 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F10R2 + F10R2 + Filter bank 10 register 2 + 0x294 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F11R1 + F11R1 + Filter bank 11 register 1 + 0x298 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F11R2 + F11R2 + Filter bank 11 register 2 + 0x29C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F12R1 + F12R1 + Filter bank 4 register 1 + 0x2A0 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F12R2 + F12R2 + Filter bank 12 register 2 + 0x2A4 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F13R1 + F13R1 + Filter bank 13 register 1 + 0x2A8 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F13R2 + F13R2 + Filter bank 13 register 2 + 0x2AC + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F14R1 + F14R1 + Filter bank 14 register 1 + 0x2B0 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F14R2 + F14R2 + Filter bank 14 register 2 + 0x2B4 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F15R1 + F15R1 + Filter bank 15 register 1 + 0x2B8 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F15R2 + F15R2 + Filter bank 15 register 2 + 0x2BC + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F16R1 + F16R1 + Filter bank 16 register 1 + 0x2C0 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F16R2 + F16R2 + Filter bank 16 register 2 + 0x2C4 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F17R1 + F17R1 + Filter bank 17 register 1 + 0x2C8 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F17R2 + F17R2 + Filter bank 17 register 2 + 0x2CC + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F18R1 + F18R1 + Filter bank 18 register 1 + 0x2D0 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F18R2 + F18R2 + Filter bank 18 register 2 + 0x2D4 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F19R1 + F19R1 + Filter bank 19 register 1 + 0x2D8 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F19R2 + F19R2 + Filter bank 19 register 2 + 0x2DC + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F20R1 + F20R1 + Filter bank 20 register 1 + 0x2E0 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F20R2 + F20R2 + Filter bank 20 register 2 + 0x2E4 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F21R1 + F21R1 + Filter bank 21 register 1 + 0x2E8 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F21R2 + F21R2 + Filter bank 21 register 2 + 0x2EC + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F22R1 + F22R1 + Filter bank 22 register 1 + 0x2F0 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F22R2 + F22R2 + Filter bank 22 register 2 + 0x2F4 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F23R1 + F23R1 + Filter bank 23 register 1 + 0x2F8 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F23R2 + F23R2 + Filter bank 23 register 2 + 0x2FC + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F24R1 + F24R1 + Filter bank 24 register 1 + 0x300 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F24R2 + F24R2 + Filter bank 24 register 2 + 0x304 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F25R1 + F25R1 + Filter bank 25 register 1 + 0x308 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F25R2 + F25R2 + Filter bank 25 register 2 + 0x30C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F26R1 + F26R1 + Filter bank 26 register 1 + 0x310 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F26R2 + F26R2 + Filter bank 26 register 2 + 0x314 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F27R1 + F27R1 + Filter bank 27 register 1 + 0x318 + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + F27R2 + F27R2 + Filter bank 27 register 2 + 0x31C + 0x20 + read-write + 0x00000000 + + + FB0 + Filter bits + 0 + 1 + + + FB1 + Filter bits + 1 + 1 + + + FB2 + Filter bits + 2 + 1 + + + FB3 + Filter bits + 3 + 1 + + + FB4 + Filter bits + 4 + 1 + + + FB5 + Filter bits + 5 + 1 + + + FB6 + Filter bits + 6 + 1 + + + FB7 + Filter bits + 7 + 1 + + + FB8 + Filter bits + 8 + 1 + + + FB9 + Filter bits + 9 + 1 + + + FB10 + Filter bits + 10 + 1 + + + FB11 + Filter bits + 11 + 1 + + + FB12 + Filter bits + 12 + 1 + + + FB13 + Filter bits + 13 + 1 + + + FB14 + Filter bits + 14 + 1 + + + FB15 + Filter bits + 15 + 1 + + + FB16 + Filter bits + 16 + 1 + + + FB17 + Filter bits + 17 + 1 + + + FB18 + Filter bits + 18 + 1 + + + FB19 + Filter bits + 19 + 1 + + + FB20 + Filter bits + 20 + 1 + + + FB21 + Filter bits + 21 + 1 + + + FB22 + Filter bits + 22 + 1 + + + FB23 + Filter bits + 23 + 1 + + + FB24 + Filter bits + 24 + 1 + + + FB25 + Filter bits + 25 + 1 + + + FB26 + Filter bits + 26 + 1 + + + FB27 + Filter bits + 27 + 1 + + + FB28 + Filter bits + 28 + 1 + + + FB29 + Filter bits + 29 + 1 + + + FB30 + Filter bits + 30 + 1 + + + FB31 + Filter bits + 31 + 1 + + + + + + + USB_FS + Universal serial bus full-speed device + interface + USB_FS + 0x40005C00 + + 0x0 + 0x400 + registers + + + USB_WKUP + USB wakeup from Suspend + 42 + + + USB_HP + USB High priority interrupt + 74 + + + USB_LP + USB Low priority interrupt + 75 + + + + USB_EP0R + USB_EP0R + endpoint 0 register + 0x0 + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_EP1R + USB_EP1R + endpoint 1 register + 0x4 + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_EP2R + USB_EP2R + endpoint 2 register + 0x8 + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_EP3R + USB_EP3R + endpoint 3 register + 0xC + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_EP4R + USB_EP4R + endpoint 4 register + 0x10 + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_EP5R + USB_EP5R + endpoint 5 register + 0x14 + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_EP6R + USB_EP6R + endpoint 6 register + 0x18 + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_EP7R + USB_EP7R + endpoint 7 register + 0x1C + 0x20 + 0x00000000 + + + EA + Endpoint address + 0 + 4 + read-write + + + STAT_TX + Status bits, for transmission + transfers + 4 + 2 + read-write + + + DTOG_TX + Data Toggle, for transmission + transfers + 6 + 1 + read-write + + + CTR_TX + Correct Transfer for + transmission + 7 + 1 + read-write + + + EP_KIND + Endpoint kind + 8 + 1 + read-write + + + EP_TYPE + Endpoint type + 9 + 2 + read-write + + + SETUP + Setup transaction + completed + 11 + 1 + read-only + + + STAT_RX + Status bits, for reception + transfers + 12 + 2 + read-write + + + DTOG_RX + Data Toggle, for reception + transfers + 14 + 1 + read-write + + + CTR_RX + Correct transfer for + reception + 15 + 1 + read-write + + + + + USB_CNTR + USB_CNTR + control register + 0x40 + 0x20 + read-write + 0x00000003 + + + FRES + Force USB Reset + 0 + 1 + + + PDWN + Power down + 1 + 1 + + + LPMODE + Low-power mode + 2 + 1 + + + FSUSP + Force suspend + 3 + 1 + + + RESUME + Resume request + 4 + 1 + + + ESOFM + Expected start of frame interrupt + mask + 8 + 1 + + + SOFM + Start of frame interrupt + mask + 9 + 1 + + + RESETM + USB reset interrupt mask + 10 + 1 + + + SUSPM + Suspend mode interrupt + mask + 11 + 1 + + + WKUPM + Wakeup interrupt mask + 12 + 1 + + + ERRM + Error interrupt mask + 13 + 1 + + + PMAOVRM + Packet memory area over / underrun + interrupt mask + 14 + 1 + + + CTRM + Correct transfer interrupt + mask + 15 + 1 + + + + + ISTR + ISTR + interrupt status register + 0x44 + 0x20 + 0x00000000 + + + EP_ID + Endpoint Identifier + 0 + 4 + read-only + + + DIR + Direction of transaction + 4 + 1 + read-only + + + ESOF + Expected start frame + 8 + 1 + read-write + + + SOF + start of frame + 9 + 1 + read-write + + + RESET + reset request + 10 + 1 + read-write + + + SUSP + Suspend mode request + 11 + 1 + read-write + + + WKUP + Wakeup + 12 + 1 + read-write + + + ERR + Error + 13 + 1 + read-write + + + PMAOVR + Packet memory area over / + underrun + 14 + 1 + read-write + + + CTR + Correct transfer + 15 + 1 + read-only + + + + + FNR + FNR + frame number register + 0x48 + 0x20 + read-only + 0x0000 + + + FN + Frame number + 0 + 11 + + + LSOF + Lost SOF + 11 + 2 + + + LCK + Locked + 13 + 1 + + + RXDM + Receive data - line status + 14 + 1 + + + RXDP + Receive data + line status + 15 + 1 + + + + + DADDR + DADDR + device address + 0x4C + 0x20 + read-write + 0x0000 + + + ADD + Device address + 0 + 1 + + + ADD1 + Device address + 1 + 1 + + + ADD2 + Device address + 2 + 1 + + + ADD3 + Device address + 3 + 1 + + + ADD4 + Device address + 4 + 1 + + + ADD5 + Device address + 5 + 1 + + + ADD6 + Device address + 6 + 1 + + + EF + Enable function + 7 + 1 + + + + + BTABLE + BTABLE + Buffer table address + 0x50 + 0x20 + read-write + 0x0000 + + + BTABLE + Buffer table + 3 + 13 + + + + + + + I2C1 + Inter-integrated circuit + I2C + 0x40005400 + + 0x0 + 0x400 + registers + + + I2C1_EV_EXTI23 + I2C1 event interrupt and EXTI Line23 + interrupt + 31 + + + I2C1_EV_EXTI23 + I2C1 event interrupt and EXTI Line23 + interrupt + 31 + + + I2C1_ER + I2C1 error interrupt + 32 + + + + CR1 + CR1 + Control register 1 + 0x0 + 0x20 + 0x00000000 + + + PE + Peripheral enable + 0 + 1 + read-write + + + TXIE + TX Interrupt enable + 1 + 1 + read-write + + + RXIE + RX Interrupt enable + 2 + 1 + read-write + + + ADDRIE + Address match interrupt enable (slave + only) + 3 + 1 + read-write + + + NACKIE + Not acknowledge received interrupt + enable + 4 + 1 + read-write + + + STOPIE + STOP detection Interrupt + enable + 5 + 1 + read-write + + + TCIE + Transfer Complete interrupt + enable + 6 + 1 + read-write + + + ERRIE + Error interrupts enable + 7 + 1 + read-write + + + DNF + Digital noise filter + 8 + 4 + read-write + + + ANFOFF + Analog noise filter OFF + 12 + 1 + read-write + + + SWRST + Software reset + 13 + 1 + write-only + + + TXDMAEN + DMA transmission requests + enable + 14 + 1 + read-write + + + RXDMAEN + DMA reception requests + enable + 15 + 1 + read-write + + + SBC + Slave byte control + 16 + 1 + read-write + + + NOSTRETCH + Clock stretching disable + 17 + 1 + read-write + + + WUPEN + Wakeup from STOP enable + 18 + 1 + read-write + + + GCEN + General call enable + 19 + 1 + read-write + + + SMBHEN + SMBus Host address enable + 20 + 1 + read-write + + + SMBDEN + SMBus Device Default address + enable + 21 + 1 + read-write + + + ALERTEN + SMBUS alert enable + 22 + 1 + read-write + + + PECEN + PEC enable + 23 + 1 + read-write + + + + + CR2 + CR2 + Control register 2 + 0x4 + 0x20 + read-write + 0x00000000 + + + PECBYTE + Packet error checking byte + 26 + 1 + + + AUTOEND + Automatic end mode (master + mode) + 25 + 1 + + + RELOAD + NBYTES reload mode + 24 + 1 + + + NBYTES + Number of bytes + 16 + 8 + + + NACK + NACK generation (slave + mode) + 15 + 1 + + + STOP + Stop generation (master + mode) + 14 + 1 + + + START + Start generation + 13 + 1 + + + HEAD10R + 10-bit address header only read + direction (master receiver mode) + 12 + 1 + + + ADD10 + 10-bit addressing mode (master + mode) + 11 + 1 + + + RD_WRN + Transfer direction (master + mode) + 10 + 1 + + + SADD8 + Slave address bit 9:8 (master + mode) + 8 + 2 + + + SADD1 + Slave address bit 7:1 (master + mode) + 1 + 7 + + + SADD0 + Slave address bit 0 (master + mode) + 0 + 1 + + + + + OAR1 + OAR1 + Own address register 1 + 0x8 + 0x20 + read-write + 0x00000000 + + + OA1_0 + Interface address + 0 + 1 + + + OA1_1 + Interface address + 1 + 7 + + + OA1_8 + Interface address + 8 + 2 + + + OA1MODE + Own Address 1 10-bit mode + 10 + 1 + + + OA1EN + Own Address 1 enable + 15 + 1 + + + + + OAR2 + OAR2 + Own address register 2 + 0xC + 0x20 + read-write + 0x00000000 + + + OA2 + Interface address + 1 + 7 + + + OA2MSK + Own Address 2 masks + 8 + 3 + + + OA2EN + Own Address 2 enable + 15 + 1 + + + + + TIMINGR + TIMINGR + Timing register + 0x10 + 0x20 + read-write + 0x00000000 + + + SCLL + SCL low period (master + mode) + 0 + 8 + + + SCLH + SCL high period (master + mode) + 8 + 8 + + + SDADEL + Data hold time + 16 + 4 + + + SCLDEL + Data setup time + 20 + 4 + + + PRESC + Timing prescaler + 28 + 4 + + + + + TIMEOUTR + TIMEOUTR + Status register 1 + 0x14 + 0x20 + read-write + 0x00000000 + + + TIMEOUTA + Bus timeout A + 0 + 12 + + + TIDLE + Idle clock timeout + detection + 12 + 1 + + + TIMOUTEN + Clock timeout enable + 15 + 1 + + + TIMEOUTB + Bus timeout B + 16 + 12 + + + TEXTEN + Extended clock timeout + enable + 31 + 1 + + + + + ISR + ISR + Interrupt and Status register + 0x18 + 0x20 + 0x00000001 + + + ADDCODE + Address match code (Slave + mode) + 17 + 7 + read-only + + + DIR + Transfer direction (Slave + mode) + 16 + 1 + read-only + + + BUSY + Bus busy + 15 + 1 + read-only + + + ALERT + SMBus alert + 13 + 1 + read-only + + + TIMEOUT + Timeout or t_low detection + flag + 12 + 1 + read-only + + + PECERR + PEC Error in reception + 11 + 1 + read-only + + + OVR + Overrun/Underrun (slave + mode) + 10 + 1 + read-only + + + ARLO + Arbitration lost + 9 + 1 + read-only + + + BERR + Bus error + 8 + 1 + read-only + + + TCR + Transfer Complete Reload + 7 + 1 + read-only + + + TC + Transfer Complete (master + mode) + 6 + 1 + read-only + + + STOPF + Stop detection flag + 5 + 1 + read-only + + + NACKF + Not acknowledge received + flag + 4 + 1 + read-only + + + ADDR + Address matched (slave + mode) + 3 + 1 + read-only + + + RXNE + Receive data register not empty + (receivers) + 2 + 1 + read-only + + + TXIS + Transmit interrupt status + (transmitters) + 1 + 1 + read-write + + + TXE + Transmit data register empty + (transmitters) + 0 + 1 + read-write + + + + + ICR + ICR + Interrupt clear register + 0x1C + 0x20 + write-only + 0x00000000 + + + ALERTCF + Alert flag clear + 13 + 1 + + + TIMOUTCF + Timeout detection flag + clear + 12 + 1 + + + PECCF + PEC Error flag clear + 11 + 1 + + + OVRCF + Overrun/Underrun flag + clear + 10 + 1 + + + ARLOCF + Arbitration lost flag + clear + 9 + 1 + + + BERRCF + Bus error flag clear + 8 + 1 + + + STOPCF + Stop detection flag clear + 5 + 1 + + + NACKCF + Not Acknowledge flag clear + 4 + 1 + + + ADDRCF + Address Matched flag clear + 3 + 1 + + + + + PECR + PECR + PEC register + 0x20 + 0x20 + read-only + 0x00000000 + + + PEC + Packet error checking + register + 0 + 8 + + + + + RXDR + RXDR + Receive data register + 0x24 + 0x20 + read-only + 0x00000000 + + + RXDATA + 8-bit receive data + 0 + 8 + + + + + TXDR + TXDR + Transmit data register + 0x28 + 0x20 + read-write + 0x00000000 + + + TXDATA + 8-bit transmit data + 0 + 8 + + + + + + + I2C2 + 0x40005800 + + I2C2_EV_EXTI24 + I2C2 event interrupt & EXTI Line24 + interrupt + 33 + + + I2C2_ER + I2C2 error interrupt + 34 + + + + I2C3 + 0x40007800 + + I2C3_EV + I2C3 Event interrupt + 72 + + + I2C3_ER + I2C3 Error interrupt + 73 + + + + IWDG + Independent watchdog + IWDG + 0x40003000 + + 0x0 + 0x400 + registers + + + + KR + KR + Key register + 0x0 + 0x20 + write-only + 0x00000000 + + + KEY + Key value + 0 + 16 + + + + + PR + PR + Prescaler register + 0x4 + 0x20 + read-write + 0x00000000 + + + PR + Prescaler divider + 0 + 3 + + + + + RLR + RLR + Reload register + 0x8 + 0x20 + read-write + 0x00000FFF + + + RL + Watchdog counter reload + value + 0 + 12 + + + + + SR + SR + Status register + 0xC + 0x20 + read-only + 0x00000000 + + + PVU + Watchdog prescaler value + update + 0 + 1 + + + RVU + Watchdog counter reload value + update + 1 + 1 + + + WVU + Watchdog counter window value + update + 2 + 1 + + + + + WINR + WINR + Window register + 0x10 + 0x20 + read-write + 0x00000FFF + + + WIN + Watchdog counter window + value + 0 + 12 + + + + + + + WWDG + Window watchdog + WWDG + 0x40002C00 + + 0x0 + 0x400 + registers + + + WWDG + Window Watchdog interrupt + 0 + + + + CR + CR + Control register + 0x0 + 0x20 + read-write + 0x0000007F + + + T + 7-bit counter + 0 + 7 + + + WDGA + Activation bit + 7 + 1 + + + + + CFR + CFR + Configuration register + 0x4 + 0x20 + read-write + 0x0000007F + + + EWI + Early wakeup interrupt + 9 + 1 + + + WDGTB + Timer base + 7 + 2 + + + W + 7-bit window value + 0 + 7 + + + + + SR + SR + Status register + 0x8 + 0x20 + read-write + 0x00000000 + + + EWIF + Early wakeup interrupt + flag + 0 + 1 + + + + + + + RTC + Real-time clock + RTC + 0x40002800 + + 0x0 + 0x400 + registers + + + RTC_WKUP + RTC Wakeup interrupt through the EXTI + line + 3 + + + RTCAlarm + RTC alarm interrupt + 41 + + + + TR + TR + time register + 0x0 + 0x20 + read-write + 0x00000000 + + + PM + AM/PM notation + 22 + 1 + + + HT + Hour tens in BCD format + 20 + 2 + + + HU + Hour units in BCD format + 16 + 4 + + + MNT + Minute tens in BCD format + 12 + 3 + + + MNU + Minute units in BCD format + 8 + 4 + + + ST + Second tens in BCD format + 4 + 3 + + + SU + Second units in BCD format + 0 + 4 + + + + + DR + DR + date register + 0x4 + 0x20 + read-write + 0x00002101 + + + YT + Year tens in BCD format + 20 + 4 + + + YU + Year units in BCD format + 16 + 4 + + + WDU + Week day units + 13 + 3 + + + MT + Month tens in BCD format + 12 + 1 + + + MU + Month units in BCD format + 8 + 4 + + + DT + Date tens in BCD format + 4 + 2 + + + DU + Date units in BCD format + 0 + 4 + + + + + CR + CR + control register + 0x8 + 0x20 + read-write + 0x00000000 + + + WCKSEL + Wakeup clock selection + 0 + 3 + + + TSEDGE + Time-stamp event active + edge + 3 + 1 + + + REFCKON + Reference clock detection enable (50 or + 60 Hz) + 4 + 1 + + + BYPSHAD + Bypass the shadow + registers + 5 + 1 + + + FMT + Hour format + 6 + 1 + + + ALRAE + Alarm A enable + 8 + 1 + + + ALRBE + Alarm B enable + 9 + 1 + + + WUTE + Wakeup timer enable + 10 + 1 + + + TSE + Time stamp enable + 11 + 1 + + + ALRAIE + Alarm A interrupt enable + 12 + 1 + + + ALRBIE + Alarm B interrupt enable + 13 + 1 + + + WUTIE + Wakeup timer interrupt + enable + 14 + 1 + + + TSIE + Time-stamp interrupt + enable + 15 + 1 + + + ADD1H + Add 1 hour (summer time + change) + 16 + 1 + + + SUB1H + Subtract 1 hour (winter time + change) + 17 + 1 + + + BKP + Backup + 18 + 1 + + + COSEL + Calibration output + selection + 19 + 1 + + + POL + Output polarity + 20 + 1 + + + OSEL + Output selection + 21 + 2 + + + COE + Calibration output enable + 23 + 1 + + + + + ISR + ISR + initialization and status + register + 0xC + 0x20 + 0x00000007 + + + ALRAWF + Alarm A write flag + 0 + 1 + read-only + + + ALRBWF + Alarm B write flag + 1 + 1 + read-only + + + WUTWF + Wakeup timer write flag + 2 + 1 + read-only + + + SHPF + Shift operation pending + 3 + 1 + read-write + + + INITS + Initialization status flag + 4 + 1 + read-only + + + RSF + Registers synchronization + flag + 5 + 1 + read-write + + + INITF + Initialization flag + 6 + 1 + read-only + + + INIT + Initialization mode + 7 + 1 + read-write + + + ALRAF + Alarm A flag + 8 + 1 + read-write + + + ALRBF + Alarm B flag + 9 + 1 + read-write + + + WUTF + Wakeup timer flag + 10 + 1 + read-write + + + TSF + Time-stamp flag + 11 + 1 + read-write + + + TSOVF + Time-stamp overflow flag + 12 + 1 + read-write + + + TAMP1F + Tamper detection flag + 13 + 1 + read-write + + + TAMP2F + RTC_TAMP2 detection flag + 14 + 1 + read-write + + + TAMP3F + RTC_TAMP3 detection flag + 15 + 1 + read-write + + + RECALPF + Recalibration pending Flag + 16 + 1 + read-only + + + + + PRER + PRER + prescaler register + 0x10 + 0x20 + read-write + 0x007F00FF + + + PREDIV_A + Asynchronous prescaler + factor + 16 + 7 + + + PREDIV_S + Synchronous prescaler + factor + 0 + 15 + + + + + WUTR + WUTR + wakeup timer register + 0x14 + 0x20 + read-write + 0x0000FFFF + + + WUT + Wakeup auto-reload value + bits + 0 + 16 + + + + + ALRMAR + ALRMAR + alarm A register + 0x1C + 0x20 + read-write + 0x00000000 + + + MSK4 + Alarm A date mask + 31 + 1 + + + WDSEL + Week day selection + 30 + 1 + + + DT + Date tens in BCD format + 28 + 2 + + + DU + Date units or day in BCD + format + 24 + 4 + + + MSK3 + Alarm A hours mask + 23 + 1 + + + PM + AM/PM notation + 22 + 1 + + + HT + Hour tens in BCD format + 20 + 2 + + + HU + Hour units in BCD format + 16 + 4 + + + MSK2 + Alarm A minutes mask + 15 + 1 + + + MNT + Minute tens in BCD format + 12 + 3 + + + MNU + Minute units in BCD format + 8 + 4 + + + MSK1 + Alarm A seconds mask + 7 + 1 + + + ST + Second tens in BCD format + 4 + 3 + + + SU + Second units in BCD format + 0 + 4 + + + + + ALRMBR + ALRMBR + alarm B register + 0x20 + 0x20 + read-write + 0x00000000 + + + MSK4 + Alarm B date mask + 31 + 1 + + + WDSEL + Week day selection + 30 + 1 + + + DT + Date tens in BCD format + 28 + 2 + + + DU + Date units or day in BCD + format + 24 + 4 + + + MSK3 + Alarm B hours mask + 23 + 1 + + + PM + AM/PM notation + 22 + 1 + + + HT + Hour tens in BCD format + 20 + 2 + + + HU + Hour units in BCD format + 16 + 4 + + + MSK2 + Alarm B minutes mask + 15 + 1 + + + MNT + Minute tens in BCD format + 12 + 3 + + + MNU + Minute units in BCD format + 8 + 4 + + + MSK1 + Alarm B seconds mask + 7 + 1 + + + ST + Second tens in BCD format + 4 + 3 + + + SU + Second units in BCD format + 0 + 4 + + + + + WPR + WPR + write protection register + 0x24 + 0x20 + write-only + 0x00000000 + + + KEY + Write protection key + 0 + 8 + + + + + SSR + SSR + sub second register + 0x28 + 0x20 + read-only + 0x00000000 + + + SS + Sub second value + 0 + 16 + + + + + SHIFTR + SHIFTR + shift control register + 0x2C + 0x20 + write-only + 0x00000000 + + + ADD1S + Add one second + 31 + 1 + + + SUBFS + Subtract a fraction of a + second + 0 + 15 + + + + + TSTR + TSTR + time stamp time register + 0x30 + 0x20 + read-only + 0x00000000 + + + SU + Second units in BCD format + 0 + 4 + + + ST + Second tens in BCD format + 4 + 3 + + + MNU + Minute units in BCD format + 8 + 4 + + + MNT + Minute tens in BCD format + 12 + 3 + + + HU + Hour units in BCD format + 16 + 4 + + + HT + Hour tens in BCD format + 20 + 2 + + + PM + AM/PM notation + 22 + 1 + + + + + TSDR + TSDR + time stamp date register + 0x34 + 0x20 + read-only + 0x00000000 + + + WDU + Week day units + 13 + 3 + + + MT + Month tens in BCD format + 12 + 1 + + + MU + Month units in BCD format + 8 + 4 + + + DT + Date tens in BCD format + 4 + 2 + + + DU + Date units in BCD format + 0 + 4 + + + + + TSSSR + TSSSR + timestamp sub second register + 0x38 + 0x20 + read-only + 0x00000000 + + + SS + Sub second value + 0 + 16 + + + + + CALR + CALR + calibration register + 0x3C + 0x20 + read-write + 0x00000000 + + + CALP + Increase frequency of RTC by 488.5 + ppm + 15 + 1 + + + CALW8 + Use an 8-second calibration cycle + period + 14 + 1 + + + CALW16 + Use a 16-second calibration cycle + period + 13 + 1 + + + CALM + Calibration minus + 0 + 9 + + + + + TAFCR + TAFCR + tamper and alternate function configuration + register + 0x40 + 0x20 + read-write + 0x00000000 + + + TAMP1E + Tamper 1 detection enable + 0 + 1 + + + TAMP1TRG + Active level for tamper 1 + 1 + 1 + + + TAMPIE + Tamper interrupt enable + 2 + 1 + + + TAMP2E + Tamper 2 detection enable + 3 + 1 + + + TAMP2TRG + Active level for tamper 2 + 4 + 1 + + + TAMP3E + Tamper 3 detection enable + 5 + 1 + + + TAMP3TRG + Active level for tamper 3 + 6 + 1 + + + TAMPTS + Activate timestamp on tamper detection + event + 7 + 1 + + + TAMPFREQ + Tamper sampling frequency + 8 + 3 + + + TAMPFLT + Tamper filter count + 11 + 2 + + + TAMPPRCH + Tamper precharge duration + 13 + 2 + + + TAMPPUDIS + TAMPER pull-up disable + 15 + 1 + + + PC13VALUE + PC13 value + 18 + 1 + + + PC13MODE + PC13 mode + 19 + 1 + + + PC14VALUE + PC14 value + 20 + 1 + + + PC14MODE + PC 14 mode + 21 + 1 + + + PC15VALUE + PC15 value + 22 + 1 + + + PC15MODE + PC15 mode + 23 + 1 + + + + + ALRMASSR + ALRMASSR + alarm A sub second register + 0x44 + 0x20 + read-write + 0x00000000 + + + MASKSS + Mask the most-significant bits starting + at this bit + 24 + 4 + + + SS + Sub seconds value + 0 + 15 + + + + + ALRMBSSR + ALRMBSSR + alarm B sub second register + 0x48 + 0x20 + read-write + 0x00000000 + + + MASKSS + Mask the most-significant bits starting + at this bit + 24 + 4 + + + SS + Sub seconds value + 0 + 15 + + + + + BKP0R + BKP0R + backup register + 0x50 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP1R + BKP1R + backup register + 0x54 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP2R + BKP2R + backup register + 0x58 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP3R + BKP3R + backup register + 0x5C + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP4R + BKP4R + backup register + 0x60 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP5R + BKP5R + backup register + 0x64 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP6R + BKP6R + backup register + 0x68 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP7R + BKP7R + backup register + 0x6C + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP8R + BKP8R + backup register + 0x70 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP9R + BKP9R + backup register + 0x74 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP10R + BKP10R + backup register + 0x78 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP11R + BKP11R + backup register + 0x7C + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP12R + BKP12R + backup register + 0x80 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP13R + BKP13R + backup register + 0x84 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP14R + BKP14R + backup register + 0x88 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP15R + BKP15R + backup register + 0x8C + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP16R + BKP16R + backup register + 0x90 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP17R + BKP17R + backup register + 0x94 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP18R + BKP18R + backup register + 0x98 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP19R + BKP19R + backup register + 0x9C + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP20R + BKP20R + backup register + 0xA0 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP21R + BKP21R + backup register + 0xA4 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP22R + BKP22R + backup register + 0xA8 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP23R + BKP23R + backup register + 0xAC + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP24R + BKP24R + backup register + 0xB0 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP25R + BKP25R + backup register + 0xB4 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP26R + BKP26R + backup register + 0xB8 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP27R + BKP27R + backup register + 0xBC + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP28R + BKP28R + backup register + 0xC0 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP29R + BKP29R + backup register + 0xC4 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP30R + BKP30R + backup register + 0xC8 + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + BKP31R + BKP31R + backup register + 0xCC + 0x20 + read-write + 0x00000000 + + + BKP + BKP + 0 + 32 + + + + + + + TIM6 + Basic timers + TIMs + 0x40001000 + + 0x0 + 0x400 + registers + + + TIM6_DACUNDER + TIM6 global and DAC12 underrun + interrupts + 54 + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + CEN + Counter enable + 0 + 1 + + + UDIS + Update disable + 1 + 1 + + + URS + Update request source + 2 + 1 + + + OPM + One-pulse mode + 3 + 1 + + + ARPE + Auto-reload preload enable + 7 + 1 + + + UIFREMAP + UIF status bit remapping + 11 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + MMS + Master mode selection + 4 + 3 + + + + + DIER + DIER + DMA/Interrupt enable register + 0xC + 0x20 + read-write + 0x0000 + + + UDE + Update DMA request enable + 8 + 1 + + + UIE + Update interrupt enable + 0 + 1 + + + + + SR + SR + status register + 0x10 + 0x20 + read-write + 0x0000 + + + UIF + Update interrupt flag + 0 + 1 + + + + + EGR + EGR + event generation register + 0x14 + 0x20 + write-only + 0x0000 + + + UG + Update generation + 0 + 1 + + + + + CNT + CNT + counter + 0x24 + 0x20 + 0x00000000 + + + CNT + Low counter value + 0 + 16 + read-write + + + UIFCPY + UIF Copy + 31 + 1 + read-only + + + + + PSC + PSC + prescaler + 0x28 + 0x20 + read-write + 0x0000 + + + PSC + Prescaler value + 0 + 16 + + + + + ARR + ARR + auto-reload register + 0x2C + 0x20 + read-write + 0x00000000 + + + ARR + Low Auto-reload value + 0 + 16 + + + + + + + TIM7 + 0x40001400 + + TIM7 + TIM7 global interrupt + 55 + + + + DAC + Digital-to-analog converter + DAC + 0x40007400 + + 0x0 + 0x400 + registers + + + + CR + CR + control register + 0x0 + 0x20 + read-write + 0x00000000 + + + DMAUDRIE2 + DAC channel2 DMA underrun interrupt + enable + 29 + 1 + + + DMAEN2 + DAC channel2 DMA enable + 28 + 1 + + + MAMP2 + DAC channel2 mask/amplitude + selector + 24 + 4 + + + WAVE2 + DAC channel2 noise/triangle wave + generation enable + 22 + 2 + + + TSEL2 + DAC channel2 trigger + selection + 19 + 3 + + + TEN2 + DAC channel2 trigger + enable + 18 + 1 + + + BOFF2 + DAC channel2 output buffer + disable + 17 + 1 + + + EN2 + DAC channel2 enable + 16 + 1 + + + DMAUDRIE1 + DAC channel1 DMA Underrun Interrupt + enable + 13 + 1 + + + DMAEN1 + DAC channel1 DMA enable + 12 + 1 + + + MAMP1 + DAC channel1 mask/amplitude + selector + 8 + 4 + + + WAVE1 + DAC channel1 noise/triangle wave + generation enable + 6 + 2 + + + TSEL1 + DAC channel1 trigger + selection + 3 + 3 + + + TEN1 + DAC channel1 trigger + enable + 2 + 1 + + + BOFF1 + DAC channel1 output buffer + disable + 1 + 1 + + + EN1 + DAC channel1 enable + 0 + 1 + + + + + SWTRIGR + SWTRIGR + software trigger register + 0x4 + 0x20 + write-only + 0x00000000 + + + SWTRIG2 + DAC channel2 software + trigger + 1 + 1 + + + SWTRIG1 + DAC channel1 software + trigger + 0 + 1 + + + + + DHR12R1 + DHR12R1 + channel1 12-bit right-aligned data holding + register + 0x8 + 0x20 + read-write + 0x00000000 + + + DACC1DHR + DAC channel1 12-bit right-aligned + data + 0 + 12 + + + + + DHR12L1 + DHR12L1 + channel1 12-bit left aligned data holding + register + 0xC + 0x20 + read-write + 0x00000000 + + + DACC1DHR + DAC channel1 12-bit left-aligned + data + 4 + 12 + + + + + DHR8R1 + DHR8R1 + channel1 8-bit right aligned data holding + register + 0x10 + 0x20 + read-write + 0x00000000 + + + DACC1DHR + DAC channel1 8-bit right-aligned + data + 0 + 8 + + + + + DHR12R2 + DHR12R2 + channel2 12-bit right aligned data holding + register + 0x14 + 0x20 + read-write + 0x00000000 + + + DACC2DHR + DAC channel2 12-bit right-aligned + data + 0 + 12 + + + + + DHR12L2 + DHR12L2 + channel2 12-bit left aligned data holding + register + 0x18 + 0x20 + read-write + 0x00000000 + + + DACC2DHR + DAC channel2 12-bit left-aligned + data + 4 + 12 + + + + + DHR8R2 + DHR8R2 + channel2 8-bit right-aligned data holding + register + 0x1C + 0x20 + read-write + 0x00000000 + + + DACC2DHR + DAC channel2 8-bit right-aligned + data + 0 + 8 + + + + + DHR12RD + DHR12RD + Dual DAC 12-bit right-aligned data holding + register + 0x20 + 0x20 + read-write + 0x00000000 + + + DACC2DHR + DAC channel2 12-bit right-aligned + data + 16 + 12 + + + DACC1DHR + DAC channel1 12-bit right-aligned + data + 0 + 12 + + + + + DHR12LD + DHR12LD + DUAL DAC 12-bit left aligned data holding + register + 0x24 + 0x20 + read-write + 0x00000000 + + + DACC2DHR + DAC channel2 12-bit left-aligned + data + 20 + 12 + + + DACC1DHR + DAC channel1 12-bit left-aligned + data + 4 + 12 + + + + + DHR8RD + DHR8RD + DUAL DAC 8-bit right aligned data holding + register + 0x28 + 0x20 + read-write + 0x00000000 + + + DACC2DHR + DAC channel2 8-bit right-aligned + data + 8 + 8 + + + DACC1DHR + DAC channel1 8-bit right-aligned + data + 0 + 8 + + + + + DOR1 + DOR1 + channel1 data output register + 0x2C + 0x20 + read-only + 0x00000000 + + + DACC1DOR + DAC channel1 data output + 0 + 12 + + + + + DOR2 + DOR2 + channel2 data output register + 0x30 + 0x20 + read-only + 0x00000000 + + + DACC2DOR + DAC channel2 data output + 0 + 12 + + + + + SR + SR + status register + 0x34 + 0x20 + read-write + 0x00000000 + + + DMAUDR2 + DAC channel2 DMA underrun + flag + 29 + 1 + + + DMAUDR1 + DAC channel1 DMA underrun + flag + 13 + 1 + + + + + + + DBGMCU + Debug support + DBGMCU + 0xE0042000 + + 0x0 + 0x400 + registers + + + + IDCODE + IDCODE + MCU Device ID Code Register + 0x0 + 0x20 + read-only + 0x0 + + + DEV_ID + Device Identifier + 0 + 12 + + + REV_ID + Revision Identifier + 16 + 16 + + + + + CR + CR + Debug MCU Configuration + Register + 0x4 + 0x20 + read-write + 0x0 + + + DBG_SLEEP + Debug Sleep mode + 0 + 1 + + + DBG_STOP + Debug Stop Mode + 1 + 1 + + + DBG_STANDBY + Debug Standby Mode + 2 + 1 + + + TRACE_IOEN + Trace pin assignment + control + 5 + 1 + + + TRACE_MODE + Trace pin assignment + control + 6 + 2 + + + + + APB1FZ + APB1FZ + APB Low Freeze Register + 0x8 + 0x20 + read-write + 0x0 + + + DBG_TIM2_STOP + Debug Timer 2 stopped when Core is + halted + 0 + 1 + + + DBG_TIM3_STOP + Debug Timer 3 stopped when Core is + halted + 1 + 1 + + + DBG_TIM4_STOP + Debug Timer 4 stopped when Core is + halted + 2 + 1 + + + DBG_TIM5_STOP + Debug Timer 5 stopped when Core is + halted + 3 + 1 + + + DBG_TIM6_STOP + Debug Timer 6 stopped when Core is + halted + 4 + 1 + + + DBG_TIM7_STOP + Debug Timer 7 stopped when Core is + halted + 5 + 1 + + + DBG_TIM12_STOP + Debug Timer 12 stopped when Core is + halted + 6 + 1 + + + DBG_TIM13_STOP + Debug Timer 13 stopped when Core is + halted + 7 + 1 + + + DBG_TIMER14_STOP + Debug Timer 14 stopped when Core is + halted + 8 + 1 + + + DBG_TIM18_STOP + Debug Timer 18 stopped when Core is + halted + 9 + 1 + + + DBG_RTC_STOP + Debug RTC stopped when Core is + halted + 10 + 1 + + + DBG_WWDG_STOP + Debug Window Wachdog stopped when Core + is halted + 11 + 1 + + + DBG_IWDG_STOP + Debug Independent Wachdog stopped when + Core is halted + 12 + 1 + + + I2C1_SMBUS_TIMEOUT + SMBUS timeout mode stopped when Core is + halted + 21 + 1 + + + I2C2_SMBUS_TIMEOUT + SMBUS timeout mode stopped when Core is + halted + 22 + 1 + + + DBG_CAN_STOP + Debug CAN stopped when core is + halted + 25 + 1 + + + + + APB2FZ + APB2FZ + APB High Freeze Register + 0xC + 0x20 + read-write + 0x0 + + + DBG_TIM15_STOP + Debug Timer 15 stopped when Core is + halted + 2 + 1 + + + DBG_TIM16_STOP + Debug Timer 16 stopped when Core is + halted + 3 + 1 + + + DBG_TIM17_STO + Debug Timer 17 stopped when Core is + halted + 4 + 1 + + + DBG_TIM19_STOP + Debug Timer 19 stopped when Core is + halted + 5 + 1 + + + + + + + TIM1 + Advanced timer + TIMs + 0x40012C00 + + 0x0 + 0x400 + registers + + + TIM1_BRK_TIM15 + TIM1 Break/TIM15 global + interruts + 24 + + + TIM1_UP_TIM16 + TIM1 Update/TIM16 global + interrupts + 25 + + + TIM1_TRG_COM_TIM17 + TIM1 trigger and commutation/TIM17 + interrupts + 26 + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + CEN + Counter enable + 0 + 1 + + + UDIS + Update disable + 1 + 1 + + + URS + Update request source + 2 + 1 + + + OPM + One-pulse mode + 3 + 1 + + + DIR + Direction + 4 + 1 + + + CMS + Center-aligned mode + selection + 5 + 2 + + + ARPE + Auto-reload preload enable + 7 + 1 + + + CKD + Clock division + 8 + 2 + + + UIFREMAP + UIF status bit remapping + 11 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + CCPC + Capture/compare preloaded + control + 0 + 1 + + + CCUS + Capture/compare control update + selection + 2 + 1 + + + CCDS + Capture/compare DMA + selection + 3 + 1 + + + MMS + Master mode selection + 4 + 3 + + + TI1S + TI1 selection + 7 + 1 + + + OIS1 + Output Idle state 1 + 8 + 1 + + + OIS1N + Output Idle state 1 + 9 + 1 + + + OIS2 + Output Idle state 2 + 10 + 1 + + + OIS2N + Output Idle state 2 + 11 + 1 + + + OIS3 + Output Idle state 3 + 12 + 1 + + + OIS3N + Output Idle state 3 + 13 + 1 + + + OIS4 + Output Idle state 4 + 14 + 1 + + + OIS5 + Output Idle state 5 + 16 + 1 + + + OIS6 + Output Idle state 6 + 18 + 1 + + + MMS2 + Master mode selection 2 + 20 + 4 + + + + + SMCR + SMCR + slave mode control register + 0x8 + 0x20 + read-write + 0x0000 + + + SMS + Slave mode selection + 0 + 3 + + + OCCS + OCREF clear selection + 3 + 1 + + + TS + Trigger selection + 4 + 3 + + + MSM + Master/Slave mode + 7 + 1 + + + ETF + External trigger filter + 8 + 4 + + + ETPS + External trigger prescaler + 12 + 2 + + + ECE + External clock enable + 14 + 1 + + + ETP + External trigger polarity + 15 + 1 + + + SMS3 + Slave mode selection bit 3 + 16 + 1 + + + + + DIER + DIER + DMA/Interrupt enable register + 0xC + 0x20 + read-write + 0x0000 + + + TDE + Trigger DMA request enable + 14 + 1 + + + COMDE + COM DMA request enable + 13 + 1 + + + CC4DE + Capture/Compare 4 DMA request + enable + 12 + 1 + + + CC3DE + Capture/Compare 3 DMA request + enable + 11 + 1 + + + CC2DE + Capture/Compare 2 DMA request + enable + 10 + 1 + + + CC1DE + Capture/Compare 1 DMA request + enable + 9 + 1 + + + UDE + Update DMA request enable + 8 + 1 + + + BIE + Break interrupt enable + 7 + 1 + + + TIE + Trigger interrupt enable + 6 + 1 + + + COMIE + COM interrupt enable + 5 + 1 + + + CC4IE + Capture/Compare 4 interrupt + enable + 4 + 1 + + + CC3IE + Capture/Compare 3 interrupt + enable + 3 + 1 + + + CC2IE + Capture/Compare 2 interrupt + enable + 2 + 1 + + + CC1IE + Capture/Compare 1 interrupt + enable + 1 + 1 + + + UIE + Update interrupt enable + 0 + 1 + + + + + SR + SR + status register + 0x10 + 0x20 + read-write + 0x0000 + + + UIF + Update interrupt flag + 0 + 1 + + + CC1IF + Capture/compare 1 interrupt + flag + 1 + 1 + + + CC2IF + Capture/Compare 2 interrupt + flag + 2 + 1 + + + CC3IF + Capture/Compare 3 interrupt + flag + 3 + 1 + + + CC4IF + Capture/Compare 4 interrupt + flag + 4 + 1 + + + COMIF + COM interrupt flag + 5 + 1 + + + TIF + Trigger interrupt flag + 6 + 1 + + + BIF + Break interrupt flag + 7 + 1 + + + B2IF + Break 2 interrupt flag + 8 + 1 + + + CC1OF + Capture/Compare 1 overcapture + flag + 9 + 1 + + + CC2OF + Capture/compare 2 overcapture + flag + 10 + 1 + + + CC3OF + Capture/Compare 3 overcapture + flag + 11 + 1 + + + CC4OF + Capture/Compare 4 overcapture + flag + 12 + 1 + + + C5IF + Capture/Compare 5 interrupt + flag + 16 + 1 + + + C6IF + Capture/Compare 6 interrupt + flag + 17 + 1 + + + + + EGR + EGR + event generation register + 0x14 + 0x20 + write-only + 0x0000 + + + UG + Update generation + 0 + 1 + + + CC1G + Capture/compare 1 + generation + 1 + 1 + + + CC2G + Capture/compare 2 + generation + 2 + 1 + + + CC3G + Capture/compare 3 + generation + 3 + 1 + + + CC4G + Capture/compare 4 + generation + 4 + 1 + + + COMG + Capture/Compare control update + generation + 5 + 1 + + + TG + Trigger generation + 6 + 1 + + + BG + Break generation + 7 + 1 + + + B2G + Break 2 generation + 8 + 1 + + + + + CCMR1_Output + CCMR1_Output + capture/compare mode register (output + mode) + 0x18 + 0x20 + read-write + 0x00000000 + + + OC2CE + Output Compare 2 clear + enable + 15 + 1 + + + OC2M + Output Compare 2 mode + 12 + 3 + + + OC2PE + Output Compare 2 preload + enable + 11 + 1 + + + OC2FE + Output Compare 2 fast + enable + 10 + 1 + + + CC2S + Capture/Compare 2 + selection + 8 + 2 + + + OC1CE + Output Compare 1 clear + enable + 7 + 1 + + + OC1M + Output Compare 1 mode + 4 + 3 + + + OC1PE + Output Compare 1 preload + enable + 3 + 1 + + + OC1FE + Output Compare 1 fast + enable + 2 + 1 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + OC1M_3 + Output Compare 1 mode bit + 3 + 16 + 1 + + + OC2M_3 + Output Compare 2 mode bit + 3 + 24 + 1 + + + + + CCMR1_Input + CCMR1_Input + capture/compare mode register 1 (input + mode) + CCMR1_Output + 0x18 + 0x20 + read-write + 0x00000000 + + + IC2F + Input capture 2 filter + 12 + 4 + + + IC2PCS + Input capture 2 prescaler + 10 + 2 + + + CC2S + Capture/Compare 2 + selection + 8 + 2 + + + IC1F + Input capture 1 filter + 4 + 4 + + + IC1PCS + Input capture 1 prescaler + 2 + 2 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + + + CCMR2_Output + CCMR2_Output + capture/compare mode register (output + mode) + 0x1C + 0x20 + read-write + 0x00000000 + + + OC4CE + Output compare 4 clear + enable + 15 + 1 + + + OC4M + Output compare 4 mode + 12 + 3 + + + OC4PE + Output compare 4 preload + enable + 11 + 1 + + + OC4FE + Output compare 4 fast + enable + 10 + 1 + + + CC4S + Capture/Compare 4 + selection + 8 + 2 + + + OC3CE + Output compare 3 clear + enable + 7 + 1 + + + OC3M + Output compare 3 mode + 4 + 3 + + + OC3PE + Output compare 3 preload + enable + 3 + 1 + + + OC3FE + Output compare 3 fast + enable + 2 + 1 + + + CC3S + Capture/Compare 3 + selection + 0 + 2 + + + OC3M_3 + Output Compare 3 mode bit + 3 + 16 + 1 + + + OC4M_3 + Output Compare 4 mode bit + 3 + 24 + 1 + + + + + CCMR2_Input + CCMR2_Input + capture/compare mode register 2 (input + mode) + CCMR2_Output + 0x1C + 0x20 + read-write + 0x00000000 + + + IC4F + Input capture 4 filter + 12 + 4 + + + IC4PSC + Input capture 4 prescaler + 10 + 2 + + + CC4S + Capture/Compare 4 + selection + 8 + 2 + + + IC3F + Input capture 3 filter + 4 + 4 + + + IC3PSC + Input capture 3 prescaler + 2 + 2 + + + CC3S + Capture/compare 3 + selection + 0 + 2 + + + + + CCER + CCER + capture/compare enable + register + 0x20 + 0x20 + read-write + 0x0000 + + + CC1E + Capture/Compare 1 output + enable + 0 + 1 + + + CC1P + Capture/Compare 1 output + Polarity + 1 + 1 + + + CC1NE + Capture/Compare 1 complementary output + enable + 2 + 1 + + + CC1NP + Capture/Compare 1 output + Polarity + 3 + 1 + + + CC2E + Capture/Compare 2 output + enable + 4 + 1 + + + CC2P + Capture/Compare 2 output + Polarity + 5 + 1 + + + CC2NE + Capture/Compare 2 complementary output + enable + 6 + 1 + + + CC2NP + Capture/Compare 2 output + Polarity + 7 + 1 + + + CC3E + Capture/Compare 3 output + enable + 8 + 1 + + + CC3P + Capture/Compare 3 output + Polarity + 9 + 1 + + + CC3NE + Capture/Compare 3 complementary output + enable + 10 + 1 + + + CC3NP + Capture/Compare 3 output + Polarity + 11 + 1 + + + CC4E + Capture/Compare 4 output + enable + 12 + 1 + + + CC4P + Capture/Compare 3 output + Polarity + 13 + 1 + + + CC4NP + Capture/Compare 4 output + Polarity + 15 + 1 + + + CC5E + Capture/Compare 5 output + enable + 16 + 1 + + + CC5P + Capture/Compare 5 output + Polarity + 17 + 1 + + + CC6E + Capture/Compare 6 output + enable + 20 + 1 + + + CC6P + Capture/Compare 6 output + Polarity + 21 + 1 + + + + + CNT + CNT + counter + 0x24 + 0x20 + 0x00000000 + + + CNT + counter value + 0 + 16 + read-write + + + UIFCPY + UIF copy + 31 + 1 + read-only + + + + + PSC + PSC + prescaler + 0x28 + 0x20 + read-write + 0x0000 + + + PSC + Prescaler value + 0 + 16 + + + + + ARR + ARR + auto-reload register + 0x2C + 0x20 + read-write + 0x00000000 + + + ARR + Auto-reload value + 0 + 16 + + + + + RCR + RCR + repetition counter register + 0x30 + 0x20 + read-write + 0x0000 + + + REP + Repetition counter value + 0 + 16 + + + + + CCR1 + CCR1 + capture/compare register 1 + 0x34 + 0x20 + read-write + 0x00000000 + + + CCR1 + Capture/Compare 1 value + 0 + 16 + + + + + CCR2 + CCR2 + capture/compare register 2 + 0x38 + 0x20 + read-write + 0x00000000 + + + CCR2 + Capture/Compare 2 value + 0 + 16 + + + + + CCR3 + CCR3 + capture/compare register 3 + 0x3C + 0x20 + read-write + 0x00000000 + + + CCR3 + Capture/Compare 3 value + 0 + 16 + + + + + CCR4 + CCR4 + capture/compare register 4 + 0x40 + 0x20 + read-write + 0x00000000 + + + CCR4 + Capture/Compare 3 value + 0 + 16 + + + + + BDTR + BDTR + break and dead-time register + 0x44 + 0x20 + read-write + 0x00000000 + + + DTG + Dead-time generator setup + 0 + 8 + + + LOCK + Lock configuration + 8 + 2 + + + OSSI + Off-state selection for Idle + mode + 10 + 1 + + + OSSR + Off-state selection for Run + mode + 11 + 1 + + + BKE + Break enable + 12 + 1 + + + BKP + Break polarity + 13 + 1 + + + AOE + Automatic output enable + 14 + 1 + + + MOE + Main output enable + 15 + 1 + + + BKF + Break filter + 16 + 4 + + + BK2F + Break 2 filter + 20 + 4 + + + BK2E + Break 2 enable + 24 + 1 + + + BK2P + Break 2 polarity + 25 + 1 + + + + + DCR + DCR + DMA control register + 0x48 + 0x20 + read-write + 0x00000000 + + + DBL + DMA burst length + 8 + 5 + + + DBA + DMA base address + 0 + 5 + + + + + DMAR + DMAR + DMA address for full transfer + 0x4C + 0x20 + read-write + 0x00000000 + + + DMAB + DMA register for burst + accesses + 0 + 16 + + + + + CCMR3_Output + CCMR3_Output + capture/compare mode register 3 (output + mode) + 0x54 + 0x20 + read-write + 0x00000000 + + + OC5FE + Output compare 5 fast + enable + 2 + 1 + + + OC5PE + Output compare 5 preload + enable + 3 + 1 + + + OC5M + Output compare 5 mode + 4 + 3 + + + OC5CE + Output compare 5 clear + enable + 7 + 1 + + + OC6FE + Output compare 6 fast + enable + 10 + 1 + + + OC6PE + Output compare 6 preload + enable + 11 + 1 + + + OC6M + Output compare 6 mode + 12 + 3 + + + OC6CE + Output compare 6 clear + enable + 15 + 1 + + + OC5M_3 + Outout Compare 5 mode bit + 3 + 16 + 1 + + + OC6M_3 + Outout Compare 6 mode bit + 3 + 24 + 1 + + + + + CCR5 + CCR5 + capture/compare register 5 + 0x58 + 0x20 + read-write + 0x00000000 + + + CCR5 + Capture/Compare 5 value + 0 + 16 + + + GC5C1 + Group Channel 5 and Channel + 1 + 29 + 1 + + + GC5C2 + Group Channel 5 and Channel + 2 + 30 + 1 + + + GC5C3 + Group Channel 5 and Channel + 3 + 31 + 1 + + + + + CCR6 + CCR6 + capture/compare register 6 + 0x5C + 0x20 + read-write + 0x00000000 + + + CCR6 + Capture/Compare 6 value + 0 + 16 + + + + + OR + OR + option registers + 0x60 + 0x20 + read-write + 0x00000000 + + + TIM1_ETR_ADC1_RMP + TIM1_ETR_ADC1 remapping + capability + 0 + 2 + + + TIM1_ETR_ADC4_RMP + TIM1_ETR_ADC4 remapping + capability + 2 + 2 + + + + + + + TIM20 + 0x40015000 + + TIM1_CC + TIM1 capture compare interrupt + 27 + + + TIM20_BRK + TIM20 Break interrupt + 77 + + + TIM20_UP + TIM20 Upgrade interrupt + 78 + + + TIM20_TRG_COM + TIM20 Trigger and Commutation + interrupt + 79 + + + TIM20_CC + TIM20 Capture Compare interrupt + 80 + + + + TIM8 + Advanced-timers + TIMs + 0x40013400 + + 0x0 + 0x400 + registers + + + TIM8_BRK + TIM8 break interrupt + 43 + + + TIM8_UP + TIM8 update interrupt + 44 + + + TIM8_TRG_COM + TIM8 Trigger and commutation + interrupts + 45 + + + TIM8_CC + TIM8 capture compare interrupt + 46 + + + + CR1 + CR1 + control register 1 + 0x0 + 0x20 + read-write + 0x0000 + + + CEN + Counter enable + 0 + 1 + + + UDIS + Update disable + 1 + 1 + + + URS + Update request source + 2 + 1 + + + OPM + One-pulse mode + 3 + 1 + + + DIR + Direction + 4 + 1 + + + CMS + Center-aligned mode + selection + 5 + 2 + + + ARPE + Auto-reload preload enable + 7 + 1 + + + CKD + Clock division + 8 + 2 + + + UIFREMAP + UIF status bit remapping + 11 + 1 + + + + + CR2 + CR2 + control register 2 + 0x4 + 0x20 + read-write + 0x0000 + + + CCPC + Capture/compare preloaded + control + 0 + 1 + + + CCUS + Capture/compare control update + selection + 2 + 1 + + + CCDS + Capture/compare DMA + selection + 3 + 1 + + + MMS + Master mode selection + 4 + 3 + + + TI1S + TI1 selection + 7 + 1 + + + OIS1 + Output Idle state 1 + 8 + 1 + + + OIS1N + Output Idle state 1 + 9 + 1 + + + OIS2 + Output Idle state 2 + 10 + 1 + + + OIS2N + Output Idle state 2 + 11 + 1 + + + OIS3 + Output Idle state 3 + 12 + 1 + + + OIS3N + Output Idle state 3 + 13 + 1 + + + OIS4 + Output Idle state 4 + 14 + 1 + + + OIS5 + Output Idle state 5 + 16 + 1 + + + OIS6 + Output Idle state 6 + 18 + 1 + + + MMS2 + Master mode selection 2 + 20 + 4 + + + + + SMCR + SMCR + slave mode control register + 0x8 + 0x20 + read-write + 0x0000 + + + SMS + Slave mode selection + 0 + 3 + + + OCCS + OCREF clear selection + 3 + 1 + + + TS + Trigger selection + 4 + 3 + + + MSM + Master/Slave mode + 7 + 1 + + + ETF + External trigger filter + 8 + 4 + + + ETPS + External trigger prescaler + 12 + 2 + + + ECE + External clock enable + 14 + 1 + + + ETP + External trigger polarity + 15 + 1 + + + SMS3 + Slave mode selection bit 3 + 16 + 1 + + + + + DIER + DIER + DMA/Interrupt enable register + 0xC + 0x20 + read-write + 0x0000 + + + TDE + Trigger DMA request enable + 14 + 1 + + + COMDE + COM DMA request enable + 13 + 1 + + + CC4DE + Capture/Compare 4 DMA request + enable + 12 + 1 + + + CC3DE + Capture/Compare 3 DMA request + enable + 11 + 1 + + + CC2DE + Capture/Compare 2 DMA request + enable + 10 + 1 + + + CC1DE + Capture/Compare 1 DMA request + enable + 9 + 1 + + + UDE + Update DMA request enable + 8 + 1 + + + BIE + Break interrupt enable + 7 + 1 + + + TIE + Trigger interrupt enable + 6 + 1 + + + COMIE + COM interrupt enable + 5 + 1 + + + CC4IE + Capture/Compare 4 interrupt + enable + 4 + 1 + + + CC3IE + Capture/Compare 3 interrupt + enable + 3 + 1 + + + CC2IE + Capture/Compare 2 interrupt + enable + 2 + 1 + + + CC1IE + Capture/Compare 1 interrupt + enable + 1 + 1 + + + UIE + Update interrupt enable + 0 + 1 + + + + + SR + SR + status register + 0x10 + 0x20 + read-write + 0x0000 + + + UIF + Update interrupt flag + 0 + 1 + + + CC1IF + Capture/compare 1 interrupt + flag + 1 + 1 + + + CC2IF + Capture/Compare 2 interrupt + flag + 2 + 1 + + + CC3IF + Capture/Compare 3 interrupt + flag + 3 + 1 + + + CC4IF + Capture/Compare 4 interrupt + flag + 4 + 1 + + + COMIF + COM interrupt flag + 5 + 1 + + + TIF + Trigger interrupt flag + 6 + 1 + + + BIF + Break interrupt flag + 7 + 1 + + + B2IF + Break 2 interrupt flag + 8 + 1 + + + CC1OF + Capture/Compare 1 overcapture + flag + 9 + 1 + + + CC2OF + Capture/compare 2 overcapture + flag + 10 + 1 + + + CC3OF + Capture/Compare 3 overcapture + flag + 11 + 1 + + + CC4OF + Capture/Compare 4 overcapture + flag + 12 + 1 + + + C5IF + Capture/Compare 5 interrupt + flag + 16 + 1 + + + C6IF + Capture/Compare 6 interrupt + flag + 17 + 1 + + + + + EGR + EGR + event generation register + 0x14 + 0x20 + write-only + 0x0000 + + + UG + Update generation + 0 + 1 + + + CC1G + Capture/compare 1 + generation + 1 + 1 + + + CC2G + Capture/compare 2 + generation + 2 + 1 + + + CC3G + Capture/compare 3 + generation + 3 + 1 + + + CC4G + Capture/compare 4 + generation + 4 + 1 + + + COMG + Capture/Compare control update + generation + 5 + 1 + + + TG + Trigger generation + 6 + 1 + + + BG + Break generation + 7 + 1 + + + B2G + Break 2 generation + 8 + 1 + + + + + CCMR1_Output + CCMR1_Output + capture/compare mode register (output + mode) + 0x18 + 0x20 + read-write + 0x00000000 + + + OC2CE + Output Compare 2 clear + enable + 15 + 1 + + + OC2M + Output Compare 2 mode + 12 + 3 + + + OC2PE + Output Compare 2 preload + enable + 11 + 1 + + + OC2FE + Output Compare 2 fast + enable + 10 + 1 + + + CC2S + Capture/Compare 2 + selection + 8 + 2 + + + OC1CE + Output Compare 1 clear + enable + 7 + 1 + + + OC1M + Output Compare 1 mode + 4 + 3 + + + OC1PE + Output Compare 1 preload + enable + 3 + 1 + + + OC1FE + Output Compare 1 fast + enable + 2 + 1 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + OC1M_3 + Output Compare 1 mode bit + 3 + 16 + 1 + + + OC2M_3 + Output Compare 2 mode bit + 3 + 24 + 1 + + + + + CCMR1_Input + CCMR1_Input + capture/compare mode register 1 (input + mode) + CCMR1_Output + 0x18 + 0x20 + read-write + 0x00000000 + + + IC2F + Input capture 2 filter + 12 + 4 + + + IC2PCS + Input capture 2 prescaler + 10 + 2 + + + CC2S + Capture/Compare 2 + selection + 8 + 2 + + + IC1F + Input capture 1 filter + 4 + 4 + + + IC1PCS + Input capture 1 prescaler + 2 + 2 + + + CC1S + Capture/Compare 1 + selection + 0 + 2 + + + + + CCMR2_Output + CCMR2_Output + capture/compare mode register (output + mode) + 0x1C + 0x20 + read-write + 0x00000000 + + + OC4CE + Output compare 4 clear + enable + 15 + 1 + + + OC4M + Output compare 4 mode + 12 + 3 + + + OC4PE + Output compare 4 preload + enable + 11 + 1 + + + OC4FE + Output compare 4 fast + enable + 10 + 1 + + + CC4S + Capture/Compare 4 + selection + 8 + 2 + + + OC3CE + Output compare 3 clear + enable + 7 + 1 + + + OC3M + Output compare 3 mode + 4 + 3 + + + OC3PE + Output compare 3 preload + enable + 3 + 1 + + + OC3FE + Output compare 3 fast + enable + 2 + 1 + + + CC3S + Capture/Compare 3 + selection + 0 + 2 + + + OC3M_3 + Output Compare 3 mode bit + 3 + 16 + 1 + + + OC4M_3 + Output Compare 4 mode bit + 3 + 24 + 1 + + + + + CCMR2_Input + CCMR2_Input + capture/compare mode register 2 (input + mode) + CCMR2_Output + 0x1C + 0x20 + read-write + 0x00000000 + + + IC4F + Input capture 4 filter + 12 + 4 + + + IC4PSC + Input capture 4 prescaler + 10 + 2 + + + CC4S + Capture/Compare 4 + selection + 8 + 2 + + + IC3F + Input capture 3 filter + 4 + 4 + + + IC3PSC + Input capture 3 prescaler + 2 + 2 + + + CC3S + Capture/compare 3 + selection + 0 + 2 + + + + + CCER + CCER + capture/compare enable + register + 0x20 + 0x20 + read-write + 0x0000 + + + CC1E + Capture/Compare 1 output + enable + 0 + 1 + + + CC1P + Capture/Compare 1 output + Polarity + 1 + 1 + + + CC1NE + Capture/Compare 1 complementary output + enable + 2 + 1 + + + CC1NP + Capture/Compare 1 output + Polarity + 3 + 1 + + + CC2E + Capture/Compare 2 output + enable + 4 + 1 + + + CC2P + Capture/Compare 2 output + Polarity + 5 + 1 + + + CC2NE + Capture/Compare 2 complementary output + enable + 6 + 1 + + + CC2NP + Capture/Compare 2 output + Polarity + 7 + 1 + + + CC3E + Capture/Compare 3 output + enable + 8 + 1 + + + CC3P + Capture/Compare 3 output + Polarity + 9 + 1 + + + CC3NE + Capture/Compare 3 complementary output + enable + 10 + 1 + + + CC3NP + Capture/Compare 3 output + Polarity + 11 + 1 + + + CC4E + Capture/Compare 4 output + enable + 12 + 1 + + + CC4P + Capture/Compare 3 output + Polarity + 13 + 1 + + + CC4NP + Capture/Compare 4 output + Polarity + 15 + 1 + + + CC5E + Capture/Compare 5 output + enable + 16 + 1 + + + CC5P + Capture/Compare 5 output + Polarity + 17 + 1 + + + CC6E + Capture/Compare 6 output + enable + 20 + 1 + + + CC6P + Capture/Compare 6 output + Polarity + 21 + 1 + + + + + CNT + CNT + counter + 0x24 + 0x20 + 0x00000000 + + + CNT + counter value + 0 + 16 + read-write + + + UIFCPY + UIF copy + 31 + 1 + read-only + + + + + PSC + PSC + prescaler + 0x28 + 0x20 + read-write + 0x0000 + + + PSC + Prescaler value + 0 + 16 + + + + + ARR + ARR + auto-reload register + 0x2C + 0x20 + read-write + 0x00000000 + + + ARR + Auto-reload value + 0 + 16 + + + + + RCR + RCR + repetition counter register + 0x30 + 0x20 + read-write + 0x0000 + + + REP + Repetition counter value + 0 + 16 + + + + + CCR1 + CCR1 + capture/compare register 1 + 0x34 + 0x20 + read-write + 0x00000000 + + + CCR1 + Capture/Compare 1 value + 0 + 16 + + + + + CCR2 + CCR2 + capture/compare register 2 + 0x38 + 0x20 + read-write + 0x00000000 + + + CCR2 + Capture/Compare 2 value + 0 + 16 + + + + + CCR3 + CCR3 + capture/compare register 3 + 0x3C + 0x20 + read-write + 0x00000000 + + + CCR3 + Capture/Compare 3 value + 0 + 16 + + + + + CCR4 + CCR4 + capture/compare register 4 + 0x40 + 0x20 + read-write + 0x00000000 + + + CCR4 + Capture/Compare 3 value + 0 + 16 + + + + + BDTR + BDTR + break and dead-time register + 0x44 + 0x20 + read-write + 0x00000000 + + + DTG + Dead-time generator setup + 0 + 8 + + + LOCK + Lock configuration + 8 + 2 + + + OSSI + Off-state selection for Idle + mode + 10 + 1 + + + OSSR + Off-state selection for Run + mode + 11 + 1 + + + BKE + Break enable + 12 + 1 + + + BKP + Break polarity + 13 + 1 + + + AOE + Automatic output enable + 14 + 1 + + + MOE + Main output enable + 15 + 1 + + + BKF + Break filter + 16 + 4 + + + BK2F + Break 2 filter + 20 + 4 + + + BK2E + Break 2 enable + 24 + 1 + + + BK2P + Break 2 polarity + 25 + 1 + + + + + DCR + DCR + DMA control register + 0x48 + 0x20 + read-write + 0x00000000 + + + DBL + DMA burst length + 8 + 5 + + + DBA + DMA base address + 0 + 5 + + + + + DMAR + DMAR + DMA address for full transfer + 0x4C + 0x20 + read-write + 0x00000000 + + + DMAB + DMA register for burst + accesses + 0 + 16 + + + + + CCMR3_Output + CCMR3_Output + capture/compare mode register 3 (output + mode) + 0x54 + 0x20 + read-write + 0x00000000 + + + OC5FE + Output compare 5 fast + enable + 2 + 1 + + + OC5PE + Output compare 5 preload + enable + 3 + 1 + + + OC5M + Output compare 5 mode + 4 + 3 + + + OC5CE + Output compare 5 clear + enable + 7 + 1 + + + OC6FE + Output compare 6 fast + enable + 10 + 1 + + + OC6PE + Output compare 6 preload + enable + 11 + 1 + + + OC6M + Output compare 6 mode + 12 + 3 + + + OC6CE + Output compare 6 clear + enable + 15 + 1 + + + OC5M_3 + Outout Compare 5 mode bit + 3 + 16 + 1 + + + OC6M_3 + Outout Compare 6 mode bit + 3 + 24 + 1 + + + + + CCR5 + CCR5 + capture/compare register 5 + 0x58 + 0x20 + read-write + 0x00000000 + + + CCR5 + Capture/Compare 5 value + 0 + 16 + + + GC5C1 + Group Channel 5 and Channel + 1 + 29 + 1 + + + GC5C2 + Group Channel 5 and Channel + 2 + 30 + 1 + + + GC5C3 + Group Channel 5 and Channel + 3 + 31 + 1 + + + + + CCR6 + CCR6 + capture/compare register 6 + 0x5C + 0x20 + read-write + 0x00000000 + + + CCR6 + Capture/Compare 6 value + 0 + 16 + + + + + OR + OR + option registers + 0x60 + 0x20 + read-write + 0x00000000 + + + TIM8_ETR_ADC2_RMP + TIM8_ETR_ADC2 remapping + capability + 0 + 2 + + + TIM8_ETR_ADC3_RMP + TIM8_ETR_ADC3 remapping + capability + 2 + 2 + + + + + + + ADC1 + Analog-to-Digital Converter + ADC + 0x50000000 + + 0x0 + 0x100 + registers + + + + ISR + ISR + interrupt and status register + 0x0 + 0x20 + read-write + 0x00000000 + + + JQOVF + JQOVF + 10 + 1 + + + AWD3 + AWD3 + 9 + 1 + + + AWD2 + AWD2 + 8 + 1 + + + AWD1 + AWD1 + 7 + 1 + + + JEOS + JEOS + 6 + 1 + + + JEOC + JEOC + 5 + 1 + + + OVR + OVR + 4 + 1 + + + EOS + EOS + 3 + 1 + + + EOC + EOC + 2 + 1 + + + EOSMP + EOSMP + 1 + 1 + + + ADRDY + ADRDY + 0 + 1 + + + + + IER + IER + interrupt enable register + 0x4 + 0x20 + read-write + 0x00000000 + + + JQOVFIE + JQOVFIE + 10 + 1 + + + AWD3IE + AWD3IE + 9 + 1 + + + AWD2IE + AWD2IE + 8 + 1 + + + AWD1IE + AWD1IE + 7 + 1 + + + JEOSIE + JEOSIE + 6 + 1 + + + JEOCIE + JEOCIE + 5 + 1 + + + OVRIE + OVRIE + 4 + 1 + + + EOSIE + EOSIE + 3 + 1 + + + EOCIE + EOCIE + 2 + 1 + + + EOSMPIE + EOSMPIE + 1 + 1 + + + ADRDYIE + ADRDYIE + 0 + 1 + + + + + CR + CR + control register + 0x8 + 0x20 + read-write + 0x00000000 + + + ADCAL + ADCAL + 31 + 1 + + + ADCALDIF + ADCALDIF + 30 + 1 + + + ADVREGEN + ADVREGEN + 28 + 2 + + + JADSTP + JADSTP + 5 + 1 + + + ADSTP + ADSTP + 4 + 1 + + + JADSTART + JADSTART + 3 + 1 + + + ADSTART + ADSTART + 2 + 1 + + + ADDIS + ADDIS + 1 + 1 + + + ADEN + ADEN + 0 + 1 + + + + + CFGR + CFGR + configuration register + 0xC + 0x20 + read-write + 0x00000000 + + + AWDCH1CH + AWDCH1CH + 26 + 5 + + + JAUTO + JAUTO + 25 + 1 + + + JAWD1EN + JAWD1EN + 24 + 1 + + + AWD1EN + AWD1EN + 23 + 1 + + + AWD1SGL + AWD1SGL + 22 + 1 + + + JQM + JQM + 21 + 1 + + + JDISCEN + JDISCEN + 20 + 1 + + + DISCNUM + DISCNUM + 17 + 3 + + + DISCEN + DISCEN + 16 + 1 + + + AUTDLY + AUTDLY + 14 + 1 + + + CONT + CONT + 13 + 1 + + + OVRMOD + OVRMOD + 12 + 1 + + + EXTEN + EXTEN + 10 + 2 + + + EXTSEL + EXTSEL + 6 + 4 + + + ALIGN + ALIGN + 5 + 1 + + + RES + RES + 3 + 2 + + + DMACFG + DMACFG + 1 + 1 + + + DMAEN + DMAEN + 0 + 1 + + + + + SMPR1 + SMPR1 + sample time register 1 + 0x14 + 0x20 + read-write + 0x00000000 + + + SMP9 + SMP9 + 27 + 3 + + + SMP8 + SMP8 + 24 + 3 + + + SMP7 + SMP7 + 21 + 3 + + + SMP6 + SMP6 + 18 + 3 + + + SMP5 + SMP5 + 15 + 3 + + + SMP4 + SMP4 + 12 + 3 + + + SMP3 + SMP3 + 9 + 3 + + + SMP2 + SMP2 + 6 + 3 + + + SMP1 + SMP1 + 3 + 3 + + + + + SMPR2 + SMPR2 + sample time register 2 + 0x18 + 0x20 + read-write + 0x00000000 + + + SMP18 + SMP18 + 24 + 3 + + + SMP17 + SMP17 + 21 + 3 + + + SMP16 + SMP16 + 18 + 3 + + + SMP15 + SMP15 + 15 + 3 + + + SMP14 + SMP14 + 12 + 3 + + + SMP13 + SMP13 + 9 + 3 + + + SMP12 + SMP12 + 6 + 3 + + + SMP11 + SMP11 + 3 + 3 + + + SMP10 + SMP10 + 0 + 3 + + + + + TR1 + TR1 + watchdog threshold register 1 + 0x20 + 0x20 + read-write + 0x0FFF0000 + + + HT1 + HT1 + 16 + 12 + + + LT1 + LT1 + 0 + 12 + + + + + TR2 + TR2 + watchdog threshold register + 0x24 + 0x20 + read-write + 0x0FFF0000 + + + HT2 + HT2 + 16 + 8 + + + LT2 + LT2 + 0 + 8 + + + + + TR3 + TR3 + watchdog threshold register 3 + 0x28 + 0x20 + read-write + 0x0FFF0000 + + + HT3 + HT3 + 16 + 8 + + + LT3 + LT3 + 0 + 8 + + + + + SQR1 + SQR1 + regular sequence register 1 + 0x30 + 0x20 + read-write + 0x00000000 + + + SQ4 + SQ4 + 24 + 5 + + + SQ3 + SQ3 + 18 + 5 + + + SQ2 + SQ2 + 12 + 5 + + + SQ1 + SQ1 + 6 + 5 + + + L3 + L3 + 0 + 4 + + + + + SQR2 + SQR2 + regular sequence register 2 + 0x34 + 0x20 + read-write + 0x00000000 + + + SQ9 + SQ9 + 24 + 5 + + + SQ8 + SQ8 + 18 + 5 + + + SQ7 + SQ7 + 12 + 5 + + + SQ6 + SQ6 + 6 + 5 + + + SQ5 + SQ5 + 0 + 5 + + + + + SQR3 + SQR3 + regular sequence register 3 + 0x38 + 0x20 + read-write + 0x00000000 + + + SQ14 + SQ14 + 24 + 5 + + + SQ13 + SQ13 + 18 + 5 + + + SQ12 + SQ12 + 12 + 5 + + + SQ11 + SQ11 + 6 + 5 + + + SQ10 + SQ10 + 0 + 5 + + + + + SQR4 + SQR4 + regular sequence register 4 + 0x3C + 0x20 + read-write + 0x00000000 + + + SQ16 + SQ16 + 6 + 5 + + + SQ15 + SQ15 + 0 + 5 + + + + + DR + DR + regular Data Register + 0x40 + 0x20 + read-only + 0x00000000 + + + regularDATA + regularDATA + 0 + 16 + + + + + JSQR + JSQR + injected sequence register + 0x4C + 0x20 + read-write + 0x00000000 + + + JSQ4 + JSQ4 + 26 + 5 + + + JSQ3 + JSQ3 + 20 + 5 + + + JSQ2 + JSQ2 + 14 + 5 + + + JSQ1 + JSQ1 + 8 + 5 + + + JEXTEN + JEXTEN + 6 + 2 + + + JEXTSEL + JEXTSEL + 2 + 4 + + + JL + JL + 0 + 2 + + + + + OFR1 + OFR1 + offset register 1 + 0x60 + 0x20 + read-write + 0x00000000 + + + OFFSET1_EN + OFFSET1_EN + 31 + 1 + + + OFFSET1_CH + OFFSET1_CH + 26 + 5 + + + OFFSET1 + OFFSET1 + 0 + 12 + + + + + OFR2 + OFR2 + offset register 2 + 0x64 + 0x20 + read-write + 0x00000000 + + + OFFSET2_EN + OFFSET2_EN + 31 + 1 + + + OFFSET2_CH + OFFSET2_CH + 26 + 5 + + + OFFSET2 + OFFSET2 + 0 + 12 + + + + + OFR3 + OFR3 + offset register 3 + 0x68 + 0x20 + read-write + 0x00000000 + + + OFFSET3_EN + OFFSET3_EN + 31 + 1 + + + OFFSET3_CH + OFFSET3_CH + 26 + 5 + + + OFFSET3 + OFFSET3 + 0 + 12 + + + + + OFR4 + OFR4 + offset register 4 + 0x6C + 0x20 + read-write + 0x00000000 + + + OFFSET4_EN + OFFSET4_EN + 31 + 1 + + + OFFSET4_CH + OFFSET4_CH + 26 + 5 + + + OFFSET4 + OFFSET4 + 0 + 12 + + + + + JDR1 + JDR1 + injected data register 1 + 0x80 + 0x20 + read-only + 0x00000000 + + + JDATA1 + JDATA1 + 0 + 16 + + + + + JDR2 + JDR2 + injected data register 2 + 0x84 + 0x20 + read-only + 0x00000000 + + + JDATA2 + JDATA2 + 0 + 16 + + + + + JDR3 + JDR3 + injected data register 3 + 0x88 + 0x20 + read-only + 0x00000000 + + + JDATA3 + JDATA3 + 0 + 16 + + + + + JDR4 + JDR4 + injected data register 4 + 0x8C + 0x20 + read-only + 0x00000000 + + + JDATA4 + JDATA4 + 0 + 16 + + + + + AWD2CR + AWD2CR + Analog Watchdog 2 Configuration + Register + 0xA0 + 0x20 + read-write + 0x00000000 + + + AWD2CH + AWD2CH + 1 + 18 + + + + + AWD3CR + AWD3CR + Analog Watchdog 3 Configuration + Register + 0xA4 + 0x20 + read-write + 0x00000000 + + + AWD3CH + AWD3CH + 1 + 18 + + + + + DIFSEL + DIFSEL + Differential Mode Selection Register + 2 + 0xB0 + 0x20 + 0x00000000 + + + DIFSEL_1_15 + Differential mode for channels 15 to + 1 + 1 + 15 + read-write + + + DIFSEL_16_18 + Differential mode for channels 18 to + 16 + 16 + 3 + read-only + + + + + CALFACT + CALFACT + Calibration Factors + 0xB4 + 0x20 + read-write + 0x00000000 + + + CALFACT_D + CALFACT_D + 16 + 7 + + + CALFACT_S + CALFACT_S + 0 + 7 + + + + + + + ADC2 + 0x50000100 + + + ADC3 + 0x50000400 + + ADC3 + ADC3 global interrupt + 47 + + + + ADC4 + 0x50000500 + + ADC4 + ADC4 global interrupt + 61 + + + + ADC1_2 + Analog-to-Digital Converter + ADC + 0x50000300 + + 0x0 + 0x10 + registers + + + ADC1_2 + ADC1 and ADC2 global interrupt + 18 + + + + CSR + CSR + ADC Common status register + 0x0 + 0x20 + read-only + 0x00000000 + + + ADDRDY_MST + ADDRDY_MST + 0 + 1 + + + EOSMP_MST + EOSMP_MST + 1 + 1 + + + EOC_MST + EOC_MST + 2 + 1 + + + EOS_MST + EOS_MST + 3 + 1 + + + OVR_MST + OVR_MST + 4 + 1 + + + JEOC_MST + JEOC_MST + 5 + 1 + + + JEOS_MST + JEOS_MST + 6 + 1 + + + AWD1_MST + AWD1_MST + 7 + 1 + + + AWD2_MST + AWD2_MST + 8 + 1 + + + AWD3_MST + AWD3_MST + 9 + 1 + + + JQOVF_MST + JQOVF_MST + 10 + 1 + + + ADRDY_SLV + ADRDY_SLV + 16 + 1 + + + EOSMP_SLV + EOSMP_SLV + 17 + 1 + + + EOC_SLV + End of regular conversion of the slave + ADC + 18 + 1 + + + EOS_SLV + End of regular sequence flag of the + slave ADC + 19 + 1 + + + OVR_SLV + Overrun flag of the slave + ADC + 20 + 1 + + + JEOC_SLV + End of injected conversion flag of the + slave ADC + 21 + 1 + + + JEOS_SLV + End of injected sequence flag of the + slave ADC + 22 + 1 + + + AWD1_SLV + Analog watchdog 1 flag of the slave + ADC + 23 + 1 + + + AWD2_SLV + Analog watchdog 2 flag of the slave + ADC + 24 + 1 + + + AWD3_SLV + Analog watchdog 3 flag of the slave + ADC + 25 + 1 + + + JQOVF_SLV + Injected Context Queue Overflow flag of + the slave ADC + 26 + 1 + + + + + CCR + CCR + ADC common control register + 0x8 + 0x20 + read-write + 0x00000000 + + + MULT + Multi ADC mode selection + 0 + 5 + + + DELAY + Delay between 2 sampling + phases + 8 + 4 + + + DMACFG + DMA configuration (for multi-ADC + mode) + 13 + 1 + + + MDMA + Direct memory access mode for multi ADC + mode + 14 + 2 + + + CKMODE + ADC clock mode + 16 + 2 + + + VREFEN + VREFINT enable + 22 + 1 + + + TSEN + Temperature sensor enable + 23 + 1 + + + VBATEN + VBAT enable + 24 + 1 + + + + + CDR + CDR + ADC common regular data register for dual + and triple modes + 0xC + 0x20 + read-only + 0x00000000 + + + RDATA_SLV + Regular data of the slave + ADC + 16 + 16 + + + RDATA_MST + Regular data of the master + ADC + 0 + 16 + + + + + + + ADC3_4 + 0x50000700 + + + SYSCFG_COMP_OPAMP + System configuration controller _Comparator and + Operational amplifier + SYSCFG_COMP_OPAMP + 0x40010000 + + 0x0 + 0x400 + registers + + + COMP123 + COMP1 & COMP2 & COMP3 interrupts + combined with EXTI Lines 21, 22 and 29 + interrupts + 64 + + + COMP456 + COMP4 & COMP5 & COMP6 interrupts + combined with EXTI Lines 30, 31 and 32 + interrupts + 65 + + + COMP7 + COMP7 interrupt combined with EXTI Line 33 + interrupt + 66 + + + + SYSCFG_CFGR1 + SYSCFG_CFGR1 + configuration register 1 + 0x0 + 0x20 + read-write + 0x00000000 + + + MEM_MODE + Memory mapping selection + bits + 0 + 2 + + + USB_IT_RMP + USB interrupt remap + 5 + 1 + + + TIM1_ITR_RMP + Timer 1 ITR3 selection + 6 + 1 + + + DAC_TRIG_RMP + DAC trigger remap (when TSEL = + 001) + 7 + 1 + + + ADC24_DMA_RMP + ADC24 DMA remapping bit + 8 + 1 + + + TIM16_DMA_RMP + TIM16 DMA request remapping + bit + 11 + 1 + + + TIM17_DMA_RMP + TIM17 DMA request remapping + bit + 12 + 1 + + + TIM6_DAC1_DMA_RMP + TIM6 and DAC1 DMA request remapping + bit + 13 + 1 + + + TIM7_DAC2_DMA_RMP + TIM7 and DAC2 DMA request remapping + bit + 14 + 1 + + + I2C_PB6_FM + Fast Mode Plus (FM+) driving capability + activation bits. + 16 + 1 + + + I2C_PB7_FM + Fast Mode Plus (FM+) driving capability + activation bits. + 17 + 1 + + + I2C_PB8_FM + Fast Mode Plus (FM+) driving capability + activation bits. + 18 + 1 + + + I2C_PB9_FM + Fast Mode Plus (FM+) driving capability + activation bits. + 19 + 1 + + + I2C1_FM + I2C1 Fast Mode Plus + 20 + 1 + + + I2C2_FM + I2C2 Fast Mode Plus + 21 + 1 + + + ENCODER_MODE + Encoder mode + 22 + 2 + + + FPU_IT + Interrupt enable bits from + FPU + 26 + 6 + + + + + SYSCFG_EXTICR1 + SYSCFG_EXTICR1 + external interrupt configuration register + 1 + 0x8 + 0x20 + read-write + 0x0000 + + + EXTI3 + EXTI 3 configuration bits + 12 + 4 + + + EXTI2 + EXTI 2 configuration bits + 8 + 4 + + + EXTI1 + EXTI 1 configuration bits + 4 + 4 + + + EXTI0 + EXTI 0 configuration bits + 0 + 4 + + + + + SYSCFG_EXTICR2 + SYSCFG_EXTICR2 + external interrupt configuration register + 2 + 0xC + 0x20 + read-write + 0x0000 + + + EXTI7 + EXTI 7 configuration bits + 12 + 4 + + + EXTI6 + EXTI 6 configuration bits + 8 + 4 + + + EXTI5 + EXTI 5 configuration bits + 4 + 4 + + + EXTI4 + EXTI 4 configuration bits + 0 + 4 + + + + + SYSCFG_EXTICR3 + SYSCFG_EXTICR3 + external interrupt configuration register + 3 + 0x10 + 0x20 + read-write + 0x0000 + + + EXTI11 + EXTI 11 configuration bits + 12 + 4 + + + EXTI10 + EXTI 10 configuration bits + 8 + 4 + + + EXTI9 + EXTI 9 configuration bits + 4 + 4 + + + EXTI8 + EXTI 8 configuration bits + 0 + 4 + + + + + SYSCFG_EXTICR4 + SYSCFG_EXTICR4 + external interrupt configuration register + 4 + 0x14 + 0x20 + read-write + 0x0000 + + + EXTI15 + EXTI 15 configuration bits + 12 + 4 + + + EXTI14 + EXTI 14 configuration bits + 8 + 4 + + + EXTI13 + EXTI 13 configuration bits + 4 + 4 + + + EXTI12 + EXTI 12 configuration bits + 0 + 4 + + + + + SYSCFG_CFGR2 + SYSCFG_CFGR2 + configuration register 2 + 0x18 + 0x20 + read-write + 0x0000 + + + LOCUP_LOCK + Cortex-M0 LOCKUP bit enable + bit + 0 + 1 + + + SRAM_PARITY_LOCK + SRAM parity lock bit + 1 + 1 + + + PVD_LOCK + PVD lock enable bit + 2 + 1 + + + BYP_ADD_PAR + Bypass address bit 29 in parity + calculation + 4 + 1 + + + SRAM_PEF + SRAM parity flag + 8 + 1 + + + + + SYSCFG_RCR + SYSCFG_RCR + CCM SRAM protection register + 0x4 + 0x20 + read-write + 0x0000 + + + PAGE0_WP + CCM SRAM page write protection + bit + 0 + 1 + + + PAGE1_WP + CCM SRAM page write protection + bit + 1 + 1 + + + PAGE2_WP + CCM SRAM page write protection + bit + 2 + 1 + + + PAGE3_WP + CCM SRAM page write protection + bit + 3 + 1 + + + PAGE4_WP + CCM SRAM page write protection + bit + 4 + 1 + + + PAGE5_WP + CCM SRAM page write protection + bit + 5 + 1 + + + PAGE6_WP + CCM SRAM page write protection + bit + 6 + 1 + + + PAGE7_WP + CCM SRAM page write protection + bit + 7 + 1 + + + + + COMP1_CSR + COMP1_CSR + control and status register + 0x1C + 0x20 + 0x0000 + + + COMP1EN + Comparator 1 enable + 0 + 1 + read-write + + + COMP1_INP_DAC + COMP1_INP_DAC + 1 + 1 + read-write + + + COMP1MODE + Comparator 1 mode + 2 + 2 + read-write + + + COMP1INSEL + Comparator 1 inverting input + selection + 4 + 3 + read-write + + + COMP1_OUT_SEL + Comparator 1 output + selection + 10 + 4 + read-write + + + COMP1POL + Comparator 1 output + polarity + 15 + 1 + read-write + + + COMP1HYST + Comparator 1 hysteresis + 16 + 2 + read-write + + + COMP1_BLANKING + Comparator 1 blanking + source + 18 + 3 + read-write + + + COMP1OUT + Comparator 1 output + 30 + 1 + read-only + + + COMP1LOCK + Comparator 1 lock + 31 + 1 + read-write + + + + + COMP2_CSR + COMP2_CSR + control and status register + 0x20 + 0x20 + read-write + 0x0000 + + + COMP2EN + Comparator 2 enable + 0 + 1 + + + COMP2MODE + Comparator 2 mode + 2 + 2 + + + COMP2INSEL + Comparator 2 inverting input + selection + 4 + 3 + + + COMP2INPSEL + Comparator 2 non inverted input + selection + 7 + 1 + + + COMP2INMSEL + Comparator 1inverting input + selection + 9 + 1 + + + COMP2_OUT_SEL + Comparator 2 output + selection + 10 + 4 + + + COMP2POL + Comparator 2 output + polarity + 15 + 1 + + + COMP2HYST + Comparator 2 hysteresis + 16 + 2 + + + COMP2_BLANKING + Comparator 2 blanking + source + 18 + 3 + + + COMP2LOCK + Comparator 2 lock + 31 + 1 + + + COMP2OUT + Comparator 2 output + 30 + 1 + + + + + COMP3_CSR + COMP3_CSR + control and status register + 0x24 + 0x20 + 0x0000 + + + COMP3EN + Comparator 3 enable + 0 + 1 + read-write + + + COMP3MODE + Comparator 3 mode + 2 + 2 + read-write + + + COMP3INSEL + Comparator 3 inverting input + selection + 4 + 3 + read-write + + + COMP3INPSEL + Comparator 3 non inverted input + selection + 7 + 1 + read-write + + + COMP3_OUT_SEL + Comparator 3 output + selection + 10 + 4 + read-write + + + COMP3POL + Comparator 3 output + polarity + 15 + 1 + read-write + + + COMP3HYST + Comparator 3 hysteresis + 16 + 2 + read-write + + + COMP3_BLANKING + Comparator 3 blanking + source + 18 + 3 + read-write + + + COMP3OUT + Comparator 3 output + 30 + 1 + read-only + + + COMP3LOCK + Comparator 3 lock + 31 + 1 + read-write + + + + + COMP4_CSR + COMP4_CSR + control and status register + 0x28 + 0x20 + 0x0000 + + + COMP4EN + Comparator 4 enable + 0 + 1 + read-write + + + COMP4MODE + Comparator 4 mode + 2 + 2 + read-write + + + COMP4INSEL + Comparator 4 inverting input + selection + 4 + 3 + read-write + + + COMP4INPSEL + Comparator 4 non inverted input + selection + 7 + 1 + read-write + + + COM4WINMODE + Comparator 4 window mode + 9 + 1 + read-write + + + COMP4_OUT_SEL + Comparator 4 output + selection + 10 + 4 + read-write + + + COMP4POL + Comparator 4 output + polarity + 15 + 1 + read-write + + + COMP4HYST + Comparator 4 hysteresis + 16 + 2 + read-write + + + COMP4_BLANKING + Comparator 4 blanking + source + 18 + 3 + read-write + + + COMP4OUT + Comparator 4 output + 30 + 1 + read-only + + + COMP4LOCK + Comparator 4 lock + 31 + 1 + read-write + + + + + COMP5_CSR + COMP5_CSR + control and status register + 0x2C + 0x20 + 0x0000 + + + COMP5EN + Comparator 5 enable + 0 + 1 + read-write + + + COMP5MODE + Comparator 5 mode + 2 + 2 + read-write + + + COMP5INSEL + Comparator 5 inverting input + selection + 4 + 3 + read-write + + + COMP5INPSEL + Comparator 5 non inverted input + selection + 7 + 1 + read-write + + + COMP5_OUT_SEL + Comparator 5 output + selection + 10 + 4 + read-write + + + COMP5POL + Comparator 5 output + polarity + 15 + 1 + read-write + + + COMP5HYST + Comparator 5 hysteresis + 16 + 2 + read-write + + + COMP5_BLANKING + Comparator 5 blanking + source + 18 + 3 + read-write + + + COMP5OUT + Comparator51 output + 30 + 1 + read-only + + + COMP5LOCK + Comparator 5 lock + 31 + 1 + read-write + + + + + COMP6_CSR + COMP6_CSR + control and status register + 0x30 + 0x20 + 0x0000 + + + COMP6EN + Comparator 6 enable + 0 + 1 + read-write + + + COMP6MODE + Comparator 6 mode + 2 + 2 + read-write + + + COMP6INSEL + Comparator 6 inverting input + selection + 4 + 3 + read-write + + + COMP6INPSEL + Comparator 6 non inverted input + selection + 7 + 1 + read-write + + + COM6WINMODE + Comparator 6 window mode + 9 + 1 + read-write + + + COMP6_OUT_SEL + Comparator 6 output + selection + 10 + 4 + read-write + + + COMP6POL + Comparator 6 output + polarity + 15 + 1 + read-write + + + COMP6HYST + Comparator 6 hysteresis + 16 + 2 + read-write + + + COMP6_BLANKING + Comparator 6 blanking + source + 18 + 3 + read-write + + + COMP6OUT + Comparator 6 output + 30 + 1 + read-only + + + COMP6LOCK + Comparator 6 lock + 31 + 1 + read-write + + + + + COMP7_CSR + COMP7_CSR + control and status register + 0x34 + 0x20 + 0x0000 + + + COMP7EN + Comparator 7 enable + 0 + 1 + read-write + + + COMP7MODE + Comparator 7 mode + 2 + 2 + read-write + + + COMP7INSEL + Comparator 7 inverting input + selection + 4 + 3 + read-write + + + COMP7INPSEL + Comparator 7 non inverted input + selection + 7 + 1 + read-write + + + COMP7_OUT_SEL + Comparator 7 output + selection + 10 + 4 + read-write + + + COMP7POL + Comparator 7 output + polarity + 15 + 1 + read-write + + + COMP7HYST + Comparator 7 hysteresis + 16 + 2 + read-write + + + COMP7_BLANKING + Comparator 7 blanking + source + 18 + 3 + read-write + + + COMP7OUT + Comparator 7 output + 30 + 1 + read-only + + + COMP7LOCK + Comparator 7 lock + 31 + 1 + read-write + + + + + OPAMP1_CSR + OPAMP1_CSR + control register + 0x38 + 0x20 + 0x0000 + + + OPAMP1_EN + OPAMP1 enable + 0 + 1 + read-write + + + FORCE_VP + FORCE_VP + 1 + 1 + read-write + + + VP_SEL + OPAMP1 Non inverting input + selection + 2 + 2 + read-write + + + VM_SEL + OPAMP1 inverting input + selection + 5 + 2 + read-write + + + TCM_EN + Timer controlled Mux mode + enable + 7 + 1 + read-write + + + VMS_SEL + OPAMP1 inverting input secondary + selection + 8 + 1 + read-write + + + VPS_SEL + OPAMP1 Non inverting input secondary + selection + 9 + 2 + read-write + + + CALON + Calibration mode enable + 11 + 1 + read-write + + + CALSEL + Calibration selection + 12 + 2 + read-write + + + PGA_GAIN + Gain in PGA mode + 14 + 4 + read-write + + + USER_TRIM + User trimming enable + 18 + 1 + read-write + + + TRIMOFFSETP + Offset trimming value + (PMOS) + 19 + 5 + read-write + + + TRIMOFFSETN + Offset trimming value + (NMOS) + 24 + 5 + read-write + + + TSTREF + TSTREF + 29 + 1 + read-write + + + OUTCAL + OPAMP 1 ouput status flag + 30 + 1 + read-only + + + LOCK + OPAMP 1 lock + 31 + 1 + read-write + + + + + OPAMP2_CSR + OPAMP2_CSR + control register + 0x3C + 0x20 + 0x0000 + + + OPAMP2EN + OPAMP2 enable + 0 + 1 + read-write + + + FORCE_VP + FORCE_VP + 1 + 1 + read-write + + + VP_SEL + OPAMP2 Non inverting input + selection + 2 + 2 + read-write + + + VM_SEL + OPAMP2 inverting input + selection + 5 + 2 + read-write + + + TCM_EN + Timer controlled Mux mode + enable + 7 + 1 + read-write + + + VMS_SEL + OPAMP2 inverting input secondary + selection + 8 + 1 + read-write + + + VPS_SEL + OPAMP2 Non inverting input secondary + selection + 9 + 2 + read-write + + + CALON + Calibration mode enable + 11 + 1 + read-write + + + CALSEL + Calibration selection + 12 + 2 + read-write + + + PGA_GAIN + Gain in PGA mode + 14 + 4 + read-write + + + USER_TRIM + User trimming enable + 18 + 1 + read-write + + + TRIMOFFSETP + Offset trimming value + (PMOS) + 19 + 5 + read-write + + + TRIMOFFSETN + Offset trimming value + (NMOS) + 24 + 5 + read-write + + + TSTREF + TSTREF + 29 + 1 + read-write + + + OUTCAL + OPAMP 2 ouput status flag + 30 + 1 + read-only + + + LOCK + OPAMP 2 lock + 31 + 1 + read-write + + + + + OPAMP3_CSR + OPAMP3_CSR + control register + 0x40 + 0x20 + 0x0000 + + + OPAMP3EN + OPAMP3 enable + 0 + 1 + read-write + + + FORCE_VP + FORCE_VP + 1 + 1 + read-write + + + VP_SEL + OPAMP3 Non inverting input + selection + 2 + 2 + read-write + + + VM_SEL + OPAMP3 inverting input + selection + 5 + 2 + read-write + + + TCM_EN + Timer controlled Mux mode + enable + 7 + 1 + read-write + + + VMS_SEL + OPAMP3 inverting input secondary + selection + 8 + 1 + read-write + + + VPS_SEL + OPAMP3 Non inverting input secondary + selection + 9 + 2 + read-write + + + CALON + Calibration mode enable + 11 + 1 + read-write + + + CALSEL + Calibration selection + 12 + 2 + read-write + + + PGA_GAIN + Gain in PGA mode + 14 + 4 + read-write + + + USER_TRIM + User trimming enable + 18 + 1 + read-write + + + TRIMOFFSETP + Offset trimming value + (PMOS) + 19 + 5 + read-write + + + TRIMOFFSETN + Offset trimming value + (NMOS) + 24 + 5 + read-write + + + TSTREF + TSTREF + 29 + 1 + read-write + + + OUTCAL + OPAMP 3 ouput status flag + 30 + 1 + read-only + + + LOCK + OPAMP 3 lock + 31 + 1 + read-write + + + + + OPAMP4_CSR + OPAMP4_CSR + control register + 0x44 + 0x20 + 0x0000 + + + OPAMP4EN + OPAMP4 enable + 0 + 1 + read-write + + + FORCE_VP + FORCE_VP + 1 + 1 + read-write + + + VP_SEL + OPAMP4 Non inverting input + selection + 2 + 2 + read-write + + + VM_SEL + OPAMP4 inverting input + selection + 5 + 2 + read-write + + + TCM_EN + Timer controlled Mux mode + enable + 7 + 1 + read-write + + + VMS_SEL + OPAMP4 inverting input secondary + selection + 8 + 1 + read-write + + + VPS_SEL + OPAMP4 Non inverting input secondary + selection + 9 + 2 + read-write + + + CALON + Calibration mode enable + 11 + 1 + read-write + + + CALSEL + Calibration selection + 12 + 2 + read-write + + + PGA_GAIN + Gain in PGA mode + 14 + 4 + read-write + + + USER_TRIM + User trimming enable + 18 + 1 + read-write + + + TRIMOFFSETP + Offset trimming value + (PMOS) + 19 + 5 + read-write + + + TRIMOFFSETN + Offset trimming value + (NMOS) + 24 + 5 + read-write + + + TSTREF + TSTREF + 29 + 1 + read-write + + + OUTCAL + OPAMP 4 ouput status flag + 30 + 1 + read-only + + + LOCK + OPAMP 4 lock + 31 + 1 + read-write + + + + + SYSCFG_CFGR3 + SYSCFG_CFGR3 + SYSCFG configuration register + 3 + 0x50 + 0x20 + read-write + 0x0000 + + + SPI1_RX_DMA_RMP + SPI1_RX DMA remapping bit + 0 + 2 + + + SPI1_TX_DMA_RMP + SPI1_TX DMA remapping bit + 2 + 2 + + + I2C1_RX_DMA_RMP + I2C1_RX DMA remapping bit + 4 + 2 + + + I2C1_TX_DMA_RMP + I2C1_TX DMA remapping bit + 6 + 2 + + + ADC2_DMA_RMP + ADC2 DMA channel remapping + bit + 8 + 2 + + + + + SYSCFG_CFGR4 + SYSCFG_CFGR4 + SYSCFG configuration register + 4 + 0x48 + 0x20 + read-write + 0x0000 + + + ADC12_EXT2_RMP + Controls the Input trigger of ADC12 + regular channel EXT2 + 0 + 1 + + + ADC12_EXT3_RMP + Controls the Input trigger of ADC12 + regular channel EXT3 + 1 + 1 + + + ADC12_EXT5_RMP + Controls the Input trigger of ADC12 + regular channel EXT5 + 2 + 1 + + + ADC12_EXT13_RMP + Controls the Input trigger of ADC12 + regular channel EXT13 + 3 + 1 + + + ADC12_EXT15_RMP + Controls the Input trigger of ADC12 + regular channel EXT15 + 4 + 1 + + + ADC12_JEXT3_RMP + Controls the Input trigger of ADC12 + injected channel EXTI3 + 5 + 1 + + + ADC12_JEXT6_RMP + Controls the Input trigger of ADC12 + injected channel EXTI6 + 6 + 1 + + + ADC12_JEXT13_RMP + Controls the Input trigger of ADC12 + injected channel EXTI13 + 7 + 1 + + + ADC34_EXT5_RMP + Controls the Input trigger of ADC34 + regular channel EXT5 + 8 + 1 + + + ADC34_EXT6_RMP + Controls the Input trigger of ADC34 + regular channel EXT6 + 9 + 1 + + + ADC34_EXT15_RMP + Controls the Input trigger of ADC34 + regular channel EXT15 + 10 + 1 + + + ADC34_JEXT5_RMP + Controls the Input trigger of ADC34 + injected channel JEXT5 + 11 + 1 + + + ADC34_JEXT11_RMP + Controls the Input trigger of ADC34 + injected channel JEXT11 + 12 + 1 + + + ADC34_JEXT14_RMP + Controls the Input trigger of ADC34 + injected channel JEXT14 + 13 + 1 + + + + + + + FMC + Flexible memory controller + FMC + 0xA0000400 + + 0x0 + 0xC00 + registers + + + FMC + FSMC global interrupt + 48 + + + + BCR1 + BCR1 + SRAM/NOR-Flash chip-select control register + 1 + 0x0 + 0x20 + read-write + 0x000030D0 + + + CCLKEN + CCLKEN + 20 + 1 + + + CBURSTRW + CBURSTRW + 19 + 1 + + + ASYNCWAIT + ASYNCWAIT + 15 + 1 + + + EXTMOD + EXTMOD + 14 + 1 + + + WAITEN + WAITEN + 13 + 1 + + + WREN + WREN + 12 + 1 + + + WAITCFG + WAITCFG + 11 + 1 + + + WAITPOL + WAITPOL + 9 + 1 + + + BURSTEN + BURSTEN + 8 + 1 + + + FACCEN + FACCEN + 6 + 1 + + + MWID + MWID + 4 + 2 + + + MTYP + MTYP + 2 + 2 + + + MUXEN + MUXEN + 1 + 1 + + + MBKEN + MBKEN + 0 + 1 + + + + + BTR1 + BTR1 + SRAM/NOR-Flash chip-select timing register + 1 + 0x4 + 0x20 + read-write + 0xFFFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + BUSTURN + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + BCR2 + BCR2 + SRAM/NOR-Flash chip-select control register + 2 + 0x8 + 0x20 + read-write + 0x000030D0 + + + CBURSTRW + CBURSTRW + 19 + 1 + + + ASYNCWAIT + ASYNCWAIT + 15 + 1 + + + EXTMOD + EXTMOD + 14 + 1 + + + WAITEN + WAITEN + 13 + 1 + + + WREN + WREN + 12 + 1 + + + WAITCFG + WAITCFG + 11 + 1 + + + WRAPMOD + WRAPMOD + 10 + 1 + + + WAITPOL + WAITPOL + 9 + 1 + + + BURSTEN + BURSTEN + 8 + 1 + + + FACCEN + FACCEN + 6 + 1 + + + MWID + MWID + 4 + 2 + + + MTYP + MTYP + 2 + 2 + + + MUXEN + MUXEN + 1 + 1 + + + MBKEN + MBKEN + 0 + 1 + + + + + BTR2 + BTR2 + SRAM/NOR-Flash chip-select timing register + 2 + 0xC + 0x20 + read-write + 0xFFFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + BUSTURN + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + BCR3 + BCR3 + SRAM/NOR-Flash chip-select control register + 3 + 0x10 + 0x20 + read-write + 0x000030D0 + + + CBURSTRW + CBURSTRW + 19 + 1 + + + ASYNCWAIT + ASYNCWAIT + 15 + 1 + + + EXTMOD + EXTMOD + 14 + 1 + + + WAITEN + WAITEN + 13 + 1 + + + WREN + WREN + 12 + 1 + + + WAITCFG + WAITCFG + 11 + 1 + + + WRAPMOD + WRAPMOD + 10 + 1 + + + WAITPOL + WAITPOL + 9 + 1 + + + BURSTEN + BURSTEN + 8 + 1 + + + FACCEN + FACCEN + 6 + 1 + + + MWID + MWID + 4 + 2 + + + MTYP + MTYP + 2 + 2 + + + MUXEN + MUXEN + 1 + 1 + + + MBKEN + MBKEN + 0 + 1 + + + + + BTR3 + BTR3 + SRAM/NOR-Flash chip-select timing register + 3 + 0x14 + 0x20 + read-write + 0xFFFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + BUSTURN + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + BCR4 + BCR4 + SRAM/NOR-Flash chip-select control register + 4 + 0x18 + 0x20 + read-write + 0x000030D0 + + + CBURSTRW + CBURSTRW + 19 + 1 + + + ASYNCWAIT + ASYNCWAIT + 15 + 1 + + + EXTMOD + EXTMOD + 14 + 1 + + + WAITEN + WAITEN + 13 + 1 + + + WREN + WREN + 12 + 1 + + + WAITCFG + WAITCFG + 11 + 1 + + + WRAPMOD + WRAPMOD + 10 + 1 + + + WAITPOL + WAITPOL + 9 + 1 + + + BURSTEN + BURSTEN + 8 + 1 + + + FACCEN + FACCEN + 6 + 1 + + + MWID + MWID + 4 + 2 + + + MTYP + MTYP + 2 + 2 + + + MUXEN + MUXEN + 1 + 1 + + + MBKEN + MBKEN + 0 + 1 + + + + + BTR4 + BTR4 + SRAM/NOR-Flash chip-select timing register + 4 + 0x1C + 0x20 + read-write + 0xFFFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + BUSTURN + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + PCR2 + PCR2 + PC Card/NAND Flash control register + 2 + 0x60 + 0x20 + read-write + 0x00000018 + + + ECCPS + ECCPS + 17 + 3 + + + TAR + TAR + 13 + 4 + + + TCLR + TCLR + 9 + 4 + + + ECCEN + ECCEN + 6 + 1 + + + PWID + PWID + 4 + 2 + + + PTYP + PTYP + 3 + 1 + + + PBKEN + PBKEN + 2 + 1 + + + PWAITEN + PWAITEN + 1 + 1 + + + + + SR2 + SR2 + FIFO status and interrupt register + 2 + 0x64 + 0x20 + 0x00000040 + + + FEMPT + FEMPT + 6 + 1 + read-only + + + IFEN + IFEN + 5 + 1 + read-write + + + ILEN + ILEN + 4 + 1 + read-write + + + IREN + IREN + 3 + 1 + read-write + + + IFS + IFS + 2 + 1 + read-write + + + ILS + ILS + 1 + 1 + read-write + + + IRS + IRS + 0 + 1 + read-write + + + + + PMEM2 + PMEM2 + Common memory space timing register + 2 + 0x68 + 0x20 + read-write + 0xFCFCFCFC + + + MEMHIZx + MEMHIZx + 24 + 8 + + + MEMHOLDx + MEMHOLDx + 16 + 8 + + + MEMWAITx + MEMWAITx + 8 + 8 + + + MEMSETx + MEMSETx + 0 + 8 + + + + + PATT2 + PATT2 + Attribute memory space timing register + 2 + 0x6C + 0x20 + read-write + 0xFCFCFCFC + + + ATTHIZx + ATTHIZx + 24 + 8 + + + ATTHOLDx + ATTHOLDx + 16 + 8 + + + ATTWAITx + ATTWAITx + 8 + 8 + + + ATTSETx + ATTSETx + 0 + 8 + + + + + ECCR2 + ECCR2 + ECC result register 2 + 0x74 + 0x20 + read-only + 0x00000000 + + + ECCx + ECCx + 0 + 32 + + + + + PCR3 + PCR3 + PC Card/NAND Flash control register + 3 + 0x80 + 0x20 + read-write + 0x00000018 + + + ECCPS + ECCPS + 17 + 3 + + + TAR + TAR + 13 + 4 + + + TCLR + TCLR + 9 + 4 + + + ECCEN + ECCEN + 6 + 1 + + + PWID + PWID + 4 + 2 + + + PTYP + PTYP + 3 + 1 + + + PBKEN + PBKEN + 2 + 1 + + + PWAITEN + PWAITEN + 1 + 1 + + + + + SR3 + SR3 + FIFO status and interrupt register + 3 + 0x84 + 0x20 + 0x00000040 + + + FEMPT + FEMPT + 6 + 1 + read-only + + + IFEN + IFEN + 5 + 1 + read-write + + + ILEN + ILEN + 4 + 1 + read-write + + + IREN + IREN + 3 + 1 + read-write + + + IFS + IFS + 2 + 1 + read-write + + + ILS + ILS + 1 + 1 + read-write + + + IRS + IRS + 0 + 1 + read-write + + + + + PMEM3 + PMEM3 + Common memory space timing register + 3 + 0x88 + 0x20 + read-write + 0xFCFCFCFC + + + MEMHIZx + MEMHIZx + 24 + 8 + + + MEMHOLDx + MEMHOLDx + 16 + 8 + + + MEMWAITx + MEMWAITx + 8 + 8 + + + MEMSETx + MEMSETx + 0 + 8 + + + + + PATT3 + PATT3 + Attribute memory space timing register + 3 + 0x8C + 0x20 + read-write + 0xFCFCFCFC + + + ATTHIZx + ATTHIZx + 24 + 8 + + + ATTHOLDx + ATTHOLDx + 16 + 8 + + + ATTWAITx + ATTWAITx + 8 + 8 + + + ATTSETx + ATTSETx + 0 + 8 + + + + + ECCR3 + ECCR3 + ECC result register 3 + 0x94 + 0x20 + read-only + 0x00000000 + + + ECCx + ECCx + 0 + 32 + + + + + PCR4 + PCR4 + PC Card/NAND Flash control register + 4 + 0xA0 + 0x20 + read-write + 0x00000018 + + + ECCPS + ECCPS + 17 + 3 + + + TAR + TAR + 13 + 4 + + + TCLR + TCLR + 9 + 4 + + + ECCEN + ECCEN + 6 + 1 + + + PWID + PWID + 4 + 2 + + + PTYP + PTYP + 3 + 1 + + + PBKEN + PBKEN + 2 + 1 + + + PWAITEN + PWAITEN + 1 + 1 + + + + + SR4 + SR4 + FIFO status and interrupt register + 4 + 0xA4 + 0x20 + 0x00000040 + + + FEMPT + FEMPT + 6 + 1 + read-only + + + IFEN + IFEN + 5 + 1 + read-write + + + ILEN + ILEN + 4 + 1 + read-write + + + IREN + IREN + 3 + 1 + read-write + + + IFS + IFS + 2 + 1 + read-write + + + ILS + ILS + 1 + 1 + read-write + + + IRS + IRS + 0 + 1 + read-write + + + + + PMEM4 + PMEM4 + Common memory space timing register + 4 + 0xA8 + 0x20 + read-write + 0xFCFCFCFC + + + MEMHIZx + MEMHIZx + 24 + 8 + + + MEMHOLDx + MEMHOLDx + 16 + 8 + + + MEMWAITx + MEMWAITx + 8 + 8 + + + MEMSETx + MEMSETx + 0 + 8 + + + + + PATT4 + PATT4 + Attribute memory space timing register + 4 + 0xAC + 0x20 + read-write + 0xFCFCFCFC + + + ATTHIZx + ATTHIZx + 24 + 8 + + + ATTHOLDx + ATTHOLDx + 16 + 8 + + + ATTWAITx + ATTWAITx + 8 + 8 + + + ATTSETx + ATTSETx + 0 + 8 + + + + + PIO4 + PIO4 + I/O space timing register 4 + 0xB0 + 0x20 + read-write + 0xFCFCFCFC + + + IOHIZx + IOHIZx + 24 + 8 + + + IOHOLDx + IOHOLDx + 16 + 8 + + + IOWAITx + IOWAITx + 8 + 8 + + + IOSETx + IOSETx + 0 + 8 + + + + + BWTR1 + BWTR1 + SRAM/NOR-Flash write timing registers + 1 + 0x104 + 0x20 + read-write + 0x0FFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + Bus turnaround phase + duration + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + BWTR2 + BWTR2 + SRAM/NOR-Flash write timing registers + 2 + 0x10C + 0x20 + read-write + 0x0FFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + Bus turnaround phase + duration + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + BWTR3 + BWTR3 + SRAM/NOR-Flash write timing registers + 3 + 0x114 + 0x20 + read-write + 0x0FFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + Bus turnaround phase + duration + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + BWTR4 + BWTR4 + SRAM/NOR-Flash write timing registers + 4 + 0x11C + 0x20 + read-write + 0x0FFFFFFF + + + ACCMOD + ACCMOD + 28 + 2 + + + DATLAT + DATLAT + 24 + 4 + + + CLKDIV + CLKDIV + 20 + 4 + + + BUSTURN + Bus turnaround phase + duration + 16 + 4 + + + DATAST + DATAST + 8 + 8 + + + ADDHLD + ADDHLD + 4 + 4 + + + ADDSET + ADDSET + 0 + 4 + + + + + + + diff --git a/satrs-example-stm32f3-disco/build.rs b/satrs-example-stm32f3-disco/build.rs deleted file mode 100644 index 98f603e..0000000 --- a/satrs-example-stm32f3-disco/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; - -fn main() { - // Put the linker script somewhere the linker can find it - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - File::create(out.join("memory.x")) - .unwrap() - .write_all(include_bytes!("memory.x")) - .unwrap(); - println!("cargo:rustc-link-search={}", out.display()); - - // Only re-run the build script when memory.x is changed, - // instead of when any part of the source code changes. - println!("cargo:rerun-if-changed=memory.x"); -} diff --git a/satrs-example-stm32f3-disco/jlink.gdb b/satrs-example-stm32f3-disco/legacy/jlink.gdb similarity index 100% rename from satrs-example-stm32f3-disco/jlink.gdb rename to satrs-example-stm32f3-disco/legacy/jlink.gdb diff --git a/satrs-example-stm32f3-disco/openocd.cfg b/satrs-example-stm32f3-disco/legacy/openocd.cfg similarity index 100% rename from satrs-example-stm32f3-disco/openocd.cfg rename to satrs-example-stm32f3-disco/legacy/openocd.cfg diff --git a/satrs-example-stm32f3-disco/openocd.gdb b/satrs-example-stm32f3-disco/legacy/openocd.gdb similarity index 92% rename from satrs-example-stm32f3-disco/openocd.gdb rename to satrs-example-stm32f3-disco/legacy/openocd.gdb index 06e81b1..2453de8 100644 --- a/satrs-example-stm32f3-disco/openocd.gdb +++ b/satrs-example-stm32f3-disco/legacy/openocd.gdb @@ -26,7 +26,7 @@ break main # # 8000000 must match the core clock frequency # # 2000000 is the frequency of the SWO pin. This was added for newer # openocd versions like v0.12.0. -monitor tpiu config internal itm.txt uart off 8000000 2000000 +# monitor tpiu config internal itm.txt uart off 8000000 2000000 # # OR: make the microcontroller SWO pin output compatible with UART (8N1) # # 8000000 must match the core clock frequency @@ -34,7 +34,7 @@ monitor tpiu config internal itm.txt uart off 8000000 2000000 # monitor tpiu config external uart off 8000000 2000000 # # enable ITM port 0 -monitor itm port 0 on +# monitor itm port 0 on load diff --git a/satrs-example-stm32f3-disco/pyclient/main.py b/satrs-example-stm32f3-disco/pyclient/main.py index d37da58..ae3099b 100755 --- a/satrs-example-stm32f3-disco/pyclient/main.py +++ b/satrs-example-stm32f3-disco/pyclient/main.py @@ -94,6 +94,7 @@ class SatRsConfigHook(HookBase): def create_cmd_definition_tree() -> CmdTreeNode: root_node = CmdTreeNode.root_node() root_node.add_child(CmdTreeNode("ping", "Send PUS ping TC")) + root_node.add_child(CmdTreeNode("change_blink_freq", "Change blink frequency")) return root_node @@ -215,6 +216,25 @@ class TcHandler(TcHandlerBase): if cmd_path == "/ping": q.add_log_cmd("Sending PUS ping telecommand") q.add_pus_tc(PusTelecommand(service=17, subservice=1)) + if cmd_path == "/change_blink_freq": + self.create_change_blink_freq_command(q) + + def create_change_blink_freq_command(self, q: DefaultPusQueueHelper): + q.add_log_cmd("Changing blink frequency") + while True: + blink_freq = int( + input( + "Please specify new blink frequency in ms. Valid Range [2..10000]: " + ) + ) + if blink_freq < 2 or blink_freq > 10000: + print( + "Invalid blink frequency. Please specify a value between 2 and 10000." + ) + continue + break + app_data = struct.pack("!I", blink_freq) + q.add_pus_tc(PusTelecommand(service=8, subservice=1, app_data=app_data)) def main(): diff --git a/satrs-example-stm32f3-disco/src/blinky.rs b/satrs-example-stm32f3-disco/src/bin/blinky.rs similarity index 68% rename from satrs-example-stm32f3-disco/src/blinky.rs rename to satrs-example-stm32f3-disco/src/bin/blinky.rs index eefdbfe..0f96176 100644 --- a/satrs-example-stm32f3-disco/src/blinky.rs +++ b/satrs-example-stm32f3-disco/src/bin/blinky.rs @@ -1,17 +1,15 @@ #![no_std] #![no_main] +use satrs_example_stm32f3_disco as _; -extern crate panic_itm; - -use cortex_m_rt::entry; - +use stm32f3_discovery::leds::Leds; use stm32f3_discovery::stm32f3xx_hal::delay::Delay; use stm32f3_discovery::stm32f3xx_hal::{pac, prelude::*}; -use stm32f3_discovery::leds::Leds; use stm32f3_discovery::switch_hal::{OutputSwitch, ToggleableOutputSwitch}; -#[entry] -fn main()-> ! { +#[cortex_m_rt::entry] +fn main() -> ! { + defmt::println!("STM32F3 Discovery Blinky"); let dp = pac::Peripherals::take().unwrap(); let mut rcc = dp.RCC.constrain(); let cp = cortex_m::Peripherals::take().unwrap(); @@ -30,49 +28,49 @@ fn main()-> ! { gpioe.pe14, gpioe.pe15, &mut gpioe.moder, - &mut gpioe.otyper + &mut gpioe.otyper, ); let delay_ms = 200u16; loop { - leds.ld3.toggle().ok(); + leds.ld3_n.toggle().ok(); delay.delay_ms(delay_ms); - leds.ld3.toggle().ok(); + leds.ld3_n.toggle().ok(); delay.delay_ms(delay_ms); //explicit on/off - leds.ld4.on().ok(); + leds.ld4_nw.on().ok(); delay.delay_ms(delay_ms); - leds.ld4.off().ok(); + leds.ld4_nw.off().ok(); delay.delay_ms(delay_ms); - leds.ld5.on().ok(); + leds.ld5_ne.on().ok(); delay.delay_ms(delay_ms); - leds.ld5.off().ok(); + leds.ld5_ne.off().ok(); delay.delay_ms(delay_ms); - leds.ld6.on().ok(); + leds.ld6_w.on().ok(); delay.delay_ms(delay_ms); - leds.ld6.off().ok(); - delay.delay_ms(delay_ms); - - leds.ld7.on().ok(); - delay.delay_ms(delay_ms); - leds.ld7.off().ok(); - delay.delay_ms(delay_ms); - - leds.ld8.on().ok(); - delay.delay_ms(delay_ms); - leds.ld8.off().ok(); - delay.delay_ms(delay_ms); - - leds.ld9.on().ok(); - delay.delay_ms(delay_ms); - leds.ld9.off().ok(); + leds.ld6_w.off().ok(); delay.delay_ms(delay_ms); - leds.ld10.on().ok(); + leds.ld7_e.on().ok(); delay.delay_ms(delay_ms); - leds.ld10.off().ok(); + leds.ld7_e.off().ok(); + delay.delay_ms(delay_ms); + + leds.ld8_sw.on().ok(); + delay.delay_ms(delay_ms); + leds.ld8_sw.off().ok(); + delay.delay_ms(delay_ms); + + leds.ld9_se.on().ok(); + delay.delay_ms(delay_ms); + leds.ld9_se.off().ok(); + delay.delay_ms(delay_ms); + + leds.ld10_s.on().ok(); + delay.delay_ms(delay_ms); + leds.ld10_s.off().ok(); delay.delay_ms(delay_ms); } } diff --git a/satrs-example-stm32f3-disco/src/lib.rs b/satrs-example-stm32f3-disco/src/lib.rs new file mode 100644 index 0000000..284660c --- /dev/null +++ b/satrs-example-stm32f3-disco/src/lib.rs @@ -0,0 +1,51 @@ +#![no_main] +#![no_std] + +use cortex_m_semihosting::debug; + +use defmt_brtt as _; // global logger + +use stm32f3xx_hal as _; // memory layout + +use panic_probe as _; + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with status code 0. +pub fn exit() -> ! { + loop { + debug::exit(debug::EXIT_SUCCESS); + } +} + +/// Hardfault handler. +/// +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with an error. This seems better than the default, which is to spin in a +/// loop. +#[cortex_m_rt::exception] +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { + loop { + debug::exit(debug::EXIT_FAILURE); + } +} + +// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used +// once within a crate. the module can be in any file but there can only be at most +// one `#[tests]` module in this library crate +#[cfg(test)] +#[defmt_test::tests] +mod unit_tests { + use defmt::assert; + + #[test] + fn it_works() { + assert!(true) + } +} diff --git a/satrs-example-stm32f3-disco/src/main.rs b/satrs-example-stm32f3-disco/src/main.rs index 8795786..623ef5f 100644 --- a/satrs-example-stm32f3-disco/src/main.rs +++ b/satrs-example-stm32f3-disco/src/main.rs @@ -1,27 +1,31 @@ #![no_std] #![no_main] -extern crate panic_itm; +use satrs::pus::verification::{ + FailParams, TcStateAccepted, VerificationReportCreator, VerificationToken, +}; +use satrs::spacepackets::ecss::tc::PusTcReader; +use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; +use satrs::spacepackets::ecss::EcssEnumU16; +use satrs::spacepackets::CcsdsPacket; +use satrs::spacepackets::{ByteConversionError, SpHeader}; +// global logger + panicking-behavior + memory layout +use satrs_example_stm32f3_disco as _; use rtic::app; use heapless::{mpmc::Q8, Vec}; #[allow(unused_imports)] -use itm_logger::{debug, info, logger_init, warn}; -use rtic_monotonics::systick::fugit::TimerInstantU32; +use rtic_monotonics::systick::fugit::{MillisDurationU32, TimerInstantU32}; use rtic_monotonics::systick::ExtU32; use satrs::seq_count::SequenceCountProviderCore; -use satrs::{ - pool::StoreError, - pus::{EcssChannel, EcssTmSenderCore, EcssTmtcError, PusTmWrapper}, - spacepackets::{ecss::PusPacket, ecss::WritablePusPacket}, -}; +use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket}; use stm32f3xx_hal::dma::dma1; use stm32f3xx_hal::gpio::{PushPull, AF7, PA2, PA3}; use stm32f3xx_hal::pac::USART2; use stm32f3xx_hal::serial::{Rx, RxEvent, Serial, SerialDmaRx, SerialDmaTx, Tx, TxEvent}; const UART_BAUD: u32 = 115200; -const BLINK_FREQ_MS: u32 = 1000; +const DEFAULT_BLINK_FREQ_MS: u32 = 1000; const TX_HANDLER_FREQ_MS: u32 = 20; const MIN_DELAY_BETWEEN_TX_PACKETS_MS: u32 = 5; const MAX_TC_LEN: usize = 128; @@ -54,7 +58,6 @@ type TcPacket = Vec; static TM_REQUESTS: Q8 = Q8::new(); -use core::cell::RefCell; use core::sync::atomic::{AtomicU16, Ordering}; pub struct SeqCountProviderAtomicRef { @@ -93,53 +96,45 @@ pub struct TxIdle { dma_channel: dma1::C7, } -pub struct TmSender { - vec: Option>>, - ctx: &'static str, +#[derive(Debug, defmt::Format)] +pub enum TmSendError { + ByteConversion(ByteConversionError), + Queue, } -impl TmSender { - pub fn new(tm_packet: TmPacket, ctx: &'static str) -> Self { - Self { - vec: Some(RefCell::new(tm_packet)), - ctx, - } +impl From for TmSendError { + fn from(value: ByteConversionError) -> Self { + Self::ByteConversion(value) } } -impl EcssChannel for TmSender { - fn id(&self) -> satrs::ChannelId { - 0 +fn send_tm(tm_creator: PusTmCreator) -> Result<(), TmSendError> { + if tm_creator.len_written() > MAX_TM_LEN { + return Err(ByteConversionError::ToSliceTooSmall { + expected: tm_creator.len_written(), + found: MAX_TM_LEN, + } + .into()); } + let mut tm_vec = TmPacket::new(); + tm_vec + .resize(tm_creator.len_written(), 0) + .expect("vec resize failed"); + tm_creator.write_to_bytes(tm_vec.as_mut_slice())?; + defmt::info!( + "Sending TM[{},{}] with size {}", + tm_creator.service(), + tm_creator.subservice(), + tm_creator.len_written() + ); + TM_REQUESTS + .enqueue(tm_vec) + .map_err(|_| TmSendError::Queue)?; + Ok(()) } -impl EcssTmSenderCore for TmSender { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { - let vec = self.vec.as_ref(); - if vec.is_none() { - panic!("send_tm should only be called once"); - } - let vec_ref = vec.unwrap(); - let mut vec = vec_ref.borrow_mut(); - match tm { - PusTmWrapper::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)), - PusTmWrapper::Direct(tm) => { - if tm.len_written() > MAX_TM_LEN { - return Err(EcssTmtcError::Store(StoreError::DataTooLarge( - tm.len_written(), - ))); - } - vec.resize(tm.len_written(), 0).expect("vec resize failed"); - tm.write_to_bytes(vec.as_mut_slice())?; - info!(target: self.ctx, "Sending TM[{},{}] with size {}", tm.service(), tm.subservice(), tm.len_written()); - drop(vec); - TM_REQUESTS - .enqueue(vec_ref.take()) - .map_err(|_| EcssTmtcError::Store(StoreError::StoreFull(0)))?; - } - } - Ok(()) - } +fn handle_tm_send_error(error: TmSendError) { + defmt::warn!("sending tm failed with error {}", error); } pub enum UartTxState { @@ -154,19 +149,106 @@ pub struct UartTxShared { state: UartTxState, } +pub struct RequestWithToken { + token: VerificationToken, + request: Request, +} + +#[derive(Debug, defmt::Format)] +pub enum Request { + Ping, + ChangeBlinkFrequency(u32), +} + +#[derive(Debug, defmt::Format)] +pub enum RequestError { + InvalidApid = 1, + InvalidService = 2, + InvalidSubservice = 3, + NotEnoughAppData = 4, +} + +pub fn convert_pus_tc_to_request( + tc: &PusTcReader, + verif_reporter: &mut VerificationReportCreator, + src_data_buf: &mut [u8], + timestamp: &[u8], +) -> Result { + defmt::info!( + "Found PUS TC [{},{}] with length {}", + tc.service(), + tc.subservice(), + tc.len_packed() + ); + + let token = verif_reporter.add_tc(tc); + if tc.apid() != PUS_APID { + defmt::warn!("Received tc with unknown APID {}", tc.apid()); + let result = send_tm( + verif_reporter + .acceptance_failure( + src_data_buf, + token, + SEQ_COUNT_PROVIDER.get_and_increment(), + 0, + FailParams::new(timestamp, &EcssEnumU16::new(0), &[]), + ) + .unwrap(), + ); + if let Err(e) = result { + handle_tm_send_error(e); + } + return Err(RequestError::InvalidApid); + } + let (tm_creator, accepted_token) = verif_reporter + .acceptance_success( + src_data_buf, + token, + SEQ_COUNT_PROVIDER.get_and_increment(), + 0, + timestamp, + ) + .unwrap(); + + if let Err(e) = send_tm(tm_creator) { + handle_tm_send_error(e); + } + + if tc.service() == 17 && tc.subservice() == 1 { + if tc.subservice() == 1 { + return Ok(RequestWithToken { + request: Request::Ping, + token: accepted_token, + }); + } else { + return Err(RequestError::InvalidSubservice); + } + } else if tc.service() == 8 { + if tc.subservice() == 1 { + if tc.user_data().len() < 4 { + return Err(RequestError::NotEnoughAppData); + } + let new_freq_ms = u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()); + return Ok(RequestWithToken { + request: Request::ChangeBlinkFrequency(new_freq_ms), + token: accepted_token, + }); + } else { + return Err(RequestError::InvalidSubservice); + } + } else { + return Err(RequestError::InvalidService); + } +} + #[app(device = stm32f3xx_hal::pac, peripherals = true)] mod app { use super::*; use core::slice::Iter; - use cortex_m::iprintln; use rtic_monotonics::systick::Systick; use rtic_monotonics::Monotonic; - use satrs::pus::verification::FailParams; - use satrs::pus::verification::VerificationReporterCore; - use satrs::spacepackets::{ - ecss::tc::PusTcReader, ecss::tm::PusTmCreator, ecss::tm::PusTmSecondaryHeader, - ecss::EcssEnumU16, time::cds::P_FIELD_BASE, CcsdsPacket, SpHeader, - }; + use satrs::pus::verification::{TcStateStarted, VerificationReportCreator}; + use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE}; #[allow(unused_imports)] use stm32f3_discovery::leds::Direction; use stm32f3_discovery::leds::Leds; @@ -179,27 +261,27 @@ mod app { #[shared] struct Shared { + blink_freq: MillisDurationU32, tx_shared: UartTxShared, rx_transfer: Option, } #[local] struct Local { + verif_reporter: VerificationReportCreator, leds: Leds, last_dir: Direction, - verif_reporter: VerificationReporterCore, curr_dir: Iter<'static, Direction>, } #[init] - fn init(mut cx: init::Context) -> (Shared, Local) { + fn init(cx: init::Context) -> (Shared, Local) { 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); - logger_init(); let mut flash = cx.device.FLASH.constrain(); let clocks = rcc .cfgr @@ -211,15 +293,9 @@ mod app { // Set up monotonic timer. //let mono_timer = MonoTimer::new(cx.core.DWT, clocks, &mut cx.core.DCB); - // setup ITM output - iprintln!( - &mut cx.core.ITM.stim[0], - "Starting sat-rs demo application for the STM32F3-Discovery" - ); + defmt::info!("Starting sat-rs demo application for the STM32F3-Discovery"); let mut gpioe = cx.device.GPIOE.split(&mut rcc.ahb); - let verif_reporter = VerificationReporterCore::new(PUS_APID).unwrap(); - let leds = Leds::new( gpioe.pe8, gpioe.pe9, @@ -265,11 +341,15 @@ mod app { // For some reason, this is also immediately triggered.. tx_serial.clear_event(TxEvent::TransmissionComplete); let rx_transfer = rx_serial.read_exact(unsafe { DMA_RX_BUF.as_mut_slice() }, dma1.ch6); - info!(target: "init", "Spawning tasks"); + defmt::info!("Spawning tasks"); blink::spawn().unwrap(); serial_tx_handler::spawn().unwrap(); + + let verif_reporter = VerificationReportCreator::new(PUS_APID).unwrap(); + ( Shared { + blink_freq: MillisDurationU32::from_ticks(DEFAULT_BLINK_FREQ_MS), tx_shared: UartTxShared { last_completed: None, state: UartTxState::Idle(Some(TxIdle { @@ -280,17 +360,16 @@ mod app { rx_transfer: Some(rx_transfer), }, Local { - //timer: mono_timer, + verif_reporter, leds, last_dir: Direction::North, curr_dir: Direction::iter(), - verif_reporter, }, ) } - #[task(local = [leds, curr_dir, last_dir])] - async fn blink(cx: blink::Context) { + #[task(local = [leds, curr_dir, last_dir], shared=[blink_freq])] + async fn blink(mut cx: blink::Context) { let blink::LocalResources { leds, curr_dir, @@ -314,7 +393,8 @@ mod app { toggle_leds(curr_dir.next().unwrap()); } } - Systick::delay(BLINK_FREQ_MS.millis()).await; + let current_blink_freq = cx.shared.blink_freq.lock(|current| *current); + Systick::delay(current_blink_freq).await; } } @@ -389,20 +469,19 @@ mod app { #[task( local = [ - stamp_buf: [u8; 7] = [0; 7], + verif_reporter, decode_buf: [u8; MAX_TC_LEN] = [0; MAX_TC_LEN], src_data_buf: [u8; MAX_TM_LEN] = [0; MAX_TM_LEN], - verif_reporter + timestamp: [u8; 7] = [0; 7], ], + shared = [blink_freq] )] async fn serial_rx_handler( - cx: serial_rx_handler::Context, + mut cx: serial_rx_handler::Context, received_packet: Vec, ) { - info!("running rx handler"); - let tgt: &'static str = "serial_rx_handler"; - cx.local.stamp_buf[0] = P_FIELD_BASE; - info!(target: tgt, "Received packet with {} bytes", received_packet.len()); + cx.local.timestamp[0] = P_FIELD_BASE; + defmt::info!("Received packet with {} bytes", received_packet.len()); let decode_buf = cx.local.decode_buf; let packet = received_packet.as_slice(); let mut start_idx = None; @@ -413,142 +492,124 @@ mod app { } } if start_idx.is_none() { - warn!( - target: tgt, - "decoding error, can only process cobs encoded frames, data is all 0" - ); + defmt::warn!("decoding error, can only process cobs encoded frames, data is all 0"); return; } let start_idx = start_idx.unwrap(); match cobs::decode(&received_packet.as_slice()[start_idx..], decode_buf) { Ok(len) => { - info!(target: tgt, "Decoded packet length: {}", len); + defmt::info!("Decoded packet length: {}", len); let pus_tc = PusTcReader::new(decode_buf); - let verif_reporter = cx.local.verif_reporter; match pus_tc { - Ok((tc, tc_len)) => handle_tc( - tc, - tc_len, - verif_reporter, - cx.local.src_data_buf, - cx.local.stamp_buf, - tgt, - ), + Ok((tc, _tc_len)) => { + match convert_pus_tc_to_request( + &tc, + cx.local.verif_reporter, + cx.local.src_data_buf, + cx.local.timestamp, + ) { + Ok(request_with_token) => { + let started_token = handle_start_verification( + request_with_token.token, + cx.local.verif_reporter, + cx.local.src_data_buf, + cx.local.timestamp, + ); + + match request_with_token.request { + Request::Ping => { + handle_ping_request(cx.local.timestamp); + } + Request::ChangeBlinkFrequency(new_freq_ms) => { + defmt::info!("Received blink frequency change request with new frequncy {}", new_freq_ms); + cx.shared.blink_freq.lock(|blink_freq| { + *blink_freq = + MillisDurationU32::from_ticks(new_freq_ms); + }); + } + } + handle_completion_verification( + started_token, + cx.local.verif_reporter, + cx.local.src_data_buf, + cx.local.timestamp, + ); + } + Err(e) => { + // TODO: Error handling: Send verification failure based on request error. + defmt::warn!("request error {}", e); + } + } + } Err(e) => { - warn!(target: tgt, "Error unpacking PUS TC: {}", e); + defmt::warn!("Error unpacking PUS TC: {}", e); } } } Err(_) => { - warn!( - target: tgt, - "decoding error, can only process cobs encoded frames" - ) + defmt::warn!("decoding error, can only process cobs encoded frames") } } } - fn handle_tc( - tc: PusTcReader, - tc_len: usize, - verif_reporter: &mut VerificationReporterCore, - src_data_buf: &mut [u8; MAX_TM_LEN], - stamp_buf: &[u8; 7], - tgt: &'static str, - ) { - info!( - target: tgt, - "Found PUS TC [{},{}] with length {}", - tc.service(), - tc.subservice(), - tc_len - ); - - let token = verif_reporter.add_tc(&tc); - if tc.apid() != PUS_APID { - warn!(target: tgt, "Received tc with unknown APID {}", tc.apid()); - let sendable = verif_reporter - .acceptance_failure( - src_data_buf, - token, - SEQ_COUNT_PROVIDER.get(), - 0, - FailParams::new(stamp_buf, &EcssEnumU16::new(0), &[]), - ) - .unwrap(); - let sender = TmSender::new(TmPacket::new(), tgt); - if let Err(e) = verif_reporter.send_acceptance_failure(sendable, &sender) { - warn!(target: tgt, "Sending acceptance failure failed: {:?}", e.0); - }; + fn handle_ping_request(timestamp: &[u8]) { + defmt::info!("Received PUS ping telecommand, sending ping reply TM[17,2]"); + let sp_header = + SpHeader::new_for_unseg_tc(PUS_APID, SEQ_COUNT_PROVIDER.get_and_increment(), 0); + let sec_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); + let ping_reply = PusTmCreator::new(sp_header, sec_header, &[], true); + let mut tm_packet = TmPacket::new(); + tm_packet + .resize(ping_reply.len_written(), 0) + .expect("vec resize failed"); + ping_reply.write_to_bytes(&mut tm_packet).unwrap(); + if TM_REQUESTS.enqueue(tm_packet).is_err() { + defmt::warn!("TC queue full"); return; } - let sendable = verif_reporter - .acceptance_success(src_data_buf, token, SEQ_COUNT_PROVIDER.get(), 0, stamp_buf) + } + + fn handle_start_verification( + accepted_token: VerificationToken, + verif_reporter: &mut VerificationReportCreator, + src_data_buf: &mut [u8], + timestamp: &[u8], + ) -> VerificationToken { + let (tm_creator, started_token) = verif_reporter + .start_success( + src_data_buf, + accepted_token, + SEQ_COUNT_PROVIDER.get(), + 0, + ×tamp, + ) .unwrap(); + let result = send_tm(tm_creator); + if let Err(e) = result { + handle_tm_send_error(e); + } + started_token + } - let sender = TmSender::new(TmPacket::new(), tgt); - let accepted_token = match verif_reporter.send_acceptance_success(sendable, &sender) { - Ok(token) => token, - Err(e) => { - warn!(target: "serial_rx_handler", "Sending acceptance success failed: {:?}", e.0); - return; - } - }; - - if tc.service() == 17 { - if tc.subservice() == 1 { - let sendable = verif_reporter - .start_success( - src_data_buf, - accepted_token, - SEQ_COUNT_PROVIDER.get(), - 0, - stamp_buf, - ) - .unwrap(); - // let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]); - let sender = TmSender::new(TmPacket::new(), tgt); - let started_token = match verif_reporter.send_start_success(sendable, &sender) { - Ok(token) => token, - Err(e) => { - warn!(target: tgt, "Sending acceptance success failed: {:?}", e.0); - return; - } - }; - info!( - target: tgt, - "Received PUS ping telecommand, sending ping reply TM[17,2]" - ); - let mut sp_header = - SpHeader::tc_unseg(PUS_APID, SEQ_COUNT_PROVIDER.get(), 0).unwrap(); - let sec_header = PusTmSecondaryHeader::new_simple(17, 2, stamp_buf); - let ping_reply = PusTmCreator::new(&mut sp_header, sec_header, &[], true); - let mut tm_packet = TmPacket::new(); - tm_packet - .resize(ping_reply.len_written(), 0) - .expect("vec resize failed"); - ping_reply.write_to_bytes(&mut tm_packet).unwrap(); - if TM_REQUESTS.enqueue(tm_packet).is_err() { - warn!(target: tgt, "TC queue full"); - return; - } - SEQ_COUNT_PROVIDER.increment(); - let sendable = verif_reporter - .completion_success( - src_data_buf, - started_token, - SEQ_COUNT_PROVIDER.get(), - 0, - stamp_buf, - ) - .unwrap(); - let sender = TmSender::new(TmPacket::new(), tgt); - if let Err(e) = verif_reporter.send_step_or_completion_success(sendable, &sender) { - warn!(target: tgt, "Sending completion success failed: {:?}", e.0); - } - } else { - // TODO: Invalid subservice - } + fn handle_completion_verification( + started_token: VerificationToken, + verif_reporter: &mut VerificationReportCreator, + src_data_buf: &mut [u8], + timestamp: &[u8], + ) { + let result = send_tm( + verif_reporter + .completion_success( + src_data_buf, + started_token, + SEQ_COUNT_PROVIDER.get(), + 0, + timestamp, + ) + .unwrap(), + ); + if let Err(e) = result { + handle_tm_send_error(e); } } @@ -573,7 +634,7 @@ mod app { serial_rx_handler::spawn(tc_packet).expect("spawning rx handler task failed"); // If this happens, there is a high chance that the maximum packet length was // exceeded. Circular mode is not used here, so data might be missed. - warn!( + defmt::warn!( "rx transfer with maximum length {}, might miss data", TC_BUF_LEN ); @@ -615,7 +676,6 @@ mod app { .expect("vec resize failed"); tc_packet[0..rx_len as usize].copy_from_slice(&buf[0..rx_len as usize]); rx.clear_event(RxEvent::Idle); - info!("spawning rx task"); serial_rx_handler::spawn(tc_packet).expect("spawning rx handler failed"); *rx_transfer = Some(rx.read_exact(buf, ch)); } diff --git a/satrs-example-stm32f3-disco/vscode/extensions.json b/satrs-example-stm32f3-disco/vscode/extensions.json index 57b3fbb..b310f9b 100644 --- a/satrs-example-stm32f3-disco/vscode/extensions.json +++ b/satrs-example-stm32f3-disco/vscode/extensions.json @@ -5,7 +5,7 @@ // List of extensions which should be recommended for users of this workspace. "recommendations": [ "rust-lang.rust", - "marus25.cortex-debug", + "probe-rs.probe-rs-debugger" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] diff --git a/satrs-example-stm32f3-disco/vscode/launch.json b/satrs-example-stm32f3-disco/vscode/launch.json index f9e65f2..f982d67 100644 --- a/satrs-example-stm32f3-disco/vscode/launch.json +++ b/satrs-example-stm32f3-disco/vscode/launch.json @@ -1,66 +1,22 @@ { - /* - * Requires the Rust Language Server (RLS) and Cortex-Debug extensions - * https://marketplace.visualstudio.com/items?itemName=rust-lang.rust - * https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug - */ - "version": "0.2.0", - "configurations": [ + "version": "0.2.0", + "configurations": [ + { + "preLaunchTask": "${defaultBuildTask}", + "type": "probe-rs-debug", + "request": "launch", + "name": "probe-rs Debugging ", + "flashingConfig": { + "flashingEnabled": true + }, + "chip": "STM32F303VCTx", + "coreConfigs": [ { - /* Launches debug session for currently open example */ - "type": "cortex-debug", - "request": "launch", - "name": "Debug", - "servertype": "openocd", - "cwd": "${workspaceRoot}", - "preLaunchTask": "cargo build", - "runToEntryPoint": "true", - "executable": "./target/thumbv7em-none-eabihf/debug/satrs-example-stm32f3-disco", - "preLaunchCommands": ["break rust_begin_unwind"], - "device": "STM32F303VCT6", - "configFiles": [ - "${workspaceRoot}/.vscode/openocd-helpers.tcl", - "interface/stlink.cfg", - "target/stm32f3x.cfg" - ], - "svdFile": "${env:HOME}/.svd/STM32F303.svd", - "swoConfig": { - "enabled": true, - "cpuFrequency": 8000000, - "swoFrequency": 2000000, - "source": "probe", - "decoders": [ - { "type": "console", "label": "ITM", "port": 0 } - ] - } - }, - { - /* Launches debug session for currently open example */ - "type": "cortex-debug", - "request": "launch", - "name": "Release", - "servertype": "openocd", - "cwd": "${workspaceRoot}", - "preLaunchTask": "cargo build", - "runToEntryPoint": "true", - "executable": "./target/thumbv7em-none-eabihf/release/satrs-example-stm32f3-disco", - "preLaunchCommands": ["break rust_begin_unwind"], - "device": "STM32F303VCT6", - "configFiles": [ - "${workspaceRoot}/.vscode/openocd-helpers.tcl", - "interface/stlink.cfg", - "target/stm32f3x.cfg" - ], - "svdFile": "${env:HOME}/.svd/STM32F303.svd", - "swoConfig": { - "enabled": true, - "cpuFrequency": 8000000, - "swoFrequency": 2000000, - "source": "probe", - "decoders": [ - { "type": "console", "label": "ITM", "port": 0 } - ] - } + "programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/satrs-example-stm32f3-disco", + "rttEnabled": true, + "svdFile": "STM32F303.svd" } - ] -} \ No newline at end of file + ] + } + ] +} diff --git a/satrs-example/Cargo.toml b/satrs-example/Cargo.toml index 27ffee9..b22904b 100644 --- a/satrs-example/Cargo.toml +++ b/satrs-example/Cargo.toml @@ -17,16 +17,23 @@ zerocopy = "0.6" csv = "1" num_enum = "0.7" thiserror = "1" +lazy_static = "1" +strum = { version = "0.26", features = ["derive"] } derive-new = "0.5" +serde = { version = "1", features = ["derive"] } +serde_json = "1" [dependencies.satrs] -# version = "0.2.0-rc.0" path = "../satrs" +features = ["test_util"] [dependencies.satrs-mib] version = "0.1.1" -# path = "../satrs-mib" +path = "../satrs-mib" [features] dyn_tmtc = [] default = ["dyn_tmtc"] + +[dev-dependencies] +env_logger = "0.11" diff --git a/satrs-example/satrs-tmtc/common.py b/satrs-example/satrs-tmtc/common.py index 8f57e54..6f56604 100644 --- a/satrs-example/satrs-tmtc/common.py +++ b/satrs-example/satrs-tmtc/common.py @@ -4,11 +4,12 @@ import dataclasses import enum import struct -from spacepackets.ecss.tc import PacketId, PacketType -EXAMPLE_PUS_APID = 0x02 -EXAMPLE_PUS_PACKET_ID_TM = PacketId(PacketType.TM, True, EXAMPLE_PUS_APID) -TM_PACKET_IDS = [EXAMPLE_PUS_PACKET_ID_TM] +class Apid(enum.IntEnum): + SCHED = 1 + GENERIC_PUS = 2 + ACS = 3 + CFDP = 4 class EventSeverity(enum.IntEnum): @@ -36,8 +37,8 @@ class EventU32: ) -class RequestTargetId(enum.IntEnum): - ACS = 1 +class AcsId(enum.IntEnum): + MGM_0 = 0 class AcsHkIds(enum.IntEnum): diff --git a/satrs-example/satrs-tmtc/main.py b/satrs-example/satrs-tmtc/main.py index 66a41e4..a3e0caf 100755 --- a/satrs-example/satrs-tmtc/main.py +++ b/satrs-example/satrs-tmtc/main.py @@ -3,10 +3,11 @@ import logging import sys import time -from typing import Optional +from typing import Any, Optional from prompt_toolkit.history import History from prompt_toolkit.history import FileHistory +from spacepackets.ccsds import PacketId, PacketType import tmtccmd from spacepackets.ecss import PusTelemetry, PusVerificator from spacepackets.ecss.pus_17_test import Service17Tm @@ -16,7 +17,7 @@ from spacepackets.ccsds.time import CdsShortTimestamp from tmtccmd import TcHandlerBase, ProcedureParamsWrapper from tmtccmd.core.base import BackendRequest from tmtccmd.pus import VerificationWrapper -from tmtccmd.tmtc import CcsdsTmHandler, SpecificApidHandlerBase +from tmtccmd.tmtc import CcsdsTmHandler, GenericApidHandlerBase from tmtccmd.com import ComInterface from tmtccmd.config import ( CmdTreeNode, @@ -46,7 +47,7 @@ from tmtccmd.util.obj_id import ObjectIdDictT import pus_tc -from common import EXAMPLE_PUS_APID, TM_PACKET_IDS, EventU32 +from common import Apid, EventU32 _LOGGER = logging.getLogger() @@ -62,10 +63,13 @@ class SatRsConfigHook(HookBase): ) assert self.cfg_path is not None + packet_id_list = [] + for apid in Apid: + packet_id_list.append(PacketId(PacketType.TM, True, apid)) cfg = create_com_interface_cfg_default( com_if_key=com_if_key, json_cfg_path=self.cfg_path, - space_packet_ids=TM_PACKET_IDS, + space_packet_ids=packet_id_list, ) assert cfg is not None return create_com_interface_default(cfg) @@ -85,19 +89,19 @@ class SatRsConfigHook(HookBase): return get_core_object_ids() -class PusHandler(SpecificApidHandlerBase): +class PusHandler(GenericApidHandlerBase): def __init__( self, file_logger: logging.Logger, verif_wrapper: VerificationWrapper, raw_logger: RawTmtcTimedLogWrapper, ): - super().__init__(EXAMPLE_PUS_APID, None) + super().__init__(None) self.file_logger = file_logger self.raw_logger = raw_logger self.verif_wrapper = verif_wrapper - def handle_tm(self, packet: bytes, _user_args: any): + def handle_tm(self, apid: int, packet: bytes, _user_args: Any): try: pus_tm = PusTelemetry.unpack(packet, time_reader=CdsShortTimestamp.empty()) except ValueError as e: @@ -177,7 +181,7 @@ class TcHandler(TcHandlerBase): tc_sched_timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE, seq_cnt_provider=seq_count_provider, pus_verificator=self.verif_wrapper.pus_verificator, - default_pus_apid=EXAMPLE_PUS_APID, + default_pus_apid=None, ) def send_cb(self, send_params: SendCbParams): @@ -221,7 +225,6 @@ def main(): post_args_wrapper.set_params_without_prompts(proc_wrapper) else: post_args_wrapper.set_params_with_prompts(proc_wrapper) - params.apid = EXAMPLE_PUS_APID setup_args = SetupWrapper( hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_wrapper ) @@ -233,8 +236,9 @@ def main(): verification_wrapper = VerificationWrapper(verificator, _LOGGER, file_logger) # Create primary TM handler and add it to the CCSDS Packet Handler tm_handler = PusHandler(file_logger, verification_wrapper, raw_logger) - ccsds_handler = CcsdsTmHandler(generic_handler=None) - ccsds_handler.add_apid_handler(tm_handler) + ccsds_handler = CcsdsTmHandler(generic_handler=tm_handler) + # TODO: We could add the CFDP handler for the CFDP APID at a later stage. + # ccsds_handler.add_apid_handler(tm_handler) # Create TC handler seq_count_provider = PusFileSeqCountProvider() diff --git a/satrs-example/satrs-tmtc/pus_tc.py b/satrs-example/satrs-tmtc/pus_tc.py index f73b755..b0febdc 100644 --- a/satrs-example/satrs-tmtc/pus_tc.py +++ b/satrs-example/satrs-tmtc/pus_tc.py @@ -1,27 +1,58 @@ import datetime +import struct import logging from spacepackets.ccsds import CdsShortTimestamp from spacepackets.ecss import PusTelecommand from tmtccmd.config import CmdTreeNode +from tmtccmd.pus.tc.s200_fsfw_mode import Mode from tmtccmd.tmtc import DefaultPusQueueHelper from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd -from tmtccmd.pus.tc.s3_fsfw_hk import create_request_one_hk_command +from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice -from common import ( - EXAMPLE_PUS_APID, - make_addressable_id, - RequestTargetId, - AcsHkIds, -) +from common import AcsId, Apid _LOGGER = logging.getLogger(__name__) +def create_set_mode_cmd( + apid: int, unique_id: int, mode: int, submode: int +) -> PusTelecommand: + app_data = bytearray() + app_data.extend(struct.pack("!I", unique_id)) + app_data.extend(struct.pack("!I", mode)) + app_data.extend(struct.pack("!H", submode)) + return PusTelecommand( + service=200, + subservice=ModeSubservice.TC_MODE_COMMAND, + apid=apid, + app_data=app_data, + ) + + def create_cmd_definition_tree() -> CmdTreeNode: root_node = CmdTreeNode.root_node() + hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True) + hk_node.add_child(CmdTreeNode("one_shot_hk", "Request One Shot HK set")) + hk_node.add_child( + CmdTreeNode("enable", "Enable periodic housekeeping data generation") + ) + hk_node.add_child( + CmdTreeNode("disable", "Disable periodic housekeeping data generation") + ) + + mode_node = CmdTreeNode("mode", "Mode Node", hide_children_for_print=True) + set_mode_node = CmdTreeNode( + "set_mode", "Set Node", hide_children_which_are_leaves=True + ) + set_mode_node.add_child(CmdTreeNode("off", "Set OFF Mode")) + set_mode_node.add_child(CmdTreeNode("on", "Set ON Mode")) + set_mode_node.add_child(CmdTreeNode("normal", "Set NORMAL Mode")) + mode_node.add_child(set_mode_node) + mode_node.add_child(CmdTreeNode("read_mode", "Read Mode")) + test_node = CmdTreeNode("test", "Test Node") test_node.add_child(CmdTreeNode("ping", "Send PUS ping TC")) test_node.add_child(CmdTreeNode("trigger_event", "Send PUS test to trigger event")) @@ -37,7 +68,9 @@ def create_cmd_definition_tree() -> CmdTreeNode: acs_node = CmdTreeNode("acs", "ACS Subsystem Node") mgm_node = CmdTreeNode("mgms", "MGM devices node") - mgm_node.add_child(CmdTreeNode("one_shot_hk", "Request one shot HK")) + mgm_node.add_child(mode_node) + mgm_node.add_child(hk_node) + acs_node.add_child(mgm_node) root_node.add_child(acs_node) @@ -54,10 +87,14 @@ def pack_pus_telecommands(q: DefaultPusQueueHelper, cmd_path: str): assert len(cmd_path_list) >= 2 if cmd_path_list[1] == "ping": q.add_log_cmd("Sending PUS ping telecommand") - return q.add_pus_tc(PusTelecommand(service=17, subservice=1)) + return q.add_pus_tc( + PusTelecommand(apid=Apid.GENERIC_PUS, service=17, subservice=1) + ) elif cmd_path_list[1] == "trigger_event": q.add_log_cmd("Triggering test event") - return q.add_pus_tc(PusTelecommand(service=17, subservice=128)) + return q.add_pus_tc( + PusTelecommand(apid=Apid.GENERIC_PUS, service=17, subservice=128) + ) if cmd_path_list[0] == "scheduler": assert len(cmd_path_list) >= 2 if cmd_path_list[1] == "schedule_ping_10_secs_ahead": @@ -69,17 +106,38 @@ def pack_pus_telecommands(q: DefaultPusQueueHelper, cmd_path: str): create_time_tagged_cmd( time_stamp, PusTelecommand(service=17, subservice=1), - apid=EXAMPLE_PUS_APID, + apid=Apid.SCHED, ) ) if cmd_path_list[0] == "acs": assert len(cmd_path_list) >= 2 - if cmd_path_list[1] == "mgm": + if cmd_path_list[1] == "mgms": assert len(cmd_path_list) >= 3 - if cmd_path_list[2] == "one_shot_hk": - q.add_log_cmd("Sending HK one shot request") - q.add_pus_tc( - create_request_one_hk_command( - make_addressable_id(RequestTargetId.ACS, AcsHkIds.MGM_SET) + if cmd_path_list[2] == "hk": + if cmd_path_list[3] == "one_shot_hk": + q.add_log_cmd("Sending HK one shot request") + # TODO: Fix + # q.add_pus_tc( + # create_request_one_hk_command( + # make_addressable_id(Apid.ACS, AcsId.MGM_SET) + # ) + # ) + if cmd_path_list[2] == "mode": + if cmd_path_list[3] == "set_mode": + handle_set_mode_cmd( + q, "MGM 0", cmd_path_list[4], Apid.ACS, AcsId.MGM_0 ) - ) + + +def handle_set_mode_cmd( + q: DefaultPusQueueHelper, target_str: str, mode_str: str, apid: int, unique_id: int +): + if mode_str == "off": + q.add_log_cmd(f"Sending Mode OFF to {target_str}") + q.add_pus_tc(create_set_mode_cmd(apid, unique_id, Mode.OFF, 0)) + elif mode_str == "on": + q.add_log_cmd(f"Sending Mode ON to {target_str}") + q.add_pus_tc(create_set_mode_cmd(apid, unique_id, Mode.ON, 0)) + elif mode_str == "normal": + q.add_log_cmd(f"Sending Mode NORMAL to {target_str}") + q.add_pus_tc(create_set_mode_cmd(apid, unique_id, Mode.NORMAL, 0)) diff --git a/satrs-example/src/acs.rs b/satrs-example/src/acs.rs deleted file mode 100644 index 4fbea90..0000000 --- a/satrs-example/src/acs.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::sync::mpsc::{self, TryRecvError}; - -use log::{info, warn}; -use satrs::pus::verification::VerificationReportingProvider; -use satrs::pus::{EcssTmSender, PusTmWrapper}; -use satrs::request::TargetAndApidId; -use satrs::spacepackets::ecss::hk::Subservice as HkSubservice; -use satrs::{ - hk::HkRequest, - spacepackets::{ - ecss::tm::{PusTmCreator, PusTmSecondaryHeader}, - time::cds::{DaysLen16Bits, TimeProvider}, - SequenceFlags, SpHeader, - }, -}; -use satrs_example::config::{RequestTargetId, PUS_APID}; - -use crate::{ - hk::{AcsHkIds, HkUniqueId}, - requests::{Request, RequestWithToken}, - update_time, -}; - -pub struct AcsTask { - timestamp: [u8; 7], - time_provider: TimeProvider, - verif_reporter: VerificationReporter, - tm_sender: Box, - request_rx: mpsc::Receiver, -} - -impl AcsTask { - pub fn new( - tm_sender: impl EcssTmSender, - request_rx: mpsc::Receiver, - verif_reporter: VerificationReporter, - ) -> Self { - Self { - timestamp: [0; 7], - time_provider: TimeProvider::new_with_u16_days(0, 0), - verif_reporter, - tm_sender: Box::new(tm_sender), - request_rx, - } - } - - fn handle_hk_request(&mut self, target_id: u32, unique_id: u32) { - assert_eq!(target_id, RequestTargetId::AcsSubsystem as u32); - if unique_id == AcsHkIds::TestMgmSet as u32 { - let mut sp_header = SpHeader::tm(PUS_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); - let sec_header = PusTmSecondaryHeader::new_simple( - 3, - HkSubservice::TmHkPacket as u8, - &self.timestamp, - ); - let mut buf: [u8; 8] = [0; 8]; - let hk_id = HkUniqueId::new(target_id, unique_id); - hk_id.write_to_be_bytes(&mut buf).unwrap(); - let pus_tm = PusTmCreator::new(&mut sp_header, sec_header, &buf, true); - self.tm_sender - .send_tm(PusTmWrapper::Direct(pus_tm)) - .expect("Sending HK TM failed"); - } - // TODO: Verification failure for invalid unique IDs. - } - - pub fn try_reading_one_request(&mut self) -> bool { - match self.request_rx.try_recv() { - Ok(request) => { - info!( - "ACS thread: Received HK request {:?}", - request.targeted_request - ); - let target_and_apid_id = TargetAndApidId::from(request.targeted_request.target_id); - match request.targeted_request.request { - Request::Hk(hk_req) => match hk_req { - HkRequest::OneShot(unique_id) => { - self.handle_hk_request(target_and_apid_id.target(), unique_id) - } - HkRequest::Enable(_) => {} - HkRequest::Disable(_) => {} - HkRequest::ModifyCollectionInterval(_, _) => {} - }, - Request::Mode(_mode_req) => { - warn!("mode request handling not implemented yet") - } - Request::Action(_action_req) => { - warn!("action request handling not implemented yet") - } - } - let started_token = self - .verif_reporter - .start_success(request.token, &self.timestamp) - .expect("Sending start success failed"); - self.verif_reporter - .completion_success(started_token, &self.timestamp) - .expect("Sending completion success failed"); - true - } - Err(e) => match e { - TryRecvError::Empty => false, - TryRecvError::Disconnected => { - warn!("ACS thread: Message Queue TX disconnected!"); - false - } - }, - } - } - - pub fn periodic_operation(&mut self) { - update_time(&mut self.time_provider, &mut self.timestamp); - loop { - if !self.try_reading_one_request() { - break; - } - } - } -} diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs new file mode 100644 index 0000000..1cb7eee --- /dev/null +++ b/satrs-example/src/acs/mgm.rs @@ -0,0 +1,284 @@ +use derive_new::new; +use satrs::hk::{HkRequest, HkRequestVariant}; +use satrs::queue::{GenericSendError, GenericTargetedMessagingError}; +use satrs::spacepackets::ecss::hk; +use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; +use satrs::spacepackets::SpHeader; +use satrs_example::{DeviceMode, TimeStampHelper}; +use std::sync::mpsc::{self}; +use std::sync::{Arc, Mutex}; + +use satrs::mode::{ + ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequest, ModeRequestHandler, +}; +use satrs::pus::{EcssTmSenderCore, PusTmVariant}; +use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId}; +use satrs_example::config::components::PUS_MODE_SERVICE; + +use crate::pus::hk::{HkReply, HkReplyVariant}; +use crate::requests::CompositeRequest; + +use serde::{Deserialize, Serialize}; + +const GAUSS_TO_MICROTESLA_FACTOR: f32 = 100.0; +// This is the selected resoltion for the STM LIS3MDL device for the 4 Gauss sensitivity setting. +const FIELD_LSB_PER_GAUSS_4_SENS: f32 = 1.0 / 6842.0; + +pub trait SpiInterface { + type Error; + fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error>; +} + +#[derive(Default)] +pub struct SpiDummyInterface { + pub dummy_val_0: i16, + pub dummy_val_1: i16, + pub dummy_val_2: i16, +} + +impl SpiInterface for SpiDummyInterface { + type Error = (); + + fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> { + rx[0..2].copy_from_slice(&self.dummy_val_0.to_be_bytes()); + rx[2..4].copy_from_slice(&self.dummy_val_1.to_be_bytes()); + rx[4..6].copy_from_slice(&self.dummy_val_2.to_be_bytes()); + Ok(()) + } +} + +#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] +pub struct MgmData { + pub valid: bool, + pub x: f32, + pub y: f32, + pub z: f32, +} + +pub struct MpscModeLeafInterface { + pub request_rx: mpsc::Receiver>, + pub reply_tx_to_pus: mpsc::Sender>, + pub reply_tx_to_parent: mpsc::Sender>, +} + +/// Example MGM device handler strongly based on the LIS3MDL MEMS device. +#[derive(new)] +#[allow(clippy::too_many_arguments)] +pub struct MgmHandlerLis3Mdl { + id: UniqueApidTargetId, + dev_str: &'static str, + mode_interface: MpscModeLeafInterface, + composite_request_receiver: mpsc::Receiver>, + hk_reply_sender: mpsc::Sender>, + tm_sender: TmSender, + com_interface: ComInterface, + shared_mgm_set: Arc>, + #[new(value = "ModeAndSubmode::new(satrs_example::DeviceMode::Off as u32, 0)")] + mode_and_submode: ModeAndSubmode, + #[new(default)] + tx_buf: [u8; 12], + #[new(default)] + rx_buf: [u8; 12], + #[new(default)] + tm_buf: [u8; 16], + #[new(default)] + stamp_helper: TimeStampHelper, +} + +impl + MgmHandlerLis3Mdl +{ + pub fn periodic_operation(&mut self) { + self.stamp_helper.update_from_now(); + // Handle requests. + self.handle_composite_requests(); + self.handle_mode_requests(); + if self.mode() == DeviceMode::Normal as u32 { + log::trace!("polling LIS3MDL sensor {}", self.dev_str); + // Communicate with the device. + let result = self.com_interface.transfer(&self.tx_buf, &mut self.rx_buf); + assert!(result.is_ok()); + // Actual data begins on the second byte, similarly to how a lot of SPI devices behave. + let x_raw = i16::from_be_bytes(self.rx_buf[1..3].try_into().unwrap()); + let y_raw = i16::from_be_bytes(self.rx_buf[3..5].try_into().unwrap()); + let z_raw = i16::from_be_bytes(self.rx_buf[5..7].try_into().unwrap()); + // Simple scaling to retrieve the float value, assuming a sensor resolution of + let mut mgm_guard = self.shared_mgm_set.lock().unwrap(); + mgm_guard.x = x_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS; + mgm_guard.y = y_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS; + mgm_guard.z = z_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS; + drop(mgm_guard); + } + } + + pub fn handle_composite_requests(&mut self) { + loop { + match self.composite_request_receiver.try_recv() { + Ok(ref msg) => match &msg.message { + CompositeRequest::Hk(hk_request) => { + self.handle_hk_request(&msg.requestor_info, hk_request) + } + // TODO: This object does not have actions (yet).. Still send back completion failure + // reply. + CompositeRequest::Action(_action_req) => {} + }, + + Err(e) => { + if e != mpsc::TryRecvError::Empty { + log::warn!( + "{}: failed to receive composite request: {:?}", + self.dev_str, + e + ); + } else { + break; + } + } + } + } + } + + pub fn handle_hk_request(&mut self, requestor_info: &MessageMetadata, hk_request: &HkRequest) { + match hk_request.variant { + HkRequestVariant::OneShot => { + self.hk_reply_sender + .send(GenericMessage::new( + *requestor_info, + HkReply::new(hk_request.unique_id, HkReplyVariant::Ack), + )) + .expect("failed to send HK reply"); + let sec_header = PusTmSecondaryHeader::new( + 3, + hk::Subservice::TmHkPacket as u8, + 0, + 0, + self.stamp_helper.stamp(), + ); + let mgm_snapshot = *self.shared_mgm_set.lock().unwrap(); + // Use binary serialization here. We want the data to be tightly packed. + self.tm_buf[0] = mgm_snapshot.valid as u8; + self.tm_buf[1..5].copy_from_slice(&mgm_snapshot.x.to_be_bytes()); + self.tm_buf[5..9].copy_from_slice(&mgm_snapshot.y.to_be_bytes()); + self.tm_buf[9..13].copy_from_slice(&mgm_snapshot.z.to_be_bytes()); + let hk_tm = PusTmCreator::new( + SpHeader::new_from_apid(self.id.apid), + sec_header, + &self.tm_buf[0..12], + true, + ); + self.tm_sender + .send_tm(self.id.id(), PusTmVariant::Direct(hk_tm)) + .expect("failed to send HK TM"); + } + HkRequestVariant::EnablePeriodic => todo!(), + HkRequestVariant::DisablePeriodic => todo!(), + HkRequestVariant::ModifyCollectionInterval(_) => todo!(), + } + } + + pub fn handle_mode_requests(&mut self) { + loop { + // TODO: Only allow one set mode request per cycle? + match self.mode_interface.request_rx.try_recv() { + Ok(msg) => { + let result = self.handle_mode_request(msg); + // TODO: Trigger event? + if result.is_err() { + log::warn!( + "{}: mode request failed with error {:?}", + self.dev_str, + result.err().unwrap() + ); + } + } + Err(e) => { + if e != mpsc::TryRecvError::Empty { + log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e); + } else { + break; + } + } + } + } + } +} + +impl ModeProvider + for MgmHandlerLis3Mdl +{ + fn mode_and_submode(&self) -> ModeAndSubmode { + self.mode_and_submode + } +} + +impl ModeRequestHandler + for MgmHandlerLis3Mdl +{ + type Error = ModeError; + fn start_transition( + &mut self, + requestor: MessageMetadata, + mode_and_submode: ModeAndSubmode, + ) -> Result<(), satrs::mode::ModeError> { + log::info!( + "{}: transitioning to mode {:?}", + self.dev_str, + mode_and_submode + ); + self.mode_and_submode = mode_and_submode; + self.handle_mode_reached(Some(requestor))?; + Ok(()) + } + + fn announce_mode(&self, _requestor_info: Option, _recursive: bool) { + log::info!( + "{} announcing mode: {:?}", + self.dev_str, + self.mode_and_submode + ); + } + + fn handle_mode_reached( + &mut self, + requestor: Option, + ) -> Result<(), Self::Error> { + self.announce_mode(requestor, false); + if let Some(requestor) = requestor { + if requestor.sender_id() != PUS_MODE_SERVICE.id() { + log::warn!( + "can not send back mode reply to sender {}", + requestor.sender_id() + ); + } else { + self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode()))?; + } + } + Ok(()) + } + + fn send_mode_reply( + &self, + requestor: MessageMetadata, + reply: ModeReply, + ) -> Result<(), Self::Error> { + if requestor.sender_id() != PUS_MODE_SERVICE.id() { + log::warn!( + "can not send back mode reply to sender {}", + requestor.sender_id() + ); + } + self.mode_interface + .reply_tx_to_pus + .send(GenericMessage::new(requestor, reply)) + .map_err(|_| GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected))?; + Ok(()) + } + + fn handle_mode_info( + &mut self, + _requestor_info: MessageMetadata, + _info: ModeAndSubmode, + ) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/satrs-example/src/acs/mod.rs b/satrs-example/src/acs/mod.rs new file mode 100644 index 0000000..bd61e8b --- /dev/null +++ b/satrs-example/src/acs/mod.rs @@ -0,0 +1 @@ +pub mod mgm; diff --git a/satrs-example/src/bin/simpleclient.rs b/satrs-example/src/bin/simpleclient.rs index 04281ca..bbe7609 100644 --- a/satrs-example/src/bin/simpleclient.rs +++ b/satrs-example/src/bin/simpleclient.rs @@ -12,8 +12,7 @@ use std::time::Duration; fn main() { let mut buf = [0; 32]; let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); - let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap(); - let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let pus_tc = PusTcCreator::new_simple(SpHeader::new_from_apid(0x02), 17, 1, &[], true); let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed"); let tc_req_id = RequestId::new(&pus_tc); println!("Packing and sending PUS ping command TC[17,1] with request ID {tc_req_id}"); diff --git a/satrs-example/src/ccsds.rs b/satrs-example/src/ccsds.rs index e61172e..1841d17 100644 --- a/satrs-example/src/ccsds.rs +++ b/satrs-example/src/ccsds.rs @@ -1,7 +1,9 @@ use satrs::pus::ReceivesEcssPusTc; use satrs::spacepackets::{CcsdsPacket, SpHeader}; use satrs::tmtc::{CcsdsPacketHandler, ReceivesCcsdsTc}; -use satrs_example::config::PUS_APID; +use satrs::ValidatorU16Id; +use satrs_example::config::components::Apid; +use satrs_example::config::APID_VALIDATOR; #[derive(Clone)] pub struct CcsdsReceiver< @@ -11,6 +13,16 @@ pub struct CcsdsReceiver< pub tc_source: TcSource, } +impl< + TcSource: ReceivesCcsdsTc + ReceivesEcssPusTc + Clone + 'static, + E: 'static, + > ValidatorU16Id for CcsdsReceiver +{ + fn validate(&self, apid: u16) -> bool { + APID_VALIDATOR.contains(&apid) + } +} + impl< TcSource: ReceivesCcsdsTc + ReceivesEcssPusTc + Clone + 'static, E: 'static, @@ -18,27 +30,24 @@ impl< { type Error = E; - fn valid_apids(&self) -> &'static [u16] { - &[PUS_APID] - } - - fn handle_known_apid( + fn handle_packet_with_valid_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], ) -> Result<(), Self::Error> { - if sp_header.apid() == PUS_APID { + if sp_header.apid() == Apid::Cfdp as u16 { + } else { return self.tc_source.pass_ccsds(sp_header, tc_raw); } Ok(()) } - fn handle_unknown_apid( + fn handle_packet_with_unknown_apid( &mut self, sp_header: &SpHeader, _tc_raw: &[u8], ) -> Result<(), Self::Error> { - println!("Unknown APID 0x{:x?} detected", sp_header.apid()); + log::warn!("unknown APID 0x{:x?} detected", sp_header.apid()); Ok(()) } } diff --git a/satrs-example/src/config.rs b/satrs-example/src/config.rs index 9d04403..7e474e9 100644 --- a/satrs-example/src/config.rs +++ b/satrs-example/src/config.rs @@ -1,7 +1,12 @@ -use satrs::res_code::ResultU16; +use lazy_static::lazy_static; +use satrs::{ + res_code::ResultU16, + spacepackets::{PacketId, PacketType}, +}; use satrs_mib::res_code::ResultU16Info; use satrs_mib::resultcode; -use std::net::Ipv4Addr; +use std::{collections::HashSet, net::Ipv4Addr}; +use strum::IntoEnumIterator; use num_enum::{IntoPrimitive, TryFromPrimitive}; use satrs::{ @@ -9,8 +14,6 @@ use satrs::{ pool::{StaticMemoryPool, StaticPoolConfig}, }; -pub const PUS_APID: u16 = 0x02; - #[derive(Copy, Clone, PartialEq, Eq, Debug, TryFromPrimitive, IntoPrimitive)] #[repr(u8)] pub enum CustomPusServiceId { @@ -29,6 +32,7 @@ pub const AOCS_APID: u16 = 1; pub enum GroupId { Tmtc = 0, Hk = 1, + Mode = 2, } pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED; @@ -37,6 +41,23 @@ pub const SERVER_PORT: u16 = 7301; pub const TEST_EVENT: EventU32TypedSev = EventU32TypedSev::::const_new(0, 0); +lazy_static! { + pub static ref PACKET_ID_VALIDATOR: HashSet = { + let mut set = HashSet::new(); + for id in components::Apid::iter() { + set.insert(PacketId::new(PacketType::Tc, true, id as u16)); + } + set + }; + pub static ref APID_VALIDATOR: HashSet = { + let mut set = HashSet::new(); + for id in components::Apid::iter() { + set.insert(id as u16); + } + set + }; +} + pub mod tmtc_err { use super::*; @@ -53,6 +74,8 @@ pub mod tmtc_err { pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 4); #[resultcode] pub const ROUTING_ERROR: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 5); + #[resultcode(info = "Request timeout for targeted PUS request. P1: Request ID. P2: Target ID")] + pub const REQUEST_TIMEOUT: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 6); #[resultcode( info = "Not enough data inside the TC application data field. Optionally includes: \ @@ -92,27 +115,59 @@ pub mod hk_err { ]; } -#[allow(clippy::enum_variant_names)] -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum TmSenderId { - PusVerification = 0, - PusTest = 1, - PusEvent = 2, - PusHk = 3, - PusAction = 4, - PusSched = 5, - AllEvents = 6, - AcsSubsystem = 7, +pub mod mode_err { + use super::*; + + #[resultcode] + pub const WRONG_MODE: ResultU16 = ResultU16::new(GroupId::Mode as u8, 0); } -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum TcReceiverId { - PusTest = 1, - PusEvent = 2, - PusHk = 3, - PusAction = 4, - PusSched = 5, +pub mod components { + use satrs::request::UniqueApidTargetId; + use strum::EnumIter; + + #[derive(Copy, Clone, PartialEq, Eq, EnumIter)] + pub enum Apid { + Sched = 1, + GenericPus = 2, + Acs = 3, + Cfdp = 4, + } + + // Component IDs for components with the PUS APID. + #[derive(Copy, Clone, PartialEq, Eq)] + pub enum PusId { + PusEventManagement = 0, + PusRouting = 1, + PusTest = 2, + PusAction = 3, + PusMode = 4, + PusHk = 5, + } + + #[derive(Copy, Clone, PartialEq, Eq)] + pub enum AcsId { + Mgm0 = 0, + } + + pub const PUS_ACTION_SERVICE: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusAction as u32); + pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::GenericPus as u16, 0); + pub const PUS_ROUTING_SERVICE: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusRouting as u32); + pub const PUS_TEST_SERVICE: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusTest as u32); + pub const PUS_MODE_SERVICE: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusMode as u32); + pub const PUS_HK_SERVICE: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusHk as u32); + pub const PUS_SCHED_SERVICE: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::Sched as u16, 0); + pub const MGM_HANDLER_0: UniqueApidTargetId = + UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32); } + pub mod pool { use super::*; pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) { diff --git a/satrs-example/src/events.rs b/satrs-example/src/events.rs index a6a0d86..4d7ea9f 100644 --- a/satrs-example/src/events.rs +++ b/satrs-example/src/events.rs @@ -1,66 +1,87 @@ use std::sync::mpsc::{self}; +use crate::pus::create_verification_reporter; +use satrs::event_man::{EventMessageU32, EventRoutingError}; +use satrs::params::WritableToBeBytes; +use satrs::pus::event::EventTmHookProvider; +use satrs::pus::verification::VerificationReporter; +use satrs::pus::EcssTmSenderCore; +use satrs::request::UniqueApidTargetId; use satrs::{ event_man::{ EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded, MpscEventReceiver, }, - events::EventU32, - params::Params, pus::{ event_man::{ DefaultPusEventU32Dispatcher, EventReporter, EventRequest, EventRequestWithToken, }, verification::{TcStateStarted, VerificationReportingProvider, VerificationToken}, - EcssTmSender, }, - spacepackets::time::cds::{self, TimeProvider}, + spacepackets::time::cds::CdsTime, }; -use satrs_example::config::PUS_APID; +use satrs_example::config::components::PUS_EVENT_MANAGEMENT; use crate::update_time; -pub struct PusEventHandler { +// This helper sets the APID of the event sender for the PUS telemetry. +#[derive(Default)] +pub struct EventApidSetter { + pub next_apid: u16, +} + +impl EventTmHookProvider for EventApidSetter { + fn modify_tm(&self, tm: &mut satrs::spacepackets::ecss::tm::PusTmCreator) { + tm.set_apid(self.next_apid); + } +} + +/// The PUS event handler subscribes for all events and converts them into ECSS PUS 5 event +/// packets. It also handles the verification completion of PUS event service requests. +pub struct PusEventHandler { event_request_rx: mpsc::Receiver, pus_event_dispatcher: DefaultPusEventU32Dispatcher<()>, - pus_event_man_rx: mpsc::Receiver<(EventU32, Option)>, - tm_sender: Box, - time_provider: TimeProvider, + pus_event_man_rx: mpsc::Receiver, + tm_sender: TmSender, + time_provider: CdsTime, timestamp: [u8; 7], verif_handler: VerificationReporter, + event_apid_setter: EventApidSetter, } -/* -*/ -impl PusEventHandler { +impl PusEventHandler { pub fn new( + tm_sender: TmSender, verif_handler: VerificationReporter, event_manager: &mut EventManagerWithBoundedMpsc, event_request_rx: mpsc::Receiver, - tm_sender: impl EcssTmSender, ) -> Self { let event_queue_cap = 30; let (pus_event_man_tx, pus_event_man_rx) = mpsc::sync_channel(event_queue_cap); // All events sent to the manager are routed to the PUS event manager, which generates PUS event // telemetry for each event. - let event_reporter = EventReporter::new(PUS_APID, 128).unwrap(); + let event_reporter = EventReporter::new(PUS_EVENT_MANAGEMENT.raw(), 0, 0, 128).unwrap(); let pus_event_dispatcher = DefaultPusEventU32Dispatcher::new_with_default_backend(event_reporter); - let pus_event_man_send_provider = - EventU32SenderMpscBounded::new(1, pus_event_man_tx, event_queue_cap); + let pus_event_man_send_provider = EventU32SenderMpscBounded::new( + PUS_EVENT_MANAGEMENT.raw(), + pus_event_man_tx, + event_queue_cap, + ); - event_manager.subscribe_all(pus_event_man_send_provider.channel_id()); + event_manager.subscribe_all(pus_event_man_send_provider.target_id()); event_manager.add_sender(pus_event_man_send_provider); Self { event_request_rx, pus_event_dispatcher, pus_event_man_rx, - time_provider: cds::TimeProvider::new_with_u16_days(0, 0), + time_provider: CdsTime::new_with_u16_days(0, 0), timestamp: [0; 7], verif_handler, - tm_sender: Box::new(tm_sender), + tm_sender, + event_apid_setter: EventApidSetter::default(), } } @@ -71,7 +92,7 @@ impl PusEventHandler PusEventHandler)>, + event_sender: mpsc::Sender, } impl EventManagerWrapper { @@ -121,14 +148,15 @@ impl EventManagerWrapper { // The sender handle is the primary sender handle for all components which want to create events. // The event manager will receive the RX handle to receive all the events. let (event_sender, event_man_rx) = mpsc::channel(); - let event_recv = MpscEventReceiver::::new(event_man_rx); + let event_recv = MpscEventReceiver::new(event_man_rx); Self { event_manager: EventManagerWithBoundedMpsc::new(event_recv), event_sender, } } - pub fn clone_event_sender(&self) -> mpsc::Sender<(EventU32, Option)> { + // Returns a cached event sender to send events to the event manager for routing. + pub fn clone_event_sender(&self) -> mpsc::Sender { self.event_sender.clone() } @@ -137,30 +165,34 @@ impl EventManagerWrapper { } pub fn try_event_routing(&mut self) { + let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| { + self.routing_error_handler(event_msg, error) + }; // Perform the event routing. - self.event_manager - .try_event_handling() - .expect("event handling failed"); + self.event_manager.try_event_handling(error_handler); + } + + pub fn routing_error_handler(&self, event_msg: &EventMessageU32, error: EventRoutingError) { + log::warn!("event routing error for event {event_msg:?}: {error:?}"); } } -pub struct EventHandler { +pub struct EventHandler { pub event_man_wrapper: EventManagerWrapper, - pub pus_event_handler: PusEventHandler, + pub pus_event_handler: PusEventHandler, } -impl EventHandler { +impl EventHandler { pub fn new( - tm_sender: impl EcssTmSender, - verif_handler: VerificationReporter, + tm_sender: TmSender, event_request_rx: mpsc::Receiver, ) -> Self { let mut event_man_wrapper = EventManagerWrapper::new(); let pus_event_handler = PusEventHandler::new( - verif_handler, + tm_sender, + create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid), event_man_wrapper.event_manager(), event_request_rx, - tm_sender, ); Self { event_man_wrapper, @@ -168,7 +200,7 @@ impl EventHandler mpsc::Sender<(EventU32, Option)> { + pub fn clone_event_sender(&self) -> mpsc::Sender { self.event_man_wrapper.clone_event_sender() } diff --git a/satrs-example/src/hk.rs b/satrs-example/src/hk.rs index 3147cbf..0852d04 100644 --- a/satrs-example/src/hk.rs +++ b/satrs-example/src/hk.rs @@ -1,27 +1,25 @@ use derive_new::new; +use satrs::hk::UniqueId; +use satrs::request::UniqueApidTargetId; use satrs::spacepackets::ByteConversionError; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum AcsHkIds { - TestMgmSet = 1, -} - #[derive(Debug, new, Copy, Clone)] pub struct HkUniqueId { - target_id: u32, - set_id: u32, + target_id: UniqueApidTargetId, + set_id: UniqueId, } impl HkUniqueId { #[allow(dead_code)] - pub fn target_id(&self) -> u32 { + pub fn target_id(&self) -> UniqueApidTargetId { self.target_id } #[allow(dead_code)] - pub fn set_id(&self) -> u32 { + pub fn set_id(&self) -> UniqueId { self.set_id } + #[allow(dead_code)] pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { if buf.len() < 8 { return Err(ByteConversionError::ToSliceTooSmall { @@ -29,7 +27,7 @@ impl HkUniqueId { expected: 8, }); } - buf[0..4].copy_from_slice(&self.target_id.to_be_bytes()); + buf[0..4].copy_from_slice(&self.target_id.unique_id.to_be_bytes()); buf[4..8].copy_from_slice(&self.set_id.to_be_bytes()); Ok(8) diff --git a/satrs-example/src/lib.rs b/satrs-example/src/lib.rs index ef68c36..a224fe5 100644 --- a/satrs-example/src/lib.rs +++ b/satrs-example/src/lib.rs @@ -1 +1,39 @@ +use satrs::spacepackets::time::{cds::CdsTime, TimeWriter}; + pub mod config; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum DeviceMode { + Off = 0, + On = 1, + Normal = 2, +} + +pub struct TimeStampHelper { + stamper: CdsTime, + time_stamp: [u8; 7], +} + +impl TimeStampHelper { + pub fn stamp(&self) -> &[u8] { + &self.time_stamp + } + + pub fn update_from_now(&mut self) { + self.stamper + .update_from_now() + .expect("Updating timestamp failed"); + self.stamper + .write_to_bytes(&mut self.time_stamp) + .expect("Writing timestamp failed"); + } +} + +impl Default for TimeStampHelper { + fn default() -> Self { + Self { + stamper: CdsTime::now_with_u16_days().expect("creating time stamper failed"), + time_stamp: Default::default(), + } + } +} diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index 0cdd462..a6456d6 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -17,52 +17,44 @@ use log::info; use pus::test::create_test_service_dynamic; use satrs::hal::std::tcp_server::ServerConfig; use satrs::hal::std::udp_server::UdpTcServer; -use satrs::request::TargetAndApidId; +use satrs::request::GenericMessage; use satrs::tmtc::tm_helper::SharedTmPool; use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools}; use satrs_example::config::tasks::{ FREQ_MS_AOCS, FREQ_MS_EVENT_HANDLING, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC, }; -use satrs_example::config::{RequestTargetId, TmSenderId, OBSW_SERVER_ADDR, PUS_APID, SERVER_PORT}; +use satrs_example::config::{OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT}; use tmtc::PusTcSourceProviderDynamic; use udp::DynamicUdpTmHandler; -use crate::acs::AcsTask; +use crate::acs::mgm::{MgmHandlerLis3Mdl, MpscModeLeafInterface, SpiDummyInterface}; use crate::ccsds::CcsdsReceiver; use crate::logger::setup_logger; use crate::pus::action::{create_action_service_dynamic, create_action_service_static}; use crate::pus::event::{create_event_service_dynamic, create_event_service_static}; use crate::pus::hk::{create_hk_service_dynamic, create_hk_service_static}; +use crate::pus::mode::{create_mode_service_dynamic, create_mode_service_static}; use crate::pus::scheduler::{create_scheduler_service_dynamic, create_scheduler_service_static}; use crate::pus::test::create_test_service_static; use crate::pus::{PusReceiver, PusTcMpscRouter}; -use crate::requests::{GenericRequestRouter, RequestWithToken}; +use crate::requests::{CompositeRequest, GenericRequestRouter}; use crate::tcp::{SyncTcpTmSource, TcpTask}; use crate::tmtc::{ PusTcSourceProviderSharedPool, SharedTcPool, TcSourceTaskDynamic, TcSourceTaskStatic, }; use crate::udp::{StaticUdpTmHandler, UdpTmtcServer}; +use satrs::mode::ModeRequest; use satrs::pus::event_man::EventRequestWithToken; -use satrs::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender}; -use satrs::pus::{EcssTmSender, TmAsVecSenderWithId, TmInSharedPoolSenderWithId}; -use satrs::spacepackets::{time::cds::TimeProvider, time::TimeWriter}; +use satrs::pus::TmInSharedPoolSender; +use satrs::spacepackets::{time::cds::CdsTime, time::TimeWriter}; use satrs::tmtc::CcsdsDistributor; -use satrs::ChannelId; +use satrs_example::config::components::MGM_HANDLER_0; use std::net::{IpAddr, SocketAddr}; -use std::sync::mpsc::{self, channel}; +use std::sync::mpsc; use std::sync::{Arc, RwLock}; use std::thread; use std::time::Duration; -fn create_verification_reporter( - verif_sender: Sender, -) -> VerificationReporterWithSender { - let verif_cfg = VerificationReporterCfg::new(PUS_APID, 1, 2, 8).unwrap(); - // Every software component which needs to generate verification telemetry, gets a cloned - // verification reporter. - VerificationReporterWithSender::new(&verif_cfg, verif_sender) -} - #[allow(dead_code)] fn static_tmtc_pool_main() { let (tm_pool, tc_pool) = create_static_pools(); @@ -74,20 +66,21 @@ fn static_tmtc_pool_main() { let (tm_funnel_tx, tm_funnel_rx) = mpsc::sync_channel(50); let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50); - // Every software component which needs to generate verification telemetry, receives a cloned - // verification reporter. - let verif_reporter = create_verification_reporter(TmInSharedPoolSenderWithId::new( - TmSenderId::PusVerification as ChannelId, - "verif_sender", - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - )); + let tm_funnel_tx_sender = + TmInSharedPoolSender::new(shared_tm_pool.clone(), tm_funnel_tx.clone()); + + let (mgm_handler_composite_tx, mgm_handler_composite_rx) = + mpsc::channel::>(); + let (mgm_handler_mode_tx, mgm_handler_mode_rx) = mpsc::channel::>(); - let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32); - let (acs_thread_tx, acs_thread_rx) = channel::(); // Some request are targetable. This map is used to retrieve sender handles based on a target ID. let mut request_map = GenericRequestRouter::default(); - request_map.0.insert(acs_target_id.into(), acs_thread_tx); + request_map + .composite_router_map + .insert(MGM_HANDLER_0.id(), mgm_handler_composite_tx); + request_map + .mode_router_map + .insert(MGM_HANDLER_0.id(), mgm_handler_mode_tx); // This helper structure is used by all telecommand providers which need to send telecommands // to the TC source. @@ -103,82 +96,80 @@ fn static_tmtc_pool_main() { // The event task is the core handler to perform the event routing and TM handling as specified // in the sat-rs documentation. - let mut event_handler = EventHandler::new( - TmInSharedPoolSenderWithId::new( - TmSenderId::AllEvents as ChannelId, - "ALL_EVENTS_TX", - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - ), - verif_reporter.clone(), - event_request_rx, - ); + let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_request_rx); + + let (pus_test_tx, pus_test_rx) = mpsc::channel(); + let (pus_event_tx, pus_event_rx) = mpsc::channel(); + let (pus_sched_tx, pus_sched_rx) = mpsc::channel(); + let (pus_hk_tx, pus_hk_rx) = mpsc::channel(); + let (pus_action_tx, pus_action_rx) = mpsc::channel(); + let (pus_mode_tx, pus_mode_rx) = mpsc::channel(); + + let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel(); + let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::channel(); + let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel(); - let (pus_test_tx, pus_test_rx) = channel(); - let (pus_event_tx, pus_event_rx) = channel(); - let (pus_sched_tx, pus_sched_rx) = channel(); - let (pus_hk_tx, pus_hk_rx) = channel(); - let (pus_action_tx, pus_action_rx) = channel(); let pus_router = PusTcMpscRouter { - test_service_receiver: pus_test_tx, - event_service_receiver: pus_event_tx, - sched_service_receiver: pus_sched_tx, - hk_service_receiver: pus_hk_tx, - action_service_receiver: pus_action_tx, + test_tc_sender: pus_test_tx, + event_tc_sender: pus_event_tx, + sched_tc_sender: pus_sched_tx, + hk_tc_sender: pus_hk_tx, + action_tc_sender: pus_action_tx, + mode_tc_sender: pus_mode_tx, }; let pus_test_service = create_test_service_static( - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - verif_reporter.clone(), + tm_funnel_tx_sender.clone(), shared_tc_pool.pool.clone(), event_handler.clone_event_sender(), pus_test_rx, ); let pus_scheduler_service = create_scheduler_service_static( - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - verif_reporter.clone(), + tm_funnel_tx_sender.clone(), tc_source.clone(), pus_sched_rx, create_sched_tc_pool(), ); let pus_event_service = create_event_service_static( - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - verif_reporter.clone(), + tm_funnel_tx_sender.clone(), shared_tc_pool.pool.clone(), pus_event_rx, event_request_tx, ); let pus_action_service = create_action_service_static( - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - verif_reporter.clone(), + tm_funnel_tx_sender.clone(), shared_tc_pool.pool.clone(), pus_action_rx, request_map.clone(), + pus_action_reply_rx, ); let pus_hk_service = create_hk_service_static( - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - verif_reporter.clone(), + tm_funnel_tx_sender.clone(), shared_tc_pool.pool.clone(), pus_hk_rx, + request_map.clone(), + pus_hk_reply_rx, + ); + let pus_mode_service = create_mode_service_static( + tm_funnel_tx_sender.clone(), + shared_tc_pool.pool.clone(), + pus_mode_rx, request_map, + pus_mode_reply_rx, ); let mut pus_stack = PusStack::new( + pus_test_service, pus_hk_service, pus_event_service, pus_action_service, pus_scheduler_service, - pus_test_service, + pus_mode_service, ); let ccsds_receiver = CcsdsReceiver { tc_source }; let mut tmtc_task = TcSourceTaskStatic::new( shared_tc_pool.clone(), tc_source_rx, - PusReceiver::new(verif_reporter.clone(), pus_router), + PusReceiver::new(tm_funnel_tx_sender, pus_router), ); let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); @@ -200,20 +191,10 @@ fn static_tmtc_pool_main() { tcp_server_cfg, sync_tm_tcp_source.clone(), tcp_ccsds_distributor, + PACKET_ID_VALIDATOR.clone(), ) .expect("tcp server creation failed"); - let mut acs_task = AcsTask::new( - TmInSharedPoolSenderWithId::new( - TmSenderId::AcsSubsystem as ChannelId, - "ACS_TASK_SENDER", - shared_tm_pool.clone(), - tm_funnel_tx.clone(), - ), - acs_thread_rx, - verif_reporter, - ); - let mut tm_funnel = TmFunnelStatic::new( shared_tm_pool, sync_tm_tcp_source, @@ -221,6 +202,27 @@ fn static_tmtc_pool_main() { tm_server_tx, ); + let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) = + mpsc::channel(); + + let dummy_spi_interface = SpiDummyInterface::default(); + let shared_mgm_set = Arc::default(); + let mode_leaf_interface = MpscModeLeafInterface { + request_rx: mgm_handler_mode_rx, + reply_tx_to_pus: pus_mode_reply_tx, + reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx, + }; + let mut mgm_handler = MgmHandlerLis3Mdl::new( + MGM_HANDLER_0, + "MGM_0", + mode_leaf_interface, + mgm_handler_composite_rx, + pus_hk_reply_tx, + tm_funnel_tx, + dummy_spi_interface, + shared_mgm_set, + ); + info!("Starting TMTC and UDP task"); let jh_udp_tmtc = thread::Builder::new() .name("TMTC and UDP".to_string()) @@ -266,7 +268,7 @@ fn static_tmtc_pool_main() { let jh_aocs = thread::Builder::new() .name("AOCS".to_string()) .spawn(move || loop { - acs_task.periodic_operation(); + mgm_handler.periodic_operation(); thread::sleep(Duration::from_millis(FREQ_MS_AOCS)); }) .unwrap(); @@ -300,22 +302,23 @@ fn static_tmtc_pool_main() { #[allow(dead_code)] fn dyn_tmtc_pool_main() { - let (tc_source_tx, tc_source_rx) = channel(); - let (tm_funnel_tx, tm_funnel_rx) = channel(); - let (tm_server_tx, tm_server_rx) = channel(); - // Every software component which needs to generate verification telemetry, gets a cloned - // verification reporter. - let verif_reporter = create_verification_reporter(TmAsVecSenderWithId::new( - TmSenderId::PusVerification as ChannelId, - "verif_sender", - tm_funnel_tx.clone(), - )); + let (tc_source_tx, tc_source_rx) = mpsc::channel(); + let (tm_funnel_tx, tm_funnel_rx) = mpsc::channel(); + let (tm_server_tx, tm_server_rx) = mpsc::channel(); + + // Some request are targetable. This map is used to retrieve sender handles based on a target ID. + let (mgm_handler_composite_tx, mgm_handler_composite_rx) = + mpsc::channel::>(); + let (mgm_handler_mode_tx, mgm_handler_mode_rx) = mpsc::channel::>(); - let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32); - let (acs_thread_tx, acs_thread_rx) = channel::(); // Some request are targetable. This map is used to retrieve sender handles based on a target ID. let mut request_map = GenericRequestRouter::default(); - request_map.0.insert(acs_target_id.into(), acs_thread_tx); + request_map + .composite_router_map + .insert(MGM_HANDLER_0.raw(), mgm_handler_composite_tx); + request_map + .mode_router_map + .insert(MGM_HANDLER_0.raw(), mgm_handler_mode_tx); let tc_source = PusTcSourceProviderDynamic(tc_source_tx); @@ -325,74 +328,74 @@ fn dyn_tmtc_pool_main() { let (event_request_tx, event_request_rx) = mpsc::channel::(); // The event task is the core handler to perform the event routing and TM handling as specified // in the sat-rs documentation. - let mut event_handler = EventHandler::new( - TmAsVecSenderWithId::new( - TmSenderId::AllEvents as ChannelId, - "ALL_EVENTS_TX", - tm_funnel_tx.clone(), - ), - verif_reporter.clone(), - event_request_rx, - ); + let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_request_rx); + + let (pus_test_tx, pus_test_rx) = mpsc::channel(); + let (pus_event_tx, pus_event_rx) = mpsc::channel(); + let (pus_sched_tx, pus_sched_rx) = mpsc::channel(); + let (pus_hk_tx, pus_hk_rx) = mpsc::channel(); + let (pus_action_tx, pus_action_rx) = mpsc::channel(); + let (pus_mode_tx, pus_mode_rx) = mpsc::channel(); + + let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel(); + let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::channel(); + let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel(); - let (pus_test_tx, pus_test_rx) = channel(); - let (pus_event_tx, pus_event_rx) = channel(); - let (pus_sched_tx, pus_sched_rx) = channel(); - let (pus_hk_tx, pus_hk_rx) = channel(); - let (pus_action_tx, pus_action_rx) = channel(); let pus_router = PusTcMpscRouter { - test_service_receiver: pus_test_tx, - event_service_receiver: pus_event_tx, - sched_service_receiver: pus_sched_tx, - hk_service_receiver: pus_hk_tx, - action_service_receiver: pus_action_tx, + test_tc_sender: pus_test_tx, + event_tc_sender: pus_event_tx, + sched_tc_sender: pus_sched_tx, + hk_tc_sender: pus_hk_tx, + action_tc_sender: pus_action_tx, + mode_tc_sender: pus_mode_tx, }; let pus_test_service = create_test_service_dynamic( tm_funnel_tx.clone(), - verif_reporter.clone(), event_handler.clone_event_sender(), pus_test_rx, ); let pus_scheduler_service = create_scheduler_service_dynamic( tm_funnel_tx.clone(), - verif_reporter.clone(), tc_source.0.clone(), pus_sched_rx, create_sched_tc_pool(), ); - let pus_event_service = create_event_service_dynamic( - tm_funnel_tx.clone(), - verif_reporter.clone(), - pus_event_rx, - event_request_tx, - ); + let pus_event_service = + create_event_service_dynamic(tm_funnel_tx.clone(), pus_event_rx, event_request_tx); let pus_action_service = create_action_service_dynamic( tm_funnel_tx.clone(), - verif_reporter.clone(), pus_action_rx, request_map.clone(), + pus_action_reply_rx, ); let pus_hk_service = create_hk_service_dynamic( tm_funnel_tx.clone(), - verif_reporter.clone(), pus_hk_rx, + request_map.clone(), + pus_hk_reply_rx, + ); + let pus_mode_service = create_mode_service_dynamic( + tm_funnel_tx.clone(), + pus_mode_rx, request_map, + pus_mode_reply_rx, ); let mut pus_stack = PusStack::new( + pus_test_service, pus_hk_service, pus_event_service, pus_action_service, pus_scheduler_service, - pus_test_service, + pus_mode_service, ); let ccsds_receiver = CcsdsReceiver { tc_source }; let mut tmtc_task = TcSourceTaskDynamic::new( tc_source_rx, - PusReceiver::new(verif_reporter.clone(), pus_router), + PusReceiver::new(tm_funnel_tx.clone(), pus_router), ); let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); @@ -413,20 +416,32 @@ fn dyn_tmtc_pool_main() { tcp_server_cfg, sync_tm_tcp_source.clone(), tcp_ccsds_distributor, + PACKET_ID_VALIDATOR.clone(), ) .expect("tcp server creation failed"); - let mut acs_task = AcsTask::new( - TmAsVecSenderWithId::new( - TmSenderId::AcsSubsystem as ChannelId, - "ACS_TASK_SENDER", - tm_funnel_tx.clone(), - ), - acs_thread_rx, - verif_reporter, - ); let mut tm_funnel = TmFunnelDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx); + let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) = + mpsc::channel(); + let dummy_spi_interface = SpiDummyInterface::default(); + let shared_mgm_set = Arc::default(); + let mode_leaf_interface = MpscModeLeafInterface { + request_rx: mgm_handler_mode_rx, + reply_tx_to_pus: pus_mode_reply_tx, + reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx, + }; + let mut mgm_handler = MgmHandlerLis3Mdl::new( + MGM_HANDLER_0, + "MGM_0", + mode_leaf_interface, + mgm_handler_composite_rx, + pus_hk_reply_tx, + tm_funnel_tx, + dummy_spi_interface, + shared_mgm_set, + ); + info!("Starting TMTC and UDP task"); let jh_udp_tmtc = thread::Builder::new() .name("TMTC and UDP".to_string()) @@ -472,7 +487,7 @@ fn dyn_tmtc_pool_main() { let jh_aocs = thread::Builder::new() .name("AOCS".to_string()) .spawn(move || loop { - acs_task.periodic_operation(); + mgm_handler.periodic_operation(); thread::sleep(Duration::from_millis(FREQ_MS_AOCS)); }) .unwrap(); @@ -513,7 +528,7 @@ fn main() { dyn_tmtc_pool_main(); } -pub fn update_time(time_provider: &mut TimeProvider, timestamp: &mut [u8]) { +pub fn update_time(time_provider: &mut CdsTime, timestamp: &mut [u8]) { time_provider .update_from_now() .expect("Could not get current time"); diff --git a/satrs-example/src/pus/action.rs b/satrs-example/src/pus/action.rs index ef23786..22b6b93 100644 --- a/satrs-example/src/pus/action.rs +++ b/satrs-example/src/pus/action.rs @@ -1,181 +1,274 @@ use log::{error, warn}; -use satrs::action::ActionRequest; -use satrs::pool::{SharedStaticMemoryPool, StoreAddr}; -use satrs::pus::action::{PusActionToRequestConverter, PusService8ActionHandler}; -use satrs::pus::verification::std_mod::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, +use satrs::action::{ActionRequest, ActionRequestVariant}; +use satrs::params::WritableToBeBytes; +use satrs::pool::SharedStaticMemoryPool; +use satrs::pus::action::{ + ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap, PusActionReply, }; use satrs::pus::verification::{ - FailParams, TcStateAccepted, VerificationReportingProvider, VerificationToken, + FailParams, FailParamsWithStep, TcStateAccepted, TcStateStarted, VerificationReporter, + VerificationReportingProvider, VerificationToken, }; use satrs::pus::{ - EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult, - PusPacketHandlingError, PusServiceHelper, TmAsVecSenderWithId, TmAsVecSenderWithMpsc, - TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithId, + ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, + EcssTcInVecConverter, EcssTmSenderCore, EcssTmtcError, GenericConversionError, MpscTcReceiver, + MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult, PusReplyHandler, + PusServiceHelper, PusTcToRequestConverter, PusTmAsVec, PusTmInPool, TmInSharedPoolSender, }; -use satrs::request::TargetAndApidId; +use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::spacepackets::ecss::tc::PusTcReader; -use satrs::spacepackets::ecss::PusPacket; -use satrs::tmtc::tm_helper::SharedTmPool; -use satrs::{ChannelId, TargetId}; -use satrs_example::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID}; -use std::sync::mpsc::{self}; +use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket}; +use satrs_example::config::components::PUS_ACTION_SERVICE; +use satrs_example::config::tmtc_err; +use std::sync::mpsc; +use std::time::Duration; use crate::requests::GenericRequestRouter; -use super::GenericRoutingErrorHandler; +use super::{ + create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus, + PusTargetedRequestService, TargetedPusService, +}; + +pub struct ActionReplyHandler { + fail_data_buf: [u8; 128], +} + +impl Default for ActionReplyHandler { + fn default() -> Self { + Self { + fail_data_buf: [0; 128], + } + } +} + +impl PusReplyHandler for ActionReplyHandler { + type Error = EcssTmtcError; + + fn handle_unrequested_reply( + &mut self, + reply: &GenericMessage, + _tm_sender: &impl EcssTmSenderCore, + ) -> Result<(), Self::Error> { + warn!("received unexpected reply for service 8: {reply:?}"); + Ok(()) + } + + fn handle_reply( + &mut self, + reply: &GenericMessage, + active_request: &ActivePusActionRequestStd, + tm_sender: &(impl EcssTmSenderCore + ?Sized), + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result { + let verif_token: VerificationToken = active_request + .token() + .try_into() + .expect("invalid token state"); + let remove_entry = match &reply.message.variant { + ActionReplyVariant::CompletionFailed { error_code, params } => { + let mut fail_data_len = 0; + if let Some(params) = params { + fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?; + } + verification_handler.completion_failure( + tm_sender, + verif_token, + FailParams::new(time_stamp, error_code, &self.fail_data_buf[..fail_data_len]), + )?; + true + } + ActionReplyVariant::StepFailed { + error_code, + step, + params, + } => { + let mut fail_data_len = 0; + if let Some(params) = params { + fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?; + } + verification_handler.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new( + time_stamp, + &EcssEnumU16::new(*step), + error_code, + &self.fail_data_buf[..fail_data_len], + ), + )?; + true + } + ActionReplyVariant::Completed => { + verification_handler.completion_success(tm_sender, verif_token, time_stamp)?; + true + } + ActionReplyVariant::StepSuccess { step } => { + verification_handler.step_success( + tm_sender, + &verif_token, + time_stamp, + EcssEnumU16::new(*step), + )?; + false + } + _ => false, + }; + Ok(remove_entry) + } + + fn handle_request_timeout( + &mut self, + active_request: &ActivePusActionRequestStd, + tm_sender: &impl EcssTmSenderCore, + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result<(), Self::Error> { + generic_pus_request_timeout_handler( + tm_sender, + active_request, + verification_handler, + time_stamp, + "action", + ) + } +} #[derive(Default)] -pub struct ExampleActionRequestConverter {} +pub struct ActionRequestConverter {} -impl PusActionToRequestConverter for ExampleActionRequestConverter { - type Error = PusPacketHandlingError; +impl PusTcToRequestConverter for ActionRequestConverter { + type Error = GenericConversionError; fn convert( &mut self, token: VerificationToken, tc: &PusTcReader, - time_stamp: &[u8], + tm_sender: &(impl EcssTmSenderCore + ?Sized), verif_reporter: &impl VerificationReportingProvider, - ) -> Result<(TargetId, ActionRequest), Self::Error> { + time_stamp: &[u8], + ) -> Result<(ActivePusActionRequestStd, ActionRequest), Self::Error> { let subservice = tc.subservice(); let user_data = tc.user_data(); if user_data.len() < 8 { verif_reporter .start_failure( + tm_sender, token, FailParams::new_no_fail_data(time_stamp, &tmtc_err::NOT_ENOUGH_APP_DATA), ) .expect("Sending start failure failed"); - return Err(PusPacketHandlingError::NotEnoughAppData { + return Err(GenericConversionError::NotEnoughAppData { expected: 8, found: user_data.len(), }); } - let target_id = TargetAndApidId::from_pus_tc(tc).unwrap(); + let target_id_and_apid = UniqueApidTargetId::from_pus_tc(tc).unwrap(); let action_id = u32::from_be_bytes(user_data[4..8].try_into().unwrap()); if subservice == 128 { + let req_variant = if user_data.len() == 8 { + ActionRequestVariant::NoData + } else { + ActionRequestVariant::VecData(user_data[8..].to_vec()) + }; Ok(( - target_id.raw(), - ActionRequest::UnsignedIdAndVecData { + ActivePusActionRequestStd::new( action_id, - data: user_data[8..].to_vec(), - }, + target_id_and_apid.into(), + token.into(), + Duration::from_secs(30), + ), + ActionRequest::new(action_id, req_variant), )) } else { verif_reporter .start_failure( + tm_sender, token, FailParams::new_no_fail_data(time_stamp, &tmtc_err::INVALID_PUS_SUBSERVICE), ) .expect("Sending start failure failed"); - Err(PusPacketHandlingError::InvalidSubservice(subservice)) + Err(GenericConversionError::InvalidSubservice(subservice)) } } } pub fn create_action_service_static( - shared_tm_store: SharedTmPool, - tm_funnel_tx: mpsc::SyncSender, - verif_reporter: VerificationReporterWithSharedPoolMpscBoundedSender, + tm_sender: TmInSharedPoolSender>, tc_pool: SharedStaticMemoryPool, pus_action_rx: mpsc::Receiver, action_router: GenericRequestRouter, -) -> Pus8Wrapper< - MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, - EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, -> { - let action_srv_tm_sender = TmInSharedPoolSenderWithId::new( - TmSenderId::PusAction as ChannelId, - "PUS_8_TM_SENDER", - shared_tm_store.clone(), - tm_funnel_tx.clone(), - ); - let action_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusAction as ChannelId, - "PUS_8_TC_RECV", - pus_action_rx, - ); - let pus_8_handler = PusService8ActionHandler::new( + reply_receiver: mpsc::Receiver>, +) -> ActionServiceWrapper { + let action_request_handler = PusTargetedRequestService::new( PusServiceHelper::new( - action_srv_receiver, - action_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_ACTION_SERVICE.id(), + pus_action_rx, + tm_sender, + create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid), EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048), ), - ExampleActionRequestConverter::default(), + ActionRequestConverter::default(), + // TODO: Implementation which does not use run-time allocation? Maybe something like + // a bounded wrapper which pre-allocates using [HashMap::with_capacity].. + DefaultActiveActionRequestMap::default(), + ActionReplyHandler::default(), action_router, - GenericRoutingErrorHandler::<8>::default(), + reply_receiver, ); - Pus8Wrapper { pus_8_handler } + ActionServiceWrapper { + service: action_request_handler, + } } pub fn create_action_service_dynamic( - tm_funnel_tx: mpsc::Sender>, - verif_reporter: VerificationReporterWithVecMpscSender, + tm_funnel_tx: mpsc::Sender, pus_action_rx: mpsc::Receiver, action_router: GenericRequestRouter, -) -> Pus8Wrapper< - MpscTcReceiver, - TmAsVecSenderWithMpsc, - EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, -> { - let action_srv_tm_sender = TmAsVecSenderWithId::new( - TmSenderId::PusAction as ChannelId, - "PUS_8_TM_SENDER", - tm_funnel_tx.clone(), - ); - let action_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusAction as ChannelId, - "PUS_8_TC_RECV", - pus_action_rx, - ); - let pus_8_handler = PusService8ActionHandler::new( + reply_receiver: mpsc::Receiver>, +) -> ActionServiceWrapper { + let action_request_handler = PusTargetedRequestService::new( PusServiceHelper::new( - action_srv_receiver, - action_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_ACTION_SERVICE.id(), + pus_action_rx, + tm_funnel_tx, + create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid), EcssTcInVecConverter::default(), ), - ExampleActionRequestConverter::default(), + ActionRequestConverter::default(), + DefaultActiveActionRequestMap::default(), + ActionReplyHandler::default(), action_router, - GenericRoutingErrorHandler::<8>::default(), + reply_receiver, ); - Pus8Wrapper { pus_8_handler } + ActionServiceWrapper { + service: action_request_handler, + } } -pub struct Pus8Wrapper< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, -> { - pub(crate) pus_8_handler: PusService8ActionHandler< - TcReceiver, +pub struct ActionServiceWrapper +{ + pub(crate) service: PusTargetedRequestService< + MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter, - ExampleActionRequestConverter, - GenericRequestRouter, - GenericRoutingErrorHandler<8>, + ActionRequestConverter, + ActionReplyHandler, + DefaultActiveActionRequestMap, + ActivePusActionRequestStd, + ActionRequest, + PusActionReply, >, } -impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - > Pus8Wrapper +impl TargetedPusService + for ActionServiceWrapper { - pub fn handle_next_packet(&mut self) -> bool { - match self.pus_8_handler.handle_one_tc() { + /// Returns [true] if the packet handling is finished. + fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> bool { + match self.service.poll_and_handle_next_tc(time_stamp) { Ok(result) => match result { PusPacketHandlerResult::RequestHandled => {} PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { @@ -197,4 +290,463 @@ impl< } false } + + fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus { + // This only fails if all senders disconnected. Treat it like an empty queue. + self.service + .poll_and_check_next_reply(time_stamp) + .unwrap_or_else(|e| { + warn!("PUS 8: Handling reply failed with error {e:?}"); + HandlingStatus::Empty + }) + } + + fn check_for_request_timeouts(&mut self) { + self.service.check_for_request_timeouts(); + } +} + +#[cfg(test)] +mod tests { + use satrs::pus::test_util::{ + TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1, + }; + use satrs::pus::verification; + use satrs::pus::verification::test_util::TestVerificationReporter; + use satrs::request::MessageMetadata; + use satrs::ComponentId; + use satrs::{ + res_code::ResultU16, + spacepackets::{ + ecss::{ + tc::{PusTcCreator, PusTcSecondaryHeader}, + tm::PusTmReader, + WritablePusPacket, + }, + SpHeader, + }, + }; + + use crate::{ + pus::tests::{PusConverterTestbench, ReplyHandlerTestbench, TargetedPusRequestTestbench}, + requests::CompositeRequest, + }; + + use super::*; + + impl + TargetedPusRequestTestbench< + ActionRequestConverter, + ActionReplyHandler, + DefaultActiveActionRequestMap, + ActivePusActionRequestStd, + ActionRequest, + PusActionReply, + > + { + pub fn new_for_action(owner_id: ComponentId, target_id: ComponentId) -> Self { + let _ = env_logger::builder().is_test(true).try_init(); + let (tm_funnel_tx, tm_funnel_rx) = mpsc::channel(); + let (pus_action_tx, pus_action_rx) = mpsc::channel(); + let (action_reply_tx, action_reply_rx) = mpsc::channel(); + let (action_req_tx, action_req_rx) = mpsc::channel(); + let verif_reporter = TestVerificationReporter::new(owner_id); + let mut generic_req_router = GenericRequestRouter::default(); + generic_req_router + .composite_router_map + .insert(target_id, action_req_tx); + Self { + service: PusTargetedRequestService::new( + PusServiceHelper::new( + owner_id, + pus_action_rx, + tm_funnel_tx.clone(), + verif_reporter, + EcssTcInVecConverter::default(), + ), + ActionRequestConverter::default(), + DefaultActiveActionRequestMap::default(), + ActionReplyHandler::default(), + generic_req_router, + action_reply_rx, + ), + request_id: None, + pus_packet_tx: pus_action_tx, + tm_funnel_rx, + reply_tx: action_reply_tx, + request_rx: action_req_rx, + } + } + + pub fn verify_packet_started(&self) { + self.service + .service_helper + .common + .verif_reporter + .check_next_is_started_success( + self.service.service_helper.id(), + self.request_id.expect("request ID not set").into(), + ); + } + + pub fn verify_packet_completed(&self) { + self.service + .service_helper + .common + .verif_reporter + .check_next_is_completion_success( + self.service.service_helper.id(), + self.request_id.expect("request ID not set").into(), + ); + } + + pub fn verify_tm_empty(&self) { + let packet = self.tm_funnel_rx.try_recv(); + if let Err(mpsc::TryRecvError::Empty) = packet { + } else { + let tm = packet.unwrap(); + let unexpected_tm = PusTmReader::new(&tm.packet, 7).unwrap().0; + panic!("unexpected TM packet {unexpected_tm:?}"); + } + } + + pub fn verify_next_tc_is_handled_properly(&mut self, time_stamp: &[u8]) { + let result = self.service.poll_and_handle_next_tc(time_stamp); + if let Err(e) = result { + panic!("unexpected error {:?}", e); + } + let result = result.unwrap(); + match result { + PusPacketHandlerResult::RequestHandled => (), + _ => panic!("unexpected result {result:?}"), + } + } + + pub fn verify_all_tcs_handled(&mut self, time_stamp: &[u8]) { + let result = self.service.poll_and_handle_next_tc(time_stamp); + if let Err(e) = result { + panic!("unexpected error {:?}", e); + } + let result = result.unwrap(); + match result { + PusPacketHandlerResult::Empty => (), + _ => panic!("unexpected result {result:?}"), + } + } + + pub fn verify_next_reply_is_handled_properly(&mut self, time_stamp: &[u8]) { + let result = self.service.poll_and_check_next_reply(time_stamp); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), HandlingStatus::HandledOne); + } + + pub fn verify_all_replies_handled(&mut self, time_stamp: &[u8]) { + let result = self.service.poll_and_check_next_reply(time_stamp); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), HandlingStatus::Empty); + } + + pub fn add_tc(&mut self, tc: &PusTcCreator) { + self.request_id = Some(verification::RequestId::new(tc).into()); + let token = self.service.service_helper.verif_reporter_mut().add_tc(tc); + let accepted_token = self + .service + .service_helper + .verif_reporter() + .acceptance_success(self.service.service_helper.tm_sender(), token, &[0; 7]) + .expect("TC acceptance failed"); + self.service + .service_helper + .verif_reporter() + .check_next_was_added(accepted_token.request_id()); + let id = self.service.service_helper.id(); + self.service + .service_helper + .verif_reporter() + .check_next_is_acceptance_success(id, accepted_token.request_id()); + self.pus_packet_tx + .send(EcssTcAndToken::new(tc.to_vec().unwrap(), accepted_token)) + .unwrap(); + } + } + + #[test] + fn basic_request() { + let mut testbench = TargetedPusRequestTestbench::new_for_action( + TEST_COMPONENT_ID_0.id(), + TEST_COMPONENT_ID_1.id(), + ); + // Create a basic action request and verify forwarding. + let sp_header = SpHeader::new_from_apid(TEST_APID); + let sec_header = PusTcSecondaryHeader::new_simple(8, 128); + let action_id = 5_u32; + let mut app_data: [u8; 8] = [0; 8]; + app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_1.to_be_bytes()); + app_data[4..8].copy_from_slice(&action_id.to_be_bytes()); + let pus8_packet = PusTcCreator::new(sp_header, sec_header, &app_data, true); + testbench.add_tc(&pus8_packet); + let time_stamp: [u8; 7] = [0; 7]; + testbench.verify_next_tc_is_handled_properly(&time_stamp); + testbench.verify_all_tcs_handled(&time_stamp); + + testbench.verify_packet_started(); + + let possible_req = testbench.request_rx.try_recv(); + assert!(possible_req.is_ok()); + let req = possible_req.unwrap(); + if let CompositeRequest::Action(action_req) = req.message { + assert_eq!(action_req.action_id, action_id); + assert_eq!(action_req.variant, ActionRequestVariant::NoData); + let action_reply = PusActionReply::new(action_id, ActionReplyVariant::Completed); + testbench + .reply_tx + .send(GenericMessage::new(req.requestor_info, action_reply)) + .unwrap(); + } else { + panic!("unexpected request type"); + } + testbench.verify_next_reply_is_handled_properly(&time_stamp); + testbench.verify_all_replies_handled(&time_stamp); + + testbench.verify_packet_completed(); + testbench.verify_tm_empty(); + } + + #[test] + fn basic_request_routing_error() { + let mut testbench = TargetedPusRequestTestbench::new_for_action( + TEST_COMPONENT_ID_0.id(), + TEST_COMPONENT_ID_1.id(), + ); + // Create a basic action request and verify forwarding. + let sec_header = PusTcSecondaryHeader::new_simple(8, 128); + let action_id = 5_u32; + let mut app_data: [u8; 8] = [0; 8]; + // Invalid ID, routing should fail. + app_data[0..4].copy_from_slice(&0_u32.to_be_bytes()); + app_data[4..8].copy_from_slice(&action_id.to_be_bytes()); + let pus8_packet = PusTcCreator::new( + SpHeader::new_from_apid(TEST_APID), + sec_header, + &app_data, + true, + ); + testbench.add_tc(&pus8_packet); + let time_stamp: [u8; 7] = [0; 7]; + + let result = testbench.service.poll_and_handle_next_tc(&time_stamp); + assert!(result.is_err()); + // Verify the correct result and completion failure. + } + + #[test] + fn converter_action_req_no_data() { + let mut testbench = PusConverterTestbench::new( + TEST_COMPONENT_ID_0.raw(), + ActionRequestConverter::default(), + ); + let sec_header = PusTcSecondaryHeader::new_simple(8, 128); + let action_id = 5_u32; + let mut app_data: [u8; 8] = [0; 8]; + // Invalid ID, routing should fail. + app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes()); + app_data[4..8].copy_from_slice(&action_id.to_be_bytes()); + let pus8_packet = PusTcCreator::new( + SpHeader::new_from_apid(TEST_APID), + sec_header, + &app_data, + true, + ); + let token = testbench.add_tc(&pus8_packet); + let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0); + assert!(result.is_ok()); + let (active_req, request) = result.unwrap(); + if let ActionRequestVariant::NoData = request.variant { + assert_eq!(request.action_id, action_id); + assert_eq!(active_req.action_id, action_id); + assert_eq!( + active_req.target_id(), + UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0).raw() + ); + assert_eq!( + active_req.token().request_id(), + testbench.request_id().unwrap() + ); + } else { + panic!("unexpected action request variant"); + } + } + + #[test] + fn converter_action_req_with_data() { + let mut testbench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ActionRequestConverter::default()); + let sec_header = PusTcSecondaryHeader::new_simple(8, 128); + let action_id = 5_u32; + let mut app_data: [u8; 16] = [0; 16]; + // Invalid ID, routing should fail. + app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes()); + app_data[4..8].copy_from_slice(&action_id.to_be_bytes()); + for i in 0..8 { + app_data[i + 8] = i as u8; + } + let pus8_packet = PusTcCreator::new( + SpHeader::new_from_apid(TEST_APID), + sec_header, + &app_data, + true, + ); + let token = testbench.add_tc(&pus8_packet); + let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0); + assert!(result.is_ok()); + let (active_req, request) = result.unwrap(); + if let ActionRequestVariant::VecData(vec) = request.variant { + assert_eq!(request.action_id, action_id); + assert_eq!(active_req.action_id, action_id); + assert_eq!(vec, app_data[8..].to_vec()); + } else { + panic!("unexpected action request variant"); + } + } + + #[test] + fn reply_handling_completion_success() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default()); + let action_id = 5_u32; + let (req_id, active_req) = testbench.add_tc(TEST_APID, TEST_UNIQUE_ID_0, &[]); + let active_action_req = + ActivePusActionRequestStd::new_from_common_req(action_id, active_req); + let reply = PusActionReply::new(action_id, ActionReplyVariant::Completed); + let generic_reply = GenericMessage::new(MessageMetadata::new(req_id.into(), 0), reply); + let result = testbench.handle_reply(&generic_reply, &active_action_req, &[]); + assert!(result.is_ok()); + assert!(result.unwrap()); + testbench.verif_reporter.assert_full_completion_success( + TEST_COMPONENT_ID_0.id(), + req_id, + None, + ); + } + + #[test] + fn reply_handling_completion_failure() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default()); + let action_id = 5_u32; + let (req_id, active_req) = testbench.add_tc(TEST_APID, TEST_UNIQUE_ID_0, &[]); + let active_action_req = + ActivePusActionRequestStd::new_from_common_req(action_id, active_req); + let error_code = ResultU16::new(2, 3); + let reply = PusActionReply::new( + action_id, + ActionReplyVariant::CompletionFailed { + error_code, + params: None, + }, + ); + let generic_reply = GenericMessage::new(MessageMetadata::new(req_id.into(), 0), reply); + let result = testbench.handle_reply(&generic_reply, &active_action_req, &[]); + assert!(result.is_ok()); + assert!(result.unwrap()); + testbench.verif_reporter.assert_completion_failure( + TEST_COMPONENT_ID_0.into(), + req_id, + None, + error_code.raw() as u64, + ); + } + + #[test] + fn reply_handling_step_success() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default()); + let action_id = 5_u32; + let (req_id, active_req) = testbench.add_tc(TEST_APID, TEST_UNIQUE_ID_0, &[]); + let active_action_req = + ActivePusActionRequestStd::new_from_common_req(action_id, active_req); + let reply = PusActionReply::new(action_id, ActionReplyVariant::StepSuccess { step: 1 }); + let generic_reply = GenericMessage::new(MessageMetadata::new(req_id.into(), 0), reply); + let result = testbench.handle_reply(&generic_reply, &active_action_req, &[]); + assert!(result.is_ok()); + // Entry should not be removed, completion not done yet. + assert!(!result.unwrap()); + testbench.verif_reporter.check_next_was_added(req_id); + testbench + .verif_reporter + .check_next_is_acceptance_success(TEST_COMPONENT_ID_0.raw(), req_id); + testbench + .verif_reporter + .check_next_is_started_success(TEST_COMPONENT_ID_0.raw(), req_id); + testbench + .verif_reporter + .check_next_is_step_success(TEST_COMPONENT_ID_0.raw(), req_id, 1); + } + + #[test] + fn reply_handling_step_failure() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default()); + let action_id = 5_u32; + let (req_id, active_req) = testbench.add_tc(TEST_APID, TEST_UNIQUE_ID_0, &[]); + let active_action_req = + ActivePusActionRequestStd::new_from_common_req(action_id, active_req); + let error_code = ResultU16::new(2, 3); + let reply = PusActionReply::new( + action_id, + ActionReplyVariant::StepFailed { + error_code, + step: 1, + params: None, + }, + ); + let generic_reply = GenericMessage::new(MessageMetadata::new(req_id.into(), 0), reply); + let result = testbench.handle_reply(&generic_reply, &active_action_req, &[]); + assert!(result.is_ok()); + assert!(result.unwrap()); + testbench.verif_reporter.check_next_was_added(req_id); + testbench + .verif_reporter + .check_next_is_acceptance_success(TEST_COMPONENT_ID_0.id(), req_id); + testbench + .verif_reporter + .check_next_is_started_success(TEST_COMPONENT_ID_0.id(), req_id); + testbench.verif_reporter.check_next_is_step_failure( + TEST_COMPONENT_ID_0.id(), + req_id, + error_code.raw().into(), + ); + } + + #[test] + fn reply_handling_unrequested_reply() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default()); + let action_reply = PusActionReply::new(5_u32, ActionReplyVariant::Completed); + let unrequested_reply = + GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), action_reply); + // Right now this function does not do a lot. We simply check that it does not panic or do + // weird stuff. + let result = testbench.handle_unrequested_reply(&unrequested_reply); + assert!(result.is_ok()); + } + + #[test] + fn reply_handling_reply_timeout() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), ActionReplyHandler::default()); + let action_id = 5_u32; + let (req_id, active_request) = testbench.add_tc(TEST_APID, TEST_UNIQUE_ID_0, &[]); + let result = testbench.handle_request_timeout( + &ActivePusActionRequestStd::new_from_common_req(action_id, active_request), + &[], + ); + assert!(result.is_ok()); + testbench.verif_reporter.assert_completion_failure( + TEST_COMPONENT_ID_0.raw(), + req_id, + None, + tmtc_err::REQUEST_TIMEOUT.raw() as u64, + ); + } } diff --git a/satrs-example/src/pus/event.rs b/satrs-example/src/pus/event.rs index 1d16f5c..865b1f1 100644 --- a/satrs-example/src/pus/event.rs +++ b/satrs-example/src/pus/event.rs @@ -1,113 +1,69 @@ use std::sync::mpsc; +use crate::pus::create_verification_reporter; use log::{error, warn}; -use satrs::pool::{SharedStaticMemoryPool, StoreAddr}; +use satrs::pool::SharedStaticMemoryPool; use satrs::pus::event_man::EventRequestWithToken; -use satrs::pus::event_srv::PusService5EventHandler; -use satrs::pus::verification::std_mod::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, -}; -use satrs::pus::verification::VerificationReportingProvider; +use satrs::pus::event_srv::PusEventServiceHandler; +use satrs::pus::verification::VerificationReporter; use satrs::pus::{ EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult, PusServiceHelper, - TmAsVecSenderWithId, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, - TmInSharedPoolSenderWithId, + EcssTmSenderCore, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, + PusPacketHandlerResult, PusServiceHelper, PusTmAsVec, PusTmInPool, TmInSharedPoolSender, }; -use satrs::tmtc::tm_helper::SharedTmPool; -use satrs::ChannelId; -use satrs_example::config::{TcReceiverId, TmSenderId, PUS_APID}; +use satrs_example::config::components::PUS_EVENT_MANAGEMENT; pub fn create_event_service_static( - shared_tm_store: SharedTmPool, - tm_funnel_tx: mpsc::SyncSender, - verif_reporter: VerificationReporterWithSharedPoolMpscBoundedSender, + tm_sender: TmInSharedPoolSender>, tc_pool: SharedStaticMemoryPool, pus_event_rx: mpsc::Receiver, event_request_tx: mpsc::Sender, -) -> Pus5Wrapper< - MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, - EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, -> { - let event_srv_tm_sender = TmInSharedPoolSenderWithId::new( - TmSenderId::PusEvent as ChannelId, - "PUS_5_TM_SENDER", - shared_tm_store.clone(), - tm_funnel_tx.clone(), - ); - let event_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusEvent as ChannelId, - "PUS_5_TC_RECV", - pus_event_rx, - ); - let pus_5_handler = PusService5EventHandler::new( +) -> EventServiceWrapper { + let pus_5_handler = PusEventServiceHandler::new( PusServiceHelper::new( - event_srv_receiver, - event_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_EVENT_MANAGEMENT.id(), + pus_event_rx, + tm_sender, + create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid), EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048), ), event_request_tx, ); - Pus5Wrapper { pus_5_handler } + EventServiceWrapper { + handler: pus_5_handler, + } } pub fn create_event_service_dynamic( - tm_funnel_tx: mpsc::Sender>, - verif_reporter: VerificationReporterWithVecMpscSender, + tm_funnel_tx: mpsc::Sender, pus_event_rx: mpsc::Receiver, event_request_tx: mpsc::Sender, -) -> Pus5Wrapper< - MpscTcReceiver, - TmAsVecSenderWithMpsc, - EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, -> { - let event_srv_tm_sender = TmAsVecSenderWithId::new( - TmSenderId::PusEvent as ChannelId, - "PUS_5_TM_SENDER", - tm_funnel_tx, - ); - let event_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusEvent as ChannelId, - "PUS_5_TC_RECV", - pus_event_rx, - ); - let pus_5_handler = PusService5EventHandler::new( +) -> EventServiceWrapper { + let pus_5_handler = PusEventServiceHandler::new( PusServiceHelper::new( - event_srv_receiver, - event_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_EVENT_MANAGEMENT.id(), + pus_event_rx, + tm_funnel_tx, + create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid), EcssTcInVecConverter::default(), ), event_request_tx, ); - Pus5Wrapper { pus_5_handler } + EventServiceWrapper { + handler: pus_5_handler, + } } -pub struct Pus5Wrapper< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, -> { - pub pus_5_handler: - PusService5EventHandler, +pub struct EventServiceWrapper { + pub handler: + PusEventServiceHandler, } -impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - > Pus5Wrapper +impl + EventServiceWrapper { - pub fn handle_next_packet(&mut self) -> bool { - match self.pus_5_handler.handle_one_tc() { + pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> bool { + match self.handler.poll_and_handle_next_tc(time_stamp) { Ok(result) => match result { PusPacketHandlerResult::RequestHandled => {} PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { diff --git a/satrs-example/src/pus/hk.rs b/satrs-example/src/pus/hk.rs index 48a54be..cb3ebb9 100644 --- a/satrs-example/src/pus/hk.rs +++ b/satrs-example/src/pus/hk.rs @@ -1,50 +1,127 @@ +use derive_new::new; use log::{error, warn}; -use satrs::hk::{CollectionIntervalFactor, HkRequest}; -use satrs::pool::{SharedStaticMemoryPool, StoreAddr}; -use satrs::pus::hk::{PusHkToRequestConverter, PusService3HkHandler}; -use satrs::pus::verification::std_mod::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, -}; +use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId}; +use satrs::pool::SharedStaticMemoryPool; use satrs::pus::verification::{ - FailParams, TcStateAccepted, VerificationReportingProvider, VerificationToken, + FailParams, TcStateAccepted, TcStateStarted, VerificationReporter, + VerificationReportingProvider, VerificationToken, }; use satrs::pus::{ - EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult, - PusPacketHandlingError, PusServiceHelper, TmAsVecSenderWithId, TmAsVecSenderWithMpsc, - TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithId, + ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken, + EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSenderCore, + EcssTmtcError, GenericConversionError, MpscTcReceiver, MpscTmAsVecSender, + MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult, PusReplyHandler, PusServiceHelper, + PusTcToRequestConverter, PusTmAsVec, PusTmInPool, TmInSharedPoolSender, }; -use satrs::request::TargetAndApidId; +use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::{hk, PusPacket}; -use satrs::tmtc::tm_helper::SharedTmPool; -use satrs::{ChannelId, TargetId}; -use satrs_example::config::{hk_err, tmtc_err, TcReceiverId, TmSenderId, PUS_APID}; -use std::sync::mpsc::{self}; +use satrs_example::config::components::PUS_HK_SERVICE; +use satrs_example::config::{hk_err, tmtc_err}; +use std::sync::mpsc; +use std::time::Duration; +use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler}; use crate::requests::GenericRequestRouter; -use super::GenericRoutingErrorHandler; +use super::{HandlingStatus, PusTargetedRequestService}; + +#[derive(Clone, PartialEq, Debug, new)] +pub struct HkReply { + pub unique_id: UniqueId, + pub variant: HkReplyVariant, +} + +#[derive(Clone, PartialEq, Debug)] +pub enum HkReplyVariant { + Ack, +} #[derive(Default)] -pub struct ExampleHkRequestConverter {} +pub struct HkReplyHandler {} -impl PusHkToRequestConverter for ExampleHkRequestConverter { - type Error = PusPacketHandlingError; +impl PusReplyHandler for HkReplyHandler { + type Error = EcssTmtcError; + + fn handle_unrequested_reply( + &mut self, + reply: &GenericMessage, + _tm_sender: &impl EcssTmSenderCore, + ) -> Result<(), Self::Error> { + log::warn!("received unexpected reply for service 3: {reply:?}"); + Ok(()) + } + + fn handle_reply( + &mut self, + reply: &GenericMessage, + active_request: &ActivePusRequestStd, + tm_sender: &impl EcssTmSenderCore, + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result { + let started_token: VerificationToken = active_request + .token() + .try_into() + .expect("invalid token state"); + match reply.message.variant { + HkReplyVariant::Ack => { + verification_handler + .completion_success(tm_sender, started_token, time_stamp) + .expect("sending completion success verification failed"); + } + }; + Ok(true) + } + + fn handle_request_timeout( + &mut self, + active_request: &ActivePusRequestStd, + tm_sender: &impl EcssTmSenderCore, + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result<(), Self::Error> { + generic_pus_request_timeout_handler( + tm_sender, + active_request, + verification_handler, + time_stamp, + "HK", + )?; + Ok(()) + } +} + +pub struct HkRequestConverter { + timeout: Duration, +} + +impl Default for HkRequestConverter { + fn default() -> Self { + Self { + timeout: Duration::from_secs(60), + } + } +} + +impl PusTcToRequestConverter for HkRequestConverter { + type Error = GenericConversionError; fn convert( &mut self, token: VerificationToken, tc: &PusTcReader, - time_stamp: &[u8], + tm_sender: &(impl EcssTmSenderCore + ?Sized), verif_reporter: &impl VerificationReportingProvider, - ) -> Result<(TargetId, HkRequest), Self::Error> { + time_stamp: &[u8], + ) -> Result<(ActivePusRequestStd, HkRequest), Self::Error> { let user_data = tc.user_data(); if user_data.is_empty() { let user_data_len = user_data.len() as u32; let user_data_len_raw = user_data_len.to_be_bytes(); verif_reporter .start_failure( + tm_sender, token, FailParams::new( time_stamp, @@ -53,7 +130,7 @@ impl PusHkToRequestConverter for ExampleHkRequestConverter { ), ) .expect("Sending start failure TM failed"); - return Err(PusPacketHandlingError::NotEnoughAppData { + return Err(GenericConversionError::NotEnoughAppData { expected: 4, found: 0, }); @@ -67,178 +144,164 @@ impl PusHkToRequestConverter for ExampleHkRequestConverter { let user_data_len = user_data.len() as u32; let user_data_len_raw = user_data_len.to_be_bytes(); verif_reporter - .start_failure(token, FailParams::new(time_stamp, err, &user_data_len_raw)) + .start_failure( + tm_sender, + token, + FailParams::new(time_stamp, err, &user_data_len_raw), + ) .expect("Sending start failure TM failed"); - return Err(PusPacketHandlingError::NotEnoughAppData { + return Err(GenericConversionError::NotEnoughAppData { expected: 8, found: 4, }); } let subservice = tc.subservice(); - let target_id = TargetAndApidId::from_pus_tc(tc).expect("invalid tc format"); + let target_id_and_apid = UniqueApidTargetId::from_pus_tc(tc).expect("invalid tc format"); let unique_id = u32::from_be_bytes(tc.user_data()[4..8].try_into().unwrap()); let standard_subservice = hk::Subservice::try_from(subservice); if standard_subservice.is_err() { verif_reporter .start_failure( + tm_sender, token, FailParams::new(time_stamp, &tmtc_err::INVALID_PUS_SUBSERVICE, &[subservice]), ) .expect("Sending start failure TM failed"); - return Err(PusPacketHandlingError::InvalidSubservice(subservice)); + return Err(GenericConversionError::InvalidSubservice(subservice)); } - Ok(( - target_id.into(), - match standard_subservice.unwrap() { - hk::Subservice::TcEnableHkGeneration | hk::Subservice::TcEnableDiagGeneration => { - HkRequest::Enable(unique_id) - } - hk::Subservice::TcDisableHkGeneration | hk::Subservice::TcDisableDiagGeneration => { - HkRequest::Disable(unique_id) - } - hk::Subservice::TcReportHkReportStructures => todo!(), - hk::Subservice::TmHkPacket => todo!(), - hk::Subservice::TcGenerateOneShotHk | hk::Subservice::TcGenerateOneShotDiag => { - HkRequest::OneShot(unique_id) - } - hk::Subservice::TcModifyDiagCollectionInterval - | hk::Subservice::TcModifyHkCollectionInterval => { - if user_data.len() < 12 { - verif_reporter - .start_failure( - token, - FailParams::new_no_fail_data( - time_stamp, - &tmtc_err::NOT_ENOUGH_APP_DATA, - ), - ) - .expect("Sending start failure TM failed"); - return Err(PusPacketHandlingError::NotEnoughAppData { - expected: 12, - found: user_data.len(), - }); - } - HkRequest::ModifyCollectionInterval( - unique_id, - CollectionIntervalFactor::from_be_bytes( - user_data[8..12].try_into().unwrap(), - ), - ) - } - _ => { + let request = match standard_subservice.unwrap() { + hk::Subservice::TcEnableHkGeneration | hk::Subservice::TcEnableDiagGeneration => { + HkRequest::new(unique_id, HkRequestVariant::EnablePeriodic) + } + hk::Subservice::TcDisableHkGeneration | hk::Subservice::TcDisableDiagGeneration => { + HkRequest::new(unique_id, HkRequestVariant::DisablePeriodic) + } + hk::Subservice::TcReportHkReportStructures => todo!(), + hk::Subservice::TmHkPacket => todo!(), + hk::Subservice::TcGenerateOneShotHk | hk::Subservice::TcGenerateOneShotDiag => { + HkRequest::new(unique_id, HkRequestVariant::OneShot) + } + hk::Subservice::TcModifyDiagCollectionInterval + | hk::Subservice::TcModifyHkCollectionInterval => { + if user_data.len() < 12 { verif_reporter .start_failure( + tm_sender, token, - FailParams::new( + FailParams::new_no_fail_data( time_stamp, - &tmtc_err::PUS_SUBSERVICE_NOT_IMPLEMENTED, - &[subservice], + &tmtc_err::NOT_ENOUGH_APP_DATA, ), ) .expect("Sending start failure TM failed"); - return Err(PusPacketHandlingError::InvalidSubservice(subservice)); + return Err(GenericConversionError::NotEnoughAppData { + expected: 12, + found: user_data.len(), + }); } - }, + HkRequest::new( + unique_id, + HkRequestVariant::ModifyCollectionInterval( + CollectionIntervalFactor::from_be_bytes( + user_data[8..12].try_into().unwrap(), + ), + ), + ) + } + _ => { + verif_reporter + .start_failure( + tm_sender, + token, + FailParams::new( + time_stamp, + &tmtc_err::PUS_SUBSERVICE_NOT_IMPLEMENTED, + &[subservice], + ), + ) + .expect("Sending start failure TM failed"); + return Err(GenericConversionError::InvalidSubservice(subservice)); + } + }; + Ok(( + ActivePusRequestStd::new(target_id_and_apid.into(), token, self.timeout), + request, )) } } pub fn create_hk_service_static( - shared_tm_store: SharedTmPool, - tm_funnel_tx: mpsc::SyncSender, - verif_reporter: VerificationReporterWithSharedPoolMpscBoundedSender, + tm_sender: TmInSharedPoolSender>, tc_pool: SharedStaticMemoryPool, pus_hk_rx: mpsc::Receiver, request_router: GenericRequestRouter, -) -> Pus3Wrapper< - MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, - EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, -> { - let hk_srv_tm_sender = TmInSharedPoolSenderWithId::new( - TmSenderId::PusHk as ChannelId, - "PUS_3_TM_SENDER", - shared_tm_store.clone(), - tm_funnel_tx.clone(), - ); - let hk_srv_receiver = - MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx); - let pus_3_handler = PusService3HkHandler::new( + reply_receiver: mpsc::Receiver>, +) -> HkServiceWrapper { + let pus_3_handler = PusTargetedRequestService::new( PusServiceHelper::new( - hk_srv_receiver, - hk_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_HK_SERVICE.id(), + pus_hk_rx, + tm_sender, + create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid), EcssTcInSharedStoreConverter::new(tc_pool, 2048), ), - ExampleHkRequestConverter::default(), + HkRequestConverter::default(), + DefaultActiveRequestMap::default(), + HkReplyHandler::default(), request_router, - GenericRoutingErrorHandler::default(), + reply_receiver, ); - Pus3Wrapper { pus_3_handler } + HkServiceWrapper { + service: pus_3_handler, + } } pub fn create_hk_service_dynamic( - tm_funnel_tx: mpsc::Sender>, - verif_reporter: VerificationReporterWithVecMpscSender, + tm_funnel_tx: mpsc::Sender, pus_hk_rx: mpsc::Receiver, request_router: GenericRequestRouter, -) -> Pus3Wrapper< - MpscTcReceiver, - TmAsVecSenderWithMpsc, - EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, -> { - let hk_srv_tm_sender = TmAsVecSenderWithId::new( - TmSenderId::PusHk as ChannelId, - "PUS_3_TM_SENDER", - tm_funnel_tx.clone(), - ); - let hk_srv_receiver = - MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx); - let pus_3_handler = PusService3HkHandler::new( + reply_receiver: mpsc::Receiver>, +) -> HkServiceWrapper { + let pus_3_handler = PusTargetedRequestService::new( PusServiceHelper::new( - hk_srv_receiver, - hk_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_HK_SERVICE.id(), + pus_hk_rx, + tm_funnel_tx, + create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid), EcssTcInVecConverter::default(), ), - ExampleHkRequestConverter::default(), + HkRequestConverter::default(), + DefaultActiveRequestMap::default(), + HkReplyHandler::default(), request_router, - GenericRoutingErrorHandler::default(), + reply_receiver, ); - Pus3Wrapper { pus_3_handler } + HkServiceWrapper { + service: pus_3_handler, + } } -pub struct Pus3Wrapper< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, -> { - pub(crate) pus_3_handler: PusService3HkHandler< - TcReceiver, +pub struct HkServiceWrapper { + pub(crate) service: PusTargetedRequestService< + MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter, - ExampleHkRequestConverter, - GenericRequestRouter, - GenericRoutingErrorHandler<3>, + HkRequestConverter, + HkReplyHandler, + DefaultActiveRequestMap, + ActivePusRequestStd, + HkRequest, + HkReply, >, } -impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - > Pus3Wrapper +impl + HkServiceWrapper { - pub fn handle_next_packet(&mut self) -> bool { - match self.pus_3_handler.handle_one_tc() { + pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> bool { + match self.service.poll_and_handle_next_tc(time_stamp) { Ok(result) => match result { PusPacketHandlerResult::RequestHandled => {} PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { @@ -260,4 +323,242 @@ impl< } false } + + pub fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus { + // This only fails if all senders disconnected. Treat it like an empty queue. + self.service + .poll_and_check_next_reply(time_stamp) + .unwrap_or_else(|e| { + warn!("PUS 3: Handling reply failed with error {e:?}"); + HandlingStatus::Empty + }) + } + + pub fn check_for_request_timeouts(&mut self) { + self.service.check_for_request_timeouts(); + } +} + +#[cfg(test)] +mod tests { + use satrs::pus::test_util::{ + TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1, + }; + use satrs::request::MessageMetadata; + use satrs::{ + hk::HkRequestVariant, + pus::test_util::TEST_APID, + request::GenericMessage, + spacepackets::{ + ecss::{hk::Subservice, tc::PusTcCreator}, + SpHeader, + }, + }; + use satrs_example::config::tmtc_err; + + use crate::pus::{ + hk::HkReplyVariant, + tests::{PusConverterTestbench, ReplyHandlerTestbench}, + }; + + use super::{HkReply, HkReplyHandler, HkRequestConverter}; + + #[test] + fn hk_converter_one_shot_req() { + let mut hk_bench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let target_id = TEST_UNIQUE_ID_0; + let unique_id = 5_u32; + let mut app_data: [u8; 8] = [0; 8]; + app_data[0..4].copy_from_slice(&target_id.to_be_bytes()); + app_data[4..8].copy_from_slice(&unique_id.to_be_bytes()); + + let hk_req = PusTcCreator::new_simple( + sp_header, + 3, + Subservice::TcGenerateOneShotHk as u8, + &app_data, + true, + ); + let accepted_token = hk_bench.add_tc(&hk_req); + let (_active_req, req) = hk_bench + .convert(accepted_token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion failed"); + + assert_eq!(req.unique_id, unique_id); + if let HkRequestVariant::OneShot = req.variant { + } else { + panic!("unexpected HK request") + } + } + + #[test] + fn hk_converter_enable_periodic_generation() { + let mut hk_bench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let target_id = TEST_UNIQUE_ID_0; + let unique_id = 5_u32; + let mut app_data: [u8; 8] = [0; 8]; + app_data[0..4].copy_from_slice(&target_id.to_be_bytes()); + app_data[4..8].copy_from_slice(&unique_id.to_be_bytes()); + let mut generic_check = |tc: &PusTcCreator| { + let accepted_token = hk_bench.add_tc(tc); + let (_active_req, req) = hk_bench + .convert(accepted_token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion failed"); + assert_eq!(req.unique_id, unique_id); + if let HkRequestVariant::EnablePeriodic = req.variant { + } else { + panic!("unexpected HK request") + } + }; + let tc0 = PusTcCreator::new_simple( + sp_header, + 3, + Subservice::TcEnableHkGeneration as u8, + &app_data, + true, + ); + generic_check(&tc0); + let tc1 = PusTcCreator::new_simple( + sp_header, + 3, + Subservice::TcEnableDiagGeneration as u8, + &app_data, + true, + ); + generic_check(&tc1); + } + + #[test] + fn hk_conversion_disable_periodic_generation() { + let mut hk_bench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let target_id = TEST_UNIQUE_ID_0; + let unique_id = 5_u32; + let mut app_data: [u8; 8] = [0; 8]; + app_data[0..4].copy_from_slice(&target_id.to_be_bytes()); + app_data[4..8].copy_from_slice(&unique_id.to_be_bytes()); + let mut generic_check = |tc: &PusTcCreator| { + let accepted_token = hk_bench.add_tc(tc); + let (_active_req, req) = hk_bench + .convert(accepted_token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion failed"); + assert_eq!(req.unique_id, unique_id); + if let HkRequestVariant::DisablePeriodic = req.variant { + } else { + panic!("unexpected HK request") + } + }; + let tc0 = PusTcCreator::new_simple( + sp_header, + 3, + Subservice::TcDisableHkGeneration as u8, + &app_data, + true, + ); + generic_check(&tc0); + let tc1 = PusTcCreator::new_simple( + sp_header, + 3, + Subservice::TcDisableDiagGeneration as u8, + &app_data, + true, + ); + generic_check(&tc1); + } + + #[test] + fn hk_conversion_modify_interval() { + let mut hk_bench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let target_id = TEST_UNIQUE_ID_0; + let unique_id = 5_u32; + let mut app_data: [u8; 12] = [0; 12]; + let collection_interval_factor = 5_u32; + app_data[0..4].copy_from_slice(&target_id.to_be_bytes()); + app_data[4..8].copy_from_slice(&unique_id.to_be_bytes()); + app_data[8..12].copy_from_slice(&collection_interval_factor.to_be_bytes()); + + let mut generic_check = |tc: &PusTcCreator| { + let accepted_token = hk_bench.add_tc(tc); + let (_active_req, req) = hk_bench + .convert(accepted_token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion failed"); + assert_eq!(req.unique_id, unique_id); + if let HkRequestVariant::ModifyCollectionInterval(interval_factor) = req.variant { + assert_eq!(interval_factor, collection_interval_factor); + } else { + panic!("unexpected HK request") + } + }; + let tc0 = PusTcCreator::new_simple( + sp_header, + 3, + Subservice::TcModifyHkCollectionInterval as u8, + &app_data, + true, + ); + generic_check(&tc0); + let tc1 = PusTcCreator::new_simple( + sp_header, + 3, + Subservice::TcModifyDiagCollectionInterval as u8, + &app_data, + true, + ); + generic_check(&tc1); + } + + #[test] + fn hk_reply_handler() { + let mut reply_testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_0.id(), HkReplyHandler::default()); + let sender_id = 2_u64; + let apid_target_id = 3_u32; + let unique_id = 5_u32; + let (req_id, active_req) = reply_testbench.add_tc(TEST_APID, apid_target_id, &[]); + let reply = GenericMessage::new( + MessageMetadata::new(req_id.into(), sender_id), + HkReply::new(unique_id, HkReplyVariant::Ack), + ); + let result = reply_testbench.handle_reply(&reply, &active_req, &[]); + assert!(result.is_ok()); + assert!(result.unwrap()); + reply_testbench + .verif_reporter + .assert_full_completion_success(TEST_COMPONENT_ID_0.raw(), req_id, None); + } + + #[test] + fn reply_handling_unrequested_reply() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_1.id(), HkReplyHandler::default()); + let action_reply = HkReply::new(5_u32, HkReplyVariant::Ack); + let unrequested_reply = + GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), action_reply); + // Right now this function does not do a lot. We simply check that it does not panic or do + // weird stuff. + let result = testbench.handle_unrequested_reply(&unrequested_reply); + assert!(result.is_ok()); + } + + #[test] + fn reply_handling_reply_timeout() { + let mut testbench = + ReplyHandlerTestbench::new(TEST_COMPONENT_ID_1.id(), HkReplyHandler::default()); + let (req_id, active_request) = testbench.add_tc(TEST_APID, TEST_UNIQUE_ID_1, &[]); + let result = testbench.handle_request_timeout(&active_request, &[]); + assert!(result.is_ok()); + testbench.verif_reporter.assert_completion_failure( + TEST_COMPONENT_ID_1.raw(), + req_id, + None, + tmtc_err::REQUEST_TIMEOUT.raw() as u64, + ); + } } diff --git a/satrs-example/src/pus/mod.rs b/satrs-example/src/pus/mod.rs index 2b6c3ed..83bd34a 100644 --- a/satrs-example/src/pus/mod.rs +++ b/satrs-example/src/pus/mod.rs @@ -1,75 +1,80 @@ +use crate::requests::GenericRequestRouter; use crate::tmtc::MpscStoreAndSendError; use log::warn; -use satrs::pus::verification::{FailParams, VerificationReportingProvider}; -use satrs::pus::{ - EcssTcAndToken, GenericRoutingError, PusPacketHandlerResult, PusRoutingErrorHandler, TcInMemory, +use satrs::pus::verification::{ + self, FailParams, TcStateAccepted, TcStateStarted, VerificationReporter, + VerificationReporterCfg, VerificationReportingProvider, VerificationToken, }; +use satrs::pus::{ + ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, + EcssTcReceiverCore, EcssTmSenderCore, EcssTmtcError, GenericConversionError, + GenericRoutingError, PusPacketHandlerResult, PusPacketHandlingError, PusReplyHandler, + PusRequestRouter, PusServiceHelper, PusTcToRequestConverter, TcInMemory, +}; +use satrs::queue::GenericReceiveError; +use satrs::request::{Apid, GenericMessage, MessageMetadata}; use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::PusServiceId; -use satrs::spacepackets::time::cds::TimeProvider; -use satrs::spacepackets::time::TimeWriter; +use satrs::ComponentId; +use satrs_example::config::components::PUS_ROUTING_SERVICE; use satrs_example::config::{tmtc_err, CustomPusServiceId}; -use std::sync::mpsc::Sender; +use satrs_example::TimeStampHelper; +use std::fmt::Debug; +use std::sync::mpsc::{self, Sender}; pub mod action; pub mod event; pub mod hk; +pub mod mode; pub mod scheduler; pub mod stack; pub mod test; -pub struct PusTcMpscRouter { - pub test_service_receiver: Sender, - pub event_service_receiver: Sender, - pub sched_service_receiver: Sender, - pub hk_service_receiver: Sender, - pub action_service_receiver: Sender, +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum HandlingStatus { + Empty, + HandledOne, } -pub struct PusReceiver { +pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter { + let verif_cfg = VerificationReporterCfg::new(apid, 1, 2, 8).unwrap(); + // Every software component which needs to generate verification telemetry, gets a cloned + // verification reporter. + VerificationReporter::new(owner_id, &verif_cfg) +} + +/// Simple router structure which forwards PUS telecommands to dedicated handlers. +pub struct PusTcMpscRouter { + pub test_tc_sender: Sender, + pub event_tc_sender: Sender, + pub sched_tc_sender: Sender, + pub hk_tc_sender: Sender, + pub action_tc_sender: Sender, + pub mode_tc_sender: Sender, +} + +pub struct PusReceiver { + pub id: ComponentId, + pub tm_sender: TmSender, pub verif_reporter: VerificationReporter, pub pus_router: PusTcMpscRouter, stamp_helper: TimeStampHelper, } -struct TimeStampHelper { - stamper: TimeProvider, - time_stamp: [u8; 7], -} - -impl TimeStampHelper { - pub fn new() -> Self { +impl PusReceiver { + pub fn new(tm_sender: TmSender, pus_router: PusTcMpscRouter) -> Self { Self { - stamper: TimeProvider::new_with_u16_days(0, 0), - time_stamp: [0; 7], - } - } - - pub fn stamp(&self) -> &[u8] { - &self.time_stamp - } - - pub fn update_from_now(&mut self) { - self.stamper - .update_from_now() - .expect("Updating timestamp failed"); - self.stamper - .write_to_bytes(&mut self.time_stamp) - .expect("Writing timestamp failed"); - } -} - -impl PusReceiver { - pub fn new(verif_reporter: VerificationReporter, pus_router: PusTcMpscRouter) -> Self { - Self { - verif_reporter, + id: PUS_ROUTING_SERVICE.raw(), + tm_sender, + verif_reporter: create_verification_reporter( + PUS_ROUTING_SERVICE.id(), + PUS_ROUTING_SERVICE.apid, + ), pus_router, - stamp_helper: TimeStampHelper::new(), + stamp_helper: TimeStampHelper::default(), } } -} -impl PusReceiver { pub fn handle_tc_packet( &mut self, tc_in_memory: TcInMemory, @@ -80,41 +85,34 @@ impl PusReceiver match standard_service { - PusServiceId::Test => { - self.pus_router.test_service_receiver.send(EcssTcAndToken { - tc_in_memory, - token: Some(accepted_token.into()), - })? - } + PusServiceId::Test => self.pus_router.test_tc_sender.send(EcssTcAndToken { + tc_in_memory, + token: Some(accepted_token.into()), + })?, PusServiceId::Housekeeping => { - self.pus_router.hk_service_receiver.send(EcssTcAndToken { + self.pus_router.hk_tc_sender.send(EcssTcAndToken { tc_in_memory, token: Some(accepted_token.into()), })? } - PusServiceId::Event => { - self.pus_router - .event_service_receiver - .send(EcssTcAndToken { - tc_in_memory, - token: Some(accepted_token.into()), - })? - } + PusServiceId::Event => self.pus_router.event_tc_sender.send(EcssTcAndToken { + tc_in_memory, + token: Some(accepted_token.into()), + })?, PusServiceId::Scheduling => { - self.pus_router - .sched_service_receiver - .send(EcssTcAndToken { - tc_in_memory, - token: Some(accepted_token.into()), - })? + self.pus_router.sched_tc_sender.send(EcssTcAndToken { + tc_in_memory, + token: Some(accepted_token.into()), + })? } _ => { let result = self.verif_reporter.start_failure( + &self.tm_sender, accepted_token, FailParams::new( self.stamp_helper.stamp(), @@ -131,14 +129,17 @@ impl PusReceiver { - // TODO: Fix mode service. - //self.handle_mode_service(pus_tc, accepted_token) + self.pus_router.mode_tc_sender.send(EcssTcAndToken { + tc_in_memory, + token: Some(accepted_token.into()), + })? } CustomPusServiceId::Health => {} } } else { self.verif_reporter .start_failure( + &self.tm_sender, accepted_token, FailParams::new( self.stamp_helper.stamp(), @@ -154,55 +155,550 @@ impl PusReceiver {} +pub trait TargetedPusService { + /// Returns [true] if the packet handling is finished. + fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> bool; + fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus; + fn check_for_request_timeouts(&mut self); +} -impl PusRoutingErrorHandler for GenericRoutingErrorHandler { - type Error = satrs::pus::GenericRoutingError; +/// This is a generic handler class for all PUS services where a PUS telecommand is converted +/// to a targeted request. +/// +/// The generic steps for this process are the following +/// +/// 1. Poll for TC packets +/// 2. Convert the raw packets to a [PusTcReader]. +/// 3. Convert the PUS TC to a typed request using the [PusTcToRequestConverter]. +/// 4. Route the requests using the [GenericRequestRouter]. +/// 5. Add the request to the active request map using the [ActiveRequestMapProvider] abstraction. +/// 6. Check for replies which complete the forwarded request. The handler takes care of +/// the verification process. +/// 7. Check for timeouts of active requests. Generally, the timeout on the service level should +/// be highest expected timeout for the given target. +/// +/// The handler exposes the following API: +/// +/// 1. [Self::handle_one_tc] which tries to poll and handle one TC packet, covering steps 1-5. +/// 2. [Self::check_one_reply] which tries to poll and handle one reply, covering step 6. +/// 3. [Self::check_for_request_timeouts] which checks for request timeouts, covering step 7. +pub struct PusTargetedRequestService< + TcReceiver: EcssTcReceiverCore, + TmSender: EcssTmSenderCore, + TcInMemConverter: EcssTcInMemConverter, + VerificationReporter: VerificationReportingProvider, + RequestConverter: PusTcToRequestConverter, + ReplyHandler: PusReplyHandler, + ActiveRequestMap: ActiveRequestMapProvider, + ActiveRequestInfo: ActiveRequestProvider, + RequestType, + ReplyType, +> { + pub service_helper: + PusServiceHelper, + pub request_router: GenericRequestRouter, + pub request_converter: RequestConverter, + pub active_request_map: ActiveRequestMap, + pub reply_handler: ReplyHandler, + pub reply_receiver: mpsc::Receiver>, + phantom: std::marker::PhantomData<(RequestType, ActiveRequestInfo, ReplyType)>, +} - fn handle_error( - &self, - target_id: satrs::TargetId, - token: satrs::pus::verification::VerificationToken< - satrs::pus::verification::TcStateAccepted, +impl< + TcReceiver: EcssTcReceiverCore, + TmSender: EcssTmSenderCore, + TcInMemConverter: EcssTcInMemConverter, + VerificationReporter: VerificationReportingProvider, + RequestConverter: PusTcToRequestConverter, + ReplyHandler: PusReplyHandler, + ActiveRequestMap: ActiveRequestMapProvider, + ActiveRequestInfo: ActiveRequestProvider, + RequestType, + ReplyType, + > + PusTargetedRequestService< + TcReceiver, + TmSender, + TcInMemConverter, + VerificationReporter, + RequestConverter, + ReplyHandler, + ActiveRequestMap, + ActiveRequestInfo, + RequestType, + ReplyType, + > +where + GenericRequestRouter: PusRequestRouter, +{ + pub fn new( + service_helper: PusServiceHelper< + TcReceiver, + TmSender, + TcInMemConverter, + VerificationReporter, >, - _tc: &PusTcReader, - error: Self::Error, + request_converter: RequestConverter, + active_request_map: ActiveRequestMap, + reply_hook: ReplyHandler, + request_router: GenericRequestRouter, + reply_receiver: mpsc::Receiver>, + ) -> Self { + Self { + service_helper, + request_converter, + active_request_map, + reply_handler: reply_hook, + request_router, + reply_receiver, + phantom: std::marker::PhantomData, + } + } + + pub fn poll_and_handle_next_tc( + &mut self, + time_stamp: &[u8], + ) -> Result { + let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; + if possible_packet.is_none() { + return Ok(PusPacketHandlerResult::Empty); + } + let ecss_tc_and_token = possible_packet.unwrap(); + self.service_helper + .tc_in_mem_converter_mut() + .cache(&ecss_tc_and_token.tc_in_memory)?; + let tc = self.service_helper.tc_in_mem_converter().convert()?; + let (mut request_info, request) = match self.request_converter.convert( + ecss_tc_and_token.token, + &tc, + self.service_helper.tm_sender(), + &self.service_helper.common.verif_reporter, + time_stamp, + ) { + Ok((info, req)) => (info, req), + Err(e) => { + self.handle_conversion_to_request_error(&e, ecss_tc_and_token.token, time_stamp); + return Err(e.into()); + } + }; + let accepted_token: VerificationToken = request_info + .token() + .try_into() + .expect("token not in expected accepted state"); + let verif_request_id = verification::RequestId::new(&tc).raw(); + match self.request_router.route( + MessageMetadata::new(verif_request_id, self.service_helper.id()), + request_info.target_id(), + request, + ) { + Ok(()) => { + let started_token = self + .service_helper + .verif_reporter() + .start_success( + &self.service_helper.common.tm_sender, + accepted_token, + time_stamp, + ) + .expect("Start success failure"); + request_info.set_token(started_token.into()); + self.active_request_map + .insert(&verif_request_id, request_info); + } + Err(e) => { + self.request_router.handle_error_generic( + &request_info, + &tc, + e.clone(), + self.service_helper.tm_sender(), + self.service_helper.verif_reporter(), + time_stamp, + ); + return Err(e.into()); + } + } + Ok(PusPacketHandlerResult::RequestHandled) + } + + fn handle_conversion_to_request_error( + &mut self, + error: &GenericConversionError, + token: VerificationToken, time_stamp: &[u8], - verif_reporter: &impl VerificationReportingProvider, ) { - warn!("Routing request for service {SERVICE_ID} failed: {error:?}"); match error { - GenericRoutingError::UnknownTargetId(id) => { - let mut fail_data: [u8; 8] = [0; 8]; - fail_data.copy_from_slice(&id.to_be_bytes()); - verif_reporter - .start_failure( + GenericConversionError::WrongService(service) => { + let service_slice: [u8; 1] = [*service]; + self.service_helper + .verif_reporter() + .completion_failure( + self.service_helper.tm_sender(), token, - FailParams::new(time_stamp, &tmtc_err::UNKNOWN_TARGET_ID, &fail_data), + FailParams::new(time_stamp, &tmtc_err::INVALID_PUS_SERVICE, &service_slice), ) - .expect("Sending start failure failed"); + .expect("Sending completion failure failed"); } - GenericRoutingError::SendError(_) => { - let mut fail_data: [u8; 8] = [0; 8]; - fail_data.copy_from_slice(&target_id.to_be_bytes()); - verif_reporter - .start_failure( + GenericConversionError::InvalidSubservice(subservice) => { + let subservice_slice: [u8; 1] = [*subservice]; + self.service_helper + .verif_reporter() + .completion_failure( + self.service_helper.tm_sender(), token, - FailParams::new(time_stamp, &tmtc_err::ROUTING_ERROR, &fail_data), + FailParams::new( + time_stamp, + &tmtc_err::INVALID_PUS_SUBSERVICE, + &subservice_slice, + ), ) - .expect("Sending start failure failed"); + .expect("Sending completion failure failed"); } - GenericRoutingError::NotEnoughAppData { expected, found } => { - let mut context_info = (found as u32).to_be_bytes().to_vec(); - context_info.extend_from_slice(&(expected as u32).to_be_bytes()); - verif_reporter - .start_failure( + GenericConversionError::NotEnoughAppData { expected, found } => { + let mut context_info = (*found as u32).to_be_bytes().to_vec(); + context_info.extend_from_slice(&(*expected as u32).to_be_bytes()); + self.service_helper + .verif_reporter() + .completion_failure( + self.service_helper.tm_sender(), token, FailParams::new(time_stamp, &tmtc_err::NOT_ENOUGH_APP_DATA, &context_info), ) - .expect("Sending start failure failed"); + .expect("Sending completion failure failed"); + } + // Do nothing.. this is service-level and can not be handled generically here. + GenericConversionError::InvalidAppData(_) => (), + } + } + + pub fn poll_and_check_next_reply( + &mut self, + time_stamp: &[u8], + ) -> Result { + match self.reply_receiver.try_recv() { + Ok(reply) => { + self.handle_reply(&reply, time_stamp)?; + Ok(HandlingStatus::HandledOne) + } + Err(e) => match e { + mpsc::TryRecvError::Empty => Ok(HandlingStatus::Empty), + mpsc::TryRecvError::Disconnected => Err(EcssTmtcError::Receive( + GenericReceiveError::TxDisconnected(None), + )), + }, + } + } + + pub fn handle_reply( + &mut self, + reply: &GenericMessage, + time_stamp: &[u8], + ) -> Result<(), EcssTmtcError> { + let active_req_opt = self.active_request_map.get(reply.request_id()); + if active_req_opt.is_none() { + self.reply_handler + .handle_unrequested_reply(reply, &self.service_helper.common.tm_sender)?; + return Ok(()); + } + let active_request = active_req_opt.unwrap(); + let request_finished = self + .reply_handler + .handle_reply( + reply, + active_request, + &self.service_helper.common.tm_sender, + &self.service_helper.common.verif_reporter, + time_stamp, + ) + .unwrap_or(false); + if request_finished { + self.active_request_map.remove(reply.request_id()); + } + Ok(()) + } + + pub fn check_for_request_timeouts(&mut self) { + let mut requests_to_delete = Vec::new(); + self.active_request_map + .for_each(|request_id, request_info| { + if request_info.has_timed_out() { + requests_to_delete.push(*request_id); + } + }); + if !requests_to_delete.is_empty() { + for request_id in requests_to_delete { + self.active_request_map.remove(request_id); } } } } + +/// Generic timeout handling: Handle the verification failure with a dedicated return code +/// and also log the error. +pub fn generic_pus_request_timeout_handler( + sender: &(impl EcssTmSenderCore + ?Sized), + active_request: &(impl ActiveRequestProvider + Debug), + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + service_str: &'static str, +) -> Result<(), EcssTmtcError> { + log::warn!("timeout for active request {active_request:?} on {service_str} service"); + let started_token: VerificationToken = active_request + .token() + .try_into() + .expect("token not in expected started state"); + verification_handler.completion_failure( + sender, + started_token, + FailParams::new(time_stamp, &tmtc_err::REQUEST_TIMEOUT, &[]), + )?; + Ok(()) +} + +#[cfg(test)] +pub(crate) mod tests { + use std::time::Duration; + + use satrs::pus::test_util::TEST_COMPONENT_ID_0; + use satrs::pus::{MpscTmAsVecSender, PusTmAsVec, PusTmVariant}; + use satrs::request::RequestId; + use satrs::{ + pus::{ + verification::test_util::TestVerificationReporter, ActivePusRequestStd, + ActiveRequestMapProvider, EcssTcInVecConverter, MpscTcReceiver, + }, + request::UniqueApidTargetId, + spacepackets::{ + ecss::{ + tc::{PusTcCreator, PusTcSecondaryHeader}, + WritablePusPacket, + }, + SpHeader, + }, + }; + + use crate::requests::CompositeRequest; + + use super::*; + + // Testbench dedicated to the testing of [PusReplyHandler]s + pub struct ReplyHandlerTestbench< + ReplyHandler: PusReplyHandler, + ActiveRequestInfo: ActiveRequestProvider, + Reply, + > { + pub id: ComponentId, + pub verif_reporter: TestVerificationReporter, + pub reply_handler: ReplyHandler, + pub tm_receiver: mpsc::Receiver, + pub default_timeout: Duration, + tm_sender: MpscTmAsVecSender, + phantom: std::marker::PhantomData<(ActiveRequestInfo, Reply)>, + } + + impl< + ReplyHandler: PusReplyHandler, + ActiveRequestInfo: ActiveRequestProvider, + Reply, + > ReplyHandlerTestbench + { + pub fn new(owner_id: ComponentId, reply_handler: ReplyHandler) -> Self { + let test_verif_reporter = TestVerificationReporter::new(owner_id); + let (tm_sender, tm_receiver) = mpsc::channel(); + Self { + id: TEST_COMPONENT_ID_0.raw(), + verif_reporter: test_verif_reporter, + reply_handler, + default_timeout: Duration::from_secs(30), + tm_sender, + tm_receiver, + phantom: std::marker::PhantomData, + } + } + + pub fn add_tc( + &mut self, + apid: u16, + apid_target: u32, + time_stamp: &[u8], + ) -> (verification::RequestId, ActivePusRequestStd) { + let sp_header = SpHeader::new_from_apid(apid); + let sec_header_dummy = PusTcSecondaryHeader::new_simple(0, 0); + let init = self.verif_reporter.add_tc(&PusTcCreator::new( + sp_header, + sec_header_dummy, + &[], + true, + )); + let accepted = self + .verif_reporter + .acceptance_success(&self.tm_sender, init, time_stamp) + .expect("acceptance failed"); + let started = self + .verif_reporter + .start_success(&self.tm_sender, accepted, time_stamp) + .expect("start failed"); + ( + started.request_id(), + ActivePusRequestStd::new( + UniqueApidTargetId::new(apid, apid_target).raw(), + started, + self.default_timeout, + ), + ) + } + + pub fn handle_reply( + &mut self, + reply: &GenericMessage, + active_request: &ActiveRequestInfo, + time_stamp: &[u8], + ) -> Result { + self.reply_handler.handle_reply( + reply, + active_request, + &self.tm_sender, + &self.verif_reporter, + time_stamp, + ) + } + + pub fn handle_unrequested_reply( + &mut self, + reply: &GenericMessage, + ) -> Result<(), ReplyHandler::Error> { + self.reply_handler + .handle_unrequested_reply(reply, &self.tm_sender) + } + pub fn handle_request_timeout( + &mut self, + active_request_info: &ActiveRequestInfo, + time_stamp: &[u8], + ) -> Result<(), ReplyHandler::Error> { + self.reply_handler.handle_request_timeout( + active_request_info, + &self.tm_sender, + &self.verif_reporter, + time_stamp, + ) + } + } + + #[derive(Default)] + pub struct DummySender {} + + /// Dummy sender component which does nothing on the [Self::send_tm] call. + /// + /// Useful for unit tests. + impl EcssTmSenderCore for DummySender { + fn send_tm(&self, _source_id: ComponentId, _tm: PusTmVariant) -> Result<(), EcssTmtcError> { + Ok(()) + } + } + + // Testbench dedicated to the testing of [PusTcToRequestConverter]s + pub struct PusConverterTestbench< + Converter: PusTcToRequestConverter, + ActiveRequestInfo: ActiveRequestProvider, + Request, + > { + pub id: ComponentId, + pub verif_reporter: TestVerificationReporter, + pub converter: Converter, + dummy_sender: DummySender, + current_request_id: Option, + current_packet: Option>, + phantom: std::marker::PhantomData<(ActiveRequestInfo, Request)>, + } + + impl< + Converter: PusTcToRequestConverter, + ActiveRequestInfo: ActiveRequestProvider, + Request, + > PusConverterTestbench + { + pub fn new(owner_id: ComponentId, converter: Converter) -> Self { + let test_verif_reporter = TestVerificationReporter::new(owner_id); + Self { + id: owner_id, + verif_reporter: test_verif_reporter, + converter, + dummy_sender: DummySender::default(), + current_request_id: None, + current_packet: None, + phantom: std::marker::PhantomData, + } + } + + pub fn add_tc(&mut self, tc: &PusTcCreator) -> VerificationToken { + let token = self.verif_reporter.add_tc(tc); + self.current_request_id = Some(verification::RequestId::new(tc)); + self.current_packet = Some(tc.to_vec().unwrap()); + self.verif_reporter + .acceptance_success(&self.dummy_sender, token, &[]) + .expect("acceptance failed") + } + + pub fn request_id(&self) -> Option { + self.current_request_id + } + + pub fn convert( + &mut self, + token: VerificationToken, + time_stamp: &[u8], + expected_apid: u16, + expected_apid_target: u32, + ) -> Result<(ActiveRequestInfo, Request), Converter::Error> { + if self.current_packet.is_none() { + return Err(GenericConversionError::InvalidAppData( + "call add_tc first".to_string(), + )); + } + let current_packet = self.current_packet.take().unwrap(); + let tc_reader = PusTcReader::new(¤t_packet).unwrap(); + let (active_info, request) = self.converter.convert( + token, + &tc_reader.0, + &self.dummy_sender, + &self.verif_reporter, + time_stamp, + )?; + assert_eq!( + active_info.token().request_id(), + self.request_id().expect("no request id is set") + ); + assert_eq!( + active_info.target_id(), + UniqueApidTargetId::new(expected_apid, expected_apid_target).raw() + ); + Ok((active_info, request)) + } + } + + pub struct TargetedPusRequestTestbench< + RequestConverter: PusTcToRequestConverter, + ReplyHandler: PusReplyHandler, + ActiveRequestMap: ActiveRequestMapProvider, + ActiveRequestInfo: ActiveRequestProvider, + RequestType, + ReplyType, + > { + pub service: PusTargetedRequestService< + MpscTcReceiver, + MpscTmAsVecSender, + EcssTcInVecConverter, + TestVerificationReporter, + RequestConverter, + ReplyHandler, + ActiveRequestMap, + ActiveRequestInfo, + RequestType, + ReplyType, + >, + pub request_id: Option, + pub tm_funnel_rx: mpsc::Receiver, + pub pus_packet_tx: mpsc::Sender, + pub reply_tx: mpsc::Sender>, + pub request_rx: mpsc::Receiver>, + } +} diff --git a/satrs-example/src/pus/mode.rs b/satrs-example/src/pus/mode.rs new file mode 100644 index 0000000..4f2ff13 --- /dev/null +++ b/satrs-example/src/pus/mode.rs @@ -0,0 +1,434 @@ +use derive_new::new; +use log::{error, warn}; +use std::sync::mpsc; +use std::time::Duration; + +use crate::requests::GenericRequestRouter; +use satrs::pool::SharedStaticMemoryPool; +use satrs::pus::verification::VerificationReporter; +use satrs::pus::{ + DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, + EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, + PusPacketHandlerResult, PusServiceHelper, PusTmAsVec, PusTmInPool, TmInSharedPoolSender, +}; +use satrs::request::GenericMessage; +use satrs::{ + mode::{ModeAndSubmode, ModeReply, ModeRequest}, + pus::{ + mode::Subservice, + verification::{ + self, FailParams, TcStateAccepted, TcStateStarted, VerificationReportingProvider, + VerificationToken, + }, + ActivePusRequestStd, ActiveRequestProvider, EcssTmSenderCore, EcssTmtcError, + GenericConversionError, PusReplyHandler, PusTcToRequestConverter, PusTmVariant, + }, + request::UniqueApidTargetId, + spacepackets::{ + ecss::{ + tc::PusTcReader, + tm::{PusTmCreator, PusTmSecondaryHeader}, + PusPacket, + }, + SpHeader, + }, + ComponentId, +}; +use satrs_example::config::components::PUS_MODE_SERVICE; +use satrs_example::config::{mode_err, tmtc_err}; + +use super::{ + create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus, + PusTargetedRequestService, TargetedPusService, +}; + +#[derive(new)] +pub struct ModeReplyHandler { + owner_id: ComponentId, +} + +impl PusReplyHandler for ModeReplyHandler { + type Error = EcssTmtcError; + + fn handle_unrequested_reply( + &mut self, + reply: &GenericMessage, + _tm_sender: &impl EcssTmSenderCore, + ) -> Result<(), Self::Error> { + log::warn!("received unexpected reply for mode service 5: {reply:?}"); + Ok(()) + } + + fn handle_reply( + &mut self, + reply: &GenericMessage, + active_request: &ActivePusRequestStd, + tm_sender: &impl EcssTmSenderCore, + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result { + let started_token: VerificationToken = active_request + .token() + .try_into() + .expect("invalid token state"); + match reply.message { + ModeReply::ModeReply(mode_reply) => { + let mut source_data: [u8; 12] = [0; 12]; + mode_reply + .write_to_be_bytes(&mut source_data) + .expect("writing mode reply failed"); + let req_id = verification::RequestId::from(reply.request_id()); + let sp_header = SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), 0, 0); + let sec_header = + PusTmSecondaryHeader::new(200, Subservice::TmModeReply as u8, 0, 0, time_stamp); + let pus_tm = PusTmCreator::new(sp_header, sec_header, &source_data, true); + tm_sender.send_tm(self.owner_id, PusTmVariant::Direct(pus_tm))?; + verification_handler.completion_success(tm_sender, started_token, time_stamp)?; + } + ModeReply::CantReachMode(error_code) => { + verification_handler.completion_failure( + tm_sender, + started_token, + FailParams::new(time_stamp, &error_code, &[]), + )?; + } + ModeReply::WrongMode { expected, reached } => { + let mut error_info: [u8; 24] = [0; 24]; + let mut written_len = expected + .write_to_be_bytes(&mut error_info[0..ModeAndSubmode::RAW_LEN]) + .expect("writing expected mode failed"); + written_len += reached + .write_to_be_bytes(&mut error_info[ModeAndSubmode::RAW_LEN..]) + .expect("writing reached mode failed"); + verification_handler.completion_failure( + tm_sender, + started_token, + FailParams::new( + time_stamp, + &mode_err::WRONG_MODE, + &error_info[..written_len], + ), + )?; + } + }; + Ok(true) + } + + fn handle_request_timeout( + &mut self, + active_request: &ActivePusRequestStd, + tm_sender: &impl EcssTmSenderCore, + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result<(), Self::Error> { + generic_pus_request_timeout_handler( + tm_sender, + active_request, + verification_handler, + time_stamp, + "HK", + )?; + Ok(()) + } +} + +#[derive(Default)] +pub struct ModeRequestConverter {} + +impl PusTcToRequestConverter for ModeRequestConverter { + type Error = GenericConversionError; + + fn convert( + &mut self, + token: VerificationToken, + tc: &PusTcReader, + tm_sender: &(impl EcssTmSenderCore + ?Sized), + verif_reporter: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result<(ActivePusRequestStd, ModeRequest), Self::Error> { + let subservice = tc.subservice(); + let user_data = tc.user_data(); + let not_enough_app_data = |expected: usize| { + verif_reporter + .start_failure( + tm_sender, + token, + FailParams::new_no_fail_data(time_stamp, &tmtc_err::NOT_ENOUGH_APP_DATA), + ) + .expect("Sending start failure failed"); + Err(GenericConversionError::NotEnoughAppData { + expected, + found: user_data.len(), + }) + }; + if user_data.len() < core::mem::size_of::() { + return not_enough_app_data(4); + } + let target_id_and_apid = UniqueApidTargetId::from_pus_tc(tc).unwrap(); + let active_request = + ActivePusRequestStd::new(target_id_and_apid.into(), token, Duration::from_secs(30)); + let subservice_typed = Subservice::try_from(subservice); + let invalid_subservice = || { + // Invalid subservice + verif_reporter + .start_failure( + tm_sender, + token, + FailParams::new_no_fail_data(time_stamp, &tmtc_err::INVALID_PUS_SUBSERVICE), + ) + .expect("Sending start failure failed"); + Err(GenericConversionError::InvalidSubservice(subservice)) + }; + if subservice_typed.is_err() { + return invalid_subservice(); + } + let subservice_typed = subservice_typed.unwrap(); + match subservice_typed { + Subservice::TcSetMode => { + if user_data.len() < core::mem::size_of::() + ModeAndSubmode::RAW_LEN { + return not_enough_app_data(4 + ModeAndSubmode::RAW_LEN); + } + let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..]) + .expect("mode and submode extraction failed"); + Ok((active_request, ModeRequest::SetMode(mode_and_submode))) + } + Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)), + Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)), + Subservice::TcAnnounceModeRecursive => { + Ok((active_request, ModeRequest::AnnounceModeRecursive)) + } + _ => invalid_subservice(), + } + } +} + +pub fn create_mode_service_static( + tm_sender: TmInSharedPoolSender>, + tc_pool: SharedStaticMemoryPool, + pus_action_rx: mpsc::Receiver, + mode_router: GenericRequestRouter, + reply_receiver: mpsc::Receiver>, +) -> ModeServiceWrapper { + let mode_request_handler = PusTargetedRequestService::new( + PusServiceHelper::new( + PUS_MODE_SERVICE.id(), + pus_action_rx, + tm_sender, + create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid), + EcssTcInSharedStoreConverter::new(tc_pool, 2048), + ), + ModeRequestConverter::default(), + DefaultActiveRequestMap::default(), + ModeReplyHandler::new(PUS_MODE_SERVICE.id()), + mode_router, + reply_receiver, + ); + ModeServiceWrapper { + service: mode_request_handler, + } +} + +pub fn create_mode_service_dynamic( + tm_funnel_tx: mpsc::Sender, + pus_action_rx: mpsc::Receiver, + mode_router: GenericRequestRouter, + reply_receiver: mpsc::Receiver>, +) -> ModeServiceWrapper { + let mode_request_handler = PusTargetedRequestService::new( + PusServiceHelper::new( + PUS_MODE_SERVICE.id(), + pus_action_rx, + tm_funnel_tx, + create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid), + EcssTcInVecConverter::default(), + ), + ModeRequestConverter::default(), + DefaultActiveRequestMap::default(), + ModeReplyHandler::new(PUS_MODE_SERVICE.id()), + mode_router, + reply_receiver, + ); + ModeServiceWrapper { + service: mode_request_handler, + } +} + +pub struct ModeServiceWrapper { + pub(crate) service: PusTargetedRequestService< + MpscTcReceiver, + TmSender, + TcInMemConverter, + VerificationReporter, + ModeRequestConverter, + ModeReplyHandler, + DefaultActiveRequestMap, + ActivePusRequestStd, + ModeRequest, + ModeReply, + >, +} + +impl TargetedPusService + for ModeServiceWrapper +{ + /// Returns [true] if the packet handling is finished. + fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> bool { + match self.service.poll_and_handle_next_tc(time_stamp) { + Ok(result) => match result { + PusPacketHandlerResult::RequestHandled => {} + PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { + warn!("PUS mode service: partial packet handling success: {e:?}") + } + PusPacketHandlerResult::CustomSubservice(invalid, _) => { + warn!("PUS mode service: invalid subservice {invalid}"); + } + PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { + warn!("PUS mode service: {subservice} not implemented"); + } + PusPacketHandlerResult::Empty => { + return true; + } + }, + Err(error) => { + error!("PUS mode service: packet handling error: {error:?}") + } + } + false + } + + fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus { + self.service + .poll_and_check_next_reply(time_stamp) + .unwrap_or_else(|e| { + warn!("PUS action service: Handling reply failed with error {e:?}"); + HandlingStatus::HandledOne + }) + } + + fn check_for_request_timeouts(&mut self) { + self.service.check_for_request_timeouts(); + } +} +#[cfg(test)] +mod tests { + use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0, TEST_UNIQUE_ID_0}; + use satrs::request::MessageMetadata; + use satrs::{ + mode::{ModeAndSubmode, ModeReply, ModeRequest}, + pus::mode::Subservice, + request::GenericMessage, + spacepackets::{ + ecss::tc::{PusTcCreator, PusTcSecondaryHeader}, + SpHeader, + }, + }; + use satrs_example::config::tmtc_err; + + use crate::pus::{ + mode::ModeReplyHandler, + tests::{PusConverterTestbench, ReplyHandlerTestbench}, + }; + + use super::ModeRequestConverter; + + #[test] + fn mode_converter_read_mode_request() { + let mut testbench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcReadMode as u8); + let mut app_data: [u8; 4] = [0; 4]; + app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes()); + let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true); + let token = testbench.add_tc(&tc); + let (_active_req, req) = testbench + .convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion has failed"); + assert_eq!(req, ModeRequest::ReadMode); + } + + #[test] + fn mode_converter_set_mode_request() { + let mut testbench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcSetMode as u8); + let mut app_data: [u8; 4 + ModeAndSubmode::RAW_LEN] = [0; 4 + ModeAndSubmode::RAW_LEN]; + let mode_and_submode = ModeAndSubmode::new(2, 1); + app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes()); + mode_and_submode + .write_to_be_bytes(&mut app_data[4..]) + .unwrap(); + let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true); + let token = testbench.add_tc(&tc); + let (_active_req, req) = testbench + .convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion has failed"); + assert_eq!(req, ModeRequest::SetMode(mode_and_submode)); + } + + #[test] + fn mode_converter_announce_mode() { + let mut testbench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceMode as u8); + let mut app_data: [u8; 4] = [0; 4]; + app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes()); + let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true); + let token = testbench.add_tc(&tc); + let (_active_req, req) = testbench + .convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion has failed"); + assert_eq!(req, ModeRequest::AnnounceMode); + } + + #[test] + fn mode_converter_announce_mode_recursively() { + let mut testbench = + PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default()); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); + let sec_header = + PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceModeRecursive as u8); + let mut app_data: [u8; 4] = [0; 4]; + app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes()); + let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true); + let token = testbench.add_tc(&tc); + let (_active_req, req) = testbench + .convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0) + .expect("conversion has failed"); + assert_eq!(req, ModeRequest::AnnounceModeRecursive); + } + + #[test] + fn reply_handling_unrequested_reply() { + let mut testbench = ReplyHandlerTestbench::new( + TEST_COMPONENT_ID_0.id(), + ModeReplyHandler::new(TEST_COMPONENT_ID_0.id()), + ); + let mode_reply = ModeReply::ModeReply(ModeAndSubmode::new(5, 1)); + let unrequested_reply = + GenericMessage::new(MessageMetadata::new(10_u32, 15_u64), mode_reply); + // Right now this function does not do a lot. We simply check that it does not panic or do + // weird stuff. + let result = testbench.handle_unrequested_reply(&unrequested_reply); + assert!(result.is_ok()); + } + + #[test] + fn reply_handling_reply_timeout() { + let mut testbench = ReplyHandlerTestbench::new( + TEST_COMPONENT_ID_0.id(), + ModeReplyHandler::new(TEST_COMPONENT_ID_0.id()), + ); + let (req_id, active_request) = testbench.add_tc(TEST_APID, TEST_UNIQUE_ID_0, &[]); + let result = testbench.handle_request_timeout(&active_request, &[]); + assert!(result.is_ok()); + testbench.verif_reporter.assert_completion_failure( + TEST_COMPONENT_ID_0.raw(), + req_id, + None, + tmtc_err::REQUEST_TIMEOUT.raw() as u64, + ); + } +} diff --git a/satrs-example/src/pus/scheduler.rs b/satrs-example/src/pus/scheduler.rs index c5d2c06..d75c666 100644 --- a/satrs-example/src/pus/scheduler.rs +++ b/satrs-example/src/pus/scheduler.rs @@ -1,23 +1,18 @@ use std::sync::mpsc; use std::time::Duration; +use crate::pus::create_verification_reporter; use log::{error, info, warn}; -use satrs::pool::{PoolProvider, StaticMemoryPool, StoreAddr}; +use satrs::pool::{PoolProvider, StaticMemoryPool}; use satrs::pus::scheduler::{PusScheduler, TcInfo}; -use satrs::pus::scheduler_srv::PusService11SchedHandler; -use satrs::pus::verification::std_mod::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, -}; -use satrs::pus::verification::VerificationReportingProvider; +use satrs::pus::scheduler_srv::PusSchedServiceHandler; +use satrs::pus::verification::VerificationReporter; use satrs::pus::{ EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult, PusServiceHelper, - TmAsVecSenderWithId, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, - TmInSharedPoolSenderWithId, + EcssTmSenderCore, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, + PusPacketHandlerResult, PusServiceHelper, PusTmAsVec, PusTmInPool, TmInSharedPoolSender, }; -use satrs::tmtc::tm_helper::SharedTmPool; -use satrs::ChannelId; -use satrs_example::config::{TcReceiverId, TmSenderId, PUS_APID}; +use satrs_example::config::components::PUS_SCHED_SERVICE; use crate::tmtc::PusTcSourceProviderSharedPool; @@ -55,14 +50,12 @@ impl TcReleaser for mpsc::Sender> { } } -pub struct Pus11Wrapper< - TcReceiver: EcssTcReceiverCore, +pub struct SchedulingServiceWrapper< TmSender: EcssTmSenderCore, TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, > { - pub pus_11_handler: PusService11SchedHandler< - TcReceiver, + pub pus_11_handler: PusSchedServiceHandler< + MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter, @@ -73,12 +66,8 @@ pub struct Pus11Wrapper< pub tc_releaser: Box, } -impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - > Pus11Wrapper +impl + SchedulingServiceWrapper { pub fn release_tcs(&mut self) { let releaser = |enabled: bool, info: &TcInfo, tc: &[u8]| -> bool { @@ -103,8 +92,11 @@ impl< } } - pub fn handle_next_packet(&mut self) -> bool { - match self.pus_11_handler.handle_one_tc(&mut self.sched_tc_pool) { + pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> bool { + match self + .pus_11_handler + .poll_and_handle_next_tc(time_stamp, &mut self.sched_tc_pool) + { Ok(result) => match result { PusPacketHandlerResult::RequestHandled => {} PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { @@ -129,42 +121,24 @@ impl< } pub fn create_scheduler_service_static( - shared_tm_store: SharedTmPool, - tm_funnel_tx: mpsc::SyncSender, - verif_reporter: VerificationReporterWithSharedPoolMpscBoundedSender, + tm_sender: TmInSharedPoolSender>, tc_releaser: PusTcSourceProviderSharedPool, pus_sched_rx: mpsc::Receiver, sched_tc_pool: StaticMemoryPool, -) -> Pus11Wrapper< - MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, - EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, -> { - let sched_srv_tm_sender = TmInSharedPoolSenderWithId::new( - TmSenderId::PusSched as ChannelId, - "PUS_11_TM_SENDER", - shared_tm_store.clone(), - tm_funnel_tx.clone(), - ); - let sched_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusSched as ChannelId, - "PUS_11_TC_RECV", - pus_sched_rx, - ); +) -> SchedulingServiceWrapper { let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5)) .expect("Creating PUS Scheduler failed"); - let pus_11_handler = PusService11SchedHandler::new( + let pus_11_handler = PusSchedServiceHandler::new( PusServiceHelper::new( - sched_srv_receiver, - sched_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_SCHED_SERVICE.id(), + pus_sched_rx, + tm_sender, + create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid), EcssTcInSharedStoreConverter::new(tc_releaser.clone_backing_pool(), 2048), ), scheduler, ); - Pus11Wrapper { + SchedulingServiceWrapper { pus_11_handler, sched_tc_pool, releaser_buf: [0; 4096], @@ -173,40 +147,26 @@ pub fn create_scheduler_service_static( } pub fn create_scheduler_service_dynamic( - tm_funnel_tx: mpsc::Sender>, - verif_reporter: VerificationReporterWithVecMpscSender, + tm_funnel_tx: mpsc::Sender, tc_source_sender: mpsc::Sender>, pus_sched_rx: mpsc::Receiver, sched_tc_pool: StaticMemoryPool, -) -> Pus11Wrapper< - MpscTcReceiver, - TmAsVecSenderWithMpsc, - EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, -> { - let sched_srv_tm_sender = TmAsVecSenderWithId::new( - TmSenderId::PusSched as ChannelId, - "PUS_11_TM_SENDER", - tm_funnel_tx, - ); - let sched_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusSched as ChannelId, - "PUS_11_TC_RECV", - pus_sched_rx, - ); +) -> SchedulingServiceWrapper { + //let sched_srv_receiver = + //MpscTcReceiver::new(PUS_SCHED_SERVICE.raw(), "PUS_11_TC_RECV", pus_sched_rx); let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5)) .expect("Creating PUS Scheduler failed"); - let pus_11_handler = PusService11SchedHandler::new( + let pus_11_handler = PusSchedServiceHandler::new( PusServiceHelper::new( - sched_srv_receiver, - sched_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_SCHED_SERVICE.id(), + pus_sched_rx, + tm_funnel_tx, + create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid), EcssTcInVecConverter::default(), ), scheduler, ); - Pus11Wrapper { + SchedulingServiceWrapper { pus_11_handler, sched_tc_pool, releaser_buf: [0; 4096], diff --git a/satrs-example/src/pus/stack.rs b/satrs-example/src/pus/stack.rs index ed06e08..a11463c 100644 --- a/satrs-example/src/pus/stack.rs +++ b/satrs-example/src/pus/stack.rs @@ -1,69 +1,71 @@ -use satrs::pus::{ - verification::VerificationReportingProvider, EcssTcInMemConverter, EcssTcReceiverCore, - EcssTmSenderCore, +use crate::pus::mode::ModeServiceWrapper; +use derive_new::new; +use satrs::{ + pus::{EcssTcInMemConverter, EcssTmSenderCore}, + spacepackets::time::{cds, TimeWriter}, }; use super::{ - action::Pus8Wrapper, event::Pus5Wrapper, hk::Pus3Wrapper, scheduler::Pus11Wrapper, - test::Service17CustomWrapper, + action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper, + scheduler::SchedulingServiceWrapper, test::TestCustomServiceWrapper, HandlingStatus, + TargetedPusService, }; -pub struct PusStack< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, -> { - event_srv: Pus5Wrapper, - hk_srv: Pus3Wrapper, - action_srv: Pus8Wrapper, - schedule_srv: Pus11Wrapper, - test_srv: Service17CustomWrapper, +#[derive(new)] +pub struct PusStack { + test_srv: TestCustomServiceWrapper, + hk_srv_wrapper: HkServiceWrapper, + event_srv: EventServiceWrapper, + action_srv_wrapper: ActionServiceWrapper, + schedule_srv: SchedulingServiceWrapper, + mode_srv: ModeServiceWrapper, } -impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - > PusStack +impl + PusStack { - pub fn new( - hk_srv: Pus3Wrapper, - event_srv: Pus5Wrapper, - action_srv: Pus8Wrapper, - schedule_srv: Pus11Wrapper, - test_srv: Service17CustomWrapper< - TcReceiver, - TmSender, - TcInMemConverter, - VerificationReporter, - >, - ) -> Self { - Self { - event_srv, - action_srv, - schedule_srv, - test_srv, - hk_srv, - } - } - pub fn periodic_operation(&mut self) { + // Release all telecommands which reached their release time before calling the service + // handlers. self.schedule_srv.release_tcs(); + let time_stamp = cds::CdsTime::now_with_u16_days() + .expect("time stamp generation error") + .to_vec() + .unwrap(); loop { - let mut all_queues_empty = true; - let mut is_srv_finished = |srv_handler_finished: bool| { - if !srv_handler_finished { - all_queues_empty = false; - } - }; - is_srv_finished(self.test_srv.handle_next_packet()); - is_srv_finished(self.schedule_srv.handle_next_packet()); - is_srv_finished(self.event_srv.handle_next_packet()); - is_srv_finished(self.action_srv.handle_next_packet()); - is_srv_finished(self.hk_srv.handle_next_packet()); - if all_queues_empty { + let mut nothing_to_do = true; + let mut is_srv_finished = + |tc_handling_done: bool, reply_handling_done: Option| { + if !tc_handling_done + || (reply_handling_done.is_some() + && reply_handling_done.unwrap() == HandlingStatus::Empty) + { + nothing_to_do = false; + } + }; + is_srv_finished(self.test_srv.poll_and_handle_next_packet(&time_stamp), None); + is_srv_finished(self.schedule_srv.poll_and_handle_next_tc(&time_stamp), None); + is_srv_finished(self.event_srv.poll_and_handle_next_tc(&time_stamp), None); + is_srv_finished( + self.action_srv_wrapper.poll_and_handle_next_tc(&time_stamp), + Some( + self.action_srv_wrapper + .poll_and_handle_next_reply(&time_stamp), + ), + ); + is_srv_finished( + self.hk_srv_wrapper.poll_and_handle_next_tc(&time_stamp), + Some(self.hk_srv_wrapper.poll_and_handle_next_reply(&time_stamp)), + ); + is_srv_finished( + self.mode_srv.poll_and_handle_next_tc(&time_stamp), + Some(self.mode_srv.poll_and_handle_next_reply(&time_stamp)), + ); + if nothing_to_do { + // Timeout checking is only done once. + self.action_srv_wrapper.check_for_request_timeouts(); + self.hk_srv_wrapper.check_for_request_timeouts(); + self.mode_srv.check_for_request_timeouts(); break; } } diff --git a/satrs-example/src/pus/test.rs b/satrs-example/src/pus/test.rs index 17822a3..0111026 100644 --- a/satrs-example/src/pus/test.rs +++ b/satrs-example/src/pus/test.rs @@ -1,118 +1,74 @@ +use crate::pus::create_verification_reporter; use log::{info, warn}; -use satrs::params::Params; -use satrs::pool::{SharedStaticMemoryPool, StoreAddr}; +use satrs::event_man::{EventMessage, EventMessageU32}; +use satrs::pool::SharedStaticMemoryPool; use satrs::pus::test::PusService17TestHandler; -use satrs::pus::verification::{FailParams, VerificationReportingProvider}; -use satrs::pus::verification::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, -}; +use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider}; +use satrs::pus::EcssTcInSharedStoreConverter; use satrs::pus::{ - EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, EcssTcReceiverCore, - EcssTmSenderCore, MpscTcReceiver, PusPacketHandlerResult, PusServiceHelper, - TmAsVecSenderWithId, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, - TmInSharedPoolSenderWithId, + EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, EcssTmSenderCore, MpscTcReceiver, + MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult, PusServiceHelper, + PusTmAsVec, PusTmInPool, TmInSharedPoolSender, }; use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::PusPacket; -use satrs::spacepackets::time::cds::TimeProvider; +use satrs::spacepackets::time::cds::CdsTime; use satrs::spacepackets::time::TimeWriter; -use satrs::tmtc::tm_helper::SharedTmPool; -use satrs::ChannelId; -use satrs::{events::EventU32, pus::EcssTcInSharedStoreConverter}; -use satrs_example::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID, TEST_EVENT}; -use std::sync::mpsc::{self, Sender}; +use satrs_example::config::components::PUS_TEST_SERVICE; +use satrs_example::config::{tmtc_err, TEST_EVENT}; +use std::sync::mpsc; pub fn create_test_service_static( - shared_tm_store: SharedTmPool, - tm_funnel_tx: mpsc::SyncSender, - verif_reporter: VerificationReporterWithSharedPoolMpscBoundedSender, + tm_sender: TmInSharedPoolSender>, tc_pool: SharedStaticMemoryPool, - event_sender: mpsc::Sender<(EventU32, Option)>, + event_sender: mpsc::Sender, pus_test_rx: mpsc::Receiver, -) -> Service17CustomWrapper< - MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, - EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, -> { - let test_srv_tm_sender = TmInSharedPoolSenderWithId::new( - TmSenderId::PusTest as ChannelId, - "PUS_17_TM_SENDER", - shared_tm_store.clone(), - tm_funnel_tx.clone(), - ); - let test_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusTest as ChannelId, - "PUS_17_TC_RECV", - pus_test_rx, - ); +) -> TestCustomServiceWrapper { let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new( - test_srv_receiver, - test_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_TEST_SERVICE.id(), + pus_test_rx, + tm_sender, + create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid), EcssTcInSharedStoreConverter::new(tc_pool, 2048), )); - Service17CustomWrapper { - pus17_handler, + TestCustomServiceWrapper { + handler: pus17_handler, test_srv_event_sender: event_sender, } } pub fn create_test_service_dynamic( - tm_funnel_tx: mpsc::Sender>, - verif_reporter: VerificationReporterWithVecMpscSender, - event_sender: mpsc::Sender<(EventU32, Option)>, + tm_funnel_tx: mpsc::Sender, + event_sender: mpsc::Sender, pus_test_rx: mpsc::Receiver, -) -> Service17CustomWrapper< - MpscTcReceiver, - TmAsVecSenderWithMpsc, - EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, -> { - let test_srv_tm_sender = TmAsVecSenderWithId::new( - TmSenderId::PusTest as ChannelId, - "PUS_17_TM_SENDER", - tm_funnel_tx.clone(), - ); - let test_srv_receiver = MpscTcReceiver::new( - TcReceiverId::PusTest as ChannelId, - "PUS_17_TC_RECV", - pus_test_rx, - ); +) -> TestCustomServiceWrapper { let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new( - test_srv_receiver, - test_srv_tm_sender, - PUS_APID, - verif_reporter.clone(), + PUS_TEST_SERVICE.id(), + pus_test_rx, + tm_funnel_tx, + create_verification_reporter(PUS_TEST_SERVICE.id(), PUS_TEST_SERVICE.apid), EcssTcInVecConverter::default(), )); - Service17CustomWrapper { - pus17_handler, + TestCustomServiceWrapper { + handler: pus17_handler, test_srv_event_sender: event_sender, } } -pub struct Service17CustomWrapper< - TcReceiver: EcssTcReceiverCore, +pub struct TestCustomServiceWrapper< TmSender: EcssTmSenderCore, TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, > { - pub pus17_handler: - PusService17TestHandler, - pub test_srv_event_sender: Sender<(EventU32, Option)>, + pub handler: + PusService17TestHandler, + pub test_srv_event_sender: mpsc::Sender, } -impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - > Service17CustomWrapper +impl + TestCustomServiceWrapper { - pub fn handle_next_packet(&mut self) -> bool { - let res = self.pus17_handler.handle_one_tc(); + pub fn poll_and_handle_next_packet(&mut self, time_stamp: &[u8]) -> bool { + let res = self.handler.poll_and_handle_next_tc(time_stamp); if res.is_err() { warn!("PUS17 handler failed with error {:?}", res.unwrap_err()); return true; @@ -133,40 +89,42 @@ impl< } PusPacketHandlerResult::CustomSubservice(subservice, token) => { let (tc, _) = PusTcReader::new( - self.pus17_handler + self.handler .service_helper .tc_in_mem_converter .tc_slice_raw(), ) .unwrap(); - let time_stamper = TimeProvider::from_now_with_u16_days().unwrap(); + let time_stamper = CdsTime::now_with_u16_days().unwrap(); let mut stamp_buf: [u8; 7] = [0; 7]; time_stamper.write_to_bytes(&mut stamp_buf).unwrap(); if subservice == 128 { info!("Generating test event"); self.test_srv_event_sender - .send((TEST_EVENT.into(), None)) + .send(EventMessage::new(PUS_TEST_SERVICE.id(), TEST_EVENT.into())) .expect("Sending test event failed"); let start_token = self - .pus17_handler + .handler .service_helper - .common - .verification_handler - .start_success(token, &stamp_buf) + .verif_reporter() + .start_success(self.handler.service_helper.tm_sender(), token, &stamp_buf) .expect("Error sending start success"); - self.pus17_handler + self.handler .service_helper - .common - .verification_handler - .completion_success(start_token, &stamp_buf) + .verif_reporter() + .completion_success( + self.handler.service_helper.tm_sender(), + start_token, + &stamp_buf, + ) .expect("Error sending completion success"); } else { let fail_data = [tc.subservice()]; - self.pus17_handler + self.handler .service_helper - .common - .verification_handler + .verif_reporter() .start_failure( + self.handler.service_helper.tm_sender(), token, FailParams::new( &stamp_buf, diff --git a/satrs-example/src/requests.rs b/satrs-example/src/requests.rs index 6703d93..498be3f 100644 --- a/satrs-example/src/requests.rs +++ b/satrs-example/src/requests.rs @@ -1,94 +1,152 @@ use std::collections::HashMap; use std::sync::mpsc; -use derive_new::new; +use log::warn; use satrs::action::ActionRequest; use satrs::hk::HkRequest; use satrs::mode::ModeRequest; -use satrs::pus::action::PusActionRequestRouter; -use satrs::pus::hk::PusHkRequestRouter; -use satrs::pus::verification::{TcStateAccepted, VerificationToken}; -use satrs::pus::GenericRoutingError; +use satrs::pus::verification::{ + FailParams, TcStateAccepted, VerificationReportingProvider, VerificationToken, +}; +use satrs::pus::{ActiveRequestProvider, EcssTmSenderCore, GenericRoutingError, PusRequestRouter}; use satrs::queue::GenericSendError; -use satrs::TargetId; +use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId}; +use satrs::spacepackets::ecss::tc::PusTcReader; +use satrs::spacepackets::ecss::PusPacket; +use satrs::ComponentId; +use satrs_example::config::components::PUS_ROUTING_SERVICE; +use satrs_example::config::tmtc_err; -#[allow(dead_code)] -#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Debug)] #[non_exhaustive] -pub enum Request { +pub enum CompositeRequest { Hk(HkRequest), - Mode(ModeRequest), Action(ActionRequest), } -#[derive(Clone, Eq, PartialEq, Debug, new)] -pub struct TargetedRequest { - pub(crate) target_id: TargetId, - pub(crate) request: Request, +#[derive(Clone)] +pub struct GenericRequestRouter { + pub id: ComponentId, + // All messages which do not have a dedicated queue. + pub composite_router_map: HashMap>>, + pub mode_router_map: HashMap>>, } -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct RequestWithToken { - pub(crate) targeted_request: TargetedRequest, - pub(crate) token: VerificationToken, -} - -impl RequestWithToken { - pub fn new( - target_id: TargetId, - request: Request, - token: VerificationToken, - ) -> Self { +impl Default for GenericRequestRouter { + fn default() -> Self { Self { - targeted_request: TargetedRequest::new(target_id, request), - token, + id: PUS_ROUTING_SERVICE.raw(), + composite_router_map: Default::default(), + mode_router_map: Default::default(), } } } - -#[derive(Default, Clone)] -pub struct GenericRequestRouter(pub HashMap>); - -impl PusHkRequestRouter for GenericRequestRouter { +impl GenericRequestRouter { + pub(crate) fn handle_error_generic( + &self, + active_request: &impl ActiveRequestProvider, + tc: &PusTcReader, + error: GenericRoutingError, + tm_sender: &(impl EcssTmSenderCore + ?Sized), + verif_reporter: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) { + warn!( + "Routing request for service {} failed: {error:?}", + tc.service() + ); + let accepted_token: VerificationToken = active_request + .token() + .try_into() + .expect("token is not in accepted state"); + match error { + GenericRoutingError::UnknownTargetId(id) => { + let apid_target_id = UniqueApidTargetId::from(id); + warn!("Target APID for request: {}", apid_target_id.apid); + warn!("Target Unique ID for request: {}", apid_target_id.unique_id); + let mut fail_data: [u8; 8] = [0; 8]; + fail_data.copy_from_slice(&id.to_be_bytes()); + verif_reporter + .completion_failure( + tm_sender, + accepted_token, + FailParams::new(time_stamp, &tmtc_err::UNKNOWN_TARGET_ID, &fail_data), + ) + .expect("Sending start failure failed"); + } + GenericRoutingError::Send(_) => { + let mut fail_data: [u8; 8] = [0; 8]; + fail_data.copy_from_slice(&active_request.target_id().to_be_bytes()); + verif_reporter + .completion_failure( + tm_sender, + accepted_token, + FailParams::new(time_stamp, &tmtc_err::ROUTING_ERROR, &fail_data), + ) + .expect("Sending start failure failed"); + } + } + } +} +impl PusRequestRouter for GenericRequestRouter { type Error = GenericRoutingError; fn route( &self, - target_id: TargetId, + requestor_info: MessageMetadata, + target_id: ComponentId, hk_request: HkRequest, - token: VerificationToken, ) -> Result<(), Self::Error> { - if let Some(sender) = self.0.get(&target_id) { + if let Some(sender) = self.composite_router_map.get(&target_id) { sender - .send(RequestWithToken::new( - target_id, - Request::Hk(hk_request), - token, + .send(GenericMessage::new( + requestor_info, + CompositeRequest::Hk(hk_request), )) - .map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?; + .map_err(|_| GenericRoutingError::Send(GenericSendError::RxDisconnected))?; + return Ok(()); } - Ok(()) + Err(GenericRoutingError::UnknownTargetId(target_id)) } } -impl PusActionRequestRouter for GenericRequestRouter { +impl PusRequestRouter for GenericRequestRouter { type Error = GenericRoutingError; fn route( &self, - target_id: TargetId, + requestor_info: MessageMetadata, + target_id: ComponentId, action_request: ActionRequest, - token: VerificationToken, ) -> Result<(), Self::Error> { - if let Some(sender) = self.0.get(&target_id) { + if let Some(sender) = self.composite_router_map.get(&target_id) { sender - .send(RequestWithToken::new( - target_id, - Request::Action(action_request), - token, + .send(GenericMessage::new( + requestor_info, + CompositeRequest::Action(action_request), )) - .map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?; + .map_err(|_| GenericRoutingError::Send(GenericSendError::RxDisconnected))?; + return Ok(()); } - Ok(()) + Err(GenericRoutingError::UnknownTargetId(target_id)) + } +} + +impl PusRequestRouter for GenericRequestRouter { + type Error = GenericRoutingError; + + fn route( + &self, + requestor_info: MessageMetadata, + target_id: ComponentId, + request: ModeRequest, + ) -> Result<(), Self::Error> { + if let Some(sender) = self.mode_router_map.get(&target_id) { + sender + .send(GenericMessage::new(requestor_info, request)) + .map_err(|_| GenericRoutingError::Send(GenericSendError::RxDisconnected))?; + return Ok(()); + } + Err(GenericRoutingError::UnknownTargetId(target_id)) } } diff --git a/satrs-example/src/tcp.rs b/satrs-example/src/tcp.rs index 014f300..04bb136 100644 --- a/satrs-example/src/tcp.rs +++ b/satrs-example/src/tcp.rs @@ -1,5 +1,5 @@ use std::{ - collections::VecDeque, + collections::{HashSet, VecDeque}, sync::{Arc, Mutex}, }; @@ -10,12 +10,9 @@ use satrs::{ spacepackets::PacketId, tmtc::{CcsdsDistributor, CcsdsError, ReceivesCcsdsTc, TmPacketSourceCore}, }; -use satrs_example::config::PUS_APID; use crate::ccsds::CcsdsReceiver; -pub const PACKET_ID_LOOKUP: &[PacketId] = &[PacketId::const_tc(true, PUS_APID)]; - #[derive(Default, Clone)] pub struct SyncTcpTmSource { tm_queue: Arc>>>, @@ -77,6 +74,7 @@ pub type TcpServerType = TcpSpacepacketsServer< CcsdsError, SyncTcpTmSource, CcsdsDistributor, MpscErrorType>, + HashSet, >; pub struct TcpTask< @@ -103,14 +101,10 @@ impl< cfg: ServerConfig, tm_source: SyncTcpTmSource, tc_receiver: CcsdsDistributor, MpscErrorType>, + packet_id_lookup: HashSet, ) -> Result { Ok(Self { - server: TcpSpacepacketsServer::new( - cfg, - tm_source, - tc_receiver, - Box::new(PACKET_ID_LOOKUP), - )?, + server: TcpSpacepacketsServer::new(cfg, tm_source, tc_receiver, packet_id_lookup)?, }) } diff --git a/satrs-example/src/tm_funnel.rs b/satrs-example/src/tm_funnel.rs index 8b6285f..61cddd1 100644 --- a/satrs-example/src/tm_funnel.rs +++ b/satrs-example/src/tm_funnel.rs @@ -4,8 +4,9 @@ use std::{ }; use log::info; +use satrs::pus::{PusTmAsVec, PusTmInPool}; use satrs::{ - pool::{PoolProvider, StoreAddr}, + pool::PoolProvider, seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore}, spacepackets::{ ecss::{tm::PusTmZeroCopyWriter, PusPacket}, @@ -77,16 +78,16 @@ impl TmFunnelCommon { pub struct TmFunnelStatic { common: TmFunnelCommon, shared_tm_store: SharedTmPool, - tm_funnel_rx: mpsc::Receiver, - tm_server_tx: mpsc::SyncSender, + tm_funnel_rx: mpsc::Receiver, + tm_server_tx: mpsc::SyncSender, } impl TmFunnelStatic { pub fn new( shared_tm_store: SharedTmPool, sync_tm_tcp_source: SyncTcpTmSource, - tm_funnel_rx: mpsc::Receiver, - tm_server_tx: mpsc::SyncSender, + tm_funnel_rx: mpsc::Receiver, + tm_server_tx: mpsc::SyncSender, ) -> Self { Self { common: TmFunnelCommon::new(sync_tm_tcp_source), @@ -97,14 +98,14 @@ impl TmFunnelStatic { } pub fn operation(&mut self) { - if let Ok(addr) = self.tm_funnel_rx.recv() { + if let Ok(pus_tm_in_pool) = self.tm_funnel_rx.recv() { // Read the TM, set sequence counter and message counter, and finally update // the CRC. let shared_pool = self.shared_tm_store.clone_backing_pool(); let mut pool_guard = shared_pool.write().expect("Locking TM pool failed"); let mut tm_copy = Vec::new(); pool_guard - .modify(&addr, |buf| { + .modify(&pus_tm_in_pool.store_addr, |buf| { let zero_copy_writer = PusTmZeroCopyWriter::new(buf, MIN_CDS_FIELD_LEN) .expect("Creating TM zero copy writer failed"); self.common.apply_packet_processing(zero_copy_writer); @@ -112,7 +113,7 @@ impl TmFunnelStatic { }) .expect("Reading TM from pool failed"); self.tm_server_tx - .send(addr) + .send(pus_tm_in_pool) .expect("Sending TM to server failed"); // We could also do this step in the update closure, but I'd rather avoid this, could // lead to nested locking. @@ -123,15 +124,15 @@ impl TmFunnelStatic { pub struct TmFunnelDynamic { common: TmFunnelCommon, - tm_funnel_rx: mpsc::Receiver>, - tm_server_tx: mpsc::Sender>, + tm_funnel_rx: mpsc::Receiver, + tm_server_tx: mpsc::Sender, } impl TmFunnelDynamic { pub fn new( sync_tm_tcp_source: SyncTcpTmSource, - tm_funnel_rx: mpsc::Receiver>, - tm_server_tx: mpsc::Sender>, + tm_funnel_rx: mpsc::Receiver, + tm_server_tx: mpsc::Sender, ) -> Self { Self { common: TmFunnelCommon::new(sync_tm_tcp_source), @@ -144,13 +145,13 @@ impl TmFunnelDynamic { if let Ok(mut tm) = self.tm_funnel_rx.recv() { // Read the TM, set sequence counter and message counter, and finally update // the CRC. - let zero_copy_writer = PusTmZeroCopyWriter::new(&mut tm, MIN_CDS_FIELD_LEN) + let zero_copy_writer = PusTmZeroCopyWriter::new(&mut tm.packet, MIN_CDS_FIELD_LEN) .expect("Creating TM zero copy writer failed"); self.common.apply_packet_processing(zero_copy_writer); + self.common.sync_tm_tcp_source.add_tm(&tm.packet); self.tm_server_tx - .send(tm.clone()) + .send(tm) .expect("Sending TM to server failed"); - self.common.sync_tm_tcp_source.add_tm(&tm); } } } diff --git a/satrs-example/src/tmtc.rs b/satrs-example/src/tmtc.rs index 0a43504..43d5889 100644 --- a/satrs-example/src/tmtc.rs +++ b/satrs-example/src/tmtc.rs @@ -1,8 +1,7 @@ use log::warn; -use satrs::pus::verification::std_mod::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, +use satrs::pus::{ + EcssTcAndToken, MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, ReceivesEcssPusTc, }; -use satrs::pus::{EcssTcAndToken, ReceivesEcssPusTc}; use satrs::spacepackets::SpHeader; use std::sync::mpsc::{self, Receiver, SendError, Sender, SyncSender, TryRecvError}; use thiserror::Error; @@ -100,14 +99,14 @@ pub struct TcSourceTaskStatic { shared_tc_pool: SharedTcPool, tc_receiver: Receiver, tc_buf: [u8; 4096], - pus_receiver: PusReceiver, + pus_receiver: PusReceiver, } impl TcSourceTaskStatic { pub fn new( shared_tc_pool: SharedTcPool, tc_receiver: Receiver, - pus_receiver: PusReceiver, + pus_receiver: PusReceiver, ) -> Self { Self { shared_tc_pool, @@ -164,13 +163,13 @@ impl TcSourceTaskStatic { // TC source components where the heap is the backing memory of the received telecommands. pub struct TcSourceTaskDynamic { pub tc_receiver: Receiver>, - pus_receiver: PusReceiver, + pus_receiver: PusReceiver, } impl TcSourceTaskDynamic { pub fn new( tc_receiver: Receiver>, - pus_receiver: PusReceiver, + pus_receiver: PusReceiver, ) -> Self { Self { tc_receiver, diff --git a/satrs-example/src/udp.rs b/satrs-example/src/udp.rs index b6d1f6b..2cb4823 100644 --- a/satrs-example/src/udp.rs +++ b/satrs-example/src/udp.rs @@ -1,12 +1,11 @@ -use std::{ - net::{SocketAddr, UdpSocket}, - sync::mpsc::Receiver, -}; +use std::net::{SocketAddr, UdpSocket}; +use std::sync::mpsc; use log::{info, warn}; +use satrs::pus::{PusTmAsVec, PusTmInPool}; use satrs::{ hal::std::udp_server::{ReceiveResult, UdpTcServer}, - pool::{PoolProviderWithGuards, SharedStaticMemoryPool, StoreAddr}, + pool::{PoolProviderWithGuards, SharedStaticMemoryPool}, tmtc::CcsdsError, }; @@ -15,20 +14,20 @@ pub trait UdpTmHandler { } pub struct StaticUdpTmHandler { - pub tm_rx: Receiver, + pub tm_rx: mpsc::Receiver, pub tm_store: SharedStaticMemoryPool, } impl UdpTmHandler for StaticUdpTmHandler { fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, &recv_addr: &SocketAddr) { - while let Ok(addr) = self.tm_rx.try_recv() { + while let Ok(pus_tm_in_pool) = self.tm_rx.try_recv() { let store_lock = self.tm_store.write(); if store_lock.is_err() { warn!("Locking TM store failed"); continue; } let mut store_lock = store_lock.unwrap(); - let pg = store_lock.read_with_guard(addr); + let pg = store_lock.read_with_guard(pus_tm_in_pool.store_addr); let read_res = pg.read_as_vec(); if read_res.is_err() { warn!("Error reading TM pool data"); @@ -44,20 +43,20 @@ impl UdpTmHandler for StaticUdpTmHandler { } pub struct DynamicUdpTmHandler { - pub tm_rx: Receiver>, + pub tm_rx: mpsc::Receiver, } impl UdpTmHandler for DynamicUdpTmHandler { fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr) { while let Ok(tm) = self.tm_rx.try_recv() { - if tm.len() > 9 { - let service = tm[7]; - let subservice = tm[8]; + if tm.packet.len() > 9 { + let service = tm.packet[7]; + let subservice = tm.packet[8]; info!("Sending PUS TM[{service},{subservice}]") } else { info!("Sending PUS TM"); } - let result = socket.send_to(&tm, recv_addr); + let result = socket.send_to(&tm.packet, recv_addr); if let Err(e) = result { warn!("Sending TM with UDP socket failed: {e}") } @@ -120,7 +119,7 @@ mod tests { }, tmtc::ReceivesTcCore, }; - use satrs_example::config::{OBSW_SERVER_ADDR, PUS_APID}; + use satrs_example::config::{components, OBSW_SERVER_ADDR}; use super::*; @@ -178,8 +177,8 @@ mod tests { udp_tc_server, tm_handler, }; - let mut sph = SpHeader::tc_unseg(PUS_APID, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true) + let sph = SpHeader::new_for_unseg_tc(components::Apid::GenericPus as u16, 0, 0); + let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true) .to_vec() .unwrap(); let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed"); diff --git a/satrs-mib/Cargo.toml b/satrs-mib/Cargo.toml index eeca52f..e97971d 100644 --- a/satrs-mib/Cargo.toml +++ b/satrs-mib/Cargo.toml @@ -23,7 +23,8 @@ version = "1" optional = true [dependencies.satrs-shared] -version = "0.1.2" +path = "../satrs-shared" +version = "0.1.3" features = ["serde"] [dependencies.satrs-mib-codegen] diff --git a/satrs-mib/codegen/Cargo.toml b/satrs-mib/codegen/Cargo.toml index 0d9522e..43ba785 100644 --- a/satrs-mib/codegen/Cargo.toml +++ b/satrs-mib/codegen/Cargo.toml @@ -26,7 +26,10 @@ features = ["full"] [dev-dependencies] trybuild = { version = "1", features = ["diff"] } -satrs-shared = "0.1.2" + +[dev-dependencies.satrs-shared] +version = "0.1.3" +path = "../../satrs-shared" [dev-dependencies.satrs-mib] path = ".." diff --git a/satrs-shared/Cargo.toml b/satrs-shared/Cargo.toml index e2998e9..fd110fd 100644 --- a/satrs-shared/Cargo.toml +++ b/satrs-shared/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "satrs-shared" description = "Components shared by multiple sat-rs crates" -version = "0.1.2" +version = "0.1.3" edition = "2021" authors = ["Robin Mueller "] homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/" @@ -18,7 +18,7 @@ default-features = false optional = true [dependencies.spacepackets] -version = "0.10" +version = "0.11.0-rc.2" default-features = false [features] diff --git a/satrs/CHANGELOG.md b/satrs/CHANGELOG.md index b0bc493..b539457 100644 --- a/satrs/CHANGELOG.md +++ b/satrs/CHANGELOG.md @@ -8,8 +8,30 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] +- `spacepackets` v0.11.0 + +## Added + +- Added `params::WritableToBeBytes::to_vec`. +- New `ComponentId` (`u64` typedef for now) which replaces former `TargetId` as a generic + way to identify components. +- Various abstraction and objects for targeted requests. This includes mode request/reply + types for actions, HK and modes. +- `VerificationReportingProvider::owner_id` method. +- Introduced generic `EventMessage` which is generic over the event type and the additional + parameter type. This message also contains the sender ID which can be useful for debugging + or application layer / FDIR logic. + ## Changed +- `encoding::ccsds::PacketIdValidator` renamed to `ValidatorU16Id`, which lives in the crate root. + It can be used for both CCSDS packet ID and CCSDS APID validation. +- `EventManager::try_event_handling` not expects a mutable error handling closure instead of + returning the occured errors. +- Renamed `EventManagerBase` to `EventReportCreator` +- Renamed `VerificationReporterCore` to `VerificationReportCreator`. +- Removed `VerificationReporterCore`. The high-level API exposed by `VerificationReporter` and + the low level API exposed by `VerificationReportCreator` should be sufficient for all use-cases. - Refactored `EventManager` to heavily use generics instead of trait objects. - `SendEventProvider` -> `EventSendProvider`. `id` trait method renamed to `channel_id`. - `ListenerTable` -> `ListenerMapProvider` @@ -18,16 +40,37 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Refactored ECSS TM sender abstractions to be generic over different message queue backends. - Refactored Verification Reporter abstractions and implementation to be generic over the sender instead of using trait objects. +- Renamed `WritableToBeBytes::raw_len` to `WritableToBeBytes::written_len` for consistency. - `PusServiceProvider` renamed to `PusServiceDistributor` to make the purpose of the object more clear - `PusServiceProvider::handle_pus_tc_packet` renamed to `PusServiceDistributor::distribute_packet`. - `PusServiceDistibutor` and `CcsdsDistributor` now use generics instead of trait objects. This makes accessing the concrete trait implementations more easy as well. +- Major overhaul of the PUS handling module. +- Replace `TargetId` by `ComponentId`. +- Replace most usages of `ChannelId` by `ComponentId`. A dedicated channel ID has limited usage + due to the nature of typed channels in Rust. +- `CheckTimer` renamed to `CountdownProvider`. +- Renamed `TargetId` to `ComponentId`. +- Replaced most `ChannelId` occurences with `ComponentId`. For typed channels, there is generally + no need for dedicated channel IDs. +- Changed `params::WritableToBeBytes::raw_len` to `written_len` for consistency. +- `EventReporter` caches component ID. +- Renamed `PusService11SchedHandler` to `PusSchedServiceHandler`. +- Fixed general naming of PUS handlers from `handle_one_tc` to `poll_and_handle_next_tc`. +- Reworked verification module: The sender (`impl EcssTmSenderCore`) + now needs to be passed explicitely to the `VerificationReportingProvider` abstraction. This + allows easier sharing of the TM sender component. ## Fixed - Update deprecated API for `PusScheduler::insert_wrapped_tc_cds_short` and `PusScheduler::insert_wrapped_tc_cds_long`. +- `EventReporter` uses interior mutability pattern to allow non-mutable API. + +## Removed + +- Remove `objects` module. # [v0.2.0-rc.0] 2024-02-21 diff --git a/satrs/Cargo.toml b/satrs/Cargo.toml index 5e3b27d..7f913e6 100644 --- a/satrs/Cargo.toml +++ b/satrs/Cargo.toml @@ -17,7 +17,10 @@ delegate = ">0.7, <=0.10" paste = "1" smallvec = "1" crc = "3" -satrs-shared = "0.1.2" + +[dependencies.satrs-shared] +version = "0.1.3" +path = "../satrs-shared" [dependencies.num_enum] version = ">0.5, <=0.7" @@ -68,7 +71,8 @@ features = ["all"] optional = true [dependencies.spacepackets] -version = "0.10" +# git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git" +version = "0.11.0-rc.2" default-features = false [dependencies.cobs] @@ -112,6 +116,8 @@ alloc = [ serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"] crossbeam = ["crossbeam-channel"] heapless = ["dep:heapless"] +defmt = ["spacepackets/defmt"] +test_util = [] doc-images = [] [package.metadata.docs.rs] diff --git a/satrs/release-checklist.md b/satrs/release-checklist.md index 349a903..e76959e 100644 --- a/satrs/release-checklist.md +++ b/satrs/release-checklist.md @@ -4,11 +4,11 @@ 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 'rustdocflags=["--cfg", "doc_cfg"]' --open`. + `cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' --open`. 2. Bump version specifier in `Cargo.toml`. 3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new `unreleased` section. -4. Run `cargo test --all-features`. +4. Run `cargo test --all-features` or `cargo nextest r --all-features` and `cargo test --doc`. 5. Run `cargo fmt` and `cargo clippy`. Check `cargo msrv` against MSRV in `Cargo.toml`. 6. Wait for CI/CD results for EGit and Github. These also check cross-compilation for bare-metal targets. diff --git a/satrs/src/action.rs b/satrs/src/action.rs index 7caeaa6..4aea9f1 100644 --- a/satrs/src/action.rs +++ b/satrs/src/action.rs @@ -1,63 +1,68 @@ -use crate::{pool::StoreAddr, TargetId}; +use crate::{params::Params, pool::StoreAddr}; + +#[cfg(feature = "alloc")] +pub use alloc_mod::*; pub type ActionId = u32; +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct ActionRequest { + pub action_id: ActionId, + pub variant: ActionRequestVariant, +} + +impl ActionRequest { + pub fn new(action_id: ActionId, variant: ActionRequestVariant) -> Self { + Self { action_id, variant } + } +} + #[non_exhaustive] #[derive(Clone, Eq, PartialEq, Debug)] -pub enum ActionRequest { - UnsignedIdAndStoreData { - action_id: ActionId, - data_addr: StoreAddr, - }, +pub enum ActionRequestVariant { + NoData, + StoreData(StoreAddr), #[cfg(feature = "alloc")] - UnsignedIdAndVecData { - action_id: ActionId, - data: alloc::vec::Vec, - }, - #[cfg(feature = "alloc")] - StringIdAndVecData { - action_id: alloc::string::String, - data: alloc::vec::Vec, - }, - #[cfg(feature = "alloc")] - StringIdAndStoreData { - action_id: alloc::string::String, - data: StoreAddr, - }, + VecData(alloc::vec::Vec), } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TargetedActionRequest { - target: TargetId, - action_request: ActionRequest, -} - -impl TargetedActionRequest { - pub fn new(target: TargetId, action_request: ActionRequest) -> Self { - Self { - target, - action_request, - } - } +#[derive(Debug, PartialEq, Clone)] +pub struct ActionReply { + pub action_id: ActionId, + pub variant: ActionReplyVariant, } /// A reply to an action request. #[non_exhaustive] -#[derive(Clone, Eq, PartialEq, Debug)] -pub enum ActionReply { - CompletionFailed(ActionId), - StepFailed { - id: ActionId, - step: u32, - }, - Completed(ActionId), - #[cfg(feature = "alloc")] - CompletedStringId(alloc::string::String), - #[cfg(feature = "alloc")] - CompletionFailedStringId(alloc::string::String), - #[cfg(feature = "alloc")] - StepFailedStringId { - id: alloc::string::String, - step: u32, - }, +#[derive(Clone, Debug, PartialEq)] +pub enum ActionReplyVariant { + CompletionFailed(Params), + StepFailed { step: u32, reason: Params }, + Completed, } + +#[cfg(feature = "alloc")] +pub mod alloc_mod { + use super::*; + + #[derive(Debug, Eq, PartialEq, Clone)] + pub struct ActionRequestStringId { + pub action_id: alloc::string::String, + pub variant: ActionRequestVariant, + } + + impl ActionRequestStringId { + pub fn new(action_id: alloc::string::String, variant: ActionRequestVariant) -> Self { + Self { action_id, variant } + } + } + + #[derive(Debug, PartialEq, Clone)] + pub struct ActionReplyStringId { + pub action_id: alloc::string::String, + pub variant: ActionReplyVariant, + } +} + +#[cfg(test)] +mod tests {} diff --git a/satrs/src/cfdp/dest.rs b/satrs/src/cfdp/dest.rs index b42df3a..4a87ce6 100644 --- a/satrs/src/cfdp/dest.rs +++ b/satrs/src/cfdp/dest.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use super::{ filestore::{FilestoreError, VirtualFilestore}, user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams}, - CheckTimer, CheckTimerCreator, EntityType, LocalEntityConfig, PacketInfo, PacketTarget, + CheckTimerCreator, CountdownProvider, EntityType, LocalEntityConfig, PacketInfo, PacketTarget, RemoteEntityConfig, RemoteEntityConfigProvider, State, TimerContext, TransactionId, TransactionStep, }; @@ -54,7 +54,7 @@ struct TransferState { completion_disposition: CompletionDisposition, checksum: u32, current_check_count: u32, - current_check_timer: Option>, + current_check_timer: Option>, } impl Default for TransferState { @@ -799,9 +799,9 @@ mod tests { }; use crate::cfdp::{ - filestore::NativeFilestore, user::OwnedMetadataRecvdParams, CheckTimer, CheckTimerCreator, - DefaultFaultHandler, IndicationConfig, RemoteEntityConfig, StdRemoteEntityConfigProvider, - UserFaultHandler, CRC_32, + filestore::NativeFilestore, user::OwnedMetadataRecvdParams, CheckTimerCreator, + CountdownProvider, DefaultFaultHandler, IndicationConfig, RemoteEntityConfig, + StdRemoteEntityConfigProvider, UserFaultHandler, CRC_32, }; use super::*; @@ -1057,7 +1057,7 @@ mod tests { expired: Arc, } - impl CheckTimer for TestCheckTimer { + impl CountdownProvider for TestCheckTimer { fn has_expired(&self) -> bool { self.expired.load(core::sync::atomic::Ordering::Relaxed) } @@ -1088,7 +1088,10 @@ mod tests { } impl CheckTimerCreator for TestCheckTimerCreator { - fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box { + fn get_check_timer_provider( + &self, + timer_context: TimerContext, + ) -> Box { match timer_context { TimerContext::CheckLimit { .. } => { Box::new(TestCheckTimer::new(self.check_limit_expired_flag.clone())) diff --git a/satrs/src/cfdp/mod.rs b/satrs/src/cfdp/mod.rs index 8c88fda..c2f6d01 100644 --- a/satrs/src/cfdp/mod.rs +++ b/satrs/src/cfdp/mod.rs @@ -17,6 +17,8 @@ use alloc::boxed::Box; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate::time::CountdownProvider; + #[cfg(feature = "std")] pub mod dest; #[cfg(feature = "alloc")] @@ -45,7 +47,15 @@ pub enum TimerContext { }, } -/// Generic abstraction for a check timer which is used by 3 mechanisms of the CFDP protocol. +/// 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 /// @@ -74,22 +84,9 @@ pub enum TimerContext { /// 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. -pub trait CheckTimer: Debug { - fn has_expired(&self) -> bool; - fn reset(&mut self); -} - -/// 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. The [CheckTimer] documentation provides more information about the purpose of the -/// check timer in the context of CFDP. -/// -/// 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. #[cfg(feature = "alloc")] pub trait CheckTimerCreator { - fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box; + fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box; } /// Simple implementation of the [CheckTimerCreator] trait assuming a standard runtime. @@ -112,7 +109,7 @@ impl StdCheckTimer { } #[cfg(feature = "std")] -impl CheckTimer for StdCheckTimer { +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 { diff --git a/satrs/src/encoding/ccsds.rs b/satrs/src/encoding/ccsds.rs index 37694d7..30adccf 100644 --- a/satrs/src/encoding/ccsds.rs +++ b/satrs/src/encoding/ccsds.rs @@ -1,65 +1,4 @@ -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -#[cfg(feature = "alloc")] -use hashbrown::HashSet; -use spacepackets::PacketId; - -use crate::tmtc::ReceivesTcCore; - -pub trait PacketIdLookup { - fn validate(&self, packet_id: u16) -> bool; -} - -#[cfg(feature = "alloc")] -impl PacketIdLookup for Vec { - fn validate(&self, packet_id: u16) -> bool { - self.contains(&packet_id) - } -} - -#[cfg(feature = "alloc")] -impl PacketIdLookup for HashSet { - fn validate(&self, packet_id: u16) -> bool { - self.contains(&packet_id) - } -} - -impl PacketIdLookup for [u16] { - fn validate(&self, packet_id: u16) -> bool { - self.binary_search(&packet_id).is_ok() - } -} - -impl PacketIdLookup for &[u16] { - fn validate(&self, packet_id: u16) -> bool { - self.binary_search(&packet_id).is_ok() - } -} - -#[cfg(feature = "alloc")] -impl PacketIdLookup for Vec { - fn validate(&self, packet_id: u16) -> bool { - self.contains(&PacketId::from(packet_id)) - } -} -#[cfg(feature = "alloc")] -impl PacketIdLookup for HashSet { - fn validate(&self, packet_id: u16) -> bool { - self.contains(&PacketId::from(packet_id)) - } -} - -impl PacketIdLookup for [PacketId] { - fn validate(&self, packet_id: u16) -> bool { - self.binary_search(&PacketId::from(packet_id)).is_ok() - } -} - -impl PacketIdLookup for &[PacketId] { - fn validate(&self, packet_id: u16) -> bool { - self.binary_search(&PacketId::from(packet_id)).is_ok() - } -} +use crate::{tmtc::ReceivesTcCore, ValidatorU16Id}; /// This function parses a given buffer for tightly packed CCSDS space packets. It uses the /// [PacketId] field of the CCSDS packets to detect the start of a CCSDS space packet and then @@ -75,7 +14,7 @@ impl PacketIdLookup for &[PacketId] { /// error will be returned. pub fn parse_buffer_for_ccsds_space_packets( buf: &mut [u8], - packet_id_lookup: &(impl PacketIdLookup + ?Sized), + packet_id_validator: &(impl ValidatorU16Id + ?Sized), tc_receiver: &mut (impl ReceivesTcCore + ?Sized), next_write_idx: &mut usize, ) -> Result { @@ -88,7 +27,7 @@ pub fn parse_buffer_for_ccsds_space_packets( break; } let packet_id = u16::from_be_bytes(buf[current_idx..current_idx + 2].try_into().unwrap()); - if packet_id_lookup.validate(packet_id) { + if packet_id_validator.validate(packet_id) { let length_field = u16::from_be_bytes(buf[current_idx + 4..current_idx + 6].try_into().unwrap()); let packet_size = length_field + 7; @@ -123,13 +62,13 @@ mod tests { const TEST_APID_0: u16 = 0x02; const TEST_APID_1: u16 = 0x10; - const TEST_PACKET_ID_0: PacketId = PacketId::const_tc(true, TEST_APID_0); - const TEST_PACKET_ID_1: PacketId = PacketId::const_tc(true, TEST_APID_1); + const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0); + const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1); #[test] fn test_basic() { - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let sph = SpHeader::new_from_apid(TEST_APID_0); + let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let mut buffer: [u8; 32] = [0; 32]; let packet_len = ping_tc .write_to_bytes(&mut buffer) @@ -155,9 +94,9 @@ mod tests { #[test] fn test_multi_packet() { - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); - let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true); + let sph = SpHeader::new_from_apid(TEST_APID_0); + let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); + let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true); let mut buffer: [u8; 32] = [0; 32]; let packet_len_ping = ping_tc .write_to_bytes(&mut buffer) @@ -190,10 +129,10 @@ mod tests { #[test] fn test_multi_apid() { - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); - sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap(); - let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true); + let sph = SpHeader::new_from_apid(TEST_APID_0); + let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); + let sph = SpHeader::new_from_apid(TEST_APID_1); + let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true); let mut buffer: [u8; 32] = [0; 32]; let packet_len_ping = ping_tc .write_to_bytes(&mut buffer) @@ -226,10 +165,10 @@ mod tests { #[test] fn test_split_packet_multi() { - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); - sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap(); - let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true); + let ping_tc = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true); + let action_tc = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true); let mut buffer: [u8; 32] = [0; 32]; let packet_len_ping = ping_tc .write_to_bytes(&mut buffer) @@ -257,8 +196,8 @@ mod tests { #[test] fn test_one_split_packet() { - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let ping_tc = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true); let mut buffer: [u8; 32] = [0; 32]; let packet_len_ping = ping_tc .write_to_bytes(&mut buffer) diff --git a/satrs/src/event_man.rs b/satrs/src/event_man.rs index 304f9a1..38752eb 100644 --- a/satrs/src/event_man.rs +++ b/satrs/src/event_man.rs @@ -11,7 +11,7 @@ //! about events first: //! //! The event manager has a listener table abstracted by the [ListenerMapProvider], which maps -//! listener groups identified by [ListenerKey]s to a [sender ID][ChannelId]. +//! listener groups identified by [ListenerKey]s to a [listener ID][ComponentId]. //! It also contains a sender table abstracted by the [SenderMapProvider] which maps these sender //! IDs to concrete [EventSendProvider]s. A simple approach would be to use one send event provider //! for each OBSW thread and then subscribe for all interesting events for a particular thread @@ -28,8 +28,8 @@ //! manager. //! 3. The event manager receives the receiver component as part of a [EventReceiveProvider] //! implementation so all events are routed to the manager. -//! 4. Create the [send event providers][EventSendProvider]s which allow routing events to -//! subscribers. You can now use their [sender IDs][EventSendProvider::channel_id] to subscribe +//! 4. Create the [event sender map][SenderMapProvider]s which allow routing events to +//! subscribers. You can now use the subscriber component IDs to subscribe //! for event groups, for example by using the [EventManager::subscribe_single] method. //! 5. Add the send provider as well using the [EventManager::add_sender] call so the event //! manager can route listener groups to a the send provider. @@ -45,12 +45,13 @@ //! for a concrete example using multi-threading where events are routed to //! different threads. use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw}; -use crate::params::{Params, ParamsHeapless}; +use crate::params::Params; use crate::queue::GenericSendError; +use core::fmt::Debug; use core::marker::PhantomData; use core::slice::Iter; -use crate::ChannelId; +use crate::ComponentId; #[cfg(feature = "alloc")] pub use alloc_mod::*; @@ -65,87 +66,122 @@ pub enum ListenerKey { All, } -pub type EventWithHeaplessAuxData = (Event, Option); -pub type EventU32WithHeaplessAuxData = EventWithHeaplessAuxData; -pub type EventU16WithHeaplessAuxData = EventWithHeaplessAuxData; +#[derive(Debug)] +pub struct EventMessage { + sender_id: ComponentId, + event: Event, + params: Option, +} -pub type EventWithAuxData = (Event, Option); -pub type EventU32WithAuxData = EventWithAuxData; -pub type EventU16WithAuxData = EventWithAuxData; - -pub trait EventSendProvider { - fn channel_id(&self) -> ChannelId; - - fn send_no_data(&self, event: EV) -> Result<(), GenericSendError> { - self.send(event, None) +impl EventMessage { + pub fn new_generic( + sender_id: ComponentId, + event: Event, + params: Option<&ParamProvider>, + ) -> Self { + Self { + sender_id, + event, + params: params.cloned(), + } } - fn send(&self, event: EV, aux_data: Option) -> Result<(), GenericSendError>; + pub fn sender_id(&self) -> ComponentId { + self.sender_id + } + + pub fn event(&self) -> Event { + self.event + } + + pub fn params(&self) -> Option<&ParamProvider> { + self.params.as_ref() + } + + pub fn new(sender_id: ComponentId, event: Event) -> Self { + Self::new_generic(sender_id, event, None) + } + + pub fn new_with_params(sender_id: ComponentId, event: Event, params: &ParamProvider) -> Self { + Self::new_generic(sender_id, event, Some(params)) + } +} + +pub type EventMessageU32 = EventMessage; +pub type EventMessageU16 = EventMessage; + +/// Generic abstraction +pub trait EventSendProvider { + type Error; + + fn target_id(&self) -> ComponentId; + + fn send(&self, message: EventMessage) -> Result<(), Self::Error>; } /// Generic abstraction for an event receiver. -pub trait EventReceiveProvider { +pub trait EventReceiveProvider { + type Error; + /// This function has to be provided by any event receiver. A call may or may not return /// an event and optional auxiliary data. - fn try_recv_event(&self) -> Option<(Event, Option)>; + fn try_recv_event(&self) -> Result>, Self::Error>; } pub trait ListenerMapProvider { #[cfg(feature = "alloc")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] fn get_listeners(&self) -> alloc::vec::Vec; fn contains_listener(&self, key: &ListenerKey) -> bool; - fn get_listener_ids(&self, key: &ListenerKey) -> Option>; - fn add_listener(&mut self, key: ListenerKey, sender_id: ChannelId) -> bool; + fn get_listener_ids(&self, key: &ListenerKey) -> Option>; + fn add_listener(&mut self, key: ListenerKey, listener_id: ComponentId) -> bool; fn remove_duplicates(&mut self, key: &ListenerKey); } pub trait SenderMapProvider< - SP: EventSendProvider, - EV: GenericEvent = EventU32, - AUX = Params, + EventSender: EventSendProvider, + Event: GenericEvent = EventU32, + ParamProvider: Debug = Params, > { - fn contains_send_event_provider(&self, id: &ChannelId) -> bool; + fn contains_send_event_provider(&self, target_id: &ComponentId) -> bool; - fn get_send_event_provider(&self, id: &ChannelId) -> Option<&SP>; - fn add_send_event_provider(&mut self, send_provider: SP) -> bool; + fn get_send_event_provider(&self, target_id: &ComponentId) -> Option<&EventSender>; + fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool; } /// Generic event manager implementation. /// /// # Generics /// -/// * `ERP`: [EventReceiveProvider] used to receive all events. -/// * `SMP`: [SenderMapProvider] which maps channel IDs to send providers. -/// * `LTR`: [ListenerMapProvider] which maps listener keys to channel IDs. -/// * `SP`: [EventSendProvider] contained within the sender map which sends the events. -/// * `EV`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32] +/// * `EventReceiver`: [EventReceiveProvider] used to receive all events. +/// * `SenderMap`: [SenderMapProvider] which maps channel IDs to send providers. +/// * `ListenerMap`: [ListenerMapProvider] which maps listener keys to channel IDs. +/// * `EventSender`: [EventSendProvider] contained within the sender map which sends the events. +/// * `Ev`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32] /// and [EventU16] are supported. -/// * `AUX`: Auxiliary data which is sent with the event to provide optional context information +/// * `Data`: Auxiliary data which is sent with the event to provide optional context information pub struct EventManager< - ERP: EventReceiveProvider, - SMP: SenderMapProvider, - LTR: ListenerMapProvider, - SP: EventSendProvider, - EV: GenericEvent = EventU32, - AUX = Params, + EventReceiver: EventReceiveProvider, + SenderMap: SenderMapProvider, + ListenerMap: ListenerMapProvider, + EventSender: EventSendProvider, + Event: GenericEvent = EventU32, + ParamProvider: Debug = Params, > { - event_receiver: ERP, - sender_map: SMP, - listener_map: LTR, - phantom: core::marker::PhantomData<(SP, EV, AUX)>, + event_receiver: EventReceiver, + sender_map: SenderMap, + listener_map: ListenerMap, + phantom: core::marker::PhantomData<(EventSender, Event, ParamProvider)>, } #[derive(Debug)] -pub enum EventRoutingResult { +pub enum EventRoutingResult { /// No event was received Empty, /// An event was received and routed to listeners. Handled { num_recipients: u32, - event: EV, - aux_data: Option, + event_msg: EventMessage, }, } @@ -153,35 +189,29 @@ pub enum EventRoutingResult { pub enum EventRoutingError { Send(GenericSendError), NoSendersForKey(ListenerKey), - NoSenderForId(ChannelId), -} - -#[derive(Debug)] -pub struct EventRoutingErrorsWithResult { - pub result: EventRoutingResult, - pub errors: [Option; 3], + NoSenderForId(ComponentId), } impl< - ER: EventReceiveProvider, - S: SenderMapProvider, - L: ListenerMapProvider, - SP: EventSendProvider, - EV: GenericEvent + Copy, - AUX: Clone, - > EventManager + EventReceiver: EventReceiveProvider, + SenderMap: SenderMapProvider, + ListenerMap: ListenerMapProvider, + EventSender: EventSendProvider, + Event: GenericEvent + Copy, + ParamProvider: Debug, + > EventManager { pub fn remove_duplicates(&mut self, key: &ListenerKey) { self.listener_map.remove_duplicates(key) } /// Subscribe for a unique event. - pub fn subscribe_single(&mut self, event: &EV, sender_id: ChannelId) { + pub fn subscribe_single(&mut self, event: &Event, sender_id: ComponentId) { self.update_listeners(ListenerKey::Single(event.raw_as_largest_type()), sender_id); } /// Subscribe for an event group. - pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: ChannelId) { + pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: ComponentId) { self.update_listeners(ListenerKey::Group(group_id), sender_id); } @@ -189,21 +219,24 @@ impl< /// /// For example, this can be useful for a handler component which sends every event as /// a telemetry packet. - pub fn subscribe_all(&mut self, sender_id: ChannelId) { + pub fn subscribe_all(&mut self, sender_id: ComponentId) { self.update_listeners(ListenerKey::All, sender_id); } } - impl< - ERP: EventReceiveProvider, - SMP: SenderMapProvider, - LTR: ListenerMapProvider, - SP: EventSendProvider, - EV: GenericEvent + Copy, - AUX: Clone, - > EventManager + EventReceiver: EventReceiveProvider, + SenderMap: SenderMapProvider, + ListenerMap: ListenerMapProvider, + EventSenderMap: EventSendProvider, + Event: GenericEvent + Copy, + ParamProvider: Debug, + > EventManager { - pub fn new_with_custom_maps(event_receiver: ERP, sender_map: SMP, listener_map: LTR) -> Self { + pub fn new_with_custom_maps( + event_receiver: EventReceiver, + sender_map: SenderMap, + listener_map: ListenerMap, + ) -> Self { EventManager { listener_map, sender_map, @@ -213,81 +246,79 @@ impl< } /// Add a new sender component which can be used to send events to subscribers. - pub fn add_sender(&mut self, send_provider: SP) { + pub fn add_sender(&mut self, send_provider: EventSenderMap) { if !self .sender_map - .contains_send_event_provider(&send_provider.channel_id()) + .contains_send_event_provider(&send_provider.target_id()) { self.sender_map.add_send_event_provider(send_provider); } } /// Generic function to update the event subscribers. - fn update_listeners(&mut self, key: ListenerKey, sender_id: ChannelId) { + fn update_listeners(&mut self, key: ListenerKey, sender_id: ComponentId) { self.listener_map.add_listener(key, sender_id); } +} +impl< + EventReceiver: EventReceiveProvider, + SenderMap: SenderMapProvider, + ListenerMap: ListenerMapProvider, + EventSenderMap: EventSendProvider, + Event: GenericEvent + Copy, + ParamProvider: Clone + Debug, + > EventManager +{ /// This function will use the cached event receiver and try to receive one event. /// If an event was received, it will try to route that event to all subscribed event listeners. /// If this works without any issues, the [EventRoutingResult] will contain context information /// about the routed event. /// - /// This function will track up to 3 errors returned as part of the - /// [EventRoutingErrorsWithResult] error struct. - pub fn try_event_handling( + /// If an error occurs during the routing, the error handler will be called. The error handler + /// should take a reference to the event message as the first argument, and the routing error + /// as the second argument. + pub fn try_event_handling, EventRoutingError)>( &self, - ) -> Result, EventRoutingErrorsWithResult> { - let mut err_idx = 0; - let mut err_slice = [None, None, None]; + mut error_handler: E, + ) -> EventRoutingResult { let mut num_recipients = 0; - let mut add_error = |error: EventRoutingError| { - if err_idx < 3 { - err_slice[err_idx] = Some(error); - err_idx += 1; - } - }; - let mut send_handler = |key: &ListenerKey, event: EV, aux_data: &Option| { - if self.listener_map.contains_listener(key) { - if let Some(ids) = self.listener_map.get_listener_ids(key) { - for id in ids { - if let Some(sender) = self.sender_map.get_send_event_provider(id) { - if let Err(e) = sender.send(event, aux_data.clone()) { - add_error(EventRoutingError::Send(e)); + let mut send_handler = + |key: &ListenerKey, event_msg: &EventMessage| { + if self.listener_map.contains_listener(key) { + if let Some(ids) = self.listener_map.get_listener_ids(key) { + for id in ids { + if let Some(sender) = self.sender_map.get_send_event_provider(id) { + if let Err(e) = sender.send(EventMessage::new_generic( + *id, + event_msg.event, + event_msg.params.as_ref(), + )) { + error_handler(event_msg, EventRoutingError::Send(e)); + } else { + num_recipients += 1; + } } else { - num_recipients += 1; + error_handler(event_msg, EventRoutingError::NoSenderForId(*id)); } - } else { - add_error(EventRoutingError::NoSenderForId(*id)); } + } else { + error_handler(event_msg, EventRoutingError::NoSendersForKey(*key)); } - } else { - add_error(EventRoutingError::NoSendersForKey(*key)); } - } - }; - if let Some((event, aux_data)) = self.event_receiver.try_recv_event() { - let single_key = ListenerKey::Single(event.raw_as_largest_type()); - send_handler(&single_key, event, &aux_data); - let group_key = ListenerKey::Group(event.group_id_as_largest_type()); - send_handler(&group_key, event, &aux_data); - send_handler(&ListenerKey::All, event, &aux_data); - if err_idx > 0 { - return Err(EventRoutingErrorsWithResult { - result: EventRoutingResult::Handled { - num_recipients, - event, - aux_data, - }, - errors: err_slice, - }); - } - return Ok(EventRoutingResult::Handled { + }; + if let Ok(Some(event_msg)) = self.event_receiver.try_recv_event() { + let single_key = ListenerKey::Single(event_msg.event.raw_as_largest_type()); + send_handler(&single_key, &event_msg); + let group_key = ListenerKey::Group(event_msg.event.group_id_as_largest_type()); + send_handler(&group_key, &event_msg); + send_handler(&ListenerKey::All, &event_msg); + return EventRoutingResult::Handled { num_recipients, - event, - aux_data, - }); + event_msg, + }; } - Ok(EventRoutingResult::Empty) + EventRoutingResult::Empty } } @@ -311,23 +342,31 @@ pub mod alloc_mod { /// and the [DefaultListenerMap]. It uses /// [bounded mpsc senders](https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html) as the /// message queue backend. - pub type EventManagerWithBoundedMpsc = EventManager< + pub type EventManagerWithBoundedMpsc = EventManager< MpscEventReceiver, - DefaultSenderMap, EV, AUX>, + DefaultSenderMap, Event, ParamProvider>, DefaultListenerMap, - EventSenderMpscBounded, + EventSenderMpscBounded, >; impl< - ER: EventReceiveProvider, - SP: EventSendProvider, - EV: GenericEvent + Copy, - AUX: 'static, - > EventManager, DefaultListenerMap, SP, EV, AUX> + EventReceiver: EventReceiveProvider, + EventSender: EventSendProvider, + Event: GenericEvent + Copy, + ParamProvider: 'static + Debug, + > + EventManager< + EventReceiver, + DefaultSenderMap, + DefaultListenerMap, + EventSender, + Event, + ParamProvider, + > { /// Create an event manager where the sender table will be the [DefaultSenderMap] /// and the listener table will be the [DefaultListenerMap]. - pub fn new(event_receiver: ER) -> Self { + pub fn new(event_receiver: EventReceiver) -> Self { Self { listener_map: DefaultListenerMap::default(), sender_map: DefaultSenderMap::default(), @@ -342,7 +381,7 @@ pub mod alloc_mod { /// Simple implementation which uses a [HashMap] and a [Vec] internally. #[derive(Default)] pub struct DefaultListenerMap { - listeners: HashMap>, + listeners: HashMap>, } impl ListenerMapProvider for DefaultListenerMap { @@ -358,11 +397,11 @@ pub mod alloc_mod { self.listeners.contains_key(key) } - fn get_listener_ids(&self, key: &ListenerKey) -> Option> { + fn get_listener_ids(&self, key: &ListenerKey) -> Option> { self.listeners.get(key).map(|vec| vec.iter()) } - fn add_listener(&mut self, key: ListenerKey, sender_id: ChannelId) -> bool { + fn add_listener(&mut self, key: ListenerKey, sender_id: ComponentId) -> bool { if let Some(existing_list) = self.listeners.get_mut(&key) { existing_list.push(sender_id); } else { @@ -384,16 +423,19 @@ pub mod alloc_mod { /// /// Simple implementation which uses a [HashMap] internally. pub struct DefaultSenderMap< - SP: EventSendProvider, - EV: GenericEvent = EventU32, - AUX = Params, + EventSender: EventSendProvider, + Event: GenericEvent = EventU32, + ParamProvider: Debug = Params, > { - senders: HashMap, - phantom: PhantomData<(EV, AUX)>, + senders: HashMap, + phantom: PhantomData<(Event, ParamProvider)>, } - impl, EV: GenericEvent, AUX> Default - for DefaultSenderMap + impl< + EventSender: EventSendProvider, + Event: GenericEvent, + ParamProvider: Debug, + > Default for DefaultSenderMap { fn default() -> Self { Self { @@ -403,21 +445,25 @@ pub mod alloc_mod { } } - impl, EV: GenericEvent, AUX> SenderMapProvider - for DefaultSenderMap + impl< + EventSender: EventSendProvider, + Event: GenericEvent, + ParamProvider: Debug, + > SenderMapProvider + for DefaultSenderMap { - fn contains_send_event_provider(&self, id: &ChannelId) -> bool { + fn contains_send_event_provider(&self, id: &ComponentId) -> bool { self.senders.contains_key(id) } - fn get_send_event_provider(&self, id: &ChannelId) -> Option<&SP> { + fn get_send_event_provider(&self, id: &ComponentId) -> Option<&EventSender> { self.senders .get(id) - .filter(|sender| sender.channel_id() == *id) + .filter(|sender| sender.target_id() == *id) } - fn add_send_event_provider(&mut self, send_provider: SP) -> bool { - let id = send_provider.channel_id(); + fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool { + let id = send_provider.target_id(); if self.senders.contains_key(&id) { return false; } @@ -428,26 +474,33 @@ pub mod alloc_mod { #[cfg(feature = "std")] pub mod std_mod { + use crate::queue::GenericReceiveError; + use super::*; use std::sync::mpsc; pub struct MpscEventReceiver { - mpsc_receiver: mpsc::Receiver<(Event, Option)>, + receiver: mpsc::Receiver>, } impl MpscEventReceiver { - pub fn new(receiver: mpsc::Receiver<(Event, Option)>) -> Self { - Self { - mpsc_receiver: receiver, - } + pub fn new(receiver: mpsc::Receiver>) -> Self { + Self { receiver } } } impl EventReceiveProvider for MpscEventReceiver { - fn try_recv_event(&self) -> Option> { - if let Ok(event_and_data) = self.mpsc_receiver.try_recv() { - return Some(event_and_data); + type Error = GenericReceiveError; + + fn try_recv_event(&self) -> Result>, Self::Error> { + match self.receiver.try_recv() { + Ok(msg) => Ok(Some(msg)), + Err(e) => match e { + mpsc::TryRecvError::Empty => Ok(None), + mpsc::TryRecvError::Disconnected => { + Err(GenericReceiveError::TxDisconnected(None)) + } + }, } - None } } @@ -458,23 +511,26 @@ pub mod std_mod { /// send events. #[derive(Clone)] pub struct EventSenderMpsc { - id: u32, - sender: mpsc::Sender<(Event, Option)>, + target_id: ComponentId, + sender: mpsc::Sender>, } impl EventSenderMpsc { - pub fn new(id: u32, sender: mpsc::Sender<(Event, Option)>) -> Self { - Self { id, sender } + pub fn new(target_id: ComponentId, sender: mpsc::Sender>) -> Self { + Self { target_id, sender } } } impl EventSendProvider for EventSenderMpsc { - fn channel_id(&self) -> u32 { - self.id + type Error = GenericSendError; + + fn target_id(&self) -> ComponentId { + self.target_id } - fn send(&self, event: Event, aux_data: Option) -> Result<(), GenericSendError> { + + fn send(&self, event_msg: EventMessage) -> Result<(), GenericSendError> { self.sender - .send((event, aux_data)) + .send(event_msg) .map_err(|_| GenericSendError::RxDisconnected) } } @@ -483,19 +539,19 @@ pub mod std_mod { /// events. This has the advantage that the channel is bounded and thus more deterministic. #[derive(Clone)] pub struct EventSenderMpscBounded { - channel_id: u32, - sender: mpsc::SyncSender<(Event, Option)>, + target_id: ComponentId, + sender: mpsc::SyncSender>, capacity: usize, } impl EventSenderMpscBounded { pub fn new( - channel_id: u32, - sender: mpsc::SyncSender<(Event, Option)>, + target_id: ComponentId, + sender: mpsc::SyncSender>, capacity: usize, ) -> Self { Self { - channel_id, + target_id, sender, capacity, } @@ -503,11 +559,14 @@ pub mod std_mod { } impl EventSendProvider for EventSenderMpscBounded { - fn channel_id(&self) -> u32 { - self.channel_id + type Error = GenericSendError; + + fn target_id(&self) -> ComponentId { + self.target_id } - fn send(&self, event: Event, aux_data: Option) -> Result<(), GenericSendError> { - if let Err(e) = self.sender.try_send((event, aux_data)) { + + fn send(&self, event_msg: EventMessage) -> Result<(), Self::Error> { + if let Err(e) = self.sender.try_send(event_msg) { return match e { mpsc::TrySendError::Full(_) => { Err(GenericSendError::QueueFull(Some(self.capacity as u32))) @@ -530,19 +589,20 @@ mod tests { use super::*; use crate::event_man::EventManager; use crate::events::{EventU32, GenericEvent, Severity}; - use crate::params::ParamsRaw; + use crate::params::{ParamsHeapless, ParamsRaw}; + use crate::pus::test_util::{TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1}; use std::format; - use std::sync::mpsc::{self, channel, Receiver, Sender}; + use std::sync::mpsc::{self}; const TEST_EVENT: EventU32 = EventU32::const_new(Severity::INFO, 0, 5); fn check_next_event( expected: EventU32, - receiver: &Receiver, + receiver: &mpsc::Receiver, ) -> Option { - if let Ok(event) = receiver.try_recv() { - assert_eq!(event.0, expected); - return event.1; + if let Ok(event_msg) = receiver.try_recv() { + assert_eq!(event_msg.event, expected); + return event_msg.params; } None } @@ -555,17 +615,16 @@ mod tests { assert!(matches!(res, EventRoutingResult::Handled { .. })); if let EventRoutingResult::Handled { num_recipients, - event, - .. + event_msg, } = res { - assert_eq!(event, expected); + assert_eq!(event_msg.event, expected); assert_eq!(num_recipients, expected_num_sent); } } - fn generic_event_man() -> (Sender, EventManagerWithMpsc) { - let (event_sender, manager_queue) = channel(); + fn generic_event_man() -> (mpsc::Sender, EventManagerWithMpsc) { + let (event_sender, manager_queue) = mpsc::channel(); let event_man_receiver = MpscEventReceiver::new(manager_queue); (event_sender, EventManager::new(event_man_receiver)) } @@ -575,48 +634,56 @@ mod tests { let (event_sender, mut event_man) = generic_event_man(); let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); - let (single_event_sender, single_event_receiver) = channel(); + let (single_event_sender, single_event_receiver) = mpsc::channel(); let single_event_listener = EventSenderMpsc::new(0, single_event_sender); - event_man.subscribe_single(&event_grp_0, single_event_listener.channel_id()); + event_man.subscribe_single(&event_grp_0, single_event_listener.target_id()); event_man.add_sender(single_event_listener); - let (group_event_sender_0, group_event_receiver_0) = channel(); + let (group_event_sender_0, group_event_receiver_0) = mpsc::channel(); let group_event_listener = EventU32SenderMpsc::new(1, group_event_sender_0); - event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.channel_id()); + event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.target_id()); event_man.add_sender(group_event_listener); + let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| { + panic!("routing error occurred for event {:?}: {:?}", event_msg, e); + }; // Test event with one listener event_sender - .send((event_grp_0, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0)) .expect("Sending single error failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_0, 1); + let res = event_man.try_event_handling(&error_handler); + // assert!(res.is_ok()); + check_handled_event(res, event_grp_0, 1); check_next_event(event_grp_0, &single_event_receiver); // Test event which is sent to all group listeners event_sender - .send((event_grp_1_0, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0)) .expect("Sending group error failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_1_0, 1); + let res = event_man.try_event_handling(&error_handler); + check_handled_event(res, event_grp_1_0, 1); check_next_event(event_grp_1_0, &group_event_receiver_0); } #[test] - fn test_with_basic_aux_data() { + fn test_with_basic_params() { + let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| { + panic!("routing error occurred for event {:?}: {:?}", event_msg, e); + }; let (event_sender, mut event_man) = generic_event_man(); let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); - let (single_event_sender, single_event_receiver) = channel(); + let (single_event_sender, single_event_receiver) = mpsc::channel(); let single_event_listener = EventSenderMpsc::new(0, single_event_sender); - event_man.subscribe_single(&event_grp_0, single_event_listener.channel_id()); + event_man.subscribe_single(&event_grp_0, single_event_listener.target_id()); event_man.add_sender(single_event_listener); event_sender - .send((event_grp_0, Some(Params::Heapless((2_u32, 3_u32).into())))) + .send(EventMessage::new_with_params( + TEST_COMPONENT_ID_0.id(), + event_grp_0, + &Params::Heapless((2_u32, 3_u32).into()), + )) .expect("Sending group error failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_0, 1); + let res = event_man.try_event_handling(&error_handler); + check_handled_event(res, event_grp_0, 1); let aux = check_next_event(event_grp_0, &single_event_receiver); assert!(aux.is_some()); let aux = aux.unwrap(); @@ -631,38 +698,37 @@ mod tests { /// Test listening for multiple groups #[test] fn test_multi_group() { + let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| { + panic!("routing error occurred for event {:?}: {:?}", event_msg, e); + }; let (event_sender, mut event_man) = generic_event_man(); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - let hres = res.unwrap(); - assert!(matches!(hres, EventRoutingResult::Empty)); + let res = event_man.try_event_handling(error_handler); + assert!(matches!(res, EventRoutingResult::Empty)); let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); - let (event_grp_0_sender, event_grp_0_receiver) = channel(); + let (event_grp_0_sender, event_grp_0_receiver) = mpsc::channel(); let event_grp_0_and_1_listener = EventU32SenderMpsc::new(0, event_grp_0_sender); event_man.subscribe_group( event_grp_0.group_id(), - event_grp_0_and_1_listener.channel_id(), + event_grp_0_and_1_listener.target_id(), ); event_man.subscribe_group( event_grp_1_0.group_id(), - event_grp_0_and_1_listener.channel_id(), + event_grp_0_and_1_listener.target_id(), ); event_man.add_sender(event_grp_0_and_1_listener); event_sender - .send((event_grp_0, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0)) .expect("Sending Event Group 0 failed"); event_sender - .send((event_grp_1_0, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0)) .expect("Sendign Event Group 1 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_0, 1); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_1_0, 1); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_grp_0, 1); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_grp_1_0, 1); check_next_event(event_grp_0, &event_grp_0_receiver); check_next_event(event_grp_1_0, &event_grp_0_receiver); @@ -672,42 +738,42 @@ mod tests { /// to both group and single events from one listener #[test] fn test_listening_to_same_event_and_multi_type() { + let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| { + panic!("routing error occurred for event {:?}: {:?}", event_msg, e); + }; let (event_sender, mut event_man) = generic_event_man(); let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); - let (event_0_tx_0, event_0_rx_0) = channel(); - let (event_0_tx_1, event_0_rx_1) = channel(); + let (event_0_tx_0, event_0_rx_0) = mpsc::channel(); + let (event_0_tx_1, event_0_rx_1) = mpsc::channel(); let event_listener_0 = EventU32SenderMpsc::new(0, event_0_tx_0); let event_listener_1 = EventU32SenderMpsc::new(1, event_0_tx_1); - let event_listener_0_sender_id = event_listener_0.channel_id(); + let event_listener_0_sender_id = event_listener_0.target_id(); event_man.subscribe_single(&event_0, event_listener_0_sender_id); event_man.add_sender(event_listener_0); - let event_listener_1_sender_id = event_listener_1.channel_id(); + let event_listener_1_sender_id = event_listener_1.target_id(); event_man.subscribe_single(&event_0, event_listener_1_sender_id); event_man.add_sender(event_listener_1); event_sender - .send((event_0, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0)) .expect("Triggering Event 0 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_0, 2); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_0, 2); check_next_event(event_0, &event_0_rx_0); check_next_event(event_0, &event_0_rx_1); event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id); event_sender - .send((event_0, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0)) .expect("Triggering Event 0 failed"); event_sender - .send((event_1, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1)) .expect("Triggering Event 1 failed"); // 3 Events messages will be sent now - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_0, 2); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_1, 1); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_0, 2); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_1, 1); // Both the single event and the group event should arrive now check_next_event(event_0, &event_0_rx_0); check_next_event(event_1, &event_0_rx_0); @@ -716,36 +782,36 @@ mod tests { event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id); event_man.remove_duplicates(&ListenerKey::Group(event_1.group_id())); event_sender - .send((event_1, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_1)) .expect("Triggering Event 1 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_1, 1); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_1, 1); } #[test] fn test_all_events_listener() { - let (event_sender, manager_queue) = channel(); + let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| { + panic!("routing error occurred for event {:?}: {:?}", event_msg, e); + }; + let (event_sender, manager_queue) = mpsc::channel(); let event_man_receiver = MpscEventReceiver::new(manager_queue); let mut event_man = EventManagerWithMpsc::new(event_man_receiver); let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); - let (event_0_tx_0, all_events_rx) = channel(); + let (event_0_tx_0, all_events_rx) = mpsc::channel(); let all_events_listener = EventU32SenderMpsc::new(0, event_0_tx_0); - event_man.subscribe_all(all_events_listener.channel_id()); + event_man.subscribe_all(all_events_listener.target_id()); event_man.add_sender(all_events_listener); event_sender - .send((event_0, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0)) .expect("Triggering event 0 failed"); event_sender - .send((event_1, None)) + .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1)) .expect("Triggering event 1 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_0, 1); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_1, 1); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_0, 1); + let res = event_man.try_event_handling(error_handler); + check_handled_event(res, event_1, 1); check_next_event(event_0, &all_events_rx); check_next_event(event_1, &all_events_rx); } @@ -755,15 +821,15 @@ mod tests { let (event_sender, _event_receiver) = mpsc::sync_channel(3); let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3); event_sender - .send_no_data(TEST_EVENT) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)) .expect("sending test event failed"); event_sender - .send_no_data(TEST_EVENT) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)) .expect("sending test event failed"); event_sender - .send_no_data(TEST_EVENT) + .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)) .expect("sending test event failed"); - let error = event_sender.send_no_data(TEST_EVENT); + let error = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)); if let Err(e) = error { assert!(matches!(e, GenericSendError::QueueFull(Some(3)))); } else { @@ -775,7 +841,7 @@ mod tests { let (event_sender, event_receiver) = mpsc::sync_channel(3); let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3); drop(event_receiver); - if let Err(e) = event_sender.send_no_data(TEST_EVENT) { + if let Err(e) = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)) { assert!(matches!(e, GenericSendError::RxDisconnected)); } else { panic!("Expected error"); diff --git a/satrs/src/events.rs b/satrs/src/events.rs index 2732726..032322a 100644 --- a/satrs/src/events.rs +++ b/satrs/src/events.rs @@ -80,7 +80,7 @@ impl HasSeverity for SeverityHigh { const SEVERITY: Severity = Severity::HIGH; } -pub trait GenericEvent: EcssEnumeration { +pub trait GenericEvent: EcssEnumeration + Copy + Clone { type Raw; type GroupId; type UniqueId; diff --git a/satrs/src/hal/mod.rs b/satrs/src/hal/mod.rs index b6ab984..c374f9b 100644 --- a/satrs/src/hal/mod.rs +++ b/satrs/src/hal/mod.rs @@ -1,4 +1,3 @@ //! # Hardware Abstraction Layer module #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub mod std; diff --git a/satrs/src/hal/std/tcp_spacepackets_server.rs b/satrs/src/hal/std/tcp_spacepackets_server.rs index 257f0c1..a33b137 100644 --- a/satrs/src/hal/std/tcp_spacepackets_server.rs +++ b/satrs/src/hal/std/tcp_spacepackets_server.rs @@ -4,11 +4,10 @@ use std::{ net::{SocketAddr, TcpListener, TcpStream}, }; -use alloc::boxed::Box; - use crate::{ - encoding::{ccsds::PacketIdLookup, parse_buffer_for_ccsds_space_packets}, + encoding::parse_buffer_for_ccsds_space_packets, tmtc::{ReceivesTc, TmPacketSource}, + ValidatorU16Id, }; use super::tcp_server::{ @@ -16,17 +15,19 @@ use super::tcp_server::{ }; /// Concrete [TcpTcParser] implementation for the [TcpSpacepacketsServer]. -pub struct SpacepacketsTcParser { - packet_id_lookup: Box, +pub struct SpacepacketsTcParser { + packet_id_lookup: PacketIdChecker, } -impl SpacepacketsTcParser { - pub fn new(packet_id_lookup: Box) -> Self { +impl SpacepacketsTcParser { + pub fn new(packet_id_lookup: PacketIdChecker) -> Self { Self { packet_id_lookup } } } -impl TcpTcParser for SpacepacketsTcParser { +impl TcpTcParser + for SpacepacketsTcParser +{ fn handle_tc_parsing( &mut self, tc_buffer: &mut [u8], @@ -38,7 +39,7 @@ impl TcpTcParser for SpacepacketsTc // Reader vec full, need to parse for packets. conn_result.num_received_tcs += parse_buffer_for_ccsds_space_packets( &mut tc_buffer[..current_write_idx], - self.packet_id_lookup.as_ref(), + &self.packet_id_lookup, tc_receiver.upcast_mut(), next_write_idx, ) @@ -95,6 +96,7 @@ pub struct TcpSpacepacketsServer< TcError: 'static, TmSource: TmPacketSource, TcReceiver: ReceivesTc, + PacketIdChecker: ValidatorU16Id, > { generic_server: TcpTmtcGenericServer< TmError, @@ -102,7 +104,7 @@ pub struct TcpSpacepacketsServer< TmSource, TcReceiver, SpacepacketsTmSender, - SpacepacketsTcParser, + SpacepacketsTcParser, >, } @@ -111,7 +113,8 @@ impl< TcError: 'static, TmSource: TmPacketSource, TcReceiver: ReceivesTc, - > TcpSpacepacketsServer + PacketIdChecker: ValidatorU16Id, + > TcpSpacepacketsServer { /// /// ## Parameter @@ -127,12 +130,12 @@ impl< cfg: ServerConfig, tm_source: TmSource, tc_receiver: TcReceiver, - packet_id_lookup: Box, + packet_id_checker: PacketIdChecker, ) -> Result { Ok(Self { generic_server: TcpTmtcGenericServer::new( cfg, - SpacepacketsTcParser::new(packet_id_lookup), + SpacepacketsTcParser::new(packet_id_checker), SpacepacketsTmSender::default(), tm_source, tc_receiver, @@ -170,7 +173,7 @@ mod tests { thread, }; - use alloc::{boxed::Box, sync::Arc}; + use alloc::sync::Arc; use hashbrown::HashSet; use spacepackets::{ ecss::{tc::PusTcCreator, WritablePusPacket}, @@ -185,21 +188,21 @@ mod tests { use super::TcpSpacepacketsServer; const TEST_APID_0: u16 = 0x02; - const TEST_PACKET_ID_0: PacketId = PacketId::const_tc(true, TEST_APID_0); + const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0); const TEST_APID_1: u16 = 0x10; - const TEST_PACKET_ID_1: PacketId = PacketId::const_tc(true, TEST_APID_1); + const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1); fn generic_tmtc_server( addr: &SocketAddr, tc_receiver: SyncTcCacher, tm_source: SyncTmSource, packet_id_lookup: HashSet, - ) -> TcpSpacepacketsServer<(), (), SyncTmSource, SyncTcCacher> { + ) -> TcpSpacepacketsServer<(), (), SyncTmSource, SyncTcCacher, HashSet> { TcpSpacepacketsServer::new( ServerConfig::new(*addr, Duration::from_millis(2), 1024, 1024), tm_source, tc_receiver, - Box::new(packet_id_lookup), + packet_id_lookup, ) .expect("TCP server generation failed") } @@ -233,8 +236,8 @@ mod tests { assert_eq!(conn_result.num_sent_tms, 0); set_if_done.store(true, Ordering::Relaxed); }); - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let ping_tc = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true); let tc_0 = ping_tc.to_vec().expect("packet generation failed"); let mut stream = TcpStream::connect(dest_addr).expect("connecting to TCP server failed"); stream @@ -265,13 +268,13 @@ mod tests { // Add telemetry let mut total_tm_len = 0; - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let verif_tm = PusTcCreator::new_simple(&mut sph, 1, 1, None, true); + let verif_tm = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 1, 1, &[], true); let tm_0 = verif_tm.to_vec().expect("writing packet failed"); total_tm_len += tm_0.len(); tm_source.add_tm(&tm_0); - let mut sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap(); - let verif_tm = PusTcCreator::new_simple(&mut sph, 1, 3, None, true); + let verif_tm = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 1, 3, &[], true); let tm_1 = verif_tm.to_vec().expect("writing packet failed"); total_tm_len += tm_1.len(); tm_source.add_tm(&tm_1); @@ -312,14 +315,14 @@ mod tests { .expect("setting reas timeout failed"); // Send telecommands - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let ping_tc = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true); let tc_0 = ping_tc.to_vec().expect("ping tc creation failed"); stream .write_all(&tc_0) .expect("writing to TCP server failed"); - let mut sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap(); - let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true); + let action_tc = + PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true); let tc_1 = action_tc.to_vec().expect("action tc creation failed"); stream .write_all(&tc_1) diff --git a/satrs/src/hal/std/udp_server.rs b/satrs/src/hal/std/udp_server.rs index fa117f0..8f77c2b 100644 --- a/satrs/src/hal/std/udp_server.rs +++ b/satrs/src/hal/std/udp_server.rs @@ -40,8 +40,8 @@ use std::vec::Vec; /// let ping_receiver = PingReceiver::default(); /// let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) /// .expect("Creating UDP TMTC server failed"); -/// let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap(); -/// let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); +/// let sph = SpHeader::new_from_apid(0x02); +/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); /// let len = pus_tc /// .write_to_bytes(&mut buf) /// .expect("Error writing PUS TC packet"); @@ -178,8 +178,8 @@ mod tests { let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) .expect("Creating UDP TMTC server failed"); is_send(&udp_tc_server); - let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap(); - let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let sph = SpHeader::new_from_apid(0x02); + let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let len = pus_tc .write_to_bytes(&mut buf) .expect("Error writing PUS TC packet"); diff --git a/satrs/src/hk.rs b/satrs/src/hk.rs index 8033e15..50edfda 100644 --- a/satrs/src/hk.rs +++ b/satrs/src/hk.rs @@ -1,40 +1,40 @@ -use crate::{ - pus::verification::{TcStateAccepted, VerificationToken}, - TargetId, -}; +use crate::ComponentId; pub type CollectionIntervalFactor = u32; +/// Unique Identifier for a certain housekeeping dataset. pub type UniqueId = u32; #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum HkRequest { - OneShot(UniqueId), - Enable(UniqueId), - Disable(UniqueId), - ModifyCollectionInterval(UniqueId, CollectionIntervalFactor), +pub struct HkRequest { + pub unique_id: UniqueId, + pub variant: HkRequestVariant, +} + +impl HkRequest { + pub fn new(unique_id: UniqueId, variant: HkRequestVariant) -> Self { + Self { unique_id, variant } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum HkRequestVariant { + OneShot, + EnablePeriodic, + DisablePeriodic, + ModifyCollectionInterval(CollectionIntervalFactor), } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TargetedHkRequest { - pub target_id: TargetId, - pub hk_request: HkRequest, + pub target_id: ComponentId, + pub hk_request: HkRequestVariant, } impl TargetedHkRequest { - pub fn new(target_id: TargetId, hk_request: HkRequest) -> Self { + pub fn new(target_id: ComponentId, hk_request: HkRequestVariant) -> Self { Self { target_id, hk_request, } } } - -pub trait PusHkRequestRouter { - type Error; - fn route( - &self, - target_id: TargetId, - hk_request: HkRequest, - token: VerificationToken, - ) -> Result<(), Self::Error>; -} diff --git a/satrs/src/lib.rs b/satrs/src/lib.rs index 5040d58..f6b43e6 100644 --- a/satrs/src/lib.rs +++ b/satrs/src/lib.rs @@ -14,7 +14,7 @@ //! - The [pus] module which provides special support for projects using //! the [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/). #![no_std] -#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![cfg_attr(docs_rs, feature(doc_auto_cfg))] #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "alloc")] @@ -23,16 +23,15 @@ extern crate downcast_rs; extern crate std; #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub mod cfdp; pub mod encoding; pub mod event_man; pub mod events; #[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub mod executable; pub mod hal; -pub mod objects; +#[cfg(feature = "std")] +pub mod mode_tree; pub mod pool; pub mod power; pub mod pus; @@ -40,6 +39,7 @@ pub mod queue; pub mod request; pub mod res_code; pub mod seq_count; +pub mod time; pub mod tmtc; pub mod action; @@ -49,8 +49,70 @@ pub mod params; pub use spacepackets; -/// Generic channel ID type. -pub type ChannelId = u32; +use spacepackets::PacketId; -/// Generic target ID type. -pub type TargetId = u64; +/// Generic component ID type. +pub type ComponentId = u64; + +pub trait ValidatorU16Id { + fn validate(&self, id: u16) -> bool; +} + +#[cfg(feature = "alloc")] +impl ValidatorU16Id for alloc::vec::Vec { + fn validate(&self, id: u16) -> bool { + self.contains(&id) + } +} + +#[cfg(feature = "alloc")] +impl ValidatorU16Id for hashbrown::HashSet { + fn validate(&self, id: u16) -> bool { + self.contains(&id) + } +} + +impl ValidatorU16Id for [u16] { + fn validate(&self, id: u16) -> bool { + self.binary_search(&id).is_ok() + } +} + +impl ValidatorU16Id for &[u16] { + fn validate(&self, id: u16) -> bool { + self.binary_search(&id).is_ok() + } +} + +#[cfg(feature = "alloc")] +impl ValidatorU16Id for alloc::vec::Vec { + fn validate(&self, packet_id: u16) -> bool { + self.contains(&PacketId::from(packet_id)) + } +} + +#[cfg(feature = "alloc")] +impl ValidatorU16Id for hashbrown::HashSet { + fn validate(&self, packet_id: u16) -> bool { + self.contains(&PacketId::from(packet_id)) + } +} + +#[cfg(feature = "std")] +impl ValidatorU16Id for std::collections::HashSet { + fn validate(&self, packet_id: u16) -> bool { + self.contains(&PacketId::from(packet_id)) + } +} + +impl ValidatorU16Id for [PacketId] { + fn validate(&self, packet_id: u16) -> bool { + self.binary_search(&PacketId::from(packet_id)).is_ok() + } +} + +impl ValidatorU16Id for &[PacketId] { + fn validate(&self, packet_id: u16) -> bool { + self.binary_search(&PacketId::from(packet_id)).is_ok() + } +} diff --git a/satrs/src/mode.rs b/satrs/src/mode.rs index c5968b4..65519a5 100644 --- a/satrs/src/mode.rs +++ b/satrs/src/mode.rs @@ -1,67 +1,95 @@ use core::mem::size_of; +use satrs_shared::res_code::ResultU16; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use spacepackets::ByteConversionError; -use crate::TargetId; +#[cfg(feature = "alloc")] +pub use alloc_mod::*; + +#[cfg(feature = "std")] +pub use std_mod::*; + +use crate::{ + queue::GenericTargetedMessagingError, + request::{GenericMessage, MessageMetadata, MessageReceiver, MessageReceiverWithId, RequestId}, + ComponentId, +}; + +pub type Mode = u32; +pub type Submode = u16; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ModeAndSubmode { - mode: u32, - submode: u16, + mode: Mode, + submode: Submode, } impl ModeAndSubmode { - pub const fn new_mode_only(mode: u32) -> Self { + pub const RAW_LEN: usize = size_of::() + size_of::(); + + pub const fn new_mode_only(mode: Mode) -> Self { Self { mode, submode: 0 } } - pub const fn new(mode: u32, submode: u16) -> Self { + pub const fn new(mode: Mode, submode: Submode) -> Self { Self { mode, submode } } - pub fn raw_len() -> usize { - size_of::() + size_of::() - } - pub fn from_be_bytes(buf: &[u8]) -> Result { if buf.len() < 6 { return Err(ByteConversionError::FromSliceTooSmall { - expected: 6, + expected: Self::RAW_LEN, found: buf.len(), }); } Ok(Self { - mode: u32::from_be_bytes(buf[0..4].try_into().unwrap()), - submode: u16::from_be_bytes(buf[4..6].try_into().unwrap()), + mode: Mode::from_be_bytes(buf[0..size_of::()].try_into().unwrap()), + submode: Submode::from_be_bytes( + buf[size_of::()..size_of::() + size_of::()] + .try_into() + .unwrap(), + ), }) } - pub fn mode(&self) -> u32 { + pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { + if buf.len() < Self::RAW_LEN { + return Err(ByteConversionError::ToSliceTooSmall { + expected: Self::RAW_LEN, + found: buf.len(), + }); + } + buf[0..size_of::()].copy_from_slice(&self.mode.to_be_bytes()); + buf[size_of::()..Self::RAW_LEN].copy_from_slice(&self.submode.to_be_bytes()); + Ok(Self::RAW_LEN) + } + + pub fn mode(&self) -> Mode { self.mode } - pub fn submode(&self) -> u16 { + pub fn submode(&self) -> Submode { self.submode } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TargetedModeCommand { - pub address: TargetId, + pub address: ComponentId, pub mode_submode: ModeAndSubmode, } impl TargetedModeCommand { - pub const fn new(address: TargetId, mode_submode: ModeAndSubmode) -> Self { + pub const fn new(address: ComponentId, mode_submode: ModeAndSubmode) -> Self { Self { address, mode_submode, } } - pub fn address(&self) -> TargetId { + pub fn address(&self) -> ComponentId { self.address } @@ -81,6 +109,8 @@ impl TargetedModeCommand { #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ModeRequest { + /// Mode information. Can be used to notify other components of changed modes. + ModeInfo(ModeAndSubmode), SetMode(ModeAndSubmode), ReadMode, AnnounceMode, @@ -90,6 +120,479 @@ pub enum ModeRequest { #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TargetedModeRequest { - target_id: TargetId, + target_id: ComponentId, mode_request: ModeRequest, } + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ModeReply { + /// Reply to a mode request to confirm the commanded mode was reached. + ModeReply(ModeAndSubmode), + // Can not reach the commanded mode. Contains a reason as a [ResultU16]. + CantReachMode(ResultU16), + /// We are in the wrong mode for unknown reasons. Contains the expected and reached mode. + WrongMode { + expected: ModeAndSubmode, + reached: ModeAndSubmode, + }, +} + +pub type GenericModeReply = GenericMessage; + +pub trait ModeRequestSender { + fn local_channel_id(&self) -> ComponentId; + fn send_mode_request( + &self, + request_id: RequestId, + target_id: ComponentId, + request: ModeRequest, + ) -> Result<(), GenericTargetedMessagingError>; +} + +pub trait ModeRequestReceiver { + fn try_recv_mode_request( + &self, + ) -> Result>, GenericTargetedMessagingError>; +} + +impl> ModeRequestReceiver + for MessageReceiverWithId +{ + fn try_recv_mode_request( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.try_recv_message() + } +} + +#[derive(Debug, Clone)] +pub enum ModeError { + Messaging(GenericTargetedMessagingError), +} + +impl From for ModeError { + fn from(value: GenericTargetedMessagingError) -> Self { + Self::Messaging(value) + } +} + +pub trait ModeProvider { + fn mode_and_submode(&self) -> ModeAndSubmode; + + fn mode(&self) -> Mode { + self.mode_and_submode().mode() + } + + fn submode(&self) -> Submode { + self.mode_and_submode().submode() + } +} + +pub trait ModeRequestHandler: ModeProvider { + type Error; + + fn start_transition( + &mut self, + requestor: MessageMetadata, + mode_and_submode: ModeAndSubmode, + ) -> Result<(), Self::Error>; + + fn announce_mode(&self, requestor_info: Option, recursive: bool); + + fn handle_mode_reached( + &mut self, + requestor_info: Option, + ) -> Result<(), Self::Error>; + + fn handle_mode_info( + &mut self, + requestor_info: MessageMetadata, + info: ModeAndSubmode, + ) -> Result<(), Self::Error>; + + fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + reply: ModeReply, + ) -> Result<(), Self::Error>; + + fn handle_mode_request( + &mut self, + request: GenericMessage, + ) -> Result<(), Self::Error> { + match request.message { + ModeRequest::SetMode(mode_and_submode) => { + self.start_transition(request.requestor_info, mode_and_submode) + } + ModeRequest::ReadMode => self.send_mode_reply( + request.requestor_info, + ModeReply::ModeReply(self.mode_and_submode()), + ), + ModeRequest::AnnounceMode => { + self.announce_mode(Some(request.requestor_info), false); + Ok(()) + } + ModeRequest::AnnounceModeRecursive => { + self.announce_mode(Some(request.requestor_info), true); + Ok(()) + } + ModeRequest::ModeInfo(info) => self.handle_mode_info(request.requestor_info, info), + } + } +} + +pub trait ModeReplyReceiver { + fn try_recv_mode_reply( + &self, + ) -> Result>, GenericTargetedMessagingError>; +} + +impl> ModeReplyReceiver for MessageReceiverWithId { + fn try_recv_mode_reply( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.try_recv_message() + } +} + +pub trait ModeReplySender { + fn local_channel_id(&self) -> ComponentId; + + /// The requestor is assumed to be the target of the reply. + fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + reply: ModeReply, + ) -> Result<(), GenericTargetedMessagingError>; +} + +#[cfg(feature = "alloc")] +pub mod alloc_mod { + use crate::{ + mode::ModeRequest, + queue::GenericTargetedMessagingError, + request::{ + MessageMetadata, MessageSender, MessageSenderAndReceiver, MessageSenderMap, + RequestAndReplySenderAndReceiver, RequestId, + }, + ComponentId, + }; + + use super::*; + + impl> MessageSenderMap { + pub fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + target_id: ComponentId, + request: ModeReply, + ) -> Result<(), GenericTargetedMessagingError> { + self.send_message(requestor_info, target_id, request) + } + + pub fn add_reply_target(&mut self, target_id: ComponentId, request_sender: S) { + self.add_message_target(target_id, request_sender) + } + } + + impl, R: MessageReceiver> ModeReplySender + for MessageSenderAndReceiver + { + fn local_channel_id(&self) -> ComponentId { + self.local_channel_id_generic() + } + + fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + request: ModeReply, + ) -> Result<(), GenericTargetedMessagingError> { + self.message_sender_map.send_mode_reply( + MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()), + requestor_info.sender_id(), + request, + ) + } + } + + impl, R: MessageReceiver> ModeReplyReceiver + for MessageSenderAndReceiver + { + fn try_recv_mode_reply( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.message_receiver.try_recv_message() + } + } + + impl< + REQUEST, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > RequestAndReplySenderAndReceiver + { + pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: S1) { + self.reply_sender_map + .add_message_target(target_id, reply_sender) + } + } + + impl< + REQUEST, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > ModeReplySender for RequestAndReplySenderAndReceiver + { + fn local_channel_id(&self) -> ComponentId { + self.local_channel_id_generic() + } + + fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + request: ModeReply, + ) -> Result<(), GenericTargetedMessagingError> { + self.reply_sender_map.send_mode_reply( + MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()), + requestor_info.sender_id(), + request, + ) + } + } + + impl< + REQUEST, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > ModeReplyReceiver + for RequestAndReplySenderAndReceiver + { + fn try_recv_mode_reply( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.reply_receiver.try_recv_message() + } + } + + /// Helper type definition for a mode handler which can handle mode requests. + pub type ModeRequestHandlerInterface = + MessageSenderAndReceiver; + + impl, R: MessageReceiver> + ModeRequestHandlerInterface + { + pub fn try_recv_mode_request( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.try_recv_message() + } + + pub fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + reply: ModeReply, + ) -> Result<(), GenericTargetedMessagingError> { + self.send_message( + requestor_info.request_id(), + requestor_info.sender_id(), + reply, + ) + } + } + + /// Helper type defintion for a mode handler object which can send mode requests and receive + /// mode replies. + pub type ModeRequestorInterface = MessageSenderAndReceiver; + + impl, R: MessageReceiver> ModeRequestorInterface { + pub fn try_recv_mode_reply( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.try_recv_message() + } + + pub fn send_mode_request( + &self, + request_id: RequestId, + target_id: ComponentId, + reply: ModeRequest, + ) -> Result<(), GenericTargetedMessagingError> { + self.send_message(request_id, target_id, reply) + } + } + + /// Helper type defintion for a mode handler object which can both send mode requests and + /// process mode requests. + pub type ModeInterface = + RequestAndReplySenderAndReceiver; + + impl> MessageSenderMap { + pub fn send_mode_request( + &self, + requestor_info: MessageMetadata, + target_id: ComponentId, + request: ModeRequest, + ) -> Result<(), GenericTargetedMessagingError> { + self.send_message(requestor_info, target_id, request) + } + + pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S) { + self.add_message_target(target_id, request_sender) + } + } + + /* + impl> ModeRequestSender for MessageSenderMapWithId { + fn local_channel_id(&self) -> ComponentId { + self.local_channel_id + } + + fn send_mode_request( + &self, + request_id: RequestId, + target_id: ComponentId, + request: ModeRequest, + ) -> Result<(), GenericTargetedMessagingError> { + self.send_message(request_id, target_id, request) + } + } + */ + + impl, R: MessageReceiver> ModeRequestReceiver + for MessageSenderAndReceiver + { + fn try_recv_mode_request( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.message_receiver.try_recv_message() + } + } + + impl, R: MessageReceiver> ModeRequestSender + for MessageSenderAndReceiver + { + fn local_channel_id(&self) -> ComponentId { + self.local_channel_id_generic() + } + + fn send_mode_request( + &self, + request_id: RequestId, + target_id: ComponentId, + request: ModeRequest, + ) -> Result<(), GenericTargetedMessagingError> { + self.message_sender_map.send_mode_request( + MessageMetadata::new(request_id, self.local_channel_id()), + target_id, + request, + ) + } + } + + impl< + REPLY, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > RequestAndReplySenderAndReceiver + { + pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S0) { + self.request_sender_map + .add_message_target(target_id, request_sender) + } + } + + impl< + REPLY, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > ModeRequestSender + for RequestAndReplySenderAndReceiver + { + fn local_channel_id(&self) -> ComponentId { + self.local_channel_id_generic() + } + + fn send_mode_request( + &self, + request_id: RequestId, + target_id: ComponentId, + request: ModeRequest, + ) -> Result<(), GenericTargetedMessagingError> { + self.request_sender_map.send_mode_request( + MessageMetadata::new(request_id, self.local_channel_id()), + target_id, + request, + ) + } + } + + impl< + REPLY, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > ModeRequestReceiver + for RequestAndReplySenderAndReceiver + { + fn try_recv_mode_request( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.request_receiver.try_recv_message() + } + } +} + +#[cfg(feature = "std")] +pub mod std_mod { + use std::sync::mpsc; + + use crate::request::GenericMessage; + + use super::*; + + pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface< + mpsc::Sender>, + mpsc::Receiver>, + >; + pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface< + mpsc::SyncSender>, + mpsc::Receiver>, + >; + + pub type ModeRequestorMpsc = ModeRequestorInterface< + mpsc::Sender>, + mpsc::Receiver>, + >; + pub type ModeRequestorBoundedMpsc = ModeRequestorInterface< + mpsc::SyncSender>, + mpsc::Receiver>, + >; + + pub type ModeRequestorAndHandlerMpsc = ModeInterface< + mpsc::Sender>, + mpsc::Receiver>, + mpsc::Sender>, + mpsc::Receiver>, + >; + pub type ModeRequestorAndHandlerMpscBounded = ModeInterface< + mpsc::SyncSender>, + mpsc::Receiver>, + mpsc::SyncSender>, + mpsc::Receiver>, + >; +} + +#[cfg(test)] +mod tests {} diff --git a/satrs/src/mode_tree.rs b/satrs/src/mode_tree.rs new file mode 100644 index 0000000..1cddd32 --- /dev/null +++ b/satrs/src/mode_tree.rs @@ -0,0 +1,37 @@ +use alloc::vec::Vec; +use hashbrown::HashMap; + +use crate::{ + mode::{Mode, ModeAndSubmode, Submode}, + ComponentId, +}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TableEntryType { + /// Target table containing information of the expected children modes for given mode. + Target, + /// Sequence table which contains information about how to reach a target table, including + /// the order of the sequences. + Sequence, +} + +pub struct ModeTableEntry { + /// Name of respective table entry. + pub name: &'static str, + /// Target channel ID. + pub channel_id: ComponentId, + pub mode_submode: ModeAndSubmode, + pub allowed_submode_mask: Option, + pub check_success: bool, +} + +pub struct ModeTableMapValue { + /// Name for a given mode table entry. + pub name: &'static str, + pub entries: Vec, +} + +pub type ModeTable = HashMap; + +#[cfg(test)] +mod tests {} diff --git a/satrs/src/objects.rs b/satrs/src/objects.rs deleted file mode 100644 index a9b6881..0000000 --- a/satrs/src/objects.rs +++ /dev/null @@ -1,308 +0,0 @@ -//! # Module providing addressable object support and a manager for them -//! -//! Each addressable object can be identified using an [object ID][ObjectId]. -//! The [system object][ManagedSystemObject] trait also allows storing these objects into the -//! [object manager][ObjectManager]. They can then be retrieved and casted back to a known type -//! using the object ID. -//! -//! # Examples -//! -//! ```rust -//! use std::any::Any; -//! use std::error::Error; -//! use satrs::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject}; -//! -//! struct ExampleSysObj { -//! id: ObjectId, -//! dummy: u32, -//! was_initialized: bool, -//! } -//! -//! impl ExampleSysObj { -//! fn new(id: ObjectId, dummy: u32) -> ExampleSysObj { -//! ExampleSysObj { -//! id, -//! dummy, -//! was_initialized: false, -//! } -//! } -//! } -//! -//! impl SystemObject for ExampleSysObj { -//! type Error = (); -//! fn get_object_id(&self) -> &ObjectId { -//! &self.id -//! } -//! -//! fn initialize(&mut self) -> Result<(), Self::Error> { -//! self.was_initialized = true; -//! Ok(()) -//! } -//! } -//! -//! impl ManagedSystemObject for ExampleSysObj {} -//! -//! let mut obj_manager = ObjectManager::default(); -//! let obj_id = ObjectId { id: 0, name: "Example 0"}; -//! let example_obj = ExampleSysObj::new(obj_id, 42); -//! obj_manager.insert(Box::new(example_obj)); -//! let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&obj_id); -//! let example_obj = obj_back_casted.unwrap(); -//! assert_eq!(example_obj.id, obj_id); -//! assert_eq!(example_obj.dummy, 42); -//! ``` -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -pub use alloc_mod::*; -#[cfg(feature = "alloc")] -use downcast_rs::Downcast; -#[cfg(feature = "alloc")] -use hashbrown::HashMap; -#[cfg(feature = "std")] -use std::error::Error; - -use crate::TargetId; - -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] -pub struct ObjectId { - pub id: TargetId, - pub name: &'static str, -} - -#[cfg(feature = "alloc")] -pub mod alloc_mod { - use super::*; - - /// Each object which is stored inside the [object manager][ObjectManager] needs to implemented - /// this trait - pub trait SystemObject: Downcast { - type Error; - fn get_object_id(&self) -> &ObjectId; - fn initialize(&mut self) -> Result<(), Self::Error>; - } - downcast_rs::impl_downcast!(SystemObject assoc Error); - - pub trait ManagedSystemObject: SystemObject + Send {} - downcast_rs::impl_downcast!(ManagedSystemObject assoc Error); - - /// Helper module to manage multiple [ManagedSystemObjects][ManagedSystemObject] by mapping them - /// using an [object ID][ObjectId] - #[cfg(feature = "alloc")] - pub struct ObjectManager { - obj_map: HashMap>>, - } - - #[cfg(feature = "alloc")] - impl Default for ObjectManager { - fn default() -> Self { - Self::new() - } - } - - #[cfg(feature = "alloc")] - impl ObjectManager { - pub fn new() -> Self { - ObjectManager { - obj_map: HashMap::new(), - } - } - pub fn insert(&mut self, sys_obj: Box>) -> bool { - let obj_id = sys_obj.get_object_id(); - if self.obj_map.contains_key(obj_id) { - return false; - } - self.obj_map.insert(*obj_id, sys_obj).is_none() - } - - /// Initializes all System Objects in the hash map and returns the number of successful - /// initializations - pub fn initialize(&mut self) -> Result> { - let mut init_success = 0; - for val in self.obj_map.values_mut() { - if val.initialize().is_ok() { - init_success += 1 - } - } - Ok(init_success) - } - - /// Retrieve a reference to an object stored inside the manager. The type to retrieve needs to - /// be explicitly passed as a generic parameter or specified on the left hand side of the - /// expression. - pub fn get_ref>(&self, key: &ObjectId) -> Option<&T> { - self.obj_map.get(key).and_then(|o| o.downcast_ref::()) - } - - /// Retrieve a mutable reference to an object stored inside the manager. The type to retrieve - /// needs to be explicitly passed as a generic parameter or specified on the left hand side - /// of the expression. - pub fn get_mut>( - &mut self, - key: &ObjectId, - ) -> Option<&mut T> { - self.obj_map - .get_mut(key) - .and_then(|o| o.downcast_mut::()) - } - } -} - -#[cfg(test)] -mod tests { - use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject}; - use std::boxed::Box; - use std::string::String; - use std::sync::{Arc, Mutex}; - use std::thread; - - struct ExampleSysObj { - id: ObjectId, - dummy: u32, - was_initialized: bool, - } - - impl ExampleSysObj { - fn new(id: ObjectId, dummy: u32) -> ExampleSysObj { - ExampleSysObj { - id, - dummy, - was_initialized: false, - } - } - } - - impl SystemObject for ExampleSysObj { - type Error = (); - fn get_object_id(&self) -> &ObjectId { - &self.id - } - - fn initialize(&mut self) -> Result<(), Self::Error> { - self.was_initialized = true; - Ok(()) - } - } - - impl ManagedSystemObject for ExampleSysObj {} - - struct OtherExampleObject { - id: ObjectId, - string: String, - was_initialized: bool, - } - - impl SystemObject for OtherExampleObject { - type Error = (); - fn get_object_id(&self) -> &ObjectId { - &self.id - } - - fn initialize(&mut self) -> Result<(), Self::Error> { - self.was_initialized = true; - Ok(()) - } - } - - impl ManagedSystemObject for OtherExampleObject {} - - #[test] - fn test_obj_manager_simple() { - let mut obj_manager = ObjectManager::default(); - let expl_obj_id = ObjectId { - id: 0, - name: "Example 0", - }; - let example_obj = ExampleSysObj::new(expl_obj_id, 42); - assert!(obj_manager.insert(Box::new(example_obj))); - let res = obj_manager.initialize(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), 1); - let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&expl_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.dummy, 42); - assert!(expl_obj_back_casted.was_initialized); - - let second_obj_id = ObjectId { - id: 12, - name: "Example 1", - }; - let second_example_obj = OtherExampleObject { - id: second_obj_id, - string: String::from("Hello Test"), - was_initialized: false, - }; - - assert!(obj_manager.insert(Box::new(second_example_obj))); - let res = obj_manager.initialize(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), 2); - let obj_back_casted: Option<&OtherExampleObject> = obj_manager.get_ref(&second_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.string, String::from("Hello Test")); - assert!(expl_obj_back_casted.was_initialized); - - let existing_obj_id = ObjectId { - id: 12, - name: "Example 1", - }; - let invalid_obj = OtherExampleObject { - id: existing_obj_id, - string: String::from("Hello Test"), - was_initialized: false, - }; - - assert!(!obj_manager.insert(Box::new(invalid_obj))); - } - - #[test] - fn object_man_threaded() { - let obj_manager = Arc::new(Mutex::new(ObjectManager::new())); - let expl_obj_id = ObjectId { - id: 0, - name: "Example 0", - }; - let example_obj = ExampleSysObj::new(expl_obj_id, 42); - let second_obj_id = ObjectId { - id: 12, - name: "Example 1", - }; - let second_example_obj = OtherExampleObject { - id: second_obj_id, - string: String::from("Hello Test"), - was_initialized: false, - }; - - let mut obj_man_handle = obj_manager.lock().expect("Mutex lock failed"); - assert!(obj_man_handle.insert(Box::new(example_obj))); - assert!(obj_man_handle.insert(Box::new(second_example_obj))); - let res = obj_man_handle.initialize(); - std::mem::drop(obj_man_handle); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), 2); - let obj_man_0 = obj_manager.clone(); - let jh0 = thread::spawn(move || { - let locked_man = obj_man_0.lock().expect("Mutex lock failed"); - let obj_back_casted: Option<&ExampleSysObj> = locked_man.get_ref(&expl_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.dummy, 42); - assert!(expl_obj_back_casted.was_initialized); - std::mem::drop(locked_man) - }); - - let jh1 = thread::spawn(move || { - let locked_man = obj_manager.lock().expect("Mutex lock failed"); - let obj_back_casted: Option<&OtherExampleObject> = locked_man.get_ref(&second_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.string, String::from("Hello Test")); - assert!(expl_obj_back_casted.was_initialized); - std::mem::drop(locked_man) - }); - jh0.join().expect("Joining thread 0 failed"); - jh1.join().expect("Joining thread 1 failed"); - } -} diff --git a/satrs/src/params.rs b/satrs/src/params.rs index 1279015..10fb41c 100644 --- a/satrs/src/params.rs +++ b/satrs/src/params.rs @@ -60,21 +60,28 @@ use alloc::vec::Vec; /// Generic trait which is used for objects which can be converted into a raw network (big) endian /// byte format. pub trait WritableToBeBytes { - fn raw_len(&self) -> usize; + fn written_len(&self) -> usize; /// Writes the object to a raw buffer in network endianness (big) fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result; + + #[cfg(feature = "alloc")] + fn to_vec(&self) -> Result, ByteConversionError> { + let mut vec = alloc::vec![0; self.written_len()]; + self.write_to_be_bytes(&mut vec)?; + Ok(vec) + } } macro_rules! param_to_be_bytes_impl { ($Newtype: ident) => { impl WritableToBeBytes for $Newtype { #[inline] - fn raw_len(&self) -> usize { + fn written_len(&self) -> usize { size_of::<::ByteArray>() } fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - let raw_len = self.raw_len(); + let raw_len = WritableToBeBytes::written_len(self); if buf.len() < raw_len { return Err(ByteConversionError::ToSliceTooSmall { found: buf.len(), @@ -382,32 +389,32 @@ pub enum ParamsRaw { } impl WritableToBeBytes for ParamsRaw { - fn raw_len(&self) -> usize { + fn written_len(&self) -> usize { match self { - ParamsRaw::U8(v) => v.raw_len(), - ParamsRaw::U8Pair(v) => v.raw_len(), - ParamsRaw::U8Triplet(v) => v.raw_len(), - ParamsRaw::I8(v) => v.raw_len(), - ParamsRaw::I8Pair(v) => v.raw_len(), - ParamsRaw::I8Triplet(v) => v.raw_len(), - ParamsRaw::U16(v) => v.raw_len(), - ParamsRaw::U16Pair(v) => v.raw_len(), - ParamsRaw::U16Triplet(v) => v.raw_len(), - ParamsRaw::I16(v) => v.raw_len(), - ParamsRaw::I16Pair(v) => v.raw_len(), - ParamsRaw::I16Triplet(v) => v.raw_len(), - ParamsRaw::U32(v) => v.raw_len(), - ParamsRaw::U32Pair(v) => v.raw_len(), - ParamsRaw::U32Triplet(v) => v.raw_len(), - ParamsRaw::I32(v) => v.raw_len(), - ParamsRaw::I32Pair(v) => v.raw_len(), - ParamsRaw::I32Triplet(v) => v.raw_len(), - ParamsRaw::F32(v) => v.raw_len(), - ParamsRaw::F32Pair(v) => v.raw_len(), - ParamsRaw::F32Triplet(v) => v.raw_len(), - ParamsRaw::U64(v) => v.raw_len(), - ParamsRaw::I64(v) => v.raw_len(), - ParamsRaw::F64(v) => v.raw_len(), + ParamsRaw::U8(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U8Pair(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U8Triplet(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I8(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I8Pair(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I8Triplet(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U16(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U16Pair(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U16Triplet(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I16(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I16Pair(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I16Triplet(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U32(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U32Pair(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U32Triplet(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I32(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I32Pair(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I32Triplet(v) => WritableToBeBytes::written_len(v), + ParamsRaw::F32(v) => WritableToBeBytes::written_len(v), + ParamsRaw::F32Pair(v) => WritableToBeBytes::written_len(v), + ParamsRaw::F32Triplet(v) => WritableToBeBytes::written_len(v), + ParamsRaw::U64(v) => WritableToBeBytes::written_len(v), + ParamsRaw::I64(v) => WritableToBeBytes::written_len(v), + ParamsRaw::F64(v) => WritableToBeBytes::written_len(v), } } @@ -460,7 +467,7 @@ params_raw_from_newtype!( ); #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum EcssEnumParams { +pub enum ParamsEcssEnum { U8(EcssEnumU8), U16(EcssEnumU16), U32(EcssEnumU32), @@ -468,40 +475,46 @@ pub enum EcssEnumParams { } macro_rules! writable_as_be_bytes_ecss_enum_impl { - ($EnumIdent: ident) => { + ($EnumIdent: ident, $Ty: ident) => { + impl From<$EnumIdent> for ParamsEcssEnum { + fn from(e: $EnumIdent) -> Self { + Self::$Ty(e) + } + } + impl WritableToBeBytes for $EnumIdent { - fn raw_len(&self) -> usize { + fn written_len(&self) -> usize { self.size() } fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - ::write_to_be_bytes(self, buf).map(|_| self.raw_len()) + ::write_to_be_bytes(self, buf).map(|_| self.written_len()) } } }; } -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU8); -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU16); -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU32); -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU64); +writable_as_be_bytes_ecss_enum_impl!(EcssEnumU8, U8); +writable_as_be_bytes_ecss_enum_impl!(EcssEnumU16, U16); +writable_as_be_bytes_ecss_enum_impl!(EcssEnumU32, U32); +writable_as_be_bytes_ecss_enum_impl!(EcssEnumU64, U64); -impl WritableToBeBytes for EcssEnumParams { - fn raw_len(&self) -> usize { +impl WritableToBeBytes for ParamsEcssEnum { + fn written_len(&self) -> usize { match self { - EcssEnumParams::U8(e) => e.raw_len(), - EcssEnumParams::U16(e) => e.raw_len(), - EcssEnumParams::U32(e) => e.raw_len(), - EcssEnumParams::U64(e) => e.raw_len(), + ParamsEcssEnum::U8(e) => e.written_len(), + ParamsEcssEnum::U16(e) => e.written_len(), + ParamsEcssEnum::U32(e) => e.written_len(), + ParamsEcssEnum::U64(e) => e.written_len(), } } fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { match self { - EcssEnumParams::U8(e) => WritableToBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U16(e) => WritableToBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U32(e) => WritableToBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U64(e) => WritableToBeBytes::write_to_be_bytes(e, buf), + ParamsEcssEnum::U8(e) => WritableToBeBytes::write_to_be_bytes(e, buf), + ParamsEcssEnum::U16(e) => WritableToBeBytes::write_to_be_bytes(e, buf), + ParamsEcssEnum::U32(e) => WritableToBeBytes::write_to_be_bytes(e, buf), + ParamsEcssEnum::U64(e) => WritableToBeBytes::write_to_be_bytes(e, buf), } } } @@ -510,7 +523,19 @@ impl WritableToBeBytes for EcssEnumParams { #[derive(Debug, Copy, Clone, PartialEq)] pub enum ParamsHeapless { Raw(ParamsRaw), - EcssEnum(EcssEnumParams), + EcssEnum(ParamsEcssEnum), +} + +impl From for ParamsHeapless { + fn from(v: ParamsRaw) -> Self { + Self::Raw(v) + } +} + +impl From for ParamsHeapless { + fn from(v: ParamsEcssEnum) -> Self { + Self::EcssEnum(v) + } } macro_rules! from_conversions_for_raw { @@ -559,16 +584,14 @@ from_conversions_for_raw!( /// Generic enumeration for additional parameters, including parameters which rely on heap /// allocations. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum Params { Heapless(ParamsHeapless), Store(StoreAddr), #[cfg(feature = "alloc")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] Vec(Vec), #[cfg(feature = "alloc")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] String(String), } @@ -584,8 +607,13 @@ impl From for Params { } } +impl From for Params { + fn from(x: ParamsRaw) -> Self { + Self::Heapless(ParamsHeapless::Raw(x)) + } +} + #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl From> for Params { fn from(val: Vec) -> Self { Self::Vec(val) @@ -594,7 +622,6 @@ impl From> for Params { /// Converts a byte slice into the [Params::Vec] variant #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl From<&[u8]> for Params { fn from(val: &[u8]) -> Self { Self::Vec(val.to_vec()) @@ -602,7 +629,6 @@ impl From<&[u8]> for Params { } #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] impl From for Params { fn from(val: String) -> Self { Self::String(val) @@ -610,7 +636,6 @@ impl From for Params { } #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] /// Converts a string slice into the [Params::String] variant impl From<&str> for Params { fn from(val: &str) -> Self { @@ -618,10 +643,90 @@ impl From<&str> for Params { } } +/// Please note while [WritableToBeBytes] is implemented for [Params], the default implementation +/// will not be able to process the [Params::Store] parameter variant. +impl WritableToBeBytes for Params { + fn written_len(&self) -> usize { + match self { + Params::Heapless(p) => match p { + ParamsHeapless::Raw(raw) => raw.written_len(), + ParamsHeapless::EcssEnum(enumeration) => enumeration.written_len(), + }, + Params::Store(_) => 0, + #[cfg(feature = "alloc")] + Params::Vec(vec) => vec.len(), + #[cfg(feature = "alloc")] + Params::String(string) => string.len(), + } + } + + fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { + match self { + Params::Heapless(p) => match p { + ParamsHeapless::Raw(raw) => raw.write_to_be_bytes(buf), + ParamsHeapless::EcssEnum(enumeration) => enumeration.write_to_be_bytes(buf), + }, + Params::Store(_) => Ok(0), + #[cfg(feature = "alloc")] + Params::Vec(vec) => { + if buf.len() < vec.len() { + return Err(ByteConversionError::ToSliceTooSmall { + found: buf.len(), + expected: vec.len(), + }); + } + buf[0..vec.len()].copy_from_slice(vec); + Ok(vec.len()) + } + #[cfg(feature = "alloc")] + Params::String(string) => { + if buf.len() < string.len() { + return Err(ByteConversionError::ToSliceTooSmall { + found: buf.len(), + expected: string.len(), + }); + } + buf[0..string.len()].copy_from_slice(string.as_bytes()); + Ok(string.len()) + } + } + } +} + #[cfg(test)] mod tests { use super::*; + fn test_cloning_works(param_raw: &impl WritableToBeBytes) { + let _new_param = param_raw; + } + + fn test_writing_fails(param_raw: &(impl WritableToBeBytes + ToBeBytes)) { + let pair_size = WritableToBeBytes::written_len(param_raw); + assert_eq!(pair_size, ToBeBytes::written_len(param_raw)); + let mut vec = alloc::vec![0; pair_size - 1]; + let result = param_raw.write_to_be_bytes(&mut vec); + if let Err(ByteConversionError::ToSliceTooSmall { found, expected }) = result { + assert_eq!(found, pair_size - 1); + assert_eq!(expected, pair_size); + } else { + panic!("Expected ByteConversionError::ToSliceTooSmall"); + } + } + + fn test_writing(params_raw: &ParamsRaw, writeable: &impl WritableToBeBytes) { + assert_eq!(params_raw.written_len(), writeable.written_len()); + let mut vec = alloc::vec![0; writeable.written_len()]; + writeable + .write_to_be_bytes(&mut vec) + .expect("writing parameter to buffer failed"); + let mut other_vec = alloc::vec![0; writeable.written_len()]; + params_raw + .write_to_be_bytes(&mut other_vec) + .expect("writing parameter to buffer failed"); + assert_eq!(vec, other_vec); + } + #[test] fn test_basic_u32_pair() { let u32_pair = U32Pair(4, 8); @@ -632,10 +737,32 @@ mod tests { assert_eq!(u32_conv_back, 4); u32_conv_back = u32::from_be_bytes(raw[4..8].try_into().unwrap()); assert_eq!(u32_conv_back, 8); + test_writing_fails(&u32_pair); + test_cloning_works(&u32_pair); + let u32_praw = ParamsRaw::from(u32_pair); + test_writing(&u32_praw, &u32_pair); } #[test] - fn basic_signed_test_pair() { + fn test_u16_pair_writing_fails() { + let u16_pair = U16Pair(4, 8); + test_writing_fails(&u16_pair); + test_cloning_works(&u16_pair); + let u16_praw = ParamsRaw::from(u16_pair); + test_writing(&u16_praw, &u16_pair); + } + + #[test] + fn test_u8_pair_writing_fails() { + let u8_pair = U8Pair(4, 8); + test_writing_fails(&u8_pair); + test_cloning_works(&u8_pair); + let u8_praw = ParamsRaw::from(u8_pair); + test_writing(&u8_praw, &u8_pair); + } + + #[test] + fn basic_i8_test() { let i8_pair = I8Pair(-3, -16); assert_eq!(i8_pair.0, -3); assert_eq!(i8_pair.1, -16); @@ -644,10 +771,31 @@ mod tests { assert_eq!(i8_conv_back, -3); i8_conv_back = i8::from_be_bytes(raw[1..2].try_into().unwrap()); assert_eq!(i8_conv_back, -16); + test_writing_fails(&i8_pair); + test_cloning_works(&i8_pair); + let i8_praw = ParamsRaw::from(i8_pair); + test_writing(&i8_praw, &i8_pair); } #[test] - fn basic_signed_test_triplet() { + fn test_from_u32_triplet() { + let raw_params = U32Triplet::from((1, 2, 3)); + assert_eq!(raw_params.0, 1); + assert_eq!(raw_params.1, 2); + assert_eq!(raw_params.2, 3); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 12); + assert_eq!( + raw_params.to_be_bytes(), + [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3] + ); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let u32_triplet = ParamsRaw::from(raw_params); + test_writing(&u32_triplet, &raw_params); + } + + #[test] + fn test_i8_triplet() { let i8_triplet = I8Triplet(-3, -16, -126); assert_eq!(i8_triplet.0, -3); assert_eq!(i8_triplet.1, -16); @@ -659,6 +807,10 @@ mod tests { assert_eq!(i8_conv_back, -16); i8_conv_back = i8::from_be_bytes(raw[2..3].try_into().unwrap()); assert_eq!(i8_conv_back, -126); + test_writing_fails(&i8_triplet); + test_cloning_works(&i8_triplet); + let i8_praw = ParamsRaw::from(i8_triplet); + test_writing(&i8_praw, &i8_triplet); } #[test] @@ -681,4 +833,352 @@ mod tests { panic!("Params type is not a vector") } } + + #[test] + fn test_params_written_len_raw() { + let param_raw = ParamsRaw::from((500_u32, 1000_u32)); + let param: Params = Params::Heapless(param_raw.into()); + assert_eq!(param.written_len(), 8); + let mut buf: [u8; 8] = [0; 8]; + param + .write_to_be_bytes(&mut buf) + .expect("writing to buffer failed"); + assert_eq!(u32::from_be_bytes(buf[0..4].try_into().unwrap()), 500); + assert_eq!(u32::from_be_bytes(buf[4..8].try_into().unwrap()), 1000); + } + + #[test] + fn test_params_written_string() { + let string = "Test String".to_string(); + let param = Params::String(string.clone()); + assert_eq!(param.written_len(), string.len()); + let vec = param.to_vec().unwrap(); + let string_conv_back = String::from_utf8(vec).expect("conversion to string failed"); + assert_eq!(string_conv_back, string); + } + + #[test] + fn test_params_written_vec() { + let vec: Vec = alloc::vec![1, 2, 3, 4, 5]; + let param = Params::Vec(vec.clone()); + assert_eq!(param.written_len(), vec.len()); + assert_eq!(param.to_vec().expect("writing vec params failed"), vec); + } + + #[test] + fn test_u32_single() { + let raw_params = U32::from(20); + assert_eq!(raw_params.0, 20); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 4); + assert_eq!(raw_params.to_be_bytes(), [0, 0, 0, 20]); + let other = U32::from(20); + assert_eq!(raw_params, other); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let u32_praw = ParamsRaw::from(raw_params); + test_writing(&u32_praw, &raw_params); + } + + #[test] + fn test_i8_single() { + let neg_number: i8 = -5_i8; + let raw_params = I8::from(neg_number); + assert_eq!(raw_params.0, neg_number); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 1); + assert_eq!(raw_params.to_be_bytes(), neg_number.to_be_bytes()); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let u8_praw = ParamsRaw::from(raw_params); + test_writing(&u8_praw, &raw_params); + } + + #[test] + fn test_u8_single() { + let raw_params = U8::from(20); + assert_eq!(raw_params.0, 20); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 1); + assert_eq!(raw_params.to_be_bytes(), [20]); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let u32_praw = ParamsRaw::from(raw_params); + test_writing(&u32_praw, &raw_params); + } + + #[test] + fn test_u16_single() { + let raw_params = U16::from(0x123); + assert_eq!(raw_params.0, 0x123); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 2); + assert_eq!(raw_params.to_be_bytes(), [0x01, 0x23]); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let u16_praw = ParamsRaw::from(raw_params); + test_writing(&u16_praw, &raw_params); + } + + #[test] + fn test_u16_triplet() { + let raw_params = U16Triplet::from((1, 2, 3)); + assert_eq!(raw_params.0, 1); + assert_eq!(raw_params.1, 2); + assert_eq!(raw_params.2, 3); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 6); + assert_eq!(raw_params.to_be_bytes(), [0, 1, 0, 2, 0, 3]); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let u16_praw = ParamsRaw::from(raw_params); + test_writing(&u16_praw, &raw_params); + } + + #[test] + fn test_u8_triplet() { + let raw_params = U8Triplet::from((1, 2, 3)); + assert_eq!(raw_params.0, 1); + assert_eq!(raw_params.1, 2); + assert_eq!(raw_params.2, 3); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 3); + assert_eq!(raw_params.to_be_bytes(), [1, 2, 3]); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let u8_praw = ParamsRaw::from(raw_params); + test_writing(&u8_praw, &raw_params); + } + + #[test] + fn test_i16_single() { + let value = -300_i16; + let raw_params = I16::from(value); + assert_eq!(raw_params.0, value); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 2); + assert_eq!(raw_params.to_be_bytes(), value.to_be_bytes()); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let i16_praw = ParamsRaw::from(raw_params); + test_writing(&i16_praw, &raw_params); + } + + #[test] + fn test_i16_pair() { + let raw_params = I16Pair::from((-300, -400)); + assert_eq!(raw_params.0, -300); + assert_eq!(raw_params.1, -400); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 4); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let i16_praw = ParamsRaw::from(raw_params); + test_writing(&i16_praw, &raw_params); + } + + #[test] + fn test_i16_triplet() { + let raw_params = I16Triplet::from((-300, -400, -350)); + assert_eq!(raw_params.0, -300); + assert_eq!(raw_params.1, -400); + assert_eq!(raw_params.2, -350); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 6); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let i16_praw = ParamsRaw::from(raw_params); + test_writing(&i16_praw, &raw_params); + } + + #[test] + fn test_i32_single() { + let raw_params = I32::from(-80000); + assert_eq!(raw_params.0, -80000); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 4); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let i32_praw = ParamsRaw::from(raw_params); + test_writing(&i32_praw, &raw_params); + } + + #[test] + fn test_i32_pair() { + let raw_params = I32Pair::from((-80000, -200)); + assert_eq!(raw_params.0, -80000); + assert_eq!(raw_params.1, -200); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 8); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let i32_praw = ParamsRaw::from(raw_params); + test_writing(&i32_praw, &raw_params); + } + + #[test] + fn test_i32_triplet() { + let raw_params = I32Triplet::from((-80000, -5, -200)); + assert_eq!(raw_params.0, -80000); + assert_eq!(raw_params.1, -5); + assert_eq!(raw_params.2, -200); + assert_eq!(WritableToBeBytes::written_len(&raw_params), 12); + test_writing_fails(&raw_params); + test_cloning_works(&raw_params); + let i32_praw = ParamsRaw::from(raw_params); + test_writing(&i32_praw, &raw_params); + } + + #[test] + fn test_f32_single() { + let param = F32::from(0.1); + assert_eq!(param.0, 0.1); + assert_eq!(WritableToBeBytes::written_len(¶m), 4); + let f32_pair_raw = param.to_be_bytes(); + let f32_0 = f32::from_be_bytes(f32_pair_raw[0..4].try_into().unwrap()); + assert_eq!(f32_0, 0.1); + test_writing_fails(¶m); + test_cloning_works(¶m); + let praw = ParamsRaw::from(param); + test_writing(&praw, ¶m); + let p_try_from = F32::try_from(param.to_be_bytes().as_ref()).expect("try_from failed"); + assert_eq!(p_try_from, param); + } + + #[test] + fn test_f32_pair() { + let param = F32Pair::from((0.1, 0.2)); + assert_eq!(param.0, 0.1); + assert_eq!(param.1, 0.2); + assert_eq!(WritableToBeBytes::written_len(¶m), 8); + let f32_pair_raw = param.to_be_bytes(); + let f32_0 = f32::from_be_bytes(f32_pair_raw[0..4].try_into().unwrap()); + assert_eq!(f32_0, 0.1); + let f32_1 = f32::from_be_bytes(f32_pair_raw[4..8].try_into().unwrap()); + assert_eq!(f32_1, 0.2); + let other_pair = F32Pair::from((0.1, 0.2)); + assert_eq!(param, other_pair); + test_writing_fails(¶m); + test_cloning_works(¶m); + let praw = ParamsRaw::from(param); + test_writing(&praw, ¶m); + let p_try_from = F32Pair::try_from(param.to_be_bytes().as_ref()).expect("try_from failed"); + assert_eq!(p_try_from, param); + } + + #[test] + fn test_f32_triplet() { + let f32 = F32Triplet::from((0.1, -0.1, -5.2)); + assert_eq!(f32.0, 0.1); + assert_eq!(f32.1, -0.1); + assert_eq!(f32.2, -5.2); + assert_eq!(WritableToBeBytes::written_len(&f32), 12); + let f32_pair_raw = f32.to_be_bytes(); + let f32_0 = f32::from_be_bytes(f32_pair_raw[0..4].try_into().unwrap()); + assert_eq!(f32_0, 0.1); + let f32_1 = f32::from_be_bytes(f32_pair_raw[4..8].try_into().unwrap()); + assert_eq!(f32_1, -0.1); + let f32_2 = f32::from_be_bytes(f32_pair_raw[8..12].try_into().unwrap()); + assert_eq!(f32_2, -5.2); + test_writing_fails(&f32); + test_cloning_works(&f32); + let f32_praw = ParamsRaw::from(f32); + test_writing(&f32_praw, &f32); + let f32_try_from = + F32Triplet::try_from(f32.to_be_bytes().as_ref()).expect("try_from failed"); + assert_eq!(f32_try_from, f32); + } + + #[test] + fn test_u64_single() { + let u64 = U64::from(0x1010101010); + assert_eq!(u64.0, 0x1010101010); + assert_eq!(WritableToBeBytes::written_len(&u64), 8); + test_writing_fails(&u64); + test_cloning_works(&u64); + let praw = ParamsRaw::from(u64); + test_writing(&praw, &u64); + } + + #[test] + fn test_i64_single() { + let i64 = I64::from(-0xfffffffff); + assert_eq!(i64.0, -0xfffffffff); + assert_eq!(WritableToBeBytes::written_len(&i64), 8); + test_writing_fails(&i64); + test_cloning_works(&i64); + let praw = ParamsRaw::from(i64); + test_writing(&praw, &i64); + } + + #[test] + fn test_f64_single() { + let value = 823_823_812_832.232_3; + let f64 = F64::from(value); + assert_eq!(f64.0, value); + assert_eq!(WritableToBeBytes::written_len(&f64), 8); + test_writing_fails(&f64); + test_cloning_works(&f64); + let praw = ParamsRaw::from(f64); + test_writing(&praw, &f64); + } + + #[test] + fn test_f64_triplet() { + let f64_triplet = F64Triplet::from((0.1, 0.2, 0.3)); + assert_eq!(f64_triplet.0, 0.1); + assert_eq!(f64_triplet.1, 0.2); + assert_eq!(f64_triplet.2, 0.3); + assert_eq!(WritableToBeBytes::written_len(&f64_triplet), 24); + let f64_triplet_raw = f64_triplet.to_be_bytes(); + let f64_0 = f64::from_be_bytes(f64_triplet_raw[0..8].try_into().unwrap()); + assert_eq!(f64_0, 0.1); + let f64_1 = f64::from_be_bytes(f64_triplet_raw[8..16].try_into().unwrap()); + assert_eq!(f64_1, 0.2); + let f64_2 = f64::from_be_bytes(f64_triplet_raw[16..24].try_into().unwrap()); + assert_eq!(f64_2, 0.3); + test_writing_fails(&f64_triplet); + test_cloning_works(&f64_triplet); + } + + #[test] + fn test_u8_ecss_enum() { + let value = 200; + let u8p = EcssEnumU8::new(value); + test_cloning_works(&u8p); + let praw = ParamsEcssEnum::from(u8p); + assert_eq!(praw.written_len(), 1); + let mut buf = [0; 1]; + praw.write_to_be_bytes(&mut buf) + .expect("writing to buffer failed"); + buf[0] = 200; + } + + #[test] + fn test_u16_ecss_enum() { + let value = 60000; + let u16p = EcssEnumU16::new(value); + test_cloning_works(&u16p); + let praw = ParamsEcssEnum::from(u16p); + assert_eq!(praw.written_len(), 2); + let mut buf = [0; 2]; + praw.write_to_be_bytes(&mut buf) + .expect("writing to buffer failed"); + assert_eq!(u16::from_be_bytes(buf), value); + } + + #[test] + fn test_u32_ecss_enum() { + let value = 70000; + let u32p = EcssEnumU32::new(value); + test_cloning_works(&u32p); + let praw = ParamsEcssEnum::from(u32p); + assert_eq!(praw.written_len(), 4); + let mut buf = [0; 4]; + praw.write_to_be_bytes(&mut buf) + .expect("writing to buffer failed"); + assert_eq!(u32::from_be_bytes(buf), value); + } + + #[test] + fn test_u64_ecss_enum() { + let value = 0xffffffffff; + let u64p = EcssEnumU64::new(value); + test_cloning_works(&u64p); + let praw = ParamsEcssEnum::from(u64p); + assert_eq!(praw.written_len(), 8); + let mut buf = [0; 8]; + praw.write_to_be_bytes(&mut buf) + .expect("writing to buffer failed"); + assert_eq!(u64::from_be_bytes(buf), value); + } } diff --git a/satrs/src/pool.rs b/satrs/src/pool.rs index d17f565..1c3b8a4 100644 --- a/satrs/src/pool.rs +++ b/satrs/src/pool.rs @@ -72,7 +72,6 @@ //! } //! ``` #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub use alloc_mod::*; use core::fmt::{Display, Formatter}; use delegate::delegate; diff --git a/satrs/src/pus/action.rs b/satrs/src/pus/action.rs index 2ee4815..875621f 100644 --- a/satrs/src/pus/action.rs +++ b/satrs/src/pus/action.rs @@ -1,6 +1,10 @@ -use crate::{action::ActionRequest, TargetId}; +use crate::{ + action::{ActionId, ActionRequest}, + params::Params, + request::{GenericMessage, MessageMetadata, RequestId}, +}; -use super::verification::{TcStateAccepted, VerificationToken}; +use satrs_shared::res_code::ResultU16; #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] @@ -8,219 +12,278 @@ pub use std_mod::*; #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +#[allow(unused_imports)] pub use alloc_mod::*; -/// This trait is an abstraction for the routing of PUS service 8 action requests to a dedicated -/// recipient using the generic [TargetId]. -pub trait PusActionRequestRouter { - type Error; - fn route( - &self, - target_id: TargetId, - hk_request: ActionRequest, - token: VerificationToken, - ) -> Result<(), Self::Error>; +#[derive(Clone, Debug)] +pub struct ActionRequestWithId { + pub request_id: RequestId, + pub request: ActionRequest, +} + +/// A reply to an action request, but tailored to the PUS standard verification process. +#[non_exhaustive] +#[derive(Clone, PartialEq, Debug)] +pub enum ActionReplyVariant { + Completed, + StepSuccess { + step: u16, + }, + CompletionFailed { + error_code: ResultU16, + params: Option, + }, + StepFailed { + error_code: ResultU16, + step: u16, + params: Option, + }, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct PusActionReply { + pub action_id: ActionId, + pub variant: ActionReplyVariant, +} + +impl PusActionReply { + pub fn new(action_id: ActionId, variant: ActionReplyVariant) -> Self { + Self { action_id, variant } + } +} + +pub type GenericActionReplyPus = GenericMessage; + +impl GenericActionReplyPus { + pub fn new_action_reply( + requestor_info: MessageMetadata, + action_id: ActionId, + reply: ActionReplyVariant, + ) -> Self { + Self::new(requestor_info, PusActionReply::new(action_id, reply)) + } } #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub mod alloc_mod { - use spacepackets::ecss::tc::PusTcReader; + use crate::{ + action::ActionRequest, + queue::GenericTargetedMessagingError, + request::{ + GenericMessage, MessageReceiver, MessageSender, MessageSenderAndReceiver, RequestId, + }, + ComponentId, + }; - use crate::pus::verification::VerificationReportingProvider; + use super::PusActionReply; - use super::*; + /// Helper type definition for a mode handler which can handle mode requests. + pub type ActionRequestHandlerInterface = + MessageSenderAndReceiver; - /// This trait is an abstraction for the conversion of a PUS service 8 action telecommand into - /// an [ActionRequest]. - /// - /// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard. - /// The only requirement is that a valid [TargetId] and an [ActionRequest] are returned by the - /// core conversion function. - /// - /// The user should take care of performing the error handling as well. Some of the following - /// aspects might be relevant: - /// - /// - Checking the validity of the APID, service ID, subservice ID. - /// - Checking the validity of the user data. - /// - /// A [VerificationReportingProvider] instance is passed to the user to also allow handling - /// of the verification process as part of the PUS standard requirements. - pub trait PusActionToRequestConverter { - type Error; - fn convert( - &mut self, - token: VerificationToken, - tc: &PusTcReader, - time_stamp: &[u8], - verif_reporter: &impl VerificationReportingProvider, - ) -> Result<(TargetId, ActionRequest), Self::Error>; + impl, R: MessageReceiver> + ActionRequestHandlerInterface + { + pub fn try_recv_action_request( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.try_recv_message() + } + + pub fn send_action_reply( + &self, + request_id: RequestId, + target_id: ComponentId, + reply: PusActionReply, + ) -> Result<(), GenericTargetedMessagingError> { + self.send_message(request_id, target_id, reply) + } + } + + /// Helper type defintion for a mode handler object which can send mode requests and receive + /// mode replies. + pub type ActionRequestorInterface = + MessageSenderAndReceiver; + + impl, R: MessageReceiver> + ActionRequestorInterface + { + pub fn try_recv_action_reply( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.try_recv_message() + } + + pub fn send_action_request( + &self, + request_id: RequestId, + target_id: ComponentId, + request: ActionRequest, + ) -> Result<(), GenericTargetedMessagingError> { + self.send_message(request_id, target_id, request) + } } } #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub mod std_mod { - use crate::pus::{ - get_current_cds_short_timestamp, verification::VerificationReportingProvider, - EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, GenericRoutingError, - PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, PusServiceHelper, + use std::sync::mpsc; + + use crate::{ + pus::{ + verification::{self, TcStateToken}, + ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, + }, + ComponentId, }; use super::*; - /// This is a high-level handler for the PUS service 8 action service. - /// - /// It performs the following handling steps: - /// - /// 1. Retrieve the next TC packet from the [PusServiceHelper]. The [EcssTcInMemConverter] - /// allows to configure the used telecommand memory backend. - /// 2. Convert the TC to a targeted action request using the provided - /// [PusActionToRequestConverter]. The generic error type is constrained to the - /// [PusPacketHandlingError] for the concrete implementation which offers a packet handler. - /// 3. Route the action request using the provided [PusActionRequestRouter]. - /// 4. Handle all routing errors using the provided [PusRoutingErrorHandler]. - pub struct PusService8ActionHandler< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - RequestConverter: PusActionToRequestConverter, - RequestRouter: PusActionRequestRouter, - RoutingErrorHandler: PusRoutingErrorHandler, - RoutingError = GenericRoutingError, - > { - service_helper: - PusServiceHelper, - pub request_converter: RequestConverter, - pub request_router: RequestRouter, - pub routing_error_handler: RoutingErrorHandler, + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct ActivePusActionRequestStd { + pub action_id: ActionId, + common: ActivePusRequestStd, } - impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - RequestConverter: PusActionToRequestConverter, - RequestRouter: PusActionRequestRouter, - RoutingErrorHandler: PusRoutingErrorHandler, - RoutingError: Clone, - > - PusService8ActionHandler< - TcReceiver, - TmSender, - TcInMemConverter, - VerificationReporter, - RequestConverter, - RequestRouter, - RoutingErrorHandler, - RoutingError, - > - where - PusPacketHandlingError: From, - { + impl ActiveRequestProvider for ActivePusActionRequestStd { + delegate::delegate! { + to self.common { + fn target_id(&self) -> ComponentId; + fn token(&self) -> verification::TcStateToken; + fn set_token(&mut self, token: verification::TcStateToken); + fn has_timed_out(&self) -> bool; + fn timeout(&self) -> core::time::Duration; + } + } + } + + impl ActivePusActionRequestStd { + pub fn new_from_common_req(action_id: ActionId, common: ActivePusRequestStd) -> Self { + Self { action_id, common } + } + pub fn new( - service_helper: PusServiceHelper< - TcReceiver, - TmSender, - TcInMemConverter, - VerificationReporter, - >, - request_converter: RequestConverter, - request_router: RequestRouter, - routing_error_handler: RoutingErrorHandler, + action_id: ActionId, + target_id: ComponentId, + token: TcStateToken, + timeout: core::time::Duration, ) -> Self { Self { - service_helper, - request_converter, - request_router, - routing_error_handler, + action_id, + common: ActivePusRequestStd::new(target_id, token, timeout), } } - - /// Core function to poll the next TC packet and try to handle it. - pub fn handle_one_tc(&mut self) -> Result { - let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; - if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); - } - let ecss_tc_and_token = possible_packet.unwrap(); - let tc = self - .service_helper - .tc_in_mem_converter - .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; - let mut partial_error = None; - let time_stamp = get_current_cds_short_timestamp(&mut partial_error); - let (target_id, action_request) = self.request_converter.convert( - ecss_tc_and_token.token, - &tc, - &time_stamp, - &self.service_helper.common.verification_handler, - )?; - if let Err(e) = - self.request_router - .route(target_id, action_request, ecss_tc_and_token.token) - { - self.routing_error_handler.handle_error( - target_id, - ecss_tc_and_token.token, - &tc, - e.clone(), - &time_stamp, - &self.service_helper.common.verification_handler, - ); - return Err(e.into()); - } - Ok(PusPacketHandlerResult::RequestHandled) - } } + pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap; + + pub type ActionRequestHandlerMpsc = ActionRequestHandlerInterface< + mpsc::Sender>, + mpsc::Receiver>, + >; + pub type ActionRequestHandlerMpscBounded = ActionRequestHandlerInterface< + mpsc::SyncSender>, + mpsc::Receiver>, + >; + + pub type ActionRequestorMpsc = ActionRequestorInterface< + mpsc::Sender>, + mpsc::Receiver>, + >; + pub type ActionRequestorBoundedMpsc = ActionRequestorInterface< + mpsc::SyncSender>, + mpsc::Receiver>, + >; + + /* + pub type ModeRequestorAndHandlerMpsc = ModeInterface< + mpsc::Sender>, + mpsc::Receiver>, + mpsc::Sender>, + mpsc::Receiver>, + >; + pub type ModeRequestorAndHandlerMpscBounded = ModeInterface< + mpsc::SyncSender>, + mpsc::Receiver>, + mpsc::SyncSender>, + mpsc::Receiver>, + >; + */ } #[cfg(test)] mod tests { + /* + use core::{cell::RefCell, time::Duration}; + use std::{sync::mpsc, time::SystemTimeError}; + + use alloc::{collections::VecDeque, vec::Vec}; use delegate::delegate; use spacepackets::{ ecss::{ - tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader}, + tc::{PusTcCreator, PusTcReader}, tm::PusTmReader, PusPacket, }, - CcsdsPacket, SequenceFlags, SpHeader, + time::{cds, TimeWriter}, + CcsdsPacket, }; - use crate::pus::{ - tests::{ - PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler, TestConverter, - TestRouter, TestRoutingErrorHandler, APP_DATA_TOO_SHORT, TEST_APID, + use crate::{ + action::ActionRequestVariant, + params::{self, ParamsRaw, WritableToBeBytes}, + pus::{ + tests::{ + PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler, + TestConverter, TestRouter, APP_DATA_TOO_SHORT, + }, + verification::{ + self, + tests::{SharedVerificationMap, TestVerificationReporter, VerificationStatus}, + FailParams, TcStateAccepted, TcStateNone, TcStateStarted, + VerificationReportingProvider, + }, + EcssTcInMemConverter, EcssTcInVecConverter, EcssTmtcError, GenericRoutingError, + MpscTcReceiver, PusPacketHandlerResult, PusPacketHandlingError, PusRequestRouter, + PusServiceHelper, PusTcToRequestConverter, TmAsVecSenderWithMpsc, }, - verification::{ - tests::TestVerificationReporter, FailParams, RequestId, VerificationReportingProvider, - }, - EcssTcInVecConverter, GenericRoutingError, MpscTcReceiver, PusPacketHandlerResult, - PusPacketHandlingError, TmAsVecSenderWithMpsc, }; use super::*; - impl PusActionRequestRouter for TestRouter { + impl PusRequestRouter for TestRouter { type Error = GenericRoutingError; fn route( &self, target_id: TargetId, - hk_request: ActionRequest, + request: Request, _token: VerificationToken, ) -> Result<(), Self::Error> { self.routing_requests .borrow_mut() - .push_back((target_id, hk_request)); + .push_back((target_id, request)); self.check_for_injected_error() } + + fn handle_error( + &self, + target_id: TargetId, + token: VerificationToken, + tc: &PusTcReader, + error: Self::Error, + time_stamp: &[u8], + verif_reporter: &impl VerificationReportingProvider, + ) { + self.routing_errors + .borrow_mut() + .push_back((target_id, error)); + } } - impl PusActionToRequestConverter for TestConverter<8> { + impl PusTcToRequestConverter for TestConverter<8> { type Error = PusPacketHandlingError; fn convert( &mut self, @@ -254,9 +317,9 @@ mod tests { .expect("start success failure"); return Ok(( target_id.into(), - ActionRequest::UnsignedIdAndVecData { + ActionRequest { action_id: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()), - data: tc.user_data()[4..].to_vec(), + variant: ActionRequestVariant::VecData(tc.user_data()[4..].to_vec()), }, )); } @@ -266,31 +329,32 @@ mod tests { } } - struct Pus8HandlerWithVecTester { - common: PusServiceHandlerWithVecCommon, - handler: PusService8ActionHandler< + pub struct PusDynRequestHandler { + srv_helper: PusServiceHelper< MpscTcReceiver, TmAsVecSenderWithMpsc, EcssTcInVecConverter, TestVerificationReporter, - TestConverter<8>, - TestRouter, - TestRoutingErrorHandler, >, + request_converter: TestConverter, + request_router: TestRouter, } - impl Pus8HandlerWithVecTester { + struct Pus8RequestTestbenchWithVec { + common: PusServiceHandlerWithVecCommon, + handler: PusDynRequestHandler<8, ActionRequest>, + } + + impl Pus8RequestTestbenchWithVec { pub fn new() -> Self { - let (common, srv_handler) = - PusServiceHandlerWithVecCommon::new_with_test_verif_sender(); + let (common, srv_helper) = PusServiceHandlerWithVecCommon::new_with_test_verif_sender(); Self { common, - handler: PusService8ActionHandler::new( - srv_handler, - TestConverter::default(), - TestRouter::default(), - TestRoutingErrorHandler::default(), - ), + handler: PusDynRequestHandler { + srv_helper, + request_converter: TestConverter::default(), + request_router: TestRouter::default(), + }, } } @@ -305,13 +369,13 @@ mod tests { } } delegate! { - to self.handler.routing_error_handler { - pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError); + to self.handler.request_router { + pub fn retrieve_next_routing_error(&mut self) -> (TargetId, GenericRoutingError); } } } - impl PusTestHarness for Pus8HandlerWithVecTester { + impl PusTestHarness for Pus8RequestTestbenchWithVec { delegate! { to self.common { fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken; @@ -320,78 +384,421 @@ mod tests { fn check_next_verification_tm( &self, subservice: u8, - expected_request_id: RequestId, + expected_request_id: verification::RequestId, ); } } } - impl SimplePusPacketHandler for Pus8HandlerWithVecTester { + impl SimplePusPacketHandler for Pus8RequestTestbenchWithVec { + fn handle_one_tc(&mut self) -> Result { + let possible_packet = self.handler.srv_helper.retrieve_and_accept_next_packet()?; + if possible_packet.is_none() { + return Ok(PusPacketHandlerResult::Empty); + } + let ecss_tc_and_token = possible_packet.unwrap(); + let tc = self + .handler + .srv_helper + .tc_in_mem_converter + .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; + let time_stamp = cds::TimeProvider::from_now_with_u16_days() + .expect("timestamp generation failed") + .to_vec() + .unwrap(); + let (target_id, action_request) = self.handler.request_converter.convert( + ecss_tc_and_token.token, + &tc, + &time_stamp, + &self.handler.srv_helper.common.verification_handler, + )?; + if let Err(e) = self.handler.request_router.route( + target_id, + action_request, + ecss_tc_and_token.token, + ) { + self.handler.request_router.handle_error( + target_id, + ecss_tc_and_token.token, + &tc, + e.clone(), + &time_stamp, + &self.handler.srv_helper.common.verification_handler, + ); + return Err(e.into()); + } + Ok(PusPacketHandlerResult::RequestHandled) + } + } + + const TIMEOUT_ERROR_CODE: ResultU16 = ResultU16::new(1, 2); + const COMPLETION_ERROR_CODE: ResultU16 = ResultU16::new(2, 0); + const COMPLETION_ERROR_CODE_STEP: ResultU16 = ResultU16::new(2, 1); + + #[derive(Default)] + pub struct TestReplyHandlerHook { + pub unexpected_replies: VecDeque, + pub timeouts: RefCell>, + } + + impl ReplyHandlerHook for TestReplyHandlerHook { + fn handle_unexpected_reply(&mut self, reply: &GenericActionReplyPus) { + self.unexpected_replies.push_back(reply.clone()); + } + + fn timeout_callback(&self, active_request: &ActivePusActionRequest) { + self.timeouts.borrow_mut().push_back(active_request.clone()); + } + + fn timeout_error_code(&self) -> ResultU16 { + TIMEOUT_ERROR_CODE + } + } + + pub struct Pus8ReplyTestbench { + verif_reporter: TestVerificationReporter, + #[allow(dead_code)] + ecss_tm_receiver: mpsc::Receiver>, + handler: PusService8ReplyHandler< + TestVerificationReporter, + DefaultActiveActionRequestMap, + TestReplyHandlerHook, + mpsc::Sender>, + >, + } + + impl Pus8ReplyTestbench { + pub fn new(normal_ctor: bool) -> Self { + let reply_handler_hook = TestReplyHandlerHook::default(); + let shared_verif_map = SharedVerificationMap::default(); + let test_verif_reporter = TestVerificationReporter::new(shared_verif_map.clone()); + let (ecss_tm_sender, ecss_tm_receiver) = mpsc::channel(); + let reply_handler = if normal_ctor { + PusService8ReplyHandler::new_from_now_with_default_map( + test_verif_reporter.clone(), + 128, + reply_handler_hook, + ecss_tm_sender, + ) + .expect("creating reply handler failed") + } else { + PusService8ReplyHandler::new_from_now( + test_verif_reporter.clone(), + DefaultActiveActionRequestMap::default(), + 128, + reply_handler_hook, + ecss_tm_sender, + ) + .expect("creating reply handler failed") + }; + Self { + verif_reporter: test_verif_reporter, + ecss_tm_receiver, + handler: reply_handler, + } + } + + pub fn init_handling_for_request( + &mut self, + request_id: RequestId, + _action_id: ActionId, + ) -> VerificationToken { + assert!(!self.handler.request_active(request_id)); + // let action_req = ActionRequest::new(action_id, ActionRequestVariant::NoData); + let token = self.add_tc_with_req_id(request_id.into()); + let token = self + .verif_reporter + .acceptance_success(token, &[]) + .expect("acceptance success failure"); + let token = self + .verif_reporter + .start_success(token, &[]) + .expect("start success failure"); + let verif_info = self + .verif_reporter + .verification_info(&verification::RequestId::from(request_id)) + .expect("no verification info found"); + assert!(verif_info.started.expect("request was not started")); + assert!(verif_info.accepted.expect("request was not accepted")); + token + } + + pub fn next_unrequested_reply(&self) -> Option { + self.handler.user_hook.unexpected_replies.front().cloned() + } + + pub fn assert_request_completion_success(&self, step: Option, request_id: RequestId) { + let verif_info = self + .verif_reporter + .verification_info(&verification::RequestId::from(request_id)) + .expect("no verification info found"); + self.assert_request_completion_common(request_id, &verif_info, step, true); + } + + pub fn assert_request_completion_failure( + &self, + step: Option, + request_id: RequestId, + fail_enum: ResultU16, + fail_data: &[u8], + ) { + let verif_info = self + .verif_reporter + .verification_info(&verification::RequestId::from(request_id)) + .expect("no verification info found"); + self.assert_request_completion_common(request_id, &verif_info, step, false); + assert_eq!(verif_info.fail_enum.unwrap(), fail_enum.raw() as u64); + assert_eq!(verif_info.failure_data.unwrap(), fail_data); + } + + pub fn assert_request_completion_common( + &self, + request_id: RequestId, + verif_info: &VerificationStatus, + step: Option, + completion_success: bool, + ) { + if let Some(step) = step { + assert!(verif_info.step_status.is_some()); + assert!(verif_info.step_status.unwrap()); + assert_eq!(step, verif_info.step); + } + assert_eq!( + verif_info.completed.expect("request is not completed"), + completion_success + ); + assert!(!self.handler.request_active(request_id)); + } + + pub fn assert_request_step_failure(&self, step: u16, request_id: RequestId) { + let verif_info = self + .verif_reporter + .verification_info(&verification::RequestId::from(request_id)) + .expect("no verification info found"); + assert!(verif_info.step_status.is_some()); + assert!(!verif_info.step_status.unwrap()); + assert_eq!(step, verif_info.step); + } + pub fn add_routed_request( + &mut self, + request_id: verification::RequestId, + target_id: TargetId, + action_id: ActionId, + token: VerificationToken, + timeout: Duration, + ) { + if self.handler.request_active(request_id.into()) { + panic!("request already present"); + } + self.handler + .add_routed_action_request(request_id, target_id, action_id, token, timeout); + if !self.handler.request_active(request_id.into()) { + panic!("request should be active now"); + } + } + delegate! { to self.handler { - fn handle_one_tc(&mut self) -> Result; + pub fn request_active(&self, request_id: RequestId) -> bool; + + pub fn handle_action_reply( + &mut self, + action_reply_with_ids: GenericMessage, + time_stamp: &[u8] + ) -> Result<(), EcssTmtcError>; + + pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError>; + + pub fn check_for_timeouts(&mut self, time_stamp: &[u8]) -> Result<(), EcssTmtcError>; + } + to self.verif_reporter { + fn add_tc_with_req_id(&mut self, req_id: verification::RequestId) -> VerificationToken; } } } #[test] - fn basic_test() { - let mut action_handler = Pus8HandlerWithVecTester::new(); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); - let sec_header = PusTcSecondaryHeader::new_simple(8, 1); - let action_id: u32 = 1; - let action_id_raw = action_id.to_be_bytes(); - let tc = PusTcCreator::new(&mut sp_header, sec_header, action_id_raw.as_ref(), true); - action_handler.send_tc(&tc); - let result = action_handler.handle_one_tc(); - assert!(result.is_ok()); - action_handler.check_next_conversion(&tc); - let (target_id, action_req) = action_handler.retrieve_next_request(); - assert_eq!(target_id, TEST_APID.into()); - if let ActionRequest::UnsignedIdAndVecData { action_id, data } = action_req { - assert_eq!(action_id, 1); - assert_eq!(data, &[]); - } + fn test_reply_handler_completion_success() { + let mut reply_testbench = Pus8ReplyTestbench::new(true); + let sender_id = 0x06; + let request_id = 0x02; + let target_id = 0x05; + let action_id = 0x03; + let token = reply_testbench.init_handling_for_request(request_id, action_id); + reply_testbench.add_routed_request( + request_id.into(), + target_id, + action_id, + token, + Duration::from_millis(1), + ); + assert!(reply_testbench.request_active(request_id)); + let action_reply = GenericMessage::new( + request_id, + sender_id, + ActionReplyPusWithActionId { + action_id, + variant: ActionReplyPus::Completed, + }, + ); + reply_testbench + .handle_action_reply(action_reply, &[]) + .expect("reply handling failure"); + reply_testbench.assert_request_completion_success(None, request_id); } #[test] - fn test_routing_error() { - let mut action_handler = Pus8HandlerWithVecTester::new(); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); - let sec_header = PusTcSecondaryHeader::new_simple(8, 1); - let action_id: u32 = 1; - let action_id_raw = action_id.to_be_bytes(); - let tc = PusTcCreator::new(&mut sp_header, sec_header, action_id_raw.as_ref(), true); - let error = GenericRoutingError::UnknownTargetId(25); - action_handler - .handler - .request_router - .inject_routing_error(error); - action_handler.send_tc(&tc); - let result = action_handler.handle_one_tc(); - assert!(result.is_err()); - let check_error = |routing_error: GenericRoutingError| { - if let GenericRoutingError::UnknownTargetId(id) = routing_error { - assert_eq!(id, 25); - } else { - panic!("unexpected error type"); - } - }; - if let PusPacketHandlingError::RequestRoutingError(routing_error) = result.unwrap_err() { - check_error(routing_error); - } else { - panic!("unexpected error type"); - } - - action_handler.check_next_conversion(&tc); - let (target_id, action_req) = action_handler.retrieve_next_request(); - assert_eq!(target_id, TEST_APID.into()); - if let ActionRequest::UnsignedIdAndVecData { action_id, data } = action_req { - assert_eq!(action_id, 1); - assert_eq!(data, &[]); - } - - let (target_id, found_error) = action_handler.retrieve_next_error(); - assert_eq!(target_id, TEST_APID.into()); - check_error(found_error); + fn test_reply_handler_step_success() { + let mut reply_testbench = Pus8ReplyTestbench::new(false); + let request_id = 0x02; + let target_id = 0x05; + let action_id = 0x03; + let token = reply_testbench.init_handling_for_request(request_id, action_id); + reply_testbench.add_routed_request( + request_id.into(), + target_id, + action_id, + token, + Duration::from_millis(1), + ); + let action_reply = GenericActionReplyPus::new_action_reply( + request_id, + action_id, + action_id, + ActionReplyPus::StepSuccess { step: 1 }, + ); + reply_testbench + .handle_action_reply(action_reply, &[]) + .expect("reply handling failure"); + let action_reply = GenericActionReplyPus::new_action_reply( + request_id, + action_id, + action_id, + ActionReplyPus::Completed, + ); + reply_testbench + .handle_action_reply(action_reply, &[]) + .expect("reply handling failure"); + reply_testbench.assert_request_completion_success(Some(1), request_id); } + + #[test] + fn test_reply_handler_completion_failure() { + let mut reply_testbench = Pus8ReplyTestbench::new(true); + let sender_id = 0x01; + let request_id = 0x02; + let target_id = 0x05; + let action_id = 0x03; + let token = reply_testbench.init_handling_for_request(request_id, action_id); + reply_testbench.add_routed_request( + request_id.into(), + target_id, + action_id, + token, + Duration::from_millis(1), + ); + let params_raw = ParamsRaw::U32(params::U32(5)); + let action_reply = GenericActionReplyPus::new_action_reply( + request_id, + sender_id, + action_id, + ActionReplyPus::CompletionFailed { + error_code: COMPLETION_ERROR_CODE, + params: params_raw.into(), + }, + ); + reply_testbench + .handle_action_reply(action_reply, &[]) + .expect("reply handling failure"); + reply_testbench.assert_request_completion_failure( + None, + request_id, + COMPLETION_ERROR_CODE, + ¶ms_raw.to_vec().unwrap(), + ); + } + + #[test] + fn test_reply_handler_step_failure() { + let mut reply_testbench = Pus8ReplyTestbench::new(false); + let sender_id = 0x01; + let request_id = 0x02; + let target_id = 0x05; + let action_id = 0x03; + let token = reply_testbench.init_handling_for_request(request_id, action_id); + reply_testbench.add_routed_request( + request_id.into(), + target_id, + action_id, + token, + Duration::from_millis(1), + ); + let action_reply = GenericActionReplyPus::new_action_reply( + request_id, + sender_id, + action_id, + ActionReplyPus::StepFailed { + error_code: COMPLETION_ERROR_CODE_STEP, + step: 2, + params: ParamsRaw::U32(crate::params::U32(5)).into(), + }, + ); + reply_testbench + .handle_action_reply(action_reply, &[]) + .expect("reply handling failure"); + reply_testbench.assert_request_step_failure(2, request_id); + } + + #[test] + fn test_reply_handler_timeout_handling() { + let mut reply_testbench = Pus8ReplyTestbench::new(true); + let request_id = 0x02; + let target_id = 0x06; + let action_id = 0x03; + let token = reply_testbench.init_handling_for_request(request_id, action_id); + reply_testbench.add_routed_request( + request_id.into(), + target_id, + action_id, + token, + Duration::from_millis(1), + ); + let timeout_param = Duration::from_millis(1).as_millis() as u64; + let timeout_param_raw = timeout_param.to_be_bytes(); + std::thread::sleep(Duration::from_millis(2)); + reply_testbench + .update_time_from_now() + .expect("time update failure"); + reply_testbench.check_for_timeouts(&[]).unwrap(); + reply_testbench.assert_request_completion_failure( + None, + request_id, + TIMEOUT_ERROR_CODE, + &timeout_param_raw, + ); + } + + #[test] + fn test_unrequested_reply() { + let mut reply_testbench = Pus8ReplyTestbench::new(true); + let sender_id = 0x01; + let request_id = 0x02; + let action_id = 0x03; + + let action_reply = GenericActionReplyPus::new_action_reply( + request_id, + sender_id, + action_id, + ActionReplyPus::Completed, + ); + reply_testbench + .handle_action_reply(action_reply, &[]) + .expect("reply handling failure"); + let reply = reply_testbench.next_unrequested_reply(); + assert!(reply.is_some()); + let reply = reply.unwrap(); + assert_eq!(reply.message.action_id, action_id); + assert_eq!(reply.request_id, request_id); + assert_eq!(reply.message.variant, ActionReplyPus::Completed); + } + */ } diff --git a/satrs/src/pus/event.rs b/satrs/src/pus/event.rs index 5cde422..3ac928f 100644 --- a/satrs/src/pus/event.rs +++ b/satrs/src/pus/event.rs @@ -1,153 +1,129 @@ -use crate::pus::{source_buffer_large_enough, EcssTmtcError}; +use crate::pus::source_buffer_large_enough; use spacepackets::ecss::tm::PusTmCreator; use spacepackets::ecss::tm::PusTmSecondaryHeader; -use spacepackets::ecss::{EcssEnumeration, PusError}; +use spacepackets::ecss::EcssEnumeration; +use spacepackets::ByteConversionError; use spacepackets::{SpHeader, MAX_APID}; -use crate::pus::EcssTmSenderCore; #[cfg(feature = "alloc")] -pub use alloc_mod::EventReporter; +pub use alloc_mod::*; + pub use spacepackets::ecss::event::*; -pub struct EventReporterBase { - msg_count: u16, +pub struct EventReportCreator { apid: u16, pub dest_id: u16, } -impl EventReporterBase { - pub fn new(apid: u16) -> Option { +impl EventReportCreator { + pub fn new(apid: u16, dest_id: u16) -> Option { if apid > MAX_APID { return None; } - Some(Self { - msg_count: 0, - dest_id: 0, - apid, - }) + Some(Self { dest_id, apid }) } - pub fn event_info( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], + pub fn event_info<'time, 'src_data>( + &self, + time_stamp: &'time [u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmtcError> { + params: Option<&'src_data [u8]>, + src_data_buf: &'src_data mut [u8], + ) -> Result, ByteConversionError> { self.generate_and_send_generic_tm( - buf, Subservice::TmInfoReport, - sender, time_stamp, event_id, - aux_data, + params, + src_data_buf, ) } - pub fn event_low_severity( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], + pub fn event_low_severity<'time, 'src_data>( + &self, + time_stamp: &'time [u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmtcError> { + params: Option<&'src_data [u8]>, + src_data_buf: &'src_data mut [u8], + ) -> Result, ByteConversionError> { self.generate_and_send_generic_tm( - buf, Subservice::TmLowSeverityReport, - sender, time_stamp, event_id, - aux_data, + params, + src_data_buf, ) } - pub fn event_medium_severity( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], + pub fn event_medium_severity<'time, 'src_data>( + &self, + time_stamp: &'time [u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmtcError> { + params: Option<&'src_data [u8]>, + buf: &'src_data mut [u8], + ) -> Result, ByteConversionError> { self.generate_and_send_generic_tm( - buf, Subservice::TmMediumSeverityReport, - sender, time_stamp, event_id, - aux_data, - ) - } - - pub fn event_high_severity( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmtcError> { - self.generate_and_send_generic_tm( + params, buf, - Subservice::TmHighSeverityReport, - sender, - time_stamp, - event_id, - aux_data, ) } - fn generate_and_send_generic_tm( - &mut self, - buf: &mut [u8], - subservice: Subservice, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], + pub fn event_high_severity<'time, 'src_data>( + &self, + time_stamp: &'time [u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmtcError> { - let tm = self.generate_generic_event_tm(buf, subservice, time_stamp, event_id, aux_data)?; - sender.send_tm(tm.into())?; - self.msg_count += 1; - Ok(()) + params: Option<&'src_data [u8]>, + src_data_buf: &'src_data mut [u8], + ) -> Result, ByteConversionError> { + self.generate_and_send_generic_tm( + Subservice::TmHighSeverityReport, + time_stamp, + event_id, + params, + src_data_buf, + ) } - fn generate_generic_event_tm<'a>( - &'a self, - buf: &'a mut [u8], + fn generate_and_send_generic_tm<'time, 'src_data>( + &self, subservice: Subservice, - time_stamp: &'a [u8], + time_stamp: &'time [u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result { + params: Option<&'src_data [u8]>, + src_data_buf: &'src_data mut [u8], + ) -> Result, ByteConversionError> { + self.generate_generic_event_tm(subservice, time_stamp, event_id, params, src_data_buf) + } + + fn generate_generic_event_tm<'time, 'src_data>( + &self, + subservice: Subservice, + time_stamp: &'time [u8], + event_id: impl EcssEnumeration, + params: Option<&'src_data [u8]>, + src_data_buf: &'src_data mut [u8], + ) -> Result, ByteConversionError> { let mut src_data_len = event_id.size(); - if let Some(aux_data) = aux_data { + if let Some(aux_data) = params { src_data_len += aux_data.len(); } - source_buffer_large_enough(buf.len(), src_data_len)?; - let mut sp_header = SpHeader::tm_unseg(self.apid, 0, 0).unwrap(); - let sec_header = PusTmSecondaryHeader::new( - 5, - subservice.into(), - self.msg_count, - self.dest_id, - Some(time_stamp), - ); + source_buffer_large_enough(src_data_buf.len(), src_data_len)?; + let sec_header = + PusTmSecondaryHeader::new(5, subservice.into(), 0, self.dest_id, time_stamp); let mut current_idx = 0; - event_id - .write_to_be_bytes(&mut buf[0..event_id.size()]) - .map_err(PusError::ByteConversion)?; + event_id.write_to_be_bytes(&mut src_data_buf[0..event_id.size()])?; current_idx += event_id.size(); - if let Some(aux_data) = aux_data { - buf[current_idx..current_idx + aux_data.len()].copy_from_slice(aux_data); + if let Some(aux_data) = params { + src_data_buf[current_idx..current_idx + aux_data.len()].copy_from_slice(aux_data); current_idx += aux_data.len(); } Ok(PusTmCreator::new( - &mut sp_header, + SpHeader::new_from_apid(self.apid), sec_header, - &buf[0..current_idx], + &src_data_buf[0..current_idx], true, )) } @@ -156,84 +132,132 @@ impl EventReporterBase { #[cfg(feature = "alloc")] mod alloc_mod { use super::*; + use crate::pus::{EcssTmSenderCore, EcssTmtcError}; + use crate::ComponentId; use alloc::vec; use alloc::vec::Vec; + use core::cell::RefCell; + use spacepackets::ecss::PusError; - pub struct EventReporter { - source_data_buf: Vec, - pub reporter: EventReporterBase, + pub trait EventTmHookProvider { + fn modify_tm(&self, tm: &mut PusTmCreator); } - impl EventReporter { - pub fn new(apid: u16, max_event_id_and_aux_data_size: usize) -> Option { - let reporter = EventReporterBase::new(apid)?; + #[derive(Default)] + pub struct DummyEventHook {} + + impl EventTmHookProvider for DummyEventHook { + fn modify_tm(&self, _tm: &mut PusTmCreator) {} + } + + pub struct EventReporter { + id: ComponentId, + // Use interior mutability pattern here. This is just an intermediate buffer to the PUS event packet + // generation. + source_data_buf: RefCell>, + pub report_creator: EventReportCreator, + pub tm_hook: EventTmHook, + } + + impl EventReporter { + pub fn new( + id: ComponentId, + default_apid: u16, + default_dest_id: u16, + max_event_id_and_aux_data_size: usize, + ) -> Option { + let reporter = EventReportCreator::new(default_apid, default_dest_id)?; Some(Self { - source_data_buf: vec![0; max_event_id_and_aux_data_size], - reporter, + id, + source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]), + report_creator: reporter, + tm_hook: DummyEventHook::default(), }) } + } + impl EventReporter { + pub fn new_with_hook( + id: ComponentId, + default_apid: u16, + default_dest_id: u16, + max_event_id_and_aux_data_size: usize, + tm_hook: EventTmHook, + ) -> Option { + let reporter = EventReportCreator::new(default_apid, default_dest_id)?; + Some(Self { + id, + source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]), + report_creator: reporter, + tm_hook, + }) + } + pub fn event_info( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), + &self, + sender: &(impl EcssTmSenderCore + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, + params: Option<&[u8]>, ) -> Result<(), EcssTmtcError> { - self.reporter.event_info( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) + let mut mut_buf = self.source_data_buf.borrow_mut(); + let mut tm_creator = self + .report_creator + .event_info(time_stamp, event_id, params, mut_buf.as_mut_slice()) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.id, tm_creator.into())?; + Ok(()) } pub fn event_low_severity( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), + &self, + sender: &(impl EcssTmSenderCore + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, + params: Option<&[u8]>, ) -> Result<(), EcssTmtcError> { - self.reporter.event_low_severity( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) + let mut mut_buf = self.source_data_buf.borrow_mut(); + let mut tm_creator = self + .report_creator + .event_low_severity(time_stamp, event_id, params, mut_buf.as_mut_slice()) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.id, tm_creator.into())?; + Ok(()) } pub fn event_medium_severity( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), + &self, + sender: &(impl EcssTmSenderCore + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, + params: Option<&[u8]>, ) -> Result<(), EcssTmtcError> { - self.reporter.event_medium_severity( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) + let mut mut_buf = self.source_data_buf.borrow_mut(); + let mut tm_creator = self + .report_creator + .event_medium_severity(time_stamp, event_id, params, mut_buf.as_mut_slice()) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.id, tm_creator.into())?; + Ok(()) } pub fn event_high_severity( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), + &self, + sender: &(impl EcssTmSenderCore + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, + params: Option<&[u8]>, ) -> Result<(), EcssTmtcError> { - self.reporter.event_high_severity( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) + let mut mut_buf = self.source_data_buf.borrow_mut(); + let mut tm_creator = self + .report_creator + .event_high_severity(time_stamp, event_id, params, mut_buf.as_mut_slice()) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.id, tm_creator.into())?; + Ok(()) } } } @@ -242,9 +266,11 @@ mod alloc_mod { mod tests { use super::*; use crate::events::{EventU32, Severity}; + use crate::pus::test_util::TEST_COMPONENT_ID_0; use crate::pus::tests::CommonTmInfo; - use crate::pus::{EcssChannel, PusTmWrapper}; - use crate::ChannelId; + use crate::pus::{ChannelWithId, EcssTmSenderCore, EcssTmtcError, PusTmVariant}; + use crate::ComponentId; + use spacepackets::ecss::PusError; use spacepackets::ByteConversionError; use std::cell::RefCell; use std::collections::VecDeque; @@ -258,6 +284,7 @@ mod tests { #[derive(Debug, Eq, PartialEq, Clone)] struct TmInfo { + pub sender_id: ComponentId, pub common: CommonTmInfo, pub event: EventU32, pub aux_data: Vec, @@ -268,19 +295,19 @@ mod tests { pub service_queue: RefCell>, } - impl EcssChannel for TestSender { - fn channel_id(&self) -> ChannelId { + impl ChannelWithId for TestSender { + fn id(&self) -> ComponentId { 0 } } impl EcssTmSenderCore for TestSender { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + fn send_tm(&self, sender_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(_) => { + PusTmVariant::InStore(_) => { panic!("TestSender: unexpected call with address"); } - PusTmWrapper::Direct(tm) => { + PusTmVariant::Direct(tm) => { assert!(!tm.source_data().is_empty()); let src_data = tm.source_data(); assert!(src_data.len() >= 4); @@ -291,6 +318,7 @@ mod tests { aux_data.extend_from_slice(&src_data[4..]); } self.service_queue.borrow_mut().push_back(TmInfo { + sender_id, common: CommonTmInfo::new_from_tm(&tm), event, aux_data, @@ -348,7 +376,12 @@ mod tests { error_data: Option<&[u8]>, ) { let mut sender = TestSender::default(); - let reporter = EventReporter::new(EXAMPLE_APID, max_event_aux_data_buf); + let reporter = EventReporter::new( + TEST_COMPONENT_ID_0.id(), + EXAMPLE_APID, + 0, + max_event_aux_data_buf, + ); assert!(reporter.is_some()); let mut reporter = reporter.unwrap(); let time_stamp_empty: [u8; 7] = [0; 7]; @@ -378,6 +411,7 @@ mod tests { assert_eq!(tm_info.common.msg_counter, 0); assert_eq!(tm_info.common.apid, EXAMPLE_APID); assert_eq!(tm_info.event, event); + assert_eq!(tm_info.sender_id, TEST_COMPONENT_ID_0.id()); assert_eq!(tm_info.aux_data, error_copy); } @@ -440,7 +474,7 @@ mod tests { fn insufficient_buffer() { let mut sender = TestSender::default(); for i in 0..3 { - let reporter = EventReporter::new(EXAMPLE_APID, i); + let reporter = EventReporter::new(0, EXAMPLE_APID, 0, i); assert!(reporter.is_some()); let mut reporter = reporter.unwrap(); check_buf_too_small(&mut reporter, &mut sender, i); diff --git a/satrs/src/pus/event_man.rs b/satrs/src/pus/event_man.rs index e6e18c9..eecb375 100644 --- a/satrs/src/pus/event_man.rs +++ b/satrs/src/pus/event_man.rs @@ -157,8 +157,8 @@ pub mod alloc_mod { phantom: PhantomData<(E, EV)>, } - impl, EV: GenericEvent, E> - PusEventDispatcher + impl, Event: GenericEvent, E> + PusEventDispatcher { pub fn new(reporter: EventReporter, backend: B) -> Self { Self { @@ -168,20 +168,20 @@ pub mod alloc_mod { } } - pub fn enable_tm_for_event(&mut self, event: &EV) -> Result { + pub fn enable_tm_for_event(&mut self, event: &Event) -> Result { self.backend.enable_event_reporting(event) } - pub fn disable_tm_for_event(&mut self, event: &EV) -> Result { + pub fn disable_tm_for_event(&mut self, event: &Event) -> Result { self.backend.disable_event_reporting(event) } pub fn generate_pus_event_tm_generic( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), + &self, + sender: &(impl EcssTmSenderCore + ?Sized), time_stamp: &[u8], - event: EV, - aux_data: Option<&[u8]>, + event: Event, + params: Option<&[u8]>, ) -> Result { if !self.backend.event_enabled(&event) { return Ok(false); @@ -189,22 +189,22 @@ pub mod alloc_mod { match event.severity() { Severity::INFO => self .reporter - .event_info(sender, time_stamp, event, aux_data) + .event_info(sender, time_stamp, event, params) .map(|_| true) .map_err(|e| e.into()), Severity::LOW => self .reporter - .event_low_severity(sender, time_stamp, event, aux_data) + .event_low_severity(sender, time_stamp, event, params) .map(|_| true) .map_err(|e| e.into()), Severity::MEDIUM => self .reporter - .event_medium_severity(sender, time_stamp, event, aux_data) + .event_medium_severity(sender, time_stamp, event, params) .map(|_| true) .map_err(|e| e.into()), Severity::HIGH => self .reporter - .event_high_severity(sender, time_stamp, event, aux_data) + .event_high_severity(sender, time_stamp, event, params) .map(|_| true) .map_err(|e| e.into()), } @@ -239,8 +239,8 @@ pub mod alloc_mod { } pub fn generate_pus_event_tm( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), + &self, + sender: &(impl EcssTmSenderCore + ?Sized), time_stamp: &[u8], event: EventU32TypedSev, aux_data: Option<&[u8]>, @@ -257,31 +257,36 @@ pub mod alloc_mod { #[cfg(test)] mod tests { use super::*; - use crate::{events::SeverityInfo, pus::TmAsVecSenderWithMpsc}; + use crate::events::SeverityInfo; + use crate::pus::PusTmAsVec; + use crate::request::UniqueApidTargetId; use std::sync::mpsc::{self, TryRecvError}; const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::::const_new(1, 0); const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5); const EMPTY_STAMP: [u8; 7] = [0; 7]; + const TEST_APID: u16 = 0x02; + const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); fn create_basic_man_1() -> DefaultPusEventU32Dispatcher<()> { - let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed"); + let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128) + .expect("Creating event repoter failed"); PusEventDispatcher::new_with_default_backend(reporter) } fn create_basic_man_2() -> DefaultPusEventU32Dispatcher<()> { - let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed"); + let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128) + .expect("Creating event repoter failed"); let backend = DefaultPusEventMgmtBackend::default(); PusEventDispatcher::new(reporter, backend) } #[test] fn test_basic() { - let mut event_man = create_basic_man_1(); - let (event_tx, event_rx) = mpsc::channel(); - let mut sender = TmAsVecSenderWithMpsc::new(0, "test_sender", event_tx); + let event_man = create_basic_man_1(); + let (event_tx, event_rx) = mpsc::channel::(); let event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) + .generate_pus_event_tm(&event_tx, &EMPTY_STAMP, INFO_EVENT, None) .expect("Sending info event failed"); assert!(event_sent); @@ -292,13 +297,13 @@ mod tests { #[test] fn test_disable_event() { let mut event_man = create_basic_man_2(); - let (event_tx, event_rx) = mpsc::channel(); - let mut sender = TmAsVecSenderWithMpsc::new(0, "test", event_tx); + let (event_tx, event_rx) = mpsc::channel::(); + // let mut sender = TmAsVecSenderWithMpsc::new(0, "test", event_tx); let res = event_man.disable_tm_for_event(&LOW_SEV_EVENT); assert!(res.is_ok()); assert!(res.unwrap()); let mut event_sent = event_man - .generate_pus_event_tm_generic(&mut sender, &EMPTY_STAMP, LOW_SEV_EVENT, None) + .generate_pus_event_tm_generic(&event_tx, &EMPTY_STAMP, LOW_SEV_EVENT, None) .expect("Sending low severity event failed"); assert!(!event_sent); let res = event_rx.try_recv(); @@ -306,7 +311,7 @@ mod tests { assert!(matches!(res.unwrap_err(), TryRecvError::Empty)); // Check that only the low severity event was disabled event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) + .generate_pus_event_tm(&event_tx, &EMPTY_STAMP, INFO_EVENT, None) .expect("Sending info event failed"); assert!(event_sent); event_rx.try_recv().expect("No info event received"); @@ -315,8 +320,7 @@ mod tests { #[test] fn test_reenable_event() { let mut event_man = create_basic_man_1(); - let (event_tx, event_rx) = mpsc::channel(); - let mut sender = TmAsVecSenderWithMpsc::new(0, "test", event_tx); + let (event_tx, event_rx) = mpsc::channel::(); let mut res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT); assert!(res.is_ok()); assert!(res.unwrap()); @@ -324,7 +328,7 @@ mod tests { assert!(res.is_ok()); assert!(res.unwrap()); let event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) + .generate_pus_event_tm(&event_tx, &EMPTY_STAMP, INFO_EVENT, None) .expect("Sending info event failed"); assert!(event_sent); event_rx.try_recv().expect("No info event received"); diff --git a/satrs/src/pus/event_srv.rs b/satrs/src/pus/event_srv.rs index 64c1ba0..bb08f58 100644 --- a/satrs/src/pus/event_srv.rs +++ b/satrs/src/pus/event_srv.rs @@ -2,17 +2,18 @@ use crate::events::EventU32; use crate::pus::event_man::{EventRequest, EventRequestWithToken}; use crate::pus::verification::TcStateToken; use crate::pus::{PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError}; +use crate::queue::GenericSendError; use spacepackets::ecss::event::Subservice; use spacepackets::ecss::PusPacket; use std::sync::mpsc::Sender; use super::verification::VerificationReportingProvider; use super::{ - get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, - PusServiceHelper, + EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, GenericConversionError, + GenericRoutingError, PusServiceHelper, }; -pub struct PusService5EventHandler< +pub struct PusEventServiceHandler< TcReceiver: EcssTcReceiverCore, TmSender: EcssTmSenderCore, TcInMemConverter: EcssTcInMemConverter, @@ -28,7 +29,7 @@ impl< TmSender: EcssTmSenderCore, TcInMemConverter: EcssTcInMemConverter, VerificationReporter: VerificationReportingProvider, - > PusService5EventHandler + > PusEventServiceHandler { pub fn new( service_helper: PusServiceHelper< @@ -45,16 +46,19 @@ impl< } } - pub fn handle_one_tc(&mut self) -> Result { + pub fn poll_and_handle_next_tc( + &mut self, + time_stamp: &[u8], + ) -> Result { let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; if possible_packet.is_none() { return Ok(PusPacketHandlerResult::Empty); } let ecss_tc_and_token = possible_packet.unwrap(); - let tc = self - .service_helper - .tc_in_mem_converter - .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; + self.service_helper + .tc_in_mem_converter_mut() + .cache(&ecss_tc_and_token.tc_in_memory)?; + let tc = self.service_helper.tc_in_mem_converter().convert()?; let subservice = tc.subservice(); let srv = Subservice::try_from(subservice); if srv.is_err() { @@ -63,63 +67,73 @@ impl< ecss_tc_and_token.token, )); } - let handle_enable_disable_request = |enable: bool, stamp: [u8; 7]| { - if tc.user_data().len() < 4 { - return Err(PusPacketHandlingError::NotEnoughAppData { - expected: 4, - found: tc.user_data().len(), - }); - } - let user_data = tc.user_data(); - let event_u32 = EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap())); - let start_token = self - .service_helper - .common - .verification_handler - .start_success(ecss_tc_and_token.token, &stamp) - .map_err(|_| PartialPusHandlingError::Verification); - let partial_error = start_token.clone().err(); - let mut token: TcStateToken = ecss_tc_and_token.token.into(); - if let Ok(start_token) = start_token { - token = start_token.into(); - } - let event_req_with_token = if enable { - EventRequestWithToken { - request: EventRequest::Enable(event_u32), - token, + let handle_enable_disable_request = + |enable: bool| -> Result { + if tc.user_data().len() < 4 { + return Err(GenericConversionError::NotEnoughAppData { + expected: 4, + found: tc.user_data().len(), + } + .into()); } - } else { - EventRequestWithToken { - request: EventRequest::Disable(event_u32), - token, + let user_data = tc.user_data(); + let event_u32 = + EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap())); + let start_token = self + .service_helper + .common + .verif_reporter + .start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) + .map_err(|_| PartialPusHandlingError::Verification); + let partial_error = start_token.clone().err(); + let mut token: TcStateToken = ecss_tc_and_token.token.into(); + if let Ok(start_token) = start_token { + token = start_token.into(); } + let event_req_with_token = if enable { + EventRequestWithToken { + request: EventRequest::Enable(event_u32), + token, + } + } else { + EventRequestWithToken { + request: EventRequest::Disable(event_u32), + token, + } + }; + self.event_request_tx + .send(event_req_with_token) + .map_err(|_| { + PusPacketHandlingError::RequestRouting(GenericRoutingError::Send( + GenericSendError::RxDisconnected, + )) + })?; + if let Some(partial_error) = partial_error { + return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( + partial_error, + )); + } + Ok(PusPacketHandlerResult::RequestHandled) }; - self.event_request_tx - .send(event_req_with_token) - .map_err(|_| { - PusPacketHandlingError::Other("Forwarding event request failed".into()) - })?; - if let Some(partial_error) = partial_error { - return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( - partial_error, - )); - } - Ok(PusPacketHandlerResult::RequestHandled) - }; - let mut partial_error = None; - let time_stamp = get_current_cds_short_timestamp(&mut partial_error); + match srv.unwrap() { Subservice::TmInfoReport | Subservice::TmLowSeverityReport | Subservice::TmMediumSeverityReport | Subservice::TmHighSeverityReport => { - return Err(PusPacketHandlingError::InvalidSubservice(tc.subservice())) + return Err(PusPacketHandlingError::RequestConversion( + GenericConversionError::WrongService(tc.subservice()), + )) } Subservice::TcEnableEventGeneration => { - handle_enable_disable_request(true, time_stamp)?; + handle_enable_disable_request(true)?; } Subservice::TcDisableEventGeneration => { - handle_enable_disable_request(false, time_stamp)?; + handle_enable_disable_request(false)?; } Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => { return Ok(PusPacketHandlerResult::SubserviceNotImplemented( @@ -137,60 +151,70 @@ impl< mod tests { use delegate::delegate; use spacepackets::ecss::event::Subservice; + use spacepackets::time::{cds, TimeWriter}; use spacepackets::util::UnsignedEnum; use spacepackets::{ ecss::{ tc::{PusTcCreator, PusTcSecondaryHeader}, tm::PusTmReader, }, - SequenceFlags, SpHeader, + SpHeader, }; use std::sync::mpsc::{self, Sender}; use crate::pus::event_man::EventRequest; - use crate::pus::tests::SimplePusPacketHandler; + use crate::pus::test_util::{PusTestHarness, SimplePusPacketHandler, TEST_APID}; use crate::pus::verification::{ - RequestId, VerificationReporterWithSharedPoolMpscBoundedSender, + RequestId, VerificationReporter, VerificationReportingProvider, }; - use crate::pus::{MpscTcReceiver, TmInSharedPoolSenderWithBoundedMpsc}; + use crate::pus::{GenericConversionError, MpscTcReceiver, MpscTmInSharedPoolSenderBounded}; use crate::{ events::EventU32, pus::{ event_man::EventRequestWithToken, - tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness, TEST_APID}, + tests::PusServiceHandlerWithSharedStoreCommon, verification::{TcStateAccepted, VerificationToken}, EcssTcInSharedStoreConverter, PusPacketHandlerResult, PusPacketHandlingError, }, }; - use super::PusService5EventHandler; + use super::PusEventServiceHandler; const TEST_EVENT_0: EventU32 = EventU32::const_new(crate::events::Severity::INFO, 5, 25); struct Pus5HandlerWithStoreTester { common: PusServiceHandlerWithSharedStoreCommon, - handler: PusService5EventHandler< + handler: PusEventServiceHandler< MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, + MpscTmInSharedPoolSenderBounded, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, + VerificationReporter, >, } impl Pus5HandlerWithStoreTester { pub fn new(event_request_tx: Sender) -> Self { - let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(); + let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(0); Self { common, - handler: PusService5EventHandler::new(srv_handler, event_request_tx), + handler: PusEventServiceHandler::new(srv_handler, event_request_tx), } } } impl PusTestHarness for Pus5HandlerWithStoreTester { + fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken { + let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc); + self.handler + .service_helper + .verif_reporter() + .acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7]) + .expect("acceptance success failure") + } + delegate! { to self.common { - fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken; + fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator); fn read_next_tm(&mut self) -> PusTmReader<'_>; fn check_no_tm_available(&self) -> bool; fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId); @@ -200,10 +224,9 @@ mod tests { } impl SimplePusPacketHandler for Pus5HandlerWithStoreTester { - delegate! { - to self.handler { - fn handle_one_tc(&mut self) -> Result; - } + fn handle_one_tc(&mut self) -> Result { + let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); + self.handler.poll_and_handle_next_tc(&time_stamp) } } @@ -213,15 +236,16 @@ mod tests { expected_event_req: EventRequest, event_req_receiver: mpsc::Receiver, ) { - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(5, subservice as u8); let mut app_data = [0; 4]; TEST_EVENT_0 .write_to_be_bytes(&mut app_data) .expect("writing test event failed"); - let ping_tc = PusTcCreator::new(&mut sp_header, sec_header, &app_data, true); - let token = test_harness.send_tc(&ping_tc); - let request_id = token.req_id(); + let ping_tc = PusTcCreator::new(sp_header, sec_header, &app_data, true); + let token = test_harness.init_verification(&ping_tc); + test_harness.send_tc(&token, &ping_tc); + let request_id = token.request_id(); test_harness.handle_one_tc().unwrap(); test_harness.check_next_verification_tm(1, request_id); test_harness.check_next_verification_tm(3, request_id); @@ -274,10 +298,11 @@ mod tests { fn test_sending_custom_subservice() { let (event_request_tx, _) = mpsc::channel(); let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(5, 200); - let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true); - test_harness.send_tc(&ping_tc); + let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true); + let token = test_harness.init_verification(&ping_tc); + test_harness.send_tc(&token, &ping_tc); let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); @@ -292,15 +317,19 @@ mod tests { fn test_sending_invalid_app_data() { let (event_request_tx, _) = mpsc::channel(); let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(5, Subservice::TcEnableEventGeneration as u8); - let ping_tc = PusTcCreator::new(&mut sp_header, sec_header, &[0, 1, 2], true); - test_harness.send_tc(&ping_tc); + let ping_tc = PusTcCreator::new(sp_header, sec_header, &[0, 1, 2], true); + let token = test_harness.init_verification(&ping_tc); + test_harness.send_tc(&token, &ping_tc); let result = test_harness.handle_one_tc(); assert!(result.is_err()); let result = result.unwrap_err(); - if let PusPacketHandlingError::NotEnoughAppData { expected, found } = result { + if let PusPacketHandlingError::RequestConversion( + GenericConversionError::NotEnoughAppData { expected, found }, + ) = result + { assert_eq!(expected, 4); assert_eq!(found, 3); } else { diff --git a/satrs/src/pus/hk.rs b/satrs/src/pus/hk.rs deleted file mode 100644 index 852e8f7..0000000 --- a/satrs/src/pus/hk.rs +++ /dev/null @@ -1,406 +0,0 @@ -pub use spacepackets::ecss::hk::*; - -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub use std_mod::*; - -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub use alloc_mod::*; - -use crate::{hk::HkRequest, TargetId}; - -use super::verification::{TcStateAccepted, VerificationToken}; - -/// This trait is an abstraction for the routing of PUS service 3 housekeeping requests to a -/// dedicated recipient using the generic [TargetId]. -pub trait PusHkRequestRouter { - type Error; - fn route( - &self, - target_id: TargetId, - hk_request: HkRequest, - token: VerificationToken, - ) -> Result<(), Self::Error>; -} - -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub mod alloc_mod { - use spacepackets::ecss::tc::PusTcReader; - - use crate::pus::verification::VerificationReportingProvider; - - use super::*; - - /// This trait is an abstraction for the conversion of a PUS service 8 action telecommand into - /// a [HkRequest]. - /// - /// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard. - /// The only requirement is that a valid [TargetId] and a [HkRequest] are returned by the - /// core conversion function. - /// - /// The user should take care of performing the error handling as well. Some of the following - /// aspects might be relevant: - /// - /// - Checking the validity of the APID, service ID, subservice ID. - /// - Checking the validity of the user data. - /// - /// A [VerificationReportingProvider] is passed to the user to also allow handling - /// of the verification process as part of the PUS standard requirements. - pub trait PusHkToRequestConverter { - type Error; - fn convert( - &mut self, - token: VerificationToken, - tc: &PusTcReader, - time_stamp: &[u8], - verif_reporter: &impl VerificationReportingProvider, - ) -> Result<(TargetId, HkRequest), Self::Error>; - } -} - -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub mod std_mod { - use crate::pus::{ - get_current_cds_short_timestamp, verification::VerificationReportingProvider, - EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, GenericRoutingError, - PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, PusServiceHelper, - }; - - use super::*; - - /// This is a generic high-level handler for the PUS service 3 housekeeping service. - /// - /// It performs the following handling steps: - /// - /// 1. Retrieve the next TC packet from the [PusServiceHelper]. The [EcssTcInMemConverter] - /// allows to configure the used telecommand memory backend. - /// 2. Convert the TC to a targeted action request using the provided - /// [PusHkToRequestConverter]. The generic error type is constrained to the - /// [PusPacketHandlerResult] for the concrete implementation which offers a packet handler. - /// 3. Route the action request using the provided [PusHkRequestRouter]. The generic error - /// type is constrained to the [GenericRoutingError] for the concrete implementation. - /// 4. Handle all routing errors using the provided [PusRoutingErrorHandler]. The generic error - /// type is constrained to the [GenericRoutingError] for the concrete implementation. - pub struct PusService3HkHandler< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - RequestConverter: PusHkToRequestConverter, - RequestRouter: PusHkRequestRouter, - RoutingErrorHandler: PusRoutingErrorHandler, - RoutingError = GenericRoutingError, - > { - service_helper: - PusServiceHelper, - pub request_converter: RequestConverter, - pub request_router: RequestRouter, - pub routing_error_handler: RoutingErrorHandler, - } - - impl< - TcReceiver: EcssTcReceiverCore, - TmSender: EcssTmSenderCore, - TcInMemConverter: EcssTcInMemConverter, - VerificationReporter: VerificationReportingProvider, - RequestConverter: PusHkToRequestConverter, - RequestRouter: PusHkRequestRouter, - RoutingErrorHandler: PusRoutingErrorHandler, - RoutingError: Clone, - > - PusService3HkHandler< - TcReceiver, - TmSender, - TcInMemConverter, - VerificationReporter, - RequestConverter, - RequestRouter, - RoutingErrorHandler, - RoutingError, - > - where - PusPacketHandlingError: From, - { - pub fn new( - service_helper: PusServiceHelper< - TcReceiver, - TmSender, - TcInMemConverter, - VerificationReporter, - >, - request_converter: RequestConverter, - request_router: RequestRouter, - routing_error_handler: RoutingErrorHandler, - ) -> Self { - Self { - service_helper, - request_converter, - request_router, - routing_error_handler, - } - } - - pub fn handle_one_tc(&mut self) -> Result { - let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; - if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); - } - let ecss_tc_and_token = possible_packet.unwrap(); - let tc = self - .service_helper - .tc_in_mem_converter - .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; - let mut partial_error = None; - let time_stamp = get_current_cds_short_timestamp(&mut partial_error); - let (target_id, hk_request) = self.request_converter.convert( - ecss_tc_and_token.token, - &tc, - &time_stamp, - &self.service_helper.common.verification_handler, - )?; - if let Err(e) = - self.request_router - .route(target_id, hk_request, ecss_tc_and_token.token) - { - self.routing_error_handler.handle_error( - target_id, - ecss_tc_and_token.token, - &tc, - e.clone(), - &time_stamp, - &self.service_helper.common.verification_handler, - ); - return Err(e.into()); - } - Ok(PusPacketHandlerResult::RequestHandled) - } - } -} - -#[cfg(test)] -mod tests { - use delegate::delegate; - use spacepackets::ecss::hk::Subservice; - - use spacepackets::{ - ecss::{ - tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader}, - tm::PusTmReader, - PusPacket, - }, - CcsdsPacket, SequenceFlags, SpHeader, - }; - - use crate::pus::{MpscTcReceiver, TmAsVecSenderWithMpsc}; - use crate::{ - hk::HkRequest, - pus::{ - tests::{ - PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler, - TestConverter, TestRouter, TestRoutingErrorHandler, APP_DATA_TOO_SHORT, TEST_APID, - }, - verification::{ - tests::TestVerificationReporter, FailParams, RequestId, TcStateAccepted, - VerificationReportingProvider, VerificationToken, - }, - EcssTcInVecConverter, GenericRoutingError, PusPacketHandlerResult, - PusPacketHandlingError, - }, - TargetId, - }; - - use super::{PusHkRequestRouter, PusHkToRequestConverter, PusService3HkHandler}; - - impl PusHkRequestRouter for TestRouter { - type Error = GenericRoutingError; - - fn route( - &self, - target_id: TargetId, - hk_request: HkRequest, - _token: VerificationToken, - ) -> Result<(), Self::Error> { - self.routing_requests - .borrow_mut() - .push_back((target_id, hk_request)); - self.check_for_injected_error() - } - } - - impl PusHkToRequestConverter for TestConverter<3> { - type Error = PusPacketHandlingError; - fn convert( - &mut self, - token: VerificationToken, - tc: &PusTcReader, - time_stamp: &[u8], - verif_reporter: &impl VerificationReportingProvider, - ) -> Result<(TargetId, HkRequest), Self::Error> { - self.conversion_request.push_back(tc.raw_data().to_vec()); - self.check_service(tc)?; - let target_id = tc.apid(); - if tc.user_data().len() < 4 { - verif_reporter - .start_failure( - token, - FailParams::new( - time_stamp, - &APP_DATA_TOO_SHORT, - (tc.user_data().len() as u32).to_be_bytes().as_ref(), - ), - ) - .expect("start success failure"); - return Err(PusPacketHandlingError::NotEnoughAppData { - expected: 4, - found: tc.user_data().len(), - }); - } - if tc.subservice() == Subservice::TcGenerateOneShotHk as u8 { - verif_reporter - .start_success(token, time_stamp) - .expect("start success failure"); - return Ok(( - target_id.into(), - HkRequest::OneShot(u32::from_be_bytes( - tc.user_data()[0..4].try_into().unwrap(), - )), - )); - } - Err(PusPacketHandlingError::InvalidAppData( - "unexpected app data".into(), - )) - } - } - - struct Pus3HandlerWithVecTester { - common: PusServiceHandlerWithVecCommon, - handler: PusService3HkHandler< - MpscTcReceiver, - TmAsVecSenderWithMpsc, - EcssTcInVecConverter, - TestVerificationReporter, - TestConverter<3>, - TestRouter, - TestRoutingErrorHandler, - >, - } - - impl Pus3HandlerWithVecTester { - pub fn new() -> Self { - let (common, srv_handler) = - PusServiceHandlerWithVecCommon::new_with_test_verif_sender(); - Self { - common, - handler: PusService3HkHandler::new( - srv_handler, - TestConverter::default(), - TestRouter::default(), - TestRoutingErrorHandler::default(), - ), - } - } - - delegate! { - to self.handler.request_converter { - pub fn check_next_conversion(&mut self, tc: &PusTcCreator); - } - } - delegate! { - to self.handler.request_router { - pub fn retrieve_next_request(&mut self) -> (TargetId, HkRequest); - } - } - delegate! { - to self.handler.routing_error_handler { - pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError); - } - } - } - - impl PusTestHarness for Pus3HandlerWithVecTester { - delegate! { - to self.common { - fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken; - fn read_next_tm(&mut self) -> PusTmReader<'_>; - fn check_no_tm_available(&self) -> bool; - fn check_next_verification_tm( - &self, - subservice: u8, - expected_request_id: RequestId, - ); - } - } - } - impl SimplePusPacketHandler for Pus3HandlerWithVecTester { - delegate! { - to self.handler { - fn handle_one_tc(&mut self) -> Result; - } - } - } - - #[test] - fn basic_test() { - let mut hk_handler = Pus3HandlerWithVecTester::new(); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); - let sec_header = PusTcSecondaryHeader::new_simple(3, Subservice::TcGenerateOneShotHk as u8); - let unique_id: u32 = 1; - let unique_id_raw = unique_id.to_be_bytes(); - let tc = PusTcCreator::new(&mut sp_header, sec_header, unique_id_raw.as_ref(), true); - hk_handler.send_tc(&tc); - let result = hk_handler.handle_one_tc(); - assert!(result.is_ok()); - hk_handler.check_next_conversion(&tc); - let (target_id, hk_request) = hk_handler.retrieve_next_request(); - assert_eq!(target_id, TEST_APID.into()); - if let HkRequest::OneShot(id) = hk_request { - assert_eq!(id, unique_id); - } else { - panic!("unexpected request"); - } - } - - #[test] - fn test_routing_error() { - let mut hk_handler = Pus3HandlerWithVecTester::new(); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); - let sec_header = PusTcSecondaryHeader::new_simple(3, Subservice::TcGenerateOneShotHk as u8); - let unique_id: u32 = 1; - let unique_id_raw = unique_id.to_be_bytes(); - let tc = PusTcCreator::new(&mut sp_header, sec_header, unique_id_raw.as_ref(), true); - let error = GenericRoutingError::UnknownTargetId(25); - hk_handler - .handler - .request_router - .inject_routing_error(error); - hk_handler.send_tc(&tc); - let result = hk_handler.handle_one_tc(); - assert!(result.is_err()); - let check_error = |routing_error: GenericRoutingError| { - if let GenericRoutingError::UnknownTargetId(id) = routing_error { - assert_eq!(id, 25); - } else { - panic!("unexpected error type"); - } - }; - if let PusPacketHandlingError::RequestRoutingError(routing_error) = result.unwrap_err() { - check_error(routing_error); - } else { - panic!("unexpected error type"); - } - - hk_handler.check_next_conversion(&tc); - let (target_id, hk_req) = hk_handler.retrieve_next_request(); - assert_eq!(target_id, TEST_APID.into()); - if let HkRequest::OneShot(unique_id) = hk_req { - assert_eq!(unique_id, 1); - } - - let (target_id, found_error) = hk_handler.retrieve_next_error(); - assert_eq!(target_id, TEST_APID.into()); - check_error(found_error); - } -} diff --git a/satrs/src/pus/mod.rs b/satrs/src/pus/mod.rs index 60509ef..64187e4 100644 --- a/satrs/src/pus/mod.rs +++ b/satrs/src/pus/mod.rs @@ -4,9 +4,11 @@ //! The satrs-example application contains various usage examples of these components. use crate::pool::{StoreAddr, StoreError}; use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken}; -use crate::queue::{GenericRecvError, GenericSendError}; -use crate::ChannelId; +use crate::queue::{GenericReceiveError, GenericSendError}; +use crate::request::{GenericMessage, MessageMetadata, RequestId}; +use crate::ComponentId; use core::fmt::{Display, Formatter}; +use core::time::Duration; #[cfg(feature = "alloc")] use downcast_rs::{impl_downcast, Downcast}; #[cfg(feature = "alloc")] @@ -24,7 +26,6 @@ pub mod event; pub mod event_man; #[cfg(feature = "std")] pub mod event_srv; -pub mod hk; pub mod mode; pub mod scheduler; #[cfg(feature = "std")] @@ -39,46 +40,48 @@ pub use alloc_mod::*; #[cfg(feature = "std")] pub use std_mod::*; +use self::verification::VerificationReportingProvider; + #[derive(Debug, PartialEq, Eq, Clone)] -pub enum PusTmWrapper<'tm> { +pub enum PusTmVariant<'time, 'src_data> { InStore(StoreAddr), - Direct(PusTmCreator<'tm>), + Direct(PusTmCreator<'time, 'src_data>), } -impl From for PusTmWrapper<'_> { +impl From for PusTmVariant<'_, '_> { fn from(value: StoreAddr) -> Self { Self::InStore(value) } } -impl<'tm> From> for PusTmWrapper<'tm> { - fn from(value: PusTmCreator<'tm>) -> Self { +impl<'time, 'src_data> From> for PusTmVariant<'time, 'src_data> { + fn from(value: PusTmCreator<'time, 'src_data>) -> Self { Self::Direct(value) } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum EcssTmtcError { - StoreLock, Store(StoreError), + ByteConversion(ByteConversionError), Pus(PusError), CantSendAddr(StoreAddr), CantSendDirectTm, Send(GenericSendError), - Recv(GenericRecvError), + Receive(GenericReceiveError), } impl Display for EcssTmtcError { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { - EcssTmtcError::StoreLock => { - write!(f, "store lock error") - } EcssTmtcError::Store(store) => { - write!(f, "store error: {store}") + write!(f, "ecss tmtc error: {store}") } - EcssTmtcError::Pus(pus_e) => { - write!(f, "PUS error: {pus_e}") + EcssTmtcError::ByteConversion(e) => { + write!(f, "ecss tmtc error: {e}") + } + EcssTmtcError::Pus(e) => { + write!(f, "ecss tmtc error: {e}") } EcssTmtcError::CantSendAddr(addr) => { write!(f, "can not send address {addr}") @@ -86,11 +89,11 @@ impl Display for EcssTmtcError { EcssTmtcError::CantSendDirectTm => { write!(f, "can not send TM directly") } - EcssTmtcError::Send(send_e) => { - write!(f, "send error {send_e}") + EcssTmtcError::Send(e) => { + write!(f, "ecss tmtc error: {e}") } - EcssTmtcError::Recv(recv_e) => { - write!(f, "recv error {recv_e}") + EcssTmtcError::Receive(e) => { + write!(f, "ecss tmtc error {e}") } } } @@ -114,9 +117,15 @@ impl From for EcssTmtcError { } } -impl From for EcssTmtcError { - fn from(value: GenericRecvError) -> Self { - Self::Recv(value) +impl From for EcssTmtcError { + fn from(value: ByteConversionError) -> Self { + Self::ByteConversion(value) + } +} + +impl From for EcssTmtcError { + fn from(value: GenericReceiveError) -> Self { + Self::Receive(value) } } @@ -125,16 +134,17 @@ impl Error for EcssTmtcError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { EcssTmtcError::Store(e) => Some(e), + EcssTmtcError::ByteConversion(e) => Some(e), EcssTmtcError::Pus(e) => Some(e), EcssTmtcError::Send(e) => Some(e), - EcssTmtcError::Recv(e) => Some(e), + EcssTmtcError::Receive(e) => Some(e), _ => None, } } } -pub trait EcssChannel: Send { +pub trait ChannelWithId: Send { /// Each sender can have an ID associated with it - fn channel_id(&self) -> ChannelId; + fn id(&self) -> ComponentId; fn name(&self) -> &'static str { "unset" } @@ -144,7 +154,7 @@ pub trait EcssChannel: Send { /// /// This sender object is responsible for sending PUS telemetry to a TM sink. pub trait EcssTmSenderCore: Send { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError>; + fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError>; } /// Generic trait for a user supplied sender object. @@ -155,6 +165,16 @@ pub trait EcssTcSenderCore { fn send_tc(&self, tc: PusTcCreator, token: Option) -> Result<(), EcssTmtcError>; } +/// Dummy object which can be useful for tests. +#[derive(Default)] +pub struct EcssTmDummySender {} + +impl EcssTmSenderCore for EcssTmDummySender { + fn send_tm(&self, _source_id: ComponentId, _tm: PusTmVariant) -> Result<(), EcssTmtcError> { + Ok(()) + } +} + /// A PUS telecommand packet can be stored in memory using different methods. Right now, /// storage inside a pool structure like [crate::pool::StaticMemoryPool], and storage inside a /// `Vec` are supported. @@ -249,7 +269,7 @@ impl From for TryRecvTmtcError { } /// Generic trait for a user supplied receiver object. -pub trait EcssTcReceiverCore: EcssChannel { +pub trait EcssTcReceiverCore { fn recv_tc(&self) -> Result; } @@ -263,9 +283,73 @@ pub trait ReceivesEcssPusTc { fn pass_pus_tc(&mut self, header: &SpHeader, pus_tc: &PusTcReader) -> Result<(), Self::Error>; } +pub trait ActiveRequestMapProvider: Sized { + fn insert(&mut self, request_id: &RequestId, request_info: V); + fn get(&self, request_id: RequestId) -> Option<&V>; + fn get_mut(&mut self, request_id: RequestId) -> Option<&mut V>; + fn remove(&mut self, request_id: RequestId) -> bool; + + /// Call a user-supplied closure for each active request. + fn for_each(&self, f: F); + + /// Call a user-supplied closure for each active request. Mutable variant. + fn for_each_mut(&mut self, f: F); +} + +pub trait ActiveRequestProvider { + fn target_id(&self) -> ComponentId; + fn token(&self) -> TcStateToken; + fn set_token(&mut self, token: TcStateToken); + fn has_timed_out(&self) -> bool; + fn timeout(&self) -> Duration; +} + +/// This trait is an abstraction for the routing of PUS request to a dedicated +/// recipient using the generic [ComponentId]. +pub trait PusRequestRouter { + type Error; + + fn route( + &self, + requestor_info: MessageMetadata, + target_id: ComponentId, + request: Request, + ) -> Result<(), Self::Error>; +} + +pub trait PusReplyHandler { + type Error; + + /// This function handles a reply for a given PUS request and returns whether that request + /// is finished. A finished PUS request will be removed from the active request map. + fn handle_reply( + &mut self, + reply: &GenericMessage, + active_request: &ActiveRequestInfo, + tm_sender: &impl EcssTmSenderCore, + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result; + + fn handle_unrequested_reply( + &mut self, + reply: &GenericMessage, + tm_sender: &impl EcssTmSenderCore, + ) -> Result<(), Self::Error>; + + /// Handle the timeout of an active request. + fn handle_request_timeout( + &mut self, + active_request: &ActiveRequestInfo, + tm_sender: &impl EcssTmSenderCore, + verification_handler: &impl VerificationReportingProvider, + time_stamp: &[u8], + ) -> Result<(), Self::Error>; +} + #[cfg(feature = "alloc")] -mod alloc_mod { - use crate::TargetId; +pub mod alloc_mod { + use hashbrown::HashMap; use super::*; @@ -351,38 +435,241 @@ mod alloc_mod { impl_downcast!(EcssTcReceiver); - pub trait PusRoutingErrorHandler { + /// This trait is an abstraction for the conversion of a PUS telecommand into a generic request + /// type. + /// + /// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard. + /// The only requirement is that a valid active request information instance and a request + /// are returned by the core conversion function. The active request type needs to fulfill + /// the [ActiveRequestProvider] trait bound. + /// + /// The user should take care of performing the error handling as well. Some of the following + /// aspects might be relevant: + /// + /// - Checking the validity of the APID, service ID, subservice ID. + /// - Checking the validity of the user data. + /// + /// A [VerificationReportingProvider] instance is passed to the user to also allow handling + /// of the verification process as part of the PUS standard requirements. + pub trait PusTcToRequestConverter { type Error; - fn handle_error( - &self, - target_id: TargetId, + fn convert( + &mut self, token: VerificationToken, tc: &PusTcReader, - error: Self::Error, - time_stamp: &[u8], + tm_sender: &(impl EcssTmSenderCore + ?Sized), verif_reporter: &impl VerificationReportingProvider, - ); + time_stamp: &[u8], + ) -> Result<(ActiveRequestInfo, Request), Self::Error>; } + + #[derive(Clone, Debug)] + pub struct DefaultActiveRequestMap(pub HashMap); + + impl Default for DefaultActiveRequestMap { + fn default() -> Self { + Self(HashMap::new()) + } + } + + impl ActiveRequestMapProvider for DefaultActiveRequestMap { + fn insert(&mut self, request_id: &RequestId, request: V) { + self.0.insert(*request_id, request); + } + + fn get(&self, request_id: RequestId) -> Option<&V> { + self.0.get(&request_id) + } + + fn get_mut(&mut self, request_id: RequestId) -> Option<&mut V> { + self.0.get_mut(&request_id) + } + + fn remove(&mut self, request_id: RequestId) -> bool { + self.0.remove(&request_id).is_some() + } + + fn for_each(&self, mut f: F) { + for (req_id, active_req) in &self.0 { + f(req_id, active_req); + } + } + + fn for_each_mut(&mut self, mut f: F) { + for (req_id, active_req) in &mut self.0 { + f(req_id, active_req); + } + } + } + + /* + /// Generic reply handler structure which can be used to handle replies for a specific PUS + /// service. + /// + /// This is done by keeping track of active requests using an internal map structure. An API + /// to register new active requests is exposed as well. + /// The reply handler performs boilerplate tasks like performing the verification handling and + /// timeout handling. + /// + /// This object is not useful by itself but serves as a common building block for high-level + /// PUS reply handlers. Concrete PUS handlers should constrain the [ActiveRequestProvider] and + /// the `ReplyType` generics to specific types tailored towards PUS services in addition to + /// providing an API which can process received replies and convert them into verification + /// completions or other operation like user hook calls. The framework also provides some + /// concrete PUS handlers for common PUS services like the mode, action and housekeeping + /// service. + /// + /// This object does not automatically update its internal time information used to check for + /// timeouts. The user should call the [Self::update_time] and [Self::update_time_from_now] + /// methods to do this. + pub struct PusServiceReplyHandler< + ActiveRequestMap: ActiveRequestMapProvider, + ReplyHook: ReplyHandlerHook, + ActiveRequestType: ActiveRequestProvider, + ReplyType, + > { + pub active_request_map: ActiveRequestMap, + pub tm_buf: alloc::vec::Vec, + pub current_time: UnixTimestamp, + pub user_hook: ReplyHook, + phantom: PhantomData<(ActiveRequestType, ReplyType)>, + } + + impl< + ActiveRequestMap: ActiveRequestMapProvider, + ReplyHook: ReplyHandlerHook, + ActiveRequestType: ActiveRequestProvider, + ReplyType, + > + PusServiceReplyHandler< + ActiveRequestMap, + ReplyHook, + ActiveRequestType, + ReplyType, + > + { + #[cfg(feature = "std")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + pub fn new_from_now( + active_request_map: ActiveRequestMap, + fail_data_buf_size: usize, + user_hook: ReplyHook, + ) -> Result { + let current_time = UnixTimestamp::from_now()?; + Ok(Self::new( + active_request_map, + fail_data_buf_size, + user_hook, + tm_sender, + current_time, + )) + } + + pub fn new( + active_request_map: ActiveRequestMap, + fail_data_buf_size: usize, + user_hook: ReplyHook, + tm_sender: TmSender, + init_time: UnixTimestamp, + ) -> Self { + Self { + active_request_map, + tm_buf: alloc::vec![0; fail_data_buf_size], + current_time: init_time, + user_hook, + tm_sender, + phantom: PhantomData, + } + } + + pub fn add_routed_request( + &mut self, + request_id: verification::RequestId, + active_request_type: ActiveRequestType, + ) { + self.active_request_map + .insert(&request_id.into(), active_request_type); + } + + pub fn request_active(&self, request_id: RequestId) -> bool { + self.active_request_map.get(request_id).is_some() + } + + /// Check for timeouts across all active requests. + /// + /// It will call [Self::handle_timeout] for all active requests which have timed out. + pub fn check_for_timeouts(&mut self, time_stamp: &[u8]) -> Result<(), EcssTmtcError> { + let mut timed_out_commands = alloc::vec::Vec::new(); + self.active_request_map.for_each(|request_id, active_req| { + let diff = self.current_time - active_req.start_time(); + if diff.duration_absolute > active_req.timeout() { + self.handle_timeout(active_req, time_stamp); + } + timed_out_commands.push(*request_id); + }); + for timed_out_command in timed_out_commands { + self.active_request_map.remove(timed_out_command); + } + Ok(()) + } + + /// Handle the timeout for a given active request. + /// + /// This implementation will report a verification completion failure with a user-provided + /// error code. It supplies the configured request timeout in milliseconds as a [u64] + /// serialized in big-endian format as the failure data. + pub fn handle_timeout(&self, active_request: &ActiveRequestType, time_stamp: &[u8]) { + let timeout = active_request.timeout().as_millis() as u64; + let timeout_raw = timeout.to_be_bytes(); + self.verification_reporter + .completion_failure( + active_request.token(), + FailParams::new( + time_stamp, + &self.user_hook.timeout_error_code(), + &timeout_raw, + ), + ) + .unwrap(); + self.user_hook.timeout_callback(active_request); + } + + /// Update the current time used for timeout checks based on the current OS time. + #[cfg(feature = "std")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] + pub fn update_time_from_now(&mut self) -> Result<(), std::time::SystemTimeError> { + self.current_time = UnixTimestamp::from_now()?; + Ok(()) + } + + /// Update the current time used for timeout checks. + pub fn update_time(&mut self, time: UnixTimestamp) { + self.current_time = time; + } + } + */ } #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub mod std_mod { - use crate::pool::{PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool, StoreAddr}; + use crate::pool::{ + PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool, StoreAddr, StoreError, + }; use crate::pus::verification::{TcStateAccepted, VerificationToken}; use crate::pus::{ - EcssChannel, EcssTcAndToken, EcssTcReceiverCore, EcssTmSenderCore, EcssTmtcError, - GenericRecvError, GenericSendError, PusTmWrapper, TryRecvTmtcError, + EcssTcAndToken, EcssTcReceiverCore, EcssTmSenderCore, EcssTmtcError, GenericReceiveError, + GenericSendError, PusTmVariant, TryRecvTmtcError, }; use crate::tmtc::tm_helper::SharedTmPool; - use crate::{ChannelId, TargetId}; + use crate::ComponentId; use alloc::vec::Vec; + use core::time::Duration; use spacepackets::ecss::tc::PusTcReader; use spacepackets::ecss::tm::PusTmCreator; - use spacepackets::ecss::{PusError, WritablePusPacket}; - use spacepackets::time::cds::TimeProvider; + use spacepackets::ecss::WritablePusPacket; use spacepackets::time::StdTimestampError; - use spacepackets::time::TimeWriter; + use spacepackets::ByteConversionError; use std::string::String; use std::sync::mpsc; use std::sync::mpsc::TryRecvError; @@ -391,8 +678,14 @@ pub mod std_mod { #[cfg(feature = "crossbeam")] pub use cb_mod::*; - use super::verification::VerificationReportingProvider; - use super::{AcceptedEcssTcAndToken, TcInMemory}; + use super::verification::{TcStateToken, VerificationReportingProvider}; + use super::{AcceptedEcssTcAndToken, ActiveRequestProvider, TcInMemory}; + + #[derive(Debug)] + pub struct PusTmInPool { + pub source_id: ComponentId, + pub store_addr: StoreAddr, + } impl From> for EcssTmtcError { fn from(_: mpsc::SendError) -> Self { @@ -400,48 +693,70 @@ pub mod std_mod { } } - impl EcssTmSenderCore for mpsc::Sender { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + impl EcssTmSenderCore for mpsc::Sender { + fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(addr) => self - .send(addr) + PusTmVariant::InStore(store_addr) => self + .send(PusTmInPool { + source_id, + store_addr, + }) .map_err(|_| GenericSendError::RxDisconnected)?, - PusTmWrapper::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm), + PusTmVariant::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm), }; Ok(()) } } - impl EcssTmSenderCore for mpsc::SyncSender { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + impl EcssTmSenderCore for mpsc::SyncSender { + fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(addr) => self - .try_send(addr) + PusTmVariant::InStore(store_addr) => self + .try_send(PusTmInPool { + source_id, + store_addr, + }) .map_err(|e| EcssTmtcError::Send(e.into()))?, - PusTmWrapper::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm), + PusTmVariant::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm), }; Ok(()) } } - impl EcssTmSenderCore for mpsc::Sender> { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + #[derive(Debug)] + pub struct PusTmAsVec { + pub source_id: ComponentId, + pub packet: Vec, + } + + pub type MpscTmAsVecSender = mpsc::Sender; + + impl EcssTmSenderCore for MpscTmAsVecSender { + fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)), - PusTmWrapper::Direct(tm) => self - .send(tm.to_vec()?) + PusTmVariant::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)), + PusTmVariant::Direct(tm) => self + .send(PusTmAsVec { + source_id, + packet: tm.to_vec()?, + }) .map_err(|e| EcssTmtcError::Send(e.into()))?, }; Ok(()) } } - impl EcssTmSenderCore for mpsc::SyncSender> { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + pub type MpscTmAsVecSenderBounded = mpsc::SyncSender; + + impl EcssTmSenderCore for MpscTmAsVecSenderBounded { + fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)), - PusTmWrapper::Direct(tm) => self - .send(tm.to_vec()?) + PusTmVariant::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)), + PusTmVariant::Direct(tm) => self + .send(PusTmAsVec { + source_id, + packet: tm.to_vec()?, + }) .map_err(|e| EcssTmtcError::Send(e.into()))?, }; Ok(()) @@ -449,144 +764,62 @@ pub mod std_mod { } #[derive(Clone)] - pub struct TmInSharedPoolSenderWithId { - channel_id: ChannelId, - name: &'static str, + pub struct TmInSharedPoolSender { shared_tm_store: SharedTmPool, sender: Sender, } - impl EcssChannel for TmInSharedPoolSenderWithId { - fn channel_id(&self) -> ChannelId { - self.channel_id - } - - fn name(&self) -> &'static str { - self.name - } - } - - impl TmInSharedPoolSenderWithId { - pub fn send_direct_tm(&self, tm: PusTmCreator) -> Result<(), EcssTmtcError> { + impl TmInSharedPoolSender { + pub fn send_direct_tm( + &self, + source_id: ComponentId, + tm: PusTmCreator, + ) -> Result<(), EcssTmtcError> { let addr = self.shared_tm_store.add_pus_tm(&tm)?; - self.sender.send_tm(PusTmWrapper::InStore(addr)) + self.sender.send_tm(source_id, PusTmVariant::InStore(addr)) } } - impl EcssTmSenderCore for TmInSharedPoolSenderWithId { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { - if let PusTmWrapper::Direct(tm) = tm { - return self.send_direct_tm(tm); + impl EcssTmSenderCore for TmInSharedPoolSender { + fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> { + if let PusTmVariant::Direct(tm) = tm { + return self.send_direct_tm(source_id, tm); } - self.sender.send_tm(tm) + self.sender.send_tm(source_id, tm) } } - impl TmInSharedPoolSenderWithId { - pub fn new( - id: ChannelId, - name: &'static str, - shared_tm_store: SharedTmPool, - sender: Sender, - ) -> Self { + impl TmInSharedPoolSender { + pub fn new(shared_tm_store: SharedTmPool, sender: Sender) -> Self { Self { - channel_id: id, - name, shared_tm_store, sender, } } } - pub type TmInSharedPoolSenderWithMpsc = TmInSharedPoolSenderWithId>; - pub type TmInSharedPoolSenderWithBoundedMpsc = - TmInSharedPoolSenderWithId>; + pub type MpscTmInSharedPoolSender = TmInSharedPoolSender>; + pub type MpscTmInSharedPoolSenderBounded = TmInSharedPoolSender>; - /// This class can be used if frequent heap allocations during run-time are not an issue. - /// PUS TM packets will be sent around as [Vec]s. Please note that the current implementation - /// of this class can not deal with store addresses, so it is assumed that is is always - /// going to be called with direct packets. - #[derive(Clone)] - pub struct TmAsVecSenderWithId { - id: ChannelId, - name: &'static str, - sender: Sender, - } - - impl From>> for EcssTmtcError { - fn from(_: mpsc::SendError>) -> Self { - Self::Send(GenericSendError::RxDisconnected) - } - } - - impl TmAsVecSenderWithId { - pub fn new(id: u32, name: &'static str, sender: Sender) -> Self { - Self { id, sender, name } - } - } - - impl EcssChannel for TmAsVecSenderWithId { - fn channel_id(&self) -> ChannelId { - self.id - } - fn name(&self) -> &'static str { - self.name - } - } - - impl EcssTmSenderCore for TmAsVecSenderWithId { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { - self.sender.send_tm(tm) - } - } - - pub type TmAsVecSenderWithMpsc = TmAsVecSenderWithId>>; - pub type TmAsVecSenderWithBoundedMpsc = TmAsVecSenderWithId>>; - - pub struct MpscTcReceiver { - id: ChannelId, - name: &'static str, - receiver: mpsc::Receiver, - } - - impl EcssChannel for MpscTcReceiver { - fn channel_id(&self) -> ChannelId { - self.id - } - - fn name(&self) -> &'static str { - self.name - } - } + pub type MpscTcReceiver = mpsc::Receiver; impl EcssTcReceiverCore for MpscTcReceiver { fn recv_tc(&self) -> Result { - self.receiver.try_recv().map_err(|e| match e { + self.try_recv().map_err(|e| match e { TryRecvError::Empty => TryRecvTmtcError::Empty, - TryRecvError::Disconnected => { - TryRecvTmtcError::Tmtc(EcssTmtcError::from(GenericRecvError::TxDisconnected)) - } + TryRecvError::Disconnected => TryRecvTmtcError::Tmtc(EcssTmtcError::from( + GenericReceiveError::TxDisconnected(None), + )), }) } } - impl MpscTcReceiver { - pub fn new( - id: ChannelId, - name: &'static str, - receiver: mpsc::Receiver, - ) -> Self { - Self { id, name, receiver } - } - } - #[cfg(feature = "crossbeam")] pub mod cb_mod { use super::*; use crossbeam_channel as cb; - pub type TmInSharedPoolSenderWithCrossbeam = - TmInSharedPoolSenderWithId>; + pub type TmInSharedPoolSenderWithCrossbeam = TmInSharedPoolSender>; impl From> for EcssTmtcError { fn from(_: cb::SendError) -> Self { @@ -605,64 +838,87 @@ pub mod std_mod { } } - impl EcssTmSenderCore for cb::Sender { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + impl EcssTmSenderCore for cb::Sender { + fn send_tm( + &self, + source_id: ComponentId, + tm: PusTmVariant, + ) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(addr) => self - .try_send(addr) + PusTmVariant::InStore(addr) => self + .try_send(PusTmInPool { + source_id, + store_addr: addr, + }) .map_err(|e| EcssTmtcError::Send(e.into()))?, - PusTmWrapper::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm), + PusTmVariant::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm), }; Ok(()) } } - impl EcssTmSenderCore for cb::Sender> { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + impl EcssTmSenderCore for cb::Sender { + fn send_tm( + &self, + source_id: ComponentId, + tm: PusTmVariant, + ) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)), - PusTmWrapper::Direct(tm) => self - .send(tm.to_vec()?) + PusTmVariant::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)), + PusTmVariant::Direct(tm) => self + .send(PusTmAsVec { + source_id, + packet: tm.to_vec()?, + }) .map_err(|e| EcssTmtcError::Send(e.into()))?, }; Ok(()) } } - pub struct CrossbeamTcReceiver { - id: ChannelId, - name: &'static str, - receiver: cb::Receiver, - } + pub type CrossbeamTcReceiver = cb::Receiver; + } - impl CrossbeamTcReceiver { - pub fn new( - id: ChannelId, - name: &'static str, - receiver: cb::Receiver, - ) -> Self { - Self { id, name, receiver } + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct ActivePusRequestStd { + target_id: ComponentId, + token: TcStateToken, + start_time: std::time::Instant, + timeout: Duration, + } + + impl ActivePusRequestStd { + pub fn new( + target_id: ComponentId, + token: impl Into, + timeout: Duration, + ) -> Self { + Self { + target_id, + token: token.into(), + start_time: std::time::Instant::now(), + timeout, } } + } - impl EcssChannel for CrossbeamTcReceiver { - fn channel_id(&self) -> ChannelId { - self.id - } - - fn name(&self) -> &'static str { - self.name - } + impl ActiveRequestProvider for ActivePusRequestStd { + fn target_id(&self) -> ComponentId { + self.target_id } - impl EcssTcReceiverCore for CrossbeamTcReceiver { - fn recv_tc(&self) -> Result { - self.receiver.try_recv().map_err(|e| match e { - cb::TryRecvError::Empty => TryRecvTmtcError::Empty, - cb::TryRecvError::Disconnected => TryRecvTmtcError::Tmtc(EcssTmtcError::from( - GenericRecvError::TxDisconnected, - )), - }) - } + fn token(&self) -> TcStateToken { + self.token + } + + fn timeout(&self) -> Duration { + self.timeout + } + fn set_token(&mut self, token: TcStateToken) { + self.token = token; + } + + fn has_timed_out(&self) -> bool { + std::time::Instant::now() - self.start_time > self.timeout } } @@ -671,37 +927,52 @@ pub mod std_mod { // will be no_std soon, see https://github.com/rust-lang/rust/issues/103765 . #[derive(Debug, Clone, Error)] - pub enum GenericRoutingError { - #[error("not enough application data, expected at least {expected}, found {found}")] - NotEnoughAppData { expected: usize, found: usize }, - #[error("Unknown target ID {0}")] - UnknownTargetId(TargetId), - #[error("Sending action request failed: {0}")] - SendError(GenericSendError), + pub enum PusTcFromMemError { + #[error("generic PUS error: {0}")] + EcssTmtc(#[from] EcssTmtcError), + #[error("invalid format of TC in memory: {0:?}")] + InvalidFormat(TcInMemory), } #[derive(Debug, Clone, Error)] - pub enum PusPacketHandlingError { - #[error("generic PUS error: {0}")] - Pus(#[from] PusError), + pub enum GenericRoutingError { + // #[error("not enough application data, expected at least {expected}, found {found}")] + // NotEnoughAppData { expected: usize, found: usize }, + #[error("Unknown target ID {0}")] + UnknownTargetId(ComponentId), + #[error("Sending action request failed: {0}")] + Send(GenericSendError), + } + + /// This error can be used for generic conversions from PUS Telecommands to request types. + /// + /// Please note that this error can also be used if no request is generated and the PUS + /// service, subservice and application data is used directly to perform some request. + #[derive(Debug, Clone, Error)] + pub enum GenericConversionError { #[error("wrong service number {0} for packet handler")] WrongService(u8), #[error("invalid subservice {0}")] InvalidSubservice(u8), #[error("not enough application data, expected at least {expected}, found {found}")] NotEnoughAppData { expected: usize, found: usize }, - #[error("PUS packet too large, does not fit in buffer: {0}")] - PusPacketTooLarge(usize), #[error("invalid application data")] InvalidAppData(String), - #[error("invalid format of TC in memory: {0:?}")] - InvalidTcInMemoryFormat(TcInMemory), - #[error("generic ECSS tmtc error: {0}")] - EcssTmtc(#[from] EcssTmtcError), + } + + /// Wrapper type which tries to encapsulate all possible errors when handling PUS packets. + #[derive(Debug, Clone, Error)] + pub enum PusPacketHandlingError { + #[error("error polling PUS TC packet: {0}")] + TcPolling(#[from] EcssTmtcError), + #[error("error generating PUS reader from memory: {0}")] + TcFromMem(#[from] PusTcFromMemError), + #[error("generic request conversion error: {0}")] + RequestConversion(#[from] GenericConversionError), + #[error("request routing error: {0}")] + RequestRouting(#[from] GenericRoutingError), #[error("invalid verification token")] InvalidVerificationToken, - #[error("request routing error: {0}")] - RequestRoutingError(#[from] GenericRoutingError), #[error("other error {0}")] Other(String), } @@ -735,19 +1006,24 @@ pub mod std_mod { } pub trait EcssTcInMemConverter { - fn cache_ecss_tc_in_memory( - &mut self, - possible_packet: &TcInMemory, - ) -> Result<(), PusPacketHandlingError>; + fn cache(&mut self, possible_packet: &TcInMemory) -> Result<(), PusTcFromMemError>; fn tc_slice_raw(&self) -> &[u8]; - fn convert_ecss_tc_in_memory_to_reader( + fn cache_and_convert( &mut self, possible_packet: &TcInMemory, - ) -> Result, PusPacketHandlingError> { - self.cache_ecss_tc_in_memory(possible_packet)?; - Ok(PusTcReader::new(self.tc_slice_raw())?.0) + ) -> Result, PusTcFromMemError> { + self.cache(possible_packet)?; + Ok(PusTcReader::new(self.tc_slice_raw()) + .map_err(EcssTmtcError::Pus)? + .0) + } + + fn convert(&self) -> Result, PusTcFromMemError> { + Ok(PusTcReader::new(self.tc_slice_raw()) + .map_err(EcssTmtcError::Pus)? + .0) } } @@ -760,16 +1036,11 @@ pub mod std_mod { } impl EcssTcInMemConverter for EcssTcInVecConverter { - fn cache_ecss_tc_in_memory( - &mut self, - tc_in_memory: &TcInMemory, - ) -> Result<(), PusPacketHandlingError> { + fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> { self.pus_tc_raw = None; match tc_in_memory { super::TcInMemory::StoreAddr(_) => { - return Err(PusPacketHandlingError::InvalidTcInMemoryFormat( - tc_in_memory.clone(), - )); + return Err(PusTcFromMemError::InvalidFormat(tc_in_memory.clone())); } super::TcInMemory::Vec(vec) => { self.pus_tc_raw = Some(vec.clone()); @@ -803,17 +1074,20 @@ pub mod std_mod { } } - pub fn copy_tc_to_buf(&mut self, addr: StoreAddr) -> Result<(), PusPacketHandlingError> { + pub fn copy_tc_to_buf(&mut self, addr: StoreAddr) -> Result<(), PusTcFromMemError> { // Keep locked section as short as possible. - let mut tc_pool = self - .shared_tc_store - .write() - .map_err(|_| PusPacketHandlingError::EcssTmtc(EcssTmtcError::StoreLock))?; - let tc_size = tc_pool - .len_of_data(&addr) - .map_err(|e| PusPacketHandlingError::EcssTmtc(EcssTmtcError::Store(e)))?; + let mut tc_pool = self.shared_tc_store.write().map_err(|_| { + PusTcFromMemError::EcssTmtc(EcssTmtcError::Store(StoreError::LockError)) + })?; + let tc_size = tc_pool.len_of_data(&addr).map_err(EcssTmtcError::Store)?; if tc_size > self.pus_buf.len() { - return Err(PusPacketHandlingError::PusPacketTooLarge(tc_size)); + return Err( + EcssTmtcError::ByteConversion(ByteConversionError::ToSliceTooSmall { + found: self.pus_buf.len(), + expected: tc_size, + }) + .into(), + ); } let tc_guard = tc_pool.read_with_guard(addr); // TODO: Proper error handling. @@ -823,18 +1097,13 @@ pub mod std_mod { } impl EcssTcInMemConverter for EcssTcInSharedStoreConverter { - fn cache_ecss_tc_in_memory( - &mut self, - tc_in_memory: &TcInMemory, - ) -> Result<(), PusPacketHandlingError> { + fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> { match tc_in_memory { super::TcInMemory::StoreAddr(addr) => { self.copy_tc_to_buf(*addr)?; } super::TcInMemory::Vec(_) => { - return Err(PusPacketHandlingError::InvalidTcInMemoryFormat( - tc_in_memory.clone(), - )); + return Err(PusTcFromMemError::InvalidFormat(tc_in_memory.clone())); } }; Ok(()) @@ -850,30 +1119,10 @@ pub mod std_mod { TmSender: EcssTmSenderCore, VerificationReporter: VerificationReportingProvider, > { + pub id: ComponentId, pub tc_receiver: TcReceiver, pub tm_sender: TmSender, - pub tm_apid: u16, - pub verification_handler: VerificationReporter, - } - #[cfg(feature = "std")] - pub fn get_current_cds_short_timestamp( - partial_error: &mut Option, - ) -> [u8; 7] { - let mut time_stamp: [u8; 7] = [0; 7]; - let time_provider = - TimeProvider::from_now_with_u16_days().map_err(PartialPusHandlingError::Time); - if let Ok(time_provider) = time_provider { - // Can't fail, we have a buffer with the exact required size. - time_provider.write_to_bytes(&mut time_stamp).unwrap(); - } else { - *partial_error = Some(time_provider.unwrap_err()); - } - time_stamp - } - #[cfg(feature = "std")] - pub fn get_current_timestamp_ignore_error() -> [u8; 7] { - let mut dummy = None; - get_current_cds_short_timestamp(&mut dummy) + pub verif_reporter: VerificationReporter, } /// This is a high-level PUS packet handler helper. @@ -903,23 +1152,31 @@ pub mod std_mod { > PusServiceHelper { pub fn new( + id: ComponentId, tc_receiver: TcReceiver, tm_sender: TmSender, - tm_apid: u16, verification_handler: VerificationReporter, tc_in_mem_converter: TcInMemConverter, ) -> Self { Self { common: PusServiceBase { + id, tc_receiver, tm_sender, - tm_apid, - verification_handler, + verif_reporter: verification_handler, }, tc_in_mem_converter, } } + pub fn id(&self) -> ComponentId { + self.common.id + } + + pub fn tm_sender(&self) -> &TmSender { + &self.common.tm_sender + } + /// This function can be used to poll the internal [EcssTcReceiverCore] object for the next /// telecommand packet. It will return `Ok(None)` if there are not packets available. /// In any other case, it will perform the acceptance of the ECSS TC packet using the @@ -945,55 +1202,101 @@ pub mod std_mod { })) } Err(e) => match e { - TryRecvTmtcError::Tmtc(e) => Err(PusPacketHandlingError::EcssTmtc(e)), + TryRecvTmtcError::Tmtc(e) => Err(PusPacketHandlingError::TcPolling(e)), TryRecvTmtcError::Empty => Ok(None), }, } } + + pub fn verif_reporter(&self) -> &VerificationReporter { + &self.common.verif_reporter + } + pub fn verif_reporter_mut(&mut self) -> &mut VerificationReporter { + &mut self.common.verif_reporter + } + + pub fn tc_in_mem_converter(&self) -> &TcInMemConverter { + &self.tc_in_mem_converter + } + + pub fn tc_in_mem_converter_mut(&mut self) -> &mut TcInMemConverter { + &mut self.tc_in_mem_converter + } } - pub type PusServiceHelperDynWithMpsc = PusServiceHelper< - MpscTcReceiver, - TmAsVecSenderWithMpsc, - TcInMemConverter, - VerificationReporter, - >; + pub type PusServiceHelperDynWithMpsc = + PusServiceHelper; pub type PusServiceHelperDynWithBoundedMpsc = PusServiceHelper< MpscTcReceiver, - TmAsVecSenderWithBoundedMpsc, + MpscTmAsVecSenderBounded, TcInMemConverter, VerificationReporter, >; pub type PusServiceHelperStaticWithMpsc = PusServiceHelper< MpscTcReceiver, - TmInSharedPoolSenderWithMpsc, + MpscTmInSharedPoolSender, TcInMemConverter, VerificationReporter, >; pub type PusServiceHelperStaticWithBoundedMpsc = PusServiceHelper< MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, + MpscTmInSharedPoolSenderBounded, TcInMemConverter, VerificationReporter, >; } -pub(crate) fn source_buffer_large_enough(cap: usize, len: usize) -> Result<(), EcssTmtcError> { +pub(crate) fn source_buffer_large_enough( + cap: usize, + len: usize, +) -> Result<(), ByteConversionError> { if len > cap { - return Err( - PusError::ByteConversion(ByteConversionError::ToSliceTooSmall { - found: cap, - expected: len, - }) - .into(), - ); + return Err(ByteConversionError::ToSliceTooSmall { + found: cap, + expected: len, + }); } Ok(()) } +#[cfg(any(feature = "test_util", test))] +pub mod test_util { + use crate::request::UniqueApidTargetId; + use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader}; + + use super::{ + verification::{self, TcStateAccepted, VerificationToken}, + PusPacketHandlerResult, PusPacketHandlingError, + }; + + pub const TEST_APID: u16 = 0x101; + pub const TEST_UNIQUE_ID_0: u32 = 0x05; + pub const TEST_UNIQUE_ID_1: u32 = 0x06; + pub const TEST_COMPONENT_ID_0: UniqueApidTargetId = + UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0); + pub const TEST_COMPONENT_ID_1: UniqueApidTargetId = + UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_1); + + pub trait PusTestHarness { + fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken; + fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator); + fn read_next_tm(&mut self) -> PusTmReader<'_>; + fn check_no_tm_available(&self) -> bool; + fn check_next_verification_tm( + &self, + subservice: u8, + expected_request_id: verification::RequestId, + ); + } + + pub trait SimplePusPacketHandler { + fn handle_one_tc(&mut self) -> Result; + } +} + #[cfg(test)] pub mod tests { use core::cell::RefCell; @@ -1008,57 +1311,53 @@ pub mod tests { use spacepackets::ecss::{PusPacket, WritablePusPacket}; use spacepackets::CcsdsPacket; - use crate::pool::{ - PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig, StoreAddr, - }; - use crate::pus::verification::RequestId; + use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig}; + use crate::pus::verification::{RequestId, VerificationReporter}; use crate::tmtc::tm_helper::SharedTmPool; - use crate::TargetId; + use crate::ComponentId; - use super::verification::std_mod::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, - }; - use super::verification::tests::{SharedVerificationMap, TestVerificationReporter}; + use super::test_util::{TEST_APID, TEST_COMPONENT_ID_0}; + + use super::verification::test_util::TestVerificationReporter; use super::verification::{ - TcStateAccepted, VerificationReporterCfg, VerificationReporterWithSender, - VerificationReportingProvider, VerificationToken, + TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken, }; - use super::{ - EcssTcAndToken, EcssTcInSharedStoreConverter, EcssTcInVecConverter, GenericRoutingError, - MpscTcReceiver, PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, - PusServiceHelper, TcInMemory, TmAsVecSenderWithId, TmAsVecSenderWithMpsc, - TmInSharedPoolSenderWithBoundedMpsc, TmInSharedPoolSenderWithId, - }; - - pub const TEST_APID: u16 = 0x101; + use super::*; #[derive(Debug, Eq, PartialEq, Clone)] pub(crate) struct CommonTmInfo { pub subservice: u8, pub apid: u16, + pub seq_count: u16, pub msg_counter: u16, pub dest_id: u16, pub time_stamp: [u8; 7], } - pub trait PusTestHarness { - fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken; - fn read_next_tm(&mut self) -> PusTmReader<'_>; - fn check_no_tm_available(&self) -> bool; - fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId); - } - - pub trait SimplePusPacketHandler { - fn handle_one_tc(&mut self) -> Result; - } - impl CommonTmInfo { + pub fn new_zero_seq_count( + subservice: u8, + apid: u16, + dest_id: u16, + time_stamp: [u8; 7], + ) -> Self { + Self { + subservice, + apid, + seq_count: 0, + msg_counter: 0, + dest_id, + time_stamp, + } + } + pub fn new_from_tm(tm: &PusTmCreator) -> Self { let mut time_stamp = [0; 7]; time_stamp.clone_from_slice(&tm.timestamp()[0..7]); Self { subservice: PusPacket::subservice(tm), apid: tm.apid(), + seq_count: tm.seq_count(), msg_counter: tm.msg_counter(), dest_id: tm.dest_id(), time_stamp, @@ -1068,20 +1367,19 @@ pub mod tests { /// Common fields for a PUS service test harness. pub struct PusServiceHandlerWithSharedStoreCommon { - pus_buf: [u8; 2048], + pus_buf: RefCell<[u8; 2048]>, tm_buf: [u8; 2048], tc_pool: SharedStaticMemoryPool, tm_pool: SharedTmPool, tc_sender: mpsc::SyncSender, - tm_receiver: mpsc::Receiver, - verification_handler: VerificationReporterWithSharedPoolMpscBoundedSender, + tm_receiver: mpsc::Receiver, } pub type PusServiceHelperStatic = PusServiceHelper< MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, + MpscTmInSharedPoolSenderBounded, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, + VerificationReporter, >; impl PusServiceHandlerWithSharedStoreCommon { @@ -1089,7 +1387,7 @@ pub mod tests { /// [PusServiceHandler] which might be required for a specific PUS service handler. /// /// The PUS service handler is instantiated with a [EcssTcInStoreConverter]. - pub fn new() -> (Self, PusServiceHelperStatic) { + pub fn new(id: ComponentId) -> (Self, PusServiceHelperStatic) { let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false); let tc_pool = StaticMemoryPool::new(pool_cfg.clone()); let tm_pool = StaticMemoryPool::new(pool_cfg); @@ -1098,62 +1396,48 @@ pub mod tests { let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::sync_channel(10); let (tm_tx, tm_rx) = mpsc::sync_channel(10); - let verif_sender = TmInSharedPoolSenderWithBoundedMpsc::new( - 0, - "verif_sender", - shared_tm_pool.clone(), - tm_tx.clone(), - ); let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); let verification_handler = - VerificationReporterWithSharedPoolMpscBoundedSender::new(&verif_cfg, verif_sender); - let test_srv_tm_sender = - TmInSharedPoolSenderWithId::new(0, "TEST_SENDER", shared_tm_pool.clone(), tm_tx); - let test_srv_tc_receiver = MpscTcReceiver::new(0, "TEST_RECEIVER", test_srv_tc_rx); + VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg); + let test_srv_tm_sender = TmInSharedPoolSender::new(shared_tm_pool.clone(), tm_tx); let in_store_converter = EcssTcInSharedStoreConverter::new(shared_tc_pool.clone(), 2048); ( Self { - pus_buf: [0; 2048], + pus_buf: RefCell::new([0; 2048]), tm_buf: [0; 2048], tc_pool: shared_tc_pool, tm_pool: shared_tm_pool, tc_sender: test_srv_tc_tx, tm_receiver: tm_rx, - verification_handler: verification_handler.clone(), }, PusServiceHelper::new( - test_srv_tc_receiver, + id, + test_srv_tc_rx, test_srv_tm_sender, - TEST_APID, verification_handler, in_store_converter, ), ) } - pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken { - let token = self.verification_handler.add_tc(tc); - let token = self - .verification_handler - .acceptance_success(token, &[0; 7]) - .unwrap(); - let tc_size = tc.write_to_bytes(&mut self.pus_buf).unwrap(); + pub fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator) { + let mut mut_buf = self.pus_buf.borrow_mut(); + let tc_size = tc.write_to_bytes(mut_buf.as_mut_slice()).unwrap(); let mut tc_pool = self.tc_pool.write().unwrap(); - let addr = tc_pool.add(&self.pus_buf[..tc_size]).unwrap(); + let addr = tc_pool.add(&mut_buf[..tc_size]).unwrap(); drop(tc_pool); // Send accepted TC to test service handler. self.tc_sender - .send(EcssTcAndToken::new(addr, token)) + .send(EcssTcAndToken::new(addr, *token)) .expect("sending tc failed"); - token } pub fn read_next_tm(&mut self) -> PusTmReader<'_> { let next_msg = self.tm_receiver.try_recv(); assert!(next_msg.is_ok()); - let tm_addr = next_msg.unwrap(); + let tm_in_pool = next_msg.unwrap(); let tm_pool = self.tm_pool.0.read().unwrap(); - let tm_raw = tm_pool.read_as_vec(&tm_addr).unwrap(); + let tm_raw = tm_pool.read_as_vec(&tm_in_pool.store_addr).unwrap(); self.tm_buf[0..tm_raw.len()].copy_from_slice(&tm_raw); PusTmReader::new(&self.tm_buf, 7).unwrap().0 } @@ -1169,9 +1453,9 @@ pub mod tests { pub fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId) { let next_msg = self.tm_receiver.try_recv(); assert!(next_msg.is_ok()); - let tm_addr = next_msg.unwrap(); + let tm_in_pool = next_msg.unwrap(); let tm_pool = self.tm_pool.0.read().unwrap(); - let tm_raw = tm_pool.read_as_vec(&tm_addr).unwrap(); + let tm_raw = tm_pool.read_as_vec(&tm_in_pool.store_addr).unwrap(); let tm = PusTmReader::new(&tm_raw, 7).unwrap().0; assert_eq!(PusPacket::service(&tm), 1); assert_eq!(PusPacket::subservice(&tm), subservice); @@ -1182,43 +1466,39 @@ pub mod tests { } } - pub struct PusServiceHandlerWithVecCommon { - current_tm: Option>, + pub struct PusServiceHandlerWithVecCommon { + current_tm: Option>, tc_sender: mpsc::Sender, - tm_receiver: mpsc::Receiver>, - pub verification_handler: VerificationReporter, + tm_receiver: mpsc::Receiver, } pub type PusServiceHelperDynamic = PusServiceHelper< MpscTcReceiver, - TmAsVecSenderWithMpsc, + MpscTmAsVecSender, EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, + VerificationReporter, >; - impl PusServiceHandlerWithVecCommon { - pub fn new_with_standard_verif_reporter() -> (Self, PusServiceHelperDynamic) { + impl PusServiceHandlerWithVecCommon { + pub fn new_with_standard_verif_reporter( + id: ComponentId, + ) -> (Self, PusServiceHelperDynamic) { let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel(); let (tm_tx, tm_rx) = mpsc::channel(); - let verif_sender = TmAsVecSenderWithId::new(0, "verififcatio-sender", tm_tx.clone()); let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); let verification_handler = - VerificationReporterWithSender::new(&verif_cfg, verif_sender); - - let test_srv_tm_sender = TmAsVecSenderWithId::new(0, "test-sender", tm_tx); - let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx); + VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg); let in_store_converter = EcssTcInVecConverter::default(); ( Self { current_tm: None, tc_sender: test_srv_tc_tx, tm_receiver: tm_rx, - verification_handler: verification_handler.clone(), }, PusServiceHelper::new( - test_srv_tc_receiver, - test_srv_tm_sender, - TEST_APID, + id, + test_srv_tc_rx, + tm_tx, verification_handler, in_store_converter, ), @@ -1226,12 +1506,14 @@ pub mod tests { } } - impl PusServiceHandlerWithVecCommon { - pub fn new_with_test_verif_sender() -> ( + impl PusServiceHandlerWithVecCommon { + pub fn new_with_test_verif_sender( + id: ComponentId, + ) -> ( Self, PusServiceHelper< MpscTcReceiver, - TmAsVecSenderWithMpsc, + MpscTmAsVecSender, EcssTcInVecConverter, TestVerificationReporter, >, @@ -1239,22 +1521,19 @@ pub mod tests { let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel(); let (tm_tx, tm_rx) = mpsc::channel(); - let test_srv_tm_sender = TmAsVecSenderWithId::new(0, "test-sender", tm_tx); - let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx); let in_store_converter = EcssTcInVecConverter::default(); - let shared_verif_map = SharedVerificationMap::default(); - let verification_handler = TestVerificationReporter::new(shared_verif_map); + let verification_handler = TestVerificationReporter::new(id); ( Self { current_tm: None, tc_sender: test_srv_tc_tx, tm_receiver: tm_rx, - verification_handler: verification_handler.clone(), + //verification_handler: verification_handler.clone(), }, PusServiceHelper::new( - test_srv_tc_receiver, - test_srv_tm_sender, - TEST_APID, + id, + test_srv_tc_rx, + tm_tx, verification_handler, in_store_converter, ), @@ -1262,29 +1541,21 @@ pub mod tests { } } - impl - PusServiceHandlerWithVecCommon - { - pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken { - let token = self.verification_handler.add_tc(tc); - let token = self - .verification_handler - .acceptance_success(token, &[0; 7]) - .unwrap(); + impl PusServiceHandlerWithVecCommon { + pub fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator) { // Send accepted TC to test service handler. self.tc_sender .send(EcssTcAndToken::new( TcInMemory::Vec(tc.to_vec().expect("pus tc conversion to vec failed")), - token, + *token, )) .expect("sending tc failed"); - token } pub fn read_next_tm(&mut self) -> PusTmReader<'_> { let next_msg = self.tm_receiver.try_recv(); assert!(next_msg.is_ok()); - self.current_tm = Some(next_msg.unwrap()); + self.current_tm = Some(next_msg.unwrap().packet); PusTmReader::new(self.current_tm.as_ref().unwrap(), 7) .unwrap() .0 @@ -1302,7 +1573,7 @@ pub mod tests { let next_msg = self.tm_receiver.try_recv(); assert!(next_msg.is_ok()); let next_msg = next_msg.unwrap(); - let tm = PusTmReader::new(next_msg.as_slice(), 7).unwrap().0; + let tm = PusTmReader::new(next_msg.packet.as_slice(), 7).unwrap().0; assert_eq!(PusPacket::service(&tm), 1); assert_eq!(PusPacket::subservice(&tm), subservice); assert_eq!(tm.apid(), TEST_APID); @@ -1322,7 +1593,9 @@ pub mod tests { impl TestConverter { pub fn check_service(&self, tc: &PusTcReader) -> Result<(), PusPacketHandlingError> { if tc.service() != SERVICE { - return Err(PusPacketHandlingError::WrongService(tc.service())); + return Err(PusPacketHandlingError::RequestConversion( + GenericConversionError::WrongService(tc.service()), + )); } Ok(()) } @@ -1340,44 +1613,9 @@ pub mod tests { } } - #[derive(Default)] - pub struct TestRoutingErrorHandler { - pub routing_errors: RefCell>, - } - - impl PusRoutingErrorHandler for TestRoutingErrorHandler { - type Error = GenericRoutingError; - - fn handle_error( - &self, - target_id: TargetId, - _token: VerificationToken, - _tc: &PusTcReader, - error: Self::Error, - _time_stamp: &[u8], - _verif_reporter: &impl VerificationReportingProvider, - ) { - self.routing_errors - .borrow_mut() - .push_back((target_id, error)); - } - } - - impl TestRoutingErrorHandler { - pub fn is_empty(&self) -> bool { - self.routing_errors.borrow().is_empty() - } - - pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError) { - if self.routing_errors.borrow().is_empty() { - panic!("no routing request available"); - } - self.routing_errors.borrow_mut().pop_front().unwrap() - } - } - pub struct TestRouter { - pub routing_requests: RefCell>, + pub routing_requests: RefCell>, + pub routing_errors: RefCell>, pub injected_routing_failure: RefCell>, } @@ -1385,6 +1623,7 @@ pub mod tests { fn default() -> Self { Self { routing_requests: Default::default(), + routing_errors: Default::default(), injected_routing_failure: Default::default(), } } @@ -1398,6 +1637,31 @@ pub mod tests { Ok(()) } + pub fn handle_error( + &self, + target_id: ComponentId, + _token: VerificationToken, + _tc: &PusTcReader, + error: GenericRoutingError, + _time_stamp: &[u8], + _verif_reporter: &impl VerificationReportingProvider, + ) { + self.routing_errors + .borrow_mut() + .push_back((target_id, error)); + } + + pub fn no_routing_errors(&self) -> bool { + self.routing_errors.borrow().is_empty() + } + + pub fn retrieve_next_routing_error(&mut self) -> (ComponentId, GenericRoutingError) { + if self.routing_errors.borrow().is_empty() { + panic!("no routing request available"); + } + self.routing_errors.borrow_mut().pop_front().unwrap() + } + pub fn inject_routing_error(&mut self, error: GenericRoutingError) { *self.injected_routing_failure.borrow_mut() = Some(error); } @@ -1406,7 +1670,7 @@ pub mod tests { self.routing_requests.borrow().is_empty() } - pub fn retrieve_next_request(&mut self) -> (TargetId, REQUEST) { + pub fn retrieve_next_request(&mut self) -> (ComponentId, REQUEST) { if self.routing_requests.borrow().is_empty() { panic!("no routing request available"); } diff --git a/satrs/src/pus/mode.rs b/satrs/src/pus/mode.rs index 1ab46ef..abb6b99 100644 --- a/satrs/src/pus/mode.rs +++ b/satrs/src/pus/mode.rs @@ -2,6 +2,16 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "alloc")] +#[allow(unused_imports)] +pub use alloc_mod::*; + +#[cfg(feature = "std")] +#[allow(unused_imports)] +pub use std_mod::*; + +pub const MODE_SERVICE_ID: u8 = 200; + #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(u8)] @@ -14,3 +24,134 @@ pub enum Subservice { TmCantReachMode = 7, TmWrongModeReply = 8, } + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +pub mod alloc_mod {} + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +pub mod std_mod {} + +#[cfg(test)] +mod tests { + + use std::sync::mpsc; + + use crate::{ + mode::{ + ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender, + ModeRequestorAndHandlerMpsc, ModeRequestorMpsc, + }, + request::{GenericMessage, MessageMetadata}, + }; + + const TEST_COMPONENT_ID_0: u64 = 5; + const TEST_COMPONENT_ID_1: u64 = 6; + const TEST_COMPONENT_ID_2: u64 = 7; + + #[test] + fn test_simple_mode_requestor() { + let (reply_sender, reply_receiver) = mpsc::channel(); + let (request_sender, request_receiver) = mpsc::channel(); + let mut mode_requestor = ModeRequestorMpsc::new(TEST_COMPONENT_ID_0, reply_receiver); + mode_requestor.add_message_target(TEST_COMPONENT_ID_1, request_sender); + + // Send a request and verify it arrives at the receiver. + let request_id = 2; + let sent_request = ModeRequest::ReadMode; + mode_requestor + .send_mode_request(request_id, TEST_COMPONENT_ID_1, sent_request) + .expect("send failed"); + let request = request_receiver.recv().expect("recv failed"); + assert_eq!(request.request_id(), 2); + assert_eq!(request.sender_id(), TEST_COMPONENT_ID_0); + assert_eq!(request.message, sent_request); + + // Send a reply and verify it arrives at the requestor. + let mode_reply = ModeReply::ModeReply(ModeAndSubmode::new(1, 5)); + reply_sender + .send(GenericMessage::new( + MessageMetadata::new(request_id, TEST_COMPONENT_ID_1), + mode_reply, + )) + .expect("send failed"); + let reply = mode_requestor.try_recv_mode_reply().expect("recv failed"); + assert!(reply.is_some()); + let reply = reply.unwrap(); + assert_eq!(reply.sender_id(), TEST_COMPONENT_ID_1); + assert_eq!(reply.request_id(), 2); + assert_eq!(reply.message, mode_reply); + } + + #[test] + fn test_mode_requestor_and_request_handler_request_sending() { + let (_reply_sender_to_connector, reply_receiver_of_connector) = mpsc::channel(); + let (_request_sender_to_connector, request_receiver_of_connector) = mpsc::channel(); + + let (request_sender_to_channel_1, request_receiver_channel_1) = mpsc::channel(); + //let (reply_sender_to_channel_2, reply_receiver_channel_2) = mpsc::channel(); + let mut mode_connector = ModeRequestorAndHandlerMpsc::new( + TEST_COMPONENT_ID_0, + request_receiver_of_connector, + reply_receiver_of_connector, + ); + assert_eq!( + ModeRequestSender::local_channel_id(&mode_connector), + TEST_COMPONENT_ID_0 + ); + assert_eq!( + ModeReplySender::local_channel_id(&mode_connector), + TEST_COMPONENT_ID_0 + ); + assert_eq!( + mode_connector.local_channel_id_generic(), + TEST_COMPONENT_ID_0 + ); + + mode_connector.add_request_target(TEST_COMPONENT_ID_1, request_sender_to_channel_1); + + // Send a request and verify it arrives at the receiver. + let request_id = 2; + let sent_request = ModeRequest::ReadMode; + mode_connector + .send_mode_request(request_id, TEST_COMPONENT_ID_1, sent_request) + .expect("send failed"); + + let request = request_receiver_channel_1.recv().expect("recv failed"); + assert_eq!(request.request_id(), 2); + assert_eq!(request.sender_id(), TEST_COMPONENT_ID_0); + assert_eq!(request.message, ModeRequest::ReadMode); + } + + #[test] + fn test_mode_requestor_and_request_handler_reply_sending() { + let (_reply_sender_to_connector, reply_receiver_of_connector) = mpsc::channel(); + let (_request_sender_to_connector, request_receiver_of_connector) = mpsc::channel(); + + let (reply_sender_to_channel_2, reply_receiver_channel_2) = mpsc::channel(); + let mut mode_connector = ModeRequestorAndHandlerMpsc::new( + TEST_COMPONENT_ID_0, + request_receiver_of_connector, + reply_receiver_of_connector, + ); + mode_connector.add_reply_target(TEST_COMPONENT_ID_2, reply_sender_to_channel_2); + + // Send a reply and verify it arrives at the receiver. + let request_id = 2; + let sent_reply = ModeReply::ModeReply(ModeAndSubmode::new(3, 5)); + mode_connector + .send_mode_reply( + MessageMetadata::new(request_id, TEST_COMPONENT_ID_2), + sent_reply, + ) + .expect("send failed"); + let reply = reply_receiver_channel_2.recv().expect("recv failed"); + assert_eq!(reply.request_id(), 2); + assert_eq!(reply.sender_id(), TEST_COMPONENT_ID_0); + assert_eq!(reply.message, sent_reply); + } + + #[test] + fn test_mode_reply_handler() {} +} diff --git a/satrs/src/pus/scheduler.rs b/satrs/src/pus/scheduler.rs index cebcf2c..b3b9ef2 100644 --- a/satrs/src/pus/scheduler.rs +++ b/satrs/src/pus/scheduler.rs @@ -9,9 +9,7 @@ use serde::{Deserialize, Serialize}; use spacepackets::ecss::scheduling::TimeWindowType; use spacepackets::ecss::tc::{GenericPusTcSecondaryHeader, IsPusTelecommand, PusTcReader}; use spacepackets::ecss::{PusError, PusPacket, WritablePusPacket}; -use spacepackets::time::{ - CcsdsTimeProvider, TimeReader, TimeWriter, TimestampError, UnixTimestamp, -}; +use spacepackets::time::{CcsdsTimeProvider, TimeReader, TimeWriter, TimestampError, UnixTime}; use spacepackets::{ByteConversionError, CcsdsPacket}; #[cfg(feature = "std")] use std::error::Error; @@ -147,9 +145,9 @@ pub enum ScheduleError { /// The first parameter is the current time, the second one the time margin, and the third one /// the release time. ReleaseTimeInTimeMargin { - current_time: UnixTimestamp, + current_time: UnixTime, time_margin: Duration, - release_time: UnixTimestamp, + release_time: UnixTime, }, /// Nested time-tagged commands are not allowed. NestedScheduledTc, @@ -256,7 +254,7 @@ pub trait PusSchedulerProvider { /// inside the telecommand packet pool. fn insert_unwrapped_and_stored_tc( &mut self, - time_stamp: UnixTimestamp, + time_stamp: UnixTime, info: TcInfo, ) -> Result<(), ScheduleError>; @@ -280,7 +278,7 @@ pub trait PusSchedulerProvider { } let user_data = pus_tc.user_data(); let stamp: Self::TimeProvider = TimeReader::from_bytes(user_data)?; - let unix_stamp = stamp.unix_stamp(); + let unix_stamp = stamp.unix_time(); let stamp_len = stamp.len_as_bytes(); self.insert_unwrapped_tc(unix_stamp, &user_data[stamp_len..], pool) } @@ -289,7 +287,7 @@ pub trait PusSchedulerProvider { /// needs to be stored inside the telecommand pool. fn insert_unwrapped_tc( &mut self, - time_stamp: UnixTimestamp, + time_stamp: UnixTime, tc: &[u8], pool: &mut (impl PoolProvider + ?Sized), ) -> Result { @@ -347,7 +345,10 @@ pub mod alloc_mod { }, vec::Vec, }; - use spacepackets::time::cds::{self, DaysLen24Bits}; + use spacepackets::time::{ + cds::{self, DaysLen24Bits}, + UnixTime, + }; use crate::pool::StoreAddr; @@ -380,7 +381,7 @@ pub mod alloc_mod { /// a [crate::pool::PoolProvider] API. This data structure just tracks the store /// addresses and their release times and offers a convenient API to insert and release /// telecommands and perform other functionality specified by the ECSS standard in section 6.11. - /// The time is tracked as a [spacepackets::time::UnixTimestamp] but the only requirement to + /// The time is tracked as a [spacepackets::time::UnixTime] but the only requirement to /// the timekeeping of the user is that it is convertible to that timestamp. /// /// The standard also specifies that the PUS scheduler can be enabled and disabled. @@ -397,8 +398,9 @@ pub mod alloc_mod { /// Currently, sub-schedules and groups are not supported. #[derive(Debug)] pub struct PusScheduler { - tc_map: BTreeMap>, - pub(crate) current_time: UnixTimestamp, + // TODO: Use MonotonicTime from tai-time crate instead of UnixTime and cache leap seconds. + tc_map: BTreeMap>, + pub(crate) current_time: UnixTime, time_margin: Duration, enabled: bool, } @@ -413,7 +415,7 @@ pub mod alloc_mod { /// added to the current time, it will not be inserted into the schedule. /// * `tc_buf_size` - Buffer for temporary storage of telecommand packets. This buffer /// should be large enough to accomodate the largest expected TC packets. - pub fn new(init_current_time: UnixTimestamp, time_margin: Duration) -> Self { + pub fn new(init_current_time: UnixTime, time_margin: Duration) -> Self { PusScheduler { tc_map: Default::default(), current_time: init_current_time, @@ -426,7 +428,7 @@ pub mod alloc_mod { #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub fn new_with_current_init_time(time_margin: Duration) -> Result { - Ok(Self::new(UnixTimestamp::from_now()?, time_margin)) + Ok(Self::new(UnixTime::now()?, time_margin)) } pub fn num_scheduled_telecommands(&self) -> u64 { @@ -437,11 +439,11 @@ pub mod alloc_mod { num_entries } - pub fn update_time(&mut self, current_time: UnixTimestamp) { + pub fn update_time(&mut self, current_time: UnixTime) { self.current_time = current_time; } - pub fn current_time(&self) -> &UnixTimestamp { + pub fn current_time(&self) -> &UnixTime { &self.current_time } @@ -449,7 +451,7 @@ pub mod alloc_mod { /// inside the telecommand packet pool. pub fn insert_unwrapped_and_stored_tc( &mut self, - time_stamp: UnixTimestamp, + time_stamp: UnixTime, info: TcInfo, ) -> Result<(), ScheduleError> { if time_stamp < self.current_time + self.time_margin { @@ -474,7 +476,7 @@ pub mod alloc_mod { /// needs to be stored inside the telecommand pool. pub fn insert_unwrapped_tc( &mut self, - time_stamp: UnixTimestamp, + time_stamp: UnixTime, tc: &[u8], pool: &mut (impl PoolProvider + ?Sized), ) -> Result { @@ -501,7 +503,7 @@ pub mod alloc_mod { pus_tc: &(impl IsPusTelecommand + PusPacket + GenericPusTcSecondaryHeader), pool: &mut (impl PoolProvider + ?Sized), ) -> Result { - self.insert_wrapped_tc::(pus_tc, pool) + self.insert_wrapped_tc::(pus_tc, pool) } /// Insert a telecommand based on the fully wrapped time-tagged telecommand using a CDS @@ -511,7 +513,7 @@ pub mod alloc_mod { pus_tc: &(impl IsPusTelecommand + PusPacket + GenericPusTcSecondaryHeader), pool: &mut (impl PoolProvider + ?Sized), ) -> Result { - self.insert_wrapped_tc::>(pus_tc, pool) + self.insert_wrapped_tc::>(pus_tc, pool) } /// This function uses [Self::retrieve_by_time_filter] to extract all scheduled commands inside @@ -557,14 +559,13 @@ pub mod alloc_mod { &mut self, pool: &mut (impl PoolProvider + ?Sized), ) -> Result { - self.delete_by_time_filter(TimeWindow::::new_select_all(), pool) + self.delete_by_time_filter(TimeWindow::::new_select_all(), pool) } /// Retrieve a range over all scheduled commands. pub fn retrieve_all( &mut self, - ) -> alloc::collections::btree_map::Range<'_, UnixTimestamp, alloc::vec::Vec> - { + ) -> alloc::collections::btree_map::Range<'_, UnixTime, alloc::vec::Vec> { self.tc_map.range(..) } @@ -575,23 +576,23 @@ pub mod alloc_mod { pub fn retrieve_by_time_filter( &mut self, time_window: TimeWindow, - ) -> Range<'_, UnixTimestamp, alloc::vec::Vec> { + ) -> Range<'_, UnixTime, alloc::vec::Vec> { match time_window.time_window_type() { TimeWindowType::SelectAll => self.tc_map.range(..), TimeWindowType::TimeTagToTimeTag => { // This should be guaranteed to be valid by library API, so unwrap is okay - let start_time = time_window.start_time().unwrap().unix_stamp(); - let end_time = time_window.end_time().unwrap().unix_stamp(); + let start_time = time_window.start_time().unwrap().unix_time(); + let end_time = time_window.end_time().unwrap().unix_time(); self.tc_map.range(start_time..=end_time) } TimeWindowType::FromTimeTag => { // This should be guaranteed to be valid by library API, so unwrap is okay - let start_time = time_window.start_time().unwrap().unix_stamp(); + let start_time = time_window.start_time().unwrap().unix_time(); self.tc_map.range(start_time..) } TimeWindowType::ToTimeTag => { // This should be guaranteed to be valid by library API, so unwrap is okay - let end_time = time_window.end_time().unwrap().unix_stamp(); + let end_time = time_window.end_time().unwrap().unix_time(); self.tc_map.range(..=end_time) } } @@ -671,7 +672,7 @@ pub mod alloc_mod { #[cfg(feature = "std")] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError> { - self.current_time = UnixTimestamp::from_now()?; + self.current_time = UnixTime::now()?; Ok(()) } @@ -781,13 +782,13 @@ pub mod alloc_mod { } /// Retrieve all telecommands which should be release based on the current time. - pub fn telecommands_to_release(&self) -> Range<'_, UnixTimestamp, Vec> { + pub fn telecommands_to_release(&self) -> Range<'_, UnixTime, Vec> { self.tc_map.range(..=self.current_time) } } impl PusSchedulerProvider for PusScheduler { - type TimeProvider = cds::TimeProvider; + type TimeProvider = cds::CdsTime; /// This will disable the scheduler and clear the schedule as specified in 6.11.4.4. /// Be careful with this command as it will delete all the commands in the schedule. @@ -826,7 +827,7 @@ pub mod alloc_mod { fn insert_unwrapped_and_stored_tc( &mut self, - time_stamp: UnixTimestamp, + time_stamp: UnixTime, info: TcInfo, ) -> Result<(), ScheduleError> { if time_stamp < self.current_time + self.time_margin { @@ -858,77 +859,72 @@ mod tests { use alloc::collections::btree_map::Range; use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader}; use spacepackets::ecss::WritablePusPacket; - use spacepackets::time::{cds, TimeWriter, UnixTimestamp}; + use spacepackets::time::{cds, TimeWriter, UnixTime}; use spacepackets::{PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader}; use std::time::Duration; use std::vec::Vec; #[allow(unused_imports)] use std::{println, vec}; - fn pus_tc_base(timestamp: UnixTimestamp, buf: &mut [u8]) -> (SpHeader, usize) { - let cds_time = cds::TimeProvider::from_unix_secs_with_u16_days(×tamp).unwrap(); + fn pus_tc_base(timestamp: UnixTime, buf: &mut [u8]) -> (SpHeader, usize) { + let cds_time = + cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent) + .unwrap(); let len_time_stamp = cds_time.write_to_bytes(buf).unwrap(); - let len_packet = base_ping_tc_simple_ctor(0, None) + let len_packet = base_ping_tc_simple_ctor(0, &[]) .write_to_bytes(&mut buf[len_time_stamp..]) .unwrap(); ( - SpHeader::tc_unseg(0x02, 0x34, len_packet as u16).unwrap(), + SpHeader::new_for_unseg_tc(0x02, 0x34, len_packet as u16), len_packet + len_time_stamp, ) } - fn scheduled_tc(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator { - let (mut sph, len_app_data) = pus_tc_base(timestamp, buf); - PusTcCreator::new_simple(&mut sph, 11, 4, Some(&buf[..len_app_data]), true) + fn scheduled_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator { + let (sph, len_app_data) = pus_tc_base(timestamp, buf); + PusTcCreator::new_simple(sph, 11, 4, &buf[..len_app_data], true) } - fn wrong_tc_service(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator { - let (mut sph, len_app_data) = pus_tc_base(timestamp, buf); - PusTcCreator::new_simple(&mut sph, 12, 4, Some(&buf[..len_app_data]), true) + fn wrong_tc_service(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator { + let (sph, len_app_data) = pus_tc_base(timestamp, buf); + PusTcCreator::new_simple(sph, 12, 4, &buf[..len_app_data], true) } - fn wrong_tc_subservice(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator { - let (mut sph, len_app_data) = pus_tc_base(timestamp, buf); - PusTcCreator::new_simple(&mut sph, 11, 5, Some(&buf[..len_app_data]), true) + fn wrong_tc_subservice(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator { + let (sph, len_app_data) = pus_tc_base(timestamp, buf); + PusTcCreator::new_simple(sph, 11, 5, &buf[..len_app_data], true) } - fn double_wrapped_time_tagged_tc(timestamp: UnixTimestamp, buf: &mut [u8]) -> PusTcCreator { - let cds_time = cds::TimeProvider::from_unix_secs_with_u16_days(×tamp).unwrap(); + fn double_wrapped_time_tagged_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator { + let cds_time = + cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent) + .unwrap(); let len_time_stamp = cds_time.write_to_bytes(buf).unwrap(); - let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap(); + let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 0); // app data should not matter, double wrapped time-tagged commands should be rejected right // away - let inner_time_tagged_tc = PusTcCreator::new_simple(&mut sph, 11, 4, None, true); + let inner_time_tagged_tc = PusTcCreator::new_simple(sph, 11, 4, &[], true); let packet_len = inner_time_tagged_tc .write_to_bytes(&mut buf[len_time_stamp..]) .expect("writing inner time tagged tc failed"); - PusTcCreator::new_simple( - &mut sph, - 11, - 4, - Some(&buf[..len_time_stamp + packet_len]), - true, - ) + PusTcCreator::new_simple(sph, 11, 4, &buf[..len_time_stamp + packet_len], true) } fn invalid_time_tagged_cmd() -> PusTcCreator<'static> { - let mut sph = SpHeader::tc_unseg(0x02, 0x34, 1).unwrap(); - PusTcCreator::new_simple(&mut sph, 11, 4, None, true) + let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 1); + PusTcCreator::new_simple(sph, 11, 4, &[], true) } - fn base_ping_tc_simple_ctor( - seq_count: u16, - app_data: Option<&'static [u8]>, - ) -> PusTcCreator<'static> { - let mut sph = SpHeader::tc_unseg(0x02, seq_count, 0).unwrap(); - PusTcCreator::new_simple(&mut sph, 17, 1, app_data, true) + fn base_ping_tc_simple_ctor(seq_count: u16, app_data: &'static [u8]) -> PusTcCreator<'static> { + let sph = SpHeader::new_for_unseg_tc(0x02, seq_count, 0); + PusTcCreator::new_simple(sph, 17, 1, app_data, true) } fn ping_tc_to_store( pool: &mut StaticMemoryPool, buf: &mut [u8], seq_count: u16, - app_data: Option<&'static [u8]>, + app_data: &'static [u8], ) -> TcInfo { let ping_tc = base_ping_tc_simple_ctor(seq_count, app_data); let ping_size = ping_tc.write_to_bytes(buf).expect("writing ping TC failed"); @@ -938,8 +934,7 @@ mod tests { #[test] fn test_enable_api() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); assert!(scheduler.is_enabled()); scheduler.disable(); assert!(!scheduler.is_enabled()); @@ -950,33 +945,32 @@ mod tests { #[test] fn test_reset() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler .insert_unwrapped_and_stored_tc( - UnixTimestamp::new_only_seconds(100), + UnixTime::new_only_secs(100), TcInfo::new(tc_info_0.addr, tc_info_0.request_id), ) .unwrap(); let app_data = &[0, 1, 2]; - let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, Some(app_data)); + let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, app_data); scheduler .insert_unwrapped_and_stored_tc( - UnixTimestamp::new_only_seconds(200), + UnixTime::new_only_secs(200), TcInfo::new(tc_info_1.addr, tc_info_1.request_id), ) .unwrap(); let app_data = &[0, 1, 2]; - let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, Some(app_data)); + let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, app_data); scheduler .insert_unwrapped_and_stored_tc( - UnixTimestamp::new_only_seconds(300), + UnixTime::new_only_secs(300), TcInfo::new(tc_info_2.addr(), tc_info_2.request_id()), ) .unwrap(); @@ -993,12 +987,11 @@ mod tests { #[test] fn insert_multi_with_same_time() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); scheduler .insert_unwrapped_and_stored_tc( - UnixTimestamp::new_only_seconds(100), + UnixTime::new_only_secs(100), TcInfo::new( StoreAddr::from(StaticPoolAddr { pool_idx: 0, @@ -1015,7 +1008,7 @@ mod tests { scheduler .insert_unwrapped_and_stored_tc( - UnixTimestamp::new_only_seconds(100), + UnixTime::new_only_secs(100), TcInfo::new( StoreAddr::from(StaticPoolAddr { pool_idx: 0, @@ -1032,7 +1025,7 @@ mod tests { scheduler .insert_unwrapped_and_stored_tc( - UnixTimestamp::new_only_seconds(300), + UnixTime::new_only_secs(300), TcInfo::new( StaticPoolAddr { pool_idx: 0, @@ -1053,9 +1046,8 @@ mod tests { #[test] fn test_time_update() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); - let time = UnixTimestamp::new(1, 2).unwrap(); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); + let time = UnixTime::new(1, 2_000_000); scheduler.update_time(time); assert_eq!(scheduler.current_time(), &time); } @@ -1086,10 +1078,10 @@ mod tests { let src_id_to_set = 12; let apid_to_set = 0x22; let seq_count = 105; - let mut sp_header = SpHeader::tc_unseg(apid_to_set, 105, 0).unwrap(); + let sp_header = SpHeader::new_for_unseg_tc(apid_to_set, 105, 0); let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1); sec_header.source_id = src_id_to_set; - let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true); + let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true); let req_id = RequestId::from_tc(&ping_tc); assert_eq!(req_id.source_id(), src_id_to_set); assert_eq!(req_id.apid(), apid_to_set); @@ -1102,19 +1094,18 @@ mod tests { #[test] fn test_release_telecommands() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("insertion failed"); - let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None); + let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(200), tc_info_1) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1) .expect("insertion failed"); let mut i = 0; @@ -1124,7 +1115,7 @@ mod tests { }; // test 1: too early, no tcs - scheduler.update_time(UnixTimestamp::new_only_seconds(99)); + scheduler.update_time(UnixTime::new_only_secs(99)); let mut tc_buf: [u8; 128] = [0; 128]; scheduler @@ -1132,7 +1123,7 @@ mod tests { .expect("deletion failed"); // test 2: exact time stamp of tc, releases 1 tc - scheduler.update_time(UnixTimestamp::new_only_seconds(100)); + scheduler.update_time(UnixTime::new_only_secs(100)); let mut released = scheduler .release_telecommands(&mut test_closure_1, &mut pool) @@ -1147,7 +1138,7 @@ mod tests { true }; - scheduler.update_time(UnixTimestamp::new_only_seconds(206)); + scheduler.update_time(UnixTime::new_only_secs(206)); released = scheduler .release_telecommands_with_buffer(&mut test_closure_2, &mut pool, &mut tc_buf) @@ -1168,19 +1159,18 @@ mod tests { #[test] fn release_multi_with_same_time() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("insertion failed"); - let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None); + let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_1) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1) .expect("insertion failed"); let mut i = 0; @@ -1195,7 +1185,7 @@ mod tests { }; // test 1: too early, no tcs - scheduler.update_time(UnixTimestamp::new_only_seconds(99)); + scheduler.update_time(UnixTime::new_only_secs(99)); let mut tc_buf: [u8; 128] = [0; 128]; let mut released = scheduler @@ -1204,7 +1194,7 @@ mod tests { assert_eq!(released, 0); // test 2: exact time stamp of tc, releases 2 tc - scheduler.update_time(UnixTimestamp::new_only_seconds(100)); + scheduler.update_time(UnixTime::new_only_secs(100)); released = scheduler .release_telecommands(&mut test_closure, &mut pool) @@ -1226,21 +1216,20 @@ mod tests { #[test] fn release_with_scheduler_disabled() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); scheduler.disable(); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("insertion failed"); - let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None); + let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(200), tc_info_1) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1) .expect("insertion failed"); let mut i = 0; @@ -1252,14 +1241,14 @@ mod tests { let mut tc_buf: [u8; 128] = [0; 128]; // test 1: too early, no tcs - scheduler.update_time(UnixTimestamp::new_only_seconds(99)); + scheduler.update_time(UnixTime::new_only_secs(99)); scheduler .release_telecommands_with_buffer(&mut test_closure_1, &mut pool, &mut tc_buf) .expect("deletion failed"); // test 2: exact time stamp of tc, releases 1 tc - scheduler.update_time(UnixTimestamp::new_only_seconds(100)); + scheduler.update_time(UnixTime::new_only_secs(100)); let mut released = scheduler .release_telecommands(&mut test_closure_1, &mut pool) @@ -1273,7 +1262,7 @@ mod tests { true }; - scheduler.update_time(UnixTimestamp::new_only_seconds(206)); + scheduler.update_time(UnixTime::new_only_secs(206)); released = scheduler .release_telecommands(&mut test_closure_2, &mut pool) @@ -1292,16 +1281,15 @@ mod tests { #[test] fn insert_unwrapped_tc() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); let info = scheduler .insert_unwrapped_tc( - UnixTimestamp::new_only_seconds(100), + UnixTime::new_only_secs(100), &buf[..pool.len_of_data(&tc_info_0.addr()).unwrap()], &mut pool, ) @@ -1312,11 +1300,11 @@ mod tests { let mut read_buf: [u8; 64] = [0; 64]; pool.read(&tc_info_0.addr(), &mut read_buf).unwrap(); let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data"); - assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None)); + assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[])); assert_eq!(scheduler.num_scheduled_telecommands(), 1); - scheduler.update_time(UnixTimestamp::new_only_seconds(101)); + scheduler.update_time(UnixTime::new_only_secs(101)); let mut addr_vec = Vec::new(); @@ -1335,20 +1323,19 @@ mod tests { let read_len = pool.read(&addr_vec[0], &mut read_buf).unwrap(); let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data"); assert_eq!(read_len, check_tc.1); - assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None)); + assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[])); } #[test] fn insert_wrapped_tc() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut buf: [u8; 32] = [0; 32]; - let tc = scheduled_tc(UnixTimestamp::new_only_seconds(100), &mut buf); + let tc = scheduled_tc(UnixTime::new_only_secs(100), &mut buf); - let info = match scheduler.insert_wrapped_tc::(&tc, &mut pool) { + let info = match scheduler.insert_wrapped_tc::(&tc, &mut pool) { Ok(addr) => addr, Err(e) => { panic!("unexpected error {e}"); @@ -1360,11 +1347,11 @@ mod tests { let read_len = pool.read(&info.addr, &mut buf).unwrap(); let check_tc = PusTcReader::new(&buf).expect("incorrect Pus tc raw data"); assert_eq!(read_len, check_tc.1); - assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None)); + assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[])); assert_eq!(scheduler.num_scheduled_telecommands(), 1); - scheduler.update_time(UnixTimestamp::new_only_seconds(101)); + scheduler.update_time(UnixTime::new_only_secs(101)); let mut addr_vec = Vec::new(); @@ -1385,20 +1372,19 @@ mod tests { let read_len = pool.read(&addr_vec[0], &mut buf).unwrap(); let check_tc = PusTcReader::new(&buf).expect("incorrect PUS tc raw data"); assert_eq!(read_len, check_tc.1); - assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None)); + assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[])); } #[test] fn insert_wrong_service() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut buf: [u8; 32] = [0; 32]; - let tc = wrong_tc_service(UnixTimestamp::new_only_seconds(100), &mut buf); + let tc = wrong_tc_service(UnixTime::new_only_secs(100), &mut buf); - let err = scheduler.insert_wrapped_tc::(&tc, &mut pool); + let err = scheduler.insert_wrapped_tc::(&tc, &mut pool); assert!(err.is_err()); let err = err.unwrap_err(); match err { @@ -1413,15 +1399,14 @@ mod tests { #[test] fn insert_wrong_subservice() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut buf: [u8; 32] = [0; 32]; - let tc = wrong_tc_subservice(UnixTimestamp::new_only_seconds(100), &mut buf); + let tc = wrong_tc_subservice(UnixTime::new_only_secs(100), &mut buf); - let err = scheduler.insert_wrapped_tc::(&tc, &mut pool); + let err = scheduler.insert_wrapped_tc::(&tc, &mut pool); assert!(err.is_err()); let err = err.unwrap_err(); match err { @@ -1436,11 +1421,10 @@ mod tests { #[test] fn insert_wrapped_tc_faulty_app_data() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let tc = invalid_time_tagged_cmd(); - let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); + let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); assert!(insert_res.is_err()); let err = insert_res.unwrap_err(); match err { @@ -1451,12 +1435,11 @@ mod tests { #[test] fn insert_doubly_wrapped_time_tagged_cmd() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut buf: [u8; 64] = [0; 64]; - let tc = double_wrapped_time_tagged_tc(UnixTimestamp::new_only_seconds(50), &mut buf); - let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); + let tc = double_wrapped_time_tagged_tc(UnixTime::new_only_secs(50), &mut buf); + let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); assert!(insert_res.is_err()); let err = insert_res.unwrap_err(); match err { @@ -1470,31 +1453,29 @@ mod tests { let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5)) .expect("creation from current time failed"); let current_time = scheduler.current_time; - assert!(current_time.unix_seconds > 0); + assert!(current_time.as_secs() > 0); } #[test] fn test_update_from_current() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); - assert_eq!(scheduler.current_time.unix_seconds, 0); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); + assert_eq!(scheduler.current_time.as_secs(), 0); scheduler .update_time_from_now() .expect("updating scheduler time from now failed"); - assert!(scheduler.current_time.unix_seconds > 0); + assert!(scheduler.current_time.as_secs() > 0); } #[test] fn release_time_within_time_margin() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut buf: [u8; 32] = [0; 32]; - let tc = scheduled_tc(UnixTimestamp::new_only_seconds(4), &mut buf); - let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); + let tc = scheduled_tc(UnixTime::new_only_secs(4), &mut buf); + let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); assert!(insert_res.is_err()); let err = insert_res.unwrap_err(); match err { @@ -1503,9 +1484,9 @@ mod tests { time_margin, release_time, } => { - assert_eq!(current_time, UnixTimestamp::new_only_seconds(0)); + assert_eq!(current_time, UnixTime::new_only_secs(0)); assert_eq!(time_margin, Duration::from_secs(5)); - assert_eq!(release_time, UnixTimestamp::new_only_seconds(4)); + assert_eq!(release_time, UnixTime::new_only_secs(4)); } _ => panic!("unexepcted error {err}"), } @@ -1514,12 +1495,11 @@ mod tests { #[test] fn test_store_error_propagation_release() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("insertion failed"); let mut i = 0; @@ -1532,7 +1512,7 @@ mod tests { pool.delete(tc_info_0.addr()).expect("deletion failed"); // scheduler will only auto-delete if it is disabled. scheduler.disable(); - scheduler.update_time(UnixTimestamp::new_only_seconds(100)); + scheduler.update_time(UnixTime::new_only_secs(100)); let release_res = scheduler.release_telecommands(test_closure_1, &mut pool); assert!(release_res.is_err()); let err = release_res.unwrap_err(); @@ -1549,12 +1529,11 @@ mod tests { #[test] fn test_store_error_propagation_reset() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("insertion failed"); // premature deletion @@ -1573,12 +1552,11 @@ mod tests { #[test] fn test_delete_by_req_id_simple_retrieve_addr() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("inserting tc failed"); assert_eq!(scheduler.num_scheduled_telecommands(), 1); let addr = scheduler @@ -1592,12 +1570,11 @@ mod tests { #[test] fn test_delete_by_req_id_simple_delete_all() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("inserting tc failed"); assert_eq!(scheduler.num_scheduled_telecommands(), 1); let del_res = @@ -1611,20 +1588,19 @@ mod tests { #[test] fn test_delete_by_req_id_complex() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("inserting tc failed"); - let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None); + let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_1) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1) .expect("inserting tc failed"); - let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, None); + let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_2) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_2) .expect("inserting tc failed"); assert_eq!(scheduler.num_scheduled_telecommands(), 3); @@ -1654,17 +1630,16 @@ mod tests { #[test] fn insert_full_store_test() { - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(1, 64)], false)); let mut buf: [u8; 32] = [0; 32]; // Store is full after this. pool.add(&[0, 1, 2]).unwrap(); - let tc = scheduled_tc(UnixTimestamp::new_only_seconds(100), &mut buf); + let tc = scheduled_tc(UnixTime::new_only_secs(100), &mut buf); - let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); + let insert_res = scheduler.insert_wrapped_tc::(&tc, &mut pool); assert!(insert_res.is_err()); let err = insert_res.unwrap_err(); match err { @@ -1683,13 +1658,10 @@ mod tests { release_secs: u64, ) -> TcInfo { let mut buf: [u8; 32] = [0; 32]; - let tc_info = ping_tc_to_store(pool, &mut buf, seq_count, None); + let tc_info = ping_tc_to_store(pool, &mut buf, seq_count, &[]); scheduler - .insert_unwrapped_and_stored_tc( - UnixTimestamp::new_only_seconds(release_secs as i64), - tc_info, - ) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(release_secs as i64), tc_info) .expect("inserting tc failed"); tc_info } @@ -1697,21 +1669,20 @@ mod tests { #[test] fn test_time_window_retrieval_select_all() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); assert_eq!(scheduler.num_scheduled_telecommands(), 2); - let check_range = |range: Range>| { + let check_range = |range: Range>| { let mut tcs_in_range = 0; for (idx, time_bucket) in range.enumerate() { tcs_in_range += 1; if idx == 0 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(50)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(50)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_0.request_id); } else if idx == 1 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(100)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id); } @@ -1720,22 +1691,22 @@ mod tests { }; let range = scheduler.retrieve_all(); check_range(range); - let range = - scheduler.retrieve_by_time_filter(TimeWindow::::new_select_all()); + let range = scheduler.retrieve_by_time_filter(TimeWindow::::new_select_all()); check_range(range); } #[test] fn test_time_window_retrieval_select_from_stamp() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150); - let start_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100)) - .expect("creating start stamp failed"); + let start_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(100), + cds::SubmillisPrecision::Absent, + ) + .expect("creating start stamp failed"); let time_window = TimeWindow::new_from_time(&start_stamp); assert_eq!(scheduler.num_scheduled_telecommands(), 3); @@ -1744,11 +1715,11 @@ mod tests { for (idx, time_bucket) in range.enumerate() { tcs_in_range += 1; if idx == 0 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(100)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id()); } else if idx == 1 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(150)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(150)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_2.request_id()); } @@ -1759,27 +1730,28 @@ mod tests { #[test] fn test_time_window_retrieval_select_to_time() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150); assert_eq!(scheduler.num_scheduled_telecommands(), 3); - let end_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100)) - .expect("creating start stamp failed"); + let end_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(100), + cds::SubmillisPrecision::Absent, + ) + .expect("creating start stamp failed"); let time_window = TimeWindow::new_to_time(&end_stamp); let range = scheduler.retrieve_by_time_filter(time_window); let mut tcs_in_range = 0; for (idx, time_bucket) in range.enumerate() { tcs_in_range += 1; if idx == 0 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(50)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(50)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_0.request_id()); } else if idx == 1 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(100)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id()); } @@ -1790,31 +1762,34 @@ mod tests { #[test] fn test_time_window_retrieval_select_from_time_to_time() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150); let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200); assert_eq!(scheduler.num_scheduled_telecommands(), 4); - let start_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100)) - .expect("creating start stamp failed"); - let end_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(150)) - .expect("creating end stamp failed"); + let start_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(100), + cds::SubmillisPrecision::Absent, + ) + .expect("creating start stamp failed"); + let end_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(150), + cds::SubmillisPrecision::Absent, + ) + .expect("creating end stamp failed"); let time_window = TimeWindow::new_from_time_to_time(&start_stamp, &end_stamp); let range = scheduler.retrieve_by_time_filter(time_window); let mut tcs_in_range = 0; for (idx, time_bucket) in range.enumerate() { tcs_in_range += 1; if idx == 0 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(100)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(100)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_1.request_id()); } else if idx == 1 { - assert_eq!(*time_bucket.0, UnixTimestamp::new_only_seconds(150)); + assert_eq!(*time_bucket.0, UnixTime::new_only_secs(150)); assert_eq!(time_bucket.1.len(), 1); assert_eq!(time_bucket.1[0].request_id, tc_info_2.request_id()); } @@ -1825,8 +1800,7 @@ mod tests { #[test] fn test_deletion_all() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); assert_eq!(scheduler.num_scheduled_telecommands(), 2); @@ -1841,7 +1815,7 @@ mod tests { insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); assert_eq!(scheduler.num_scheduled_telecommands(), 2); let del_res = scheduler - .delete_by_time_filter(TimeWindow::::new_select_all(), &mut pool); + .delete_by_time_filter(TimeWindow::::new_select_all(), &mut pool); assert!(del_res.is_ok()); assert_eq!(del_res.unwrap(), 2); assert_eq!(scheduler.num_scheduled_telecommands(), 0); @@ -1852,15 +1826,16 @@ mod tests { #[test] fn test_deletion_from_start_time() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150); assert_eq!(scheduler.num_scheduled_telecommands(), 3); - let start_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100)) - .expect("creating start stamp failed"); + let start_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(100), + cds::SubmillisPrecision::Absent, + ) + .expect("creating start stamp failed"); let time_window = TimeWindow::new_from_time(&start_stamp); let del_res = scheduler.delete_by_time_filter(time_window, &mut pool); assert!(del_res.is_ok()); @@ -1873,16 +1848,17 @@ mod tests { #[test] fn test_deletion_to_end_time() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150); assert_eq!(scheduler.num_scheduled_telecommands(), 3); - let end_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100)) - .expect("creating start stamp failed"); + let end_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(100), + cds::SubmillisPrecision::Absent, + ) + .expect("creating start stamp failed"); let time_window = TimeWindow::new_to_time(&end_stamp); let del_res = scheduler.delete_by_time_filter(time_window, &mut pool); assert!(del_res.is_ok()); @@ -1895,8 +1871,7 @@ mod tests { #[test] fn test_deletion_from_start_time_to_end_time() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let cmd_out_of_range_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150); @@ -1904,12 +1879,16 @@ mod tests { insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200); assert_eq!(scheduler.num_scheduled_telecommands(), 4); - let start_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(100)) - .expect("creating start stamp failed"); - let end_stamp = - cds::TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::new_only_seconds(150)) - .expect("creating end stamp failed"); + let start_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(100), + cds::SubmillisPrecision::Absent, + ) + .expect("creating start stamp failed"); + let end_stamp = cds::CdsTime::from_unix_time_with_u16_days( + &UnixTime::new_only_secs(150), + cds::SubmillisPrecision::Absent, + ) + .expect("creating end stamp failed"); let time_window = TimeWindow::new_from_time_to_time(&start_stamp, &end_stamp); let del_res = scheduler.delete_by_time_filter(time_window, &mut pool); assert!(del_res.is_ok()); @@ -1924,19 +1903,18 @@ mod tests { #[test] fn test_release_without_deletion() { let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); - let mut scheduler = - PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5)); + let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut buf: [u8; 32] = [0; 32]; - let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None); + let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(100), tc_info_0) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0) .expect("insertion failed"); - let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None); + let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]); scheduler - .insert_unwrapped_and_stored_tc(UnixTimestamp::new_only_seconds(200), tc_info_1) + .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1) .expect("insertion failed"); let mut i = 0; @@ -1949,7 +1927,7 @@ mod tests { ); }; - scheduler.update_time(UnixTimestamp::new_only_seconds(205)); + scheduler.update_time(UnixTime::new_only_secs(205)); let mut tc_buf: [u8; 64] = [0; 64]; let tc_info_vec = scheduler @@ -1961,21 +1939,21 @@ mod tests { #[test] fn test_generic_insert_app_data_test() { - let time_writer = cds::TimeProvider::new_with_u16_days(1, 1); - let mut sph = SpHeader::new( - PacketId::const_new(PacketType::Tc, true, 0x002), - PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5), + let time_writer = cds::CdsTime::new_with_u16_days(1, 1); + let sph = SpHeader::new( + PacketId::new(PacketType::Tc, true, 0x002), + PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5), 0, ); let sec_header = PusTcSecondaryHeader::new_simple(17, 1); - let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true); + let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true); let mut buf: [u8; 64] = [0; 64]; let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc); assert!(result.is_ok()); assert_eq!(result.unwrap(), 2 + 7 + ping_tc.len_written()); let n = u16::from_be_bytes(buf[0..2].try_into().unwrap()); assert_eq!(n, 1); - let time_reader = cds::TimeProvider::from_bytes_with_u16_days(&buf[2..2 + 7]).unwrap(); + let time_reader = cds::CdsTime::from_bytes_with_u16_days(&buf[2..2 + 7]).unwrap(); assert_eq!(time_reader, time_writer); let pus_tc_reader = PusTcReader::new(&buf[9..]).unwrap().0; assert_eq!(pus_tc_reader, ping_tc); @@ -1983,14 +1961,14 @@ mod tests { #[test] fn test_generic_insert_app_data_test_byte_conv_error() { - let time_writer = cds::TimeProvider::new_with_u16_days(1, 1); - let mut sph = SpHeader::new( - PacketId::const_new(PacketType::Tc, true, 0x002), - PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5), + let time_writer = cds::CdsTime::new_with_u16_days(1, 1); + let sph = SpHeader::new( + PacketId::new(PacketType::Tc, true, 0x002), + PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5), 0, ); let sec_header = PusTcSecondaryHeader::new_simple(17, 1); - let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true); + let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true); let mut buf: [u8; 16] = [0; 16]; let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc); assert!(result.is_err()); @@ -2012,14 +1990,14 @@ mod tests { #[test] fn test_generic_insert_app_data_test_as_vec() { - let time_writer = cds::TimeProvider::new_with_u16_days(1, 1); - let mut sph = SpHeader::new( - PacketId::const_new(PacketType::Tc, true, 0x002), - PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5), + let time_writer = cds::CdsTime::new_with_u16_days(1, 1); + let sph = SpHeader::new( + PacketId::new(PacketType::Tc, true, 0x002), + PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5), 0, ); let sec_header = PusTcSecondaryHeader::new_simple(17, 1); - let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true); + let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true); let mut buf: [u8; 64] = [0; 64]; generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap(); let vec = generate_insert_telecommand_app_data_as_vec(&time_writer, &ping_tc) diff --git a/satrs/src/pus/scheduler_srv.rs b/satrs/src/pus/scheduler_srv.rs index 9cfb920..6812770 100644 --- a/satrs/src/pus/scheduler_srv.rs +++ b/satrs/src/pus/scheduler_srv.rs @@ -1,20 +1,16 @@ use super::scheduler::PusSchedulerProvider; -use super::verification::{ - VerificationReporterWithSharedPoolMpscBoundedSender, - VerificationReporterWithSharedPoolMpscSender, VerificationReporterWithVecMpscBoundedSender, - VerificationReporterWithVecMpscSender, VerificationReportingProvider, -}; +use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::{ - get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcInSharedStoreConverter, - EcssTcInVecConverter, EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusServiceHelper, - TmAsVecSenderWithBoundedMpsc, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, - TmInSharedPoolSenderWithMpsc, + EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiverCore, + EcssTmSenderCore, MpscTcReceiver, MpscTmInSharedPoolSender, MpscTmInSharedPoolSenderBounded, + PusServiceHelper, PusTmAsVec, }; use crate::pool::PoolProvider; use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError}; use alloc::string::ToString; use spacepackets::ecss::{scheduling, PusPacket}; -use spacepackets::time::cds::TimeProvider; +use spacepackets::time::cds::CdsTime; +use std::sync::mpsc; /// This is a helper class for [std] environments to handle generic PUS 11 (scheduling service) /// packets. This handler is able to handle the most important PUS requests for a scheduling @@ -24,7 +20,7 @@ use spacepackets::time::cds::TimeProvider; /// telecommands inside the scheduler. The user can retrieve the wrapped scheduler via the /// [Self::scheduler] and [Self::scheduler_mut] function and then use the scheduler API to release /// telecommands when applicable. -pub struct PusService11SchedHandler< +pub struct PusSchedServiceHandler< TcReceiver: EcssTcReceiverCore, TmSender: EcssTmSenderCore, TcInMemConverter: EcssTcInMemConverter, @@ -43,13 +39,7 @@ impl< VerificationReporter: VerificationReportingProvider, Scheduler: PusSchedulerProvider, > - PusService11SchedHandler< - TcReceiver, - TmSender, - TcInMemConverter, - VerificationReporter, - Scheduler, - > + PusSchedServiceHandler { pub fn new( service_helper: PusServiceHelper< @@ -74,8 +64,9 @@ impl< &self.scheduler } - pub fn handle_one_tc( + pub fn poll_and_handle_next_tc( &mut self, + time_stamp: &[u8], sched_tc_pool: &mut (impl PoolProvider + ?Sized), ) -> Result { let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; @@ -83,10 +74,10 @@ impl< return Ok(PusPacketHandlerResult::Empty); } let ecss_tc_and_token = possible_packet.unwrap(); - let tc = self - .service_helper - .tc_in_mem_converter - .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; + self.service_helper + .tc_in_mem_converter_mut() + .cache(&ecss_tc_and_token.tc_in_memory)?; + let tc = self.service_helper.tc_in_mem_converter().convert()?; let subservice = PusPacket::subservice(&tc); let standard_subservice = scheduling::Subservice::try_from(subservice); if standard_subservice.is_err() { @@ -95,23 +86,28 @@ impl< ecss_tc_and_token.token, )); } - let mut partial_error = None; - let time_stamp = get_current_cds_short_timestamp(&mut partial_error); + let partial_error = None; match standard_subservice.unwrap() { scheduling::Subservice::TcEnableScheduling => { let start_token = self .service_helper - .common - .verification_handler - .start_success(ecss_tc_and_token.token, &time_stamp) + .verif_reporter() + .start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) .expect("Error sending start success"); self.scheduler.enable(); if self.scheduler.is_enabled() { self.service_helper - .common - .verification_handler - .completion_success(start_token, &time_stamp) + .verif_reporter() + .completion_success( + &self.service_helper.common.tm_sender, + start_token, + time_stamp, + ) .expect("Error sending completion success"); } else { return Err(PusPacketHandlingError::Other( @@ -122,17 +118,23 @@ impl< scheduling::Subservice::TcDisableScheduling => { let start_token = self .service_helper - .common - .verification_handler - .start_success(ecss_tc_and_token.token, &time_stamp) + .verif_reporter() + .start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) .expect("Error sending start success"); self.scheduler.disable(); if !self.scheduler.is_enabled() { self.service_helper - .common - .verification_handler - .completion_success(start_token, &time_stamp) + .verif_reporter() + .completion_success( + &self.service_helper.common.tm_sender, + start_token, + time_stamp, + ) .expect("Error sending completion success"); } else { return Err(PusPacketHandlingError::Other( @@ -143,9 +145,12 @@ impl< scheduling::Subservice::TcResetScheduling => { let start_token = self .service_helper - .common - .verification_handler - .start_success(ecss_tc_and_token.token, &time_stamp) + .verif_reporter() + .start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) .expect("Error sending start success"); self.scheduler @@ -153,28 +158,38 @@ impl< .expect("Error resetting TC Pool"); self.service_helper - .common - .verification_handler - .completion_success(start_token, &time_stamp) + .verif_reporter() + .completion_success( + &self.service_helper.common.tm_sender, + start_token, + time_stamp, + ) .expect("Error sending completion success"); } scheduling::Subservice::TcInsertActivity => { let start_token = self .service_helper .common - .verification_handler - .start_success(ecss_tc_and_token.token, &time_stamp) + .verif_reporter + .start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) .expect("error sending start success"); // let mut pool = self.sched_tc_pool.write().expect("locking pool failed"); self.scheduler - .insert_wrapped_tc::(&tc, sched_tc_pool) + .insert_wrapped_tc::(&tc, sched_tc_pool) .expect("insertion of activity into pool failed"); self.service_helper - .common - .verification_handler - .completion_success(start_token, &time_stamp) + .verif_reporter() + .completion_success( + &self.service_helper.common.tm_sender, + start_token, + time_stamp, + ) .expect("sending completion success failed"); } _ => { @@ -195,53 +210,57 @@ impl< } /// Helper type definition for a PUS 11 handler with a dynamic TMTC memory backend and regular /// mpsc queues. -pub type PusService11SchedHandlerDynWithMpsc = PusService11SchedHandler< +pub type PusService11SchedHandlerDynWithMpsc = PusSchedServiceHandler< MpscTcReceiver, - TmAsVecSenderWithMpsc, + mpsc::Sender, EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, + VerificationReporter, PusScheduler, >; /// Helper type definition for a PUS 11 handler with a dynamic TMTC memory backend and bounded MPSC /// queues. -pub type PusService11SchedHandlerDynWithBoundedMpsc = PusService11SchedHandler< +pub type PusService11SchedHandlerDynWithBoundedMpsc = PusSchedServiceHandler< MpscTcReceiver, - TmAsVecSenderWithBoundedMpsc, + mpsc::SyncSender, EcssTcInVecConverter, - VerificationReporterWithVecMpscBoundedSender, + VerificationReporter, PusScheduler, >; /// Helper type definition for a PUS 11 handler with a shared store TMTC memory backend and regular /// mpsc queues. -pub type PusService11SchedHandlerStaticWithMpsc = PusService11SchedHandler< +pub type PusService11SchedHandlerStaticWithMpsc = PusSchedServiceHandler< MpscTcReceiver, - TmInSharedPoolSenderWithMpsc, + MpscTmInSharedPoolSender, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscSender, + VerificationReporter, PusScheduler, >; /// Helper type definition for a PUS 11 handler with a shared store TMTC memory backend and bounded /// mpsc queues. -pub type PusService11SchedHandlerStaticWithBoundedMpsc = PusService11SchedHandler< +pub type PusService11SchedHandlerStaticWithBoundedMpsc = PusSchedServiceHandler< MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, + MpscTmInSharedPoolSenderBounded, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, + VerificationReporter, PusScheduler, >; #[cfg(test)] mod tests { use crate::pool::{StaticMemoryPool, StaticPoolConfig}; - use crate::pus::tests::TEST_APID; - use crate::pus::verification::VerificationReporterWithSharedPoolMpscBoundedSender; + use crate::pus::test_util::{PusTestHarness, TEST_APID}; + use crate::pus::verification::{VerificationReporter, VerificationReportingProvider}; + use crate::pus::{ scheduler::{self, PusSchedulerProvider, TcInfo}, - tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness}, + tests::PusServiceHandlerWithSharedStoreCommon, verification::{RequestId, TcStateAccepted, VerificationToken}, EcssTcInSharedStoreConverter, }; - use crate::pus::{MpscTcReceiver, TmInSharedPoolSenderWithBoundedMpsc}; + use crate::pus::{ + MpscTcReceiver, MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult, + PusPacketHandlingError, + }; use alloc::collections::VecDeque; use delegate::delegate; use spacepackets::ecss::scheduling::Subservice; @@ -254,15 +273,15 @@ mod tests { time::cds, }; - use super::PusService11SchedHandler; + use super::PusSchedServiceHandler; struct Pus11HandlerWithStoreTester { common: PusServiceHandlerWithSharedStoreCommon, - handler: PusService11SchedHandler< + handler: PusSchedServiceHandler< MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, + MpscTmInSharedPoolSenderBounded, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, + VerificationReporter, TestScheduler, >, sched_tc_pool: StaticMemoryPool, @@ -273,19 +292,34 @@ mod tests { let test_scheduler = TestScheduler::default(); let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false); let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone()); - let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(); + let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(0); Self { common, - handler: PusService11SchedHandler::new(srv_handler, test_scheduler), + handler: PusSchedServiceHandler::new(srv_handler, test_scheduler), sched_tc_pool, } } + + pub fn handle_one_tc(&mut self) -> Result { + let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); + self.handler + .poll_and_handle_next_tc(&time_stamp, &mut self.sched_tc_pool) + } } impl PusTestHarness for Pus11HandlerWithStoreTester { + fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken { + let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc); + self.handler + .service_helper + .verif_reporter() + .acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7]) + .expect("acceptance success failure") + } + delegate! { to self.common { - fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken; + fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator); fn read_next_tm(&mut self) -> PusTmReader<'_>; fn check_no_tm_available(&self) -> bool; fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId); @@ -303,7 +337,7 @@ mod tests { } impl PusSchedulerProvider for TestScheduler { - type TimeProvider = cds::TimeProvider; + type TimeProvider = cds::CdsTime; fn reset( &mut self, @@ -329,7 +363,7 @@ mod tests { fn insert_unwrapped_and_stored_tc( &mut self, - _time_stamp: spacepackets::time::UnixTimestamp, + _time_stamp: spacepackets::time::UnixTime, info: crate::pus::scheduler::TcInfo, ) -> Result<(), crate::pus::scheduler::ScheduleError> { self.inserted_tcs.push_back(info); @@ -341,15 +375,17 @@ mod tests { test_harness: &mut Pus11HandlerWithStoreTester, subservice: Subservice, ) { - let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap(); + let reply_header = SpHeader::new_for_unseg_tm(TEST_APID, 0, 0); let tc_header = PusTcSecondaryHeader::new_simple(11, subservice as u8); - let enable_scheduling = PusTcCreator::new(&mut reply_header, tc_header, &[0; 7], true); - let token = test_harness.send_tc(&enable_scheduling); + let enable_scheduling = PusTcCreator::new(reply_header, tc_header, &[0; 7], true); + let token = test_harness.init_verification(&enable_scheduling); + test_harness.send_tc(&token, &enable_scheduling); - let request_id = token.req_id(); + let request_id = token.request_id(); + let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); test_harness .handler - .handle_one_tc(&mut test_harness.sched_tc_pool) + .poll_and_handle_next_tc(&time_stamp, &mut test_harness.sched_tc_pool) .unwrap(); test_harness.check_next_verification_tm(1, request_id); test_harness.check_next_verification_tm(3, request_id); @@ -386,31 +422,29 @@ mod tests { #[test] fn test_insert_activity_tc() { let mut test_harness = Pus11HandlerWithStoreTester::new(); - let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap(); + let mut reply_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1); - let ping_tc = PusTcCreator::new(&mut reply_header, sec_header, &[], true); + let ping_tc = PusTcCreator::new(reply_header, sec_header, &[], true); let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc); - let stamper = cds::TimeProvider::from_now_with_u16_days().expect("time provider failed"); + let stamper = cds::CdsTime::now_with_u16_days().expect("time provider failed"); let mut sched_app_data: [u8; 64] = [0; 64]; let mut written_len = stamper.write_to_bytes(&mut sched_app_data).unwrap(); let ping_raw = ping_tc.to_vec().expect("generating raw tc failed"); sched_app_data[written_len..written_len + ping_raw.len()].copy_from_slice(&ping_raw); written_len += ping_raw.len(); - reply_header = SpHeader::tm_unseg(TEST_APID, 1, 0).unwrap(); + reply_header = SpHeader::new_for_unseg_tc(TEST_APID, 1, 0); sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8); let enable_scheduling = PusTcCreator::new( - &mut reply_header, + reply_header, sec_header, &sched_app_data[..written_len], true, ); - let token = test_harness.send_tc(&enable_scheduling); + let token = test_harness.init_verification(&enable_scheduling); + test_harness.send_tc(&token, &enable_scheduling); - let request_id = token.req_id(); - test_harness - .handler - .handle_one_tc(&mut test_harness.sched_tc_pool) - .unwrap(); + let request_id = token.request_id(); + test_harness.handle_one_tc().unwrap(); test_harness.check_next_verification_tm(1, request_id); test_harness.check_next_verification_tm(3, request_id); test_harness.check_next_verification_tm(7, request_id); diff --git a/satrs/src/pus/test.rs b/satrs/src/pus/test.rs index ea5a720..58abb0f 100644 --- a/satrs/src/pus/test.rs +++ b/satrs/src/pus/test.rs @@ -1,20 +1,17 @@ use crate::pus::{ - PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmWrapper, + PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmAsVec, + PusTmInPool, PusTmVariant, }; use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; use spacepackets::ecss::PusPacket; use spacepackets::SpHeader; +use std::sync::mpsc; -use super::verification::{ - VerificationReporterWithSharedPoolMpscBoundedSender, - VerificationReporterWithSharedPoolMpscSender, VerificationReporterWithVecMpscBoundedSender, - VerificationReporterWithVecMpscSender, VerificationReportingProvider, -}; +use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::{ - get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcInSharedStoreConverter, - EcssTcInVecConverter, EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusServiceHelper, - TmAsVecSenderWithBoundedMpsc, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, - TmInSharedPoolSenderWithMpsc, + EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiverCore, + EcssTmSenderCore, GenericConversionError, MpscTcReceiver, MpscTmInSharedPoolSender, + MpscTmInSharedPoolSenderBounded, PusServiceHelper, }; /// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets. @@ -47,27 +44,32 @@ impl< Self { service_helper } } - pub fn handle_one_tc(&mut self) -> Result { + pub fn poll_and_handle_next_tc( + &mut self, + time_stamp: &[u8], + ) -> Result { let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; if possible_packet.is_none() { return Ok(PusPacketHandlerResult::Empty); } let ecss_tc_and_token = possible_packet.unwrap(); - let tc = self - .service_helper - .tc_in_mem_converter - .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; + self.service_helper + .tc_in_mem_converter_mut() + .cache(&ecss_tc_and_token.tc_in_memory)?; + let tc = self.service_helper.tc_in_mem_converter().convert()?; if tc.service() != 17 { - return Err(PusPacketHandlingError::WrongService(tc.service())); + return Err(GenericConversionError::WrongService(tc.service()).into()); } if tc.subservice() == 1 { let mut partial_error = None; - let time_stamp = get_current_cds_short_timestamp(&mut partial_error); let result = self .service_helper - .common - .verification_handler - .start_success(ecss_tc_and_token.token, &time_stamp) + .verif_reporter() + .start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) .map_err(|_| PartialPusHandlingError::Verification); let start_token = if let Ok(result) = result { Some(result) @@ -76,15 +78,17 @@ impl< None }; // Sequence count will be handled centrally in TM funnel. - let mut reply_header = - SpHeader::tm_unseg(self.service_helper.common.tm_apid, 0, 0).unwrap(); - let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp); - let ping_reply = PusTmCreator::new(&mut reply_header, tc_header, &[], true); + // It is assumed that the verification reporter was built with a valid APID, so we use + // the unchecked API here. + let reply_header = + SpHeader::new_for_unseg_tm(self.service_helper.verif_reporter().apid(), 0, 0); + let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp); + let ping_reply = PusTmCreator::new(reply_header, tc_header, &[], true); let result = self .service_helper .common .tm_sender - .send_tm(PusTmWrapper::Direct(ping_reply)) + .send_tm(self.service_helper.id(), PusTmVariant::Direct(ping_reply)) .map_err(PartialPusHandlingError::TmSend); if let Err(err) = result { partial_error = Some(err); @@ -93,9 +97,12 @@ impl< if let Some(start_token) = start_token { if self .service_helper - .common - .verification_handler - .completion_success(start_token, &time_stamp) + .verif_reporter() + .completion_success( + &self.service_helper.common.tm_sender, + start_token, + time_stamp, + ) .is_err() { partial_error = Some(PartialPusHandlingError::Verification) @@ -120,55 +127,57 @@ impl< /// mpsc queues. pub type PusService17TestHandlerDynWithMpsc = PusService17TestHandler< MpscTcReceiver, - TmAsVecSenderWithMpsc, + mpsc::Sender, EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, + VerificationReporter, >; /// Helper type definition for a PUS 17 handler with a dynamic TMTC memory backend and bounded MPSC /// queues. pub type PusService17TestHandlerDynWithBoundedMpsc = PusService17TestHandler< MpscTcReceiver, - TmAsVecSenderWithBoundedMpsc, + mpsc::SyncSender, EcssTcInVecConverter, - VerificationReporterWithVecMpscBoundedSender, + VerificationReporter, >; /// Helper type definition for a PUS 17 handler with a shared store TMTC memory backend and regular /// mpsc queues. pub type PusService17TestHandlerStaticWithMpsc = PusService17TestHandler< MpscTcReceiver, - TmInSharedPoolSenderWithMpsc, + MpscTmInSharedPoolSender, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscSender, + VerificationReporter, >; /// Helper type definition for a PUS 17 handler with a shared store TMTC memory backend and bounded /// mpsc queues. pub type PusService17TestHandlerStaticWithBoundedMpsc = PusService17TestHandler< MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, + MpscTmInSharedPoolSenderBounded, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, + VerificationReporter, >; #[cfg(test)] mod tests { + use crate::pus::test_util::{PusTestHarness, SimplePusPacketHandler, TEST_APID}; use crate::pus::tests::{ - PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon, PusTestHarness, - SimplePusPacketHandler, TEST_APID, + PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon, }; - use crate::pus::verification::std_mod::{ - VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender, + use crate::pus::verification::{ + RequestId, VerificationReporter, VerificationReportingProvider, }; - use crate::pus::verification::RequestId; use crate::pus::verification::{TcStateAccepted, VerificationToken}; use crate::pus::{ - EcssTcInSharedStoreConverter, EcssTcInVecConverter, MpscTcReceiver, PusPacketHandlerResult, - PusPacketHandlingError, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc, + EcssTcInSharedStoreConverter, EcssTcInVecConverter, GenericConversionError, MpscTcReceiver, + MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult, + PusPacketHandlingError, }; + use crate::ComponentId; use delegate::delegate; use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader}; use spacepackets::ecss::tm::PusTmReader; use spacepackets::ecss::PusPacket; - use spacepackets::{SequenceFlags, SpHeader}; + use spacepackets::time::{cds, TimeWriter}; + use spacepackets::SpHeader; use super::PusService17TestHandler; @@ -176,15 +185,15 @@ mod tests { common: PusServiceHandlerWithSharedStoreCommon, handler: PusService17TestHandler< MpscTcReceiver, - TmInSharedPoolSenderWithBoundedMpsc, + MpscTmInSharedPoolSenderBounded, EcssTcInSharedStoreConverter, - VerificationReporterWithSharedPoolMpscBoundedSender, + VerificationReporter, >, } impl Pus17HandlerWithStoreTester { - pub fn new() -> Self { - let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(); + pub fn new(id: ComponentId) -> Self { + let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(id); let pus_17_handler = PusService17TestHandler::new(srv_handler); Self { common, @@ -194,10 +203,19 @@ mod tests { } impl PusTestHarness for Pus17HandlerWithStoreTester { + fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken { + let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc); + self.handler + .service_helper + .verif_reporter() + .acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7]) + .expect("acceptance success failure") + } + delegate! { to self.common { - fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken; fn read_next_tm(&mut self) -> PusTmReader<'_>; + fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator); fn check_no_tm_available(&self) -> bool; fn check_next_verification_tm( &self, @@ -208,27 +226,26 @@ mod tests { } } impl SimplePusPacketHandler for Pus17HandlerWithStoreTester { - delegate! { - to self.handler { - fn handle_one_tc(&mut self) -> Result; - } + fn handle_one_tc(&mut self) -> Result { + let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); + self.handler.poll_and_handle_next_tc(&time_stamp) } } struct Pus17HandlerWithVecTester { - common: PusServiceHandlerWithVecCommon, + common: PusServiceHandlerWithVecCommon, handler: PusService17TestHandler< MpscTcReceiver, - TmAsVecSenderWithMpsc, + MpscTmAsVecSender, EcssTcInVecConverter, - VerificationReporterWithVecMpscSender, + VerificationReporter, >, } impl Pus17HandlerWithVecTester { - pub fn new() -> Self { + pub fn new(id: ComponentId) -> Self { let (common, srv_handler) = - PusServiceHandlerWithVecCommon::new_with_standard_verif_reporter(); + PusServiceHandlerWithVecCommon::new_with_standard_verif_reporter(id); Self { common, handler: PusService17TestHandler::new(srv_handler), @@ -237,9 +254,18 @@ mod tests { } impl PusTestHarness for Pus17HandlerWithVecTester { + fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken { + let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc); + self.handler + .service_helper + .verif_reporter() + .acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7]) + .expect("acceptance success failure") + } + delegate! { to self.common { - fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken; + fn send_tc(&self, token: &VerificationToken, tc: &PusTcCreator); fn read_next_tm(&mut self) -> PusTmReader<'_>; fn check_no_tm_available(&self) -> bool; fn check_next_verification_tm( @@ -251,20 +277,20 @@ mod tests { } } impl SimplePusPacketHandler for Pus17HandlerWithVecTester { - delegate! { - to self.handler { - fn handle_one_tc(&mut self) -> Result; - } + fn handle_one_tc(&mut self) -> Result { + let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); + self.handler.poll_and_handle_next_tc(&time_stamp) } } fn ping_test(test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler)) { // Create a ping TC, verify acceptance. - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(17, 1); - let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true); - let token = test_harness.send_tc(&ping_tc); - let request_id = token.req_id(); + let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true); + let token = test_harness.init_verification(&ping_tc); + test_harness.send_tc(&token, &ping_tc); + let request_id = token.request_id(); let result = test_harness.handle_one_tc(); assert!(result.is_ok()); // We should see 4 replies in the TM queue now: Acceptance TM, Start TM, ping reply and @@ -288,19 +314,19 @@ mod tests { #[test] fn test_basic_ping_processing_using_store() { - let mut test_harness = Pus17HandlerWithStoreTester::new(); + let mut test_harness = Pus17HandlerWithStoreTester::new(0); ping_test(&mut test_harness); } #[test] fn test_basic_ping_processing_using_vec() { - let mut test_harness = Pus17HandlerWithVecTester::new(); + let mut test_harness = Pus17HandlerWithVecTester::new(0); ping_test(&mut test_harness); } #[test] fn test_empty_tc_queue() { - let mut test_harness = Pus17HandlerWithStoreTester::new(); + let mut test_harness = Pus17HandlerWithStoreTester::new(0); let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); @@ -312,15 +338,19 @@ mod tests { #[test] fn test_sending_unsupported_service() { - let mut test_harness = Pus17HandlerWithStoreTester::new(); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); + let mut test_harness = Pus17HandlerWithStoreTester::new(0); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(3, 1); - let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true); - test_harness.send_tc(&ping_tc); + let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true); + let token = test_harness.init_verification(&ping_tc); + test_harness.send_tc(&token, &ping_tc); let result = test_harness.handle_one_tc(); assert!(result.is_err()); let error = result.unwrap_err(); - if let PusPacketHandlingError::WrongService(num) = error { + if let PusPacketHandlingError::RequestConversion(GenericConversionError::WrongService( + num, + )) = error + { assert_eq!(num, 3); } else { panic!("unexpected error type {error}") @@ -329,11 +359,12 @@ mod tests { #[test] fn test_sending_custom_subservice() { - let mut test_harness = Pus17HandlerWithStoreTester::new(); - let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap(); + let mut test_harness = Pus17HandlerWithStoreTester::new(0); + let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let sec_header = PusTcSecondaryHeader::new_simple(17, 200); - let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true); - test_harness.send_tc(&ping_tc); + let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true); + let token = test_harness.init_verification(&ping_tc); + test_harness.send_tc(&token, &ping_tc); let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); diff --git a/satrs/src/pus/verification.rs b/satrs/src/pus/verification.rs index 89ce9b9..2cab962 100644 --- a/satrs/src/pus/verification.rs +++ b/satrs/src/pus/verification.rs @@ -17,10 +17,11 @@ //! use std::time::Duration; //! use satrs::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig}; //! use satrs::pus::verification::{ -//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporterWithSender +//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporter //! }; //! use satrs::seq_count::SeqCountProviderSimple; -//! use satrs::pus::TmInSharedPoolSenderWithMpsc; +//! use satrs::request::UniqueApidTargetId; +//! use satrs::pus::MpscTmInSharedPoolSender; //! use satrs::tmtc::tm_helper::SharedTmPool; //! use spacepackets::ecss::PusPacket; //! use spacepackets::SpHeader; @@ -29,35 +30,39 @@ //! //! const EMPTY_STAMP: [u8; 7] = [0; 7]; //! const TEST_APID: u16 = 0x02; +//! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); //! //! let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false); //! let tm_pool = StaticMemoryPool::new(pool_cfg.clone()); //! let shared_tm_store = SharedTmPool::new(tm_pool); //! let tm_store = shared_tm_store.clone_backing_pool(); //! let (verif_tx, verif_rx) = mpsc::channel(); -//! let sender = TmInSharedPoolSenderWithMpsc::new(0, "Test Sender", shared_tm_store, verif_tx); +//! let sender = MpscTmInSharedPoolSender::new(shared_tm_store, verif_tx); //! let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); -//! let mut reporter = VerificationReporterWithSender::new(&cfg , sender); +//! let mut reporter = VerificationReporter::new(TEST_COMPONENT_ID.id(), &cfg); //! -//! let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap(); //! let tc_header = PusTcSecondaryHeader::new_simple(17, 1); -//! let pus_tc_0 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true); +//! let pus_tc_0 = PusTcCreator::new_no_app_data( +//! SpHeader::new_from_apid(TEST_APID), +//! tc_header, +//! true +//! ); //! let init_token = reporter.add_tc(&pus_tc_0); //! //! // Complete success sequence for a telecommand -//! let accepted_token = reporter.acceptance_success(init_token, &EMPTY_STAMP).unwrap(); -//! let started_token = reporter.start_success(accepted_token, &EMPTY_STAMP).unwrap(); -//! reporter.completion_success(started_token, &EMPTY_STAMP).unwrap(); +//! let accepted_token = reporter.acceptance_success(&sender, init_token, &EMPTY_STAMP).unwrap(); +//! let started_token = reporter.start_success(&sender, accepted_token, &EMPTY_STAMP).unwrap(); +//! reporter.completion_success(&sender, started_token, &EMPTY_STAMP).unwrap(); //! //! // Verify it arrives correctly on receiver end //! let mut tm_buf: [u8; 1024] = [0; 1024]; //! let mut packet_idx = 0; //! while packet_idx < 3 { -//! let addr = verif_rx.recv_timeout(Duration::from_millis(10)).unwrap(); +//! let tm_in_store = verif_rx.recv_timeout(Duration::from_millis(10)).unwrap(); //! let tm_len; //! { //! let mut rg = tm_store.write().expect("Error locking shared pool"); -//! let store_guard = rg.read_with_guard(addr); +//! let store_guard = rg.read_with_guard(tm_in_store.store_addr); //! tm_len = store_guard.read(&mut tm_buf).expect("Error reading TM slice"); //! } //! let (pus_tm, _) = PusTmReader::new(&tm_buf[0..tm_len], 7) @@ -87,8 +92,8 @@ use delegate::delegate; use serde::{Deserialize, Serialize}; use spacepackets::ecss::tc::IsPusTelecommand; use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; -use spacepackets::ecss::{EcssEnumeration, PusError, WritablePusPacket}; -use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; +use spacepackets::ecss::EcssEnumeration; +use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceCtrl}; use spacepackets::{SpHeader, MAX_APID}; pub use crate::seq_count::SeqCountProviderSimple; @@ -98,9 +103,14 @@ pub use spacepackets::ecss::verification::*; #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "alloc")))] pub use alloc_mod::*; +use crate::request::Apid; +use crate::ComponentId; + +/* #[cfg(feature = "std")] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "std")))] pub use std_mod::*; + */ /// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard. /// @@ -139,12 +149,34 @@ impl PartialEq for RequestId { impl RequestId { pub const SIZE_AS_BYTES: usize = size_of::(); + /// This allows extracting the request ID from a given PUS telecommand. + pub fn new(tc: &(impl CcsdsPacket + IsPusTelecommand)) -> Self { + Self::new_from_ccsds_tc(tc) + } + + /// Extract the request ID from a CCSDS TC packet. + pub fn new_from_ccsds_tc(tc: &impl CcsdsPacket) -> Self { + RequestId { + version_number: tc.ccsds_version(), + packet_id: tc.packet_id(), + psc: tc.psc(), + } + } + pub fn raw(&self) -> u32 { ((self.version_number as u32) << 29) | ((self.packet_id.raw() as u32) << 16) | self.psc.raw() as u32 } + pub fn packet_id(&self) -> PacketId { + self.packet_id + } + + pub fn packet_seq_ctrl(&self) -> PacketSequenceCtrl { + self.psc + } + pub fn to_bytes(&self, buf: &mut [u8]) { let raw = self.raw(); buf.copy_from_slice(raw.to_be_bytes().as_slice()); @@ -162,17 +194,23 @@ impl RequestId { }) } } -impl RequestId { - /// This allows extracting the request ID from a given PUS telecommand. - pub fn new(tc: &(impl CcsdsPacket + IsPusTelecommand)) -> Self { - RequestId { - version_number: tc.ccsds_version(), - packet_id: tc.packet_id(), - psc: tc.psc(), + +impl From for RequestId { + fn from(value: u32) -> Self { + Self { + version_number: ((value >> 29) & 0b111) as u8, + packet_id: PacketId::from(((value >> 16) & 0xffff) as u16), + psc: PacketSequenceCtrl::from((value & 0xffff) as u16), } } } +impl From for u32 { + fn from(value: RequestId) -> Self { + value.raw() + } +} + /// If a verification operation fails, the passed token will be returned as well. This allows /// re-trying the operation at a later point. #[derive(Debug, Clone)] @@ -186,12 +224,48 @@ impl From> for VerificationOrSendErrorWithToken VerificationOrSendErrorWithToken(value.0, value.1) } } + /// Support token to allow type-state programming. This prevents calling the verification /// steps in an invalid order. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct VerificationToken { state: PhantomData, - req_id: RequestId, + request_id: RequestId, +} + +impl VerificationToken { + fn new(req_id: RequestId) -> VerificationToken { + VerificationToken { + state: PhantomData, + request_id: req_id, + } + } + + pub fn request_id(&self) -> RequestId { + self.request_id + } +} + +impl VerificationToken { + /// Create a verification token with the accepted state. This can be useful for test purposes. + /// For general purposes, it is recommended to use the API exposed by verification handlers. + pub fn new_accepted_state(req_id: RequestId) -> VerificationToken { + VerificationToken { + state: PhantomData, + request_id: req_id, + } + } +} + +impl VerificationToken { + /// Create a verification token with the started state. This can be useful for test purposes. + /// For general purposes, it is recommended to use the API exposed by verification handlers. + pub fn new_started_state(req_id: RequestId) -> VerificationToken { + VerificationToken { + state: PhantomData, + request_id: req_id, + } + } } pub trait WasAtLeastAccepted {} @@ -219,6 +293,17 @@ pub enum TcStateToken { Completed(VerificationToken), } +impl TcStateToken { + pub fn request_id(&self) -> RequestId { + match self { + TcStateToken::None(token) => token.request_id(), + TcStateToken::Accepted(token) => token.request_id(), + TcStateToken::Started(token) => token.request_id(), + TcStateToken::Completed(token) => token.request_id(), + } + } +} + impl From> for TcStateToken { fn from(t: VerificationToken) -> Self { TcStateToken::None(t) @@ -267,24 +352,11 @@ impl From> for TcStateToken { } } -impl VerificationToken { - fn new(req_id: RequestId) -> VerificationToken { - VerificationToken { - state: PhantomData, - req_id, - } - } - - pub fn req_id(&self) -> RequestId { - self.req_id - } -} - /// Composite helper struct to pass failure parameters to the [VerificationReporter] pub struct FailParams<'stamp, 'fargs> { - time_stamp: &'stamp [u8], - failure_code: &'fargs dyn EcssEnumeration, - failure_data: &'fargs [u8], + pub time_stamp: &'stamp [u8], + pub failure_code: &'fargs dyn EcssEnumeration, + pub failure_data: &'fargs [u8], } impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> { @@ -310,8 +382,8 @@ impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> { /// Composite helper struct to pass step failure parameters to the [VerificationReporter] pub struct FailParamsWithStep<'stamp, 'fargs> { - bp: FailParams<'stamp, 'fargs>, - step: &'fargs dyn EcssEnumeration, + pub common: FailParams<'stamp, 'fargs>, + pub step: &'fargs dyn EcssEnumeration, } impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> { @@ -322,94 +394,26 @@ impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> { failure_data: &'fargs [u8], ) -> Self { Self { - bp: FailParams::new(time_stamp, failure_code, failure_data), + common: FailParams::new(time_stamp, failure_code, failure_data), step, } } } -#[derive(Clone)] -pub struct VerificationReporterCore { - pub dest_id: u16, - apid: u16, -} - -pub enum VerifSuccess {} -pub enum VerifFailure {} - -/// Abstraction for a sendable PUS TM. The user is expected to send the TM packet to a TM sink. +/// This is a generic trait implemented by an object which can perform the ECSS PUS 1 verification +/// process according to PUS standard ECSS-E-ST-70-41C. /// -/// This struct generally mutably borrows the source data buffer. -pub struct VerificationSendable<'src_data, State, SuccessOrFailure> { - token: Option>, - pus_tm: Option>, - phantom: PhantomData, -} - -impl<'src_data, State, SuccessOrFailure> VerificationSendable<'src_data, State, SuccessOrFailure> { - pub(crate) fn new(pus_tm: PusTmCreator<'src_data>, token: VerificationToken) -> Self { - Self { - token: Some(token), - pus_tm: Some(pus_tm), - phantom: PhantomData, - } - } - pub(crate) fn new_no_token(pus_tm: PusTmCreator<'src_data>) -> Self { - Self { - token: None, - pus_tm: Some(pus_tm), - phantom: PhantomData, - } - } - - pub fn len_packed(&self) -> usize { - self.pus_tm.as_ref().unwrap().len_written() - } - - pub fn pus_tm(&self) -> &PusTmCreator<'src_data> { - self.pus_tm.as_ref().unwrap() - } - - pub fn pus_tm_mut(&mut self) -> &mut PusTmCreator<'src_data> { - self.pus_tm.as_mut().unwrap() - } -} - -impl<'src_data, State> VerificationSendable<'src_data, State, VerifFailure> { - pub fn send_success_verif_failure(self) {} -} - -impl<'src_data, State> VerificationSendable<'src_data, State, VerifFailure> { - pub fn send_failure(self) -> (PusTmCreator<'src_data>, VerificationToken) { - (self.pus_tm.unwrap(), self.token.unwrap()) - } -} - -impl<'src_data> VerificationSendable<'src_data, TcStateNone, VerifSuccess> { - pub fn send_success_acceptance_success(self) -> VerificationToken { - VerificationToken { - state: PhantomData, - req_id: self.token.unwrap().req_id(), - } - } -} - -impl<'src_data> VerificationSendable<'src_data, TcStateAccepted, VerifSuccess> { - pub fn send_success_start_success(self) -> VerificationToken { - VerificationToken { - state: PhantomData, - req_id: self.token.unwrap().req_id(), - } - } -} - -impl<'src_data, TcState: WasAtLeastAccepted + Copy> - VerificationSendable<'src_data, TcState, VerifSuccess> -{ - pub fn send_success_step_or_completion_success(self) {} -} - +/// This trait allows using different message queue backends for the verification reporting process +/// or to swap the actual reporter with a test reporter for unit tests. +/// For general purposes, the [VerificationReporter] should be sufficient. pub trait VerificationReportingProvider { + /// It is generally assumed that the reporting provider is owned by some PUS service with + /// a unique ID. + fn owner_id(&self) -> ComponentId; + + fn set_apid(&mut self, apid: Apid); + fn apid(&self) -> Apid; + fn add_tc( &mut self, pus_tc: &(impl CcsdsPacket + IsPusTelecommand), @@ -421,30 +425,35 @@ pub trait VerificationReportingProvider { fn acceptance_success( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, time_stamp: &[u8], - ) -> Result, VerificationOrSendErrorWithToken>; + ) -> Result, EcssTmtcError>; fn acceptance_failure( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken>; + ) -> Result<(), EcssTmtcError>; fn start_success( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, time_stamp: &[u8], - ) -> Result, VerificationOrSendErrorWithToken>; + ) -> Result, EcssTmtcError>; fn start_failure( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken>; + ) -> Result<(), EcssTmtcError>; fn step_success( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: &VerificationToken, time_stamp: &[u8], step: impl EcssEnumeration, @@ -452,31 +461,40 @@ pub trait VerificationReportingProvider { fn step_failure( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParamsWithStep, - ) -> Result<(), VerificationOrSendErrorWithToken>; + ) -> Result<(), EcssTmtcError>; fn completion_success( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, time_stamp: &[u8], - ) -> Result<(), VerificationOrSendErrorWithToken>; + ) -> Result<(), EcssTmtcError>; fn completion_failure( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken>; + ) -> Result<(), EcssTmtcError>; } -/// Primary verification handler. It provides an API to send PUS 1 verification telemetry packets -/// and verify the various steps of telecommand handling as specified in the PUS standard. +/// Low level object which generates ECSS PUS 1 verification packets to verify the various steps +/// of telecommand handling as specified in the PUS standard. /// /// This is the core component which can be used without [`alloc`] support. Please note that /// the buffer passed to the API exposes by this struct will be used to serialize the source data. /// This buffer may not be re-used to serialize the whole telemetry because that would overwrite /// the source data itself. -impl VerificationReporterCore { +#[derive(Clone)] +pub struct VerificationReportCreator { + pub dest_id: u16, + apid: u16, +} + +impl VerificationReportCreator { pub fn new(apid: u16) -> Option { if apid > MAX_APID { return None; @@ -519,36 +537,30 @@ impl VerificationReporterCore { VerificationToken::::new(req_id) } - fn sendable_success_no_step<'src_data, State: Copy>( + fn success_verification_no_step<'time, 'src_data, State: Copy>( &self, src_data_buf: &'src_data mut [u8], subservice: u8, token: VerificationToken, seq_count: u16, msg_count: u16, - time_stamp: &'src_data [u8], - ) -> Result< - VerificationSendable<'src_data, State, VerifSuccess>, - VerificationErrorWithToken, - > { - Ok(VerificationSendable::new( - self.create_pus_verif_success_tm( - src_data_buf, - subservice, - seq_count, - msg_count, - &token.req_id, - time_stamp, - None::<&dyn EcssEnumeration>, - ) - .map_err(|e| VerificationErrorWithToken(e, token))?, - token, - )) + time_stamp: &'time [u8], + ) -> Result, ByteConversionError> { + let tm_creator = self.create_pus_verif_success_tm( + src_data_buf, + subservice, + seq_count, + msg_count, + &token.request_id(), + time_stamp, + None::<&dyn EcssEnumeration>, + )?; + Ok(tm_creator) } // Internal helper function, too many arguments is acceptable for this case. #[allow(clippy::too_many_arguments)] - fn sendable_failure_no_step<'src_data, State: Copy>( + fn failure_verification_no_step<'time, 'src_data, State: Copy>( &self, src_data_buf: &'src_data mut [u8], subservice: u8, @@ -556,85 +568,62 @@ impl VerificationReporterCore { seq_count: u16, msg_count: u16, step: Option<&(impl EcssEnumeration + ?Sized)>, - params: &FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, State, VerifFailure>, - VerificationErrorWithToken, - > { - Ok(VerificationSendable::new( - self.create_pus_verif_fail_tm( - src_data_buf, - subservice, - seq_count, - msg_count, - &token.req_id, - step, - params, - ) - .map_err(|e| VerificationErrorWithToken(e, token))?, - token, - )) + params: &FailParams<'time, '_>, + ) -> Result, ByteConversionError> { + let tm_creator = self.create_pus_verif_fail_tm( + src_data_buf, + subservice, + seq_count, + msg_count, + &token.request_id(), + step, + params, + )?; + Ok(tm_creator) } /// Package a PUS TM\[1, 1\] packet, see 8.1.2.1 of the PUS standard. - pub fn acceptance_success<'src_data>( + pub fn acceptance_success<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], token: VerificationToken, seq_count: u16, msg_count: u16, - time_stamp: &'src_data [u8], + time_stamp: &'time [u8], ) -> Result< - VerificationSendable<'src_data, TcStateNone, VerifSuccess>, - VerificationErrorWithToken, + ( + PusTmCreator<'time, 'src_data>, + VerificationToken, + ), + ByteConversionError, > { - self.sendable_success_no_step( + let tm_creator = self.success_verification_no_step( src_data_buf, Subservice::TmAcceptanceSuccess.into(), token, seq_count, msg_count, time_stamp, - ) - } - - pub fn send_acceptance_success( - &self, - mut sendable: VerificationSendable<'_, TcStateNone, VerifSuccess>, - sender: &(impl EcssTmSenderCore + ?Sized), - ) -> Result, VerificationOrSendErrorWithToken> - { - sender - .send_tm(sendable.pus_tm.take().unwrap().into()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - Ok(sendable.send_success_acceptance_success()) - } - - pub fn send_acceptance_failure( - &self, - mut sendable: VerificationSendable<'_, TcStateNone, VerifFailure>, - sender: &(impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap().into()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_verif_failure(); - Ok(()) + )?; + Ok(( + tm_creator, + VerificationToken { + state: PhantomData, + request_id: token.request_id(), + }, + )) } /// Package a PUS TM\[1, 2\] packet, see 8.1.2.2 of the PUS standard. - pub fn acceptance_failure<'src_data>( + pub fn acceptance_failure<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], token: VerificationToken, seq_count: u16, msg_count: u16, - params: FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcStateNone, VerifFailure>, - VerificationErrorWithToken, - > { - self.sendable_failure_no_step( + params: FailParams<'time, '_>, + ) -> Result, ByteConversionError> { + self.failure_verification_no_step( src_data_buf, Subservice::TmAcceptanceFailure.into(), token, @@ -648,55 +637,50 @@ impl VerificationReporterCore { /// Package and send a PUS TM\[1, 3\] packet, see 8.1.2.3 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::acceptance_success]. - pub fn start_success<'src_data>( + pub fn start_success<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], token: VerificationToken, seq_count: u16, msg_count: u16, - time_stamp: &'src_data [u8], + time_stamp: &'time [u8], ) -> Result< - VerificationSendable<'src_data, TcStateAccepted, VerifSuccess>, - VerificationErrorWithToken, + ( + PusTmCreator<'time, 'src_data>, + VerificationToken, + ), + ByteConversionError, > { - self.sendable_success_no_step( + let tm_creator = self.success_verification_no_step( src_data_buf, Subservice::TmStartSuccess.into(), token, seq_count, msg_count, time_stamp, - ) - } - - pub fn send_start_success( - &self, - mut sendable: VerificationSendable<'_, TcStateAccepted, VerifSuccess>, - sender: &(impl EcssTmSenderCore + ?Sized), - ) -> Result, VerificationOrSendErrorWithToken> - { - sender - .send_tm(sendable.pus_tm.take().unwrap().into()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - Ok(sendable.send_success_start_success()) + )?; + Ok(( + tm_creator, + VerificationToken { + state: PhantomData, + request_id: token.request_id(), + }, + )) } /// Package and send a PUS TM\[1, 4\] packet, see 8.1.2.4 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::acceptance_success]. It consumes /// the token because verification handling is done. - pub fn start_failure<'src_data>( + pub fn start_failure<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], token: VerificationToken, seq_count: u16, msg_count: u16, - params: FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcStateAccepted, VerifFailure>, - VerificationErrorWithToken, - > { - self.sendable_failure_no_step( + params: FailParams<'time, '_>, + ) -> Result, ByteConversionError> { + self.failure_verification_no_step( src_data_buf, Subservice::TmStartFailure.into(), token, @@ -707,89 +691,65 @@ impl VerificationReporterCore { ) } - pub fn send_start_failure( - &self, - mut sendable: VerificationSendable<'_, TcStateAccepted, VerifFailure>, - sender: &(impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap().into()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_verif_failure(); - Ok(()) - } - /// Package and send a PUS TM\[1, 5\] packet, see 8.1.2.5 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::start_success]. - pub fn step_success<'src_data>( + pub fn step_success<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], token: &VerificationToken, seq_count: u16, msg_count: u16, - time_stamp: &'src_data [u8], + time_stamp: &'time [u8], step: impl EcssEnumeration, - ) -> Result, EcssTmtcError> { - Ok(VerificationSendable::new_no_token( - self.create_pus_verif_success_tm( - src_data_buf, - Subservice::TmStepSuccess.into(), - seq_count, - msg_count, - &token.req_id, - time_stamp, - Some(&step), - )?, - )) + ) -> Result, ByteConversionError> { + self.create_pus_verif_success_tm( + src_data_buf, + Subservice::TmStepSuccess.into(), + seq_count, + msg_count, + &token.request_id(), + time_stamp, + Some(&step), + ) } /// Package and send a PUS TM\[1, 6\] packet, see 8.1.2.6 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::start_success]. It consumes the /// token because verification handling is done. - pub fn step_failure<'src_data>( + pub fn step_failure<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], token: VerificationToken, seq_count: u16, msg_count: u16, - params: FailParamsWithStep<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcStateStarted, VerifFailure>, - VerificationErrorWithToken, - > { - Ok(VerificationSendable::new( - self.create_pus_verif_fail_tm( - src_data_buf, - Subservice::TmStepFailure.into(), - seq_count, - msg_count, - &token.req_id, - Some(params.step), - ¶ms.bp, - ) - .map_err(|e| VerificationErrorWithToken(e, token))?, - token, - )) + params: FailParamsWithStep<'time, '_>, + ) -> Result, ByteConversionError> { + self.create_pus_verif_fail_tm( + src_data_buf, + Subservice::TmStepFailure.into(), + seq_count, + msg_count, + &token.request_id(), + Some(params.step), + ¶ms.common, + ) } /// Package and send a PUS TM\[1, 7\] packet, see 8.1.2.7 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::start_success]. It consumes the /// token because verification handling is done. - pub fn completion_success<'src_data, TcState: WasAtLeastAccepted + Copy>( + pub fn completion_success<'time, 'src_data, TcState: WasAtLeastAccepted + Copy>( &self, src_data_buf: &'src_data mut [u8], token: VerificationToken, seq_counter: u16, msg_counter: u16, - time_stamp: &'src_data [u8], - ) -> Result< - VerificationSendable<'src_data, TcState, VerifSuccess>, - VerificationErrorWithToken, - > { - self.sendable_success_no_step( + time_stamp: &'time [u8], + ) -> Result, ByteConversionError> { + self.success_verification_no_step( src_data_buf, Subservice::TmCompletionSuccess.into(), token, @@ -803,18 +763,15 @@ impl VerificationReporterCore { /// /// Requires a token previously acquired by calling [Self::start_success]. It consumes the /// token because verification handling is done. - pub fn completion_failure<'src_data, TcState: WasAtLeastAccepted + Copy>( + pub fn completion_failure<'time, 'src_data, TcState: WasAtLeastAccepted + Copy>( &self, src_data_buf: &'src_data mut [u8], token: VerificationToken, seq_count: u16, msg_count: u16, - params: FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcState, VerifFailure>, - VerificationErrorWithToken, - > { - self.sendable_failure_no_step( + params: FailParams<'time, '_>, + ) -> Result, ByteConversionError> { + self.failure_verification_no_step( src_data_buf, Subservice::TmCompletionFailure.into(), token, @@ -825,42 +782,18 @@ impl VerificationReporterCore { ) } - pub fn send_step_or_completion_success( - &self, - mut sendable: VerificationSendable<'_, TcState, VerifSuccess>, - sender: &(impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap().into()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_step_or_completion_success(); - Ok(()) - } - - pub fn send_step_or_completion_failure( - &self, - mut sendable: VerificationSendable<'_, TcState, VerifFailure>, - sender: &(impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap().into()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_verif_failure(); - Ok(()) - } - // Internal helper function, too many arguments is acceptable for this case. #[allow(clippy::too_many_arguments)] - fn create_pus_verif_success_tm<'src_data>( + fn create_pus_verif_success_tm<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], subservice: u8, seq_count: u16, msg_counter: u16, req_id: &RequestId, - time_stamp: &'src_data [u8], + time_stamp: &'time [u8], step: Option<&(impl EcssEnumeration + ?Sized)>, - ) -> Result, EcssTmtcError> { + ) -> Result, ByteConversionError> { let mut source_data_len = size_of::(); if let Some(step) = step { source_data_len += step.size(); @@ -874,12 +807,12 @@ impl VerificationReporterCore { step.write_to_be_bytes(&mut src_data_buf[idx..idx + step.size()]) .unwrap(); } - let mut sp_header = SpHeader::tm_unseg(self.apid(), seq_count, 0).unwrap(); + let sp_header = SpHeader::new_for_unseg_tm(self.apid(), seq_count, 0); Ok(self.create_pus_verif_tm_base( src_data_buf, subservice, msg_counter, - &mut sp_header, + sp_header, time_stamp, source_data_len, )) @@ -887,7 +820,7 @@ impl VerificationReporterCore { // Internal helper function, too many arguments is acceptable for this case. #[allow(clippy::too_many_arguments)] - fn create_pus_verif_fail_tm<'src_data>( + fn create_pus_verif_fail_tm<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], subservice: u8, @@ -895,8 +828,8 @@ impl VerificationReporterCore { msg_counter: u16, req_id: &RequestId, step: Option<&(impl EcssEnumeration + ?Sized)>, - params: &FailParams<'src_data, '_>, - ) -> Result, EcssTmtcError> { + params: &FailParams<'time, '_>, + ) -> Result, ByteConversionError> { let mut idx = 0; let mut source_data_len = RequestId::SIZE_AS_BYTES + params.failure_code.size(); if let Some(step) = step { @@ -914,32 +847,31 @@ impl VerificationReporterCore { } params .failure_code - .write_to_be_bytes(&mut src_data_buf[idx..idx + params.failure_code.size()]) - .map_err(PusError::ByteConversion)?; + .write_to_be_bytes(&mut src_data_buf[idx..idx + params.failure_code.size()])?; idx += params.failure_code.size(); src_data_buf[idx..idx + params.failure_data.len()].copy_from_slice(params.failure_data); - let mut sp_header = SpHeader::tm_unseg(self.apid(), seq_count, 0).unwrap(); + let sp_header = SpHeader::new_for_unseg_tm(self.apid(), seq_count, 0); Ok(self.create_pus_verif_tm_base( src_data_buf, subservice, msg_counter, - &mut sp_header, + sp_header, params.time_stamp, source_data_len, )) } - fn create_pus_verif_tm_base<'src_data>( + fn create_pus_verif_tm_base<'time, 'src_data>( &self, src_data_buf: &'src_data mut [u8], subservice: u8, msg_counter: u16, - sp_header: &mut SpHeader, - time_stamp: &'src_data [u8], + sp_header: SpHeader, + time_stamp: &'time [u8], source_data_len: usize, - ) -> PusTmCreator<'src_data> { + ) -> PusTmCreator<'time, 'src_data> { let tm_sec_header = - PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, Some(time_stamp)); + PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, time_stamp); PusTmCreator::new( sp_header, tm_sec_header, @@ -951,11 +883,10 @@ impl VerificationReporterCore { #[cfg(feature = "alloc")] pub mod alloc_mod { + use spacepackets::ecss::PusError; + use super::*; - use crate::{ - pus::{TmAsVecSenderWithId, TmInSharedPoolSenderWithId}, - seq_count::SequenceCountProvider, - }; + use crate::{pus::PusTmVariant, ComponentId}; use core::cell::RefCell; #[derive(Clone)] @@ -985,22 +916,46 @@ pub mod alloc_mod { } } - /// Primary verification handler. It provides an API to send PUS 1 verification telemetry packets - /// and verify the various steps of telecommand handling as specified in the PUS standard. - /// It is assumed that the sequence counter and message counters are updated in a central - /// TM funnel. This helper will always set those fields to 0. - #[derive(Clone)] - pub struct VerificationReporter { - source_data_buf: RefCell>, - pub seq_count_provider: Option + Send>>, - pub msg_count_provider: Option + Send>>, - pub reporter: VerificationReporterCore, + /// This trait allows hooking into the TM generation process of the [VerificationReporter]. + /// + /// The [Self::modify_tm] function is called before the TM is sent. This allows users to change + /// fields like the message count or sequence counter before the TM is sent. + pub trait VerificationHookProvider { + fn modify_tm(&self, tm: &mut PusTmCreator); } - impl VerificationReporter { - pub fn new(cfg: &VerificationReporterCfg) -> Self { - let reporter = VerificationReporterCore::new(cfg.apid).unwrap(); + /// [VerificationHookProvider] which does nothing. This is the default hook variant for + /// the [VerificationReporter], assuming that any necessary packet manipulation is performed by + /// a centralized TM funnel or inlet. + #[derive(Default, Copy, Clone)] + pub struct DummyVerificationHook {} + + impl VerificationHookProvider for DummyVerificationHook { + fn modify_tm(&self, _tm: &mut PusTmCreator) {} + } + + /// Primary verification reportewr object. It provides an API to send PUS 1 verification + /// telemetry packets and verify the various steps of telecommand handling as specified in the + /// PUS standard. + /// + /// It is assumed that the sequence counter and message counters are updated in a central + /// TM funnel or TM inlet. This helper will always set those fields to 0. The APID and + /// destination fields are assumed to be constant for a given repoter instance. + #[derive(Clone)] + pub struct VerificationReporter< + VerificationHook: VerificationHookProvider = DummyVerificationHook, + > { + owner_id: ComponentId, + source_data_buf: RefCell>, + pub reporter_creator: VerificationReportCreator, + pub tm_hook: VerificationHook, + } + + impl VerificationReporter { + pub fn new(owner_id: ComponentId, cfg: &VerificationReporterCfg) -> Self { + let reporter = VerificationReportCreator::new(cfg.apid).unwrap(); Self { + owner_id, source_data_buf: RefCell::new(alloc::vec![ 0; RequestId::SIZE_AS_BYTES @@ -1008,14 +963,37 @@ pub mod alloc_mod { + cfg.fail_code_field_width + cfg.max_fail_data_len ]), - seq_count_provider: None, - msg_count_provider: None, - reporter, + reporter_creator: reporter, + tm_hook: DummyVerificationHook::default(), + } + } + } + + impl VerificationReporter { + /// The provided [VerificationHookProvider] can be used to modify a verification packet + /// before it is sent. + pub fn new_with_hook( + owner_id: ComponentId, + cfg: &VerificationReporterCfg, + tm_hook: VerificationHook, + ) -> Self { + let reporter = VerificationReportCreator::new(cfg.apid).unwrap(); + Self { + owner_id, + source_data_buf: RefCell::new(alloc::vec![ + 0; + RequestId::SIZE_AS_BYTES + + cfg.step_field_width + + cfg.fail_code_field_width + + cfg.max_fail_data_len + ]), + reporter_creator: reporter, + tm_hook, } } delegate!( - to self.reporter { + to self.reporter_creator { pub fn set_apid(&mut self, apid: u16) -> bool; pub fn apid(&self) -> u16; pub fn add_tc(&mut self, pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> VerificationToken; @@ -1028,618 +1006,677 @@ pub mod alloc_mod { pub fn allowed_source_data_len(&self) -> usize { self.source_data_buf.borrow().capacity() } + } + + impl VerificationReportingProvider + for VerificationReporter + { + delegate!( + to self.reporter_creator { + fn set_apid(&mut self, apid: Apid); + fn apid(&self) -> Apid; + fn add_tc(&mut self, pus_tc: &(impl CcsdsPacket + IsPusTelecommand)) -> VerificationToken; + fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken; + } + ); + + fn owner_id(&self) -> ComponentId { + self.owner_id + } /// Package and send a PUS TM\[1, 1\] packet, see 8.1.2.1 of the PUS standard - pub fn acceptance_success( + fn acceptance_success( &self, - token: VerificationToken, sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, time_stamp: &[u8], - ) -> Result, VerificationOrSendErrorWithToken> - { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); + ) -> Result, EcssTmtcError> { let mut source_data_buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.acceptance_success( - source_data_buf.as_mut_slice(), - token, - seq_count, - msg_count, - time_stamp, - )?; - self.reporter.send_acceptance_success(sendable, sender) + let (mut tm_creator, token) = self + .reporter_creator + .acceptance_success(source_data_buf.as_mut_slice(), token, 0, 0, time_stamp) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?; + Ok(token) } /// Package and send a PUS TM\[1, 2\] packet, see 8.1.2.2 of the PUS standard - pub fn acceptance_failure( + fn acceptance_failure( &self, - token: VerificationToken, sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); + ) -> Result<(), EcssTmtcError> { let mut buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.acceptance_failure( - buf.as_mut_slice(), - token, - seq_count, - msg_count, - params, - )?; - self.reporter.send_acceptance_failure(sendable, sender) + let mut tm_creator = self + .reporter_creator + .acceptance_failure(buf.as_mut_slice(), token, 0, 0, params) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?; + Ok(()) } /// Package and send a PUS TM\[1, 3\] packet, see 8.1.2.3 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::acceptance_success]. - pub fn start_success( + fn start_success( &self, - token: VerificationToken, sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, time_stamp: &[u8], - ) -> Result< - VerificationToken, - VerificationOrSendErrorWithToken, - > { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); + ) -> Result, EcssTmtcError> { let mut buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.start_success( - buf.as_mut_slice(), - token, - seq_count, - msg_count, - time_stamp, - )?; - self.reporter.send_start_success(sendable, sender) + let (mut tm_creator, started_token) = self + .reporter_creator + .start_success(buf.as_mut_slice(), token, 0, 0, time_stamp) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?; + Ok(started_token) } /// Package and send a PUS TM\[1, 4\] packet, see 8.1.2.4 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::acceptance_success]. It consumes /// the token because verification handling is done. - pub fn start_failure( + fn start_failure( &self, - token: VerificationToken, sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); + ) -> Result<(), EcssTmtcError> { let mut buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.start_failure( - buf.as_mut_slice(), - token, - seq_count, - msg_count, - params, - )?; - self.reporter.send_start_failure(sendable, sender) + let mut tm_creator = self + .reporter_creator + .start_failure(buf.as_mut_slice(), token, 0, 0, params) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?; + Ok(()) } /// Package and send a PUS TM\[1, 5\] packet, see 8.1.2.5 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::start_success]. - pub fn step_success( + fn step_success( &self, - token: &VerificationToken, sender: &(impl EcssTmSenderCore + ?Sized), + token: &VerificationToken, time_stamp: &[u8], step: impl EcssEnumeration, ) -> Result<(), EcssTmtcError> { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); let mut buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.step_success( - buf.as_mut_slice(), - token, - seq_count, - msg_count, - time_stamp, - step, - )?; - self.reporter - .send_step_or_completion_success(sendable, sender) - .map_err(|e| e.0) + let mut tm_creator = self + .reporter_creator + .step_success(buf.as_mut_slice(), token, 0, 0, time_stamp, step) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?; + Ok(()) } /// Package and send a PUS TM\[1, 6\] packet, see 8.1.2.6 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::start_success]. It consumes the /// token because verification handling is done. - pub fn step_failure( + fn step_failure( &self, - token: VerificationToken, sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, params: FailParamsWithStep, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); + ) -> Result<(), EcssTmtcError> { let mut buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.step_failure( - buf.as_mut_slice(), - token, - seq_count, - msg_count, - params, - )?; - self.reporter - .send_step_or_completion_failure(sendable, sender) + let mut tm_creator = self + .reporter_creator + .step_failure(buf.as_mut_slice(), token, 0, 0, params) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?; + Ok(()) } /// Package and send a PUS TM\[1, 7\] packet, see 8.1.2.7 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::start_success]. It consumes the /// token because verification handling is done. - pub fn completion_success( + fn completion_success( &self, - token: VerificationToken, + // sender_id: ComponentId, sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, time_stamp: &[u8], - ) -> Result<(), VerificationOrSendErrorWithToken> { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); + ) -> Result<(), EcssTmtcError> { let mut buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.completion_success( - buf.as_mut_slice(), - token, - seq_count, - msg_count, - time_stamp, - )?; - self.reporter - .send_step_or_completion_success(sendable, sender) + let mut tm_creator = self + .reporter_creator + .completion_success(buf.as_mut_slice(), token, 0, 0, time_stamp) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id, PusTmVariant::Direct(tm_creator))?; + Ok(()) } /// Package and send a PUS TM\[1, 8\] packet, see 8.1.2.8 of the PUS standard. /// /// Requires a token previously acquired by calling [Self::start_success]. It consumes the /// token because verification handling is done. - pub fn completion_failure( - &self, - token: VerificationToken, - sender: &(impl EcssTmSenderCore + ?Sized), - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let seq_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let msg_count = self - .seq_count_provider - .as_ref() - .map_or(0, |v| v.get_and_increment()); - let mut buf = self.source_data_buf.borrow_mut(); - let sendable = self.reporter.completion_failure( - buf.as_mut_slice(), - token, - seq_count, - msg_count, - params, - )?; - self.reporter - .send_step_or_completion_failure(sendable, sender) - } - } - - /// Helper object which caches the sender passed as a trait object. Provides the same - /// API as [VerificationReporter] but without the explicit sender arguments. - #[derive(Clone)] - pub struct VerificationReporterWithSender { - pub reporter: VerificationReporter, - pub sender: Sender, - } - - impl VerificationReporterWithSender { - pub fn new(cfg: &VerificationReporterCfg, sender: Sender) -> Self { - let reporter = VerificationReporter::new(cfg); - Self::new_from_reporter(reporter, sender) - } - - pub fn new_from_reporter(reporter: VerificationReporter, sender: Sender) -> Self { - Self { reporter, sender } - } - - delegate! { - to self.reporter { - pub fn set_apid(&mut self, apid: u16) -> bool; - pub fn apid(&self) -> u16; - pub fn dest_id(&self) -> u16; - pub fn set_dest_id(&mut self, dest_id: u16); - } - } - } - - impl VerificationReportingProvider - for VerificationReporterWithSender - { - delegate! { - to self.reporter { - fn add_tc( - &mut self, - pus_tc: &(impl CcsdsPacket + IsPusTelecommand), - ) -> VerificationToken; - fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken; - } - } - - fn acceptance_success( - &self, - token: VerificationToken, - time_stamp: &[u8], - ) -> Result, VerificationOrSendErrorWithToken> - { - self.reporter - .acceptance_success(token, &self.sender, time_stamp) - } - - fn acceptance_failure( - &self, - token: VerificationToken, - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .acceptance_failure(token, &self.sender, params) - } - - fn start_success( - &self, - token: VerificationToken, - time_stamp: &[u8], - ) -> Result< - VerificationToken, - VerificationOrSendErrorWithToken, - > { - self.reporter.start_success(token, &self.sender, time_stamp) - } - - fn start_failure( - &self, - token: VerificationToken, - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter.start_failure(token, &self.sender, params) - } - - fn step_success( - &self, - token: &VerificationToken, - time_stamp: &[u8], - step: impl EcssEnumeration, - ) -> Result<(), EcssTmtcError> { - self.reporter - .step_success(token, &self.sender, time_stamp, step) - } - - fn step_failure( - &self, - token: VerificationToken, - params: FailParamsWithStep, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter.step_failure(token, &self.sender, params) - } - - fn completion_success( - &self, - token: VerificationToken, - time_stamp: &[u8], - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .completion_success(token, &self.sender, time_stamp) - } - fn completion_failure( &self, + sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .completion_failure(token, &self.sender, params) + ) -> Result<(), EcssTmtcError> { + let mut buf = self.source_data_buf.borrow_mut(); + let mut tm_creator = self + .reporter_creator + .completion_failure(buf.as_mut_slice(), token, 0, 00, params) + .map_err(PusError::ByteConversion)?; + self.tm_hook.modify_tm(&mut tm_creator); + sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?; + Ok(()) } } - - pub type VerificationReporterWithSharedPoolSender = - VerificationReporterWithSender>; - pub type VerificationReporterWithVecSender = - VerificationReporterWithSender>; } +/* #[cfg(feature = "std")] pub mod std_mod { use std::sync::mpsc; use crate::pool::StoreAddr; + use crate::pus::verification::VerificationReporterWithSender; - use super::alloc_mod::{ - VerificationReporterWithSharedPoolSender, VerificationReporterWithVecSender, - }; + use super::alloc_mod::VerificationReporterWithSharedPoolSender; pub type VerificationReporterWithSharedPoolMpscSender = VerificationReporterWithSharedPoolSender>; pub type VerificationReporterWithSharedPoolMpscBoundedSender = VerificationReporterWithSharedPoolSender>; pub type VerificationReporterWithVecMpscSender = - VerificationReporterWithVecSender>>; + VerificationReporterWithSender>>; pub type VerificationReporterWithVecMpscBoundedSender = - VerificationReporterWithVecSender>>; + VerificationReporterWithSender>>; } + */ -#[cfg(test)] -pub mod tests { - use crate::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig}; - use crate::pus::tests::CommonTmInfo; - use crate::pus::verification::{ - EcssTmSenderCore, EcssTmtcError, FailParams, FailParamsWithStep, RequestId, TcStateNone, - VerificationReporter, VerificationReporterCfg, VerificationReporterWithSender, - VerificationToken, - }; - use crate::pus::{ - EcssChannel, PusTmWrapper, TmInSharedPoolSenderWithId, TmInSharedPoolSenderWithMpsc, - }; - use crate::tmtc::tm_helper::SharedTmPool; - use crate::ChannelId; - use alloc::format; - use alloc::sync::Arc; - use hashbrown::HashMap; - use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader}; - use spacepackets::ecss::tm::PusTmReader; - use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, PusError, PusPacket}; - use spacepackets::util::UnsignedEnum; - use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader}; - use std::cell::RefCell; +#[cfg(any(feature = "test_util", test))] +pub mod test_util { + use alloc::vec::Vec; + use core::cell::RefCell; use std::collections::VecDeque; - use std::sync::{mpsc, Mutex}; - use std::time::Duration; - use std::vec; - use std::vec::Vec; - use super::VerificationReportingProvider; + use super::*; - fn is_send(_: &T) {} - #[allow(dead_code)] - fn is_sync(_: &T) {} - - pub struct VerificationStatus { - pub accepted: Option, - pub started: Option, - pub step: u64, - pub step_status: Option, - pub completed: Option, - pub failure_data: Option>, - pub fail_enum: Option, + #[derive(Debug, PartialEq)] + pub struct SuccessData { + pub sender: ComponentId, + pub time_stamp: Vec, } - pub type SharedVerificationMap = Arc>>>; + #[derive(Debug, PartialEq)] + pub struct FailureData { + pub sender: ComponentId, + pub error_enum: u64, + pub fail_data: Vec, + pub time_stamp: Vec, + } + + #[derive(Debug, PartialEq)] + pub enum VerificationReportInfo { + Added, + AcceptanceSuccess(SuccessData), + AcceptanceFailure(FailureData), + StartedSuccess(SuccessData), + StartedFailure(FailureData), + StepSuccess { data: SuccessData, step: u16 }, + StepFailure(FailureData), + CompletionSuccess(SuccessData), + CompletionFailure(FailureData), + } - #[derive(Clone)] pub struct TestVerificationReporter { - pub verification_map: SharedVerificationMap, + pub id: ComponentId, + pub report_queue: RefCell>, } impl TestVerificationReporter { - pub fn new(verification_map: SharedVerificationMap) -> Self { - Self { verification_map } + pub fn new(id: ComponentId) -> Self { + Self { + id, + report_queue: Default::default(), + } } } impl VerificationReportingProvider for TestVerificationReporter { + fn set_apid(&mut self, _apid: Apid) {} + + fn apid(&self) -> Apid { + 0 + } + fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken { - let verif_map = self.verification_map.lock().unwrap(); - verif_map.borrow_mut().insert( - req_id, - VerificationStatus { - accepted: None, - started: None, - step: 0, - step_status: None, - completed: None, - failure_data: None, - fail_enum: None, - }, - ); + self.report_queue + .borrow_mut() + .push_back((req_id, VerificationReportInfo::Added)); VerificationToken { - state: core::marker::PhantomData, - req_id, + state: PhantomData, + request_id: req_id, } } fn acceptance_success( &self, + _sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, - _time_stamp: &[u8], - ) -> Result< - VerificationToken, - super::VerificationOrSendErrorWithToken, - > { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => entry.accepted = Some(true), - None => panic!( - "unexpected acceptance success for request ID {}", - token.req_id() - ), - }; + time_stamp: &[u8], + ) -> Result, EcssTmtcError> { + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::AcceptanceSuccess(SuccessData { + sender: self.owner_id(), + time_stamp: time_stamp.to_vec(), + }), + )); Ok(VerificationToken { - state: core::marker::PhantomData, - req_id: token.req_id, + state: PhantomData, + request_id: token.request_id, }) } fn acceptance_failure( &self, + _sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParams, - ) -> Result<(), super::VerificationOrSendErrorWithToken> { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => { - entry.accepted = Some(false); - entry.failure_data = Some(params.failure_data.to_vec()); - entry.fail_enum = Some(params.failure_code.value()); - } - None => panic!( - "unexpected acceptance failure for request ID {}", - token.req_id() - ), - }; + ) -> Result<(), EcssTmtcError> { + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::AcceptanceFailure(FailureData { + sender: self.owner_id(), + error_enum: params.failure_code.value(), + fail_data: params.failure_data.to_vec(), + time_stamp: params.time_stamp.to_vec(), + }), + )); Ok(()) } fn start_success( &self, - token: VerificationToken, - _time_stamp: &[u8], - ) -> Result< - VerificationToken, - super::VerificationOrSendErrorWithToken, - > { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => entry.started = Some(true), - None => panic!("unexpected start success for request ID {}", token.req_id()), - }; + _sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, + time_stamp: &[u8], + ) -> Result, EcssTmtcError> { + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::StartedSuccess(SuccessData { + sender: self.owner_id(), + time_stamp: time_stamp.to_vec(), + }), + )); Ok(VerificationToken { - state: core::marker::PhantomData, - req_id: token.req_id, + state: PhantomData, + request_id: token.request_id, }) } fn start_failure( &self, + _sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParams, - ) -> Result<(), super::VerificationOrSendErrorWithToken> { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => { - entry.started = Some(false); - entry.failure_data = Some(params.failure_data.to_vec()); - entry.fail_enum = Some(params.failure_code.value()); - } - None => panic!("unexpected start failure for request ID {}", token.req_id()), - }; + ) -> Result<(), EcssTmtcError> { + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::StartedFailure(FailureData { + sender: self.owner_id(), + error_enum: params.failure_code.value(), + fail_data: params.failure_data.to_vec(), + time_stamp: params.time_stamp.to_vec(), + }), + )); Ok(()) } fn step_success( &self, - token: &VerificationToken, - _time_stamp: &[u8], - step: impl spacepackets::ecss::EcssEnumeration, + _sender: &(impl EcssTmSenderCore + ?Sized), + token: &VerificationToken, + time_stamp: &[u8], + step: impl EcssEnumeration, ) -> Result<(), EcssTmtcError> { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => { - entry.step = step.value(); - entry.step_status = Some(true); - } - None => panic!("unexpected start success for request ID {}", token.req_id()), - }; + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::StepSuccess { + data: SuccessData { + sender: self.owner_id(), + time_stamp: time_stamp.to_vec(), + }, + step: step.value() as u16, + }, + )); Ok(()) } fn step_failure( &self, - token: VerificationToken, - _params: FailParamsWithStep, - ) -> Result<(), super::VerificationOrSendErrorWithToken> { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => { - entry.step_status = Some(false); - } - None => panic!("unexpected start success for request ID {}", token.req_id()), - }; + _sender: &(impl EcssTmSenderCore + ?Sized), + token: VerificationToken, + params: FailParamsWithStep, + ) -> Result<(), EcssTmtcError> { + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::StepFailure(FailureData { + sender: self.owner_id(), + error_enum: params.common.failure_code.value(), + fail_data: params.common.failure_data.to_vec(), + time_stamp: params.common.time_stamp.to_vec(), + }), + )); Ok(()) } fn completion_success( &self, + _sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, - _time_stamp: &[u8], - ) -> Result<(), super::VerificationOrSendErrorWithToken> { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => entry.completed = Some(true), - None => panic!( - "unexpected acceptance success for request ID {}", - token.req_id() - ), - }; + time_stamp: &[u8], + ) -> Result<(), EcssTmtcError> { + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::CompletionSuccess(SuccessData { + sender: self.owner_id(), + time_stamp: time_stamp.to_vec(), + }), + )); Ok(()) } - fn completion_failure( + fn completion_failure( &self, + _sender: &(impl EcssTmSenderCore + ?Sized), token: VerificationToken, params: FailParams, - ) -> Result<(), super::VerificationOrSendErrorWithToken> { - let verif_map = self.verification_map.lock().unwrap(); - match verif_map.borrow_mut().get_mut(&token.req_id) { - Some(entry) => { - entry.completed = Some(false); - entry.failure_data = Some(params.failure_data.to_vec()); - entry.fail_enum = Some(params.failure_code.value()); - } - None => panic!( - "unexpected acceptance success for request ID {}", - token.req_id() - ), - }; + ) -> Result<(), EcssTmtcError> { + self.report_queue.borrow_mut().push_back(( + token.request_id(), + VerificationReportInfo::CompletionFailure(FailureData { + sender: self.owner_id(), + error_enum: params.failure_code.value(), + fail_data: params.failure_data.to_vec(), + time_stamp: params.time_stamp.to_vec(), + }), + )); Ok(()) } + + fn owner_id(&self) -> ComponentId { + self.id + } } - const TEST_APID: u16 = 0x02; + impl TestVerificationReporter { + pub fn check_next_was_added(&self, request_id: RequestId) { + let (last_report_req_id, info) = self + .report_queue + .borrow_mut() + .pop_front() + .expect("report queue is empty"); + assert_eq!(request_id, last_report_req_id); + assert_eq!(info, VerificationReportInfo::Added); + } + pub fn check_next_is_acceptance_success(&self, sender_id: ComponentId, req_id: RequestId) { + let (last_report_req_id, info) = self + .report_queue + .borrow_mut() + .pop_front() + .expect("report queue is empty"); + assert_eq!(req_id, last_report_req_id); + if let VerificationReportInfo::AcceptanceSuccess(data) = info { + assert_eq!(data.sender, sender_id); + return; + } + panic!("next message is not acceptance success message") + } + + pub fn check_next_is_started_success(&self, sender_id: ComponentId, req_id: RequestId) { + let (last_report_req_id, info) = self + .report_queue + .borrow_mut() + .pop_front() + .expect("report queue is empty"); + assert_eq!(req_id, last_report_req_id); + if let VerificationReportInfo::StartedSuccess(data) = info { + assert_eq!(data.sender, sender_id); + return; + } + panic!("next message is not start success message") + } + + pub fn check_next_is_step_success( + &self, + sender_id: ComponentId, + request_id: RequestId, + expected_step: u16, + ) { + let (last_report_req_id, info) = self + .report_queue + .borrow_mut() + .pop_front() + .expect("report queue is empty"); + assert_eq!(request_id, last_report_req_id); + if let VerificationReportInfo::StepSuccess { data, step } = info { + assert_eq!(data.sender, sender_id); + assert_eq!(expected_step, step); + return; + } + panic!("next message is not step success message: {info:?}") + } + + pub fn check_next_is_step_failure( + &self, + sender_id: ComponentId, + request_id: RequestId, + error_code: u64, + ) { + let (last_report_req_id, info) = self + .report_queue + .borrow_mut() + .pop_front() + .expect("report queue is empty"); + assert_eq!(request_id, last_report_req_id); + if let VerificationReportInfo::StepFailure(data) = info { + assert_eq!(data.sender, sender_id); + assert_eq!(data.error_enum, error_code); + return; + } + panic!("next message is not step failure message") + } + + pub fn check_next_is_completion_success( + &self, + sender_id: ComponentId, + request_id: RequestId, + ) { + let (last_report_req_id, info) = self + .report_queue + .borrow_mut() + .pop_front() + .expect("report queue is empty"); + assert_eq!(request_id, last_report_req_id); + if let VerificationReportInfo::CompletionSuccess(data) = info { + assert_eq!(data.sender, sender_id); + return; + } + panic!("next message is not completion success message: {info:?}") + } + + pub fn check_next_is_completion_failure( + &mut self, + sender_id: ComponentId, + request_id: RequestId, + error_code: u64, + ) { + let (last_report_req_id, info) = self + .report_queue + .get_mut() + .pop_front() + .expect("report queue is empty"); + assert_eq!(request_id, last_report_req_id); + if let VerificationReportInfo::CompletionFailure(data) = info { + assert_eq!(data.sender, sender_id); + assert_eq!(data.error_enum, error_code); + return; + } + panic!("next message is not completion failure message: {info:?}") + } + + pub fn assert_full_completion_success( + &mut self, + sender_id: ComponentId, + request_id: RequestId, + expected_steps: Option, + ) { + self.check_next_was_added(request_id); + self.check_next_is_acceptance_success(sender_id, request_id); + self.check_next_is_started_success(sender_id, request_id); + if let Some(highest_num) = expected_steps { + for i in 0..highest_num { + self.check_next_is_step_success(sender_id, request_id, i); + } + } + self.check_next_is_completion_success(sender_id, request_id); + } + + pub fn assert_completion_failure( + &mut self, + sender_id: ComponentId, + request_id: RequestId, + expected_steps: Option, + error_code: u64, + ) { + self.check_next_was_added(request_id); + self.check_next_is_acceptance_success(sender_id, request_id); + self.check_next_is_started_success(sender_id, request_id); + if let Some(highest_num) = expected_steps { + for i in 0..highest_num { + self.check_next_is_step_success(sender_id, request_id, i); + } + } + self.check_next_is_completion_failure(sender_id, request_id, error_code); + } + + pub fn get_next_verification_message(&mut self) -> (RequestId, VerificationReportInfo) { + self.report_queue + .get_mut() + .pop_front() + .expect("report queue is empty") + } + /* + pub fn verification_info(&self, req_id: &RequestId) -> Option { + let verif_map = self.verification_map.lock().unwrap(); + let value = verif_map.borrow().get(req_id).cloned(); + value + } + + + pub fn check_started(&self, req_id: &RequestId) -> bool { + let verif_map = self.verification_map.lock().unwrap(); + if let Some(entry) = verif_map.borrow().get(req_id) { + return entry.started.unwrap_or(false); + } + false + } + + fn generic_completion_checks( + entry: &VerificationStatus, + step: Option, + completion_success: bool, + ) { + assert!(entry.accepted.unwrap()); + assert!(entry.started.unwrap()); + if let Some(step) = step { + assert!(entry.step_status.unwrap()); + assert_eq!(entry.step, step); + } else { + assert!(entry.step_status.is_none()); + } + assert_eq!(entry.completed.unwrap(), completion_success); + } + + + pub fn assert_completion_failure( + &self, + req_id: &RequestId, + step: Option, + error_code: u64, + ) { + let verif_map = self.verification_map.lock().unwrap(); + if let Some(entry) = verif_map.borrow().get(req_id) { + Self::generic_completion_checks(entry, step, false); + assert_eq!(entry.fail_enum.unwrap(), error_code); + return; + } + panic!("request not in verification map"); + } + + pub fn completion_status(&self, req_id: &RequestId) -> Option { + let verif_map = self.verification_map.lock().unwrap(); + if let Some(entry) = verif_map.borrow().get(req_id) { + return entry.completed; + } + panic!("request not in verification map"); + } + */ + } +} + +#[cfg(test)] +pub mod tests { + use crate::pool::{StaticMemoryPool, StaticPoolConfig}; + use crate::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0}; + use crate::pus::tests::CommonTmInfo; + use crate::pus::verification::{ + EcssTmSenderCore, EcssTmtcError, FailParams, FailParamsWithStep, RequestId, TcStateNone, + VerificationReporter, VerificationReporterCfg, VerificationToken, + }; + use crate::pus::{ChannelWithId, MpscTmInSharedPoolSender, PusTmVariant}; + use crate::request::MessageMetadata; + use crate::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore}; + use crate::tmtc::tm_helper::SharedTmPool; + use crate::ComponentId; + use alloc::format; + use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader}; + use spacepackets::ecss::{ + EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusError, PusPacket, + WritablePusPacket, + }; + use spacepackets::util::UnsignedEnum; + use spacepackets::{ByteConversionError, SpHeader}; + use std::cell::RefCell; + use std::collections::VecDeque; + use std::sync::mpsc; + use std::vec; + use std::vec::Vec; + + use super::{ + DummyVerificationHook, SeqCountProviderSimple, TcStateAccepted, TcStateStarted, + VerificationHookProvider, VerificationReportingProvider, WasAtLeastAccepted, + }; + + fn is_send(_: &T) {} + #[allow(dead_code)] + fn is_sync(_: &T) {} + const EMPTY_STAMP: [u8; 7] = [0; 7]; #[derive(Debug, Eq, PartialEq, Clone)] struct TmInfo { + pub requestor: MessageMetadata, pub common: CommonTmInfo, - pub req_id: RequestId, pub additional_data: Option>, } @@ -1648,8 +1685,8 @@ pub mod tests { pub service_queue: RefCell>, } - impl EcssChannel for TestSender { - fn channel_id(&self) -> ChannelId { + impl ChannelWithId for TestSender { + fn id(&self) -> ComponentId { 0 } fn name(&self) -> &'static str { @@ -1658,12 +1695,12 @@ pub mod tests { } impl EcssTmSenderCore for TestSender { - fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> { + fn send_tm(&self, sender_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> { match tm { - PusTmWrapper::InStore(_) => { + PusTmVariant::InStore(_) => { panic!("TestSender: Can not deal with addresses"); } - PusTmWrapper::Direct(tm) => { + PusTmVariant::Direct(tm) => { assert_eq!(PusPacket::service(&tm), 1); assert!(!tm.source_data().is_empty()); let mut time_stamp = [0; 7]; @@ -1679,8 +1716,8 @@ pub mod tests { vec = Some(new_vec); } self.service_queue.borrow_mut().push_back(TmInfo { + requestor: MessageMetadata::new(req_id.into(), sender_id), common: CommonTmInfo::new_from_tm(&tm), - req_id, additional_data: vec, }); Ok(()) @@ -1689,464 +1726,575 @@ pub mod tests { } } - struct TestBase<'a> { - vr: VerificationReporter, - #[allow(dead_code)] - tc: PusTcCreator<'a>, + #[derive(Default)] + pub struct SequenceCounterHook { + pub seq_counter: CcsdsSimpleSeqCountProvider, + pub msg_counter: SeqCountProviderSimple, } - impl<'a> TestBase<'a> { - fn rep(&mut self) -> &mut VerificationReporter { - &mut self.vr - } - } - struct TestBaseWithHelper<'a, Sender: EcssTmSenderCore + Clone + 'static> { - helper: VerificationReporterWithSender, - #[allow(dead_code)] - tc: PusTcCreator<'a>, - } - - impl<'a, Sender: EcssTmSenderCore + Clone + 'static> TestBaseWithHelper<'a, Sender> { - fn rep(&mut self) -> &mut VerificationReporter { - &mut self.helper.reporter + impl VerificationHookProvider for SequenceCounterHook { + fn modify_tm(&self, tm: &mut spacepackets::ecss::tm::PusTmCreator) { + tm.set_seq_count(self.seq_counter.get_and_increment()); + tm.set_msg_counter(self.msg_counter.get_and_increment()); } } - fn base_reporter() -> VerificationReporter { + struct VerificationReporterTestbench< + VerificationHook: VerificationHookProvider = DummyVerificationHook, + > { + pub id: ComponentId, + sender: TestSender, + reporter: VerificationReporter, + pub request_id: RequestId, + tc: Vec, + } + + fn base_reporter(id: ComponentId) -> VerificationReporter { let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); - VerificationReporter::new(&cfg) + VerificationReporter::new(id, &cfg) } - fn base_tc_init(app_data: Option<&[u8]>) -> (PusTcCreator, RequestId) { - let mut sph = SpHeader::tc_unseg(TEST_APID, 0x34, 0).unwrap(); + fn reporter_with_hook( + id: ComponentId, + hook: VerificationHook, + ) -> VerificationReporter { + let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); + VerificationReporter::new_with_hook(id, &cfg, hook) + } + + impl VerificationReporterTestbench { + fn new_with_hook(id: ComponentId, tc: PusTcCreator, tm_hook: VerificiationHook) -> Self { + let reporter = reporter_with_hook(id, tm_hook); + Self { + id, + sender: TestSender::default(), + reporter, + request_id: RequestId::new(&tc), + tc: tc.to_vec().unwrap(), + } + } + + #[allow(dead_code)] + fn set_dest_id(&mut self, dest_id: u16) { + self.reporter.set_dest_id(dest_id); + } + + fn init(&mut self) -> VerificationToken { + self.reporter.add_tc(&PusTcReader::new(&self.tc).unwrap().0) + } + + fn acceptance_success( + &self, + token: VerificationToken, + time_stamp: &[u8], + ) -> Result, EcssTmtcError> { + self.reporter + .acceptance_success(&self.sender, token, time_stamp) + } + + fn acceptance_failure( + &self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), EcssTmtcError> { + self.reporter + .acceptance_failure(&self.sender, token, params) + } + + fn start_success( + &self, + token: VerificationToken, + time_stamp: &[u8], + ) -> Result, EcssTmtcError> { + self.reporter.start_success(&self.sender, token, time_stamp) + } + + fn start_failure( + &self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), EcssTmtcError> { + self.reporter.start_failure(&self.sender, token, params) + } + + fn step_success( + &self, + token: &VerificationToken, + time_stamp: &[u8], + step: impl EcssEnumeration, + ) -> Result<(), EcssTmtcError> { + self.reporter + .step_success(&self.sender, token, time_stamp, step) + } + + fn step_failure( + &self, + token: VerificationToken, + params: FailParamsWithStep, + ) -> Result<(), EcssTmtcError> { + self.reporter.step_failure(&self.sender, token, params) + } + + fn completion_success( + &self, + token: VerificationToken, + time_stamp: &[u8], + ) -> Result<(), EcssTmtcError> { + self.reporter + .completion_success(&self.sender, token, time_stamp) + } + + fn completion_failure( + &self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), EcssTmtcError> { + self.reporter + .completion_failure(&self.sender, token, params) + } + + fn completion_success_check(&mut self, incrementing_couters: bool) { + assert_eq!(self.sender.service_queue.borrow().len(), 3); + let mut current_seq_count = 0; + let cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo { + subservice: 1, + apid: TEST_APID, + seq_count: current_seq_count, + msg_counter: current_seq_count, + dest_id: self.reporter.dest_id(), + time_stamp: EMPTY_STAMP, + }, + additional_data: None, + }; + let mut info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + + if incrementing_couters { + current_seq_count += 1; + } + + let cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo { + subservice: 3, + apid: TEST_APID, + msg_counter: current_seq_count, + seq_count: current_seq_count, + dest_id: self.reporter.dest_id(), + time_stamp: [0, 1, 0, 1, 0, 1, 0], + }, + additional_data: None, + }; + info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + + if incrementing_couters { + current_seq_count += 1; + } + let cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo { + subservice: 7, + apid: TEST_APID, + msg_counter: current_seq_count, + seq_count: current_seq_count, + dest_id: self.reporter.dest_id(), + time_stamp: EMPTY_STAMP, + }, + additional_data: None, + }; + info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + } + + impl VerificationReporterTestbench { + fn new(id: ComponentId, tc: PusTcCreator) -> Self { + let reporter = base_reporter(id); + Self { + id, + sender: TestSender::default(), + reporter, + request_id: RequestId::new(&tc), + tc: tc.to_vec().unwrap(), + } + } + + fn acceptance_check(&self, time_stamp: &[u8; 7]) { + let cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo { + subservice: 1, + apid: TEST_APID, + seq_count: 0, + msg_counter: 0, + dest_id: self.reporter.dest_id(), + time_stamp: *time_stamp, + }, + additional_data: None, + }; + let mut service_queue = self.sender.service_queue.borrow_mut(); + assert_eq!(service_queue.len(), 1); + let info = service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + fn acceptance_fail_check(&mut self, stamp_buf: [u8; 7]) { + let cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo { + subservice: 2, + seq_count: 0, + apid: TEST_APID, + msg_counter: 0, + dest_id: self.reporter.dest_id(), + time_stamp: stamp_buf, + }, + additional_data: Some([0, 2].to_vec()), + }; + let service_queue = self.sender.service_queue.get_mut(); + assert_eq!(service_queue.len(), 1); + let info = service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + fn start_fail_check(&mut self, fail_data_raw: [u8; 4]) { + let mut srv_queue = self.sender.service_queue.borrow_mut(); + assert_eq!(srv_queue.len(), 2); + let mut cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, EMPTY_STAMP), + additional_data: None, + }; + let mut info = srv_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count(4, TEST_APID, 0, EMPTY_STAMP), + additional_data: Some([&[22], fail_data_raw.as_slice()].concat().to_vec()), + }; + info = srv_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + fn step_success_check(&mut self, time_stamp: &[u8; 7]) { + let mut cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, *time_stamp), + additional_data: None, + }; + let mut srv_queue = self.sender.service_queue.borrow_mut(); + let mut info = srv_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count(3, TEST_APID, 0, *time_stamp), + additional_data: None, + }; + info = srv_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, *time_stamp), + additional_data: Some([0].to_vec()), + }; + info = srv_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, *time_stamp), + additional_data: Some([1].to_vec()), + }; + info = srv_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + fn check_step_failure(&mut self, fail_data_raw: [u8; 4]) { + assert_eq!(self.sender.service_queue.borrow().len(), 4); + let mut cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count( + 1, + TEST_APID, + self.reporter.dest_id(), + EMPTY_STAMP, + ), + additional_data: None, + }; + let mut info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count( + 3, + TEST_APID, + self.reporter.dest_id(), + [0, 1, 0, 1, 0, 1, 0], + ), + additional_data: None, + }; + info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count( + 5, + TEST_APID, + self.reporter.dest_id(), + EMPTY_STAMP, + ), + additional_data: Some([0].to_vec()), + }; + info = self.sender.service_queue.get_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count( + 6, + TEST_APID, + self.reporter.dest_id(), + EMPTY_STAMP, + ), + additional_data: Some( + [ + [1].as_slice(), + &[0, 0, 0x10, 0x20], + fail_data_raw.as_slice(), + ] + .concat() + .to_vec(), + ), + }; + info = self.sender.service_queue.get_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + fn completion_fail_check(&mut self) { + assert_eq!(self.sender.service_queue.borrow().len(), 3); + + let mut cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count( + 1, + TEST_APID, + self.reporter.dest_id(), + EMPTY_STAMP, + ), + additional_data: None, + }; + let mut info = self.sender.service_queue.get_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count( + 3, + TEST_APID, + self.reporter.dest_id(), + [0, 1, 0, 1, 0, 1, 0], + ), + additional_data: None, + }; + info = self.sender.service_queue.get_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + requestor: MessageMetadata::new(self.request_id.into(), self.id), + common: CommonTmInfo::new_zero_seq_count( + 8, + TEST_APID, + self.reporter.dest_id(), + EMPTY_STAMP, + ), + additional_data: Some([0, 0, 0x10, 0x20].to_vec()), + }; + info = self.sender.service_queue.get_mut().pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + } + + fn create_generic_ping() -> PusTcCreator<'static> { + let sph = SpHeader::new_for_unseg_tc(TEST_APID, 0x34, 0); let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let app_data = app_data.unwrap_or(&[]); - let pus_tc = PusTcCreator::new(&mut sph, tc_header, app_data, true); - let req_id = RequestId::new(&pus_tc); - (pus_tc, req_id) - } - - fn base_init(api_sel: bool) -> (TestBase<'static>, VerificationToken) { - let mut reporter = base_reporter(); - let (tc, req_id) = base_tc_init(None); - let init_tok = if api_sel { - reporter.add_tc_with_req_id(req_id) - } else { - reporter.add_tc(&tc) - }; - (TestBase { vr: reporter, tc }, init_tok) - } - - fn base_with_helper_init() -> ( - TestBaseWithHelper<'static, TestSender>, - VerificationToken, - ) { - let mut reporter = base_reporter(); - let (tc, _) = base_tc_init(None); - let init_tok = reporter.add_tc(&tc); - let sender = TestSender::default(); - let helper = VerificationReporterWithSender::new_from_reporter(reporter, sender); - (TestBaseWithHelper { helper, tc }, init_tok) - } - - fn acceptance_check(sender: &mut TestSender, req_id: &RequestId) { - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id: *req_id, - }; - let mut service_queue = sender.service_queue.borrow_mut(); - assert_eq!(service_queue.len(), 1); - let info = service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); + PusTcCreator::new(sph, tc_header, &[], true) } #[test] - fn test_mpsc_verif_send_sync() { + fn test_mpsc_verif_send() { let pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(8, 8)], false)); let shared_tm_store = SharedTmPool::new(pool); let (tx, _) = mpsc::channel(); - let mpsc_verif_sender = - TmInSharedPoolSenderWithMpsc::new(0, "verif_sender", shared_tm_store, tx); + let mpsc_verif_sender = MpscTmInSharedPoolSender::new(shared_tm_store, tx); is_send(&mpsc_verif_sender); } #[test] fn test_state() { - let (mut b, _) = base_init(false); - assert_eq!(b.vr.apid(), TEST_APID); - b.vr.set_apid(TEST_APID + 1); - assert_eq!(b.vr.apid(), TEST_APID + 1); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + assert_eq!(testbench.reporter.apid(), TEST_APID); + testbench.reporter.set_apid(TEST_APID + 1); + assert_eq!(testbench.reporter.apid(), TEST_APID + 1); } #[test] fn test_basic_acceptance_success() { - let (b, tok) = base_init(false); - let mut sender = TestSender::default(); - b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - acceptance_check(&mut sender, &tok.req_id); - } - - #[test] - fn test_basic_acceptance_success_with_helper() { - let (mut b, tok) = base_with_helper_init(); - b.helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - acceptance_check(&mut b.helper.sender, &tok.req_id); - } - - fn acceptance_fail_check(sender: &mut TestSender, req_id: RequestId, stamp_buf: [u8; 7]) { - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 2, - apid: TEST_APID, - msg_counter: 0, - dest_id: 5, - time_stamp: stamp_buf, - }, - additional_data: Some([0, 2].to_vec()), - req_id, - }; - let mut service_queue = sender.service_queue.borrow_mut(); - assert_eq!(service_queue.len(), 1); - let info = service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let token = testbench.init(); + testbench + .acceptance_success(token, &EMPTY_STAMP) + .expect("sending acceptance success failed"); + testbench.acceptance_check(&EMPTY_STAMP); } #[test] fn test_basic_acceptance_failure() { - let (mut b, tok) = base_init(true); - b.rep().reporter.dest_id = 5; + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let init_token = testbench.init(); let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; - let mut sender = TestSender::default(); let fail_code = EcssEnumU16::new(2); let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code); - b.vr.acceptance_failure(tok, &sender, fail_params) - .expect("Sending acceptance success failed"); - acceptance_fail_check(&mut sender, tok.req_id, stamp_buf); + testbench + .acceptance_failure(init_token, fail_params) + .expect("sending acceptance failure failed"); + testbench.acceptance_fail_check(stamp_buf); } #[test] fn test_basic_acceptance_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); - b.rep().reporter.dest_id = 5; + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let init_token = testbench.init(); let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; let fail_code = EcssEnumU16::new(2); let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code); - b.helper - .acceptance_failure(tok, fail_params) - .expect("Sending acceptance success failed"); - acceptance_fail_check(&mut b.helper.sender, tok.req_id, stamp_buf); + testbench + .acceptance_failure(init_token, fail_params) + .expect("sending acceptance failure failed"); + testbench.acceptance_fail_check(stamp_buf); } #[test] fn test_acceptance_fail_data_too_large() { - let (mut b, tok) = base_with_helper_init(); - b.rep().reporter.dest_id = 5; + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let init_token = testbench.init(); let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; let fail_code = EcssEnumU16::new(2); let fail_data: [u8; 16] = [0; 16]; // 4 req ID + 1 byte step + 2 byte error code + 8 byte fail data - assert_eq!(b.rep().allowed_source_data_len(), 15); + assert_eq!(testbench.reporter.allowed_source_data_len(), 15); let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, fail_data.as_slice()); - let res = b.helper.acceptance_failure(tok, fail_params); - assert!(res.is_err()); - let err_with_token = res.unwrap_err(); - assert_eq!(err_with_token.1, tok); - match err_with_token.0 { + let result = testbench.acceptance_failure(init_token, fail_params); + assert!(result.is_err()); + let error = result.unwrap_err(); + match error { EcssTmtcError::Pus(PusError::ByteConversion(e)) => match e { ByteConversionError::ToSliceTooSmall { found, expected } => { assert_eq!( expected, fail_data.len() + RequestId::SIZE_AS_BYTES + fail_code.size() ); - assert_eq!(found, b.rep().allowed_source_data_len()); + assert_eq!(found, testbench.reporter.allowed_source_data_len()); } _ => { panic!("{}", format!("Unexpected error {:?}", e)) } }, _ => { - panic!("{}", format!("Unexpected error {:?}", err_with_token.0)) + panic!("{}", format!("Unexpected error {:?}", error)) } } } #[test] fn test_basic_acceptance_failure_with_fail_data() { - let (b, tok) = base_init(false); - let sender = TestSender::default(); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let fail_code = EcssEnumU8::new(10); let fail_data = EcssEnumU32::new(12); let mut fail_data_raw = [0; 4]; fail_data.write_to_be_bytes(&mut fail_data_raw).unwrap(); let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice()); - b.vr.acceptance_failure(tok, &sender, fail_params) - .expect("Sending acceptance success failed"); + let init_token = testbench.init(); + testbench + .acceptance_failure(init_token, fail_params) + .expect("sending acceptance failure failed"); let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 2, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, + requestor: MessageMetadata::new(testbench.request_id.into(), testbench.id), + common: CommonTmInfo::new_zero_seq_count(2, TEST_APID, 0, EMPTY_STAMP), additional_data: Some([10, 0, 0, 0, 12].to_vec()), - req_id: tok.req_id, }; - let mut service_queue = sender.service_queue.borrow_mut(); + let mut service_queue = testbench.sender.service_queue.borrow_mut(); assert_eq!(service_queue.len(), 1); let info = service_queue.pop_front().unwrap(); assert_eq!(info, cmp_info); } - fn start_fail_check(sender: &mut TestSender, req_id: RequestId, fail_data_raw: [u8; 4]) { - let mut srv_queue = sender.service_queue.borrow_mut(); - assert_eq!(srv_queue.len(), 2); - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 4, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([&[22], fail_data_raw.as_slice()].concat().to_vec()), - req_id, - }; - info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - #[test] fn test_start_failure() { - let (b, tok) = base_init(false); - let mut sender = TestSender::default(); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let init_token = testbench.init(); let fail_code = EcssEnumU8::new(22); let fail_data: i32 = -12; let mut fail_data_raw = [0; 4]; fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice()); - let accepted_token = - b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - b.vr.start_failure(accepted_token, &sender, fail_params) + let accepted_token = testbench + .acceptance_success(init_token, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + testbench + .start_failure(accepted_token, fail_params) .expect("Start failure failure"); - start_fail_check(&mut sender, tok.req_id, fail_data_raw); + testbench.start_fail_check(fail_data_raw); } #[test] fn test_start_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let token = testbench.init(); let fail_code = EcssEnumU8::new(22); let fail_data: i32 = -12; let mut fail_data_raw = [0; 4]; fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice()); - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - b.helper + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) + .expect("acceptance failed"); + testbench .start_failure(accepted_token, fail_params) - .expect("Start failure failure"); - start_fail_check(&mut b.helper.sender, tok.req_id, fail_data_raw); - } - - fn step_success_check(sender: &mut TestSender, req_id: RequestId) { - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut srv_queue = sender.service_queue.borrow_mut(); - let mut info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 5, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([0].to_vec()), - req_id, - }; - info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 5, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([1].to_vec()), - req_id, - }; - info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); + .expect("start failure failed"); + testbench.start_fail_check(fail_data_raw); } #[test] fn test_steps_success() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - let accepted_token = b - .rep() - .acceptance_success(tok, &sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = b - .rep() - .start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - b.rep() - .step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(0)) - .expect("Sending step 0 success failed"); - b.vr.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(1)) - .expect("Sending step 1 success failed"); - assert_eq!(sender.service_queue.borrow().len(), 4); - step_success_check(&mut sender, tok.req_id); - } - - #[test] - fn test_steps_success_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = b - .helper - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - b.helper + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let token = testbench.init(); + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) + .expect("acceptance failed"); + let started_token = testbench + .start_success(accepted_token, &EMPTY_STAMP) + .expect("acceptance failed"); + testbench .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) - .expect("Sending step 0 success failed"); - b.helper + .expect("step 0 failed"); + testbench .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1)) - .expect("Sending step 1 success failed"); - assert_eq!(b.helper.sender.service_queue.borrow().len(), 4); - step_success_check(&mut b.helper.sender, tok.req_id); - } - - fn check_step_failure(sender: &mut TestSender, req_id: RequestId, fail_data_raw: [u8; 4]) { - assert_eq!(sender.service_queue.borrow().len(), 4); - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.borrow_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.borrow_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 5, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([0].to_vec()), - req_id, - }; - info = sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 6, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some( - [ - [1].as_slice(), - &[0, 0, 0x10, 0x20], - fail_data_raw.as_slice(), - ] - .concat() - .to_vec(), - ), - req_id, - }; - info = sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); + .expect("step 1 failed"); + assert_eq!(testbench.sender.service_queue.borrow().len(), 4); + testbench.step_success_check(&EMPTY_STAMP); } #[test] fn test_step_failure() { - let (b, tok) = base_init(false); - let mut sender = TestSender::default(); - let req_id = tok.req_id; + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let token = testbench.init(); let fail_code = EcssEnumU32::new(0x1020); let fail_data: f32 = -22.3232; let mut fail_data_raw = [0; 4]; @@ -2159,269 +2307,74 @@ pub mod tests { fail_data_raw.as_slice(), ); - let accepted_token = - b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = - b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - b.vr.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(0)) - .expect("Sending completion success failed"); - b.vr.step_failure(started_token, &sender, fail_params) - .expect("Step failure failed"); - check_step_failure(&mut sender, req_id, fail_data_raw); - } - - #[test] - fn test_steps_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let req_id = tok.req_id; - let fail_code = EcssEnumU32::new(0x1020); - let fail_data: f32 = -22.3232; - let mut fail_data_raw = [0; 4]; - fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); - let fail_step = EcssEnumU8::new(1); - let fail_params = FailParamsWithStep::new( - &EMPTY_STAMP, - &fail_step, - &fail_code, - fail_data_raw.as_slice(), - ); - - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) .expect("Sending acceptance success failed"); - let started_token = b - .helper + let started_token = testbench .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) .expect("Sending start success failed"); - b.helper + testbench .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) .expect("Sending completion success failed"); - b.helper + testbench .step_failure(started_token, fail_params) .expect("Step failure failed"); - check_step_failure(&mut b.helper.sender, req_id, fail_data_raw); - } - - fn completion_fail_check(sender: &mut TestSender, req_id: RequestId) { - assert_eq!(sender.service_queue.borrow().len(), 3); - - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 8, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([0, 0, 0x10, 0x20].to_vec()), - req_id, - }; - info = sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); + testbench.check_step_failure(fail_data_raw); } #[test] fn test_completion_failure() { - let (b, tok) = base_init(false); - let mut sender = TestSender::default(); - let req_id = tok.req_id; + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let token = testbench.init(); let fail_code = EcssEnumU32::new(0x1020); let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code); - let accepted_token = - b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = - b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - b.vr.completion_failure(started_token, &sender, fail_params) - .expect("Completion failure"); - completion_fail_check(&mut sender, req_id); - } - - #[test] - fn test_completion_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let req_id = tok.req_id; - let fail_code = EcssEnumU32::new(0x1020); - let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code); - - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) .expect("Sending acceptance success failed"); - let started_token = b - .helper + let started_token = testbench .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) .expect("Sending start success failed"); - b.helper + testbench .completion_failure(started_token, fail_params) .expect("Completion failure"); - completion_fail_check(&mut b.helper.sender, req_id); - } - - fn completion_success_check(sender: &mut TestSender, req_id: RequestId) { - assert_eq!(sender.service_queue.borrow().len(), 3); - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.borrow_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.borrow_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 7, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.borrow_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); + testbench.completion_fail_check(); } #[test] fn test_complete_success_sequence() { - let (b, tok) = base_init(false); - let mut sender = TestSender::default(); - let accepted_token = - b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = - b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - b.vr.completion_success(started_token, &sender, &EMPTY_STAMP) - .expect("Sending completion success failed"); - completion_success_check(&mut sender, tok.req_id); - } - - #[test] - fn test_complete_success_sequence_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) + let mut testbench = + VerificationReporterTestbench::new(TEST_COMPONENT_ID_0.id(), create_generic_ping()); + let token = testbench.init(); + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) .expect("Sending acceptance success failed"); - let started_token = b - .helper + let started_token = testbench .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) .expect("Sending start success failed"); - b.helper + testbench .completion_success(started_token, &EMPTY_STAMP) .expect("Sending completion success failed"); - completion_success_check(&mut b.helper.sender, tok.req_id); + testbench.completion_success_check(false); } #[test] - fn test_seq_count_increment() { - let pool_cfg = - StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false); - let tm_pool = StaticMemoryPool::new(pool_cfg.clone()); - let shared_tm_store = SharedTmPool::new(tm_pool); - let shared_tm_pool = shared_tm_store.clone_backing_pool(); - let (verif_tx, verif_rx) = mpsc::channel(); - let sender = - TmInSharedPoolSenderWithId::new(0, "Verification Sender", shared_tm_store, verif_tx); - let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); - let mut reporter = VerificationReporterWithSender::new(&cfg, sender); - - let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap(); - let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let pus_tc_0 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true); - let init_token = reporter.add_tc(&pus_tc_0); - - // Complete success sequence for a telecommand - let accepted_token = reporter - .acceptance_success(init_token, &EMPTY_STAMP) - .unwrap(); - let started_token = reporter - .start_success(accepted_token, &EMPTY_STAMP) - .unwrap(); - reporter + fn test_packet_manipulation() { + let mut testbench = VerificationReporterTestbench::new_with_hook( + TEST_COMPONENT_ID_0.id(), + create_generic_ping(), + SequenceCounterHook::default(), + ); + let token = testbench.init(); + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = testbench + .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + testbench .completion_success(started_token, &EMPTY_STAMP) - .unwrap(); - - // Verify it arrives correctly on receiver end - let mut tm_buf: [u8; 1024] = [0; 1024]; - let mut packet_idx = 0; - while packet_idx < 3 { - let addr = verif_rx.recv_timeout(Duration::from_millis(10)).unwrap(); - let tm_len; - { - let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); - let store_guard = rg.read_with_guard(addr); - tm_len = store_guard - .read(&mut tm_buf) - .expect("Error reading TM slice"); - } - let (pus_tm, _) = - PusTmReader::new(&tm_buf[0..tm_len], 7).expect("Error reading verification TM"); - if packet_idx == 0 { - assert_eq!(pus_tm.subservice(), 1); - assert_eq!(pus_tm.sp_header.seq_count(), 0); - } else if packet_idx == 1 { - assert_eq!(pus_tm.subservice(), 3); - assert_eq!(pus_tm.sp_header.seq_count(), 0); - } else if packet_idx == 2 { - assert_eq!(pus_tm.subservice(), 7); - assert_eq!(pus_tm.sp_header.seq_count(), 0); - } - packet_idx += 1; - } + .expect("Sending completion success failed"); + testbench.completion_success_check(true); } } diff --git a/satrs/src/queue.rs b/satrs/src/queue.rs index 5ba4bdc..93c8ec8 100644 --- a/satrs/src/queue.rs +++ b/satrs/src/queue.rs @@ -4,11 +4,17 @@ use std::error::Error; #[cfg(feature = "std")] use std::sync::mpsc; +use crate::ComponentId; + +/// Generic channel ID type. +pub type ChannelId = u32; + /// Generic error type for sending something via a message queue. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum GenericSendError { RxDisconnected, QueueFull(Option), + TargetDoesNotExist(ComponentId), } impl Display for GenericSendError { @@ -20,6 +26,9 @@ impl Display for GenericSendError { GenericSendError::QueueFull(max_cap) => { write!(f, "queue with max capacity of {max_cap:?} is full") } + GenericSendError::TargetDoesNotExist(target) => { + write!(f, "target queue with ID {target} does not exist") + } } } } @@ -28,17 +37,17 @@ impl Display for GenericSendError { impl Error for GenericSendError {} /// Generic error type for sending something via a message queue. -#[derive(Debug, Copy, Clone)] -pub enum GenericRecvError { +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum GenericReceiveError { Empty, - TxDisconnected, + TxDisconnected(Option), } -impl Display for GenericRecvError { +impl Display for GenericReceiveError { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { - Self::TxDisconnected => { - write!(f, "tx side has disconnected") + Self::TxDisconnected(channel_id) => { + write!(f, "tx side with id {channel_id:?} has disconnected") } Self::Empty => { write!(f, "nothing to receive") @@ -48,7 +57,43 @@ impl Display for GenericRecvError { } #[cfg(feature = "std")] -impl Error for GenericRecvError {} +impl Error for GenericReceiveError {} + +#[derive(Debug, Clone)] +pub enum GenericTargetedMessagingError { + Send(GenericSendError), + Receive(GenericReceiveError), +} +impl From for GenericTargetedMessagingError { + fn from(value: GenericSendError) -> Self { + Self::Send(value) + } +} + +impl From for GenericTargetedMessagingError { + fn from(value: GenericReceiveError) -> Self { + Self::Receive(value) + } +} + +impl Display for GenericTargetedMessagingError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::Send(err) => write!(f, "generic targeted messaging error: {}", err), + Self::Receive(err) => write!(f, "generic targeted messaging error: {}", err), + } + } +} + +#[cfg(feature = "std")] +impl Error for GenericTargetedMessagingError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + GenericTargetedMessagingError::Send(send) => Some(send), + GenericTargetedMessagingError::Receive(receive) => Some(receive), + } + } +} #[cfg(feature = "std")] impl From> for GenericSendError { diff --git a/satrs/src/request.rs b/satrs/src/request.rs index 24ca497..f2104ed 100644 --- a/satrs/src/request.rs +++ b/satrs/src/request.rs @@ -1,110 +1,586 @@ -use core::fmt; +use core::{fmt, marker::PhantomData}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "alloc")] +pub use alloc_mod::*; + #[cfg(feature = "std")] -use std::error::Error; +pub use std_mod::*; use spacepackets::{ ecss::{tc::IsPusTelecommand, PusPacket}, ByteConversionError, CcsdsPacket, }; -use crate::TargetId; +use crate::{queue::GenericTargetedMessagingError, ComponentId}; +/// Generic request ID type. Requests can be associated with an ID to have a unique identifier +/// for them. This can be useful for tasks like tracking their progress. +pub type RequestId = u32; + +/// CCSDS APID type definition. Please note that the APID is a 14 bit value. pub type Apid = u16; -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TargetIdCreationError { - ByteConversion(ByteConversionError), - NotEnoughAppData(usize), +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct UniqueApidTargetId { + pub apid: Apid, + pub unique_id: u32, } -impl From for TargetIdCreationError { - fn from(e: ByteConversionError) -> Self { - Self::ByteConversion(e) +impl UniqueApidTargetId { + pub const fn new(apid: Apid, target: u32) -> Self { + Self { + apid, + unique_id: target, + } + } + + pub fn raw(&self) -> ComponentId { + ((self.apid as u64) << 32) | (self.unique_id as u64) + } + + pub fn id(&self) -> ComponentId { + self.raw() + } + + /// This function attempts to build the ID from a PUS telecommand by extracting the APID + /// and the first four bytes of the application data field as the target field. + pub fn from_pus_tc( + tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand), + ) -> Result { + if tc.user_data().len() < 4 { + return Err(ByteConversionError::FromSliceTooSmall { + found: tc.user_data().len(), + expected: 4, + }); + } + Ok(Self::new( + tc.apid(), + u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()), + )) } } -impl fmt::Display for TargetIdCreationError { +impl From for UniqueApidTargetId { + fn from(raw: u64) -> Self { + Self { + apid: (raw >> 32) as u16, + unique_id: raw as u32, + } + } +} + +impl From for u64 { + fn from(target_and_apid_id: UniqueApidTargetId) -> Self { + target_and_apid_id.raw() + } +} + +impl fmt::Display for UniqueApidTargetId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::ByteConversion(e) => write!(f, "target ID creation: {}", e), - Self::NotEnoughAppData(len) => { - write!(f, "not enough app data to generate target ID: {}", len) + write!( + f, + "Target and APID ID with APID {:#03x} and target {}", + self.apid, self.unique_id + ) + } +} + +/// This contains metadata information which might be useful when used together with a +/// generic message tpye. +/// +/// This could for example be used to build request/reply patterns or state tracking for request. +#[derive(Debug, Copy, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MessageMetadata { + request_id: RequestId, + sender_id: ComponentId, +} + +impl MessageMetadata { + pub const fn new(request_id: RequestId, sender_id: ComponentId) -> Self { + Self { + request_id, + sender_id, + } + } + + pub fn request_id(&self) -> RequestId { + self.request_id + } + + pub fn sender_id(&self) -> ComponentId { + self.sender_id + } +} + +/// Generic message type which adds [metadata][MessageMetadata] to a generic message typ. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GenericMessage { + pub requestor_info: MessageMetadata, + pub message: Message, +} + +impl GenericMessage { + pub fn new(requestor_info: MessageMetadata, message: Message) -> Self { + Self { + requestor_info, + message, + } + } + + delegate::delegate! { + to self.requestor_info { + pub fn request_id(&self) -> RequestId; + pub fn sender_id(&self) -> ComponentId; + } + } +} + +/// Generic trait for objects which can send targeted messages. +pub trait MessageSender: Send { + fn send(&self, message: GenericMessage) -> Result<(), GenericTargetedMessagingError>; +} + +// Generic trait for objects which can receive targeted messages. +pub trait MessageReceiver { + fn try_recv(&self) -> Result>, GenericTargetedMessagingError>; +} + +pub struct MessageWithSenderIdReceiver>(pub R, PhantomData); + +impl> From for MessageWithSenderIdReceiver { + fn from(receiver: R) -> Self { + MessageWithSenderIdReceiver(receiver, PhantomData) + } +} + +impl> MessageWithSenderIdReceiver { + pub fn try_recv_message( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.0.try_recv() + } +} + +pub struct MessageReceiverWithId> { + local_channel_id: ComponentId, + reply_receiver: MessageWithSenderIdReceiver, +} + +impl> MessageReceiverWithId { + pub fn new(local_channel_id: ComponentId, reply_receiver: R) -> Self { + Self { + local_channel_id, + reply_receiver: MessageWithSenderIdReceiver::from(reply_receiver), + } + } + + pub fn local_channel_id(&self) -> ComponentId { + self.local_channel_id + } +} + +impl> MessageReceiverWithId { + pub fn try_recv_message( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.reply_receiver.0.try_recv() + } +} + +#[cfg(feature = "alloc")] +pub mod alloc_mod { + use core::marker::PhantomData; + + use crate::queue::GenericSendError; + + use super::*; + use hashbrown::HashMap; + + pub struct MessageSenderMap>( + pub HashMap, + pub(crate) PhantomData, + ); + + impl> Default for MessageSenderMap { + fn default() -> Self { + Self(Default::default(), PhantomData) + } + } + + impl> MessageSenderMap { + pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) { + self.0.insert(target_id, message_sender); + } + + pub fn send_message( + &self, + requestor_info: MessageMetadata, + target_channel_id: ComponentId, + message: MSG, + ) -> Result<(), GenericTargetedMessagingError> { + if self.0.contains_key(&target_channel_id) { + return self + .0 + .get(&target_channel_id) + .unwrap() + .send(GenericMessage::new(requestor_info, message)); } + Err(GenericSendError::TargetDoesNotExist(target_channel_id).into()) + } + } + + pub struct MessageSenderAndReceiver, R: MessageReceiver> { + pub local_channel_id: ComponentId, + pub message_sender_map: MessageSenderMap, + pub message_receiver: MessageWithSenderIdReceiver, + } + + impl, R: MessageReceiver> + MessageSenderAndReceiver + { + pub fn new(local_channel_id: ComponentId, message_receiver: R) -> Self { + Self { + local_channel_id, + message_sender_map: Default::default(), + message_receiver: MessageWithSenderIdReceiver::from(message_receiver), + } + } + + pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) { + self.message_sender_map + .add_message_target(target_id, message_sender) + } + + pub fn local_channel_id_generic(&self) -> ComponentId { + self.local_channel_id + } + + /// Try to send a message, which can be a reply or a request, depending on the generics. + pub fn send_message( + &self, + request_id: RequestId, + target_id: ComponentId, + message: TO, + ) -> Result<(), GenericTargetedMessagingError> { + self.message_sender_map.send_message( + MessageMetadata::new(request_id, self.local_channel_id_generic()), + target_id, + message, + ) + } + + /// Try to receive a message, which can be a reply or a request, depending on the generics. + pub fn try_recv_message( + &self, + ) -> Result>, GenericTargetedMessagingError> { + self.message_receiver.try_recv_message() + } + } + + pub struct RequestAndReplySenderAndReceiver< + REQUEST, + REPLY, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > { + pub local_channel_id: ComponentId, + // These 2 are a functional group. + pub request_sender_map: MessageSenderMap, + pub reply_receiver: MessageWithSenderIdReceiver, + // These 2 are a functional group. + pub request_receiver: MessageWithSenderIdReceiver, + pub reply_sender_map: MessageSenderMap, + } + + impl< + REQUEST, + REPLY, + S0: MessageSender, + R0: MessageReceiver, + S1: MessageSender, + R1: MessageReceiver, + > RequestAndReplySenderAndReceiver + { + pub fn new( + local_channel_id: ComponentId, + request_receiver: R1, + reply_receiver: R0, + ) -> Self { + Self { + local_channel_id, + request_receiver: request_receiver.into(), + reply_receiver: reply_receiver.into(), + request_sender_map: Default::default(), + reply_sender_map: Default::default(), + } + } + + pub fn local_channel_id_generic(&self) -> ComponentId { + self.local_channel_id } } } #[cfg(feature = "std")] -impl Error for TargetIdCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - if let Self::ByteConversion(e) = self { - return Some(e); +pub mod std_mod { + + use super::*; + use std::sync::mpsc; + + use crate::queue::{GenericReceiveError, GenericSendError, GenericTargetedMessagingError}; + + impl MessageSender for mpsc::Sender> { + fn send(&self, message: GenericMessage) -> Result<(), GenericTargetedMessagingError> { + self.send(message) + .map_err(|_| GenericSendError::RxDisconnected)?; + Ok(()) } - None } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct TargetAndApidId { - pub apid: Apid, - pub target: u32, -} - -impl TargetAndApidId { - pub fn new(apid: Apid, target: u32) -> Self { - Self { apid, target } - } - - pub fn apid(&self) -> Apid { - self.apid - } - - pub fn target(&self) -> u32 { - self.target - } - - pub fn raw(&self) -> TargetId { - ((self.apid as u64) << 32) | (self.target as u64) - } - - pub fn target_id(&self) -> TargetId { - self.raw() - } - - pub fn from_pus_tc( - tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand), - ) -> Result { - if tc.user_data().len() < 4 { - return Err(ByteConversionError::FromSliceTooSmall { - found: tc.user_data().len(), - expected: 8, + impl MessageSender for mpsc::SyncSender> { + fn send(&self, message: GenericMessage) -> Result<(), GenericTargetedMessagingError> { + if let Err(e) = self.try_send(message) { + return match e { + mpsc::TrySendError::Full(_) => Err(GenericSendError::QueueFull(None).into()), + mpsc::TrySendError::Disconnected(_) => { + Err(GenericSendError::RxDisconnected.into()) + } + }; } - .into()); + Ok(()) } - Ok(Self { - apid: tc.apid(), - target: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()), - }) } + + pub type MessageSenderMapMpsc = MessageReceiverWithId>; + pub type MessageSenderMapBoundedMpsc = MessageReceiverWithId>; + + impl MessageReceiver for mpsc::Receiver> { + fn try_recv(&self) -> Result>, GenericTargetedMessagingError> { + match self.try_recv() { + Ok(msg) => Ok(Some(msg)), + Err(e) => match e { + mpsc::TryRecvError::Empty => Ok(None), + mpsc::TryRecvError::Disconnected => { + Err(GenericReceiveError::TxDisconnected(None).into()) + } + }, + } + } + } + + pub type MessageReceiverWithIdMpsc = MessageReceiverWithId>; } -impl From for TargetAndApidId { - fn from(raw: u64) -> Self { - Self { - apid: (raw >> 32) as u16, - target: raw as u32, +#[cfg(test)] +mod tests { + use std::sync::mpsc; + + use alloc::string::ToString; + use spacepackets::{ + ecss::tc::{PusTcCreator, PusTcSecondaryHeader}, + ByteConversionError, SpHeader, + }; + + use crate::{ + queue::{GenericReceiveError, GenericSendError, GenericTargetedMessagingError}, + request::{MessageMetadata, MessageSenderMap}, + }; + + use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId}; + + const TEST_CHANNEL_ID_0: u64 = 1; + const TEST_CHANNEL_ID_1: u64 = 2; + const TEST_CHANNEL_ID_2: u64 = 3; + + #[test] + fn test_basic_target_id_with_apid() { + let id = UniqueApidTargetId::new(0x111, 0x01); + assert_eq!(id.apid, 0x111); + assert_eq!(id.unique_id, 0x01); + assert_eq!(id.id(), id.raw()); + assert_eq!(u64::from(id), id.raw()); + let id_raw = id.raw(); + let id_from_raw = UniqueApidTargetId::from(id_raw); + assert_eq!(id_from_raw, id); + assert_eq!(id.id(), (0x111 << 32) | 0x01); + let string = id.to_string(); + assert_eq!( + string, + "Target and APID ID with APID 0x111 and target 1".to_string() + ); + } + + #[test] + fn test_basic_target_id_with_apid_from_pus_tc() { + let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0); + let app_data = 1_u32.to_be_bytes(); + let pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &app_data, true); + let id = UniqueApidTargetId::from_pus_tc(&pus_tc).unwrap(); + assert_eq!(id.apid, 0x111); + assert_eq!(id.unique_id, 1); + } + + #[test] + fn test_basic_target_id_with_apid_from_pus_tc_invalid_app_data() { + let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0); + let sec_header = PusTcSecondaryHeader::new_simple(17, 1); + let pus_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true); + let error = UniqueApidTargetId::from_pus_tc(&pus_tc); + assert!(error.is_err()); + let error = error.unwrap_err(); + if let ByteConversionError::FromSliceTooSmall { found, expected } = error { + assert_eq!(found, 0); + assert_eq!(expected, 4); + } else { + panic!("Unexpected error type"); + } + } + + #[test] + fn test_receiver_only() { + let (sender, receiver) = mpsc::channel(); + // Test structure with only a receiver which has a channel ID. + let receiver = MessageReceiverWithId::new(TEST_CHANNEL_ID_0, receiver); + let request_id = 5; + sender + .send(GenericMessage::new( + MessageMetadata::new(request_id, TEST_CHANNEL_ID_1), + 5, + )) + .unwrap(); + let reply = receiver.try_recv_message().unwrap(); + assert!(reply.is_some()); + assert_eq!(receiver.local_channel_id(), TEST_CHANNEL_ID_0); + let reply = reply.unwrap(); + assert_eq!(reply.requestor_info.request_id, request_id); + assert_eq!(reply.requestor_info.sender_id, TEST_CHANNEL_ID_1); + assert_eq!(reply.message, 5); + } + + #[test] + fn test_receiver_empty() { + let (_sender, receiver) = mpsc::sync_channel::>(2); + // Test structure with only a receiver which has a channel ID. + let receiver = MessageReceiverWithId::new(TEST_CHANNEL_ID_0, receiver); + let reply = receiver.try_recv_message().unwrap(); + assert!(reply.is_none()); + } + + #[test] + fn test_all_tx_disconnected() { + let (sender, receiver) = mpsc::sync_channel::>(2); + // Test structure with only a receiver which has a channel ID. + let receiver = MessageReceiverWithId::new(TEST_CHANNEL_ID_0, receiver); + drop(sender); + let reply = receiver.try_recv_message(); + assert!(reply.is_err()); + let error = reply.unwrap_err(); + if let GenericTargetedMessagingError::Receive(GenericReceiveError::TxDisconnected(None)) = + error + { + } else { + panic!("unexpected error type"); + } + } + + #[test] + fn test_sender_map() { + let (sender0, receiver0) = mpsc::channel(); + let (sender1, receiver1) = mpsc::channel(); + let mut sender_map = MessageSenderMap::default(); + sender_map.add_message_target(TEST_CHANNEL_ID_1, sender0); + sender_map.add_message_target(TEST_CHANNEL_ID_2, sender1); + sender_map + .send_message( + MessageMetadata::new(1, TEST_CHANNEL_ID_0), + TEST_CHANNEL_ID_1, + 5, + ) + .expect("sending message failed"); + let mut reply = receiver0.recv().expect("receiving message failed"); + assert_eq!(reply.request_id(), 1); + assert_eq!(reply.sender_id(), TEST_CHANNEL_ID_0); + assert_eq!(reply.message, 5); + sender_map + .send_message( + MessageMetadata::new(2, TEST_CHANNEL_ID_0), + TEST_CHANNEL_ID_2, + 10, + ) + .expect("sending message failed"); + reply = receiver1.recv().expect("receiving message failed"); + assert_eq!(reply.request_id(), 2); + assert_eq!(reply.sender_id(), TEST_CHANNEL_ID_0); + assert_eq!(reply.message, 10); + } + + #[test] + fn test_sender_map_target_does_not_exist() { + let (sender0, _) = mpsc::channel(); + let mut sender_map_with_id = MessageSenderMap::default(); + sender_map_with_id.add_message_target(TEST_CHANNEL_ID_1, sender0); + let result = sender_map_with_id.send_message( + MessageMetadata::new(1, TEST_CHANNEL_ID_0), + TEST_CHANNEL_ID_2, + 5, + ); + assert!(result.is_err()); + let error = result.unwrap_err(); + if let GenericTargetedMessagingError::Send(GenericSendError::TargetDoesNotExist(target)) = + error + { + assert_eq!(target, TEST_CHANNEL_ID_2); + } else { + panic!("Unexpected error type"); + } + } + #[test] + fn test_sender_map_queue_full() { + let (sender0, _receiver0) = mpsc::sync_channel(1); + let mut sender_map_with_id = MessageSenderMap::default(); + sender_map_with_id.add_message_target(TEST_CHANNEL_ID_1, sender0); + sender_map_with_id + .send_message( + MessageMetadata::new(1, TEST_CHANNEL_ID_0), + TEST_CHANNEL_ID_1, + 5, + ) + .expect("sending message failed"); + let result = sender_map_with_id.send_message( + MessageMetadata::new(1, TEST_CHANNEL_ID_0), + TEST_CHANNEL_ID_1, + 5, + ); + assert!(result.is_err()); + let error = result.unwrap_err(); + if let GenericTargetedMessagingError::Send(GenericSendError::QueueFull(capacity)) = error { + assert!(capacity.is_none()); + } else { + panic!("Unexpected error type {}", error); + } + } + + #[test] + fn test_sender_map_queue_receiver_disconnected() { + let (sender0, receiver0) = mpsc::sync_channel(1); + let mut sender_map_with_id = MessageSenderMap::default(); + sender_map_with_id.add_message_target(TEST_CHANNEL_ID_1, sender0); + drop(receiver0); + let result = sender_map_with_id.send_message( + MessageMetadata::new(1, TEST_CHANNEL_ID_0), + TEST_CHANNEL_ID_1, + 5, + ); + assert!(result.is_err()); + let error = result.unwrap_err(); + if let GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected) = error { + } else { + panic!("Unexpected error type {}", error); } } } - -impl From for u64 { - fn from(target_and_apid_id: TargetAndApidId) -> Self { - target_and_apid_id.raw() - } -} - -impl fmt::Display for TargetAndApidId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}, {}", self.apid, self.target) - } -} diff --git a/satrs/src/seq_count.rs b/satrs/src/seq_count.rs index c60c8f7..b4539b0 100644 --- a/satrs/src/seq_count.rs +++ b/satrs/src/seq_count.rs @@ -32,7 +32,7 @@ dyn_clone::clone_trait_object!(SequenceCountProvider); #[cfg(feature = "alloc")] impl SequenceCountProvider for T where T: SequenceCountProviderCore + Clone {} -#[derive(Default, Clone)] +#[derive(Clone)] pub struct SeqCountProviderSimple { seq_count: Cell, max_val: T, @@ -43,13 +43,12 @@ macro_rules! impl_for_primitives { $( paste! { impl SeqCountProviderSimple<$ty> { - pub fn [](max_val: $ty) -> Self { + pub fn [](max_val: $ty) -> Self { Self { seq_count: Cell::new(0), max_val, } } - pub fn []() -> Self { Self { seq_count: Cell::new(0), @@ -58,6 +57,12 @@ macro_rules! impl_for_primitives { } } + impl Default for SeqCountProviderSimple<$ty> { + fn default() -> Self { + Self::[]() + } + } + impl SequenceCountProviderCore<$ty> for SeqCountProviderSimple<$ty> { fn get(&self) -> $ty { self.seq_count.get() @@ -86,21 +91,16 @@ macro_rules! impl_for_primitives { 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, } -impl CcsdsSimpleSeqCountProvider { - pub fn new() -> Self { - Self { - provider: SeqCountProviderSimple::new_u16_max_val(MAX_SEQ_COUNT), - } - } -} - impl Default for CcsdsSimpleSeqCountProvider { fn default() -> Self { - Self::new() + Self { + provider: SeqCountProviderSimple::new_custom_max_val_u16(MAX_SEQ_COUNT), + } } } @@ -187,7 +187,7 @@ mod tests { #[test] fn test_u8_counter() { - let u8_counter = SeqCountProviderSimple::new_u8(); + let u8_counter = SeqCountProviderSimple::::default(); assert_eq!(u8_counter.get(), 0); assert_eq!(u8_counter.get_and_increment(), 0); assert_eq!(u8_counter.get_and_increment(), 1); diff --git a/satrs/src/time.rs b/satrs/src/time.rs new file mode 100644 index 0000000..abd3fac --- /dev/null +++ b/satrs/src/time.rs @@ -0,0 +1,7 @@ +use core::fmt::Debug; + +/// Generic abstraction for a check/countdown timer. +pub trait CountdownProvider: Debug { + fn has_expired(&self) -> bool; + fn reset(&mut self); +} diff --git a/satrs/src/tmtc/ccsds_distrib.rs b/satrs/src/tmtc/ccsds_distrib.rs index a0d01b0..607b461 100644 --- a/satrs/src/tmtc/ccsds_distrib.rs +++ b/satrs/src/tmtc/ccsds_distrib.rs @@ -18,11 +18,12 @@ //! # Example //! //! ```rust +//! use satrs::ValidatorU16Id; //! use satrs::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor}; //! use satrs::tmtc::{ReceivesTc, ReceivesTcCore}; //! use spacepackets::{CcsdsPacket, SpHeader}; //! use spacepackets::ecss::WritablePusPacket; -//! use spacepackets::ecss::tc::{PusTc, PusTcCreator}; +//! use spacepackets::ecss::tc::PusTcCreator; //! //! #[derive (Default)] //! struct ConcreteApidHandler { @@ -34,16 +35,19 @@ //! fn mutable_foo(&mut self) {} //! } //! +//! impl ValidatorU16Id for ConcreteApidHandler { +//! fn validate(&self, apid: u16) -> bool { apid == 0x0002 } +//! } +//! //! impl CcsdsPacketHandler for ConcreteApidHandler { //! type Error = (); -//! fn valid_apids(&self) -> &'static [u16] { &[0x002] } -//! fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { +//! fn handle_packet_with_valid_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { //! assert_eq!(sp_header.apid(), 0x002); //! assert_eq!(tc_raw.len(), 13); //! self.known_call_count += 1; //! Ok(()) //! } -//! fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { +//! fn handle_packet_with_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { //! assert_eq!(sp_header.apid(), 0x003); //! assert_eq!(tc_raw.len(), 13); //! self.unknown_call_count += 1; @@ -55,8 +59,8 @@ //! let mut ccsds_distributor = CcsdsDistributor::new(apid_handler); //! //! // Create and pass PUS telecommand with a valid APID -//! let mut space_packet_header = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); -//! let mut pus_tc = PusTcCreator::new_simple(&mut space_packet_header, 17, 1, None, true); +//! let sp_header = SpHeader::new_for_unseg_tc(0x002, 0x34, 0); +//! let mut pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &[], true); //! let mut test_buf: [u8; 32] = [0; 32]; //! let mut size = pus_tc //! .write_to_bytes(test_buf.as_mut_slice()) @@ -81,7 +85,10 @@ //! let mutable_handler_ref = ccsds_distributor.packet_handler_mut(); //! mutable_handler_ref.mutable_foo(); //! ``` -use crate::tmtc::{ReceivesCcsdsTc, ReceivesTcCore}; +use crate::{ + tmtc::{ReceivesCcsdsTc, ReceivesTcCore}, + ValidatorU16Id, +}; use core::fmt::{Display, Formatter}; use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader}; #[cfg(feature = "std")] @@ -92,14 +99,18 @@ use std::error::Error; /// Users should implement this trait on their custom CCSDS packet handler and then pass a boxed /// instance of this handler to the [CcsdsDistributor]. The distributor will use the trait /// interface to dispatch received packets to the user based on the Application Process Identifier -/// (APID) field of the CCSDS packet. -pub trait CcsdsPacketHandler { +/// (APID) field of the CCSDS packet. The APID will be checked using the generic [ValidatorU16Id] +/// trait. +pub trait CcsdsPacketHandler: ValidatorU16Id { type Error; - fn valid_apids(&self) -> &'static [u16]; - fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) - -> Result<(), Self::Error>; - fn handle_unknown_apid( + fn handle_packet_with_valid_apid( + &mut self, + sp_header: &SpHeader, + tc_raw: &[u8], + ) -> Result<(), Self::Error>; + + fn handle_packet_with_unknown_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], @@ -183,18 +194,15 @@ impl, E: 'static> CcsdsDistributor< } fn dispatch_ccsds(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), CcsdsError> { - let apid = sp_header.apid(); - let valid_apids = self.packet_handler.valid_apids(); - for &valid_apid in valid_apids { - if valid_apid == apid { - return self - .packet_handler - .handle_known_apid(sp_header, tc_raw) - .map_err(|e| CcsdsError::CustomError(e)); - } + let valid_apid = self.packet_handler().validate(sp_header.apid()); + if valid_apid { + self.packet_handler + .handle_packet_with_valid_apid(sp_header, tc_raw) + .map_err(|e| CcsdsError::CustomError(e))?; + return Ok(()); } self.packet_handler - .handle_unknown_apid(sp_header, tc_raw) + .handle_packet_with_unknown_apid(sp_header, tc_raw) .map_err(|e| CcsdsError::CustomError(e)) } } @@ -213,8 +221,8 @@ pub(crate) mod tests { fn is_send(_: &T) {} pub fn generate_ping_tc(buf: &mut [u8]) -> &[u8] { - let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); - let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0); + let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let size = pus_tc .write_to_bytes(buf) .expect("Error writing TC to buffer"); @@ -223,8 +231,8 @@ pub(crate) mod tests { } pub fn generate_ping_tc_as_vec() -> Vec { - let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); - PusTcCreator::new_simple(&mut sph, 17, 1, None, true) + let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0); + PusTcCreator::new_simple(sph, 17, 1, &[], true) .to_vec() .unwrap() } @@ -241,13 +249,16 @@ pub(crate) mod tests { pub unknown_packet_queue: VecDeque<(u16, Vec)>, } + impl ValidatorU16Id for BasicApidHandlerSharedQueue { + fn validate(&self, packet_id: u16) -> bool { + [0x000, 0x002].contains(&packet_id) + } + } + impl CcsdsPacketHandler for BasicApidHandlerSharedQueue { type Error = (); - fn valid_apids(&self) -> &'static [u16] { - &[0x000, 0x002] - } - fn handle_known_apid( + fn handle_packet_with_valid_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], @@ -261,7 +272,7 @@ pub(crate) mod tests { Ok(()) } - fn handle_unknown_apid( + fn handle_packet_with_unknown_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], @@ -276,14 +287,16 @@ pub(crate) mod tests { } } + impl ValidatorU16Id for BasicApidHandlerOwnedQueue { + fn validate(&self, packet_id: u16) -> bool { + [0x000, 0x002].contains(&packet_id) + } + } + impl CcsdsPacketHandler for BasicApidHandlerOwnedQueue { type Error = (); - fn valid_apids(&self) -> &'static [u16] { - &[0x000, 0x002] - } - - fn handle_known_apid( + fn handle_packet_with_valid_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], @@ -294,7 +307,7 @@ pub(crate) mod tests { Ok(()) } - fn handle_unknown_apid( + fn handle_packet_with_unknown_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], @@ -332,8 +345,8 @@ pub(crate) mod tests { fn test_unknown_apid_handling() { let apid_handler = BasicApidHandlerOwnedQueue::default(); let mut ccsds_distrib = CcsdsDistributor::new(apid_handler); - let mut sph = SpHeader::tc_unseg(0x004, 0x34, 0).unwrap(); - let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let sph = SpHeader::new_for_unseg_tc(0x004, 0x34, 0); + let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let mut test_buf: [u8; 32] = [0; 32]; pus_tc .write_to_bytes(test_buf.as_mut_slice()) @@ -351,8 +364,8 @@ pub(crate) mod tests { #[test] fn test_ccsds_distribution() { let mut ccsds_distrib = CcsdsDistributor::new(BasicApidHandlerOwnedQueue::default()); - let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); - let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0); + let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let tc_vec = pus_tc.to_vec().unwrap(); ccsds_distrib .pass_ccsds(&sph, &tc_vec) @@ -370,8 +383,8 @@ pub(crate) mod tests { #[test] fn test_distribution_short_packet_fails() { let mut ccsds_distrib = CcsdsDistributor::new(BasicApidHandlerOwnedQueue::default()); - let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); - let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0); + let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let tc_vec = pus_tc.to_vec().unwrap(); let result = ccsds_distrib.pass_tc(&tc_vec[0..6]); assert!(result.is_err()); diff --git a/satrs/src/tmtc/pus_distrib.rs b/satrs/src/tmtc/pus_distrib.rs index f5d6c8d..53056bc 100644 --- a/satrs/src/tmtc/pus_distrib.rs +++ b/satrs/src/tmtc/pus_distrib.rs @@ -46,8 +46,8 @@ //! let mut pus_distributor = PusDistributor::new(service_handler); //! //! // Create and pass PUS ping telecommand with a valid APID -//! let mut space_packet_header = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); -//! let mut pus_tc = PusTcCreator::new_simple(&mut space_packet_header, 17, 1, None, true); +//! let sp_header = SpHeader::new_for_unseg_tc(0x002, 0x34, 0); +//! let mut pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &[], true); //! let mut test_buf: [u8; 32] = [0; 32]; //! let mut size = pus_tc //! .write_to_bytes(test_buf.as_mut_slice()) @@ -176,6 +176,7 @@ mod tests { BasicApidHandlerSharedQueue, }; use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler}; + use crate::ValidatorU16Id; use alloc::format; use alloc::vec::Vec; use spacepackets::ecss::PusError; @@ -253,17 +254,13 @@ mod tests { () => { type Error = PusError; - fn valid_apids(&self) -> &'static [u16] { - &[0x000, 0x002] - } - - fn handle_known_apid( + fn handle_packet_with_valid_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], ) -> Result<(), Self::Error> { self.handler_base - .handle_known_apid(&sp_header, tc_raw) + .handle_packet_with_valid_apid(&sp_header, tc_raw) .ok() .expect("Unexpected error"); match self.pus_distrib.pass_ccsds(&sp_header, tc_raw) { @@ -275,13 +272,13 @@ mod tests { } } - fn handle_unknown_apid( + fn handle_packet_with_unknown_apid( &mut self, sp_header: &SpHeader, tc_raw: &[u8], ) -> Result<(), Self::Error> { self.handler_base - .handle_unknown_apid(&sp_header, tc_raw) + .handle_packet_with_unknown_apid(&sp_header, tc_raw) .ok() .expect("Unexpected error"); Ok(()) @@ -289,6 +286,18 @@ mod tests { }; } + impl ValidatorU16Id for ApidHandlerOwned { + fn validate(&self, packet_id: u16) -> bool { + [0x000, 0x002].contains(&packet_id) + } + } + + impl ValidatorU16Id for ApidHandlerShared { + fn validate(&self, packet_id: u16) -> bool { + [0x000, 0x002].contains(&packet_id) + } + } + impl CcsdsPacketHandler for ApidHandlerOwned { apid_handler_impl!(); } diff --git a/satrs/src/tmtc/tm_helper.rs b/satrs/src/tmtc/tm_helper.rs index 192d574..a305472 100644 --- a/satrs/src/tmtc/tm_helper.rs +++ b/satrs/src/tmtc/tm_helper.rs @@ -1,5 +1,5 @@ use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; -use spacepackets::time::cds::TimeProvider; +use spacepackets::time::cds::CdsTime; use spacepackets::time::TimeWriter; use spacepackets::SpHeader; @@ -8,7 +8,9 @@ pub use std_mod::*; #[cfg(feature = "std")] pub mod std_mod { - use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StoreAddr}; + use crate::pool::{ + PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StoreAddr, StoreError, + }; use crate::pus::EcssTmtcError; use spacepackets::ecss::tm::PusTmCreator; use spacepackets::ecss::WritablePusPacket; @@ -34,7 +36,7 @@ pub mod std_mod { } pub fn add_pus_tm(&self, pus_tm: &PusTmCreator) -> Result { - let mut pg = self.0.write().map_err(|_| EcssTmtcError::StoreLock)?; + let mut pg = self.0.write().map_err(|_| StoreError::LockError)?; let addr = pg.free_element(pus_tm.len_written(), |buf| { pus_tm .write_to_bytes(buf) @@ -66,7 +68,7 @@ impl PusTmWithCdsShortHelper { source_data: &'a [u8], seq_count: u16, ) -> PusTmCreator { - let time_stamp = TimeProvider::from_now_with_u16_days().unwrap(); + 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) } @@ -76,7 +78,7 @@ impl PusTmWithCdsShortHelper { service: u8, subservice: u8, source_data: &'a [u8], - stamper: &TimeProvider, + stamper: &CdsTime, seq_count: u16, ) -> PusTmCreator { stamper.write_to_bytes(&mut self.cds_short_buf).unwrap(); @@ -90,22 +92,22 @@ impl PusTmWithCdsShortHelper { source_data: &'a [u8], seq_count: u16, ) -> PusTmCreator { - let mut reply_header = SpHeader::tm_unseg(self.apid, seq_count, 0).unwrap(); + 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(&mut reply_header, tc_header, source_data, true) + PusTmCreator::new(reply_header, tc_header, source_data, true) } } #[cfg(test)] mod tests { - use spacepackets::{ecss::PusPacket, time::cds::TimeProvider, CcsdsPacket}; + use spacepackets::{ecss::PusPacket, time::cds::CdsTime, CcsdsPacket}; use super::PusTmWithCdsShortHelper; #[test] fn test_helper_with_stamper() { let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123); - let stamper = TimeProvider::new_with_u16_days(0, 0); + let stamper = CdsTime::new_with_u16_days(0, 0); let tm = pus_tm_helper.create_pus_tm_with_stamper(17, 1, &[1, 2, 3, 4], &stamper, 25); assert_eq!(tm.service(), 17); assert_eq!(tm.subservice(), 1); diff --git a/satrs/tests/hk_helpers.rs b/satrs/tests/hk_helpers.rs index 8791b1e..d1b545e 100644 --- a/satrs/tests/hk_helpers.rs +++ b/satrs/tests/hk_helpers.rs @@ -2,7 +2,7 @@ use core::mem::size_of; use serde::{Deserialize, Serialize}; use spacepackets::ecss::{PfcReal, PfcUnsigned, Ptc}; -use spacepackets::time::cds::TimeProvider; +use spacepackets::time::cds::CdsTime; use spacepackets::time::{CcsdsTimeProvider, TimeWriter}; enum NumOfParamsInfo { @@ -36,7 +36,7 @@ struct TestMgmHkWithIndividualValidity { #[derive(Serialize, Deserialize)] struct TestMgmHkWithGroupValidity { - last_valid_stamp: TimeProvider, + last_valid_stamp: CdsTime, valid: bool, temp: f32, mgm_vals: [u16; 3], @@ -150,7 +150,7 @@ pub fn main() { // The easiest and probably best approach, trading off big advantages for TM downlink capacity: // Use a JSON format let mgm_hk_group_validity = TestMgmHkWithGroupValidity { - last_valid_stamp: TimeProvider::from_now_with_u16_days().unwrap(), + last_valid_stamp: CdsTime::now_with_u16_days().unwrap(), valid: false, temp: 20.0, mgm_vals: [0x1f1f, 0x2f2f, 0x3f3f], diff --git a/satrs/tests/mode_tree.rs b/satrs/tests/mode_tree.rs new file mode 100644 index 0000000..17f9836 --- /dev/null +++ b/satrs/tests/mode_tree.rs @@ -0,0 +1,358 @@ +use core::cell::Cell; +use std::{println, sync::mpsc}; + +use satrs::mode::{ + ModeError, ModeProvider, ModeReplyReceiver, ModeReplySender, ModeRequestHandler, + ModeRequestHandlerMpscBounded, ModeRequestReceiver, ModeRequestorAndHandlerMpscBounded, + ModeRequestorBoundedMpsc, +}; +use satrs::request::MessageMetadata; +use satrs::{ + mode::{ModeAndSubmode, ModeReply, ModeRequest}, + queue::GenericTargetedMessagingError, + request::GenericMessage, + ComponentId, +}; +use std::string::{String, ToString}; + +pub enum TestComponentId { + Device1 = 1, + Device2 = 2, + Assembly = 3, + PusModeService = 4, +} + +struct PusModeService { + pub request_id_counter: Cell, + pub mode_node: ModeRequestorBoundedMpsc, +} + +impl PusModeService { + pub fn send_announce_mode_cmd_to_assy(&self) { + self.mode_node + .send_mode_request( + self.request_id_counter.get(), + TestComponentId::Assembly as ComponentId, + ModeRequest::AnnounceModeRecursive, + ) + .unwrap(); + self.request_id_counter + .replace(self.request_id_counter.get() + 1); + } +} + +struct TestDevice { + pub name: String, + pub mode_node: ModeRequestHandlerMpscBounded, + pub mode_and_submode: ModeAndSubmode, +} + +impl TestDevice { + pub fn run(&mut self) { + self.check_mode_requests().expect("mode messaging error"); + } + + pub fn check_mode_requests(&mut self) -> Result<(), ModeError> { + if let Some(request) = self.mode_node.try_recv_mode_request()? { + self.handle_mode_request(request)? + } + Ok(()) + } +} + +impl ModeProvider for TestDevice { + fn mode_and_submode(&self) -> ModeAndSubmode { + self.mode_and_submode + } +} + +impl ModeRequestHandler for TestDevice { + type Error = ModeError; + + fn start_transition( + &mut self, + requestor: MessageMetadata, + mode_and_submode: ModeAndSubmode, + ) -> Result<(), ModeError> { + self.mode_and_submode = mode_and_submode; + self.handle_mode_reached(Some(requestor))?; + Ok(()) + } + + fn announce_mode(&self, _requestor_info: Option, _recursive: bool) { + println!( + "{}: announcing mode: {:?}", + self.name, self.mode_and_submode + ); + } + + fn handle_mode_reached(&mut self, requestor: Option) -> Result<(), ModeError> { + if let Some(requestor) = requestor { + self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode))?; + } + Ok(()) + } + fn send_mode_reply( + &self, + requestor_info: MessageMetadata, + reply: ModeReply, + ) -> Result<(), ModeError> { + self.mode_node.send_mode_reply(requestor_info, reply)?; + Ok(()) + } + + fn handle_mode_info( + &mut self, + requestor_info: MessageMetadata, + info: ModeAndSubmode, + ) -> Result<(), ModeError> { + // A device is a leaf in the tree.. so this really should not happen + println!( + "{}: unexpected mode info from {:?} with mode: {:?}", + self.name, + requestor_info.sender_id(), + info + ); + Ok(()) + } +} + +struct TestAssembly { + pub mode_node: ModeRequestorAndHandlerMpscBounded, + pub mode_requestor_info: Option, + pub mode_and_submode: ModeAndSubmode, + pub target_mode_and_submode: Option, +} + +impl ModeProvider for TestAssembly { + fn mode_and_submode(&self) -> ModeAndSubmode { + self.mode_and_submode + } +} + +impl TestAssembly { + pub fn run(&mut self) { + self.check_mode_requests().expect("mode messaging error"); + self.check_mode_replies().expect("mode messaging error"); + } + + pub fn check_mode_requests(&mut self) -> Result<(), GenericTargetedMessagingError> { + if let Some(request) = self.mode_node.try_recv_mode_request()? { + match request.message { + ModeRequest::SetMode(mode_and_submode) => { + self.start_transition(request.requestor_info, mode_and_submode) + .unwrap(); + } + ModeRequest::ReadMode => self + .mode_node + .send_mode_reply( + request.requestor_info, + ModeReply::ModeReply(self.mode_and_submode), + ) + .unwrap(), + ModeRequest::AnnounceMode => { + self.announce_mode(Some(request.requestor_info), false) + } + ModeRequest::AnnounceModeRecursive => { + self.announce_mode(Some(request.requestor_info), true) + } + ModeRequest::ModeInfo(_) => todo!(), + } + } + Ok(()) + } + + pub fn check_mode_replies(&mut self) -> Result<(), GenericTargetedMessagingError> { + if let Some(reply_and_id) = self.mode_node.try_recv_mode_reply()? { + match reply_and_id.message { + ModeReply::ModeReply(reply) => { + println!( + "TestAssembly: Received mode reply from {:?}, reached: {:?}", + reply_and_id.sender_id(), + reply + ); + } + ModeReply::CantReachMode(_) => todo!(), + ModeReply::WrongMode { expected, reached } => { + println!( + "TestAssembly: Wrong mode reply from {:?}, reached {:?}, expected {:?}", + reply_and_id.sender_id(), + reached, + expected + ); + } + } + } + Ok(()) + } +} + +impl ModeRequestHandler for TestAssembly { + type Error = ModeError; + fn start_transition( + &mut self, + requestor: MessageMetadata, + mode_and_submode: ModeAndSubmode, + ) -> Result<(), Self::Error> { + self.mode_requestor_info = Some(requestor); + self.target_mode_and_submode = Some(mode_and_submode); + Ok(()) + } + + fn announce_mode(&self, requestor_info: Option, recursive: bool) { + println!( + "TestAssembly: Announcing mode (recursively: {}): {:?}", + recursive, self.mode_and_submode + ); + // self.mode_requestor_info = Some((request_id, sender_id)); + let mut mode_request = ModeRequest::AnnounceMode; + if recursive { + mode_request = ModeRequest::AnnounceModeRecursive; + } + let request_id = requestor_info.map_or(0, |info| info.request_id()); + self.mode_node + .request_sender_map + .0 + .iter() + .for_each(|(_, sender)| { + sender + .send(GenericMessage::new( + MessageMetadata::new(request_id, self.mode_node.local_channel_id_generic()), + mode_request, + )) + .expect("sending mode request failed"); + }); + } + + fn handle_mode_reached( + &mut self, + mode_requestor: Option, + ) -> Result<(), Self::Error> { + if let Some(requestor) = mode_requestor { + self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode))?; + } + Ok(()) + } + + fn send_mode_reply( + &self, + requestor: MessageMetadata, + reply: ModeReply, + ) -> Result<(), Self::Error> { + self.mode_node.send_mode_reply(requestor, reply)?; + Ok(()) + } + + fn handle_mode_info( + &mut self, + _requestor_info: MessageMetadata, + _info: ModeAndSubmode, + ) -> Result<(), Self::Error> { + // TODO: A proper assembly must reach to mode changes of its children.. + Ok(()) + } +} + +fn main() { + // All request channel handles. + let (request_sender_to_dev1, request_receiver_dev1) = mpsc::sync_channel(10); + let (request_sender_to_dev2, request_receiver_dev2) = mpsc::sync_channel(10); + let (request_sender_to_assy, request_receiver_assy) = mpsc::sync_channel(10); + + // All reply channel handles. + let (reply_sender_to_assy, reply_receiver_assy) = mpsc::sync_channel(10); + let (reply_sender_to_pus, reply_receiver_pus) = mpsc::sync_channel(10); + + // Mode requestors and handlers. + let mut mode_node_assy = ModeRequestorAndHandlerMpscBounded::new( + TestComponentId::Assembly as ComponentId, + request_receiver_assy, + reply_receiver_assy, + ); + // Mode requestors only. + let mut mode_node_pus = ModeRequestorBoundedMpsc::new( + TestComponentId::PusModeService as ComponentId, + reply_receiver_pus, + ); + + // Request handlers only. + let mut mode_node_dev1 = ModeRequestHandlerMpscBounded::new( + TestComponentId::Device1 as ComponentId, + request_receiver_dev1, + ); + let mut mode_node_dev2 = ModeRequestHandlerMpscBounded::new( + TestComponentId::Device2 as ComponentId, + request_receiver_dev2, + ); + + // Set up mode request senders first. + mode_node_pus.add_message_target( + TestComponentId::Assembly as ComponentId, + request_sender_to_assy, + ); + mode_node_pus.add_message_target( + TestComponentId::Device1 as ComponentId, + request_sender_to_dev1.clone(), + ); + mode_node_pus.add_message_target( + TestComponentId::Device2 as ComponentId, + request_sender_to_dev2.clone(), + ); + mode_node_assy.add_request_target( + TestComponentId::Device1 as ComponentId, + request_sender_to_dev1, + ); + mode_node_assy.add_request_target( + TestComponentId::Device2 as ComponentId, + request_sender_to_dev2, + ); + + // Set up mode reply senders. + mode_node_dev1.add_message_target( + TestComponentId::Assembly as ComponentId, + reply_sender_to_assy.clone(), + ); + mode_node_dev1.add_message_target( + TestComponentId::PusModeService as ComponentId, + reply_sender_to_pus.clone(), + ); + mode_node_dev2.add_message_target( + TestComponentId::Assembly as ComponentId, + reply_sender_to_assy, + ); + mode_node_dev2.add_message_target( + TestComponentId::PusModeService as ComponentId, + reply_sender_to_pus.clone(), + ); + mode_node_assy.add_reply_target( + TestComponentId::PusModeService as ComponentId, + reply_sender_to_pus, + ); + + let mut device1 = TestDevice { + name: "Test Device 1".to_string(), + mode_node: mode_node_dev1, + mode_and_submode: ModeAndSubmode::new(0, 0), + }; + let mut device2 = TestDevice { + name: "Test Device 2".to_string(), + mode_node: mode_node_dev2, + mode_and_submode: ModeAndSubmode::new(0, 0), + }; + let mut assy = TestAssembly { + mode_node: mode_node_assy, + mode_requestor_info: None, + mode_and_submode: ModeAndSubmode::new(0, 0), + target_mode_and_submode: None, + }; + let pus_service = PusModeService { + request_id_counter: Cell::new(0), + mode_node: mode_node_pus, + }; + + pus_service.send_announce_mode_cmd_to_assy(); + assy.run(); + device1.run(); + device2.run(); + assy.run(); +} diff --git a/satrs/tests/pus_events.rs b/satrs/tests/pus_events.rs index ca6d71e..6fc518f 100644 --- a/satrs/tests/pus_events.rs +++ b/satrs/tests/pus_events.rs @@ -1,11 +1,14 @@ use satrs::event_man::{ - EventManagerWithMpsc, EventSendProvider, EventU32SenderMpsc, MpscEventU32Receiver, + EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider, + EventU32SenderMpsc, MpscEventU32Receiver, }; use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use satrs::params::U32Pair; use satrs::params::{Params, ParamsHeapless, WritableToBeBytes}; use satrs::pus::event_man::{DefaultPusEventMgmtBackend, EventReporter, PusEventDispatcher}; -use satrs::pus::TmAsVecSenderWithMpsc; +use satrs::pus::test_util::TEST_COMPONENT_ID_0; +use satrs::pus::PusTmAsVec; +use satrs::request::UniqueApidTargetId; use spacepackets::ecss::tm::PusTmReader; use spacepackets::ecss::{PusError, PusPacket}; use std::sync::mpsc::{self, SendError, TryRecvError}; @@ -15,6 +18,8 @@ const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::::const_new(1, 0); const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5); const EMPTY_STAMP: [u8; 7] = [0; 7]; +const TEST_APID: u16 = 0x02; +const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); #[derive(Debug, Clone)] pub enum CustomTmSenderError { @@ -30,42 +35,43 @@ fn test_threaded_usage() { let (pus_event_man_tx, pus_event_man_rx) = mpsc::channel(); let pus_event_man_send_provider = EventU32SenderMpsc::new(1, pus_event_man_tx); - event_man.subscribe_all(pus_event_man_send_provider.channel_id()); + event_man.subscribe_all(pus_event_man_send_provider.target_id()); event_man.add_sender(pus_event_man_send_provider); - let (event_tx, event_rx) = mpsc::channel(); - let reporter = EventReporter::new(0x02, 128).expect("Creating event reporter failed"); - let mut pus_event_man = - PusEventDispatcher::new(reporter, DefaultPusEventMgmtBackend::default()); + let (event_tx, event_rx) = mpsc::channel::(); + let reporter = + EventReporter::new(TEST_ID.raw(), 0x02, 0, 128).expect("Creating event reporter failed"); + let pus_event_man = PusEventDispatcher::new(reporter, DefaultPusEventMgmtBackend::default()); + let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| { + panic!("received routing error for event {event_msg:?}: {error:?}"); + }; // PUS + Generic event manager thread let jh0 = thread::spawn(move || { - let mut sender = TmAsVecSenderWithMpsc::new(0, "event_sender", event_tx); let mut event_cnt = 0; let mut params_array: [u8; 128] = [0; 128]; loop { - let res = event_man.try_event_handling(); - assert!(res.is_ok()); + event_man.try_event_handling(error_handler); match pus_event_man_rx.try_recv() { - Ok((event, aux_data)) => { - let mut gen_event = |aux_data| { + Ok(event_msg) => { + let gen_event = |aux_data| { pus_event_man.generate_pus_event_tm_generic( - &mut sender, + &event_tx, &EMPTY_STAMP, - event, + event_msg.event(), aux_data, ) }; - let res = if let Some(aux_data) = aux_data { + let res = if let Some(aux_data) = event_msg.params() { match aux_data { Params::Heapless(heapless) => match heapless { ParamsHeapless::Raw(raw) => { raw.write_to_be_bytes(&mut params_array) .expect("Writing raw parameter failed"); - gen_event(Some(¶ms_array[0..raw.raw_len()])) + gen_event(Some(¶ms_array[0..raw.written_len()])) } ParamsHeapless::EcssEnum(e) => { e.write_to_be_bytes(&mut params_array) .expect("Writing ECSS enum failed"); - gen_event(Some(¶ms_array[0..e.raw_len()])) + gen_event(Some(¶ms_array[0..e.written_len()])) } }, Params::Vec(vec) => gen_event(Some(vec.as_slice())), @@ -95,14 +101,17 @@ fn test_threaded_usage() { // Event sender and TM checker thread let jh1 = thread::spawn(move || { event_sender - .send((INFO_EVENT.into(), None)) + .send(EventMessage::new( + TEST_COMPONENT_ID_0.id(), + INFO_EVENT.into(), + )) .expect("Sending info event failed"); loop { match event_rx.try_recv() { // Event TM received successfully Ok(event_tm) => { - let tm = - PusTmReader::new(event_tm.as_slice(), 7).expect("Deserializing TM failed"); + let tm = PusTmReader::new(event_tm.packet.as_slice(), 7) + .expect("Deserializing TM failed"); assert_eq!(tm.0.service(), 5); assert_eq!(tm.0.subservice(), 1); let src_data = tm.0.source_data(); @@ -121,14 +130,18 @@ fn test_threaded_usage() { } } event_sender - .send((LOW_SEV_EVENT, Some(Params::Heapless((2_u32, 3_u32).into())))) + .send(EventMessage::new_with_params( + TEST_COMPONENT_ID_0.id(), + LOW_SEV_EVENT, + &Params::Heapless((2_u32, 3_u32).into()), + )) .expect("Sending low severity event failed"); loop { match event_rx.try_recv() { // Event TM received successfully Ok(event_tm) => { - let tm = - PusTmReader::new(event_tm.as_slice(), 7).expect("Deserializing TM failed"); + let tm = PusTmReader::new(event_tm.packet.as_slice(), 7) + .expect("Deserializing TM failed"); assert_eq!(tm.0.service(), 5); assert_eq!(tm.0.subservice(), 2); let src_data = tm.0.source_data(); diff --git a/satrs/tests/pus_verification.rs b/satrs/tests/pus_verification.rs index 386fea6..743535f 100644 --- a/satrs/tests/pus_verification.rs +++ b/satrs/tests/pus_verification.rs @@ -1,9 +1,10 @@ -#[cfg(feature = "crossbeam")] +// #[cfg(feature = "crossbeam")] pub mod crossbeam_test { use hashbrown::HashMap; use satrs::pool::{PoolProvider, PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig}; + use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0}; use satrs::pus::verification::{ - FailParams, RequestId, VerificationReporterCfg, VerificationReporterWithSender, + FailParams, RequestId, VerificationReporter, VerificationReporterCfg, VerificationReportingProvider, }; use satrs::pus::TmInSharedPoolSenderWithCrossbeam; @@ -16,7 +17,6 @@ pub mod crossbeam_test { use std::thread; use std::time::Duration; - const TEST_APID: u16 = 0x03; const FIXED_STAMP: [u8; 7] = [0; 7]; const PACKETS_SENT: u8 = 8; @@ -40,13 +40,9 @@ pub mod crossbeam_test { let shared_tc_pool_0 = Arc::new(RwLock::new(StaticMemoryPool::new(pool_cfg))); let shared_tc_pool_1 = shared_tc_pool_0.clone(); let (tx, rx) = crossbeam_channel::bounded(10); - let sender = TmInSharedPoolSenderWithCrossbeam::new( - 0, - "verif_sender", - shared_tm_pool.clone(), - tx.clone(), - ); - let mut reporter_with_sender_0 = VerificationReporterWithSender::new(&cfg, sender); + let sender_0 = TmInSharedPoolSenderWithCrossbeam::new(shared_tm_pool.clone(), tx.clone()); + let sender_1 = sender_0.clone(); + let mut reporter_with_sender_0 = VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &cfg); let mut reporter_with_sender_1 = reporter_with_sender_0.clone(); // For test purposes, we retrieve the request ID from the TCs and pass them to the receiver // tread. @@ -57,9 +53,9 @@ pub mod crossbeam_test { let (tx_tc_1, rx_tc_1) = crossbeam_channel::bounded(3); { let mut tc_guard = shared_tc_pool_0.write().unwrap(); - let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap(); + let sph = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0); let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let pus_tc_0 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true); + let pus_tc_0 = PusTcCreator::new_no_app_data(sph, tc_header, true); req_id_0 = RequestId::new(&pus_tc_0); let addr = tc_guard .free_element(pus_tc_0.len_written(), |buf| { @@ -67,9 +63,9 @@ pub mod crossbeam_test { }) .unwrap(); tx_tc_0.send(addr).unwrap(); - let mut sph = SpHeader::tc_unseg(TEST_APID, 1, 0).unwrap(); + let sph = SpHeader::new_for_unseg_tc(TEST_APID, 1, 0); let tc_header = PusTcSecondaryHeader::new_simple(5, 1); - let pus_tc_1 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true); + let pus_tc_1 = PusTcCreator::new_no_app_data(sph, tc_header, true); req_id_1 = RequestId::new(&pus_tc_1); let addr = tc_guard .free_element(pus_tc_0.len_written(), |buf| { @@ -93,24 +89,24 @@ pub mod crossbeam_test { let token = reporter_with_sender_0.add_tc_with_req_id(req_id_0); let accepted_token = reporter_with_sender_0 - .acceptance_success(token, &FIXED_STAMP) + .acceptance_success(&sender_0, token, &FIXED_STAMP) .expect("Acceptance success failed"); // Do some start handling here let started_token = reporter_with_sender_0 - .start_success(accepted_token, &FIXED_STAMP) + .start_success(&sender_0, accepted_token, &FIXED_STAMP) .expect("Start success failed"); // Do some step handling here reporter_with_sender_0 - .step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(0)) + .step_success(&sender_0, &started_token, &FIXED_STAMP, EcssEnumU8::new(0)) .expect("Start success failed"); // Finish up reporter_with_sender_0 - .step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(1)) + .step_success(&sender_0, &started_token, &FIXED_STAMP, EcssEnumU8::new(1)) .expect("Start success failed"); reporter_with_sender_0 - .completion_success(started_token, &FIXED_STAMP) + .completion_success(&sender_0, started_token, &FIXED_STAMP) .expect("Completion success failed"); }); @@ -128,15 +124,15 @@ pub mod crossbeam_test { let (tc, _) = PusTcReader::new(&tc_buf[0..tc_len]).unwrap(); let token = reporter_with_sender_1.add_tc(&tc); let accepted_token = reporter_with_sender_1 - .acceptance_success(token, &FIXED_STAMP) + .acceptance_success(&sender_1, token, &FIXED_STAMP) .expect("Acceptance success failed"); let started_token = reporter_with_sender_1 - .start_success(accepted_token, &FIXED_STAMP) + .start_success(&sender_1, accepted_token, &FIXED_STAMP) .expect("Start success failed"); let fail_code = EcssEnumU16::new(2); let params = FailParams::new_no_fail_data(&FIXED_STAMP, &fail_code); reporter_with_sender_1 - .completion_failure(started_token, params) + .completion_failure(&sender_1, started_token, params) .expect("Completion success failed"); }); @@ -145,14 +141,14 @@ pub mod crossbeam_test { let mut tm_buf: [u8; 1024] = [0; 1024]; let mut verif_map = HashMap::new(); while packet_counter < PACKETS_SENT { - let verif_addr = rx + let tm_in_pool = rx .recv_timeout(Duration::from_millis(50)) .expect("Packet reception timeout"); let tm_len; let shared_tm_store = shared_tm_pool.clone_backing_pool(); { let mut rg = shared_tm_store.write().expect("Error locking shared pool"); - let store_guard = rg.read_with_guard(verif_addr); + let store_guard = rg.read_with_guard(tm_in_pool.store_addr); tm_len = store_guard .read(&mut tm_buf) .expect("Error reading TM slice"); diff --git a/satrs/tests/tcp_servers.rs b/satrs/tests/tcp_servers.rs index b960df7..ff3fe78 100644 --- a/satrs/tests/tcp_servers.rs +++ b/satrs/tests/tcp_servers.rs @@ -31,7 +31,7 @@ use spacepackets::{ ecss::{tc::PusTcCreator, WritablePusPacket}, PacketId, SpHeader, }; -use std::{boxed::Box, collections::VecDeque, sync::Arc, vec::Vec}; +use std::{collections::VecDeque, sync::Arc, vec::Vec}; #[derive(Default, Clone)] struct SyncTcCacher { @@ -162,14 +162,14 @@ fn test_cobs_server() { } const TEST_APID_0: u16 = 0x02; -const TEST_PACKET_ID_0: PacketId = PacketId::const_tc(true, TEST_APID_0); +const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0); #[test] fn test_ccsds_server() { let tc_receiver = SyncTcCacher::default(); let mut tm_source = SyncTmSource::default(); - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let verif_tm = PusTcCreator::new_simple(&mut sph, 1, 1, None, true); + let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, 0, 0); + let verif_tm = PusTcCreator::new_simple(sph, 1, 1, &[], true); let tm_0 = verif_tm.to_vec().expect("tm generation failed"); tm_source.add_tm(&tm_0); let mut packet_id_lookup = HashSet::new(); @@ -178,7 +178,7 @@ fn test_ccsds_server() { ServerConfig::new(AUTO_PORT_ADDR, Duration::from_millis(2), 1024, 1024), tm_source, tc_receiver.clone(), - Box::new(packet_id_lookup), + packet_id_lookup, ) .expect("TCP server generation failed"); let dest_addr = tcp_server @@ -203,8 +203,8 @@ fn test_ccsds_server() { .expect("setting reas timeout failed"); // Send ping telecommand. - let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap(); - let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true); + let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, 0, 0); + let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let tc_0 = ping_tc.to_vec().expect("packet creation failed"); stream .write_all(&tc_0)