Compare commits
No commits in common. "main" and "satrs-v0.2.1" have entirely different histories.
main
...
satrs-v0.2
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -11,9 +11,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- run: cargo check
|
- run: cargo check --release
|
||||||
# Check example with static pool configuration
|
|
||||||
- run: cargo check -p satrs-example --no-default-features
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Run Tests
|
name: Run Tests
|
||||||
@ -24,7 +22,7 @@ jobs:
|
|||||||
- name: Install nextest
|
- name: Install nextest
|
||||||
uses: taiki-e/install-action@nextest
|
uses: taiki-e/install-action@nextest
|
||||||
- run: cargo nextest run --all-features
|
- run: cargo nextest run --all-features
|
||||||
- run: cargo test --doc --all-features
|
- run: cargo test --doc
|
||||||
|
|
||||||
cross-check:
|
cross-check:
|
||||||
name: Check Cross-Compilation
|
name: Check Cross-Compilation
|
||||||
@ -39,7 +37,7 @@ jobs:
|
|||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
|
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
|
||||||
- run: cargo check -p satrs --target=${{matrix.target}} --no-default-features
|
- run: cargo check -p satrs --release --target=${{matrix.target}} --no-default-features
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Check formatting
|
name: Check formatting
|
||||||
|
@ -9,6 +9,6 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"embedded-examples/stm32f3-disco-rtic",
|
"satrs-example-stm32f3-disco",
|
||||||
"embedded-examples/stm32h7-nucleo-rtic",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
31
README.md
31
README.md
@ -37,24 +37,16 @@ This project currently contains following crates:
|
|||||||
* [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example):
|
* [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example):
|
||||||
Example of a simple example on-board software using various sat-rs components which can be run
|
Example of a simple example on-board software using various sat-rs components which can be run
|
||||||
on a host computer or on any system with a standard runtime like a Raspberry Pi.
|
on a host computer or on any system with a standard runtime like a Raspberry Pi.
|
||||||
* [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim):
|
|
||||||
Mini-Simulator based on [asynchronix](https://github.com/asynchronics/asynchronix) which
|
|
||||||
simulates some physical devices for the `satrs-example` application device handlers.
|
|
||||||
* [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-mib):
|
* [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-mib):
|
||||||
Components to build a mission information base from the on-board software directly.
|
Components to build a mission information base from the on-board software directly.
|
||||||
* [`satrs-stm32f3-disco-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32f3-disco-rtic):
|
* [`satrs-example-stm32f3-disco`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example-stm32f3-disco):
|
||||||
Example of a simple example using low-level sat-rs components on a bare-metal system
|
Example of a simple example using low-level sat-rs components on a bare-metal system
|
||||||
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
|
with constrained resources.
|
||||||
framework on the STM32F3-Discovery device.
|
|
||||||
* [`satrs-stm32h-nucleo-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32h7-nucleo-rtic):
|
|
||||||
Example of a simple example using sat-rs components on a bare-metal system
|
|
||||||
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
|
|
||||||
framework on the STM32H743ZIT device.
|
|
||||||
|
|
||||||
Each project has its own `CHANGELOG.md`.
|
Each project has its own `CHANGELOG.md`.
|
||||||
|
|
||||||
# Related projects
|
# Related projects
|
||||||
|
|
||||||
In addition to the crates in this repository, the sat-rs project also maintains other libraries.
|
In addition to the crates in this repository, the sat-rs project also maintains other libraries.
|
||||||
|
|
||||||
* [`spacepackets`](https://egit.irs.uni-stuttgart.de/rust/spacepackets): Basic ECSS and CCSDS
|
* [`spacepackets`](https://egit.irs.uni-stuttgart.de/rust/spacepackets): Basic ECSS and CCSDS
|
||||||
@ -62,21 +54,6 @@ Each project has its own `CHANGELOG.md`.
|
|||||||
[`satrs`](https://egit.irs.uni-stuttgart.de/rust/satrs/src/branch/main/satrs)
|
[`satrs`](https://egit.irs.uni-stuttgart.de/rust/satrs/src/branch/main/satrs)
|
||||||
crate.
|
crate.
|
||||||
|
|
||||||
# Flight Heritage
|
|
||||||
|
|
||||||
There is an active and continuous effort to get early flight heritage for the sat-rs library.
|
|
||||||
Currently this library has the following flight heritage:
|
|
||||||
|
|
||||||
- Submission as an [OPS-SAT experiment](https://www.esa.int/Enabling_Support/Operations/OPS-SAT)
|
|
||||||
which has also
|
|
||||||
[flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/).
|
|
||||||
The application is strongly based on the sat-rs example application. You can find the repository
|
|
||||||
of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs).
|
|
||||||
- Development and use of a sat-rs-based [demonstration on-board software](https://egit.irs.uni-stuttgart.de/rust/eurosim-obsw)
|
|
||||||
alongside a Flight System Simulator in the context of a
|
|
||||||
[Bachelors Thesis](https://www.researchgate.net/publication/380785984_Design_and_Development_of_a_Hardware-in-the-Loop_EuroSim_Demonstrator)
|
|
||||||
at [Airbus Netherlands](https://www.airbusdefenceandspacenetherlands.nl/).
|
|
||||||
|
|
||||||
# Coverage
|
# Coverage
|
||||||
|
|
||||||
Coverage was generated using [`grcov`](https://github.com/mozilla/grcov). If you have not done so
|
Coverage was generated using [`grcov`](https://github.com/mozilla/grcov). If you have not done so
|
||||||
@ -87,5 +64,5 @@ rustup component add llvm-tools-preview
|
|||||||
cargo install grcov --locked
|
cargo install grcov --locked
|
||||||
```
|
```
|
||||||
|
|
||||||
After that, you can simply run `coverage.py` to test the `satrs` crate with coverage. You can
|
After that, you can simply run `coverage.py` to test the `satrs-core` crate with coverage. You can
|
||||||
optionally supply the `--open` flag to open the coverage report in your webbrowser.
|
optionally supply the `--open` flag to open the coverage report in your webbrowser.
|
||||||
|
2
automation/Jenkinsfile
vendored
2
automation/Jenkinsfile
vendored
@ -33,7 +33,7 @@ pipeline {
|
|||||||
stage('Test') {
|
stage('Test') {
|
||||||
steps {
|
steps {
|
||||||
sh 'cargo nextest r --all-features'
|
sh 'cargo nextest r --all-features'
|
||||||
sh 'cargo test --doc --all-features'
|
sh 'cargo test --doc'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Check with all features') {
|
stage('Check with all features') {
|
||||||
|
3
docs.sh
3
docs.sh
@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
|
||||||
cargo +nightly doc --all-features --open
|
|
@ -1,29 +0,0 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
|
||||||
runner = "probe-rs run --chip STM32H743ZITx"
|
|
||||||
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
|
||||||
|
|
||||||
rustflags = [
|
|
||||||
"-C", "linker=flip-link",
|
|
||||||
"-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",
|
|
||||||
# Can be useful for debugging.
|
|
||||||
# "-Clink-args=-Map=app.map"
|
|
||||||
]
|
|
||||||
|
|
||||||
[build]
|
|
||||||
# (`thumbv6m-*` is compatible with all ARM Cortex-M chips but using the right
|
|
||||||
# target improves performance)
|
|
||||||
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
|
||||||
# target = "thumbv7m-none-eabi" # Cortex-M3
|
|
||||||
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
|
|
||||||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
|
||||||
|
|
||||||
[alias]
|
|
||||||
rb = "run --bin"
|
|
||||||
rrb = "run --release --bin"
|
|
||||||
|
|
||||||
[env]
|
|
||||||
DEFMT_LOG = "info"
|
|
@ -1,4 +0,0 @@
|
|||||||
/target
|
|
||||||
/.cargo/config*
|
|
||||||
/.vscode
|
|
||||||
/app.map
|
|
871
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
871
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
@ -1,871 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bare-metal"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
|
||||||
dependencies = [
|
|
||||||
"rustc_version",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bare-metal"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitfield"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cast"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cobs"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-default"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cortex-m"
|
|
||||||
version = "0.7.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
|
||||||
dependencies = [
|
|
||||||
"bare-metal 0.2.5",
|
|
||||||
"bitfield",
|
|
||||||
"critical-section",
|
|
||||||
"embedded-hal 0.2.7",
|
|
||||||
"volatile-register",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cortex-m-rt"
|
|
||||||
version = "0.7.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
|
||||||
dependencies = [
|
|
||||||
"cortex-m-rt-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cortex-m-rt-macros"
|
|
||||||
version = "0.7.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[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.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
|
||||||
dependencies = [
|
|
||||||
"crc-catalog",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc-catalog"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "critical-section"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "defmt"
|
|
||||||
version = "0.3.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
|
||||||
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.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
|
||||||
dependencies = [
|
|
||||||
"defmt-parser",
|
|
||||||
"proc-macro-error2",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "defmt-parser"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
|
||||||
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.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "delegate"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "297806318ef30ad066b15792a8372858020ae3ca2e414ee6c2133b1eb9e9e945"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive-new"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-alloc"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd"
|
|
||||||
dependencies = [
|
|
||||||
"const-default",
|
|
||||||
"critical-section",
|
|
||||||
"linked_list_allocator",
|
|
||||||
"rlsf",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-dma"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
|
|
||||||
dependencies = [
|
|
||||||
"stable_deref_trait",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
|
||||||
dependencies = [
|
|
||||||
"nb 0.1.3",
|
|
||||||
"void",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
|
||||||
dependencies = [
|
|
||||||
"defmt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal-async"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
|
|
||||||
dependencies = [
|
|
||||||
"defmt",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal-bus"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57b4e6ede84339ebdb418cd986e6320a34b017cdf99b5cc3efceec6450b06886"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
"defmt",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"embedded-hal-async",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-storage"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equivalent"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fugit"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
|
|
||||||
dependencies = [
|
|
||||||
"gcd",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-core"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"pin-project-lite",
|
|
||||||
"pin-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gcd"
|
|
||||||
version = "2.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hash32"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.15.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heapless"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
|
||||||
dependencies = [
|
|
||||||
"defmt",
|
|
||||||
"hash32",
|
|
||||||
"stable_deref_trait",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "indexmap"
|
|
||||||
version = "2.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
|
||||||
dependencies = [
|
|
||||||
"equivalent",
|
|
||||||
"hashbrown",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.169"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linked_list_allocator"
|
|
||||||
version = "0.10.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "managed"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nb"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
|
||||||
dependencies = [
|
|
||||||
"nb 1.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nb"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_enum"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
|
||||||
dependencies = [
|
|
||||||
"num_enum_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_enum_derive"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "panic-probe"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4047d9235d1423d66cc97da7d07eddb54d4f154d6c13805c6d0793956f4f25b0"
|
|
||||||
dependencies = [
|
|
||||||
"cortex-m",
|
|
||||||
"defmt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "paste"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "portable-atomic"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error-attr2"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error2"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error-attr2",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.93"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.38"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rlsf"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"const-default",
|
|
||||||
"libc",
|
|
||||||
"svgbobdoc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic"
|
|
||||||
version = "2.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1"
|
|
||||||
dependencies = [
|
|
||||||
"bare-metal 1.0.0",
|
|
||||||
"cortex-m",
|
|
||||||
"critical-section",
|
|
||||||
"portable-atomic",
|
|
||||||
"rtic-core",
|
|
||||||
"rtic-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic-common"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0786b50b81ef9d2a944a000f60405bb28bf30cd45da2d182f3fe636b2321f35c"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic-core"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic-macros"
|
|
||||||
version = "2.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"proc-macro-error2",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic-monotonics"
|
|
||||||
version = "2.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cortex-m",
|
|
||||||
"fugit",
|
|
||||||
"portable-atomic",
|
|
||||||
"rtic-time",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic-sync"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "49b1200137ccb2bf272a1801fa6e27264535facd356cb2c1d5bc8e12aa211bad"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
"defmt",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"embedded-hal-async",
|
|
||||||
"embedded-hal-bus",
|
|
||||||
"heapless",
|
|
||||||
"portable-atomic",
|
|
||||||
"rtic-common",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic-time"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"embedded-hal-async",
|
|
||||||
"fugit",
|
|
||||||
"futures-util",
|
|
||||||
"rtic-common",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc_version"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
|
||||||
dependencies = [
|
|
||||||
"semver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "satrs"
|
|
||||||
version = "0.2.1"
|
|
||||||
dependencies = [
|
|
||||||
"cobs",
|
|
||||||
"crc",
|
|
||||||
"defmt",
|
|
||||||
"delegate",
|
|
||||||
"derive-new",
|
|
||||||
"heapless",
|
|
||||||
"num-traits",
|
|
||||||
"num_enum",
|
|
||||||
"paste",
|
|
||||||
"satrs-shared",
|
|
||||||
"smallvec",
|
|
||||||
"spacepackets",
|
|
||||||
"static_cell",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "satrs-shared"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f10b962c131d0bcb0de06fefa5d4716cbd7f836167f90229f7f38405dc573bd3"
|
|
||||||
dependencies = [
|
|
||||||
"spacepackets",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "satrs-stm32h7-nucleo-rtic"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"cortex-m",
|
|
||||||
"cortex-m-rt",
|
|
||||||
"cortex-m-semihosting",
|
|
||||||
"defmt",
|
|
||||||
"defmt-brtt",
|
|
||||||
"defmt-test",
|
|
||||||
"embedded-alloc",
|
|
||||||
"panic-probe",
|
|
||||||
"rtic",
|
|
||||||
"rtic-monotonics",
|
|
||||||
"rtic-sync",
|
|
||||||
"satrs",
|
|
||||||
"smoltcp",
|
|
||||||
"stm32h7xx-hal",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
|
||||||
dependencies = [
|
|
||||||
"semver-parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver-parser"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smoltcp"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"byteorder",
|
|
||||||
"cfg-if",
|
|
||||||
"defmt",
|
|
||||||
"heapless",
|
|
||||||
"managed",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spacepackets"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "591d42bfa6af2ab308e5efd8f0870e2d0eb5dd19a141bdcb7da731f5ff9cc11c"
|
|
||||||
dependencies = [
|
|
||||||
"crc",
|
|
||||||
"defmt",
|
|
||||||
"delegate",
|
|
||||||
"num-traits",
|
|
||||||
"num_enum",
|
|
||||||
"paste",
|
|
||||||
"thiserror",
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stable_deref_trait"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_cell"
|
|
||||||
version = "2.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e"
|
|
||||||
dependencies = [
|
|
||||||
"portable-atomic",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stm32h7"
|
|
||||||
version = "0.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "362f288cd8341e9209587b889c385f323e82fc237b60c272868965bb879bb9b1"
|
|
||||||
dependencies = [
|
|
||||||
"bare-metal 1.0.0",
|
|
||||||
"cortex-m",
|
|
||||||
"cortex-m-rt",
|
|
||||||
"vcell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stm32h7xx-hal"
|
|
||||||
version = "0.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3bd869329be25440b24e2b3583a1c016151b4a54bc36d96d82af7fcd9d010b98"
|
|
||||||
dependencies = [
|
|
||||||
"bare-metal 1.0.0",
|
|
||||||
"cast",
|
|
||||||
"cortex-m",
|
|
||||||
"embedded-dma",
|
|
||||||
"embedded-hal 0.2.7",
|
|
||||||
"embedded-storage",
|
|
||||||
"fugit",
|
|
||||||
"nb 1.1.0",
|
|
||||||
"paste",
|
|
||||||
"smoltcp",
|
|
||||||
"stm32h7",
|
|
||||||
"void",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "svgbobdoc"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50"
|
|
||||||
dependencies = [
|
|
||||||
"base64",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.96"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror"
|
|
||||||
version = "2.0.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror-impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror-impl"
|
|
||||||
version = "2.0.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.1.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vcell"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "void"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "volatile-register"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
|
||||||
dependencies = [
|
|
||||||
"vcell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.8.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.8.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
@ -1,85 +0,0 @@
|
|||||||
[package]
|
|
||||||
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"]
|
|
||||||
name = "satrs-stm32h7-nucleo-rtic"
|
|
||||||
edition = "2021"
|
|
||||||
version = "0.1.0"
|
|
||||||
default-run = "satrs-stm32h7-nucleo-rtic"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
# needed for each integration test
|
|
||||||
[[test]]
|
|
||||||
name = "integration"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[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"] }
|
|
||||||
cortex-m-semihosting = "0.5.0"
|
|
||||||
stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] }
|
|
||||||
embedded-alloc = "0.6"
|
|
||||||
rtic-sync = { version = "1", features = ["defmt-03"] }
|
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
|
||||||
version = "0.11"
|
|
||||||
default-features = false
|
|
||||||
features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"]
|
|
||||||
|
|
||||||
[dependencies.rtic]
|
|
||||||
version = "2"
|
|
||||||
features = ["thumbv7-backend"]
|
|
||||||
|
|
||||||
[dependencies.rtic-monotonics]
|
|
||||||
version = "2"
|
|
||||||
features = ["cortex-m-systick"]
|
|
||||||
|
|
||||||
[dependencies.satrs]
|
|
||||||
path = "../../satrs"
|
|
||||||
version = "0.2"
|
|
||||||
default-features = false
|
|
||||||
features = ["defmt", "heapless"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
defmt-test = "0.3"
|
|
||||||
|
|
||||||
# cargo build/run
|
|
||||||
[profile.dev]
|
|
||||||
codegen-units = 1
|
|
||||||
debug = 2
|
|
||||||
debug-assertions = true # <-
|
|
||||||
incremental = false
|
|
||||||
opt-level = 's' # <-
|
|
||||||
overflow-checks = true # <-
|
|
||||||
|
|
||||||
# cargo test
|
|
||||||
[profile.test]
|
|
||||||
codegen-units = 1
|
|
||||||
debug = 2
|
|
||||||
debug-assertions = true # <-
|
|
||||||
incremental = false
|
|
||||||
opt-level = 3 # <-
|
|
||||||
overflow-checks = true # <-
|
|
||||||
|
|
||||||
# cargo build/run --release
|
|
||||||
[profile.release]
|
|
||||||
codegen-units = 1
|
|
||||||
debug = 2
|
|
||||||
debug-assertions = false # <-
|
|
||||||
incremental = false
|
|
||||||
lto = 'fat'
|
|
||||||
opt-level = 3 # <-
|
|
||||||
overflow-checks = false # <-
|
|
||||||
|
|
||||||
# cargo test --release
|
|
||||||
[profile.bench]
|
|
||||||
codegen-units = 1
|
|
||||||
debug = 2
|
|
||||||
debug-assertions = false # <-
|
|
||||||
incremental = false
|
|
||||||
lto = 'fat'
|
|
||||||
opt-level = 3 # <-
|
|
||||||
overflow-checks = false # <-
|
|
@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
@ -1,118 +0,0 @@
|
|||||||
sat-rs example for the STM32H73ZI-Nucleo board
|
|
||||||
=======
|
|
||||||
|
|
||||||
This example application shows how the [sat-rs library](https://egit.irs.uni-stuttgart.de/rust/sat-rs)
|
|
||||||
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/2/book/en/) as the concurrency framework and the
|
|
||||||
[defmt](https://defmt.ferrous-systems.com/) framework for logging.
|
|
||||||
|
|
||||||
The STM32H743ZIT device was picked because it is one of the more powerful Cortex-M based devices
|
|
||||||
available for STM with which also has a little bit more RAM available and also allows commanding
|
|
||||||
via TCP/IP.
|
|
||||||
|
|
||||||
## Pre-Requisites
|
|
||||||
|
|
||||||
Make sure the following tools are installed:
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Building an application requires the `thumbv7em-none-eabihf` cross-compiler toolchain.
|
|
||||||
If you have not installed it yet, you can do so with
|
|
||||||
|
|
||||||
```sh
|
|
||||||
rustup target add thumbv7em-none-eabihf
|
|
||||||
```
|
|
||||||
|
|
||||||
A default `.cargo` config file is provided for this project, but needs to be copied to have
|
|
||||||
the correct name. This is so that the config file can be updated or edited for custom needs
|
|
||||||
without being tracked by git.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cp def_config.toml config.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
The configuration file will also set the target so it does not always have to be specified with
|
|
||||||
the `--target` argument.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
After that, assuming that you have a `.cargo/config.toml` setting the correct build target,
|
|
||||||
you can simply build the application with
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Flashing from the command line
|
|
||||||
|
|
||||||
You can flash the application from the command line using `probe-rs`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
probe-rs run --chip STM32H743ZITx
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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 [`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.
|
|
||||||
Use `cp vscode .vscode -r` to use them for your project.
|
|
||||||
|
|
||||||
Some sample configuration files for VS Code were provided as well. You can simply use `Run` and `Debug`
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 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)
|
|
||||||
on how to activate the environment and then use the following command to install the required
|
|
||||||
dependency:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
The packets are exchanged using a dedicated serial interface. You can use any generic USB-to-UART
|
|
||||||
converter device with the TX pin connected to the PA3 pin and the RX pin connected to the PA2 pin.
|
|
||||||
|
|
||||||
A default configuration file for the python application is provided and can be used by running
|
|
||||||
|
|
||||||
```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.
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
- [STM32H743ZI Ethernet link checker example](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/ethernet-nucleo-h743zi2.rs)
|
|
||||||
- [smoltcp DHCP client](https://github.com/smoltcp-rs/smoltcp/blob/main/examples/dhcp_client.rs)
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -1,119 +0,0 @@
|
|||||||
/* Taken from https://github.com/stm32-rs/stm32h7xx-hal/pull/299, adapted slightly to work with */
|
|
||||||
/* flip-link */
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
/* This file is intended for parts in the STM32H743/743v/753/753v families (RM0433), */
|
|
||||||
/* with the exception of the STM32H742/742v parts which have a different RAM layout. */
|
|
||||||
/* - FLASH and RAM are mandatory memory sections. */
|
|
||||||
/* - The sum of all non-FLASH sections must add to 1060K total device RAM. */
|
|
||||||
/* - The FLASH section size must match your device, see table below. */
|
|
||||||
|
|
||||||
/* FLASH */
|
|
||||||
/* Flash is divided in two independent banks (except 750xB). */
|
|
||||||
/* Select the appropriate FLASH size for your device. */
|
|
||||||
/* - STM32H750xB 128K (only FLASH1) */
|
|
||||||
/* - STM32H750xB 1M (512K + 512K) */
|
|
||||||
/* - STM32H743xI/753xI 2M ( 1M + 1M) */
|
|
||||||
FLASH1 : ORIGIN = 0x08000000, LENGTH = 1M
|
|
||||||
FLASH2 : ORIGIN = 0x08100000, LENGTH = 1M
|
|
||||||
|
|
||||||
/* Data TCM */
|
|
||||||
/* - Two contiguous 64KB RAMs. */
|
|
||||||
/* - Used for interrupt handlers, stacks and general RAM. */
|
|
||||||
/* - Zero wait-states. */
|
|
||||||
/* - The DTCM is taken as the origin of the base ram. (See below.) */
|
|
||||||
/* This is also where the interrupt table and such will live, */
|
|
||||||
/* which is required for deterministic performance. */
|
|
||||||
/* Need a region called RAM */
|
|
||||||
/* DTCM : ORIGIN = 0x20000000, LENGTH = 128K */
|
|
||||||
RAM : ORIGIN = 0x20000000, LENGTH = 128K
|
|
||||||
|
|
||||||
/* Instruction TCM */
|
|
||||||
/* - Used for latency-critical interrupt handlers etc. */
|
|
||||||
/* - Zero wait-states. */
|
|
||||||
ITCM : ORIGIN = 0x00000000, LENGTH = 64K
|
|
||||||
|
|
||||||
/* AXI SRAM */
|
|
||||||
/* - AXISRAM is in D1 and accessible by all system masters except BDMA. */
|
|
||||||
/* - Suitable for application data not stored in DTCM. */
|
|
||||||
/* - Zero wait-states. */
|
|
||||||
AXISRAM : ORIGIN = 0x24000000, LENGTH = 512K
|
|
||||||
|
|
||||||
/* AHB SRAM */
|
|
||||||
/* - SRAM1-3 are in D2 and accessible by all system masters except BDMA. */
|
|
||||||
/* Suitable for use as DMA buffers. */
|
|
||||||
/* - SRAM4 is in D3 and additionally accessible by the BDMA. Used for BDMA */
|
|
||||||
/* buffers, for storing application data in lower-power modes. */
|
|
||||||
/* - Zero wait-states. */
|
|
||||||
SRAM1 : ORIGIN = 0x30000000, LENGTH = 128K
|
|
||||||
SRAM2 : ORIGIN = 0x30020000, LENGTH = 128K
|
|
||||||
SRAM3 : ORIGIN = 0x30040000, LENGTH = 32K
|
|
||||||
SRAM4 : ORIGIN = 0x38000000, LENGTH = 64K
|
|
||||||
|
|
||||||
/* Backup SRAM */
|
|
||||||
BSRAM : ORIGIN = 0x38800000, LENGTH = 4K
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
/* Assign the memory regions defined above for use. */
|
|
||||||
/*
|
|
||||||
|
|
||||||
/* Provide the mandatory FLASH and RAM definitions for cortex-m-rt's linker script. */
|
|
||||||
/* These do not work with flip-link */
|
|
||||||
REGION_ALIAS(FLASH, FLASH1);
|
|
||||||
/* REGION_ALIAS(RAM, DTCM); */
|
|
||||||
|
|
||||||
/* The location of the stack can be overridden using the `_stack_start` symbol. */
|
|
||||||
/* - Set the stack location at the end of RAM, using all remaining space. */
|
|
||||||
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
|
||||||
|
|
||||||
/* The location of the .text section can be overridden using the */
|
|
||||||
/* `_stext` symbol. By default it will place after .vector_table. */
|
|
||||||
/* _stext = ORIGIN(FLASH) + 0x40c; */
|
|
||||||
|
|
||||||
/* Define sections for placing symbols into the extra memory regions above. */
|
|
||||||
/* This makes them accessible from code. */
|
|
||||||
/* - ITCM, DTCM and AXISRAM connect to a 64-bit wide bus -> align to 8 bytes. */
|
|
||||||
/* - All other memories connect to a 32-bit wide bus -> align to 4 bytes. */
|
|
||||||
SECTIONS {
|
|
||||||
.flash2 (NOLOAD) : ALIGN(4) {
|
|
||||||
*(.flash2 .flash2.*);
|
|
||||||
. = ALIGN(4);
|
|
||||||
} > FLASH2
|
|
||||||
|
|
||||||
.itcm (NOLOAD) : ALIGN(8) {
|
|
||||||
*(.itcm .itcm.*);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} > ITCM
|
|
||||||
|
|
||||||
.axisram (NOLOAD) : ALIGN(8) {
|
|
||||||
*(.axisram .axisram.*);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} > AXISRAM
|
|
||||||
|
|
||||||
.sram1 (NOLOAD) : ALIGN(8) {
|
|
||||||
*(.sram1 .sram1.*);
|
|
||||||
. = ALIGN(4);
|
|
||||||
} > SRAM1
|
|
||||||
|
|
||||||
.sram2 (NOLOAD) : ALIGN(8) {
|
|
||||||
*(.sram2 .sram2.*);
|
|
||||||
. = ALIGN(4);
|
|
||||||
} > SRAM2
|
|
||||||
|
|
||||||
.sram3 (NOLOAD) : ALIGN(4) {
|
|
||||||
*(.sram3 .sram3.*);
|
|
||||||
. = ALIGN(4);
|
|
||||||
} > SRAM3
|
|
||||||
|
|
||||||
.sram4 (NOLOAD) : ALIGN(4) {
|
|
||||||
*(.sram4 .sram4.*);
|
|
||||||
. = ALIGN(4);
|
|
||||||
} > SRAM4
|
|
||||||
|
|
||||||
.bsram (NOLOAD) : ALIGN(4) {
|
|
||||||
*(.bsram .bsram.*);
|
|
||||||
. = ALIGN(4);
|
|
||||||
} > BSRAM
|
|
||||||
|
|
||||||
};
|
|
@ -1,8 +0,0 @@
|
|||||||
/venv
|
|
||||||
/.tmtc-history.txt
|
|
||||||
/log
|
|
||||||
/.idea/*
|
|
||||||
!/.idea/runConfigurations
|
|
||||||
|
|
||||||
/seqcnt.txt
|
|
||||||
/tmtc_conf.json
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"com_if": "udp",
|
|
||||||
"tcpip_udp_port": 7301
|
|
||||||
}
|
|
@ -1,305 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Example client for the sat-rs example application"""
|
|
||||||
import struct
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from typing import Any, Optional, cast
|
|
||||||
from prompt_toolkit.history import FileHistory, History
|
|
||||||
from spacepackets.ecss.tm import CdsShortTimestamp
|
|
||||||
|
|
||||||
import tmtccmd
|
|
||||||
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusTm, PusVerificator
|
|
||||||
from spacepackets.ecss.pus_17_test import Service17Tm
|
|
||||||
from spacepackets.ecss.pus_1_verification import UnpackParams, Service1Tm
|
|
||||||
|
|
||||||
from tmtccmd import TcHandlerBase, ProcedureParamsWrapper
|
|
||||||
from tmtccmd.core.base import BackendRequest
|
|
||||||
from tmtccmd.core.ccsds_backend import QueueWrapper
|
|
||||||
from tmtccmd.logging import add_colorlog_console_logger
|
|
||||||
from tmtccmd.pus import VerificationWrapper
|
|
||||||
from tmtccmd.tmtc import CcsdsTmHandler, SpecificApidHandlerBase
|
|
||||||
from tmtccmd.com import ComInterface
|
|
||||||
from tmtccmd.config import (
|
|
||||||
CmdTreeNode,
|
|
||||||
default_json_path,
|
|
||||||
SetupParams,
|
|
||||||
HookBase,
|
|
||||||
params_to_procedure_conversion,
|
|
||||||
)
|
|
||||||
from tmtccmd.config.com import SerialCfgWrapper
|
|
||||||
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
|
||||||
from tmtccmd.logging.pus import (
|
|
||||||
RegularTmtcLogWrapper,
|
|
||||||
RawTmtcTimedLogWrapper,
|
|
||||||
TimedLogWhen,
|
|
||||||
)
|
|
||||||
from tmtccmd.tmtc import (
|
|
||||||
TcQueueEntryType,
|
|
||||||
ProcedureWrapper,
|
|
||||||
TcProcedureType,
|
|
||||||
FeedWrapper,
|
|
||||||
SendCbParams,
|
|
||||||
DefaultPusQueueHelper,
|
|
||||||
)
|
|
||||||
from tmtccmd.pus.s5_fsfw_event import Service5Tm
|
|
||||||
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
|
|
||||||
from tmtccmd.util.obj_id import ObjectIdDictT
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger()
|
|
||||||
|
|
||||||
EXAMPLE_PUS_APID = 0x02
|
|
||||||
|
|
||||||
|
|
||||||
class SatRsConfigHook(HookBase):
|
|
||||||
def __init__(self, json_cfg_path: str):
|
|
||||||
super().__init__(json_cfg_path)
|
|
||||||
|
|
||||||
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
|
||||||
from tmtccmd.config.com import (
|
|
||||||
create_com_interface_default,
|
|
||||||
create_com_interface_cfg_default,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert self.cfg_path is not None
|
|
||||||
cfg = create_com_interface_cfg_default(
|
|
||||||
com_if_key=com_if_key,
|
|
||||||
json_cfg_path=self.cfg_path,
|
|
||||||
space_packet_ids=None,
|
|
||||||
)
|
|
||||||
if cfg is None:
|
|
||||||
raise ValueError(
|
|
||||||
f"No valid configuration could be retrieved for the COM IF with key {com_if_key}"
|
|
||||||
)
|
|
||||||
if cfg.com_if_key == "serial_cobs":
|
|
||||||
cfg = cast(SerialCfgWrapper, cfg)
|
|
||||||
cfg.serial_cfg.serial_timeout = 0.5
|
|
||||||
return create_com_interface_default(cfg)
|
|
||||||
|
|
||||||
def get_command_definitions(self) -> CmdTreeNode:
|
|
||||||
"""This function should return the root node of the command definition tree."""
|
|
||||||
return create_cmd_definition_tree()
|
|
||||||
|
|
||||||
def get_cmd_history(self) -> Optional[History]:
|
|
||||||
"""Optionlly return a history class for the past command paths which will be used
|
|
||||||
when prompting a command path from the user in CLI mode."""
|
|
||||||
return FileHistory(".tmtc-history.txt")
|
|
||||||
|
|
||||||
def get_object_ids(self) -> ObjectIdDictT:
|
|
||||||
from tmtccmd.config.objects import get_core_object_ids
|
|
||||||
|
|
||||||
return get_core_object_ids()
|
|
||||||
|
|
||||||
|
|
||||||
def create_cmd_definition_tree() -> CmdTreeNode:
|
|
||||||
root_node = CmdTreeNode.root_node()
|
|
||||||
root_node.add_child(CmdTreeNode("ping", "Send PUS ping TC"))
|
|
||||||
root_node.add_child(CmdTreeNode("change_blink_freq", "Change blink frequency"))
|
|
||||||
return root_node
|
|
||||||
|
|
||||||
|
|
||||||
class PusHandler(SpecificApidHandlerBase):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
file_logger: logging.Logger,
|
|
||||||
verif_wrapper: VerificationWrapper,
|
|
||||||
raw_logger: RawTmtcTimedLogWrapper,
|
|
||||||
):
|
|
||||||
super().__init__(EXAMPLE_PUS_APID, None)
|
|
||||||
self.file_logger = file_logger
|
|
||||||
self.raw_logger = raw_logger
|
|
||||||
self.verif_wrapper = verif_wrapper
|
|
||||||
|
|
||||||
def handle_tm(self, packet: bytes, _user_args: Any):
|
|
||||||
try:
|
|
||||||
pus_tm = PusTm.unpack(
|
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
|
||||||
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
|
||||||
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
|
||||||
raise e
|
|
||||||
service = pus_tm.service
|
|
||||||
tm_packet = None
|
|
||||||
if service == 1:
|
|
||||||
tm_packet = Service1Tm.unpack(
|
|
||||||
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 1, 2)
|
|
||||||
)
|
|
||||||
res = self.verif_wrapper.add_tm(tm_packet)
|
|
||||||
if res is None:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Received Verification TM[{tm_packet.service}, {tm_packet.subservice}] "
|
|
||||||
f"with Request ID {tm_packet.tc_req_id.as_u32():#08x}"
|
|
||||||
)
|
|
||||||
_LOGGER.warning(
|
|
||||||
f"No matching telecommand found for {tm_packet.tc_req_id}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.verif_wrapper.log_to_console(tm_packet, res)
|
|
||||||
self.verif_wrapper.log_to_file(tm_packet, res)
|
|
||||||
if service == 3:
|
|
||||||
_LOGGER.info("No handling for HK packets implemented")
|
|
||||||
_LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
|
|
||||||
pus_tm = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
if pus_tm.subservice == 25:
|
|
||||||
if len(pus_tm.source_data) < 8:
|
|
||||||
raise ValueError("No addressable ID in HK packet")
|
|
||||||
json_str = pus_tm.source_data[8:]
|
|
||||||
_LOGGER.info("received JSON string: " + json_str.decode("utf-8"))
|
|
||||||
if service == 5:
|
|
||||||
tm_packet = Service5Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
if service == 17:
|
|
||||||
tm_packet = Service17Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
if tm_packet.subservice == 2:
|
|
||||||
_LOGGER.info("Received Ping Reply TM[17,2]")
|
|
||||||
else:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
|
||||||
)
|
|
||||||
if tm_packet is None:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"The service {service} is not implemented in Telemetry Factory"
|
|
||||||
)
|
|
||||||
tm_packet = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
|
||||||
self.raw_logger.log_tm(pus_tm)
|
|
||||||
|
|
||||||
|
|
||||||
def make_addressable_id(target_id: int, unique_id: int) -> bytes:
|
|
||||||
byte_string = bytearray(struct.pack("!I", target_id))
|
|
||||||
byte_string.extend(struct.pack("!I", unique_id))
|
|
||||||
return byte_string
|
|
||||||
|
|
||||||
|
|
||||||
class TcHandler(TcHandlerBase):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
seq_count_provider: FileSeqCountProvider,
|
|
||||||
verif_wrapper: VerificationWrapper,
|
|
||||||
):
|
|
||||||
super(TcHandler, self).__init__()
|
|
||||||
self.seq_count_provider = seq_count_provider
|
|
||||||
self.verif_wrapper = verif_wrapper
|
|
||||||
self.queue_helper = DefaultPusQueueHelper(
|
|
||||||
queue_wrapper=QueueWrapper.empty(),
|
|
||||||
tc_sched_timestamp_len=7,
|
|
||||||
seq_cnt_provider=seq_count_provider,
|
|
||||||
pus_verificator=verif_wrapper.pus_verificator,
|
|
||||||
default_pus_apid=EXAMPLE_PUS_APID,
|
|
||||||
)
|
|
||||||
|
|
||||||
def send_cb(self, send_params: SendCbParams):
|
|
||||||
entry_helper = send_params.entry
|
|
||||||
if entry_helper.is_tc:
|
|
||||||
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
|
||||||
pus_tc_wrapper = entry_helper.to_pus_tc_entry()
|
|
||||||
pus_tc_wrapper.pus_tc.seq_count = (
|
|
||||||
self.seq_count_provider.get_and_increment()
|
|
||||||
)
|
|
||||||
self.verif_wrapper.add_tc(pus_tc_wrapper.pus_tc)
|
|
||||||
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
|
||||||
_LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
|
||||||
send_params.com_if.send(raw_tc)
|
|
||||||
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
|
||||||
log_entry = entry_helper.to_log_entry()
|
|
||||||
_LOGGER.info(log_entry.log_str)
|
|
||||||
|
|
||||||
def queue_finished_cb(self, info: ProcedureWrapper):
|
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
|
||||||
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
|
||||||
|
|
||||||
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
|
||||||
q = self.queue_helper
|
|
||||||
q.queue_wrapper = wrapper.queue_wrapper
|
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
|
||||||
cmd_path = def_proc.cmd_path
|
|
||||||
if cmd_path == "/ping":
|
|
||||||
q.add_log_cmd("Sending PUS ping telecommand")
|
|
||||||
q.add_pus_tc(PusTelecommand(service=17, subservice=1))
|
|
||||||
if cmd_path == "/change_blink_freq":
|
|
||||||
self.create_change_blink_freq_command(q)
|
|
||||||
|
|
||||||
def create_change_blink_freq_command(self, q: DefaultPusQueueHelper):
|
|
||||||
q.add_log_cmd("Changing blink frequency")
|
|
||||||
while True:
|
|
||||||
blink_freq = int(
|
|
||||||
input(
|
|
||||||
"Please specify new blink frequency in ms. Valid Range [2..10000]: "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if blink_freq < 2 or blink_freq > 10000:
|
|
||||||
print(
|
|
||||||
"Invalid blink frequency. Please specify a value between 2 and 10000."
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
app_data = struct.pack("!I", blink_freq)
|
|
||||||
q.add_pus_tc(PusTelecommand(service=8, subservice=1, app_data=app_data))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
add_colorlog_console_logger(_LOGGER)
|
|
||||||
tmtccmd.init_printout(False)
|
|
||||||
hook_obj = SatRsConfigHook(json_cfg_path=default_json_path())
|
|
||||||
parser_wrapper = PreArgsParsingWrapper()
|
|
||||||
parser_wrapper.create_default_parent_parser()
|
|
||||||
parser_wrapper.create_default_parser()
|
|
||||||
parser_wrapper.add_def_proc_args()
|
|
||||||
params = SetupParams()
|
|
||||||
post_args_wrapper = parser_wrapper.parse(hook_obj, params)
|
|
||||||
proc_wrapper = ProcedureParamsWrapper()
|
|
||||||
if post_args_wrapper.use_gui:
|
|
||||||
post_args_wrapper.set_params_without_prompts(proc_wrapper)
|
|
||||||
else:
|
|
||||||
post_args_wrapper.set_params_with_prompts(proc_wrapper)
|
|
||||||
params.apid = EXAMPLE_PUS_APID
|
|
||||||
setup_args = SetupWrapper(
|
|
||||||
hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_wrapper
|
|
||||||
)
|
|
||||||
# Create console logger helper and file loggers
|
|
||||||
tmtc_logger = RegularTmtcLogWrapper()
|
|
||||||
file_logger = tmtc_logger.logger
|
|
||||||
raw_logger = RawTmtcTimedLogWrapper(when=TimedLogWhen.PER_HOUR, interval=1)
|
|
||||||
verificator = PusVerificator()
|
|
||||||
verification_wrapper = VerificationWrapper(verificator, _LOGGER, file_logger)
|
|
||||||
# Create primary TM handler and add it to the CCSDS Packet Handler
|
|
||||||
tm_handler = PusHandler(file_logger, verification_wrapper, raw_logger)
|
|
||||||
ccsds_handler = CcsdsTmHandler(generic_handler=None)
|
|
||||||
ccsds_handler.add_apid_handler(tm_handler)
|
|
||||||
|
|
||||||
# Create TC handler
|
|
||||||
seq_count_provider = PusFileSeqCountProvider()
|
|
||||||
tc_handler = TcHandler(seq_count_provider, verification_wrapper)
|
|
||||||
tmtccmd.setup(setup_args=setup_args)
|
|
||||||
init_proc = params_to_procedure_conversion(setup_args.proc_param_wrapper)
|
|
||||||
tmtc_backend = tmtccmd.create_default_tmtc_backend(
|
|
||||||
setup_wrapper=setup_args,
|
|
||||||
tm_handler=ccsds_handler,
|
|
||||||
tc_handler=tc_handler,
|
|
||||||
init_procedure=init_proc,
|
|
||||||
)
|
|
||||||
tmtccmd.start(tmtc_backend=tmtc_backend, hook_obj=hook_obj)
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
state = tmtc_backend.periodic_op(None)
|
|
||||||
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
|
||||||
sys.exit(0)
|
|
||||||
elif state.request == BackendRequest.DELAY_IDLE:
|
|
||||||
_LOGGER.info("TMTC Client in IDLE mode")
|
|
||||||
time.sleep(3.0)
|
|
||||||
elif state.request == BackendRequest.DELAY_LISTENER:
|
|
||||||
time.sleep(0.8)
|
|
||||||
elif state.request == BackendRequest.DELAY_CUSTOM:
|
|
||||||
if state.next_delay.total_seconds() <= 0.4:
|
|
||||||
time.sleep(state.next_delay.total_seconds())
|
|
||||||
else:
|
|
||||||
time.sleep(0.4)
|
|
||||||
elif state.request == BackendRequest.CALL_NEXT:
|
|
||||||
pass
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,2 +0,0 @@
|
|||||||
tmtccmd == 8.0.1
|
|
||||||
# -e git+https://github.com/robamu-org/tmtccmd.git@main#egg=tmtccmd
|
|
@ -1,55 +0,0 @@
|
|||||||
//! Blinks an LED
|
|
||||||
//!
|
|
||||||
//! This assumes that LD2 (blue) is connected to pb7 and LD3 (red) is connected
|
|
||||||
//! to pb14. This assumption is true for the nucleo-h743zi board.
|
|
||||||
|
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
use satrs_stm32h7_nucleo_rtic as _;
|
|
||||||
|
|
||||||
use stm32h7xx_hal::{block, prelude::*, timer::Timer};
|
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
|
||||||
|
|
||||||
#[entry]
|
|
||||||
fn main() -> ! {
|
|
||||||
defmt::println!("starting stm32h7 blinky example");
|
|
||||||
|
|
||||||
// Get access to the device specific peripherals from the peripheral access crate
|
|
||||||
let dp = stm32h7xx_hal::stm32::Peripherals::take().unwrap();
|
|
||||||
|
|
||||||
// Take ownership over the RCC devices and convert them into the corresponding HAL structs
|
|
||||||
let rcc = dp.RCC.constrain();
|
|
||||||
|
|
||||||
let pwr = dp.PWR.constrain();
|
|
||||||
let pwrcfg = pwr.freeze();
|
|
||||||
|
|
||||||
// Freeze the configuration of all the clocks in the system and
|
|
||||||
// retrieve the Core Clock Distribution and Reset (CCDR) object
|
|
||||||
let rcc = rcc.use_hse(8.MHz()).bypass_hse();
|
|
||||||
let ccdr = rcc.freeze(pwrcfg, &dp.SYSCFG);
|
|
||||||
|
|
||||||
// Acquire the GPIOB peripheral
|
|
||||||
let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);
|
|
||||||
|
|
||||||
// Configure gpio B pin 0 as a push-pull output.
|
|
||||||
let mut ld1 = gpiob.pb0.into_push_pull_output();
|
|
||||||
|
|
||||||
// Configure gpio B pin 7 as a push-pull output.
|
|
||||||
let mut ld2 = gpiob.pb7.into_push_pull_output();
|
|
||||||
|
|
||||||
// Configure gpio B pin 14 as a push-pull output.
|
|
||||||
let mut ld3 = gpiob.pb14.into_push_pull_output();
|
|
||||||
|
|
||||||
// Configure the timer to trigger an update every second
|
|
||||||
let mut timer = Timer::tim1(dp.TIM1, ccdr.peripheral.TIM1, &ccdr.clocks);
|
|
||||||
timer.start(1.Hz());
|
|
||||||
|
|
||||||
// Wait for the timer to trigger an update and change the state of the LED
|
|
||||||
loop {
|
|
||||||
ld1.toggle();
|
|
||||||
ld2.toggle();
|
|
||||||
ld3.toggle();
|
|
||||||
block!(timer.wait()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
use satrs_stm32h7_nucleo_rtic as _; // global logger + panicking-behavior + memory layout
|
|
||||||
|
|
||||||
#[cortex_m_rt::entry]
|
|
||||||
fn main() -> ! {
|
|
||||||
defmt::println!("Hello, world!");
|
|
||||||
|
|
||||||
satrs_stm32h7_nucleo_rtic::exit()
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
use cortex_m_semihosting::debug;
|
|
||||||
|
|
||||||
use defmt_brtt as _; // global logger
|
|
||||||
|
|
||||||
// TODO(5) adjust HAL import
|
|
||||||
use stm32h7xx_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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,523 +0,0 @@
|
|||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use rtic::app;
|
|
||||||
use rtic_monotonics::systick::prelude::*;
|
|
||||||
use satrs::pool::{PoolAddr, PoolProvider, StaticHeaplessMemoryPool};
|
|
||||||
use satrs::static_subpool;
|
|
||||||
// global logger + panicking-behavior + memory layout
|
|
||||||
use satrs_stm32h7_nucleo_rtic as _;
|
|
||||||
use smoltcp::socket::udp::UdpMetadata;
|
|
||||||
use smoltcp::socket::{dhcpv4, udp};
|
|
||||||
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
use embedded_alloc::LlffHeap as Heap;
|
|
||||||
use smoltcp::iface::{Config, Interface, SocketHandle, SocketSet, SocketStorage};
|
|
||||||
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};
|
|
||||||
use stm32h7xx_hal::ethernet;
|
|
||||||
|
|
||||||
const DEFAULT_BLINK_FREQ_MS: u32 = 1000;
|
|
||||||
const PORT: u16 = 7301;
|
|
||||||
|
|
||||||
const HEAP_SIZE: usize = 131_072;
|
|
||||||
|
|
||||||
const TC_SOURCE_CHANNEL_DEPTH: usize = 16;
|
|
||||||
pub type SharedPool = StaticHeaplessMemoryPool<3>;
|
|
||||||
pub type TcSourceChannel = rtic_sync::channel::Channel<PoolAddr, TC_SOURCE_CHANNEL_DEPTH>;
|
|
||||||
pub type TcSourceTx = rtic_sync::channel::Sender<'static, PoolAddr, TC_SOURCE_CHANNEL_DEPTH>;
|
|
||||||
pub type TcSourceRx = rtic_sync::channel::Receiver<'static, PoolAddr, TC_SOURCE_CHANNEL_DEPTH>;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static HEAP: Heap = Heap::empty();
|
|
||||||
|
|
||||||
systick_monotonic!(Mono, 1000);
|
|
||||||
|
|
||||||
// We place the memory pool buffers inside the larger AXISRAM.
|
|
||||||
pub const SUBPOOL_SMALL_NUM_BLOCKS: u16 = 32;
|
|
||||||
pub const SUBPOOL_SMALL_BLOCK_SIZE: usize = 32;
|
|
||||||
pub const SUBPOOL_MEDIUM_NUM_BLOCKS: u16 = 16;
|
|
||||||
pub const SUBPOOL_MEDIUM_BLOCK_SIZE: usize = 128;
|
|
||||||
pub const SUBPOOL_LARGE_NUM_BLOCKS: u16 = 8;
|
|
||||||
pub const SUBPOOL_LARGE_BLOCK_SIZE: usize = 2048;
|
|
||||||
|
|
||||||
// This data will be held by Net through a mutable reference
|
|
||||||
pub struct NetStorageStatic<'a> {
|
|
||||||
socket_storage: [SocketStorage<'a>; 8],
|
|
||||||
}
|
|
||||||
// MaybeUninit allows us write code that is correct even if STORE is not
|
|
||||||
// initialised by the runtime
|
|
||||||
static mut STORE: MaybeUninit<NetStorageStatic> = MaybeUninit::uninit();
|
|
||||||
|
|
||||||
static mut UDP_RX_META: [udp::PacketMetadata; 12] = [udp::PacketMetadata::EMPTY; 12];
|
|
||||||
static mut UDP_RX: [u8; 2048] = [0; 2048];
|
|
||||||
static mut UDP_TX_META: [udp::PacketMetadata; 12] = [udp::PacketMetadata::EMPTY; 12];
|
|
||||||
static mut UDP_TX: [u8; 2048] = [0; 2048];
|
|
||||||
|
|
||||||
/// Locally administered MAC address
|
|
||||||
const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44];
|
|
||||||
|
|
||||||
pub struct Net {
|
|
||||||
iface: Interface,
|
|
||||||
ethdev: ethernet::EthernetDMA<4, 4>,
|
|
||||||
dhcp_handle: SocketHandle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Net {
|
|
||||||
pub fn new(
|
|
||||||
sockets: &mut SocketSet<'static>,
|
|
||||||
mut ethdev: ethernet::EthernetDMA<4, 4>,
|
|
||||||
ethernet_addr: HardwareAddress,
|
|
||||||
) -> Self {
|
|
||||||
let config = Config::new(ethernet_addr);
|
|
||||||
let mut iface = Interface::new(
|
|
||||||
config,
|
|
||||||
&mut ethdev,
|
|
||||||
smoltcp::time::Instant::from_millis(Mono::now().duration_since_epoch().to_millis()),
|
|
||||||
);
|
|
||||||
// Create sockets
|
|
||||||
let dhcp_socket = dhcpv4::Socket::new();
|
|
||||||
iface.update_ip_addrs(|addrs| {
|
|
||||||
let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0));
|
|
||||||
});
|
|
||||||
|
|
||||||
let dhcp_handle = sockets.add(dhcp_socket);
|
|
||||||
Net {
|
|
||||||
iface,
|
|
||||||
ethdev,
|
|
||||||
dhcp_handle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Polls on the ethernet interface. You should refer to the smoltcp
|
|
||||||
/// documentation for poll() to understand how to call poll efficiently
|
|
||||||
pub fn poll<'a>(&mut self, sockets: &'a mut SocketSet) -> bool {
|
|
||||||
let uptime = Mono::now().duration_since_epoch();
|
|
||||||
let timestamp = smoltcp::time::Instant::from_millis(uptime.to_millis());
|
|
||||||
|
|
||||||
self.iface.poll(timestamp, &mut self.ethdev, sockets)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_dhcp<'a>(&mut self, sockets: &'a mut SocketSet) -> Option<dhcpv4::Event<'a>> {
|
|
||||||
let opt_event = sockets.get_mut::<dhcpv4::Socket>(self.dhcp_handle).poll();
|
|
||||||
if let Some(event) = &opt_event {
|
|
||||||
match event {
|
|
||||||
dhcpv4::Event::Deconfigured => {
|
|
||||||
defmt::info!("DHCP lost configuration");
|
|
||||||
self.iface.update_ip_addrs(|addrs| addrs.clear());
|
|
||||||
self.iface.routes_mut().remove_default_ipv4_route();
|
|
||||||
}
|
|
||||||
dhcpv4::Event::Configured(config) => {
|
|
||||||
defmt::info!("DHCP configuration acquired");
|
|
||||||
defmt::info!("IP address: {}", config.address);
|
|
||||||
self.iface.update_ip_addrs(|addrs| {
|
|
||||||
addrs.clear();
|
|
||||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(router) = config.router {
|
|
||||||
defmt::debug!("Default gateway: {}", router);
|
|
||||||
self.iface
|
|
||||||
.routes_mut()
|
|
||||||
.add_default_ipv4_route(router)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
defmt::debug!("Default gateway: None");
|
|
||||||
self.iface.routes_mut().remove_default_ipv4_route();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opt_event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UdpNet {
|
|
||||||
udp_handle: SocketHandle,
|
|
||||||
last_client: Option<UdpMetadata>,
|
|
||||||
tc_source_tx: TcSourceTx,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UdpNet {
|
|
||||||
pub fn new<'sockets>(sockets: &mut SocketSet<'sockets>, tc_source_tx: TcSourceTx) -> Self {
|
|
||||||
// SAFETY: The RX and TX buffers are passed here and not used anywhere else.
|
|
||||||
let udp_rx_buffer =
|
|
||||||
smoltcp::socket::udp::PacketBuffer::new(unsafe { &mut UDP_RX_META[..] }, unsafe {
|
|
||||||
&mut UDP_RX[..]
|
|
||||||
});
|
|
||||||
let udp_tx_buffer =
|
|
||||||
smoltcp::socket::udp::PacketBuffer::new(unsafe { &mut UDP_TX_META[..] }, unsafe {
|
|
||||||
&mut UDP_TX[..]
|
|
||||||
});
|
|
||||||
let udp_socket = smoltcp::socket::udp::Socket::new(udp_rx_buffer, udp_tx_buffer);
|
|
||||||
|
|
||||||
let udp_handle = sockets.add(udp_socket);
|
|
||||||
Self {
|
|
||||||
udp_handle,
|
|
||||||
last_client: None,
|
|
||||||
tc_source_tx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll<'sockets>(
|
|
||||||
&mut self,
|
|
||||||
sockets: &'sockets mut SocketSet,
|
|
||||||
shared_pool: &mut SharedPool,
|
|
||||||
) {
|
|
||||||
let socket = sockets.get_mut::<udp::Socket>(self.udp_handle);
|
|
||||||
if !socket.is_open() {
|
|
||||||
if let Err(e) = socket.bind(PORT) {
|
|
||||||
defmt::warn!("binding UDP socket failed: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
match socket.recv() {
|
|
||||||
Ok((data, client)) => {
|
|
||||||
match shared_pool.add(data) {
|
|
||||||
Ok(store_addr) => {
|
|
||||||
if let Err(e) = self.tc_source_tx.try_send(store_addr) {
|
|
||||||
defmt::warn!("TC source channel is full: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
defmt::warn!("could not add UDP packet to shared pool: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.last_client = Some(client);
|
|
||||||
// TODO: Implement packet wiretapping.
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
udp::RecvError::Exhausted => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
udp::RecvError::Truncated => {
|
|
||||||
defmt::warn!("UDP packet was truncacted");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[app(device = stm32h7xx_hal::stm32, peripherals = true)]
|
|
||||||
mod app {
|
|
||||||
use core::ptr::addr_of_mut;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use rtic_monotonics::fugit::MillisDurationU32;
|
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
|
||||||
use stm32h7xx_hal::ethernet::{EthernetMAC, PHY};
|
|
||||||
use stm32h7xx_hal::gpio::{Output, Pin};
|
|
||||||
use stm32h7xx_hal::prelude::*;
|
|
||||||
use stm32h7xx_hal::stm32::Interrupt;
|
|
||||||
|
|
||||||
struct BlinkyLeds {
|
|
||||||
led1: Pin<'B', 7, Output>,
|
|
||||||
led2: Pin<'B', 14, Output>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[local]
|
|
||||||
struct Local {
|
|
||||||
leds: BlinkyLeds,
|
|
||||||
link_led: Pin<'B', 0, Output>,
|
|
||||||
net: Net,
|
|
||||||
udp: UdpNet,
|
|
||||||
tc_source_rx: TcSourceRx,
|
|
||||||
phy: ethernet::phy::LAN8742A<EthernetMAC>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[shared]
|
|
||||||
struct Shared {
|
|
||||||
blink_freq: MillisDurationU32,
|
|
||||||
eth_link_up: bool,
|
|
||||||
sockets: SocketSet<'static>,
|
|
||||||
shared_pool: SharedPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[init]
|
|
||||||
fn init(mut cx: init::Context) -> (Shared, Local) {
|
|
||||||
defmt::println!("Starting sat-rs demo application for the STM32H743ZIT");
|
|
||||||
|
|
||||||
let pwr = cx.device.PWR.constrain();
|
|
||||||
let pwrcfg = pwr.freeze();
|
|
||||||
|
|
||||||
let rcc = cx.device.RCC.constrain();
|
|
||||||
// Try to keep the clock configuration similar to one used in STM examples:
|
|
||||||
// https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/NUCLEO-H743ZI/Examples/GPIO/GPIO_EXTI/Src/main.c
|
|
||||||
let ccdr = rcc
|
|
||||||
.sys_ck(400.MHz())
|
|
||||||
.hclk(200.MHz())
|
|
||||||
.use_hse(8.MHz())
|
|
||||||
.bypass_hse()
|
|
||||||
.pclk1(100.MHz())
|
|
||||||
.pclk2(100.MHz())
|
|
||||||
.pclk3(100.MHz())
|
|
||||||
.pclk4(100.MHz())
|
|
||||||
.freeze(pwrcfg, &cx.device.SYSCFG);
|
|
||||||
|
|
||||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
|
||||||
Mono::start(cx.core.SYST, ccdr.clocks.sys_ck().to_Hz());
|
|
||||||
|
|
||||||
// Those are used in the smoltcp of the stm32h7xx-hal , I am not fully sure what they are
|
|
||||||
// good for.
|
|
||||||
cx.core.SCB.enable_icache();
|
|
||||||
cx.core.DWT.enable_cycle_counter();
|
|
||||||
|
|
||||||
let gpioa = cx.device.GPIOA.split(ccdr.peripheral.GPIOA);
|
|
||||||
let gpiob = cx.device.GPIOB.split(ccdr.peripheral.GPIOB);
|
|
||||||
let gpioc = cx.device.GPIOC.split(ccdr.peripheral.GPIOC);
|
|
||||||
let gpiog = cx.device.GPIOG.split(ccdr.peripheral.GPIOG);
|
|
||||||
|
|
||||||
let link_led = gpiob.pb0.into_push_pull_output();
|
|
||||||
let mut led1 = gpiob.pb7.into_push_pull_output();
|
|
||||||
let mut led2 = gpiob.pb14.into_push_pull_output();
|
|
||||||
|
|
||||||
// Criss-cross pattern looks cooler.
|
|
||||||
led1.set_high();
|
|
||||||
led2.set_low();
|
|
||||||
let leds = BlinkyLeds { led1, led2 };
|
|
||||||
|
|
||||||
let rmii_ref_clk = gpioa.pa1.into_alternate::<11>();
|
|
||||||
let rmii_mdio = gpioa.pa2.into_alternate::<11>();
|
|
||||||
let rmii_mdc = gpioc.pc1.into_alternate::<11>();
|
|
||||||
let rmii_crs_dv = gpioa.pa7.into_alternate::<11>();
|
|
||||||
let rmii_rxd0 = gpioc.pc4.into_alternate::<11>();
|
|
||||||
let rmii_rxd1 = gpioc.pc5.into_alternate::<11>();
|
|
||||||
let rmii_tx_en = gpiog.pg11.into_alternate::<11>();
|
|
||||||
let rmii_txd0 = gpiog.pg13.into_alternate::<11>();
|
|
||||||
let rmii_txd1 = gpiob.pb13.into_alternate::<11>();
|
|
||||||
|
|
||||||
let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS);
|
|
||||||
|
|
||||||
/// Ethernet descriptor rings are a global singleton
|
|
||||||
#[link_section = ".sram3.eth"]
|
|
||||||
static mut DES_RING: MaybeUninit<ethernet::DesRing<4, 4>> = MaybeUninit::uninit();
|
|
||||||
|
|
||||||
let (eth_dma, eth_mac) = ethernet::new(
|
|
||||||
cx.device.ETHERNET_MAC,
|
|
||||||
cx.device.ETHERNET_MTL,
|
|
||||||
cx.device.ETHERNET_DMA,
|
|
||||||
(
|
|
||||||
rmii_ref_clk,
|
|
||||||
rmii_mdio,
|
|
||||||
rmii_mdc,
|
|
||||||
rmii_crs_dv,
|
|
||||||
rmii_rxd0,
|
|
||||||
rmii_rxd1,
|
|
||||||
rmii_tx_en,
|
|
||||||
rmii_txd0,
|
|
||||||
rmii_txd1,
|
|
||||||
),
|
|
||||||
// SAFETY: We do not move the returned DMA struct across thread boundaries, so this
|
|
||||||
// should be safe according to the docs.
|
|
||||||
unsafe { DES_RING.assume_init_mut() },
|
|
||||||
mac_addr,
|
|
||||||
ccdr.peripheral.ETH1MAC,
|
|
||||||
&ccdr.clocks,
|
|
||||||
);
|
|
||||||
// Initialise ethernet PHY...
|
|
||||||
let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac.set_phy_addr(0));
|
|
||||||
lan8742a.phy_reset();
|
|
||||||
lan8742a.phy_init();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ethernet::enable_interrupt();
|
|
||||||
cx.core.NVIC.set_priority(Interrupt::ETH, 196); // Mid prio
|
|
||||||
cortex_m::peripheral::NVIC::unmask(Interrupt::ETH);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsafe: mutable reference to static storage, we only do this once
|
|
||||||
let store = unsafe {
|
|
||||||
let store_ptr = STORE.as_mut_ptr();
|
|
||||||
|
|
||||||
// Initialise the socket_storage field. Using `write` instead of
|
|
||||||
// assignment via `=` to not call `drop` on the old, uninitialised
|
|
||||||
// value
|
|
||||||
addr_of_mut!((*store_ptr).socket_storage).write([SocketStorage::EMPTY; 8]);
|
|
||||||
|
|
||||||
// Now that all fields are initialised we can safely use
|
|
||||||
// assume_init_mut to return a mutable reference to STORE
|
|
||||||
STORE.assume_init_mut()
|
|
||||||
};
|
|
||||||
|
|
||||||
let (tc_source_tx, tc_source_rx) =
|
|
||||||
rtic_sync::make_channel!(PoolAddr, TC_SOURCE_CHANNEL_DEPTH);
|
|
||||||
|
|
||||||
let mut sockets = SocketSet::new(&mut store.socket_storage[..]);
|
|
||||||
let net = Net::new(&mut sockets, eth_dma, mac_addr.into());
|
|
||||||
let udp = UdpNet::new(&mut sockets, tc_source_tx);
|
|
||||||
|
|
||||||
let mut shared_pool: SharedPool = StaticHeaplessMemoryPool::new(true);
|
|
||||||
static_subpool!(
|
|
||||||
SUBPOOL_SMALL,
|
|
||||||
SUBPOOL_SMALL_SIZES,
|
|
||||||
SUBPOOL_SMALL_NUM_BLOCKS as usize,
|
|
||||||
SUBPOOL_SMALL_BLOCK_SIZE,
|
|
||||||
link_section = ".axisram"
|
|
||||||
);
|
|
||||||
static_subpool!(
|
|
||||||
SUBPOOL_MEDIUM,
|
|
||||||
SUBPOOL_MEDIUM_SIZES,
|
|
||||||
SUBPOOL_MEDIUM_NUM_BLOCKS as usize,
|
|
||||||
SUBPOOL_MEDIUM_BLOCK_SIZE,
|
|
||||||
link_section = ".axisram"
|
|
||||||
);
|
|
||||||
static_subpool!(
|
|
||||||
SUBPOOL_LARGE,
|
|
||||||
SUBPOOL_LARGE_SIZES,
|
|
||||||
SUBPOOL_LARGE_NUM_BLOCKS as usize,
|
|
||||||
SUBPOOL_LARGE_BLOCK_SIZE,
|
|
||||||
link_section = ".axisram"
|
|
||||||
);
|
|
||||||
|
|
||||||
shared_pool
|
|
||||||
.grow(
|
|
||||||
SUBPOOL_SMALL.get_mut().unwrap(),
|
|
||||||
SUBPOOL_SMALL_SIZES.get_mut().unwrap(),
|
|
||||||
SUBPOOL_SMALL_NUM_BLOCKS,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.expect("growing heapless memory pool failed");
|
|
||||||
shared_pool
|
|
||||||
.grow(
|
|
||||||
SUBPOOL_MEDIUM.get_mut().unwrap(),
|
|
||||||
SUBPOOL_MEDIUM_SIZES.get_mut().unwrap(),
|
|
||||||
SUBPOOL_MEDIUM_NUM_BLOCKS,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.expect("growing heapless memory pool failed");
|
|
||||||
shared_pool
|
|
||||||
.grow(
|
|
||||||
SUBPOOL_LARGE.get_mut().unwrap(),
|
|
||||||
SUBPOOL_LARGE_SIZES.get_mut().unwrap(),
|
|
||||||
SUBPOOL_LARGE_NUM_BLOCKS,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.expect("growing heapless memory pool failed");
|
|
||||||
|
|
||||||
// Set up global allocator. Use AXISRAM for the heap.
|
|
||||||
#[link_section = ".axisram"]
|
|
||||||
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
|
||||||
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
|
|
||||||
|
|
||||||
eth_link_check::spawn().expect("eth link check failed");
|
|
||||||
blinky::spawn().expect("spawning blink task failed");
|
|
||||||
udp_task::spawn().expect("spawning UDP task failed");
|
|
||||||
tc_source_task::spawn().expect("spawning TC source task failed");
|
|
||||||
|
|
||||||
(
|
|
||||||
Shared {
|
|
||||||
blink_freq: MillisDurationU32::from_ticks(DEFAULT_BLINK_FREQ_MS),
|
|
||||||
eth_link_up: false,
|
|
||||||
sockets,
|
|
||||||
shared_pool,
|
|
||||||
},
|
|
||||||
Local {
|
|
||||||
link_led,
|
|
||||||
leds,
|
|
||||||
net,
|
|
||||||
udp,
|
|
||||||
tc_source_rx,
|
|
||||||
phy: lan8742a,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(local = [leds], shared=[blink_freq])]
|
|
||||||
async fn blinky(mut cx: blinky::Context) {
|
|
||||||
let leds = cx.local.leds;
|
|
||||||
loop {
|
|
||||||
leds.led1.toggle();
|
|
||||||
leds.led2.toggle();
|
|
||||||
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
|
||||||
Mono::delay(current_blink_freq).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This task checks for the network link.
|
|
||||||
#[task(local=[link_led, phy], shared=[eth_link_up])]
|
|
||||||
async fn eth_link_check(mut cx: eth_link_check::Context) {
|
|
||||||
let phy = cx.local.phy;
|
|
||||||
let link_led = cx.local.link_led;
|
|
||||||
loop {
|
|
||||||
let link_was_up = cx.shared.eth_link_up.lock(|link_up| *link_up);
|
|
||||||
if phy.poll_link() {
|
|
||||||
if !link_was_up {
|
|
||||||
link_led.set_high();
|
|
||||||
cx.shared.eth_link_up.lock(|link_up| *link_up = true);
|
|
||||||
defmt::info!("Ethernet link up");
|
|
||||||
}
|
|
||||||
} else if link_was_up {
|
|
||||||
link_led.set_low();
|
|
||||||
cx.shared.eth_link_up.lock(|link_up| *link_up = false);
|
|
||||||
defmt::info!("Ethernet link down");
|
|
||||||
}
|
|
||||||
Mono::delay(100.millis()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(binds=ETH, local=[net], shared=[sockets])]
|
|
||||||
fn eth_isr(mut cx: eth_isr::Context) {
|
|
||||||
// SAFETY: We do not write the register mentioned inside the docs anywhere else.
|
|
||||||
unsafe {
|
|
||||||
ethernet::interrupt_handler();
|
|
||||||
}
|
|
||||||
// Check and process ETH frames and DHCP. UDP is checked in a different task.
|
|
||||||
cx.shared.sockets.lock(|sockets| {
|
|
||||||
cx.local.net.poll(sockets);
|
|
||||||
cx.local.net.poll_dhcp(sockets);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This task routes UDP packets.
|
|
||||||
#[task(local=[udp], shared=[sockets, shared_pool])]
|
|
||||||
async fn udp_task(mut cx: udp_task::Context) {
|
|
||||||
loop {
|
|
||||||
cx.shared.sockets.lock(|sockets| {
|
|
||||||
cx.shared.shared_pool.lock(|pool| {
|
|
||||||
cx.local.udp.poll(sockets, pool);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
Mono::delay(40.millis()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This task handles all the incoming telecommands.
|
|
||||||
#[task(local=[read_buf: [u8; 1024] = [0; 1024], tc_source_rx], shared=[shared_pool])]
|
|
||||||
async fn tc_source_task(mut cx: tc_source_task::Context) {
|
|
||||||
loop {
|
|
||||||
let recv_result = cx.local.tc_source_rx.recv().await;
|
|
||||||
match recv_result {
|
|
||||||
Ok(pool_addr) => {
|
|
||||||
cx.shared.shared_pool.lock(|pool| {
|
|
||||||
match pool.read(&pool_addr, cx.local.read_buf.as_mut()) {
|
|
||||||
Ok(packet_len) => {
|
|
||||||
defmt::info!("received {} bytes in the TC source task", packet_len);
|
|
||||||
match PusTcReader::new(&cx.local.read_buf[0..packet_len]) {
|
|
||||||
Ok((packet, _tc_len)) => {
|
|
||||||
// TODO: Handle packet here or dispatch to dedicated PUS
|
|
||||||
// handler? Dispatching could simplify some things and make
|
|
||||||
// the software more scalable..
|
|
||||||
defmt::info!("received PUS packet: {}", packet);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
defmt::info!("invalid TC format, not a PUS packet: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Err(e) = pool.delete(pool_addr) {
|
|
||||||
defmt::warn!("deleting TC data failed: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
defmt::warn!("TC packet read failed: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
defmt::warn!("TC source reception error: {}", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
use stm32h7_testapp as _; // memory layout + panic handler
|
|
||||||
|
|
||||||
// See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state'
|
|
||||||
// feature)
|
|
||||||
#[defmt_test::tests]
|
|
||||||
mod tests {
|
|
||||||
use defmt::assert;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
assert!(true)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
/settings.json
|
|
||||||
/.cortex-debug.*
|
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
|
||||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
|
||||||
|
|
||||||
// List of extensions which should be recommended for users of this workspace.
|
|
||||||
"recommendations": [
|
|
||||||
"rust-lang.rust",
|
|
||||||
"probe-rs.probe-rs-debugger"
|
|
||||||
],
|
|
||||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
|
||||||
"unwantedRecommendations": []
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"preLaunchTask": "${defaultBuildTask}",
|
|
||||||
"type": "probe-rs-debug",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "probe-rs Debugging ",
|
|
||||||
"flashingConfig": {
|
|
||||||
"flashingEnabled": true
|
|
||||||
},
|
|
||||||
"chip": "STM32H743ZITx",
|
|
||||||
"coreConfigs": [
|
|
||||||
{
|
|
||||||
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/satrs-stm32h7-nucleo-rtic",
|
|
||||||
"rttEnabled": true,
|
|
||||||
"svdFile": "STM32H743.svd"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
|
||||||
// for the documentation about the tasks.json format
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "cargo build",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
|
||||||
"args": [
|
|
||||||
"build"
|
|
||||||
],
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,260 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
|
||||||
<!--Created by yEd 3.23.2-->
|
|
||||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
|
||||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
|
||||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
|
||||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
|
||||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
|
||||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
|
||||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
|
||||||
<graph edgedefault="directed" id="G">
|
|
||||||
<data key="d0" xml:space="preserve"/>
|
|
||||||
<node id="n0">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="360.0" width="479.0" x="771.3047672479152" y="458.0"/>
|
|
||||||
<y:Fill hasColor="false" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="237.5" y="178.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n1">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="177.64799999999997" width="200.75199999999973" x="1037.5527672479152" y="470.15200000000027"/>
|
|
||||||
<y:Fill hasColor="false" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="67.919921875" x="13.264464667588754" xml:space="preserve" y="8.302185845943427">Simulation<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="-0.5" nodeRatioX="-0.433926114471642" nodeRatioY="-0.45326608886143704" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n2">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="34.0" width="84.39999999999986" x="1068.8351781652768" y="508.2800000000002"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.638671875" x="23.380664062499818" xml:space="preserve" y="8.015625">PCDU<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n3">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="34.0" width="120.39999999999986" x="1068.8351781652768" y="550.4800000000001"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="92.453125" x="13.973437499999818" xml:space="preserve" y="8.015625">Magnetometer<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n4">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="34.0" width="120.39999999999986" x="1068.8351781652768" y="594.9000000000001"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="88.83203125" x="15.783984374999818" xml:space="preserve" y="8.015625">Magnetorquer<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n5">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="34.0" width="120.39999999999986" x="783.4063563305535" y="545.2800000000002"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="85.931640625" x="17.234179687499932" xml:space="preserve" y="8.015625">SimController<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n6">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="34.0" width="120.39999999999986" x="840.5407126611072" y="677.8000000000002"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="105.05078125" x="7.674609374999932" xml:space="preserve" y="8.015625">UDP TC Receiver<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n7">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="34.0" width="120.39999999999986" x="1005.2814253222144" y="677.8000000000002"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="97.111328125" x="11.644335937499932" xml:space="preserve" y="8.015625">UDP TM Sender<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n8">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="34.0" width="120.39999999999986" x="931.6174253222144" y="775.5920000000002"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="38.740234375" x="40.82988281249993" xml:space="preserve" y="8.015625">Client<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<edge id="e0" source="n5" target="n3">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="-5.199999999999932"/>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e1" source="n5" target="n2">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="60.19999999999993" sy="0.0" tx="0.0" ty="0.0">
|
|
||||||
<y:Point x="1023.8695890826383" y="562.2800000000002"/>
|
|
||||||
<y:Point x="1023.8695890826383" y="525.2800000000002"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e2" source="n5" target="n4">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="48.72964366944643" sy="0.0" tx="0.0" ty="0.0">
|
|
||||||
<y:Point x="1023.8695890826383" y="562.2800000000002"/>
|
|
||||||
<y:Point x="1023.8695890826383" y="611.9000000000001"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="97.955078125" x="12.686124396959713" xml:space="preserve" y="-22.50440429687478">schedule_event<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="13.519999999999978" distanceToCenter="true" position="left" ratio="0.11621274698385183" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e3" source="n6" target="n5">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="-5.329643669446341" ty="0.0">
|
|
||||||
<y:Point x="900.7407126611072" y="628.5400000000002"/>
|
|
||||||
<y:Point x="838.2767126611071" y="628.5400000000002"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="75.923828125" x="-87.89792405764274" xml:space="preserve" y="-40.606550292968564">SimRequest<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="49.935999999999936" distanceToCenter="true" position="left" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e4" source="n4" target="n7">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="60.200000000000045" sy="0.0" tx="0.0" ty="0.0">
|
|
||||||
<y:Point x="1223.8814253222142" y="611.9000000000001"/>
|
|
||||||
<y:Point x="1223.8814253222142" y="694.8000000000002"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e5" source="n3" target="n7">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
|
|
||||||
<y:Point x="1223.8814253222142" y="567.4800000000001"/>
|
|
||||||
<y:Point x="1223.8814253222142" y="694.8000000000002"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e6" source="n2" target="n7">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="11.514125426161627" sy="-2.5781798912005343" tx="45.553752843062284" ty="0.0">
|
|
||||||
<y:Point x="1223.8814253222142" y="522.7018201087997"/>
|
|
||||||
<y:Point x="1223.8814253222142" y="694.8000000000002"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="60.4140625" x="-2.4087265765670054" xml:space="preserve" y="145.1356018470808">SimReply<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="17.97817989120062" distanceToCenter="true" position="right" ratio="0.679561684469248" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e7" source="n8" target="n6">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="-25.212712661107275" sy="0.0" tx="-11.264000000000124" ty="0.0">
|
|
||||||
<y:Point x="966.6047126611071" y="731.8000000000002"/>
|
|
||||||
<y:Point x="889.4767126611071" y="731.8000000000002"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="119.751953125" x="-132.27600022951788" xml:space="preserve" y="-32.03587548828091">SimRequest in UDP<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="20.73181250000017" distanceToCenter="true" position="left" ratio="0.9386993050513424" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e8" source="n7" target="n8">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="29.18399999999997" sy="0.0" tx="24.28800000000001" ty="0.0">
|
|
||||||
<y:Point x="1094.6654253222143" y="731.8000000000002"/>
|
|
||||||
<y:Point x="1016.1054253222144" y="731.8000000000002"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="104.2421875" x="-62.15307370122309" xml:space="preserve" y="34.80927001953137">SimReply in UDP<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="23.81218750000005" distanceToCenter="true" position="left" ratio="0.12769857433808468" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e9" source="n5" target="n1">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="23.921741802203996" sy="-3.0501798912007416" tx="0.0" ty="-56.27417989120056">
|
|
||||||
<y:Point x="867.5280981327575" y="502.70182010879967"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="29.95703125" x="73.38950633588263" xml:space="preserve" y="-62.699758016200235">step<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="11.126187499999986" distanceToCenter="true" position="left" ratio="0.5889387894625147" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
</graph>
|
|
||||||
<data key="d7">
|
|
||||||
<y:Resources/>
|
|
||||||
</data>
|
|
||||||
</graphml>
|
|
Binary file not shown.
Before Width: | Height: | Size: 98 KiB |
@ -5,8 +5,10 @@ This book is the primary information resource for the [sat-rs library](https://e
|
|||||||
in addition to the regular API documentation. It contains the following resources:
|
in addition to the regular API documentation. It contains the following resources:
|
||||||
|
|
||||||
1. Architecture informations and consideration which would exceeds the scope of the regular API.
|
1. Architecture informations and consideration which would exceeds the scope of the regular API.
|
||||||
2. General information on how to build on-board Software and how `sat-rs` can help to fulfill
|
2. General information on how to build On-Board Software and how `sat-rs` can help to fulfill
|
||||||
the unique requirements of writing software for remote systems.
|
the unique requirements of writing software for remote systems.
|
||||||
|
2. A Getting-Started workshop where a small On-Board Software is built from scratch using
|
||||||
|
sat-rs components.
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
@ -29,21 +31,4 @@ and [EIVE](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-
|
|||||||
The [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
|
The [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
|
||||||
provides various practical usage examples of the `sat-rs` framework. If you are more interested in
|
provides various practical usage examples of the `sat-rs` framework. If you are more interested in
|
||||||
the practical application of `sat-rs` inside an application, it is recommended to have a look at
|
the practical application of `sat-rs` inside an application, it is recommended to have a look at
|
||||||
the example application. The [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim)
|
the example application.
|
||||||
applicatin complements the example application and can be used to simulate some physical devices
|
|
||||||
for the `satrs-example` device handlers.
|
|
||||||
|
|
||||||
# Flight Heritage
|
|
||||||
|
|
||||||
There is an active and continuous effort to get early flight heritage for the sat-rs library.
|
|
||||||
Currently this library has the following flight heritage:
|
|
||||||
|
|
||||||
- Submission as an [OPS-SAT experiment](https://www.esa.int/Enabling_Support/Operations/OPS-SAT)
|
|
||||||
which has also
|
|
||||||
[flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/).
|
|
||||||
The application is strongly based on the sat-rs example application. You can find the repository
|
|
||||||
of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs).
|
|
||||||
- Development and use of a sat-rs-based [demonstration on-board software](https://egit.irs.uni-stuttgart.de/rust/eurosim-obsw)
|
|
||||||
alongside a Flight System Simulator in the context of a
|
|
||||||
[Bachelors Thesis](https://www.researchgate.net/publication/380785984_Design_and_Development_of_a_Hardware-in-the-Loop_EuroSim_Demonstrator)
|
|
||||||
at [Airbus Netherlands](https://www.airbusdefenceandspacenetherlands.nl/).
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accelerometer"
|
name = "accelerometer"
|
||||||
@ -12,10 +12,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "atomic-polyfill"
|
||||||
version = "1.4.0"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bare-metal"
|
name = "bare-metal"
|
||||||
@ -68,7 +77,7 @@ version = "0.2.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
|
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc_version 0.4.1",
|
"rustc_version 0.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -79,9 +88,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.39"
|
version = "0.4.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@ -89,17 +98,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cobs"
|
name = "cobs"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
|
||||||
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cobs"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cortex-m"
|
||||||
@ -116,22 +115,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m-rt"
|
name = "cortex-m-rt"
|
||||||
version = "0.7.5"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6"
|
checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m-rt-macros",
|
"cortex-m-rt-macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m-rt-macros"
|
name = "cortex-m-rt-macros"
|
||||||
version = "0.7.5"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
|
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -145,9 +144,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc"
|
name = "crc"
|
||||||
version = "3.2.1"
|
version = "3.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc-catalog",
|
"crc-catalog",
|
||||||
]
|
]
|
||||||
@ -160,15 +159,15 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "critical-section"
|
name = "critical-section"
|
||||||
version = "1.2.0"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.10"
|
version = "0.20.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"darling_macro",
|
"darling_macro",
|
||||||
@ -176,33 +175,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_core"
|
name = "darling_core"
|
||||||
version = "0.20.10"
|
version = "0.20.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.20.10"
|
version = "0.20.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "defmt"
|
name = "defmt"
|
||||||
version = "0.3.10"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
checksum = "3939552907426de152b3c2c6f51ed53f98f448babd26f28694c95f5906194595"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"defmt-macros",
|
"defmt-macros",
|
||||||
@ -220,22 +219,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "defmt-macros"
|
name = "defmt-macros"
|
||||||
version = "0.4.0"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
checksum = "18bdc7a7b92ac413e19e95240e75d3a73a8d8e78aa24a594c22cbb4d44b4bbda"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"defmt-parser",
|
"defmt-parser",
|
||||||
"proc-macro-error2",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "defmt-parser"
|
name = "defmt-parser"
|
||||||
version = "0.4.1"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
@ -260,7 +259,7 @@ checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -274,17 +273,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive-new"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.96",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-dma"
|
name = "embedded-dma"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -310,15 +298,6 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal-async"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-time"
|
name = "embedded-time"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -330,23 +309,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enumset"
|
name = "enumset"
|
||||||
version = "1.1.5"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293"
|
checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enumset_derive",
|
"enumset_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enumset_derive"
|
name = "enumset_derive"
|
||||||
version = "0.10.0"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242"
|
checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -372,21 +351,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.31"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.31"
|
version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
@ -430,9 +409,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.2"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heapless"
|
name = "heapless"
|
||||||
@ -452,9 +431,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.7.1"
|
version = "2.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -528,9 +507,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-iter"
|
name = "num-iter"
|
||||||
version = "0.1.45"
|
version = "0.1.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
@ -550,38 +529,38 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum"
|
name = "num_enum"
|
||||||
version = "0.7.3"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
|
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num_enum_derive",
|
"num_enum_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum_derive"
|
name = "num_enum_derive"
|
||||||
version = "0.7.3"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "panic-probe"
|
name = "panic-probe"
|
||||||
version = "0.3.2"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4047d9235d1423d66cc97da7d07eddb54d4f154d6c13805c6d0793956f4f25b0"
|
checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"defmt",
|
"defmt",
|
||||||
@ -589,15 +568,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.15"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
@ -606,47 +585,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "proc-macro-error"
|
||||||
version = "1.10.0"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error-attr2"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error2"
|
name = "proc-macro-error-attr"
|
||||||
version = "2.0.1"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-error-attr2",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.93"
|
version = "1.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.38"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -662,14 +637,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtic"
|
name = "rtic"
|
||||||
version = "2.1.2"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "401961431a1e491124cdd216a313fada2d395aa2b5bee2867c872fc8af7c1bc1"
|
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
"bare-metal 1.0.0",
|
"bare-metal 1.0.0",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"critical-section",
|
"critical-section",
|
||||||
"portable-atomic",
|
|
||||||
"rtic-core",
|
"rtic-core",
|
||||||
"rtic-macros",
|
"rtic-macros",
|
||||||
]
|
]
|
||||||
@ -691,40 +666,38 @@ checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtic-macros"
|
name = "rtic-macros"
|
||||||
version = "2.1.2"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac22ab522d80079b48f46ac66ded4d349e1adf81b52430d6a74faa3a7790ed80"
|
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"proc-macro-error2",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtic-monotonics"
|
name = "rtic-monotonics"
|
||||||
version = "2.0.3"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1cb90bcfdbbacf3ca37340cdab52ec2de5611c744095ef7889e9c50c233b748"
|
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
"fugit",
|
"fugit",
|
||||||
"portable-atomic",
|
|
||||||
"rtic-time",
|
"rtic-time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtic-time"
|
name = "rtic-time"
|
||||||
version = "2.0.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7b1d853fa50dc125695414ce4510567a0d420221e455b1568cfa8c9aece9614"
|
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"critical-section",
|
"critical-section",
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"embedded-hal-async",
|
|
||||||
"fugit",
|
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"rtic-common",
|
"rtic-common",
|
||||||
]
|
]
|
||||||
@ -740,24 +713,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver 1.0.25",
|
"semver 1.0.22",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "satrs"
|
name = "satrs"
|
||||||
version = "0.2.1"
|
version = "0.2.0-rc.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "866fcae3b683ccc37b5ad77982483a0ee01d5dc408dea5aad2117ad404b60fe1"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cobs 0.2.3",
|
"cobs",
|
||||||
"crc",
|
"crc",
|
||||||
"defmt",
|
|
||||||
"delegate",
|
"delegate",
|
||||||
"derive-new",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"paste",
|
"paste",
|
||||||
@ -767,19 +736,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "satrs-shared"
|
name = "satrs-example-stm32f3-disco"
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6042477018c2d43fffccaaa5099bc299a58485139b4d31c5b276889311e474f1"
|
|
||||||
dependencies = [
|
|
||||||
"spacepackets",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "satrs-stm32f3-disco-rtic"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cobs 0.3.0",
|
"cobs",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"cortex-m-semihosting",
|
"cortex-m-semihosting",
|
||||||
@ -797,6 +757,13 @@ dependencies = [
|
|||||||
"stm32f3xx-hal",
|
"stm32f3xx-hal",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "satrs-shared"
|
||||||
|
version = "0.1.3"
|
||||||
|
dependencies = [
|
||||||
|
"spacepackets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -808,9 +775,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.25"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver-parser"
|
name = "semver-parser"
|
||||||
@ -832,9 +799,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spacepackets"
|
name = "spacepackets"
|
||||||
version = "0.11.2"
|
version = "0.11.0-rc.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e85574d113a06312010c0ba51aadccd4ba2806231ebe9a49fc6473d0534d8696"
|
checksum = "c2cfd5f9a4c7f10714d21f9bc61f2d176cb7ae092cdd687e7ade2d4e6f7d7125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc",
|
"crc",
|
||||||
"defmt",
|
"defmt",
|
||||||
@ -932,9 +899,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.96"
|
version = "2.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -943,22 +910,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.11"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.11"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -969,9 +936,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.16"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usb-device"
|
name = "usb-device"
|
||||||
@ -987,9 +954,9 @@ checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "void"
|
name = "void"
|
||||||
@ -1008,9 +975,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.7.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
@ -1018,11 +985,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.7.35"
|
version = "0.7.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.96",
|
"syn 2.0.58",
|
||||||
]
|
]
|
@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "satrs-stm32f3-disco-rtic"
|
name = "satrs-example-stm32f3-disco"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
default-run = "satrs-stm32f3-disco-rtic"
|
default-run = "satrs-example-stm32f3-disco"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -22,11 +22,12 @@ version = "2"
|
|||||||
features = ["thumbv7-backend"]
|
features = ["thumbv7-backend"]
|
||||||
|
|
||||||
[dependencies.rtic-monotonics]
|
[dependencies.rtic-monotonics]
|
||||||
version = "2"
|
version = "1"
|
||||||
features = ["cortex-m-systick"]
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
[dependencies.cobs]
|
[dependencies.cobs]
|
||||||
version = "0.3"
|
git = "https://github.com/robamu/cobs.rs.git"
|
||||||
|
branch = "all_features"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.stm32f3xx-hal]
|
[dependencies.stm32f3xx-hal]
|
||||||
@ -45,8 +46,7 @@ branch = "complete-dma-update-hal"
|
|||||||
# path = "../stm32f3-discovery"
|
# path = "../stm32f3-discovery"
|
||||||
|
|
||||||
[dependencies.satrs]
|
[dependencies.satrs]
|
||||||
# path = "satrs"
|
path = "../satrs"
|
||||||
version = "0.2"
|
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["defmt"]
|
features = ["defmt"]
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
sat-rs example for the STM32F3-Discovery board
|
sat-rs example for the STM32F3-Discovery board
|
||||||
=======
|
=======
|
||||||
|
|
||||||
This example application shows how the [sat-rs library](https://egit.irs.uni-stuttgart.de/rust/sat-rs)
|
This example application shows how the [sat-rs framework](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad)
|
||||||
can be used on an embedded target.
|
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 also shows how a relatively simple OBSW could be built when no standard runtime is available.
|
||||||
It uses [RTIC](https://rtic.rs/2/book/en/) as the concurrency framework and the
|
It uses [RTIC](https://rtic.rs/1/book/en/) as the concurrency framework and the
|
||||||
[defmt](https://defmt.ferrous-systems.com/) framework for logging.
|
[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
|
The STM32F3-Discovery device was picked because it is a cheap Cortex-M4 based device which is also
|
@ -9,7 +9,7 @@ from prompt_toolkit.history import FileHistory, History
|
|||||||
from spacepackets.ecss.tm import CdsShortTimestamp
|
from spacepackets.ecss.tm import CdsShortTimestamp
|
||||||
|
|
||||||
import tmtccmd
|
import tmtccmd
|
||||||
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusTm, PusVerificator
|
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusVerificator
|
||||||
from spacepackets.ecss.pus_17_test import Service17Tm
|
from spacepackets.ecss.pus_17_test import Service17Tm
|
||||||
from spacepackets.ecss.pus_1_verification import UnpackParams, Service1Tm
|
from spacepackets.ecss.pus_1_verification import UnpackParams, Service1Tm
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ from tmtccmd.tmtc import (
|
|||||||
DefaultPusQueueHelper,
|
DefaultPusQueueHelper,
|
||||||
)
|
)
|
||||||
from tmtccmd.pus.s5_fsfw_event import Service5Tm
|
from tmtccmd.pus.s5_fsfw_event import Service5Tm
|
||||||
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
|
from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider
|
||||||
from tmtccmd.util.obj_id import ObjectIdDictT
|
from tmtccmd.util.obj_id import ObjectIdDictT
|
||||||
|
|
||||||
_LOGGER = logging.getLogger()
|
_LOGGER = logging.getLogger()
|
||||||
@ -53,7 +53,7 @@ EXAMPLE_PUS_APID = 0x02
|
|||||||
|
|
||||||
class SatRsConfigHook(HookBase):
|
class SatRsConfigHook(HookBase):
|
||||||
def __init__(self, json_cfg_path: str):
|
def __init__(self, json_cfg_path: str):
|
||||||
super().__init__(json_cfg_path)
|
super().__init__(json_cfg_path=json_cfg_path)
|
||||||
|
|
||||||
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
||||||
from tmtccmd.config.com import (
|
from tmtccmd.config.com import (
|
||||||
@ -111,10 +111,9 @@ class PusHandler(SpecificApidHandlerBase):
|
|||||||
self.verif_wrapper = verif_wrapper
|
self.verif_wrapper = verif_wrapper
|
||||||
|
|
||||||
def handle_tm(self, packet: bytes, _user_args: Any):
|
def handle_tm(self, packet: bytes, _user_args: Any):
|
||||||
|
time_reader = CdsShortTimestamp.empty()
|
||||||
try:
|
try:
|
||||||
pus_tm = PusTm.unpack(
|
pus_tm = PusTelemetry.unpack(packet, time_reader=CdsShortTimestamp.empty())
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
||||||
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
||||||
@ -123,7 +122,7 @@ class PusHandler(SpecificApidHandlerBase):
|
|||||||
tm_packet = None
|
tm_packet = None
|
||||||
if service == 1:
|
if service == 1:
|
||||||
tm_packet = Service1Tm.unpack(
|
tm_packet = Service1Tm.unpack(
|
||||||
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 1, 2)
|
data=packet, params=UnpackParams(time_reader, 1, 2)
|
||||||
)
|
)
|
||||||
res = self.verif_wrapper.add_tm(tm_packet)
|
res = self.verif_wrapper.add_tm(tm_packet)
|
||||||
if res is None:
|
if res is None:
|
||||||
@ -140,16 +139,16 @@ class PusHandler(SpecificApidHandlerBase):
|
|||||||
if service == 3:
|
if service == 3:
|
||||||
_LOGGER.info("No handling for HK packets implemented")
|
_LOGGER.info("No handling for HK packets implemented")
|
||||||
_LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
|
_LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
|
||||||
pus_tm = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
pus_tm = PusTelemetry.unpack(packet, CdsShortTimestamp.empty())
|
||||||
if pus_tm.subservice == 25:
|
if pus_tm.subservice == 25:
|
||||||
if len(pus_tm.source_data) < 8:
|
if len(pus_tm.source_data) < 8:
|
||||||
raise ValueError("No addressable ID in HK packet")
|
raise ValueError("No addressable ID in HK packet")
|
||||||
json_str = pus_tm.source_data[8:]
|
json_str = pus_tm.source_data[8:]
|
||||||
_LOGGER.info("received JSON string: " + json_str.decode("utf-8"))
|
_LOGGER.info("received JSON string: " + json_str.decode("utf-8"))
|
||||||
if service == 5:
|
if service == 5:
|
||||||
tm_packet = Service5Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
tm_packet = Service5Tm.unpack(packet, time_reader)
|
||||||
if service == 17:
|
if service == 17:
|
||||||
tm_packet = Service17Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
tm_packet = Service17Tm.unpack(packet, time_reader)
|
||||||
if tm_packet.subservice == 2:
|
if tm_packet.subservice == 2:
|
||||||
_LOGGER.info("Received Ping Reply TM[17,2]")
|
_LOGGER.info("Received Ping Reply TM[17,2]")
|
||||||
else:
|
else:
|
||||||
@ -160,7 +159,7 @@ class PusHandler(SpecificApidHandlerBase):
|
|||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
f"The service {service} is not implemented in Telemetry Factory"
|
f"The service {service} is not implemented in Telemetry Factory"
|
||||||
)
|
)
|
||||||
tm_packet = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
tm_packet = PusTelemetry.unpack(packet, time_reader)
|
||||||
self.raw_logger.log_tm(pus_tm)
|
self.raw_logger.log_tm(pus_tm)
|
||||||
|
|
||||||
|
|
||||||
@ -204,15 +203,15 @@ class TcHandler(TcHandlerBase):
|
|||||||
_LOGGER.info(log_entry.log_str)
|
_LOGGER.info(log_entry.log_str)
|
||||||
|
|
||||||
def queue_finished_cb(self, info: ProcedureWrapper):
|
def queue_finished_cb(self, info: ProcedureWrapper):
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
if info.proc_type == TcProcedureType.DEFAULT:
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
def_proc = info.to_def_procedure()
|
||||||
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
||||||
|
|
||||||
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
||||||
q = self.queue_helper
|
q = self.queue_helper
|
||||||
q.queue_wrapper = wrapper.queue_wrapper
|
q.queue_wrapper = wrapper.queue_wrapper
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
if info.proc_type == TcProcedureType.DEFAULT:
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
def_proc = info.to_def_procedure()
|
||||||
cmd_path = def_proc.cmd_path
|
cmd_path = def_proc.cmd_path
|
||||||
if cmd_path == "/ping":
|
if cmd_path == "/ping":
|
||||||
q.add_log_cmd("Sending PUS ping telecommand")
|
q.add_log_cmd("Sending PUS ping telecommand")
|
@ -1,2 +1,2 @@
|
|||||||
tmtccmd == 8.0.1
|
tmtccmd == 8.0.0rc.0
|
||||||
# -e git+https://github.com/robamu-org/tmtccmd.git@main#egg=tmtccmd
|
# -e git+https://github.com/robamu-org/tmtccmd.git@main#egg=tmtccmd
|
@ -1,6 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use satrs_stm32f3_disco_rtic as _;
|
use satrs_example_stm32f3_disco as _;
|
||||||
|
|
||||||
use stm32f3_discovery::leds::Leds;
|
use stm32f3_discovery::leds::Leds;
|
||||||
use stm32f3_discovery::stm32f3xx_hal::delay::Delay;
|
use stm32f3_discovery::stm32f3xx_hal::delay::Delay;
|
@ -9,14 +9,14 @@ use satrs::spacepackets::ecss::EcssEnumU16;
|
|||||||
use satrs::spacepackets::CcsdsPacket;
|
use satrs::spacepackets::CcsdsPacket;
|
||||||
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
||||||
// global logger + panicking-behavior + memory layout
|
// global logger + panicking-behavior + memory layout
|
||||||
use satrs_stm32f3_disco_rtic as _;
|
use satrs_example_stm32f3_disco as _;
|
||||||
|
|
||||||
use rtic::app;
|
use rtic::app;
|
||||||
|
|
||||||
use heapless::{mpmc::Q8, Vec};
|
use heapless::{mpmc::Q8, Vec};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use rtic_monotonics::fugit::{MillisDurationU32, TimerInstantU32};
|
use rtic_monotonics::systick::fugit::{MillisDurationU32, TimerInstantU32};
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::ExtU32;
|
||||||
use satrs::seq_count::SequenceCountProviderCore;
|
use satrs::seq_count::SequenceCountProviderCore;
|
||||||
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
|
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
|
||||||
use stm32f3xx_hal::dma::dma1;
|
use stm32f3xx_hal::dma::dma1;
|
||||||
@ -245,6 +245,8 @@ pub fn convert_pus_tc_to_request(
|
|||||||
mod app {
|
mod app {
|
||||||
use super::*;
|
use super::*;
|
||||||
use core::slice::Iter;
|
use core::slice::Iter;
|
||||||
|
use rtic_monotonics::systick::Systick;
|
||||||
|
use rtic_monotonics::Monotonic;
|
||||||
use satrs::pus::verification::{TcStateStarted, VerificationReportCreator};
|
use satrs::pus::verification::{TcStateStarted, VerificationReportCreator};
|
||||||
use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE};
|
use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -257,8 +259,6 @@ mod app {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
|
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
|
||||||
|
|
||||||
systick_monotonic!(Mono, 1000);
|
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {
|
struct Shared {
|
||||||
blink_freq: MillisDurationU32,
|
blink_freq: MillisDurationU32,
|
||||||
@ -279,7 +279,8 @@ mod app {
|
|||||||
let mut rcc = cx.device.RCC.constrain();
|
let mut rcc = cx.device.RCC.constrain();
|
||||||
|
|
||||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||||
Mono::start(cx.core.SYST, 8_000_000);
|
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||||
|
Systick::start(cx.core.SYST, 8_000_000, systick_mono_token);
|
||||||
|
|
||||||
let mut flash = cx.device.FLASH.constrain();
|
let mut flash = cx.device.FLASH.constrain();
|
||||||
let clocks = rcc
|
let clocks = rcc
|
||||||
@ -393,7 +394,7 @@ mod app {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
||||||
Mono::delay(current_blink_freq).await;
|
Systick::delay(current_blink_freq).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,14 +412,15 @@ mod app {
|
|||||||
if is_idle {
|
if is_idle {
|
||||||
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
|
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
|
||||||
if let Some(last_completed) = last_completed {
|
if let Some(last_completed) = last_completed {
|
||||||
let elapsed_ms = (Mono::now() - last_completed).to_millis();
|
let elapsed_ms = (Systick::now() - last_completed).to_millis();
|
||||||
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
|
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
|
||||||
Mono::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis()).await;
|
Systick::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis())
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Check for completion after 1 ms
|
// Check for completion after 1 ms
|
||||||
Mono::delay(1.millis()).await;
|
Systick::delay(1.millis()).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(vec) = TM_REQUESTS.dequeue() {
|
if let Some(vec) = TM_REQUESTS.dequeue() {
|
||||||
@ -457,11 +459,11 @@ mod app {
|
|||||||
UartTxState::Transmitting(_) => (),
|
UartTxState::Transmitting(_) => (),
|
||||||
});
|
});
|
||||||
// Check for completion after 1 ms
|
// Check for completion after 1 ms
|
||||||
Mono::delay(1.millis()).await;
|
Systick::delay(1.millis()).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Nothing to do, and we are idle.
|
// Nothing to do, and we are idle.
|
||||||
Mono::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
Systick::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,7 +657,7 @@ mod app {
|
|||||||
tx_shared.state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
|
tx_shared.state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
|
||||||
// We cache the last completed time to ensure that there is a minimum delay between consecutive
|
// We cache the last completed time to ensure that there is a minimum delay between consecutive
|
||||||
// transferred packets.
|
// transferred packets.
|
||||||
tx_shared.last_completed = Some(Mono::now());
|
tx_shared.last_completed = Some(Systick::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -12,11 +12,11 @@
|
|||||||
"chip": "STM32F303VCTx",
|
"chip": "STM32F303VCTx",
|
||||||
"coreConfigs": [
|
"coreConfigs": [
|
||||||
{
|
{
|
||||||
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/satrs-stm32f3-disco-rtic",
|
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/satrs-example-stm32f3-disco",
|
||||||
"rttEnabled": true,
|
"rttEnabled": true,
|
||||||
"svdFile": "STM32F303.svd"
|
"svdFile": "STM32F303.svd"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -7,13 +7,3 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
# [v0.1.1] 2024-02-21
|
|
||||||
|
|
||||||
satrs v0.2.0-rc.0
|
|
||||||
satrs-mib v0.1.1
|
|
||||||
|
|
||||||
# [v0.1.0] 2024-02-13
|
|
||||||
|
|
||||||
satrs v0.1.1
|
|
||||||
satrs-mib v0.1.0
|
|
||||||
|
@ -8,19 +8,18 @@ homepage = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
|||||||
repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fern = "0.7"
|
fern = "0.6"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
delegate = "0.13"
|
delegate = "0.10"
|
||||||
zerocopy = "0.8"
|
zerocopy = "0.6"
|
||||||
csv = "1"
|
csv = "1"
|
||||||
num_enum = "0.7"
|
num_enum = "0.7"
|
||||||
thiserror = "2"
|
thiserror = "1"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
derive-new = "0.7"
|
derive-new = "0.5"
|
||||||
cfg-if = "1"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
@ -28,16 +27,13 @@ serde_json = "1"
|
|||||||
path = "../satrs"
|
path = "../satrs"
|
||||||
features = ["test_util"]
|
features = ["test_util"]
|
||||||
|
|
||||||
[dependencies.satrs-minisim]
|
|
||||||
path = "../satrs-minisim"
|
|
||||||
|
|
||||||
[dependencies.satrs-mib]
|
[dependencies.satrs-mib]
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
path = "../satrs-mib"
|
path = "../satrs-mib"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
heap_tmtc = []
|
dyn_tmtc = []
|
||||||
default = ["heap_tmtc"]
|
default = ["dyn_tmtc"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
|
@ -14,7 +14,7 @@ You can run the application using `cargo run`.
|
|||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
The example has the `heap_tmtc` feature which is enabled by default. With this feature enabled,
|
The example has the `dyn_tmtc` feature which is enabled by default. With this feature enabled,
|
||||||
TMTC packets are exchanged using the heap as the backing memory instead of pre-allocated static
|
TMTC packets are exchanged using the heap as the backing memory instead of pre-allocated static
|
||||||
stores.
|
stores.
|
||||||
|
|
||||||
@ -48,17 +48,16 @@ It is recommended to use a virtual environment to do this. To set up one in the
|
|||||||
you can use `python3 -m venv venv` on Unix systems or `py -m venv venv` on Windows systems.
|
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)
|
After doing this, you can check the [venv tutorial](https://docs.python.org/3/tutorial/venv.html)
|
||||||
on how to activate the environment and then use the following command to install the required
|
on how to activate the environment and then use the following command to install the required
|
||||||
dependency interactively:
|
dependency:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install -e .
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, if you would like to use the GUI functionality provided by `tmtccmd`, you can also
|
Alternatively, if you would like to use the GUI functionality provided by `tmtccmd`, you can also
|
||||||
install it manually with
|
install it manually with
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install -e .
|
|
||||||
pip install tmtccmd[gui]
|
pip install tmtccmd[gui]
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -73,22 +72,3 @@ the `simpleclient`:
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can also simply call the script without any arguments to view the command tree.
|
You can also simply call the script without any arguments to view the command tree.
|
||||||
|
|
||||||
## Adding the mini simulator application
|
|
||||||
|
|
||||||
This example application features a few device handlers. The
|
|
||||||
[`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim)
|
|
||||||
can be used to simulate the physical devices managed by these device handlers.
|
|
||||||
|
|
||||||
The example application will attempt communication with the mini simulator on UDP port 7303.
|
|
||||||
If this works, the device handlers will use communication interfaces dedicated to the communication
|
|
||||||
with the mini simulator. Otherwise, they will be replaced by dummy interfaces which either
|
|
||||||
return constant values or behave like ideal devices.
|
|
||||||
|
|
||||||
In summary, you can use the following command command to run the mini-simulator first:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo run -p satrs-minisim
|
|
||||||
```
|
|
||||||
|
|
||||||
and then start the example using `cargo run -p satrs-example`.
|
|
||||||
|
134
satrs-example/pytmtc/.gitignore
vendored
134
satrs-example/pytmtc/.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
/tmtc_conf.json
|
|
||||||
__pycache__
|
__pycache__
|
||||||
|
|
||||||
/venv
|
/venv
|
||||||
@ -8,136 +7,3 @@ __pycache__
|
|||||||
|
|
||||||
/seqcnt.txt
|
/seqcnt.txt
|
||||||
/.tmtc-history.txt
|
/.tmtc-history.txt
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
pip-wheel-metadata/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
.python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# PyCharm
|
|
||||||
.idea
|
|
||||||
|
@ -39,10 +39,7 @@ class EventU32:
|
|||||||
|
|
||||||
|
|
||||||
class AcsId(enum.IntEnum):
|
class AcsId(enum.IntEnum):
|
||||||
SUBSYSTEM = 1
|
MGM_0 = 0
|
||||||
MGM_ASSEMBLY = 2
|
|
||||||
MGM_0 = 3
|
|
||||||
MGM_1 = 4
|
|
||||||
|
|
||||||
|
|
||||||
class AcsHkIds(enum.IntEnum):
|
class AcsHkIds(enum.IntEnum):
|
@ -1,20 +1,29 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Example client for the sat-rs example application"""
|
"""Example client for the sat-rs example application"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
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
|
import tmtccmd
|
||||||
from spacepackets.ecss import PusVerificator
|
from spacepackets.ecss import PusTelemetry, PusVerificator
|
||||||
|
from spacepackets.ecss.pus_17_test import Service17Tm
|
||||||
|
from spacepackets.ecss.pus_1_verification import UnpackParams, Service1Tm
|
||||||
|
from spacepackets.ccsds.time import CdsShortTimestamp
|
||||||
|
|
||||||
from tmtccmd import ProcedureParamsWrapper
|
from tmtccmd import TcHandlerBase, ProcedureParamsWrapper
|
||||||
from tmtccmd.core.base import BackendRequest
|
from tmtccmd.core.base import BackendRequest
|
||||||
from tmtccmd.pus import VerificationWrapper
|
from tmtccmd.pus import VerificationWrapper
|
||||||
from tmtccmd.tmtc import CcsdsTmHandler
|
from tmtccmd.tmtc import CcsdsTmHandler, GenericApidHandlerBase
|
||||||
|
from tmtccmd.com import ComInterface
|
||||||
from tmtccmd.config import (
|
from tmtccmd.config import (
|
||||||
|
CmdTreeNode,
|
||||||
default_json_path,
|
default_json_path,
|
||||||
SetupParams,
|
SetupParams,
|
||||||
|
HookBase,
|
||||||
params_to_procedure_conversion,
|
params_to_procedure_conversion,
|
||||||
)
|
)
|
||||||
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
||||||
@ -24,20 +33,193 @@ from tmtccmd.logging.pus import (
|
|||||||
RawTmtcTimedLogWrapper,
|
RawTmtcTimedLogWrapper,
|
||||||
TimedLogWhen,
|
TimedLogWhen,
|
||||||
)
|
)
|
||||||
from spacepackets.seqcount import PusFileSeqCountProvider
|
from tmtccmd.tmtc import (
|
||||||
|
TcQueueEntryType,
|
||||||
|
ProcedureWrapper,
|
||||||
|
TcProcedureType,
|
||||||
|
FeedWrapper,
|
||||||
|
SendCbParams,
|
||||||
|
DefaultPusQueueHelper,
|
||||||
|
QueueWrapper,
|
||||||
|
)
|
||||||
|
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
|
||||||
|
from tmtccmd.util.obj_id import ObjectIdDictT
|
||||||
|
|
||||||
|
|
||||||
from pytmtc.config import SatrsConfigHook
|
import pus_tc
|
||||||
from pytmtc.pus_tc import TcHandler
|
from common import Apid, EventU32
|
||||||
from pytmtc.pus_tm import PusHandler
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger()
|
_LOGGER = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SatRsConfigHook(HookBase):
|
||||||
|
def __init__(self, json_cfg_path: str):
|
||||||
|
super().__init__(json_cfg_path=json_cfg_path)
|
||||||
|
|
||||||
|
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
||||||
|
from tmtccmd.config.com import (
|
||||||
|
create_com_interface_default,
|
||||||
|
create_com_interface_cfg_default,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert self.cfg_path is not None
|
||||||
|
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=packet_id_list,
|
||||||
|
)
|
||||||
|
assert cfg is not None
|
||||||
|
return create_com_interface_default(cfg)
|
||||||
|
|
||||||
|
def get_command_definitions(self) -> CmdTreeNode:
|
||||||
|
"""This function should return the root node of the command definition tree."""
|
||||||
|
return pus_tc.create_cmd_definition_tree()
|
||||||
|
|
||||||
|
def get_cmd_history(self) -> Optional[History]:
|
||||||
|
"""Optionlly return a history class for the past command paths which will be used
|
||||||
|
when prompting a command path from the user in CLI mode."""
|
||||||
|
return FileHistory(".tmtc-history.txt")
|
||||||
|
|
||||||
|
def get_object_ids(self) -> ObjectIdDictT:
|
||||||
|
from tmtccmd.config.objects import get_core_object_ids
|
||||||
|
|
||||||
|
return get_core_object_ids()
|
||||||
|
|
||||||
|
|
||||||
|
class PusHandler(GenericApidHandlerBase):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
file_logger: logging.Logger,
|
||||||
|
verif_wrapper: VerificationWrapper,
|
||||||
|
raw_logger: RawTmtcTimedLogWrapper,
|
||||||
|
):
|
||||||
|
super().__init__(None)
|
||||||
|
self.file_logger = file_logger
|
||||||
|
self.raw_logger = raw_logger
|
||||||
|
self.verif_wrapper = verif_wrapper
|
||||||
|
|
||||||
|
def handle_tm(self, apid: int, packet: bytes, _user_args: Any):
|
||||||
|
try:
|
||||||
|
pus_tm = PusTelemetry.unpack(
|
||||||
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
|
)
|
||||||
|
except ValueError as e:
|
||||||
|
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
||||||
|
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
||||||
|
raise e
|
||||||
|
service = pus_tm.service
|
||||||
|
if service == 1:
|
||||||
|
tm_packet = Service1Tm.unpack(
|
||||||
|
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 1, 2)
|
||||||
|
)
|
||||||
|
res = self.verif_wrapper.add_tm(tm_packet)
|
||||||
|
if res is None:
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Received Verification TM[{tm_packet.service}, {tm_packet.subservice}] "
|
||||||
|
f"with Request ID {tm_packet.tc_req_id.as_u32():#08x}"
|
||||||
|
)
|
||||||
|
_LOGGER.warning(
|
||||||
|
f"No matching telecommand found for {tm_packet.tc_req_id}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.verif_wrapper.log_to_console(tm_packet, res)
|
||||||
|
self.verif_wrapper.log_to_file(tm_packet, res)
|
||||||
|
elif service == 3:
|
||||||
|
_LOGGER.info("No handling for HK packets implemented")
|
||||||
|
_LOGGER.info(f"Raw packet: 0x[{packet.hex(sep=',')}]")
|
||||||
|
pus_tm = PusTelemetry.unpack(
|
||||||
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
|
)
|
||||||
|
if pus_tm.subservice == 25:
|
||||||
|
if len(pus_tm.source_data) < 8:
|
||||||
|
raise ValueError("No addressable ID in HK packet")
|
||||||
|
json_str = pus_tm.source_data[8:]
|
||||||
|
_LOGGER.info(json_str)
|
||||||
|
elif service == 5:
|
||||||
|
tm_packet = PusTelemetry.unpack(
|
||||||
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
|
)
|
||||||
|
src_data = tm_packet.source_data
|
||||||
|
event_u32 = EventU32.unpack(src_data)
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Received event packet. Source APID: {Apid(tm_packet.apid)!r}, Event: {event_u32}"
|
||||||
|
)
|
||||||
|
if event_u32.group_id == 0 and event_u32.unique_id == 0:
|
||||||
|
_LOGGER.info("Received test event")
|
||||||
|
elif service == 17:
|
||||||
|
tm_packet = Service17Tm.unpack(
|
||||||
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
|
)
|
||||||
|
if tm_packet.subservice == 2:
|
||||||
|
self.file_logger.info("Received Ping Reply TM[17,2]")
|
||||||
|
_LOGGER.info("Received Ping Reply TM[17,2]")
|
||||||
|
else:
|
||||||
|
self.file_logger.info(
|
||||||
|
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
||||||
|
)
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_LOGGER.info(
|
||||||
|
f"The service {service} is not implemented in Telemetry Factory"
|
||||||
|
)
|
||||||
|
tm_packet = PusTelemetry.unpack(
|
||||||
|
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
||||||
|
)
|
||||||
|
self.raw_logger.log_tm(pus_tm)
|
||||||
|
|
||||||
|
|
||||||
|
class TcHandler(TcHandlerBase):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
seq_count_provider: FileSeqCountProvider,
|
||||||
|
verif_wrapper: VerificationWrapper,
|
||||||
|
):
|
||||||
|
super(TcHandler, self).__init__()
|
||||||
|
self.seq_count_provider = seq_count_provider
|
||||||
|
self.verif_wrapper = verif_wrapper
|
||||||
|
self.queue_helper = DefaultPusQueueHelper(
|
||||||
|
queue_wrapper=QueueWrapper.empty(),
|
||||||
|
tc_sched_timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE,
|
||||||
|
seq_cnt_provider=seq_count_provider,
|
||||||
|
pus_verificator=self.verif_wrapper.pus_verificator,
|
||||||
|
default_pus_apid=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_cb(self, send_params: SendCbParams):
|
||||||
|
entry_helper = send_params.entry
|
||||||
|
if entry_helper.is_tc:
|
||||||
|
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
||||||
|
pus_tc_wrapper = entry_helper.to_pus_tc_entry()
|
||||||
|
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
||||||
|
_LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
||||||
|
send_params.com_if.send(raw_tc)
|
||||||
|
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
||||||
|
log_entry = entry_helper.to_log_entry()
|
||||||
|
_LOGGER.info(log_entry.log_str)
|
||||||
|
|
||||||
|
def queue_finished_cb(self, info: ProcedureWrapper):
|
||||||
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
|
def_proc = info.to_tree_commanding_procedure()
|
||||||
|
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
||||||
|
|
||||||
|
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
||||||
|
q = self.queue_helper
|
||||||
|
q.queue_wrapper = wrapper.queue_wrapper
|
||||||
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
|
def_proc = info.to_tree_commanding_procedure()
|
||||||
|
assert def_proc.cmd_path is not None
|
||||||
|
pus_tc.pack_pus_telecommands(q, def_proc.cmd_path)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
add_colorlog_console_logger(_LOGGER)
|
add_colorlog_console_logger(_LOGGER)
|
||||||
tmtccmd.init_printout(False)
|
tmtccmd.init_printout(False)
|
||||||
hook_obj = SatrsConfigHook(json_cfg_path=default_json_path())
|
hook_obj = SatRsConfigHook(json_cfg_path=default_json_path())
|
||||||
parser_wrapper = PreArgsParsingWrapper()
|
parser_wrapper = PreArgsParsingWrapper()
|
||||||
parser_wrapper.create_default_parent_parser()
|
parser_wrapper.create_default_parent_parser()
|
||||||
parser_wrapper.create_default_parser()
|
parser_wrapper.create_default_parser()
|
||||||
|
@ -1,73 +1,37 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
import struct
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from spacepackets.ccsds import CdsShortTimestamp
|
from spacepackets.ccsds import CdsShortTimestamp
|
||||||
from spacepackets.ecss import PusTelecommand
|
from spacepackets.ecss import PusTelecommand
|
||||||
from spacepackets.seqcount import FileSeqCountProvider
|
|
||||||
from tmtccmd import ProcedureWrapper, TcHandlerBase
|
|
||||||
from tmtccmd.config import CmdTreeNode
|
from tmtccmd.config import CmdTreeNode
|
||||||
from tmtccmd.pus import VerificationWrapper
|
from tmtccmd.pus.tc.s200_fsfw_mode import Mode
|
||||||
from tmtccmd.tmtc import (
|
from tmtccmd.tmtc import DefaultPusQueueHelper
|
||||||
DefaultPusQueueHelper,
|
|
||||||
FeedWrapper,
|
|
||||||
QueueWrapper,
|
|
||||||
SendCbParams,
|
|
||||||
TcProcedureType,
|
|
||||||
TcQueueEntryType,
|
|
||||||
)
|
|
||||||
from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd
|
from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd
|
||||||
|
from tmtccmd.pus.s200_fsfw_mode import Subservice as ModeSubservice
|
||||||
|
|
||||||
from pytmtc.acs import create_acs_node
|
from common import AcsId, Apid
|
||||||
from pytmtc.common import Apid
|
|
||||||
from pytmtc.acs.mgms import create_mgm_cmds
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TcHandler(TcHandlerBase):
|
def create_set_mode_cmd(
|
||||||
def __init__(
|
apid: int, unique_id: int, mode: int, submode: int
|
||||||
self,
|
) -> PusTelecommand:
|
||||||
seq_count_provider: FileSeqCountProvider,
|
app_data = bytearray()
|
||||||
verif_wrapper: VerificationWrapper,
|
app_data.extend(struct.pack("!I", unique_id))
|
||||||
):
|
app_data.extend(struct.pack("!I", mode))
|
||||||
super(TcHandler, self).__init__()
|
app_data.extend(struct.pack("!H", submode))
|
||||||
self.seq_count_provider = seq_count_provider
|
return PusTelecommand(
|
||||||
self.verif_wrapper = verif_wrapper
|
service=200,
|
||||||
self.queue_helper = DefaultPusQueueHelper(
|
subservice=ModeSubservice.TC_MODE_COMMAND,
|
||||||
queue_wrapper=QueueWrapper.empty(),
|
apid=apid,
|
||||||
tc_sched_timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE,
|
app_data=app_data,
|
||||||
seq_cnt_provider=seq_count_provider,
|
)
|
||||||
pus_verificator=self.verif_wrapper.pus_verificator,
|
|
||||||
default_pus_apid=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
def send_cb(self, send_params: SendCbParams):
|
|
||||||
entry_helper = send_params.entry
|
|
||||||
if entry_helper.is_tc:
|
|
||||||
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
|
||||||
pus_tc_wrapper = entry_helper.to_pus_tc_entry()
|
|
||||||
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
|
||||||
_LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
|
||||||
send_params.com_if.send(raw_tc)
|
|
||||||
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
|
||||||
log_entry = entry_helper.to_log_entry()
|
|
||||||
_LOGGER.info(log_entry.log_str)
|
|
||||||
|
|
||||||
def queue_finished_cb(self, info: ProcedureWrapper):
|
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
|
||||||
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
|
||||||
|
|
||||||
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
|
||||||
q = self.queue_helper
|
|
||||||
q.queue_wrapper = wrapper.queue_wrapper
|
|
||||||
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
|
||||||
def_proc = info.to_tree_commanding_procedure()
|
|
||||||
assert def_proc.cmd_path is not None
|
|
||||||
pack_pus_telecommands(q, def_proc.cmd_path)
|
|
||||||
|
|
||||||
|
|
||||||
def create_cmd_definition_tree() -> CmdTreeNode:
|
def create_cmd_definition_tree() -> CmdTreeNode:
|
||||||
|
|
||||||
root_node = CmdTreeNode.root_node()
|
root_node = CmdTreeNode.root_node()
|
||||||
|
|
||||||
hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True)
|
hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True)
|
||||||
@ -101,7 +65,15 @@ def create_cmd_definition_tree() -> CmdTreeNode:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
root_node.add_child(scheduler_node)
|
root_node.add_child(scheduler_node)
|
||||||
root_node.add_child(create_acs_node(mode_node, hk_node))
|
|
||||||
|
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
||||||
|
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
||||||
|
mgm_node.add_child(mode_node)
|
||||||
|
mgm_node.add_child(hk_node)
|
||||||
|
|
||||||
|
acs_node.add_child(mgm_node)
|
||||||
|
root_node.add_child(acs_node)
|
||||||
|
|
||||||
return root_node
|
return root_node
|
||||||
|
|
||||||
|
|
||||||
@ -140,4 +112,32 @@ def pack_pus_telecommands(q: DefaultPusQueueHelper, cmd_path: str):
|
|||||||
if cmd_path_list[0] == "acs":
|
if cmd_path_list[0] == "acs":
|
||||||
assert len(cmd_path_list) >= 2
|
assert len(cmd_path_list) >= 2
|
||||||
if cmd_path_list[1] == "mgms":
|
if cmd_path_list[1] == "mgms":
|
||||||
create_mgm_cmds(q, cmd_path_list)
|
assert len(cmd_path_list) >= 3
|
||||||
|
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))
|
@ -1,27 +0,0 @@
|
|||||||
[build-system]
|
|
||||||
requires = ["setuptools>=61.0"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "pytmtc"
|
|
||||||
description = "Python TMTC client for OPS-SAT"
|
|
||||||
readme = "README.md"
|
|
||||||
version = "0.1.0"
|
|
||||||
requires-python = ">=3.8"
|
|
||||||
authors = [
|
|
||||||
{name = "Robin Mueller", email = "robin.mueller.m@gmail.com"},
|
|
||||||
]
|
|
||||||
dependencies = [
|
|
||||||
"tmtccmd~=8.1",
|
|
||||||
"pydantic~=2.7"
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.setuptools.packages]
|
|
||||||
find = {}
|
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
extend-exclude = ["archive"]
|
|
||||||
[tool.ruff.lint]
|
|
||||||
ignore = ["E501"]
|
|
||||||
[tool.ruff.lint.extend-per-file-ignores]
|
|
||||||
"__init__.py" = ["F401"]
|
|
@ -1,11 +0,0 @@
|
|||||||
from tmtccmd.config import CmdTreeNode
|
|
||||||
|
|
||||||
|
|
||||||
def create_acs_node(mode_node: CmdTreeNode, hk_node: CmdTreeNode) -> CmdTreeNode:
|
|
||||||
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
|
||||||
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
|
||||||
mgm_node.add_child(mode_node)
|
|
||||||
mgm_node.add_child(hk_node)
|
|
||||||
|
|
||||||
acs_node.add_child(mgm_node)
|
|
||||||
return acs_node
|
|
@ -1,45 +0,0 @@
|
|||||||
import logging
|
|
||||||
import struct
|
|
||||||
import enum
|
|
||||||
from typing import List
|
|
||||||
from spacepackets.ecss import PusTm
|
|
||||||
from tmtccmd.tmtc import DefaultPusQueueHelper
|
|
||||||
|
|
||||||
from pytmtc.common import AcsId, Apid
|
|
||||||
from pytmtc.hk_common import create_request_one_shot_hk_cmd
|
|
||||||
from pytmtc.mode import handle_set_mode_cmd
|
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SetId(enum.IntEnum):
|
|
||||||
SENSOR_SET = 0
|
|
||||||
|
|
||||||
|
|
||||||
def create_mgm_cmds(q: DefaultPusQueueHelper, cmd_path: List[str]):
|
|
||||||
assert len(cmd_path) >= 3
|
|
||||||
if cmd_path[2] == "hk":
|
|
||||||
if cmd_path[3] == "one_shot_hk":
|
|
||||||
q.add_log_cmd("Sending HK one shot request")
|
|
||||||
q.add_pus_tc(
|
|
||||||
create_request_one_shot_hk_cmd(Apid.ACS, AcsId.MGM_0, SetId.SENSOR_SET)
|
|
||||||
)
|
|
||||||
|
|
||||||
if cmd_path[2] == "mode":
|
|
||||||
if cmd_path[3] == "set_mode":
|
|
||||||
handle_set_mode_cmd(q, "MGM 0", cmd_path[4], Apid.ACS, AcsId.MGM_0)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_mgm_hk_report(pus_tm: PusTm, set_id: int, hk_data: bytes):
|
|
||||||
if set_id == SetId.SENSOR_SET:
|
|
||||||
if len(hk_data) != 13:
|
|
||||||
raise ValueError(f"invalid HK data length, expected 13, got {len(hk_data)}")
|
|
||||||
data_valid = hk_data[0]
|
|
||||||
mgm_x = struct.unpack("!f", hk_data[1:5])[0]
|
|
||||||
mgm_y = struct.unpack("!f", hk_data[5:9])[0]
|
|
||||||
mgm_z = struct.unpack("!f", hk_data[9:13])[0]
|
|
||||||
_LOGGER.info(
|
|
||||||
f"received MGM HK set in uT: Valid {data_valid} X {mgm_x} Y {mgm_y} Z {mgm_z}"
|
|
||||||
)
|
|
||||||
pass
|
|
@ -1,47 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
from prompt_toolkit.history import FileHistory, History
|
|
||||||
from spacepackets.ccsds import PacketId, PacketType
|
|
||||||
from tmtccmd import HookBase
|
|
||||||
from tmtccmd.com import ComInterface
|
|
||||||
from tmtccmd.config import CmdTreeNode
|
|
||||||
from tmtccmd.util.obj_id import ObjectIdDictT
|
|
||||||
|
|
||||||
from pytmtc.common import Apid
|
|
||||||
from pytmtc.pus_tc import create_cmd_definition_tree
|
|
||||||
|
|
||||||
|
|
||||||
class SatrsConfigHook(HookBase):
|
|
||||||
def __init__(self, json_cfg_path: str):
|
|
||||||
super().__init__(json_cfg_path)
|
|
||||||
|
|
||||||
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
|
||||||
from tmtccmd.config.com import (
|
|
||||||
create_com_interface_default,
|
|
||||||
create_com_interface_cfg_default,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert self.cfg_path is not None
|
|
||||||
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=packet_id_list,
|
|
||||||
)
|
|
||||||
assert cfg is not None
|
|
||||||
return create_com_interface_default(cfg)
|
|
||||||
|
|
||||||
def get_command_definitions(self) -> CmdTreeNode:
|
|
||||||
"""This function should return the root node of the command definition tree."""
|
|
||||||
return create_cmd_definition_tree()
|
|
||||||
|
|
||||||
def get_cmd_history(self) -> Optional[History]:
|
|
||||||
"""Optionlly return a history class for the past command paths which will be used
|
|
||||||
when prompting a command path from the user in CLI mode."""
|
|
||||||
return FileHistory(".tmtc-history.txt")
|
|
||||||
|
|
||||||
def get_object_ids(self) -> ObjectIdDictT:
|
|
||||||
from tmtccmd.config.objects import get_core_object_ids
|
|
||||||
|
|
||||||
return get_core_object_ids()
|
|
@ -1,42 +0,0 @@
|
|||||||
import logging
|
|
||||||
import struct
|
|
||||||
from spacepackets.ecss.pus_3_hk import Subservice
|
|
||||||
from spacepackets.ecss import PusTm
|
|
||||||
|
|
||||||
from pytmtc.common import AcsId, Apid
|
|
||||||
from pytmtc.acs.mgms import handle_mgm_hk_report
|
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_hk_packet(pus_tm: PusTm):
|
|
||||||
if len(pus_tm.source_data) < 4:
|
|
||||||
raise ValueError("no unique ID in HK packet")
|
|
||||||
unique_id = struct.unpack("!I", pus_tm.source_data[:4])[0]
|
|
||||||
if (
|
|
||||||
pus_tm.subservice == Subservice.TM_HK_REPORT
|
|
||||||
or pus_tm.subservice == Subservice.TM_DIAGNOSTICS_REPORT
|
|
||||||
):
|
|
||||||
if len(pus_tm.source_data) < 8:
|
|
||||||
raise ValueError("no set ID in HK packet")
|
|
||||||
set_id = struct.unpack("!I", pus_tm.source_data[4:8])[0]
|
|
||||||
handle_hk_report(pus_tm, unique_id, set_id)
|
|
||||||
_LOGGER.warning(
|
|
||||||
f"handling for HK packet with subservice {pus_tm.subservice} not implemented yet"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_hk_report(pus_tm: PusTm, unique_id: int, set_id: int):
|
|
||||||
hk_data = pus_tm.source_data[8:]
|
|
||||||
if pus_tm.apid == Apid.ACS:
|
|
||||||
if unique_id == AcsId.MGM_0:
|
|
||||||
handle_mgm_hk_report(pus_tm, set_id, hk_data)
|
|
||||||
else:
|
|
||||||
_LOGGER.warning(
|
|
||||||
f"handling for HK report with unique ID {unique_id} not implemented yet"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
_LOGGER.warning(
|
|
||||||
f"handling for HK report with apid {pus_tm.apid} not implemented yet"
|
|
||||||
)
|
|
@ -1,16 +0,0 @@
|
|||||||
import struct
|
|
||||||
|
|
||||||
from spacepackets.ecss import PusService, PusTc
|
|
||||||
from spacepackets.ecss.pus_3_hk import Subservice
|
|
||||||
|
|
||||||
|
|
||||||
def create_request_one_shot_hk_cmd(apid: int, unique_id: int, set_id: int) -> PusTc:
|
|
||||||
app_data = bytearray()
|
|
||||||
app_data.extend(struct.pack("!I", unique_id))
|
|
||||||
app_data.extend(struct.pack("!I", set_id))
|
|
||||||
return PusTc(
|
|
||||||
service=PusService.S3_HOUSEKEEPING,
|
|
||||||
subservice=Subservice.TC_GENERATE_ONE_PARAMETER_REPORT,
|
|
||||||
apid=apid,
|
|
||||||
app_data=app_data,
|
|
||||||
)
|
|
@ -1,31 +0,0 @@
|
|||||||
import struct
|
|
||||||
from spacepackets.ecss import PusTc
|
|
||||||
from tmtccmd.pus.s200_fsfw_mode import Mode, Subservice
|
|
||||||
from tmtccmd.tmtc import DefaultPusQueueHelper
|
|
||||||
|
|
||||||
|
|
||||||
def create_set_mode_cmd(apid: int, unique_id: int, mode: int, submode: int) -> PusTc:
|
|
||||||
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 PusTc(
|
|
||||||
service=200,
|
|
||||||
subservice=Subservice.TC_MODE_COMMAND,
|
|
||||||
apid=apid,
|
|
||||||
app_data=app_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
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))
|
|
@ -1,93 +0,0 @@
|
|||||||
import logging
|
|
||||||
from typing import Any
|
|
||||||
from spacepackets.ccsds.time import CdsShortTimestamp
|
|
||||||
from spacepackets.ecss import PusTm
|
|
||||||
from spacepackets.ecss.pus_17_test import Service17Tm
|
|
||||||
from spacepackets.ecss.pus_1_verification import Service1Tm, UnpackParams
|
|
||||||
from tmtccmd.logging.pus import RawTmtcTimedLogWrapper
|
|
||||||
from tmtccmd.pus import VerificationWrapper
|
|
||||||
from tmtccmd.tmtc import GenericApidHandlerBase
|
|
||||||
|
|
||||||
from pytmtc.common import Apid, EventU32
|
|
||||||
from pytmtc.hk import handle_hk_packet
|
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class PusHandler(GenericApidHandlerBase):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
file_logger: logging.Logger,
|
|
||||||
verif_wrapper: VerificationWrapper,
|
|
||||||
raw_logger: RawTmtcTimedLogWrapper,
|
|
||||||
):
|
|
||||||
super().__init__(None)
|
|
||||||
self.file_logger = file_logger
|
|
||||||
self.raw_logger = raw_logger
|
|
||||||
self.verif_wrapper = verif_wrapper
|
|
||||||
|
|
||||||
def handle_tm(self, apid: int, packet: bytes, _user_args: Any):
|
|
||||||
try:
|
|
||||||
pus_tm = PusTm.unpack(
|
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
|
||||||
_LOGGER.warning("Could not generate PUS TM object from raw data")
|
|
||||||
_LOGGER.warning(f"Raw Packet: [{packet.hex(sep=',')}], REPR: {packet!r}")
|
|
||||||
raise e
|
|
||||||
service = pus_tm.service
|
|
||||||
if service == 1:
|
|
||||||
tm_packet = Service1Tm.unpack(
|
|
||||||
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 1, 2)
|
|
||||||
)
|
|
||||||
res = self.verif_wrapper.add_tm(tm_packet)
|
|
||||||
if res is None:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Received Verification TM[{tm_packet.service}, {tm_packet.subservice}] "
|
|
||||||
f"with Request ID {tm_packet.tc_req_id.as_u32():#08x}"
|
|
||||||
)
|
|
||||||
_LOGGER.warning(
|
|
||||||
f"No matching telecommand found for {tm_packet.tc_req_id}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.verif_wrapper.log_to_console(tm_packet, res)
|
|
||||||
self.verif_wrapper.log_to_file(tm_packet, res)
|
|
||||||
elif service == 3:
|
|
||||||
pus_tm = PusTm.unpack(
|
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
handle_hk_packet(pus_tm)
|
|
||||||
elif service == 5:
|
|
||||||
tm_packet = PusTm.unpack(
|
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
src_data = tm_packet.source_data
|
|
||||||
event_u32 = EventU32.unpack(src_data)
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Received event packet. Source APID: {Apid(tm_packet.apid)!r}, Event: {event_u32}"
|
|
||||||
)
|
|
||||||
if event_u32.group_id == 0 and event_u32.unique_id == 0:
|
|
||||||
_LOGGER.info("Received test event")
|
|
||||||
elif service == 17:
|
|
||||||
tm_packet = Service17Tm.unpack(
|
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
if tm_packet.subservice == 2:
|
|
||||||
self.file_logger.info("Received Ping Reply TM[17,2]")
|
|
||||||
_LOGGER.info("Received Ping Reply TM[17,2]")
|
|
||||||
else:
|
|
||||||
self.file_logger.info(
|
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
|
||||||
)
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
_LOGGER.info(
|
|
||||||
f"The service {service} is not implemented in Telemetry Factory"
|
|
||||||
)
|
|
||||||
tm_packet = PusTm.unpack(
|
|
||||||
packet, timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE
|
|
||||||
)
|
|
||||||
self.raw_logger.log_tm(pus_tm)
|
|
@ -1 +1,2 @@
|
|||||||
.
|
tmtccmd == 8.0.0rc2
|
||||||
|
# -e git+https://github.com/robamu-org/tmtccmd@97e5e51101a08b21472b3ddecc2063359f7e307a#egg=tmtccmd
|
||||||
|
38
satrs-example/pytmtc/tc_definitions.py
Normal file
38
satrs-example/pytmtc/tc_definitions.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from tmtccmd.config import OpCodeEntry, TmtcDefinitionWrapper, CoreServiceList
|
||||||
|
from tmtccmd.config.globals import get_default_tmtc_defs
|
||||||
|
|
||||||
|
from common import HkOpCodes
|
||||||
|
|
||||||
|
|
||||||
|
def tc_definitions() -> TmtcDefinitionWrapper:
|
||||||
|
defs = get_default_tmtc_defs()
|
||||||
|
srv_5 = OpCodeEntry()
|
||||||
|
srv_5.add("0", "Event Test")
|
||||||
|
defs.add_service(
|
||||||
|
name=CoreServiceList.SERVICE_5.value,
|
||||||
|
info="PUS Service 5 Event",
|
||||||
|
op_code_entry=srv_5,
|
||||||
|
)
|
||||||
|
srv_17 = OpCodeEntry()
|
||||||
|
srv_17.add("ping", "Ping Test")
|
||||||
|
srv_17.add("trigger_event", "Trigger Event")
|
||||||
|
defs.add_service(
|
||||||
|
name=CoreServiceList.SERVICE_17_ALT,
|
||||||
|
info="PUS Service 17 Test",
|
||||||
|
op_code_entry=srv_17,
|
||||||
|
)
|
||||||
|
srv_3 = OpCodeEntry()
|
||||||
|
srv_3.add(HkOpCodes.GENERATE_ONE_SHOT, "Generate AOCS one shot HK")
|
||||||
|
defs.add_service(
|
||||||
|
name=CoreServiceList.SERVICE_3,
|
||||||
|
info="PUS Service 3 Housekeeping",
|
||||||
|
op_code_entry=srv_3,
|
||||||
|
)
|
||||||
|
srv_11 = OpCodeEntry()
|
||||||
|
srv_11.add("0", "Scheduled TC Test")
|
||||||
|
defs.add_service(
|
||||||
|
name=CoreServiceList.SERVICE_11,
|
||||||
|
info="PUS Service 11 TC Scheduling",
|
||||||
|
op_code_entry=srv_11,
|
||||||
|
)
|
||||||
|
return defs
|
@ -1,48 +0,0 @@
|
|||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
from spacepackets.ccsds import CdsShortTimestamp
|
|
||||||
from tmtccmd.tmtc import DefaultPusQueueHelper, QueueEntryHelper
|
|
||||||
from tmtccmd.tmtc.queue import QueueWrapper
|
|
||||||
|
|
||||||
from pytmtc.config import SatrsConfigHook
|
|
||||||
from pytmtc.pus_tc import pack_pus_telecommands
|
|
||||||
|
|
||||||
|
|
||||||
class TestTcModules(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.hook = SatrsConfigHook(json_cfg_path="tmtc_conf.json")
|
|
||||||
self.queue_helper = DefaultPusQueueHelper(
|
|
||||||
queue_wrapper=QueueWrapper.empty(),
|
|
||||||
tc_sched_timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE,
|
|
||||||
seq_cnt_provider=None,
|
|
||||||
pus_verificator=None,
|
|
||||||
default_pus_apid=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_cmd_tree_creation_works_without_errors(self):
|
|
||||||
cmd_defs = self.hook.get_command_definitions()
|
|
||||||
self.assertIsNotNone(cmd_defs)
|
|
||||||
|
|
||||||
def test_ping_cmd_generation(self):
|
|
||||||
pack_pus_telecommands(self.queue_helper, "/test/ping")
|
|
||||||
queue_entry = self.queue_helper.queue_wrapper.queue.popleft()
|
|
||||||
entry_helper = QueueEntryHelper(queue_entry)
|
|
||||||
log_queue = entry_helper.to_log_entry()
|
|
||||||
self.assertEqual(log_queue.log_str, "Sending PUS ping telecommand")
|
|
||||||
queue_entry = self.queue_helper.queue_wrapper.queue.popleft()
|
|
||||||
entry_helper.entry = queue_entry
|
|
||||||
pus_tc_entry = entry_helper.to_pus_tc_entry()
|
|
||||||
self.assertEqual(pus_tc_entry.pus_tc.service, 17)
|
|
||||||
self.assertEqual(pus_tc_entry.pus_tc.subservice, 1)
|
|
||||||
|
|
||||||
def test_event_trigger_generation(self):
|
|
||||||
pack_pus_telecommands(self.queue_helper, "/test/trigger_event")
|
|
||||||
queue_entry = self.queue_helper.queue_wrapper.queue.popleft()
|
|
||||||
entry_helper = QueueEntryHelper(queue_entry)
|
|
||||||
log_queue = entry_helper.to_log_entry()
|
|
||||||
self.assertEqual(log_queue.log_str, "Triggering test event")
|
|
||||||
queue_entry = self.queue_helper.queue_wrapper.queue.popleft()
|
|
||||||
entry_helper.entry = queue_entry
|
|
||||||
pus_tc_entry = entry_helper.to_pus_tc_entry()
|
|
||||||
self.assertEqual(pus_tc_entry.pus_tc.service, 17)
|
|
||||||
self.assertEqual(pus_tc_entry.pus_tc.subservice, 128)
|
|
@ -1 +0,0 @@
|
|||||||
// TODO: Write the assembly
|
|
@ -1 +0,0 @@
|
|||||||
// TODO: Write dummy controller
|
|
@ -1,125 +1,52 @@
|
|||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::hk::{HkRequest, HkRequestVariant};
|
use satrs::hk::{HkRequest, HkRequestVariant};
|
||||||
use satrs::mode_tree::{ModeChild, ModeNode};
|
use satrs::queue::{GenericSendError, GenericTargetedMessagingError};
|
||||||
use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender};
|
use satrs::spacepackets::ecss::hk;
|
||||||
use satrs_example::config::pus::PUS_MODE_SERVICE;
|
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
use satrs_example::{DeviceMode, TimestampHelper};
|
use satrs::spacepackets::SpHeader;
|
||||||
use satrs_minisim::acs::lis3mdl::{
|
use satrs_example::{DeviceMode, TimeStampHelper};
|
||||||
MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR,
|
|
||||||
};
|
|
||||||
use satrs_minisim::acs::MgmRequestLis3Mdl;
|
|
||||||
use satrs_minisim::eps::PcduSwitch;
|
|
||||||
use satrs_minisim::{SerializableSimMsgPayload, SimReply, SimRequest};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::sync::mpsc::{self};
|
use std::sync::mpsc::{self};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use satrs::mode::{
|
use satrs::mode::{
|
||||||
ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler,
|
ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequest, ModeRequestHandler,
|
||||||
ModeRequestHandlerMpscBounded,
|
|
||||||
};
|
};
|
||||||
use satrs::pus::{EcssTmSender, PusTmVariant};
|
use satrs::pus::{EcssTmSender, PusTmVariant};
|
||||||
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||||
use satrs_example::config::components::NO_SENDER;
|
use satrs_example::config::components::PUS_MODE_SERVICE;
|
||||||
|
|
||||||
use crate::hk::PusHkHelper;
|
|
||||||
use crate::pus::hk::{HkReply, HkReplyVariant};
|
use crate::pus::hk::{HkReply, HkReplyVariant};
|
||||||
use crate::requests::CompositeRequest;
|
use crate::requests::CompositeRequest;
|
||||||
use crate::spi::SpiInterface;
|
|
||||||
use crate::tmtc::sender::TmTcSender;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub const NR_OF_DATA_AND_CFG_REGISTERS: usize = 14;
|
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;
|
||||||
|
|
||||||
// Register adresses to access various bytes from the raw reply.
|
pub trait SpiInterface {
|
||||||
pub const X_LOWBYTE_IDX: usize = 9;
|
type Error;
|
||||||
pub const Y_LOWBYTE_IDX: usize = 11;
|
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error>;
|
||||||
pub const Z_LOWBYTE_IDX: usize = 13;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum SetId {
|
|
||||||
SensorData = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
|
||||||
pub enum TransitionState {
|
|
||||||
#[default]
|
|
||||||
Idle,
|
|
||||||
PowerSwitching,
|
|
||||||
Done,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SpiDummyInterface {
|
pub struct SpiDummyInterface {
|
||||||
pub dummy_values: MgmLis3RawValues,
|
pub dummy_val_0: i16,
|
||||||
|
pub dummy_val_1: i16,
|
||||||
|
pub dummy_val_2: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpiInterface for SpiDummyInterface {
|
impl SpiInterface for SpiDummyInterface {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
|
fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
rx[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.x.to_le_bytes());
|
rx[0..2].copy_from_slice(&self.dummy_val_0.to_be_bytes());
|
||||||
rx[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.y.to_be_bytes());
|
rx[2..4].copy_from_slice(&self.dummy_val_1.to_be_bytes());
|
||||||
rx[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.z.to_be_bytes());
|
rx[4..6].copy_from_slice(&self.dummy_val_2.to_be_bytes());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpiSimInterface {
|
|
||||||
pub sim_request_tx: mpsc::Sender<SimRequest>,
|
|
||||||
pub sim_reply_rx: mpsc::Receiver<SimReply>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpiInterface for SpiSimInterface {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
// Right now, we only support requesting sensor data and not configuration of the sensor.
|
|
||||||
fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
let mgm_sensor_request = MgmRequestLis3Mdl::RequestSensorData;
|
|
||||||
if let Err(e) = self
|
|
||||||
.sim_request_tx
|
|
||||||
.send(SimRequest::new_with_epoch_time(mgm_sensor_request))
|
|
||||||
{
|
|
||||||
log::error!("failed to send MGM LIS3 request: {}", e);
|
|
||||||
}
|
|
||||||
match self.sim_reply_rx.recv_timeout(Duration::from_millis(50)) {
|
|
||||||
Ok(sim_reply) => {
|
|
||||||
let sim_reply_lis3 = MgmLis3MdlReply::from_sim_message(&sim_reply)
|
|
||||||
.expect("failed to parse LIS3 reply");
|
|
||||||
rx[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2]
|
|
||||||
.copy_from_slice(&sim_reply_lis3.raw.x.to_le_bytes());
|
|
||||||
rx[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2]
|
|
||||||
.copy_from_slice(&sim_reply_lis3.raw.y.to_le_bytes());
|
|
||||||
rx[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2]
|
|
||||||
.copy_from_slice(&sim_reply_lis3.raw.z.to_le_bytes());
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("MGM LIS3 SIM reply timeout: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum SpiSimInterfaceWrapper {
|
|
||||||
Dummy(SpiDummyInterface),
|
|
||||||
Sim(SpiSimInterface),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpiInterface for SpiSimInterfaceWrapper {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
match self {
|
|
||||||
SpiSimInterfaceWrapper::Dummy(dummy) => dummy.transfer(tx, rx),
|
|
||||||
SpiSimInterfaceWrapper::Sim(sim_if) => sim_if.transfer(tx, rx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
|
||||||
pub struct MgmData {
|
pub struct MgmData {
|
||||||
pub valid: bool,
|
pub valid: bool,
|
||||||
@ -128,79 +55,63 @@ pub struct MgmData {
|
|||||||
pub z: f32,
|
pub z: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
pub struct MpscModeLeafInterface {
|
||||||
pub struct BufWrapper {
|
pub request_rx: mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||||
tx_buf: [u8; 32],
|
pub reply_tx_to_pus: mpsc::Sender<GenericMessage<ModeReply>>,
|
||||||
rx_buf: [u8; 32],
|
pub reply_tx_to_parent: mpsc::Sender<GenericMessage<ModeReply>>,
|
||||||
tm_buf: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ModeHelpers {
|
|
||||||
current: ModeAndSubmode,
|
|
||||||
target: Option<ModeAndSubmode>,
|
|
||||||
requestor_info: Option<MessageMetadata>,
|
|
||||||
transition_state: TransitionState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ModeHelpers {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
current: ModeAndSubmode::new(DeviceMode::Off as u32, 0),
|
|
||||||
target: Default::default(),
|
|
||||||
requestor_info: Default::default(),
|
|
||||||
transition_state: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Example MGM device handler strongly based on the LIS3MDL MEMS device.
|
/// Example MGM device handler strongly based on the LIS3MDL MEMS device.
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub struct MgmHandlerLis3Mdl<
|
pub struct MgmHandlerLis3Mdl<ComInterface: SpiInterface, TmSender: EcssTmSender> {
|
||||||
ComInterface: SpiInterface,
|
|
||||||
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
|
||||||
> {
|
|
||||||
id: UniqueApidTargetId,
|
id: UniqueApidTargetId,
|
||||||
dev_str: &'static str,
|
dev_str: &'static str,
|
||||||
mode_node: ModeRequestHandlerMpscBounded,
|
mode_interface: MpscModeLeafInterface,
|
||||||
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
composite_request_receiver: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||||
hk_reply_tx: mpsc::SyncSender<GenericMessage<HkReply>>,
|
hk_reply_sender: mpsc::Sender<GenericMessage<HkReply>>,
|
||||||
switch_helper: SwitchHelper,
|
tm_sender: TmSender,
|
||||||
tm_sender: TmTcSender,
|
com_interface: ComInterface,
|
||||||
pub com_interface: ComInterface,
|
|
||||||
shared_mgm_set: Arc<Mutex<MgmData>>,
|
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||||
#[new(value = "PusHkHelper::new(id)")]
|
#[new(value = "ModeAndSubmode::new(satrs_example::DeviceMode::Off as u32, 0)")]
|
||||||
hk_helper: PusHkHelper,
|
mode_and_submode: ModeAndSubmode,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
mode_helpers: ModeHelpers,
|
tx_buf: [u8; 12],
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
bufs: BufWrapper,
|
rx_buf: [u8; 12],
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
stamp_helper: TimestampHelper,
|
tm_buf: [u8; 16],
|
||||||
|
#[new(default)]
|
||||||
|
stamp_helper: TimeStampHelper,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<ComInterface: SpiInterface, TmSender: EcssTmSender> MgmHandlerLis3Mdl<ComInterface, TmSender> {
|
||||||
ComInterface: SpiInterface,
|
|
||||||
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
|
||||||
> MgmHandlerLis3Mdl<ComInterface, SwitchHelper>
|
|
||||||
{
|
|
||||||
pub fn periodic_operation(&mut self) {
|
pub fn periodic_operation(&mut self) {
|
||||||
self.stamp_helper.update_from_now();
|
self.stamp_helper.update_from_now();
|
||||||
// Handle requests.
|
// Handle requests.
|
||||||
self.handle_composite_requests();
|
self.handle_composite_requests();
|
||||||
self.handle_mode_requests();
|
self.handle_mode_requests();
|
||||||
if let Some(target_mode_submode) = self.mode_helpers.target {
|
|
||||||
self.handle_mode_transition(target_mode_submode);
|
|
||||||
}
|
|
||||||
if self.mode() == DeviceMode::Normal as u32 {
|
if self.mode() == DeviceMode::Normal as u32 {
|
||||||
log::trace!("polling LIS3MDL sensor {}", self.dev_str);
|
log::trace!("polling LIS3MDL sensor {}", self.dev_str);
|
||||||
self.poll_sensor();
|
// 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) {
|
pub fn handle_composite_requests(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
match self.composite_request_rx.try_recv() {
|
match self.composite_request_receiver.try_recv() {
|
||||||
Ok(ref msg) => match &msg.message {
|
Ok(ref msg) => match &msg.message {
|
||||||
CompositeRequest::Hk(hk_request) => {
|
CompositeRequest::Hk(hk_request) => {
|
||||||
self.handle_hk_request(&msg.requestor_info, hk_request)
|
self.handle_hk_request(&msg.requestor_info, hk_request)
|
||||||
@ -228,33 +139,34 @@ impl<
|
|||||||
pub fn handle_hk_request(&mut self, requestor_info: &MessageMetadata, hk_request: &HkRequest) {
|
pub fn handle_hk_request(&mut self, requestor_info: &MessageMetadata, hk_request: &HkRequest) {
|
||||||
match hk_request.variant {
|
match hk_request.variant {
|
||||||
HkRequestVariant::OneShot => {
|
HkRequestVariant::OneShot => {
|
||||||
let mgm_snapshot = *self.shared_mgm_set.lock().unwrap();
|
self.hk_reply_sender
|
||||||
if let Ok(hk_tm) = self.hk_helper.generate_hk_report_packet(
|
.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(),
|
self.stamp_helper.stamp(),
|
||||||
SetId::SensorData as u32,
|
);
|
||||||
&mut |hk_buf| {
|
let mgm_snapshot = *self.shared_mgm_set.lock().unwrap();
|
||||||
hk_buf[0] = mgm_snapshot.valid as u8;
|
// Use binary serialization here. We want the data to be tightly packed.
|
||||||
hk_buf[1..5].copy_from_slice(&mgm_snapshot.x.to_be_bytes());
|
self.tm_buf[0] = mgm_snapshot.valid as u8;
|
||||||
hk_buf[5..9].copy_from_slice(&mgm_snapshot.y.to_be_bytes());
|
self.tm_buf[1..5].copy_from_slice(&mgm_snapshot.x.to_be_bytes());
|
||||||
hk_buf[9..13].copy_from_slice(&mgm_snapshot.z.to_be_bytes());
|
self.tm_buf[5..9].copy_from_slice(&mgm_snapshot.y.to_be_bytes());
|
||||||
Ok(13)
|
self.tm_buf[9..13].copy_from_slice(&mgm_snapshot.z.to_be_bytes());
|
||||||
},
|
let hk_tm = PusTmCreator::new(
|
||||||
&mut self.bufs.tm_buf,
|
SpHeader::new_from_apid(self.id.apid),
|
||||||
) {
|
sec_header,
|
||||||
// TODO: If sending the TM fails, we should also send a failure reply.
|
&self.tm_buf[0..12],
|
||||||
self.tm_sender
|
true,
|
||||||
.send_tm(self.id.id(), PusTmVariant::Direct(hk_tm))
|
);
|
||||||
.expect("failed to send HK TM");
|
self.tm_sender
|
||||||
self.hk_reply_tx
|
.send_tm(self.id.id(), PusTmVariant::Direct(hk_tm))
|
||||||
.send(GenericMessage::new(
|
.expect("failed to send HK TM");
|
||||||
*requestor_info,
|
|
||||||
HkReply::new(hk_request.unique_id, HkReplyVariant::Ack),
|
|
||||||
))
|
|
||||||
.expect("failed to send HK reply");
|
|
||||||
} else {
|
|
||||||
// TODO: Send back failure reply. Need result code for this.
|
|
||||||
log::error!("TM buffer too small to generate HK data");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
HkRequestVariant::EnablePeriodic => todo!(),
|
HkRequestVariant::EnablePeriodic => todo!(),
|
||||||
HkRequestVariant::DisablePeriodic => todo!(),
|
HkRequestVariant::DisablePeriodic => todo!(),
|
||||||
@ -265,137 +177,54 @@ impl<
|
|||||||
pub fn handle_mode_requests(&mut self) {
|
pub fn handle_mode_requests(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
// TODO: Only allow one set mode request per cycle?
|
// TODO: Only allow one set mode request per cycle?
|
||||||
match self.mode_node.try_recv_mode_request() {
|
match self.mode_interface.request_rx.try_recv() {
|
||||||
Ok(opt_msg) => {
|
Ok(msg) => {
|
||||||
if let Some(msg) = opt_msg {
|
let result = self.handle_mode_request(msg);
|
||||||
let result = self.handle_mode_request(msg);
|
// TODO: Trigger event?
|
||||||
// TODO: Trigger event?
|
if result.is_err() {
|
||||||
if result.is_err() {
|
log::warn!(
|
||||||
log::warn!(
|
"{}: mode request failed with error {:?}",
|
||||||
"{}: mode request failed with error {:?}",
|
self.dev_str,
|
||||||
self.dev_str,
|
result.err().unwrap()
|
||||||
result.err().unwrap()
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if e != mpsc::TryRecvError::Empty {
|
||||||
|
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => match e {
|
|
||||||
satrs::queue::GenericReceiveError::Empty => break,
|
|
||||||
satrs::queue::GenericReceiveError::TxDisconnected(e) => {
|
|
||||||
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_sensor(&mut self) {
|
|
||||||
// Communicate with the device. This is actually how to read the data from the LIS3 device
|
|
||||||
// SPI interface.
|
|
||||||
self.com_interface
|
|
||||||
.transfer(
|
|
||||||
&self.bufs.tx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1],
|
|
||||||
&mut self.bufs.rx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1],
|
|
||||||
)
|
|
||||||
.expect("failed to transfer data");
|
|
||||||
let x_raw = i16::from_le_bytes(
|
|
||||||
self.bufs.rx_buf[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2]
|
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
let y_raw = i16::from_le_bytes(
|
|
||||||
self.bufs.rx_buf[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2]
|
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
let z_raw = i16::from_le_bytes(
|
|
||||||
self.bufs.rx_buf[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2]
|
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
// Simple scaling to retrieve the float value, assuming the best sensor resolution.
|
|
||||||
let mut mgm_guard = self.shared_mgm_set.lock().unwrap();
|
|
||||||
mgm_guard.x = x_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS;
|
|
||||||
mgm_guard.y = y_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS;
|
|
||||||
mgm_guard.z = z_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS;
|
|
||||||
mgm_guard.valid = true;
|
|
||||||
drop(mgm_guard);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_mode_transition(&mut self, target_mode_submode: ModeAndSubmode) {
|
|
||||||
if target_mode_submode.mode() == DeviceMode::On as u32
|
|
||||||
|| target_mode_submode.mode() == DeviceMode::Normal as u32
|
|
||||||
{
|
|
||||||
if self.mode_helpers.transition_state == TransitionState::Idle {
|
|
||||||
let result = self
|
|
||||||
.switch_helper
|
|
||||||
.send_switch_on_cmd(MessageMetadata::new(0, self.id.id()), PcduSwitch::Mgm);
|
|
||||||
if result.is_err() {
|
|
||||||
// Could not send switch command.. still continue with transition.
|
|
||||||
log::error!("failed to send switch on command");
|
|
||||||
}
|
|
||||||
self.mode_helpers.transition_state = TransitionState::PowerSwitching;
|
|
||||||
}
|
|
||||||
if self.mode_helpers.transition_state == TransitionState::PowerSwitching
|
|
||||||
&& self
|
|
||||||
.switch_helper
|
|
||||||
.is_switch_on(PcduSwitch::Mgm)
|
|
||||||
.expect("switch info error")
|
|
||||||
{
|
|
||||||
self.mode_helpers.transition_state = TransitionState::Done;
|
|
||||||
}
|
|
||||||
if self.mode_helpers.transition_state == TransitionState::Done {
|
|
||||||
self.mode_helpers.current = self.mode_helpers.target.unwrap();
|
|
||||||
self.handle_mode_reached(self.mode_helpers.requestor_info)
|
|
||||||
.expect("failed to handle mode reached");
|
|
||||||
self.mode_helpers.transition_state = TransitionState::Idle;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<ComInterface: SpiInterface, TmSender: EcssTmSender> ModeProvider
|
||||||
ComInterface: SpiInterface,
|
for MgmHandlerLis3Mdl<ComInterface, TmSender>
|
||||||
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
|
||||||
> ModeProvider for MgmHandlerLis3Mdl<ComInterface, SwitchHelper>
|
|
||||||
{
|
{
|
||||||
fn mode_and_submode(&self) -> ModeAndSubmode {
|
fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||||
self.mode_helpers.current
|
self.mode_and_submode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<ComInterface: SpiInterface, TmSender: EcssTmSender> ModeRequestHandler
|
||||||
ComInterface: SpiInterface,
|
for MgmHandlerLis3Mdl<ComInterface, TmSender>
|
||||||
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
|
||||||
> ModeRequestHandler for MgmHandlerLis3Mdl<ComInterface, SwitchHelper>
|
|
||||||
{
|
{
|
||||||
type Error = ModeError;
|
type Error = ModeError;
|
||||||
|
|
||||||
fn start_transition(
|
fn start_transition(
|
||||||
&mut self,
|
&mut self,
|
||||||
requestor: MessageMetadata,
|
requestor: MessageMetadata,
|
||||||
mode_and_submode: ModeAndSubmode,
|
mode_and_submode: ModeAndSubmode,
|
||||||
_forced: bool,
|
|
||||||
) -> Result<(), satrs::mode::ModeError> {
|
) -> Result<(), satrs::mode::ModeError> {
|
||||||
log::info!(
|
log::info!(
|
||||||
"{}: transitioning to mode {:?}",
|
"{}: transitioning to mode {:?}",
|
||||||
self.dev_str,
|
self.dev_str,
|
||||||
mode_and_submode
|
mode_and_submode
|
||||||
);
|
);
|
||||||
self.mode_helpers.current = mode_and_submode;
|
self.mode_and_submode = mode_and_submode;
|
||||||
if mode_and_submode.mode() == DeviceMode::Off as u32 {
|
self.handle_mode_reached(Some(requestor))?;
|
||||||
self.shared_mgm_set.lock().unwrap().valid = false;
|
|
||||||
self.handle_mode_reached(Some(requestor))?;
|
|
||||||
} else if mode_and_submode.mode() == DeviceMode::Normal as u32
|
|
||||||
|| mode_and_submode.mode() == DeviceMode::On as u32
|
|
||||||
{
|
|
||||||
// TODO: Write helper method for the struct? Might help for other handlers as well..
|
|
||||||
self.mode_helpers.transition_state = TransitionState::Idle;
|
|
||||||
self.mode_helpers.requestor_info = Some(requestor);
|
|
||||||
self.mode_helpers.target = Some(mode_and_submode);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,7 +232,7 @@ impl<
|
|||||||
log::info!(
|
log::info!(
|
||||||
"{} announcing mode: {:?}",
|
"{} announcing mode: {:?}",
|
||||||
self.dev_str,
|
self.dev_str,
|
||||||
self.mode_and_submode()
|
self.mode_and_submode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,15 +240,11 @@ impl<
|
|||||||
&mut self,
|
&mut self,
|
||||||
requestor: Option<MessageMetadata>,
|
requestor: Option<MessageMetadata>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.mode_helpers.target = None;
|
|
||||||
self.announce_mode(requestor, false);
|
self.announce_mode(requestor, false);
|
||||||
if let Some(requestor) = requestor {
|
if let Some(requestor) = requestor {
|
||||||
if requestor.sender_id() == NO_SENDER {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"can not send back mode reply to sender {:x}",
|
"can not send back mode reply to sender {}",
|
||||||
requestor.sender_id()
|
requestor.sender_id()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -440,9 +265,10 @@ impl<
|
|||||||
requestor.sender_id()
|
requestor.sender_id()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.mode_node
|
self.mode_interface
|
||||||
.send_mode_reply(requestor, reply)
|
.reply_tx_to_pus
|
||||||
.map_err(ModeError::Send)?;
|
.send(GenericMessage::new(requestor, reply))
|
||||||
|
.map_err(|_| GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,265 +280,3 @@ impl<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
|
||||||
ComInterface: SpiInterface,
|
|
||||||
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
|
||||||
> ModeNode for MgmHandlerLis3Mdl<ComInterface, SwitchHelper>
|
|
||||||
{
|
|
||||||
fn id(&self) -> satrs::ComponentId {
|
|
||||||
self.id.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
ComInterface: SpiInterface,
|
|
||||||
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
|
||||||
> ModeChild for MgmHandlerLis3Mdl<ComInterface, SwitchHelper>
|
|
||||||
{
|
|
||||||
type Sender = mpsc::SyncSender<GenericMessage<ModeReply>>;
|
|
||||||
|
|
||||||
fn add_mode_parent(&mut self, id: satrs::ComponentId, reply_sender: Self::Sender) {
|
|
||||||
self.mode_node.add_message_target(id, reply_sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{mpsc, Arc},
|
|
||||||
};
|
|
||||||
|
|
||||||
use satrs::{
|
|
||||||
mode::{ModeReply, ModeRequest},
|
|
||||||
mode_tree::ModeParent,
|
|
||||||
power::SwitchStateBinary,
|
|
||||||
request::{GenericMessage, UniqueApidTargetId},
|
|
||||||
tmtc::PacketAsVec,
|
|
||||||
ComponentId,
|
|
||||||
};
|
|
||||||
use satrs_example::config::{acs::MGM_ASSEMBLY, components::Apid};
|
|
||||||
use satrs_minisim::acs::lis3mdl::MgmLis3RawValues;
|
|
||||||
|
|
||||||
use crate::{eps::TestSwitchHelper, pus::hk::HkReply, requests::CompositeRequest};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct TestSpiInterface {
|
|
||||||
pub call_count: u32,
|
|
||||||
pub next_mgm_data: MgmLis3RawValues,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpiInterface for TestSpiInterface {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
rx[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2]
|
|
||||||
.copy_from_slice(&self.next_mgm_data.x.to_le_bytes());
|
|
||||||
rx[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2]
|
|
||||||
.copy_from_slice(&self.next_mgm_data.y.to_le_bytes());
|
|
||||||
rx[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2]
|
|
||||||
.copy_from_slice(&self.next_mgm_data.z.to_le_bytes());
|
|
||||||
self.call_count += 1;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct MgmTestbench {
|
|
||||||
pub mode_request_tx: mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
|
||||||
pub mode_reply_rx_to_pus: mpsc::Receiver<GenericMessage<ModeReply>>,
|
|
||||||
pub mode_reply_rx_to_parent: mpsc::Receiver<GenericMessage<ModeReply>>,
|
|
||||||
pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
|
|
||||||
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
|
|
||||||
pub tm_rx: mpsc::Receiver<PacketAsVec>,
|
|
||||||
pub handler: MgmHandlerLis3Mdl<TestSpiInterface, TestSwitchHelper>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MgmAssemblyMock(
|
|
||||||
pub HashMap<ComponentId, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl ModeNode for MgmAssemblyMock {
|
|
||||||
fn id(&self) -> satrs::ComponentId {
|
|
||||||
PUS_MODE_SERVICE.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModeParent for MgmAssemblyMock {
|
|
||||||
type Sender = mpsc::SyncSender<GenericMessage<ModeRequest>>;
|
|
||||||
|
|
||||||
fn add_mode_child(&mut self, id: satrs::ComponentId, request_sender: Self::Sender) {
|
|
||||||
self.0.insert(id, request_sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PusMock {
|
|
||||||
pub request_sender_map: HashMap<ComponentId, mpsc::SyncSender<GenericMessage<ModeRequest>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModeNode for PusMock {
|
|
||||||
fn id(&self) -> satrs::ComponentId {
|
|
||||||
PUS_MODE_SERVICE.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModeParent for PusMock {
|
|
||||||
type Sender = mpsc::SyncSender<GenericMessage<ModeRequest>>;
|
|
||||||
|
|
||||||
fn add_mode_child(&mut self, id: satrs::ComponentId, request_sender: Self::Sender) {
|
|
||||||
self.request_sender_map.insert(id, request_sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MgmTestbench {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let (request_tx, request_rx) = mpsc::sync_channel(5);
|
|
||||||
let (reply_tx_to_pus, reply_rx_to_pus) = mpsc::sync_channel(5);
|
|
||||||
let (reply_tx_to_parent, reply_rx_to_parent) = mpsc::sync_channel(5);
|
|
||||||
let id = UniqueApidTargetId::new(Apid::Acs as u16, 1);
|
|
||||||
let mode_node = ModeRequestHandlerMpscBounded::new(id.into(), request_rx);
|
|
||||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
|
||||||
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
|
||||||
let (tm_tx, tm_rx) = mpsc::sync_channel(10);
|
|
||||||
let tm_sender = TmTcSender::Heap(tm_tx);
|
|
||||||
let shared_mgm_set = Arc::default();
|
|
||||||
let mut handler = MgmHandlerLis3Mdl::new(
|
|
||||||
id,
|
|
||||||
"TEST_MGM",
|
|
||||||
mode_node,
|
|
||||||
composite_request_rx,
|
|
||||||
hk_reply_tx,
|
|
||||||
TestSwitchHelper::default(),
|
|
||||||
tm_sender,
|
|
||||||
TestSpiInterface::default(),
|
|
||||||
shared_mgm_set,
|
|
||||||
);
|
|
||||||
handler.add_mode_parent(PUS_MODE_SERVICE.into(), reply_tx_to_pus);
|
|
||||||
handler.add_mode_parent(MGM_ASSEMBLY.into(), reply_tx_to_parent);
|
|
||||||
Self {
|
|
||||||
mode_request_tx: request_tx,
|
|
||||||
mode_reply_rx_to_pus: reply_rx_to_pus,
|
|
||||||
mode_reply_rx_to_parent: reply_rx_to_parent,
|
|
||||||
composite_request_tx,
|
|
||||||
handler,
|
|
||||||
tm_rx,
|
|
||||||
hk_reply_rx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_basic_handler() {
|
|
||||||
let mut testbench = MgmTestbench::new();
|
|
||||||
assert_eq!(testbench.handler.com_interface.call_count, 0);
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.mode_and_submode().mode(),
|
|
||||||
DeviceMode::Off as u32
|
|
||||||
);
|
|
||||||
assert_eq!(testbench.handler.mode_and_submode().submode(), 0_u16);
|
|
||||||
testbench.handler.periodic_operation();
|
|
||||||
// Handler is OFF, no changes expected.
|
|
||||||
assert_eq!(testbench.handler.com_interface.call_count, 0);
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.mode_and_submode().mode(),
|
|
||||||
DeviceMode::Off as u32
|
|
||||||
);
|
|
||||||
assert_eq!(testbench.handler.mode_and_submode().submode(), 0_u16);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_normal_handler() {
|
|
||||||
let mut testbench = MgmTestbench::new();
|
|
||||||
testbench
|
|
||||||
.mode_request_tx
|
|
||||||
.send(GenericMessage::new(
|
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
|
||||||
ModeRequest::SetMode {
|
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
|
||||||
forced: false,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.expect("failed to send mode request");
|
|
||||||
testbench.handler.periodic_operation();
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.mode_and_submode().mode(),
|
|
||||||
DeviceMode::Normal as u32
|
|
||||||
);
|
|
||||||
assert_eq!(testbench.handler.mode_and_submode().submode(), 0);
|
|
||||||
|
|
||||||
// Verify power switch handling.
|
|
||||||
let mut switch_requests = testbench.handler.switch_helper.switch_requests.borrow_mut();
|
|
||||||
assert_eq!(switch_requests.len(), 1);
|
|
||||||
let switch_req = switch_requests.pop_front().expect("no switch request");
|
|
||||||
assert_eq!(switch_req.target_state, SwitchStateBinary::On);
|
|
||||||
assert_eq!(switch_req.switch_id, PcduSwitch::Mgm);
|
|
||||||
let mut switch_info_requests = testbench
|
|
||||||
.handler
|
|
||||||
.switch_helper
|
|
||||||
.switch_info_requests
|
|
||||||
.borrow_mut();
|
|
||||||
assert_eq!(switch_info_requests.len(), 1);
|
|
||||||
let switch_info_req = switch_info_requests.pop_front().expect("no switch request");
|
|
||||||
assert_eq!(switch_info_req, PcduSwitch::Mgm);
|
|
||||||
|
|
||||||
let mode_reply = testbench
|
|
||||||
.mode_reply_rx_to_pus
|
|
||||||
.try_recv()
|
|
||||||
.expect("no mode reply generated");
|
|
||||||
match mode_reply.message {
|
|
||||||
ModeReply::ModeReply(mode) => {
|
|
||||||
assert_eq!(mode.mode(), DeviceMode::Normal as u32);
|
|
||||||
assert_eq!(mode.submode(), 0);
|
|
||||||
}
|
|
||||||
_ => panic!("unexpected mode reply"),
|
|
||||||
}
|
|
||||||
// The device should have been polled once.
|
|
||||||
assert_eq!(testbench.handler.com_interface.call_count, 1);
|
|
||||||
let mgm_set = *testbench.handler.shared_mgm_set.lock().unwrap();
|
|
||||||
assert!(mgm_set.x < 0.001);
|
|
||||||
assert!(mgm_set.y < 0.001);
|
|
||||||
assert!(mgm_set.z < 0.001);
|
|
||||||
assert!(mgm_set.valid);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_normal_handler_mgm_set_conversion() {
|
|
||||||
let mut testbench = MgmTestbench::new();
|
|
||||||
let raw_values = MgmLis3RawValues {
|
|
||||||
x: 1000,
|
|
||||||
y: -1000,
|
|
||||||
z: 1000,
|
|
||||||
};
|
|
||||||
testbench.handler.com_interface.next_mgm_data = raw_values;
|
|
||||||
testbench
|
|
||||||
.mode_request_tx
|
|
||||||
.send(GenericMessage::new(
|
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
|
||||||
ModeRequest::SetMode {
|
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
|
||||||
forced: false,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.expect("failed to send mode request");
|
|
||||||
testbench.handler.periodic_operation();
|
|
||||||
let mgm_set = *testbench.handler.shared_mgm_set.lock().unwrap();
|
|
||||||
let expected_x =
|
|
||||||
raw_values.x as f32 * GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS;
|
|
||||||
let expected_y =
|
|
||||||
raw_values.y as f32 * GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS;
|
|
||||||
let expected_z =
|
|
||||||
raw_values.z as f32 * GAUSS_TO_MICROTESLA_FACTOR as f32 * FIELD_LSB_PER_GAUSS_4_SENS;
|
|
||||||
let x_diff = (mgm_set.x - expected_x).abs();
|
|
||||||
let y_diff = (mgm_set.y - expected_y).abs();
|
|
||||||
let z_diff = (mgm_set.z - expected_z).abs();
|
|
||||||
assert!(x_diff < 0.001, "x diff too large: {}", x_diff);
|
|
||||||
assert!(y_diff < 0.001, "y diff too large: {}", y_diff);
|
|
||||||
assert!(z_diff < 0.001, "z diff too large: {}", z_diff);
|
|
||||||
assert!(mgm_set.valid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1 @@
|
|||||||
pub mod assembly;
|
|
||||||
pub mod ctrl;
|
|
||||||
pub mod mgm;
|
pub mod mgm;
|
||||||
pub mod subsystem;
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
// TODO: Write subsystem
|
|
@ -3,7 +3,7 @@
|
|||||||
use crossbeam_channel::{bounded, Receiver, Sender};
|
use crossbeam_channel::{bounded, Receiver, Sender};
|
||||||
use std::sync::atomic::{AtomicU16, Ordering};
|
use std::sync::atomic::{AtomicU16, Ordering};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16};
|
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||||
|
|
||||||
trait FieldDataProvider: Send {
|
trait FieldDataProvider: Send {
|
||||||
fn get_data(&self) -> &[u8];
|
fn get_data(&self) -> &[u8];
|
||||||
@ -35,7 +35,7 @@ struct ExampleMgmSet {
|
|||||||
temperature: u16,
|
temperature: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromBytes, IntoBytes, Immutable, Unaligned)]
|
#[derive(FromBytes, AsBytes, Unaligned)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct ExampleMgmSetZc {
|
struct ExampleMgmSetZc {
|
||||||
mgm_vec: [u8; 12],
|
mgm_vec: [u8; 12],
|
||||||
|
@ -122,7 +122,7 @@ pub mod mode_err {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod components {
|
pub mod components {
|
||||||
use satrs::{request::UniqueApidTargetId, ComponentId};
|
use satrs::request::UniqueApidTargetId;
|
||||||
use strum::EnumIter;
|
use strum::EnumIter;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, EnumIter)]
|
#[derive(Copy, Clone, PartialEq, Eq, EnumIter)]
|
||||||
@ -132,36 +132,8 @@ pub mod components {
|
|||||||
Acs = 3,
|
Acs = 3,
|
||||||
Cfdp = 4,
|
Cfdp = 4,
|
||||||
Tmtc = 5,
|
Tmtc = 5,
|
||||||
Eps = 6,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum EpsId {
|
|
||||||
Pcdu = 0,
|
|
||||||
Subsystem = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum TmtcId {
|
|
||||||
UdpServer = 0,
|
|
||||||
TcpServer = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const EPS_SUBSYSTEM: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Subsystem as u32);
|
|
||||||
pub const PCDU_HANDLER: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Eps as u16, EpsId::Pcdu as u32);
|
|
||||||
pub const UDP_SERVER: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::UdpServer as u32);
|
|
||||||
pub const TCP_SERVER: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::TcpServer as u32);
|
|
||||||
pub const NO_SENDER: ComponentId = ComponentId::MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod pus {
|
|
||||||
use super::components::Apid;
|
|
||||||
use satrs::request::UniqueApidTargetId;
|
|
||||||
|
|
||||||
// Component IDs for components with the PUS APID.
|
// Component IDs for components with the PUS APID.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum PusId {
|
pub enum PusId {
|
||||||
@ -172,6 +144,18 @@ pub mod pus {
|
|||||||
PusMode = 4,
|
PusMode = 4,
|
||||||
PusHk = 5,
|
PusHk = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum AcsId {
|
||||||
|
Mgm0 = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum TmtcId {
|
||||||
|
UdpServer = 0,
|
||||||
|
TcpServer = 1,
|
||||||
|
}
|
||||||
|
|
||||||
pub const PUS_ACTION_SERVICE: UniqueApidTargetId =
|
pub const PUS_ACTION_SERVICE: UniqueApidTargetId =
|
||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusAction as u32);
|
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusAction as u32);
|
||||||
pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId =
|
pub const PUS_EVENT_MANAGEMENT: UniqueApidTargetId =
|
||||||
@ -186,33 +170,19 @@ pub mod pus {
|
|||||||
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusHk as u32);
|
UniqueApidTargetId::new(Apid::GenericPus as u16, PusId::PusHk as u32);
|
||||||
pub const PUS_SCHED_SERVICE: UniqueApidTargetId =
|
pub const PUS_SCHED_SERVICE: UniqueApidTargetId =
|
||||||
UniqueApidTargetId::new(Apid::Sched as u16, 0);
|
UniqueApidTargetId::new(Apid::Sched as u16, 0);
|
||||||
}
|
|
||||||
|
|
||||||
pub mod acs {
|
|
||||||
use super::components::Apid;
|
|
||||||
use satrs::request::UniqueApidTargetId;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum AcsId {
|
|
||||||
Subsystem = 1,
|
|
||||||
Assembly = 2,
|
|
||||||
Mgm0 = 3,
|
|
||||||
Mgm1 = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MGM_ASSEMBLY: UniqueApidTargetId =
|
|
||||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Assembly as u32);
|
|
||||||
pub const MGM_HANDLER_0: UniqueApidTargetId =
|
pub const MGM_HANDLER_0: UniqueApidTargetId =
|
||||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
|
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
|
||||||
pub const MGM_HANDLER_1: UniqueApidTargetId =
|
pub const UDP_SERVER: UniqueApidTargetId =
|
||||||
UniqueApidTargetId::new(Apid::Acs as u16, AcsId::Mgm0 as u32);
|
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::UdpServer as u32);
|
||||||
|
pub const TCP_SERVER: UniqueApidTargetId =
|
||||||
|
UniqueApidTargetId::new(Apid::Tmtc as u16, TmtcId::TcpServer as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod pool {
|
pub mod pool {
|
||||||
use super::*;
|
use super::*;
|
||||||
pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) {
|
pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) {
|
||||||
(
|
(
|
||||||
StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
|
StaticMemoryPool::new(StaticPoolConfig::new(
|
||||||
vec![
|
vec![
|
||||||
(30, 32),
|
(30, 32),
|
||||||
(15, 64),
|
(15, 64),
|
||||||
@ -223,7 +193,7 @@ pub mod pool {
|
|||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
)),
|
)),
|
||||||
StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
|
StaticMemoryPool::new(StaticPoolConfig::new(
|
||||||
vec![
|
vec![
|
||||||
(30, 32),
|
(30, 32),
|
||||||
(15, 64),
|
(15, 64),
|
||||||
@ -238,7 +208,7 @@ pub mod pool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_sched_tc_pool() -> StaticMemoryPool {
|
pub fn create_sched_tc_pool() -> StaticMemoryPool {
|
||||||
StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
|
StaticMemoryPool::new(StaticPoolConfig::new(
|
||||||
vec![
|
vec![
|
||||||
(30, 32),
|
(30, 32),
|
||||||
(15, 64),
|
(15, 64),
|
||||||
@ -254,7 +224,7 @@ pub mod pool {
|
|||||||
|
|
||||||
pub mod tasks {
|
pub mod tasks {
|
||||||
pub const FREQ_MS_UDP_TMTC: u64 = 200;
|
pub const FREQ_MS_UDP_TMTC: u64 = 200;
|
||||||
|
pub const FREQ_MS_EVENT_HANDLING: u64 = 400;
|
||||||
pub const FREQ_MS_AOCS: u64 = 500;
|
pub const FREQ_MS_AOCS: u64 = 500;
|
||||||
pub const FREQ_MS_PUS_STACK: u64 = 200;
|
pub const FREQ_MS_PUS_STACK: u64 = 200;
|
||||||
pub const SIM_CLIENT_IDLE_DELAY_MS: u64 = 5;
|
|
||||||
}
|
}
|
||||||
|
@ -1,197 +0,0 @@
|
|||||||
use derive_new::new;
|
|
||||||
use std::{cell::RefCell, collections::VecDeque, sync::mpsc, time::Duration};
|
|
||||||
|
|
||||||
use satrs::{
|
|
||||||
power::{
|
|
||||||
PowerSwitchInfo, PowerSwitcherCommandSender, SwitchRequest, SwitchState, SwitchStateBinary,
|
|
||||||
},
|
|
||||||
queue::GenericSendError,
|
|
||||||
request::{GenericMessage, MessageMetadata},
|
|
||||||
};
|
|
||||||
use satrs_minisim::eps::{PcduSwitch, SwitchMapWrapper};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use self::pcdu::SharedSwitchSet;
|
|
||||||
|
|
||||||
pub mod pcdu;
|
|
||||||
|
|
||||||
#[derive(new, Clone)]
|
|
||||||
pub struct PowerSwitchHelper {
|
|
||||||
switcher_tx: mpsc::SyncSender<GenericMessage<SwitchRequest>>,
|
|
||||||
shared_switch_set: SharedSwitchSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum SwitchCommandingError {
|
|
||||||
#[error("send error: {0}")]
|
|
||||||
Send(#[from] GenericSendError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum SwitchInfoError {
|
|
||||||
/// This is a configuration error which should not occur.
|
|
||||||
#[error("switch ID not in map")]
|
|
||||||
SwitchIdNotInMap(PcduSwitch),
|
|
||||||
#[error("switch set invalid")]
|
|
||||||
SwitchSetInvalid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PowerSwitchInfo<PcduSwitch> for PowerSwitchHelper {
|
|
||||||
type Error = SwitchInfoError;
|
|
||||||
|
|
||||||
fn switch_state(
|
|
||||||
&self,
|
|
||||||
switch_id: PcduSwitch,
|
|
||||||
) -> Result<satrs::power::SwitchState, Self::Error> {
|
|
||||||
let switch_set = self
|
|
||||||
.shared_switch_set
|
|
||||||
.lock()
|
|
||||||
.expect("failed to lock switch set");
|
|
||||||
if !switch_set.valid {
|
|
||||||
return Err(SwitchInfoError::SwitchSetInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(state) = switch_set.switch_map.get(&switch_id) {
|
|
||||||
return Ok(*state);
|
|
||||||
}
|
|
||||||
Err(SwitchInfoError::SwitchIdNotInMap(switch_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn switch_delay_ms(&self) -> Duration {
|
|
||||||
// Here, we could set device specific switch delays theoretically. Set it to this value
|
|
||||||
// for now.
|
|
||||||
Duration::from_millis(1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PowerSwitcherCommandSender<PcduSwitch> for PowerSwitchHelper {
|
|
||||||
type Error = SwitchCommandingError;
|
|
||||||
|
|
||||||
fn send_switch_on_cmd(
|
|
||||||
&self,
|
|
||||||
requestor_info: satrs::request::MessageMetadata,
|
|
||||||
switch_id: PcduSwitch,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.switcher_tx
|
|
||||||
.send_switch_on_cmd(requestor_info, switch_id)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_switch_off_cmd(
|
|
||||||
&self,
|
|
||||||
requestor_info: satrs::request::MessageMetadata,
|
|
||||||
switch_id: PcduSwitch,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.switcher_tx
|
|
||||||
.send_switch_off_cmd(requestor_info, switch_id)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(new)]
|
|
||||||
pub struct SwitchRequestInfo {
|
|
||||||
pub requestor_info: MessageMetadata,
|
|
||||||
pub switch_id: PcduSwitch,
|
|
||||||
pub target_state: satrs::power::SwitchStateBinary,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test switch helper which can be used for unittests.
|
|
||||||
pub struct TestSwitchHelper {
|
|
||||||
pub switch_requests: RefCell<VecDeque<SwitchRequestInfo>>,
|
|
||||||
pub switch_info_requests: RefCell<VecDeque<PcduSwitch>>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub switch_delay_request_count: u32,
|
|
||||||
pub next_switch_delay: Duration,
|
|
||||||
pub switch_map: RefCell<SwitchMapWrapper>,
|
|
||||||
pub switch_map_valid: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TestSwitchHelper {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
switch_requests: Default::default(),
|
|
||||||
switch_info_requests: Default::default(),
|
|
||||||
switch_delay_request_count: Default::default(),
|
|
||||||
next_switch_delay: Duration::from_millis(1000),
|
|
||||||
switch_map: Default::default(),
|
|
||||||
switch_map_valid: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PowerSwitchInfo<PcduSwitch> for TestSwitchHelper {
|
|
||||||
type Error = SwitchInfoError;
|
|
||||||
|
|
||||||
fn switch_state(
|
|
||||||
&self,
|
|
||||||
switch_id: PcduSwitch,
|
|
||||||
) -> Result<satrs::power::SwitchState, Self::Error> {
|
|
||||||
let mut switch_info_requests_mut = self.switch_info_requests.borrow_mut();
|
|
||||||
switch_info_requests_mut.push_back(switch_id);
|
|
||||||
if !self.switch_map_valid {
|
|
||||||
return Err(SwitchInfoError::SwitchSetInvalid);
|
|
||||||
}
|
|
||||||
let switch_map_mut = self.switch_map.borrow_mut();
|
|
||||||
if let Some(state) = switch_map_mut.0.get(&switch_id) {
|
|
||||||
return Ok(*state);
|
|
||||||
}
|
|
||||||
Err(SwitchInfoError::SwitchIdNotInMap(switch_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn switch_delay_ms(&self) -> Duration {
|
|
||||||
self.next_switch_delay
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PowerSwitcherCommandSender<PcduSwitch> for TestSwitchHelper {
|
|
||||||
type Error = SwitchCommandingError;
|
|
||||||
|
|
||||||
fn send_switch_on_cmd(
|
|
||||||
&self,
|
|
||||||
requestor_info: MessageMetadata,
|
|
||||||
switch_id: PcduSwitch,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let mut switch_requests_mut = self.switch_requests.borrow_mut();
|
|
||||||
switch_requests_mut.push_back(SwitchRequestInfo {
|
|
||||||
requestor_info,
|
|
||||||
switch_id,
|
|
||||||
target_state: SwitchStateBinary::On,
|
|
||||||
});
|
|
||||||
// By default, the test helper immediately acknowledges the switch request by setting
|
|
||||||
// the appropriate switch state in the internal switch map.
|
|
||||||
let mut switch_map_mut = self.switch_map.borrow_mut();
|
|
||||||
if let Some(switch_state) = switch_map_mut.0.get_mut(&switch_id) {
|
|
||||||
*switch_state = SwitchState::On;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_switch_off_cmd(
|
|
||||||
&self,
|
|
||||||
requestor_info: MessageMetadata,
|
|
||||||
switch_id: PcduSwitch,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let mut switch_requests_mut = self.switch_requests.borrow_mut();
|
|
||||||
switch_requests_mut.push_back(SwitchRequestInfo {
|
|
||||||
requestor_info,
|
|
||||||
switch_id,
|
|
||||||
target_state: SwitchStateBinary::Off,
|
|
||||||
});
|
|
||||||
// By default, the test helper immediately acknowledges the switch request by setting
|
|
||||||
// the appropriate switch state in the internal switch map.
|
|
||||||
let mut switch_map_mut = self.switch_map.borrow_mut();
|
|
||||||
if let Some(switch_state) = switch_map_mut.0.get_mut(&switch_id) {
|
|
||||||
*switch_state = SwitchState::Off;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl TestSwitchHelper {
|
|
||||||
// Helper function which can be used to force a switch to another state for test purposes.
|
|
||||||
pub fn set_switch_state(&mut self, switch: PcduSwitch, state: SwitchState) {
|
|
||||||
self.switch_map.get_mut().0.insert(switch, state);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,753 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cell::RefCell,
|
|
||||||
collections::VecDeque,
|
|
||||||
sync::{mpsc, Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use derive_new::new;
|
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|
||||||
use satrs::{
|
|
||||||
hk::{HkRequest, HkRequestVariant},
|
|
||||||
mode::{
|
|
||||||
ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler,
|
|
||||||
ModeRequestHandlerMpscBounded,
|
|
||||||
},
|
|
||||||
mode_tree::{ModeChild, ModeNode},
|
|
||||||
power::SwitchRequest,
|
|
||||||
pus::{EcssTmSender, PusTmVariant},
|
|
||||||
queue::GenericSendError,
|
|
||||||
request::{GenericMessage, MessageMetadata, UniqueApidTargetId},
|
|
||||||
spacepackets::ByteConversionError,
|
|
||||||
};
|
|
||||||
use satrs_example::{
|
|
||||||
config::{
|
|
||||||
components::{NO_SENDER, PCDU_HANDLER},
|
|
||||||
pus::PUS_MODE_SERVICE,
|
|
||||||
},
|
|
||||||
DeviceMode, TimestampHelper,
|
|
||||||
};
|
|
||||||
use satrs_minisim::{
|
|
||||||
eps::{
|
|
||||||
PcduReply, PcduRequest, PcduSwitch, SwitchMap, SwitchMapBinaryWrapper, SwitchMapWrapper,
|
|
||||||
},
|
|
||||||
SerializableSimMsgPayload, SimReply, SimRequest,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
hk::PusHkHelper,
|
|
||||||
pus::hk::{HkReply, HkReplyVariant},
|
|
||||||
requests::CompositeRequest,
|
|
||||||
tmtc::sender::TmTcSender,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait SerialInterface {
|
|
||||||
type Error: core::fmt::Debug;
|
|
||||||
|
|
||||||
/// Send some data via the serial interface.
|
|
||||||
fn send(&self, data: &[u8]) -> Result<(), Self::Error>;
|
|
||||||
/// Receive all replies received on the serial interface so far. This function takes a closure
|
|
||||||
/// and call its for each received packet, passing the received packet into it.
|
|
||||||
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
|
|
||||||
&self,
|
|
||||||
f: ReplyHandler,
|
|
||||||
) -> Result<(), Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(new)]
|
|
||||||
pub struct SerialInterfaceToSim {
|
|
||||||
pub sim_request_tx: mpsc::Sender<SimRequest>,
|
|
||||||
pub sim_reply_rx: mpsc::Receiver<SimReply>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum SetId {
|
|
||||||
SwitcherSet = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerialInterface for SerialInterfaceToSim {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
let request: PcduRequest = serde_json::from_slice(data).expect("expected a PCDU request");
|
|
||||||
self.sim_request_tx
|
|
||||||
.send(SimRequest::new_with_epoch_time(request))
|
|
||||||
.expect("failed to send request to simulation");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
|
|
||||||
&self,
|
|
||||||
mut f: ReplyHandler,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
loop {
|
|
||||||
match self.sim_reply_rx.try_recv() {
|
|
||||||
Ok(reply) => {
|
|
||||||
let reply = serde_json::to_string(&reply).unwrap();
|
|
||||||
f(reply.as_bytes());
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
mpsc::TryRecvError::Empty => break,
|
|
||||||
mpsc::TryRecvError::Disconnected => {
|
|
||||||
log::warn!("sim reply sender has disconnected");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SerialInterfaceDummy {
|
|
||||||
// Need interior mutability here for both fields.
|
|
||||||
pub switch_map: RefCell<SwitchMapBinaryWrapper>,
|
|
||||||
pub reply_deque: RefCell<VecDeque<SimReply>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerialInterface for SerialInterfaceDummy {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
let pcdu_req: PcduRequest = serde_json::from_slice(data).unwrap();
|
|
||||||
let switch_map_mut = &mut self.switch_map.borrow_mut().0;
|
|
||||||
match pcdu_req {
|
|
||||||
PcduRequest::SwitchDevice { switch, state } => {
|
|
||||||
match switch_map_mut.entry(switch) {
|
|
||||||
std::collections::hash_map::Entry::Occupied(mut val) => {
|
|
||||||
*val.get_mut() = state;
|
|
||||||
}
|
|
||||||
std::collections::hash_map::Entry::Vacant(vacant) => {
|
|
||||||
vacant.insert(state);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
PcduRequest::RequestSwitchInfo => {
|
|
||||||
let mut reply_deque_mut = self.reply_deque.borrow_mut();
|
|
||||||
reply_deque_mut.push_back(SimReply::new(&PcduReply::SwitchInfo(
|
|
||||||
switch_map_mut.clone(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
|
|
||||||
&self,
|
|
||||||
mut f: ReplyHandler,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
if self.reply_queue_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
let reply = self.get_next_reply_as_string();
|
|
||||||
f(reply.as_bytes());
|
|
||||||
if self.reply_queue_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerialInterfaceDummy {
|
|
||||||
fn get_next_reply_as_string(&self) -> String {
|
|
||||||
let mut reply_deque_mut = self.reply_deque.borrow_mut();
|
|
||||||
let next_reply = reply_deque_mut.pop_front().unwrap();
|
|
||||||
serde_json::to_string(&next_reply).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reply_queue_empty(&self) -> bool {
|
|
||||||
self.reply_deque.borrow().is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum SerialSimInterfaceWrapper {
|
|
||||||
Dummy(SerialInterfaceDummy),
|
|
||||||
Sim(SerialInterfaceToSim),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerialInterface for SerialSimInterfaceWrapper {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
match self {
|
|
||||||
SerialSimInterfaceWrapper::Dummy(dummy) => dummy.send(data),
|
|
||||||
SerialSimInterfaceWrapper::Sim(sim) => sim.send(data),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
|
|
||||||
&self,
|
|
||||||
f: ReplyHandler,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
match self {
|
|
||||||
SerialSimInterfaceWrapper::Dummy(dummy) => dummy.try_recv_replies(f),
|
|
||||||
SerialSimInterfaceWrapper::Sim(sim) => sim.try_recv_replies(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum OpCode {
|
|
||||||
RegularOp = 0,
|
|
||||||
PollAndRecvReplies = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
|
||||||
pub struct SwitchSet {
|
|
||||||
pub valid: bool,
|
|
||||||
pub switch_map: SwitchMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type SharedSwitchSet = Arc<Mutex<SwitchSet>>;
|
|
||||||
|
|
||||||
/// Example PCDU device handler.
|
|
||||||
#[derive(new)]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub struct PcduHandler<ComInterface: SerialInterface> {
|
|
||||||
id: UniqueApidTargetId,
|
|
||||||
dev_str: &'static str,
|
|
||||||
mode_node: ModeRequestHandlerMpscBounded,
|
|
||||||
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
|
||||||
hk_reply_tx: mpsc::SyncSender<GenericMessage<HkReply>>,
|
|
||||||
switch_request_rx: mpsc::Receiver<GenericMessage<SwitchRequest>>,
|
|
||||||
tm_sender: TmTcSender,
|
|
||||||
pub com_interface: ComInterface,
|
|
||||||
shared_switch_map: Arc<Mutex<SwitchSet>>,
|
|
||||||
#[new(value = "PusHkHelper::new(id)")]
|
|
||||||
hk_helper: PusHkHelper,
|
|
||||||
#[new(value = "ModeAndSubmode::new(satrs_example::DeviceMode::Off as u32, 0)")]
|
|
||||||
mode_and_submode: ModeAndSubmode,
|
|
||||||
#[new(default)]
|
|
||||||
stamp_helper: TimestampHelper,
|
|
||||||
#[new(value = "[0; 256]")]
|
|
||||||
tm_buf: [u8; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
|
|
||||||
pub fn periodic_operation(&mut self, op_code: OpCode) {
|
|
||||||
match op_code {
|
|
||||||
OpCode::RegularOp => {
|
|
||||||
self.stamp_helper.update_from_now();
|
|
||||||
// Handle requests.
|
|
||||||
self.handle_composite_requests();
|
|
||||||
self.handle_mode_requests();
|
|
||||||
self.handle_switch_requests();
|
|
||||||
// Poll the switch states and/or telemetry regularly here.
|
|
||||||
if self.mode() == DeviceMode::Normal as u32 || self.mode() == DeviceMode::On as u32
|
|
||||||
{
|
|
||||||
self.handle_periodic_commands();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OpCode::PollAndRecvReplies => {
|
|
||||||
self.poll_and_handle_replies();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_composite_requests(&mut self) {
|
|
||||||
loop {
|
|
||||||
match self.composite_request_rx.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 => {
|
|
||||||
if hk_request.unique_id == SetId::SwitcherSet as u32 {
|
|
||||||
if let Ok(hk_tm) = self.hk_helper.generate_hk_report_packet(
|
|
||||||
self.stamp_helper.stamp(),
|
|
||||||
SetId::SwitcherSet as u32,
|
|
||||||
&mut |hk_buf| {
|
|
||||||
// Send TM down as JSON.
|
|
||||||
let switch_map_snapshot = self
|
|
||||||
.shared_switch_map
|
|
||||||
.lock()
|
|
||||||
.expect("failed to lock switch map")
|
|
||||||
.clone();
|
|
||||||
let switch_map_json = serde_json::to_string(&switch_map_snapshot)
|
|
||||||
.expect("failed to serialize switch map");
|
|
||||||
if switch_map_json.len() > hk_buf.len() {
|
|
||||||
log::error!("switch map JSON too large for HK buffer");
|
|
||||||
return Err(ByteConversionError::ToSliceTooSmall {
|
|
||||||
found: hk_buf.len(),
|
|
||||||
expected: switch_map_json.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(switch_map_json.len())
|
|
||||||
},
|
|
||||||
&mut self.tm_buf,
|
|
||||||
) {
|
|
||||||
self.tm_sender
|
|
||||||
.send_tm(self.id.id(), PusTmVariant::Direct(hk_tm))
|
|
||||||
.expect("failed to send HK TM");
|
|
||||||
self.hk_reply_tx
|
|
||||||
.send(GenericMessage::new(
|
|
||||||
*requestor_info,
|
|
||||||
HkReply::new(hk_request.unique_id, HkReplyVariant::Ack),
|
|
||||||
))
|
|
||||||
.expect("failed to send HK reply");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HkRequestVariant::EnablePeriodic => todo!(),
|
|
||||||
HkRequestVariant::DisablePeriodic => todo!(),
|
|
||||||
HkRequestVariant::ModifyCollectionInterval(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_periodic_commands(&self) {
|
|
||||||
let pcdu_req = PcduRequest::RequestSwitchInfo;
|
|
||||||
let pcdu_req_ser = serde_json::to_string(&pcdu_req).unwrap();
|
|
||||||
if let Err(_e) = self.com_interface.send(pcdu_req_ser.as_bytes()) {
|
|
||||||
log::warn!("polling PCDU switch info failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_mode_requests(&mut self) {
|
|
||||||
loop {
|
|
||||||
// TODO: Only allow one set mode request per cycle?
|
|
||||||
match self.mode_node.try_recv_mode_request() {
|
|
||||||
Ok(opt_msg) => {
|
|
||||||
if let Some(msg) = opt_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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
satrs::queue::GenericReceiveError::Empty => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
satrs::queue::GenericReceiveError::TxDisconnected(_) => {
|
|
||||||
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_switch_requests(&mut self) {
|
|
||||||
loop {
|
|
||||||
match self.switch_request_rx.try_recv() {
|
|
||||||
Ok(switch_req) => match PcduSwitch::try_from(switch_req.message.switch_id()) {
|
|
||||||
Ok(pcdu_switch) => {
|
|
||||||
let pcdu_req = PcduRequest::SwitchDevice {
|
|
||||||
switch: pcdu_switch,
|
|
||||||
state: switch_req.message.target_state(),
|
|
||||||
};
|
|
||||||
let pcdu_req_ser = serde_json::to_string(&pcdu_req).unwrap();
|
|
||||||
self.com_interface
|
|
||||||
.send(pcdu_req_ser.as_bytes())
|
|
||||||
.expect("failed to send switch request to PCDU");
|
|
||||||
}
|
|
||||||
Err(e) => todo!("failed to convert switch ID {:?} to typed PCDU switch", e),
|
|
||||||
},
|
|
||||||
Err(e) => match e {
|
|
||||||
mpsc::TryRecvError::Empty => break,
|
|
||||||
mpsc::TryRecvError::Disconnected => {
|
|
||||||
log::warn!("switch request receiver has disconnected");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_and_handle_replies(&mut self) {
|
|
||||||
if let Err(e) = self.com_interface.try_recv_replies(|reply| {
|
|
||||||
let sim_reply: SimReply = serde_json::from_slice(reply).expect("invalid reply format");
|
|
||||||
let pcdu_reply = PcduReply::from_sim_message(&sim_reply).expect("invalid reply format");
|
|
||||||
match pcdu_reply {
|
|
||||||
PcduReply::SwitchInfo(switch_info) => {
|
|
||||||
let switch_map_wrapper =
|
|
||||||
SwitchMapWrapper::from_binary_switch_map_ref(&switch_info);
|
|
||||||
let mut shared_switch_map = self
|
|
||||||
.shared_switch_map
|
|
||||||
.lock()
|
|
||||||
.expect("failed to lock switch map");
|
|
||||||
shared_switch_map.switch_map = switch_map_wrapper.0;
|
|
||||||
shared_switch_map.valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
log::warn!("receiving PCDU replies failed: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ComInterface: SerialInterface> ModeProvider for PcduHandler<ComInterface> {
|
|
||||||
fn mode_and_submode(&self) -> ModeAndSubmode {
|
|
||||||
self.mode_and_submode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ComInterface: SerialInterface> ModeRequestHandler for PcduHandler<ComInterface> {
|
|
||||||
type Error = ModeError;
|
|
||||||
fn start_transition(
|
|
||||||
&mut self,
|
|
||||||
requestor: MessageMetadata,
|
|
||||||
mode_and_submode: ModeAndSubmode,
|
|
||||||
_forced: bool,
|
|
||||||
) -> Result<(), satrs::mode::ModeError> {
|
|
||||||
log::info!(
|
|
||||||
"{}: transitioning to mode {:?}",
|
|
||||||
self.dev_str,
|
|
||||||
mode_and_submode
|
|
||||||
);
|
|
||||||
self.mode_and_submode = mode_and_submode;
|
|
||||||
if mode_and_submode.mode() == DeviceMode::Off as u32 {
|
|
||||||
self.shared_switch_map.lock().unwrap().valid = false;
|
|
||||||
}
|
|
||||||
self.handle_mode_reached(Some(requestor))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn announce_mode(&self, _requestor_info: Option<MessageMetadata>, _recursive: bool) {
|
|
||||||
log::info!(
|
|
||||||
"{} announcing mode: {:?}",
|
|
||||||
self.dev_str,
|
|
||||||
self.mode_and_submode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_mode_reached(
|
|
||||||
&mut self,
|
|
||||||
requestor: Option<MessageMetadata>,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.announce_mode(requestor, false);
|
|
||||||
if let Some(requestor) = requestor {
|
|
||||||
if requestor.sender_id() == NO_SENDER {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
|
||||||
log::warn!(
|
|
||||||
"can not send back mode reply to sender {}",
|
|
||||||
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_node
|
|
||||||
.send_mode_reply(requestor, reply)
|
|
||||||
.map_err(|_| GenericSendError::RxDisconnected)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_mode_info(
|
|
||||||
&mut self,
|
|
||||||
_requestor_info: MessageMetadata,
|
|
||||||
_info: ModeAndSubmode,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ComInterface: SerialInterface> ModeNode for PcduHandler<ComInterface> {
|
|
||||||
fn id(&self) -> satrs::ComponentId {
|
|
||||||
PCDU_HANDLER.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ComInterface: SerialInterface> ModeChild for PcduHandler<ComInterface> {
|
|
||||||
type Sender = mpsc::SyncSender<GenericMessage<ModeReply>>;
|
|
||||||
|
|
||||||
fn add_mode_parent(&mut self, id: satrs::ComponentId, reply_sender: Self::Sender) {
|
|
||||||
self.mode_node.add_message_target(id, reply_sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::sync::mpsc;
|
|
||||||
|
|
||||||
use satrs::{
|
|
||||||
mode::ModeRequest, power::SwitchStateBinary, request::GenericMessage, tmtc::PacketAsVec,
|
|
||||||
};
|
|
||||||
use satrs_example::config::{
|
|
||||||
acs::MGM_HANDLER_0,
|
|
||||||
components::{Apid, EPS_SUBSYSTEM, PCDU_HANDLER},
|
|
||||||
};
|
|
||||||
use satrs_minisim::eps::SwitchMapBinary;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SerialInterfaceTest {
|
|
||||||
pub inner: SerialInterfaceDummy,
|
|
||||||
pub send_queue: RefCell<VecDeque<Vec<u8>>>,
|
|
||||||
pub reply_queue: RefCell<VecDeque<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SerialInterface for SerialInterfaceTest {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn send(&self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
let mut send_queue_mut = self.send_queue.borrow_mut();
|
|
||||||
send_queue_mut.push_back(data.to_vec());
|
|
||||||
self.inner.send(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_recv_replies<ReplyHandler: FnMut(&[u8])>(
|
|
||||||
&self,
|
|
||||||
mut f: ReplyHandler,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
if self.inner.reply_queue_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
let reply = self.inner.get_next_reply_as_string();
|
|
||||||
self.reply_queue.borrow_mut().push_back(reply.clone());
|
|
||||||
f(reply.as_bytes());
|
|
||||||
if self.inner.reply_queue_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PcduTestbench {
|
|
||||||
pub mode_request_tx: mpsc::SyncSender<GenericMessage<ModeRequest>>,
|
|
||||||
pub mode_reply_rx_to_pus: mpsc::Receiver<GenericMessage<ModeReply>>,
|
|
||||||
pub mode_reply_rx_to_parent: mpsc::Receiver<GenericMessage<ModeReply>>,
|
|
||||||
pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
|
|
||||||
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
|
|
||||||
pub tm_rx: mpsc::Receiver<PacketAsVec>,
|
|
||||||
pub switch_request_tx: mpsc::Sender<GenericMessage<SwitchRequest>>,
|
|
||||||
pub handler: PcduHandler<SerialInterfaceTest>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PcduTestbench {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let (mode_request_tx, mode_request_rx) = mpsc::sync_channel(5);
|
|
||||||
let (mode_reply_tx_to_pus, mode_reply_rx_to_pus) = mpsc::sync_channel(5);
|
|
||||||
let (mode_reply_tx_to_parent, mode_reply_rx_to_parent) = mpsc::sync_channel(5);
|
|
||||||
let mode_node =
|
|
||||||
ModeRequestHandlerMpscBounded::new(PCDU_HANDLER.into(), mode_request_rx);
|
|
||||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
|
||||||
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
|
||||||
let (tm_tx, tm_rx) = mpsc::sync_channel::<PacketAsVec>(5);
|
|
||||||
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
|
|
||||||
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
|
|
||||||
let mut handler = PcduHandler::new(
|
|
||||||
UniqueApidTargetId::new(Apid::Eps as u16, 0),
|
|
||||||
"TEST_PCDU",
|
|
||||||
mode_node,
|
|
||||||
composite_request_rx,
|
|
||||||
hk_reply_tx,
|
|
||||||
switch_reqest_rx,
|
|
||||||
TmTcSender::Heap(tm_tx.clone()),
|
|
||||||
SerialInterfaceTest::default(),
|
|
||||||
shared_switch_map,
|
|
||||||
);
|
|
||||||
handler.add_mode_parent(EPS_SUBSYSTEM.into(), mode_reply_tx_to_parent);
|
|
||||||
handler.add_mode_parent(PUS_MODE_SERVICE.into(), mode_reply_tx_to_pus);
|
|
||||||
Self {
|
|
||||||
mode_request_tx,
|
|
||||||
mode_reply_rx_to_pus,
|
|
||||||
mode_reply_rx_to_parent,
|
|
||||||
composite_request_tx,
|
|
||||||
hk_reply_rx,
|
|
||||||
tm_rx,
|
|
||||||
switch_request_tx,
|
|
||||||
handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify_switch_info_req_was_sent(&self, expected_queue_len: usize) {
|
|
||||||
// Check that there is now communication happening.
|
|
||||||
let mut send_queue_mut = self.handler.com_interface.send_queue.borrow_mut();
|
|
||||||
assert_eq!(send_queue_mut.len(), expected_queue_len);
|
|
||||||
let packet_sent = send_queue_mut.pop_front().unwrap();
|
|
||||||
drop(send_queue_mut);
|
|
||||||
let pcdu_req: PcduRequest = serde_json::from_slice(&packet_sent).unwrap();
|
|
||||||
assert_eq!(pcdu_req, PcduRequest::RequestSwitchInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify_switch_req_was_sent(
|
|
||||||
&self,
|
|
||||||
expected_queue_len: usize,
|
|
||||||
switch_id: PcduSwitch,
|
|
||||||
target_state: SwitchStateBinary,
|
|
||||||
) {
|
|
||||||
// Check that there is now communication happening.
|
|
||||||
let mut send_queue_mut = self.handler.com_interface.send_queue.borrow_mut();
|
|
||||||
assert_eq!(send_queue_mut.len(), expected_queue_len);
|
|
||||||
let packet_sent = send_queue_mut.pop_front().unwrap();
|
|
||||||
drop(send_queue_mut);
|
|
||||||
let pcdu_req: PcduRequest = serde_json::from_slice(&packet_sent).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
pcdu_req,
|
|
||||||
PcduRequest::SwitchDevice {
|
|
||||||
switch: switch_id,
|
|
||||||
state: target_state
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify_switch_reply_received(
|
|
||||||
&self,
|
|
||||||
expected_queue_len: usize,
|
|
||||||
expected_map: SwitchMapBinary,
|
|
||||||
) {
|
|
||||||
// Check that a switch reply was read back.
|
|
||||||
let mut reply_received_mut = self.handler.com_interface.reply_queue.borrow_mut();
|
|
||||||
assert_eq!(reply_received_mut.len(), expected_queue_len);
|
|
||||||
let reply_received = reply_received_mut.pop_front().unwrap();
|
|
||||||
let sim_reply: SimReply = serde_json::from_str(&reply_received).unwrap();
|
|
||||||
let pcdu_reply = PcduReply::from_sim_message(&sim_reply).unwrap();
|
|
||||||
assert_eq!(pcdu_reply, PcduReply::SwitchInfo(expected_map));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_basic_handler() {
|
|
||||||
let mut testbench = PcduTestbench::new();
|
|
||||||
assert_eq!(testbench.handler.com_interface.send_queue.borrow().len(), 0);
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.com_interface.reply_queue.borrow().len(),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.mode_and_submode().mode(),
|
|
||||||
DeviceMode::Off as u32
|
|
||||||
);
|
|
||||||
assert_eq!(testbench.handler.mode_and_submode().submode(), 0_u16);
|
|
||||||
testbench.handler.periodic_operation(OpCode::RegularOp);
|
|
||||||
testbench
|
|
||||||
.handler
|
|
||||||
.periodic_operation(OpCode::PollAndRecvReplies);
|
|
||||||
// Handler is OFF, no changes expected.
|
|
||||||
assert_eq!(testbench.handler.com_interface.send_queue.borrow().len(), 0);
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.com_interface.reply_queue.borrow().len(),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.mode_and_submode().mode(),
|
|
||||||
DeviceMode::Off as u32
|
|
||||||
);
|
|
||||||
assert_eq!(testbench.handler.mode_and_submode().submode(), 0_u16);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_normal_mode() {
|
|
||||||
let mut testbench = PcduTestbench::new();
|
|
||||||
testbench
|
|
||||||
.mode_request_tx
|
|
||||||
.send(GenericMessage::new(
|
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
|
||||||
ModeRequest::SetMode {
|
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
|
||||||
forced: false,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.expect("failed to send mode request");
|
|
||||||
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
|
|
||||||
assert!(!switch_map_shared.valid);
|
|
||||||
drop(switch_map_shared);
|
|
||||||
testbench.handler.periodic_operation(OpCode::RegularOp);
|
|
||||||
testbench
|
|
||||||
.handler
|
|
||||||
.periodic_operation(OpCode::PollAndRecvReplies);
|
|
||||||
// Check correctness of mode.
|
|
||||||
assert_eq!(
|
|
||||||
testbench.handler.mode_and_submode().mode(),
|
|
||||||
DeviceMode::Normal as u32
|
|
||||||
);
|
|
||||||
assert_eq!(testbench.handler.mode_and_submode().submode(), 0);
|
|
||||||
|
|
||||||
testbench.verify_switch_info_req_was_sent(1);
|
|
||||||
testbench.verify_switch_reply_received(1, SwitchMapBinaryWrapper::default().0);
|
|
||||||
|
|
||||||
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
|
|
||||||
assert!(switch_map_shared.valid);
|
|
||||||
drop(switch_map_shared);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_switch_request_handling() {
|
|
||||||
let mut testbench = PcduTestbench::new();
|
|
||||||
testbench
|
|
||||||
.mode_request_tx
|
|
||||||
.send(GenericMessage::new(
|
|
||||||
MessageMetadata::new(0, PUS_MODE_SERVICE.id()),
|
|
||||||
ModeRequest::SetMode {
|
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as u32, 0),
|
|
||||||
forced: false,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.expect("failed to send mode request");
|
|
||||||
testbench
|
|
||||||
.switch_request_tx
|
|
||||||
.send(GenericMessage::new(
|
|
||||||
MessageMetadata::new(0, MGM_HANDLER_0.id()),
|
|
||||||
SwitchRequest::new(0, SwitchStateBinary::On),
|
|
||||||
))
|
|
||||||
.expect("failed to send switch request");
|
|
||||||
testbench.handler.periodic_operation(OpCode::RegularOp);
|
|
||||||
testbench
|
|
||||||
.handler
|
|
||||||
.periodic_operation(OpCode::PollAndRecvReplies);
|
|
||||||
|
|
||||||
testbench.verify_switch_req_was_sent(2, PcduSwitch::Mgm, SwitchStateBinary::On);
|
|
||||||
testbench.verify_switch_info_req_was_sent(1);
|
|
||||||
let mut switch_map = SwitchMapBinaryWrapper::default().0;
|
|
||||||
*switch_map
|
|
||||||
.get_mut(&PcduSwitch::Mgm)
|
|
||||||
.expect("switch state setting failed") = SwitchStateBinary::On;
|
|
||||||
testbench.verify_switch_reply_received(1, switch_map);
|
|
||||||
|
|
||||||
let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap();
|
|
||||||
assert!(switch_map_shared.valid);
|
|
||||||
drop(switch_map_shared);
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ use satrs::{
|
|||||||
},
|
},
|
||||||
spacepackets::time::cds::CdsTime,
|
spacepackets::time::cds::CdsTime,
|
||||||
};
|
};
|
||||||
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT;
|
use satrs_example::config::components::PUS_EVENT_MANAGEMENT;
|
||||||
|
|
||||||
use crate::update_time;
|
use crate::update_time;
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::hk::UniqueId;
|
use satrs::hk::UniqueId;
|
||||||
use satrs::request::UniqueApidTargetId;
|
use satrs::request::UniqueApidTargetId;
|
||||||
use satrs::spacepackets::ecss::hk;
|
use satrs::spacepackets::ByteConversionError;
|
||||||
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
|
||||||
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
|
||||||
|
|
||||||
#[derive(Debug, new, Copy, Clone)]
|
#[derive(Debug, new, Copy, Clone)]
|
||||||
pub struct HkUniqueId {
|
pub struct HkUniqueId {
|
||||||
@ -35,35 +33,3 @@ impl HkUniqueId {
|
|||||||
Ok(8)
|
Ok(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(new)]
|
|
||||||
pub struct PusHkHelper {
|
|
||||||
component_id: UniqueApidTargetId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PusHkHelper {
|
|
||||||
pub fn generate_hk_report_packet<
|
|
||||||
'a,
|
|
||||||
'b,
|
|
||||||
HkWriter: FnMut(&mut [u8]) -> Result<usize, ByteConversionError>,
|
|
||||||
>(
|
|
||||||
&self,
|
|
||||||
timestamp: &'a [u8],
|
|
||||||
set_id: u32,
|
|
||||||
hk_data_writer: &mut HkWriter,
|
|
||||||
buf: &'b mut [u8],
|
|
||||||
) -> Result<PusTmCreator<'a, 'b>, ByteConversionError> {
|
|
||||||
let sec_header =
|
|
||||||
PusTmSecondaryHeader::new(3, hk::Subservice::TmHkPacket as u8, 0, 0, timestamp);
|
|
||||||
buf[0..4].copy_from_slice(&self.component_id.unique_id.to_be_bytes());
|
|
||||||
buf[4..8].copy_from_slice(&set_id.to_be_bytes());
|
|
||||||
let (_, second_half) = buf.split_at_mut(8);
|
|
||||||
let hk_data_len = hk_data_writer(second_half)?;
|
|
||||||
Ok(PusTmCreator::new(
|
|
||||||
SpHeader::new_from_apid(self.component_id.apid),
|
|
||||||
sec_header,
|
|
||||||
&buf[0..8 + hk_data_len],
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//! This module contains all component related to the direct interface of the example.
|
//! This module contains all component related to the direct interface of the example.
|
||||||
pub mod sim_client_udp;
|
|
||||||
pub mod tcp;
|
pub mod tcp;
|
||||||
pub mod udp;
|
pub mod udp;
|
||||||
|
@ -1,420 +0,0 @@
|
|||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket},
|
|
||||||
sync::mpsc,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use satrs::pus::HandlingStatus;
|
|
||||||
use satrs_minisim::{
|
|
||||||
udp::SIM_CTRL_PORT, SerializableSimMsgPayload, SimComponent, SimMessageProvider, SimReply,
|
|
||||||
SimRequest,
|
|
||||||
};
|
|
||||||
use satrs_minisim::{SimCtrlReply, SimCtrlRequest};
|
|
||||||
|
|
||||||
struct SimReplyMap(pub HashMap<SimComponent, mpsc::Sender<SimReply>>);
|
|
||||||
|
|
||||||
pub fn create_sim_client(sim_request_rx: mpsc::Receiver<SimRequest>) -> Option<SimClientUdp> {
|
|
||||||
match SimClientUdp::new(
|
|
||||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, SIM_CTRL_PORT)),
|
|
||||||
sim_request_rx,
|
|
||||||
) {
|
|
||||||
Ok(sim_client) => {
|
|
||||||
log::info!("simulator client connection success");
|
|
||||||
return Some(sim_client);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("sim client creation error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub enum SimClientCreationError {
|
|
||||||
#[error("io error: {0}")]
|
|
||||||
Io(#[from] std::io::Error),
|
|
||||||
#[error("timeout when trying to connect to sim UDP server")]
|
|
||||||
Timeout,
|
|
||||||
#[error("invalid ping reply when trying connection to UDP sim server")]
|
|
||||||
InvalidReplyJsonError(#[from] serde_json::Error),
|
|
||||||
#[error("invalid sim reply, not pong reply as expected: {0:?}")]
|
|
||||||
ReplyIsNotPong(SimReply),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SimClientUdp {
|
|
||||||
udp_client: UdpSocket,
|
|
||||||
simulator_addr: SocketAddr,
|
|
||||||
sim_request_rx: mpsc::Receiver<SimRequest>,
|
|
||||||
reply_map: SimReplyMap,
|
|
||||||
reply_buf: [u8; 4096],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SimClientUdp {
|
|
||||||
pub fn new(
|
|
||||||
simulator_addr: SocketAddr,
|
|
||||||
sim_request_rx: mpsc::Receiver<SimRequest>,
|
|
||||||
) -> Result<Self, SimClientCreationError> {
|
|
||||||
let mut reply_buf: [u8; 4096] = [0; 4096];
|
|
||||||
let mut udp_client = UdpSocket::bind("127.0.0.1:0")?;
|
|
||||||
udp_client.set_read_timeout(Some(Duration::from_millis(100)))?;
|
|
||||||
Self::attempt_connection(&mut udp_client, simulator_addr, &mut reply_buf)?;
|
|
||||||
udp_client.set_nonblocking(true)?;
|
|
||||||
Ok(Self {
|
|
||||||
udp_client,
|
|
||||||
simulator_addr,
|
|
||||||
sim_request_rx,
|
|
||||||
reply_map: SimReplyMap(HashMap::new()),
|
|
||||||
reply_buf,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attempt_connection(
|
|
||||||
udp_client: &mut UdpSocket,
|
|
||||||
simulator_addr: SocketAddr,
|
|
||||||
reply_buf: &mut [u8],
|
|
||||||
) -> Result<(), SimClientCreationError> {
|
|
||||||
let sim_req = SimRequest::new_with_epoch_time(SimCtrlRequest::Ping);
|
|
||||||
let sim_req_json = serde_json::to_string(&sim_req).expect("failed to serialize SimRequest");
|
|
||||||
udp_client.send_to(sim_req_json.as_bytes(), simulator_addr)?;
|
|
||||||
match udp_client.recv(reply_buf) {
|
|
||||||
Ok(reply_len) => {
|
|
||||||
let sim_reply: SimReply = serde_json::from_slice(&reply_buf[0..reply_len])?;
|
|
||||||
if sim_reply.component() != SimComponent::SimCtrl {
|
|
||||||
return Err(SimClientCreationError::ReplyIsNotPong(sim_reply));
|
|
||||||
}
|
|
||||||
let sim_ctrl_reply =
|
|
||||||
SimCtrlReply::from_sim_message(&sim_reply).expect("invalid SIM reply");
|
|
||||||
match sim_ctrl_reply {
|
|
||||||
SimCtrlReply::InvalidRequest(_) => {
|
|
||||||
panic!("received invalid request reply from UDP sim server")
|
|
||||||
}
|
|
||||||
SimCtrlReply::Pong => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() == std::io::ErrorKind::TimedOut
|
|
||||||
|| e.kind() == std::io::ErrorKind::WouldBlock
|
|
||||||
{
|
|
||||||
Err(SimClientCreationError::Timeout)
|
|
||||||
} else {
|
|
||||||
Err(SimClientCreationError::Io(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn operation(&mut self) -> HandlingStatus {
|
|
||||||
let mut no_sim_requests_handled = true;
|
|
||||||
let mut no_data_from_udp_server_received = true;
|
|
||||||
loop {
|
|
||||||
match self.sim_request_rx.try_recv() {
|
|
||||||
Ok(request) => {
|
|
||||||
let request_json =
|
|
||||||
serde_json::to_string(&request).expect("failed to serialize SimRequest");
|
|
||||||
if let Err(e) = self
|
|
||||||
.udp_client
|
|
||||||
.send_to(request_json.as_bytes(), self.simulator_addr)
|
|
||||||
{
|
|
||||||
log::error!("error sending data to UDP SIM server: {}", e);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
no_sim_requests_handled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
mpsc::TryRecvError::Empty => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mpsc::TryRecvError::Disconnected => {
|
|
||||||
log::warn!("SIM request sender disconnected");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
match self.udp_client.recv(&mut self.reply_buf) {
|
|
||||||
Ok(recvd_bytes) => {
|
|
||||||
no_data_from_udp_server_received = false;
|
|
||||||
let sim_reply_result: serde_json::Result<SimReply> =
|
|
||||||
serde_json::from_slice(&self.reply_buf[0..recvd_bytes]);
|
|
||||||
match sim_reply_result {
|
|
||||||
Ok(sim_reply) => {
|
|
||||||
if let Some(sender) = self.reply_map.0.get(&sim_reply.component()) {
|
|
||||||
sender.send(sim_reply).expect("failed to send SIM reply");
|
|
||||||
} else {
|
|
||||||
log::warn!(
|
|
||||||
"no recipient for SIM reply from component {:?}",
|
|
||||||
sim_reply.component()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("failed to deserialize SIM reply: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() == std::io::ErrorKind::WouldBlock
|
|
||||||
|| e.kind() == std::io::ErrorKind::TimedOut
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
log::error!("error receiving data from UDP SIM server: {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if no_sim_requests_handled && no_data_from_udp_server_received {
|
|
||||||
return HandlingStatus::Empty;
|
|
||||||
}
|
|
||||||
HandlingStatus::HandledOne
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_reply_recipient(
|
|
||||||
&mut self,
|
|
||||||
component: SimComponent,
|
|
||||||
reply_sender: mpsc::Sender<SimReply>,
|
|
||||||
) {
|
|
||||||
self.reply_map.0.insert(component, reply_sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests {
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
net::{SocketAddr, UdpSocket},
|
|
||||||
sync::{
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
mpsc, Arc,
|
|
||||||
},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use satrs_minisim::{
|
|
||||||
eps::{PcduReply, PcduRequest},
|
|
||||||
SerializableSimMsgPayload, SimComponent, SimCtrlReply, SimCtrlRequest, SimMessageProvider,
|
|
||||||
SimReply, SimRequest,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::SimClientUdp;
|
|
||||||
|
|
||||||
struct UdpSimTestServer {
|
|
||||||
udp_server: UdpSocket,
|
|
||||||
request_tx: mpsc::Sender<SimRequest>,
|
|
||||||
reply_rx: mpsc::Receiver<SimReply>,
|
|
||||||
last_sender: Option<SocketAddr>,
|
|
||||||
stop_signal: Arc<AtomicBool>,
|
|
||||||
recv_buf: [u8; 1024],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UdpSimTestServer {
|
|
||||||
pub fn new(
|
|
||||||
request_tx: mpsc::Sender<SimRequest>,
|
|
||||||
reply_rx: mpsc::Receiver<SimReply>,
|
|
||||||
stop_signal: Arc<AtomicBool>,
|
|
||||||
) -> Self {
|
|
||||||
let udp_server = UdpSocket::bind("127.0.0.1:0").expect("creating UDP server failed");
|
|
||||||
udp_server
|
|
||||||
.set_nonblocking(true)
|
|
||||||
.expect("failed to set UDP server to non-blocking");
|
|
||||||
Self {
|
|
||||||
udp_server,
|
|
||||||
request_tx,
|
|
||||||
reply_rx,
|
|
||||||
last_sender: None,
|
|
||||||
stop_signal,
|
|
||||||
recv_buf: [0; 1024],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn operation(&mut self) {
|
|
||||||
loop {
|
|
||||||
let mut no_sim_replies_handled = true;
|
|
||||||
let mut no_data_received = true;
|
|
||||||
if self.stop_signal.load(Ordering::Relaxed) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if let Some(last_sender) = self.last_sender {
|
|
||||||
loop {
|
|
||||||
match self.reply_rx.try_recv() {
|
|
||||||
Ok(sim_reply) => {
|
|
||||||
let sim_reply_json = serde_json::to_string(&sim_reply)
|
|
||||||
.expect("failed to serialize SimReply");
|
|
||||||
self.udp_server
|
|
||||||
.send_to(sim_reply_json.as_bytes(), last_sender)
|
|
||||||
.expect("failed to send reply to client from UDP server");
|
|
||||||
no_sim_replies_handled = false;
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
mpsc::TryRecvError::Empty => break,
|
|
||||||
mpsc::TryRecvError::Disconnected => {
|
|
||||||
panic!("reply sender disconnected")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match self.udp_server.recv_from(&mut self.recv_buf) {
|
|
||||||
Ok((read_bytes, from)) => {
|
|
||||||
let sim_request: SimRequest =
|
|
||||||
serde_json::from_slice(&self.recv_buf[0..read_bytes])
|
|
||||||
.expect("failed to deserialize SimRequest");
|
|
||||||
if sim_request.component() == SimComponent::SimCtrl {
|
|
||||||
// For a ping, we perform the reply handling here directly
|
|
||||||
let sim_ctrl_request =
|
|
||||||
SimCtrlRequest::from_sim_message(&sim_request)
|
|
||||||
.expect("failed to convert SimRequest to SimCtrlRequest");
|
|
||||||
match sim_ctrl_request {
|
|
||||||
SimCtrlRequest::Ping => {
|
|
||||||
no_data_received = false;
|
|
||||||
self.last_sender = Some(from);
|
|
||||||
let sim_reply = SimReply::new(&SimCtrlReply::Pong);
|
|
||||||
let sim_reply_json = serde_json::to_string(&sim_reply)
|
|
||||||
.expect("failed to serialize SimReply");
|
|
||||||
self.udp_server
|
|
||||||
.send_to(sim_reply_json.as_bytes(), from)
|
|
||||||
.expect(
|
|
||||||
"failed to send reply to client from UDP server",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Forward each SIM request for testing purposes.
|
|
||||||
self.request_tx
|
|
||||||
.send(sim_request)
|
|
||||||
.expect("failed to send request");
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() != std::io::ErrorKind::WouldBlock
|
|
||||||
&& e.kind() != std::io::ErrorKind::TimedOut
|
|
||||||
{
|
|
||||||
panic!("UDP server error: {}", e);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if no_sim_replies_handled && no_data_received {
|
|
||||||
std::thread::sleep(Duration::from_millis(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_addr(&self) -> SocketAddr {
|
|
||||||
self.udp_server.local_addr().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_connection_test() {
|
|
||||||
let (server_sim_request_tx, server_sim_request_rx) = mpsc::channel();
|
|
||||||
let (_server_sim_reply_tx, server_sim_reply_rx) = mpsc::channel();
|
|
||||||
let stop_signal = Arc::new(AtomicBool::new(false));
|
|
||||||
let mut udp_server = UdpSimTestServer::new(
|
|
||||||
server_sim_request_tx,
|
|
||||||
server_sim_reply_rx,
|
|
||||||
stop_signal.clone(),
|
|
||||||
);
|
|
||||||
let server_addr = udp_server.local_addr();
|
|
||||||
let (_client_sim_req_tx, client_sim_req_rx) = mpsc::channel();
|
|
||||||
// Need to spawn the simulator UDP server before calling the client constructor.
|
|
||||||
let jh0 = std::thread::spawn(move || {
|
|
||||||
udp_server.operation();
|
|
||||||
});
|
|
||||||
// Creating the client also performs the connection test.
|
|
||||||
SimClientUdp::new(server_addr, client_sim_req_rx).unwrap();
|
|
||||||
let sim_request = server_sim_request_rx
|
|
||||||
.recv_timeout(Duration::from_millis(50))
|
|
||||||
.expect("no SIM request received");
|
|
||||||
let ping_request = SimCtrlRequest::from_sim_message(&sim_request)
|
|
||||||
.expect("failed to create SimCtrlRequest");
|
|
||||||
assert_eq!(ping_request, SimCtrlRequest::Ping);
|
|
||||||
// Stop the server.
|
|
||||||
stop_signal.store(true, Ordering::Relaxed);
|
|
||||||
jh0.join().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_request_reply_test() {
|
|
||||||
let (server_sim_request_tx, server_sim_request_rx) = mpsc::channel();
|
|
||||||
let (server_sim_reply_tx, sever_sim_reply_rx) = mpsc::channel();
|
|
||||||
let stop_signal = Arc::new(AtomicBool::new(false));
|
|
||||||
let mut udp_server = UdpSimTestServer::new(
|
|
||||||
server_sim_request_tx,
|
|
||||||
sever_sim_reply_rx,
|
|
||||||
stop_signal.clone(),
|
|
||||||
);
|
|
||||||
let server_addr = udp_server.local_addr();
|
|
||||||
let (client_sim_req_tx, client_sim_req_rx) = mpsc::channel();
|
|
||||||
let (client_pcdu_reply_tx, client_pcdu_reply_rx) = mpsc::channel();
|
|
||||||
// Need to spawn the simulator UDP server before calling the client constructor.
|
|
||||||
let jh0 = std::thread::spawn(move || {
|
|
||||||
udp_server.operation();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Creating the client also performs the connection test.
|
|
||||||
let mut client = SimClientUdp::new(server_addr, client_sim_req_rx).unwrap();
|
|
||||||
client.add_reply_recipient(SimComponent::Pcdu, client_pcdu_reply_tx);
|
|
||||||
|
|
||||||
let sim_request = server_sim_request_rx
|
|
||||||
.recv_timeout(Duration::from_millis(50))
|
|
||||||
.expect("no SIM request received");
|
|
||||||
let ping_request = SimCtrlRequest::from_sim_message(&sim_request)
|
|
||||||
.expect("failed to create SimCtrlRequest");
|
|
||||||
assert_eq!(ping_request, SimCtrlRequest::Ping);
|
|
||||||
|
|
||||||
let pcdu_req = PcduRequest::RequestSwitchInfo;
|
|
||||||
client_sim_req_tx
|
|
||||||
.send(SimRequest::new_with_epoch_time(pcdu_req))
|
|
||||||
.expect("send failed");
|
|
||||||
client.operation();
|
|
||||||
|
|
||||||
// Check that the request arrives properly at the server.
|
|
||||||
let sim_request = server_sim_request_rx
|
|
||||||
.recv_timeout(Duration::from_millis(50))
|
|
||||||
.expect("no SIM request received");
|
|
||||||
let req_recvd_on_server =
|
|
||||||
PcduRequest::from_sim_message(&sim_request).expect("failed to create SimCtrlRequest");
|
|
||||||
matches!(req_recvd_on_server, PcduRequest::RequestSwitchInfo);
|
|
||||||
|
|
||||||
// We inject the reply ourselves.
|
|
||||||
let pcdu_reply = PcduReply::SwitchInfo(HashMap::new());
|
|
||||||
server_sim_reply_tx
|
|
||||||
.send(SimReply::new(&pcdu_reply))
|
|
||||||
.expect("sending PCDU reply failed");
|
|
||||||
|
|
||||||
// Now we verify that the reply is sent by the UDP server back to the client, and then
|
|
||||||
// forwarded by the clients internal map.
|
|
||||||
let mut pcdu_reply_received = false;
|
|
||||||
for _ in 0..3 {
|
|
||||||
client.operation();
|
|
||||||
|
|
||||||
match client_pcdu_reply_rx.try_recv() {
|
|
||||||
Ok(sim_reply) => {
|
|
||||||
assert_eq!(sim_reply.component(), SimComponent::Pcdu);
|
|
||||||
let pcdu_reply_from_client = PcduReply::from_sim_message(&sim_reply)
|
|
||||||
.expect("failed to create PcduReply");
|
|
||||||
assert_eq!(pcdu_reply_from_client, pcdu_reply);
|
|
||||||
pcdu_reply_received = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
mpsc::TryRecvError::Empty => std::thread::sleep(Duration::from_millis(10)),
|
|
||||||
mpsc::TryRecvError::Disconnected => panic!("reply sender disconnected"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !pcdu_reply_received {
|
|
||||||
panic!("no reply received");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the server.
|
|
||||||
stop_signal.store(true, Ordering::Relaxed);
|
|
||||||
jh0.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,19 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashSet, VecDeque},
|
collections::{HashSet, VecDeque},
|
||||||
|
fmt::Debug,
|
||||||
|
marker::PhantomData,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use satrs::tmtc::StoreAndSendError;
|
|
||||||
use satrs::{
|
use satrs::{
|
||||||
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
||||||
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
|
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
|
||||||
spacepackets::{CcsdsPacket, PacketId},
|
spacepackets::{CcsdsPacket, PacketId},
|
||||||
tmtc::PacketSource,
|
tmtc::{PacketSenderRaw, PacketSource},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::tmtc::sender::TmTcSender;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ConnectionFinishedHandler {}
|
pub struct ConnectionFinishedHandler {}
|
||||||
|
|
||||||
@ -112,23 +111,31 @@ pub type TcpServer<ReceivesTc, SendError> = TcpSpacepacketsServer<
|
|||||||
SendError,
|
SendError,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub struct TcpTask(pub TcpServer<TmTcSender, StoreAndSendError>);
|
pub struct TcpTask<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>(
|
||||||
|
pub TcpServer<TcSender, SendError>,
|
||||||
|
PhantomData<SendError>,
|
||||||
|
);
|
||||||
|
|
||||||
impl TcpTask {
|
impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
|
||||||
|
TcpTask<TcSender, SendError>
|
||||||
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cfg: ServerConfig,
|
cfg: ServerConfig,
|
||||||
tm_source: SyncTcpTmSource,
|
tm_source: SyncTcpTmSource,
|
||||||
tc_sender: TmTcSender,
|
tc_sender: TcSender,
|
||||||
valid_ids: HashSet<PacketId>,
|
valid_ids: HashSet<PacketId>,
|
||||||
) -> Result<Self, std::io::Error> {
|
) -> Result<Self, std::io::Error> {
|
||||||
Ok(Self(TcpSpacepacketsServer::new(
|
Ok(Self(
|
||||||
cfg,
|
TcpSpacepacketsServer::new(
|
||||||
tm_source,
|
cfg,
|
||||||
tc_sender,
|
tm_source,
|
||||||
SimplePacketValidator { valid_ids },
|
tc_sender,
|
||||||
ConnectionFinishedHandler::default(),
|
SimplePacketValidator { valid_ids },
|
||||||
None,
|
ConnectionFinishedHandler::default(),
|
||||||
)?))
|
None,
|
||||||
|
)?,
|
||||||
|
PhantomData,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn periodic_operation(&mut self) {
|
pub fn periodic_operation(&mut self) {
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
|
use core::fmt::Debug;
|
||||||
use std::net::{SocketAddr, UdpSocket};
|
use std::net::{SocketAddr, UdpSocket};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use satrs::pus::HandlingStatus;
|
use satrs::pus::HandlingStatus;
|
||||||
use satrs::tmtc::{PacketAsVec, PacketInPool, StoreAndSendError};
|
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderRaw};
|
||||||
use satrs::{
|
use satrs::{
|
||||||
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
||||||
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
|
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::tmtc::sender::TmTcSender;
|
|
||||||
|
|
||||||
pub trait UdpTmHandler {
|
pub trait UdpTmHandler {
|
||||||
fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
|
fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
|
||||||
}
|
}
|
||||||
@ -66,12 +65,21 @@ impl UdpTmHandler for DynamicUdpTmHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UdpTmtcServer<TmHandler: UdpTmHandler> {
|
pub struct UdpTmtcServer<
|
||||||
pub udp_tc_server: UdpTcServer<TmTcSender, StoreAndSendError>,
|
TcSender: PacketSenderRaw<Error = SendError>,
|
||||||
|
TmHandler: UdpTmHandler,
|
||||||
|
SendError,
|
||||||
|
> {
|
||||||
|
pub udp_tc_server: UdpTcServer<TcSender, SendError>,
|
||||||
pub tm_handler: TmHandler,
|
pub tm_handler: TmHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TmHandler: UdpTmHandler> UdpTmtcServer<TmHandler> {
|
impl<
|
||||||
|
TcSender: PacketSenderRaw<Error = SendError>,
|
||||||
|
TmHandler: UdpTmHandler,
|
||||||
|
SendError: Debug + 'static,
|
||||||
|
> UdpTmtcServer<TcSender, TmHandler, SendError>
|
||||||
|
{
|
||||||
pub fn periodic_operation(&mut self) {
|
pub fn periodic_operation(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
if self.poll_tc_server() == HandlingStatus::Empty {
|
if self.poll_tc_server() == HandlingStatus::Empty {
|
||||||
@ -107,6 +115,7 @@ impl<TmHandler: UdpTmHandler> UdpTmtcServer<TmHandler> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::{
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
net::IpAddr,
|
net::IpAddr,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
@ -117,16 +126,30 @@ mod tests {
|
|||||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||||
SpHeader,
|
SpHeader,
|
||||||
},
|
},
|
||||||
|
tmtc::PacketSenderRaw,
|
||||||
ComponentId,
|
ComponentId,
|
||||||
};
|
};
|
||||||
use satrs_example::config::{components, OBSW_SERVER_ADDR};
|
use satrs_example::config::{components, OBSW_SERVER_ADDR};
|
||||||
|
|
||||||
use crate::tmtc::sender::{MockSender, TmTcSender};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const UDP_SERVER_ID: ComponentId = 0x05;
|
const UDP_SERVER_ID: ComponentId = 0x05;
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct TestSender {
|
||||||
|
tc_vec: RefCell<VecDeque<PacketAsVec>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketSenderRaw for TestSender {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
let mut mut_queue = self.tc_vec.borrow_mut();
|
||||||
|
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct TestTmHandler {
|
pub struct TestTmHandler {
|
||||||
addrs_to_send_to: Arc<Mutex<VecDeque<SocketAddr>>>,
|
addrs_to_send_to: Arc<Mutex<VecDeque<SocketAddr>>>,
|
||||||
@ -141,7 +164,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_basic() {
|
fn test_basic() {
|
||||||
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0);
|
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0);
|
||||||
let test_receiver = TmTcSender::Mock(MockSender::default());
|
let test_receiver = TestSender::default();
|
||||||
|
// let tc_queue = test_receiver.tc_vec.clone();
|
||||||
let udp_tc_server =
|
let udp_tc_server =
|
||||||
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
||||||
let tm_handler = TestTmHandler::default();
|
let tm_handler = TestTmHandler::default();
|
||||||
@ -151,13 +175,7 @@ mod tests {
|
|||||||
tm_handler,
|
tm_handler,
|
||||||
};
|
};
|
||||||
udp_dyn_server.periodic_operation();
|
udp_dyn_server.periodic_operation();
|
||||||
let queue = udp_dyn_server
|
let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
|
||||||
.udp_tc_server
|
|
||||||
.tc_sender
|
|
||||||
.get_mock_sender()
|
|
||||||
.unwrap()
|
|
||||||
.0
|
|
||||||
.borrow();
|
|
||||||
assert!(queue.is_empty());
|
assert!(queue.is_empty());
|
||||||
assert!(tm_handler_calls.lock().unwrap().is_empty());
|
assert!(tm_handler_calls.lock().unwrap().is_empty());
|
||||||
}
|
}
|
||||||
@ -165,7 +183,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_transactions() {
|
fn test_transactions() {
|
||||||
let sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
|
let sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
|
||||||
let test_receiver = TmTcSender::Mock(MockSender::default());
|
let test_receiver = TestSender::default();
|
||||||
|
// let tc_queue = test_receiver.tc_vec.clone();
|
||||||
let udp_tc_server =
|
let udp_tc_server =
|
||||||
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
||||||
let server_addr = udp_tc_server.socket.local_addr().unwrap();
|
let server_addr = udp_tc_server.socket.local_addr().unwrap();
|
||||||
@ -185,13 +204,7 @@ mod tests {
|
|||||||
client.send_to(&ping_tc, server_addr).unwrap();
|
client.send_to(&ping_tc, server_addr).unwrap();
|
||||||
udp_dyn_server.periodic_operation();
|
udp_dyn_server.periodic_operation();
|
||||||
{
|
{
|
||||||
let mut queue = udp_dyn_server
|
let mut queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow_mut();
|
||||||
.udp_tc_server
|
|
||||||
.tc_sender
|
|
||||||
.get_mock_sender()
|
|
||||||
.unwrap()
|
|
||||||
.0
|
|
||||||
.borrow_mut();
|
|
||||||
assert!(!queue.is_empty());
|
assert!(!queue.is_empty());
|
||||||
let packet_with_sender = queue.pop_front().unwrap();
|
let packet_with_sender = queue.pop_front().unwrap();
|
||||||
assert_eq!(packet_with_sender.packet, ping_tc);
|
assert_eq!(packet_with_sender.packet, ping_tc);
|
||||||
@ -206,13 +219,7 @@ mod tests {
|
|||||||
assert_eq!(received_addr, client_addr);
|
assert_eq!(received_addr, client_addr);
|
||||||
}
|
}
|
||||||
udp_dyn_server.periodic_operation();
|
udp_dyn_server.periodic_operation();
|
||||||
let queue = udp_dyn_server
|
let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
|
||||||
.udp_tc_server
|
|
||||||
.tc_sender
|
|
||||||
.get_mock_sender()
|
|
||||||
.unwrap()
|
|
||||||
.0
|
|
||||||
.borrow();
|
|
||||||
assert!(queue.is_empty());
|
assert!(queue.is_empty());
|
||||||
drop(queue);
|
drop(queue);
|
||||||
// Still tries to send to the same client.
|
// Still tries to send to the same client.
|
||||||
|
@ -9,12 +9,12 @@ pub enum DeviceMode {
|
|||||||
Normal = 2,
|
Normal = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TimestampHelper {
|
pub struct TimeStampHelper {
|
||||||
stamper: CdsTime,
|
stamper: CdsTime,
|
||||||
time_stamp: [u8; 7],
|
time_stamp: [u8; 7],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimestampHelper {
|
impl TimeStampHelper {
|
||||||
pub fn stamp(&self) -> &[u8] {
|
pub fn stamp(&self) -> &[u8] {
|
||||||
&self.time_stamp
|
&self.time_stamp
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ impl TimestampHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TimestampHelper {
|
impl Default for TimeStampHelper {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
stamper: CdsTime::now_with_u16_days().expect("creating time stamper failed"),
|
stamper: CdsTime::now_with_u16_days().expect("creating time stamper failed"),
|
||||||
|
@ -1,149 +1,81 @@
|
|||||||
use std::{
|
|
||||||
net::{IpAddr, SocketAddr},
|
|
||||||
sync::{mpsc, Arc, Mutex},
|
|
||||||
thread,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use acs::mgm::{MgmHandlerLis3Mdl, SpiDummyInterface, SpiSimInterface, SpiSimInterfaceWrapper};
|
|
||||||
use eps::{
|
|
||||||
pcdu::{PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper},
|
|
||||||
PowerSwitchHelper,
|
|
||||||
};
|
|
||||||
use events::EventHandler;
|
|
||||||
use interface::{
|
|
||||||
sim_client_udp::create_sim_client,
|
|
||||||
tcp::{SyncTcpTmSource, TcpTask},
|
|
||||||
udp::UdpTmtcServer,
|
|
||||||
};
|
|
||||||
use log::info;
|
|
||||||
use logger::setup_logger;
|
|
||||||
use pus::{
|
|
||||||
action::create_action_service,
|
|
||||||
event::create_event_service,
|
|
||||||
hk::create_hk_service,
|
|
||||||
mode::create_mode_service,
|
|
||||||
scheduler::{create_scheduler_service, TcReleaser},
|
|
||||||
stack::PusStack,
|
|
||||||
test::create_test_service,
|
|
||||||
PusTcDistributor, PusTcMpscRouter,
|
|
||||||
};
|
|
||||||
use requests::GenericRequestRouter;
|
|
||||||
use satrs::{
|
|
||||||
hal::std::{tcp_server::ServerConfig, udp_server::UdpTcServer},
|
|
||||||
mode::{Mode, ModeAndSubmode, ModeRequest, ModeRequestHandlerMpscBounded},
|
|
||||||
mode_tree::connect_mode_nodes,
|
|
||||||
pus::{event_man::EventRequestWithToken, EcssTcInMemConverter, HandlingStatus},
|
|
||||||
request::{GenericMessage, MessageMetadata},
|
|
||||||
spacepackets::time::{cds::CdsTime, TimeWriter},
|
|
||||||
};
|
|
||||||
use satrs_example::{
|
|
||||||
config::{
|
|
||||||
acs::{MGM_HANDLER_0, MGM_HANDLER_1},
|
|
||||||
components::{NO_SENDER, PCDU_HANDLER, TCP_SERVER, UDP_SERVER},
|
|
||||||
pool::create_sched_tc_pool,
|
|
||||||
tasks::{FREQ_MS_AOCS, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS},
|
|
||||||
OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT,
|
|
||||||
},
|
|
||||||
DeviceMode,
|
|
||||||
};
|
|
||||||
use tmtc::sender::TmTcSender;
|
|
||||||
use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink};
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "heap_tmtc")] {
|
|
||||||
use interface::udp::DynamicUdpTmHandler;
|
|
||||||
use satrs::pus::EcssTcInVecConverter;
|
|
||||||
use tmtc::{tc_source::TcSourceTaskDynamic, tm_sink::TmSinkDynamic};
|
|
||||||
} else {
|
|
||||||
use std::sync::RwLock;
|
|
||||||
use interface::udp::StaticUdpTmHandler;
|
|
||||||
use satrs::pus::EcssTcInSharedPoolConverter;
|
|
||||||
use satrs::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
|
||||||
use satrs_example::config::pool::create_static_pools;
|
|
||||||
use tmtc::{
|
|
||||||
tc_source::TcSourceTaskStatic,
|
|
||||||
tm_sink::TmSinkStatic,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod acs;
|
mod acs;
|
||||||
mod eps;
|
|
||||||
mod events;
|
mod events;
|
||||||
mod hk;
|
mod hk;
|
||||||
mod interface;
|
mod interface;
|
||||||
mod logger;
|
mod logger;
|
||||||
mod pus;
|
mod pus;
|
||||||
mod requests;
|
mod requests;
|
||||||
mod spi;
|
|
||||||
mod tmtc;
|
mod tmtc;
|
||||||
|
|
||||||
fn main() {
|
use crate::events::EventHandler;
|
||||||
setup_logger().expect("setting up logging with fern failed");
|
use crate::interface::udp::DynamicUdpTmHandler;
|
||||||
println!("Running OBSW example");
|
use crate::pus::stack::PusStack;
|
||||||
|
use crate::tmtc::tc_source::{TcSourceTaskDynamic, TcSourceTaskStatic};
|
||||||
|
use crate::tmtc::tm_sink::{TmSinkDynamic, TmSinkStatic};
|
||||||
|
use log::info;
|
||||||
|
use pus::test::create_test_service_dynamic;
|
||||||
|
use satrs::hal::std::tcp_server::ServerConfig;
|
||||||
|
use satrs::hal::std::udp_server::UdpTcServer;
|
||||||
|
use satrs::request::GenericMessage;
|
||||||
|
use satrs::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
|
||||||
|
use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools};
|
||||||
|
use satrs_example::config::tasks::{
|
||||||
|
FREQ_MS_AOCS, FREQ_MS_EVENT_HANDLING, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC,
|
||||||
|
};
|
||||||
|
use satrs_example::config::{OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT};
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
use crate::acs::mgm::{MgmHandlerLis3Mdl, MpscModeLeafInterface, SpiDummyInterface};
|
||||||
if #[cfg(not(feature = "heap_tmtc"))] {
|
use crate::interface::tcp::{SyncTcpTmSource, TcpTask};
|
||||||
let (tm_pool, tc_pool) = create_static_pools();
|
use crate::interface::udp::{StaticUdpTmHandler, UdpTmtcServer};
|
||||||
let shared_tm_pool = Arc::new(RwLock::new(tm_pool));
|
use crate::logger::setup_logger;
|
||||||
let shared_tc_pool = Arc::new(RwLock::new(tc_pool));
|
use crate::pus::action::{create_action_service_dynamic, create_action_service_static};
|
||||||
let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool);
|
use crate::pus::event::{create_event_service_dynamic, create_event_service_static};
|
||||||
let shared_tc_pool_wrapper = SharedPacketPool::new(&shared_tc_pool);
|
use crate::pus::hk::{create_hk_service_dynamic, create_hk_service_static};
|
||||||
}
|
use crate::pus::mode::{create_mode_service_dynamic, create_mode_service_static};
|
||||||
}
|
use crate::pus::scheduler::{create_scheduler_service_dynamic, create_scheduler_service_static};
|
||||||
|
use crate::pus::test::create_test_service_static;
|
||||||
|
use crate::pus::{PusTcDistributor, PusTcMpscRouter};
|
||||||
|
use crate::requests::{CompositeRequest, GenericRequestRouter};
|
||||||
|
use satrs::mode::ModeRequest;
|
||||||
|
use satrs::pus::event_man::EventRequestWithToken;
|
||||||
|
use satrs::spacepackets::{time::cds::CdsTime, time::TimeWriter};
|
||||||
|
use satrs_example::config::components::{MGM_HANDLER_0, TCP_SERVER, UDP_SERVER};
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn static_tmtc_pool_main() {
|
||||||
|
let (tm_pool, tc_pool) = create_static_pools();
|
||||||
|
let shared_tm_pool = Arc::new(RwLock::new(tm_pool));
|
||||||
|
let shared_tc_pool = Arc::new(RwLock::new(tc_pool));
|
||||||
|
let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool);
|
||||||
|
let shared_tc_pool_wrapper = SharedPacketPool::new(&shared_tc_pool);
|
||||||
let (tc_source_tx, tc_source_rx) = mpsc::sync_channel(50);
|
let (tc_source_tx, tc_source_rx) = mpsc::sync_channel(50);
|
||||||
let (tm_sink_tx, tm_sink_rx) = mpsc::sync_channel(50);
|
let (tm_sink_tx, tm_sink_rx) = mpsc::sync_channel(50);
|
||||||
let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50);
|
let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50);
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
let tm_sink_tx_sender =
|
||||||
if #[cfg(not(feature = "heap_tmtc"))] {
|
PacketSenderWithSharedPool::new(tm_sink_tx.clone(), shared_tm_pool_wrapper.clone());
|
||||||
let tm_sender = TmTcSender::Static(
|
|
||||||
PacketSenderWithSharedPool::new(tm_sink_tx.clone(), shared_tm_pool_wrapper.clone())
|
|
||||||
);
|
|
||||||
} else if #[cfg(feature = "heap_tmtc")] {
|
|
||||||
let tm_sender = TmTcSender::Heap(tm_sink_tx.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (sim_request_tx, sim_request_rx) = mpsc::channel();
|
let (mgm_handler_composite_tx, mgm_handler_composite_rx) =
|
||||||
let (mgm_0_sim_reply_tx, mgm_0_sim_reply_rx) = mpsc::channel();
|
mpsc::channel::<GenericMessage<CompositeRequest>>();
|
||||||
let (mgm_1_sim_reply_tx, mgm_1_sim_reply_rx) = mpsc::channel();
|
let (mgm_handler_mode_tx, mgm_handler_mode_rx) = mpsc::channel::<GenericMessage<ModeRequest>>();
|
||||||
let (pcdu_sim_reply_tx, pcdu_sim_reply_rx) = mpsc::channel();
|
|
||||||
let mut opt_sim_client = create_sim_client(sim_request_rx);
|
|
||||||
|
|
||||||
let (mgm_0_handler_composite_tx, mgm_0_handler_composite_rx) = mpsc::sync_channel(10);
|
|
||||||
let (mgm_1_handler_composite_tx, mgm_1_handler_composite_rx) = mpsc::sync_channel(10);
|
|
||||||
let (pcdu_handler_composite_tx, pcdu_handler_composite_rx) = mpsc::sync_channel(30);
|
|
||||||
let (mgm_0_handler_mode_tx, mgm_0_handler_mode_rx) = mpsc::sync_channel(5);
|
|
||||||
let (mgm_1_handler_mode_tx, mgm_1_handler_mode_rx) = mpsc::sync_channel(5);
|
|
||||||
let (pcdu_handler_mode_tx, pcdu_handler_mode_rx) = mpsc::sync_channel(5);
|
|
||||||
|
|
||||||
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||||
let mut request_map = GenericRequestRouter::default();
|
let mut request_map = GenericRequestRouter::default();
|
||||||
request_map
|
request_map
|
||||||
.composite_router_map
|
.composite_router_map
|
||||||
.insert(MGM_HANDLER_0.id(), mgm_0_handler_composite_tx);
|
.insert(MGM_HANDLER_0.id(), mgm_handler_composite_tx);
|
||||||
request_map
|
request_map
|
||||||
.composite_router_map
|
.mode_router_map
|
||||||
.insert(MGM_HANDLER_0.id(), mgm_1_handler_composite_tx);
|
.insert(MGM_HANDLER_0.id(), mgm_handler_mode_tx);
|
||||||
request_map
|
|
||||||
.composite_router_map
|
|
||||||
.insert(PCDU_HANDLER.id(), pcdu_handler_composite_tx);
|
|
||||||
|
|
||||||
// This helper structure is used by all telecommand providers which need to send telecommands
|
// This helper structure is used by all telecommand providers which need to send telecommands
|
||||||
// to the TC source.
|
// to the TC source.
|
||||||
cfg_if::cfg_if! {
|
let tc_source = PacketSenderWithSharedPool::new(tc_source_tx, shared_tc_pool_wrapper.clone());
|
||||||
if #[cfg(not(feature = "heap_tmtc"))] {
|
|
||||||
let tc_sender_with_shared_pool =
|
|
||||||
PacketSenderWithSharedPool::new(tc_source_tx, shared_tc_pool_wrapper.clone());
|
|
||||||
let tc_in_mem_converter =
|
|
||||||
EcssTcInMemConverter::Static(EcssTcInSharedPoolConverter::new(shared_tc_pool, 4096));
|
|
||||||
} else if #[cfg(feature = "heap_tmtc")] {
|
|
||||||
let tc_in_mem_converter = EcssTcInMemConverter::Heap(EcssTcInVecConverter::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create event handling components
|
// Create event handling components
|
||||||
// These sender handles are used to send event requests, for example to enable or disable
|
// These sender handles are used to send event requests, for example to enable or disable
|
||||||
@ -155,24 +87,17 @@ fn main() {
|
|||||||
// in the sat-rs documentation.
|
// in the sat-rs documentation.
|
||||||
let mut event_handler = EventHandler::new(tm_sink_tx.clone(), event_rx, event_request_rx);
|
let mut event_handler = EventHandler::new(tm_sink_tx.clone(), event_rx, event_request_rx);
|
||||||
|
|
||||||
let (pus_test_tx, pus_test_rx) = mpsc::sync_channel(20);
|
let (pus_test_tx, pus_test_rx) = mpsc::channel();
|
||||||
let (pus_event_tx, pus_event_rx) = mpsc::sync_channel(10);
|
let (pus_event_tx, pus_event_rx) = mpsc::channel();
|
||||||
let (pus_sched_tx, pus_sched_rx) = mpsc::sync_channel(50);
|
let (pus_sched_tx, pus_sched_rx) = mpsc::channel();
|
||||||
let (pus_hk_tx, pus_hk_rx) = mpsc::sync_channel(50);
|
let (pus_hk_tx, pus_hk_rx) = mpsc::channel();
|
||||||
let (pus_action_tx, pus_action_rx) = mpsc::sync_channel(50);
|
let (pus_action_tx, pus_action_rx) = mpsc::channel();
|
||||||
let (pus_mode_tx, pus_mode_rx) = mpsc::sync_channel(50);
|
let (pus_mode_tx, pus_mode_rx) = mpsc::channel();
|
||||||
|
|
||||||
let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel();
|
let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel();
|
||||||
let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::sync_channel(50);
|
let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::channel();
|
||||||
let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::sync_channel(30);
|
let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel();
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(not(feature = "heap_tmtc"))] {
|
|
||||||
let tc_releaser = TcReleaser::Static(tc_sender_with_shared_pool.clone());
|
|
||||||
} else if #[cfg(feature = "heap_tmtc")] {
|
|
||||||
let tc_releaser = TcReleaser::Heap(tc_source_tx.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let pus_router = PusTcMpscRouter {
|
let pus_router = PusTcMpscRouter {
|
||||||
test_tc_sender: pus_test_tx,
|
test_tc_sender: pus_test_tx,
|
||||||
event_tc_sender: pus_event_tx,
|
event_tc_sender: pus_event_tx,
|
||||||
@ -181,42 +106,41 @@ fn main() {
|
|||||||
action_tc_sender: pus_action_tx,
|
action_tc_sender: pus_action_tx,
|
||||||
mode_tc_sender: pus_mode_tx,
|
mode_tc_sender: pus_mode_tx,
|
||||||
};
|
};
|
||||||
let pus_test_service = create_test_service(
|
let pus_test_service = create_test_service_static(
|
||||||
tm_sender.clone(),
|
tm_sink_tx_sender.clone(),
|
||||||
tc_in_mem_converter.clone(),
|
shared_tc_pool.clone(),
|
||||||
event_tx.clone(),
|
event_tx.clone(),
|
||||||
pus_test_rx,
|
pus_test_rx,
|
||||||
);
|
);
|
||||||
let pus_scheduler_service = create_scheduler_service(
|
let pus_scheduler_service = create_scheduler_service_static(
|
||||||
tm_sender.clone(),
|
tm_sink_tx_sender.clone(),
|
||||||
tc_in_mem_converter.clone(),
|
tc_source.clone(),
|
||||||
tc_releaser,
|
|
||||||
pus_sched_rx,
|
pus_sched_rx,
|
||||||
create_sched_tc_pool(),
|
create_sched_tc_pool(),
|
||||||
);
|
);
|
||||||
let pus_event_service = create_event_service(
|
let pus_event_service = create_event_service_static(
|
||||||
tm_sender.clone(),
|
tm_sink_tx_sender.clone(),
|
||||||
tc_in_mem_converter.clone(),
|
shared_tc_pool.clone(),
|
||||||
pus_event_rx,
|
pus_event_rx,
|
||||||
event_request_tx,
|
event_request_tx,
|
||||||
);
|
);
|
||||||
let pus_action_service = create_action_service(
|
let pus_action_service = create_action_service_static(
|
||||||
tm_sender.clone(),
|
tm_sink_tx_sender.clone(),
|
||||||
tc_in_mem_converter.clone(),
|
shared_tc_pool.clone(),
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
request_map.clone(),
|
request_map.clone(),
|
||||||
pus_action_reply_rx,
|
pus_action_reply_rx,
|
||||||
);
|
);
|
||||||
let pus_hk_service = create_hk_service(
|
let pus_hk_service = create_hk_service_static(
|
||||||
tm_sender.clone(),
|
tm_sink_tx_sender.clone(),
|
||||||
tc_in_mem_converter.clone(),
|
shared_tc_pool.clone(),
|
||||||
pus_hk_rx,
|
pus_hk_rx,
|
||||||
request_map.clone(),
|
request_map.clone(),
|
||||||
pus_hk_reply_rx,
|
pus_hk_reply_rx,
|
||||||
);
|
);
|
||||||
let pus_mode_service = create_mode_service(
|
let pus_mode_service = create_mode_service_static(
|
||||||
tm_sender.clone(),
|
tm_sink_tx_sender.clone(),
|
||||||
tc_in_mem_converter.clone(),
|
shared_tc_pool.clone(),
|
||||||
pus_mode_rx,
|
pus_mode_rx,
|
||||||
request_map,
|
request_map,
|
||||||
pus_mode_reply_rx,
|
pus_mode_reply_rx,
|
||||||
@ -230,36 +154,21 @@ fn main() {
|
|||||||
pus_mode_service,
|
pus_mode_service,
|
||||||
);
|
);
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
let mut tmtc_task = TcSourceTaskStatic::new(
|
||||||
if #[cfg(not(feature = "heap_tmtc"))] {
|
shared_tc_pool_wrapper.clone(),
|
||||||
let mut tmtc_task = TcSourceTask::Static(TcSourceTaskStatic::new(
|
tc_source_rx,
|
||||||
shared_tc_pool_wrapper.clone(),
|
PusTcDistributor::new(tm_sink_tx_sender, pus_router),
|
||||||
tc_source_rx,
|
);
|
||||||
PusTcDistributor::new(tm_sender.clone(), pus_router),
|
|
||||||
));
|
|
||||||
let tc_sender = TmTcSender::Static(tc_sender_with_shared_pool);
|
|
||||||
let udp_tm_handler = StaticUdpTmHandler {
|
|
||||||
tm_rx: tm_server_rx,
|
|
||||||
tm_store: shared_tm_pool.clone(),
|
|
||||||
};
|
|
||||||
} else if #[cfg(feature = "heap_tmtc")] {
|
|
||||||
let mut tmtc_task = TcSourceTask::Heap(TcSourceTaskDynamic::new(
|
|
||||||
tc_source_rx,
|
|
||||||
PusTcDistributor::new(tm_sender.clone(), pus_router),
|
|
||||||
));
|
|
||||||
let tc_sender = TmTcSender::Heap(tc_source_tx.clone());
|
|
||||||
let udp_tm_handler = DynamicUdpTmHandler {
|
|
||||||
tm_rx: tm_server_rx,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||||
let udp_tc_server = UdpTcServer::new(UDP_SERVER.id(), sock_addr, 2048, tc_sender.clone())
|
let udp_tc_server = UdpTcServer::new(UDP_SERVER.id(), sock_addr, 2048, tc_source.clone())
|
||||||
.expect("creating UDP TMTC server failed");
|
.expect("creating UDP TMTC server failed");
|
||||||
let mut udp_tmtc_server = UdpTmtcServer {
|
let mut udp_tmtc_server = UdpTmtcServer {
|
||||||
udp_tc_server,
|
udp_tc_server,
|
||||||
tm_handler: udp_tm_handler,
|
tm_handler: StaticUdpTmHandler {
|
||||||
|
tm_rx: tm_server_rx,
|
||||||
|
tm_store: shared_tm_pool.clone(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let tcp_server_cfg = ServerConfig::new(
|
let tcp_server_cfg = ServerConfig::new(
|
||||||
@ -273,135 +182,38 @@ fn main() {
|
|||||||
let mut tcp_server = TcpTask::new(
|
let mut tcp_server = TcpTask::new(
|
||||||
tcp_server_cfg,
|
tcp_server_cfg,
|
||||||
sync_tm_tcp_source.clone(),
|
sync_tm_tcp_source.clone(),
|
||||||
tc_sender,
|
tc_source.clone(),
|
||||||
PACKET_ID_VALIDATOR.clone(),
|
PACKET_ID_VALIDATOR.clone(),
|
||||||
)
|
)
|
||||||
.expect("tcp server creation failed");
|
.expect("tcp server creation failed");
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
let mut tm_sink = TmSinkStatic::new(
|
||||||
if #[cfg(not(feature = "heap_tmtc"))] {
|
shared_tm_pool_wrapper,
|
||||||
let mut tm_sink = TmSink::Static(TmSinkStatic::new(
|
sync_tm_tcp_source,
|
||||||
shared_tm_pool_wrapper,
|
tm_sink_rx,
|
||||||
sync_tm_tcp_source,
|
tm_server_tx,
|
||||||
tm_sink_rx,
|
);
|
||||||
tm_server_tx,
|
|
||||||
));
|
|
||||||
} else if #[cfg(feature = "heap_tmtc")] {
|
|
||||||
let mut tm_sink = TmSink::Heap(TmSinkDynamic::new(
|
|
||||||
sync_tm_tcp_source,
|
|
||||||
tm_sink_rx,
|
|
||||||
tm_server_tx,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let shared_switch_set = Arc::new(Mutex::default());
|
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
|
||||||
let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20);
|
mpsc::channel();
|
||||||
let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone());
|
|
||||||
|
|
||||||
let shared_mgm_0_set = Arc::default();
|
let dummy_spi_interface = SpiDummyInterface::default();
|
||||||
let shared_mgm_1_set = Arc::default();
|
let shared_mgm_set = Arc::default();
|
||||||
let mgm_0_mode_node =
|
let mode_leaf_interface = MpscModeLeafInterface {
|
||||||
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_0.into(), mgm_0_handler_mode_rx);
|
request_rx: mgm_handler_mode_rx,
|
||||||
let mgm_1_mode_node =
|
reply_tx_to_pus: pus_mode_reply_tx,
|
||||||
ModeRequestHandlerMpscBounded::new(MGM_HANDLER_1.into(), mgm_1_handler_mode_rx);
|
reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx,
|
||||||
let (mgm_0_spi_interface, mgm_1_spi_interface) =
|
};
|
||||||
if let Some(sim_client) = opt_sim_client.as_mut() {
|
let mut mgm_handler = MgmHandlerLis3Mdl::new(
|
||||||
sim_client
|
|
||||||
.add_reply_recipient(satrs_minisim::SimComponent::Mgm0Lis3Mdl, mgm_0_sim_reply_tx);
|
|
||||||
sim_client
|
|
||||||
.add_reply_recipient(satrs_minisim::SimComponent::Mgm1Lis3Mdl, mgm_1_sim_reply_tx);
|
|
||||||
(
|
|
||||||
SpiSimInterfaceWrapper::Sim(SpiSimInterface {
|
|
||||||
sim_request_tx: sim_request_tx.clone(),
|
|
||||||
sim_reply_rx: mgm_0_sim_reply_rx,
|
|
||||||
}),
|
|
||||||
SpiSimInterfaceWrapper::Sim(SpiSimInterface {
|
|
||||||
sim_request_tx: sim_request_tx.clone(),
|
|
||||||
sim_reply_rx: mgm_1_sim_reply_rx,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()),
|
|
||||||
SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let mut mgm_0_handler = MgmHandlerLis3Mdl::new(
|
|
||||||
MGM_HANDLER_0,
|
MGM_HANDLER_0,
|
||||||
"MGM_0",
|
"MGM_0",
|
||||||
mgm_0_mode_node,
|
mode_leaf_interface,
|
||||||
mgm_0_handler_composite_rx,
|
mgm_handler_composite_rx,
|
||||||
pus_hk_reply_tx.clone(),
|
|
||||||
switch_helper.clone(),
|
|
||||||
tm_sender.clone(),
|
|
||||||
mgm_0_spi_interface,
|
|
||||||
shared_mgm_0_set,
|
|
||||||
);
|
|
||||||
let mut mgm_1_handler = MgmHandlerLis3Mdl::new(
|
|
||||||
MGM_HANDLER_1,
|
|
||||||
"MGM_1",
|
|
||||||
mgm_1_mode_node,
|
|
||||||
mgm_1_handler_composite_rx,
|
|
||||||
pus_hk_reply_tx.clone(),
|
|
||||||
switch_helper.clone(),
|
|
||||||
tm_sender.clone(),
|
|
||||||
mgm_1_spi_interface,
|
|
||||||
shared_mgm_1_set,
|
|
||||||
);
|
|
||||||
// Connect PUS service to device handlers.
|
|
||||||
connect_mode_nodes(
|
|
||||||
&mut pus_stack.mode_srv,
|
|
||||||
mgm_0_handler_mode_tx,
|
|
||||||
&mut mgm_0_handler,
|
|
||||||
pus_mode_reply_tx.clone(),
|
|
||||||
);
|
|
||||||
connect_mode_nodes(
|
|
||||||
&mut pus_stack.mode_srv,
|
|
||||||
mgm_1_handler_mode_tx,
|
|
||||||
&mut mgm_1_handler,
|
|
||||||
pus_mode_reply_tx.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() {
|
|
||||||
sim_client.add_reply_recipient(satrs_minisim::SimComponent::Pcdu, pcdu_sim_reply_tx);
|
|
||||||
SerialSimInterfaceWrapper::Sim(SerialInterfaceToSim::new(
|
|
||||||
sim_request_tx.clone(),
|
|
||||||
pcdu_sim_reply_rx,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
SerialSimInterfaceWrapper::Dummy(SerialInterfaceDummy::default())
|
|
||||||
};
|
|
||||||
let pcdu_mode_node =
|
|
||||||
ModeRequestHandlerMpscBounded::new(PCDU_HANDLER.into(), pcdu_handler_mode_rx);
|
|
||||||
let mut pcdu_handler = PcduHandler::new(
|
|
||||||
PCDU_HANDLER,
|
|
||||||
"PCDU",
|
|
||||||
pcdu_mode_node,
|
|
||||||
pcdu_handler_composite_rx,
|
|
||||||
pus_hk_reply_tx,
|
pus_hk_reply_tx,
|
||||||
switch_request_rx,
|
tm_sink_tx,
|
||||||
tm_sender.clone(),
|
dummy_spi_interface,
|
||||||
pcdu_serial_interface,
|
shared_mgm_set,
|
||||||
shared_switch_set,
|
|
||||||
);
|
);
|
||||||
connect_mode_nodes(
|
|
||||||
&mut pus_stack.mode_srv,
|
|
||||||
pcdu_handler_mode_tx.clone(),
|
|
||||||
&mut pcdu_handler,
|
|
||||||
pus_mode_reply_tx,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The PCDU is a critical component which should be in normal mode immediately.
|
|
||||||
pcdu_handler_mode_tx
|
|
||||||
.send(GenericMessage::new(
|
|
||||||
MessageMetadata::new(0, NO_SENDER),
|
|
||||||
ModeRequest::SetMode {
|
|
||||||
mode_and_submode: ModeAndSubmode::new(DeviceMode::Normal as Mode, 0),
|
|
||||||
forced: false,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.expect("sending initial mode request failed");
|
|
||||||
|
|
||||||
info!("Starting TMTC and UDP task");
|
info!("Starting TMTC and UDP task");
|
||||||
let jh_udp_tmtc = thread::Builder::new()
|
let jh_udp_tmtc = thread::Builder::new()
|
||||||
@ -435,53 +247,28 @@ fn main() {
|
|||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut opt_jh_sim_client = None;
|
info!("Starting event handling task");
|
||||||
if let Some(mut sim_client) = opt_sim_client {
|
let jh_event_handling = thread::Builder::new()
|
||||||
info!("Starting UDP sim client task");
|
.name("sat-rs events".to_string())
|
||||||
opt_jh_sim_client = Some(
|
.spawn(move || loop {
|
||||||
thread::Builder::new()
|
event_handler.periodic_operation();
|
||||||
.name("sat-rs sim adapter".to_string())
|
thread::sleep(Duration::from_millis(FREQ_MS_EVENT_HANDLING));
|
||||||
.spawn(move || loop {
|
})
|
||||||
if sim_client.operation() == HandlingStatus::Empty {
|
.unwrap();
|
||||||
std::thread::sleep(Duration::from_millis(SIM_CLIENT_IDLE_DELAY_MS));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Starting AOCS thread");
|
info!("Starting AOCS thread");
|
||||||
let jh_aocs = thread::Builder::new()
|
let jh_aocs = thread::Builder::new()
|
||||||
.name("sat-rs aocs".to_string())
|
.name("sat-rs aocs".to_string())
|
||||||
.spawn(move || loop {
|
.spawn(move || loop {
|
||||||
mgm_0_handler.periodic_operation();
|
mgm_handler.periodic_operation();
|
||||||
mgm_1_handler.periodic_operation();
|
|
||||||
thread::sleep(Duration::from_millis(FREQ_MS_AOCS));
|
thread::sleep(Duration::from_millis(FREQ_MS_AOCS));
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
info!("Starting EPS thread");
|
|
||||||
let jh_eps = thread::Builder::new()
|
|
||||||
.name("sat-rs eps".to_string())
|
|
||||||
.spawn(move || loop {
|
|
||||||
// TODO: We should introduce something like a fixed timeslot helper to allow a more
|
|
||||||
// declarative API. It would also be very useful for the AOCS task.
|
|
||||||
//
|
|
||||||
// TODO: The fixed timeslot handler exists.. use it.
|
|
||||||
pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::RegularOp);
|
|
||||||
thread::sleep(Duration::from_millis(50));
|
|
||||||
pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies);
|
|
||||||
thread::sleep(Duration::from_millis(50));
|
|
||||||
pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies);
|
|
||||||
thread::sleep(Duration::from_millis(300));
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
info!("Starting PUS handler thread");
|
info!("Starting PUS handler thread");
|
||||||
let jh_pus_handler = thread::Builder::new()
|
let jh_pus_handler = thread::Builder::new()
|
||||||
.name("sat-rs pus".to_string())
|
.name("sat-rs pus".to_string())
|
||||||
.spawn(move || loop {
|
.spawn(move || loop {
|
||||||
event_handler.periodic_operation();
|
|
||||||
pus_stack.periodic_operation();
|
pus_stack.periodic_operation();
|
||||||
thread::sleep(Duration::from_millis(FREQ_MS_PUS_STACK));
|
thread::sleep(Duration::from_millis(FREQ_MS_PUS_STACK));
|
||||||
})
|
})
|
||||||
@ -496,18 +283,241 @@ fn main() {
|
|||||||
jh_tm_funnel
|
jh_tm_funnel
|
||||||
.join()
|
.join()
|
||||||
.expect("Joining TM Funnel thread failed");
|
.expect("Joining TM Funnel thread failed");
|
||||||
if let Some(jh_sim_client) = opt_jh_sim_client {
|
jh_event_handling
|
||||||
jh_sim_client
|
.join()
|
||||||
.join()
|
.expect("Joining Event Manager thread failed");
|
||||||
.expect("Joining SIM client thread failed");
|
|
||||||
}
|
|
||||||
jh_aocs.join().expect("Joining AOCS thread failed");
|
jh_aocs.join().expect("Joining AOCS thread failed");
|
||||||
jh_eps.join().expect("Joining EPS thread failed");
|
|
||||||
jh_pus_handler
|
jh_pus_handler
|
||||||
.join()
|
.join()
|
||||||
.expect("Joining PUS handler thread failed");
|
.expect("Joining PUS handler thread failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn dyn_tmtc_pool_main() {
|
||||||
|
let (tc_source_tx, tc_source_rx) = mpsc::channel();
|
||||||
|
let (tm_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::<GenericMessage<CompositeRequest>>();
|
||||||
|
let (mgm_handler_mode_tx, mgm_handler_mode_rx) = mpsc::channel::<GenericMessage<ModeRequest>>();
|
||||||
|
|
||||||
|
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||||
|
let mut request_map = GenericRequestRouter::default();
|
||||||
|
request_map
|
||||||
|
.composite_router_map
|
||||||
|
.insert(MGM_HANDLER_0.raw(), mgm_handler_composite_tx);
|
||||||
|
request_map
|
||||||
|
.mode_router_map
|
||||||
|
.insert(MGM_HANDLER_0.raw(), mgm_handler_mode_tx);
|
||||||
|
|
||||||
|
// Create event handling components
|
||||||
|
// These sender handles are used to send event requests, for example to enable or disable
|
||||||
|
// certain events.
|
||||||
|
let (event_tx, event_rx) = mpsc::sync_channel(100);
|
||||||
|
let (event_request_tx, event_request_rx) = mpsc::channel::<EventRequestWithToken>();
|
||||||
|
// The event task is the core handler to perform the event routing and TM handling as specified
|
||||||
|
// in the sat-rs documentation.
|
||||||
|
let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_rx, event_request_rx);
|
||||||
|
|
||||||
|
let (pus_test_tx, pus_test_rx) = mpsc::channel();
|
||||||
|
let (pus_event_tx, pus_event_rx) = mpsc::channel();
|
||||||
|
let (pus_sched_tx, pus_sched_rx) = mpsc::channel();
|
||||||
|
let (pus_hk_tx, pus_hk_rx) = mpsc::channel();
|
||||||
|
let (pus_action_tx, pus_action_rx) = mpsc::channel();
|
||||||
|
let (pus_mode_tx, pus_mode_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let (_pus_action_reply_tx, pus_action_reply_rx) = mpsc::channel();
|
||||||
|
let (pus_hk_reply_tx, pus_hk_reply_rx) = mpsc::channel();
|
||||||
|
let (pus_mode_reply_tx, pus_mode_reply_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let pus_router = PusTcMpscRouter {
|
||||||
|
test_tc_sender: pus_test_tx,
|
||||||
|
event_tc_sender: pus_event_tx,
|
||||||
|
sched_tc_sender: pus_sched_tx,
|
||||||
|
hk_tc_sender: pus_hk_tx,
|
||||||
|
action_tc_sender: pus_action_tx,
|
||||||
|
mode_tc_sender: pus_mode_tx,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pus_test_service =
|
||||||
|
create_test_service_dynamic(tm_funnel_tx.clone(), event_tx.clone(), pus_test_rx);
|
||||||
|
let pus_scheduler_service = create_scheduler_service_dynamic(
|
||||||
|
tm_funnel_tx.clone(),
|
||||||
|
tc_source_tx.clone(),
|
||||||
|
pus_sched_rx,
|
||||||
|
create_sched_tc_pool(),
|
||||||
|
);
|
||||||
|
|
||||||
|
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(),
|
||||||
|
pus_action_rx,
|
||||||
|
request_map.clone(),
|
||||||
|
pus_action_reply_rx,
|
||||||
|
);
|
||||||
|
let pus_hk_service = create_hk_service_dynamic(
|
||||||
|
tm_funnel_tx.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_mode_service,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut tmtc_task = TcSourceTaskDynamic::new(
|
||||||
|
tc_source_rx,
|
||||||
|
PusTcDistributor::new(tm_funnel_tx.clone(), pus_router),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||||
|
let udp_tc_server = UdpTcServer::new(UDP_SERVER.id(), sock_addr, 2048, tc_source_tx.clone())
|
||||||
|
.expect("creating UDP TMTC server failed");
|
||||||
|
let mut udp_tmtc_server = UdpTmtcServer {
|
||||||
|
udp_tc_server,
|
||||||
|
tm_handler: DynamicUdpTmHandler {
|
||||||
|
tm_rx: tm_server_rx,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let tcp_server_cfg = ServerConfig::new(
|
||||||
|
TCP_SERVER.id(),
|
||||||
|
sock_addr,
|
||||||
|
Duration::from_millis(400),
|
||||||
|
4096,
|
||||||
|
8192,
|
||||||
|
);
|
||||||
|
let sync_tm_tcp_source = SyncTcpTmSource::new(200);
|
||||||
|
let mut tcp_server = TcpTask::new(
|
||||||
|
tcp_server_cfg,
|
||||||
|
sync_tm_tcp_source.clone(),
|
||||||
|
tc_source_tx.clone(),
|
||||||
|
PACKET_ID_VALIDATOR.clone(),
|
||||||
|
)
|
||||||
|
.expect("tcp server creation failed");
|
||||||
|
|
||||||
|
let mut tm_funnel = TmSinkDynamic::new(sync_tm_tcp_source, tm_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("sat-rs tmtc-udp".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
info!("Running UDP server on port {SERVER_PORT}");
|
||||||
|
loop {
|
||||||
|
udp_tmtc_server.periodic_operation();
|
||||||
|
tmtc_task.periodic_operation();
|
||||||
|
thread::sleep(Duration::from_millis(FREQ_MS_UDP_TMTC));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("Starting TCP task");
|
||||||
|
let jh_tcp = thread::Builder::new()
|
||||||
|
.name("sat-rs tcp".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
info!("Running TCP server on port {SERVER_PORT}");
|
||||||
|
loop {
|
||||||
|
tcp_server.periodic_operation();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("Starting TM funnel task");
|
||||||
|
let jh_tm_funnel = thread::Builder::new()
|
||||||
|
.name("sat-rs tm-sink".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
tm_funnel.operation();
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("Starting event handling task");
|
||||||
|
let jh_event_handling = thread::Builder::new()
|
||||||
|
.name("sat-rs events".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
event_handler.periodic_operation();
|
||||||
|
thread::sleep(Duration::from_millis(FREQ_MS_EVENT_HANDLING));
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("Starting AOCS thread");
|
||||||
|
let jh_aocs = thread::Builder::new()
|
||||||
|
.name("sat-rs aocs".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
mgm_handler.periodic_operation();
|
||||||
|
thread::sleep(Duration::from_millis(FREQ_MS_AOCS));
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("Starting PUS handler thread");
|
||||||
|
let jh_pus_handler = thread::Builder::new()
|
||||||
|
.name("sat-rs pus".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
pus_stack.periodic_operation();
|
||||||
|
thread::sleep(Duration::from_millis(FREQ_MS_PUS_STACK));
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
jh_udp_tmtc
|
||||||
|
.join()
|
||||||
|
.expect("Joining UDP TMTC server thread failed");
|
||||||
|
jh_tcp
|
||||||
|
.join()
|
||||||
|
.expect("Joining TCP TMTC server thread failed");
|
||||||
|
jh_tm_funnel
|
||||||
|
.join()
|
||||||
|
.expect("Joining TM Funnel thread failed");
|
||||||
|
jh_event_handling
|
||||||
|
.join()
|
||||||
|
.expect("Joining Event Manager thread failed");
|
||||||
|
jh_aocs.join().expect("Joining AOCS thread failed");
|
||||||
|
jh_pus_handler
|
||||||
|
.join()
|
||||||
|
.expect("Joining PUS handler thread failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
setup_logger().expect("setting up logging with fern failed");
|
||||||
|
println!("Running OBSW example");
|
||||||
|
#[cfg(not(feature = "dyn_tmtc"))]
|
||||||
|
static_tmtc_pool_main();
|
||||||
|
#[cfg(feature = "dyn_tmtc")]
|
||||||
|
dyn_tmtc_pool_main();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_time(time_provider: &mut CdsTime, timestamp: &mut [u8]) {
|
pub fn update_time(time_provider: &mut CdsTime, timestamp: &mut [u8]) {
|
||||||
time_provider
|
time_provider
|
||||||
.update_from_now()
|
.update_from_now()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
use satrs::action::{ActionRequest, ActionRequestVariant};
|
use satrs::action::{ActionRequest, ActionRequestVariant};
|
||||||
|
use satrs::pool::SharedStaticMemoryPool;
|
||||||
use satrs::pus::action::{
|
use satrs::pus::action::{
|
||||||
ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap,
|
ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap,
|
||||||
};
|
};
|
||||||
@ -9,20 +10,21 @@ use satrs::pus::verification::{
|
|||||||
VerificationReportingProvider, VerificationToken,
|
VerificationReportingProvider, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTmSender, EcssTmtcError,
|
ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
|
||||||
GenericConversionError, MpscTcReceiver, PusPacketHandlingError, PusReplyHandler,
|
EcssTcInVecConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver,
|
||||||
PusServiceHelper, PusTcToRequestConverter,
|
MpscTmAsVecSender, PusPacketHandlingError, PusReplyHandler, PusServiceHelper,
|
||||||
|
PusTcToRequestConverter,
|
||||||
};
|
};
|
||||||
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId};
|
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId};
|
||||||
use satrs_example::config::pus::PUS_ACTION_SERVICE;
|
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
||||||
|
use satrs_example::config::components::PUS_ACTION_SERVICE;
|
||||||
use satrs_example::config::tmtc_err;
|
use satrs_example::config::tmtc_err;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::requests::GenericRequestRouter;
|
use crate::requests::GenericRequestRouter;
|
||||||
use crate::tmtc::sender::TmTcSender;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus,
|
create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus,
|
||||||
@ -205,20 +207,20 @@ impl PusTcToRequestConverter<ActivePusActionRequestStd, ActionRequest> for Actio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_action_service(
|
pub fn create_action_service_static(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: PacketSenderWithSharedPool,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_pool: SharedStaticMemoryPool,
|
||||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
action_router: GenericRequestRouter,
|
action_router: GenericRequestRouter,
|
||||||
reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
|
reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
|
||||||
) -> ActionServiceWrapper {
|
) -> ActionServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
|
||||||
let action_request_handler = PusTargetedRequestService::new(
|
let action_request_handler = PusTargetedRequestService::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_ACTION_SERVICE.id(),
|
PUS_ACTION_SERVICE.id(),
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid),
|
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid),
|
||||||
tc_in_mem_converter,
|
EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
|
||||||
),
|
),
|
||||||
ActionRequestConverter::default(),
|
ActionRequestConverter::default(),
|
||||||
// TODO: Implementation which does not use run-time allocation? Maybe something like
|
// TODO: Implementation which does not use run-time allocation? Maybe something like
|
||||||
@ -233,9 +235,36 @@ pub fn create_action_service(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ActionServiceWrapper {
|
pub fn create_action_service_dynamic(
|
||||||
|
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
|
||||||
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
|
action_router: GenericRequestRouter,
|
||||||
|
reply_receiver: mpsc::Receiver<GenericMessage<ActionReplyPus>>,
|
||||||
|
) -> ActionServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
|
||||||
|
let action_request_handler = PusTargetedRequestService::new(
|
||||||
|
PusServiceHelper::new(
|
||||||
|
PUS_ACTION_SERVICE.id(),
|
||||||
|
pus_action_rx,
|
||||||
|
tm_funnel_tx,
|
||||||
|
create_verification_reporter(PUS_ACTION_SERVICE.id(), PUS_ACTION_SERVICE.apid),
|
||||||
|
EcssTcInVecConverter::default(),
|
||||||
|
),
|
||||||
|
ActionRequestConverter::default(),
|
||||||
|
DefaultActiveActionRequestMap::default(),
|
||||||
|
ActionReplyHandler::default(),
|
||||||
|
action_router,
|
||||||
|
reply_receiver,
|
||||||
|
);
|
||||||
|
ActionServiceWrapper {
|
||||||
|
service: action_request_handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ActionServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
|
||||||
pub(crate) service: PusTargetedRequestService<
|
pub(crate) service: PusTargetedRequestService<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
|
TmSender,
|
||||||
|
TcInMemConverter,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
ActionRequestConverter,
|
ActionRequestConverter,
|
||||||
ActionReplyHandler,
|
ActionReplyHandler,
|
||||||
@ -246,7 +275,9 @@ pub struct ActionServiceWrapper {
|
|||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TargetedPusService for ActionServiceWrapper {
|
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
|
||||||
|
for ActionServiceWrapper<TmSender, TcInMemConverter>
|
||||||
|
{
|
||||||
const SERVICE_ID: u8 = PusServiceId::Action as u8;
|
const SERVICE_ID: u8 = PusServiceId::Action as u8;
|
||||||
const SERVICE_STR: &'static str = "action";
|
const SERVICE_STR: &'static str = "action";
|
||||||
|
|
||||||
@ -272,10 +303,9 @@ mod tests {
|
|||||||
use satrs::pus::test_util::{
|
use satrs::pus::test_util::{
|
||||||
TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
|
TEST_APID, TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
|
||||||
};
|
};
|
||||||
|
use satrs::pus::verification;
|
||||||
use satrs::pus::verification::test_util::TestVerificationReporter;
|
use satrs::pus::verification::test_util::TestVerificationReporter;
|
||||||
use satrs::pus::{verification, EcssTcInVecConverter};
|
|
||||||
use satrs::request::MessageMetadata;
|
use satrs::request::MessageMetadata;
|
||||||
use satrs::tmtc::PacketAsVec;
|
|
||||||
use satrs::ComponentId;
|
use satrs::ComponentId;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
res_code::ResultU16,
|
res_code::ResultU16,
|
||||||
@ -308,10 +338,10 @@ mod tests {
|
|||||||
{
|
{
|
||||||
pub fn new_for_action(owner_id: ComponentId, target_id: ComponentId) -> Self {
|
pub fn new_for_action(owner_id: ComponentId, target_id: ComponentId) -> Self {
|
||||||
let _ = env_logger::builder().is_test(true).try_init();
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
let (tm_funnel_tx, tm_funnel_rx) = mpsc::sync_channel(5);
|
let (tm_funnel_tx, tm_funnel_rx) = mpsc::channel();
|
||||||
let (pus_action_tx, pus_action_rx) = mpsc::channel();
|
let (pus_action_tx, pus_action_rx) = mpsc::channel();
|
||||||
let (action_reply_tx, action_reply_rx) = mpsc::channel();
|
let (action_reply_tx, action_reply_rx) = mpsc::channel();
|
||||||
let (action_req_tx, action_req_rx) = mpsc::sync_channel(10);
|
let (action_req_tx, action_req_rx) = mpsc::channel();
|
||||||
let verif_reporter = TestVerificationReporter::new(owner_id);
|
let verif_reporter = TestVerificationReporter::new(owner_id);
|
||||||
let mut generic_req_router = GenericRequestRouter::default();
|
let mut generic_req_router = GenericRequestRouter::default();
|
||||||
generic_req_router
|
generic_req_router
|
||||||
@ -322,9 +352,9 @@ mod tests {
|
|||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
owner_id,
|
owner_id,
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
TmTcSender::Heap(tm_funnel_tx.clone()),
|
tm_funnel_tx.clone(),
|
||||||
verif_reporter,
|
verif_reporter,
|
||||||
EcssTcInMemConverter::Heap(EcssTcInVecConverter::default()),
|
EcssTcInVecConverter::default(),
|
||||||
),
|
),
|
||||||
ActionRequestConverter::default(),
|
ActionRequestConverter::default(),
|
||||||
DefaultActiveActionRequestMap::default(),
|
DefaultActiveActionRequestMap::default(),
|
||||||
|
@ -1,32 +1,34 @@
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use crate::pus::create_verification_reporter;
|
use crate::pus::create_verification_reporter;
|
||||||
use crate::tmtc::sender::TmTcSender;
|
use satrs::pool::SharedStaticMemoryPool;
|
||||||
use satrs::pus::event_man::EventRequestWithToken;
|
use satrs::pus::event_man::EventRequestWithToken;
|
||||||
use satrs::pus::event_srv::PusEventServiceHandler;
|
use satrs::pus::event_srv::PusEventServiceHandler;
|
||||||
use satrs::pus::verification::VerificationReporter;
|
use satrs::pus::verification::VerificationReporter;
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver,
|
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter,
|
||||||
PartialPusHandlingError, PusServiceHelper,
|
EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver,
|
||||||
|
MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper,
|
||||||
};
|
};
|
||||||
use satrs::spacepackets::ecss::PusServiceId;
|
use satrs::spacepackets::ecss::PusServiceId;
|
||||||
use satrs_example::config::pus::PUS_EVENT_MANAGEMENT;
|
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
||||||
|
use satrs_example::config::components::PUS_EVENT_MANAGEMENT;
|
||||||
|
|
||||||
use super::{DirectPusService, HandlingStatus};
|
use super::{DirectPusService, HandlingStatus};
|
||||||
|
|
||||||
pub fn create_event_service(
|
pub fn create_event_service_static(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: PacketSenderWithSharedPool,
|
||||||
tm_in_pool_converter: EcssTcInMemConverter,
|
tc_pool: SharedStaticMemoryPool,
|
||||||
pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
||||||
) -> EventServiceWrapper {
|
) -> EventServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
|
||||||
let pus_5_handler = PusEventServiceHandler::new(
|
let pus_5_handler = PusEventServiceHandler::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_EVENT_MANAGEMENT.id(),
|
PUS_EVENT_MANAGEMENT.id(),
|
||||||
pus_event_rx,
|
pus_event_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
|
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
|
||||||
tm_in_pool_converter,
|
EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
|
||||||
),
|
),
|
||||||
event_request_tx,
|
event_request_tx,
|
||||||
);
|
);
|
||||||
@ -35,16 +37,34 @@ pub fn create_event_service(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventServiceWrapper {
|
pub fn create_event_service_dynamic(
|
||||||
pub handler: PusEventServiceHandler<
|
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
|
||||||
MpscTcReceiver,
|
pus_event_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
TmTcSender,
|
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
||||||
EcssTcInMemConverter,
|
) -> EventServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
|
||||||
VerificationReporter,
|
let pus_5_handler = PusEventServiceHandler::new(
|
||||||
>,
|
PusServiceHelper::new(
|
||||||
|
PUS_EVENT_MANAGEMENT.id(),
|
||||||
|
pus_event_rx,
|
||||||
|
tm_funnel_tx,
|
||||||
|
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
|
||||||
|
EcssTcInVecConverter::default(),
|
||||||
|
),
|
||||||
|
event_request_tx,
|
||||||
|
);
|
||||||
|
EventServiceWrapper {
|
||||||
|
handler: pus_5_handler,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectPusService for EventServiceWrapper {
|
pub struct EventServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
|
||||||
|
pub handler:
|
||||||
|
PusEventServiceHandler<MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
|
||||||
|
for EventServiceWrapper<TmSender, TcInMemConverter>
|
||||||
|
{
|
||||||
const SERVICE_ID: u8 = PusServiceId::Event as u8;
|
const SERVICE_ID: u8 = PusServiceId::Event as u8;
|
||||||
|
|
||||||
const SERVICE_STR: &'static str = "events";
|
const SERVICE_STR: &'static str = "events";
|
||||||
|
@ -1,26 +1,27 @@
|
|||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId};
|
use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId};
|
||||||
|
use satrs::pool::SharedStaticMemoryPool;
|
||||||
use satrs::pus::verification::{
|
use satrs::pus::verification::{
|
||||||
FailParams, TcStateAccepted, TcStateStarted, VerificationReporter,
|
FailParams, TcStateAccepted, TcStateStarted, VerificationReporter,
|
||||||
VerificationReportingProvider, VerificationToken,
|
VerificationReportingProvider, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken,
|
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken,
|
||||||
EcssTcInMemConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver,
|
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender,
|
||||||
|
EcssTmtcError, GenericConversionError, MpscTcReceiver, MpscTmAsVecSender,
|
||||||
PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter,
|
PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter,
|
||||||
};
|
};
|
||||||
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
use satrs::request::{GenericMessage, UniqueApidTargetId};
|
||||||
use satrs::res_code::ResultU16;
|
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId};
|
use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId};
|
||||||
use satrs_example::config::pus::PUS_HK_SERVICE;
|
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
||||||
|
use satrs_example::config::components::PUS_HK_SERVICE;
|
||||||
use satrs_example::config::{hk_err, tmtc_err};
|
use satrs_example::config::{hk_err, tmtc_err};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler};
|
use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler};
|
||||||
use crate::requests::GenericRequestRouter;
|
use crate::requests::GenericRequestRouter;
|
||||||
use crate::tmtc::sender::TmTcSender;
|
|
||||||
|
|
||||||
use super::{HandlingStatus, PusTargetedRequestService, TargetedPusService};
|
use super::{HandlingStatus, PusTargetedRequestService, TargetedPusService};
|
||||||
|
|
||||||
@ -31,10 +32,8 @@ pub struct HkReply {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum HkReplyVariant {
|
pub enum HkReplyVariant {
|
||||||
Ack,
|
Ack,
|
||||||
Failed(ResultU16),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -70,15 +69,6 @@ impl PusReplyHandler<ActivePusRequestStd, HkReply> for HkReplyHandler {
|
|||||||
.completion_success(tm_sender, started_token, time_stamp)
|
.completion_success(tm_sender, started_token, time_stamp)
|
||||||
.expect("sending completion success verification failed");
|
.expect("sending completion success verification failed");
|
||||||
}
|
}
|
||||||
HkReplyVariant::Failed(failure_code) => {
|
|
||||||
verification_handler
|
|
||||||
.completion_failure(
|
|
||||||
tm_sender,
|
|
||||||
started_token,
|
|
||||||
FailParams::new(time_stamp, &failure_code, &[]),
|
|
||||||
)
|
|
||||||
.expect("sending completion success verification failed");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@ -240,20 +230,20 @@ impl PusTcToRequestConverter<ActivePusRequestStd, HkRequest> for HkRequestConver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_hk_service(
|
pub fn create_hk_service_static(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: PacketSenderWithSharedPool,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_pool: SharedStaticMemoryPool,
|
||||||
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
request_router: GenericRequestRouter,
|
request_router: GenericRequestRouter,
|
||||||
reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>,
|
reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>,
|
||||||
) -> HkServiceWrapper {
|
) -> HkServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
|
||||||
let pus_3_handler = PusTargetedRequestService::new(
|
let pus_3_handler = PusTargetedRequestService::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_HK_SERVICE.id(),
|
PUS_HK_SERVICE.id(),
|
||||||
pus_hk_rx,
|
pus_hk_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid),
|
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid),
|
||||||
tc_in_mem_converter,
|
EcssTcInSharedStoreConverter::new(tc_pool, 2048),
|
||||||
),
|
),
|
||||||
HkRequestConverter::default(),
|
HkRequestConverter::default(),
|
||||||
DefaultActiveRequestMap::default(),
|
DefaultActiveRequestMap::default(),
|
||||||
@ -266,9 +256,36 @@ pub fn create_hk_service(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HkServiceWrapper {
|
pub fn create_hk_service_dynamic(
|
||||||
|
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
|
||||||
|
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
|
request_router: GenericRequestRouter,
|
||||||
|
reply_receiver: mpsc::Receiver<GenericMessage<HkReply>>,
|
||||||
|
) -> HkServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
|
||||||
|
let pus_3_handler = PusTargetedRequestService::new(
|
||||||
|
PusServiceHelper::new(
|
||||||
|
PUS_HK_SERVICE.id(),
|
||||||
|
pus_hk_rx,
|
||||||
|
tm_funnel_tx,
|
||||||
|
create_verification_reporter(PUS_HK_SERVICE.id(), PUS_HK_SERVICE.apid),
|
||||||
|
EcssTcInVecConverter::default(),
|
||||||
|
),
|
||||||
|
HkRequestConverter::default(),
|
||||||
|
DefaultActiveRequestMap::default(),
|
||||||
|
HkReplyHandler::default(),
|
||||||
|
request_router,
|
||||||
|
reply_receiver,
|
||||||
|
);
|
||||||
|
HkServiceWrapper {
|
||||||
|
service: pus_3_handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HkServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
|
||||||
pub(crate) service: PusTargetedRequestService<
|
pub(crate) service: PusTargetedRequestService<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
|
TmSender,
|
||||||
|
TcInMemConverter,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
HkRequestConverter,
|
HkRequestConverter,
|
||||||
HkReplyHandler,
|
HkReplyHandler,
|
||||||
@ -279,7 +296,9 @@ pub struct HkServiceWrapper {
|
|||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TargetedPusService for HkServiceWrapper {
|
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
|
||||||
|
for HkServiceWrapper<TmSender, TcInMemConverter>
|
||||||
|
{
|
||||||
const SERVICE_ID: u8 = PusServiceId::Housekeeping as u8;
|
const SERVICE_ID: u8 = PusServiceId::Housekeeping as u8;
|
||||||
const SERVICE_STR: &'static str = "housekeeping";
|
const SERVICE_STR: &'static str = "housekeeping";
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::requests::GenericRequestRouter;
|
use crate::requests::GenericRequestRouter;
|
||||||
use crate::tmtc::sender::TmTcSender;
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use satrs::pool::PoolAddr;
|
use satrs::pool::PoolAddr;
|
||||||
use satrs::pus::verification::{
|
use satrs::pus::verification::{
|
||||||
@ -7,10 +6,10 @@ use satrs::pus::verification::{
|
|||||||
VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
|
VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConversionProvider,
|
ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter,
|
||||||
EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError,
|
EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, GenericRoutingError,
|
||||||
GenericRoutingError, HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter,
|
HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, PusServiceHelper,
|
||||||
PusServiceHelper, PusTcToRequestConverter, TcInMemory,
|
PusTcToRequestConverter, TcInMemory,
|
||||||
};
|
};
|
||||||
use satrs::queue::{GenericReceiveError, GenericSendError};
|
use satrs::queue::{GenericReceiveError, GenericSendError};
|
||||||
use satrs::request::{Apid, GenericMessage, MessageMetadata};
|
use satrs::request::{Apid, GenericMessage, MessageMetadata};
|
||||||
@ -18,11 +17,11 @@ use satrs::spacepackets::ecss::tc::PusTcReader;
|
|||||||
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
|
||||||
use satrs::tmtc::{PacketAsVec, PacketInPool};
|
use satrs::tmtc::{PacketAsVec, PacketInPool};
|
||||||
use satrs::ComponentId;
|
use satrs::ComponentId;
|
||||||
use satrs_example::config::pus::PUS_ROUTING_SERVICE;
|
use satrs_example::config::components::PUS_ROUTING_SERVICE;
|
||||||
use satrs_example::config::{tmtc_err, CustomPusServiceId};
|
use satrs_example::config::{tmtc_err, CustomPusServiceId};
|
||||||
use satrs_example::TimestampHelper;
|
use satrs_example::TimeStampHelper;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc::{self, Sender};
|
||||||
|
|
||||||
pub mod action;
|
pub mod action;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
@ -41,26 +40,24 @@ pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> Verifi
|
|||||||
|
|
||||||
/// Simple router structure which forwards PUS telecommands to dedicated handlers.
|
/// Simple router structure which forwards PUS telecommands to dedicated handlers.
|
||||||
pub struct PusTcMpscRouter {
|
pub struct PusTcMpscRouter {
|
||||||
pub test_tc_sender: mpsc::SyncSender<EcssTcAndToken>,
|
pub test_tc_sender: Sender<EcssTcAndToken>,
|
||||||
pub event_tc_sender: mpsc::SyncSender<EcssTcAndToken>,
|
pub event_tc_sender: Sender<EcssTcAndToken>,
|
||||||
pub sched_tc_sender: mpsc::SyncSender<EcssTcAndToken>,
|
pub sched_tc_sender: Sender<EcssTcAndToken>,
|
||||||
pub hk_tc_sender: mpsc::SyncSender<EcssTcAndToken>,
|
pub hk_tc_sender: Sender<EcssTcAndToken>,
|
||||||
#[allow(dead_code)]
|
pub action_tc_sender: Sender<EcssTcAndToken>,
|
||||||
pub action_tc_sender: mpsc::SyncSender<EcssTcAndToken>,
|
pub mode_tc_sender: Sender<EcssTcAndToken>,
|
||||||
pub mode_tc_sender: mpsc::SyncSender<EcssTcAndToken>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PusTcDistributor {
|
pub struct PusTcDistributor<TmSender: EcssTmSender> {
|
||||||
#[allow(dead_code)]
|
|
||||||
pub id: ComponentId,
|
pub id: ComponentId,
|
||||||
pub tm_sender: TmTcSender,
|
pub tm_sender: TmSender,
|
||||||
pub verif_reporter: VerificationReporter,
|
pub verif_reporter: VerificationReporter,
|
||||||
pub pus_router: PusTcMpscRouter,
|
pub pus_router: PusTcMpscRouter,
|
||||||
stamp_helper: TimestampHelper,
|
stamp_helper: TimeStampHelper,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTcDistributor {
|
impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
|
||||||
pub fn new(tm_sender: TmTcSender, pus_router: PusTcMpscRouter) -> Self {
|
pub fn new(tm_sender: TmSender, pus_router: PusTcMpscRouter) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: PUS_ROUTING_SERVICE.raw(),
|
id: PUS_ROUTING_SERVICE.raw(),
|
||||||
tm_sender,
|
tm_sender,
|
||||||
@ -69,7 +66,7 @@ impl PusTcDistributor {
|
|||||||
PUS_ROUTING_SERVICE.apid,
|
PUS_ROUTING_SERVICE.apid,
|
||||||
),
|
),
|
||||||
pus_router,
|
pus_router,
|
||||||
stamp_helper: TimestampHelper::default(),
|
stamp_helper: TimeStampHelper::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,6 +266,8 @@ pub trait DirectPusService {
|
|||||||
/// 3. [Self::check_for_request_timeouts] which checks for request timeouts, covering step 7.
|
/// 3. [Self::check_for_request_timeouts] which checks for request timeouts, covering step 7.
|
||||||
pub struct PusTargetedRequestService<
|
pub struct PusTargetedRequestService<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
TcInMemConverter: EcssTcInMemConverter,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
||||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
||||||
@ -278,7 +277,7 @@ pub struct PusTargetedRequestService<
|
|||||||
ReplyType,
|
ReplyType,
|
||||||
> {
|
> {
|
||||||
pub service_helper:
|
pub service_helper:
|
||||||
PusServiceHelper<TcReceiver, TmTcSender, EcssTcInMemConverter, VerificationReporter>,
|
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
|
||||||
pub request_router: GenericRequestRouter,
|
pub request_router: GenericRequestRouter,
|
||||||
pub request_converter: RequestConverter,
|
pub request_converter: RequestConverter,
|
||||||
pub active_request_map: ActiveRequestMap,
|
pub active_request_map: ActiveRequestMap,
|
||||||
@ -289,6 +288,8 @@ pub struct PusTargetedRequestService<
|
|||||||
|
|
||||||
impl<
|
impl<
|
||||||
TcReceiver: EcssTcReceiver,
|
TcReceiver: EcssTcReceiver,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
TcInMemConverter: EcssTcInMemConverter,
|
||||||
VerificationReporter: VerificationReportingProvider,
|
VerificationReporter: VerificationReportingProvider,
|
||||||
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
RequestConverter: PusTcToRequestConverter<ActiveRequestInfo, RequestType, Error = GenericConversionError>,
|
||||||
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
ReplyHandler: PusReplyHandler<ActiveRequestInfo, ReplyType, Error = EcssTmtcError>,
|
||||||
@ -299,6 +300,8 @@ impl<
|
|||||||
>
|
>
|
||||||
PusTargetedRequestService<
|
PusTargetedRequestService<
|
||||||
TcReceiver,
|
TcReceiver,
|
||||||
|
TmSender,
|
||||||
|
TcInMemConverter,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
RequestConverter,
|
RequestConverter,
|
||||||
ReplyHandler,
|
ReplyHandler,
|
||||||
@ -313,8 +316,8 @@ where
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
service_helper: PusServiceHelper<
|
service_helper: PusServiceHelper<
|
||||||
TcReceiver,
|
TcReceiver,
|
||||||
TmTcSender,
|
TmSender,
|
||||||
EcssTcInMemConverter,
|
TcInMemConverter,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
>,
|
>,
|
||||||
request_converter: RequestConverter,
|
request_converter: RequestConverter,
|
||||||
@ -540,7 +543,7 @@ pub(crate) mod tests {
|
|||||||
use satrs::{
|
use satrs::{
|
||||||
pus::{
|
pus::{
|
||||||
verification::test_util::TestVerificationReporter, ActivePusRequestStd,
|
verification::test_util::TestVerificationReporter, ActivePusRequestStd,
|
||||||
ActiveRequestMapProvider, MpscTcReceiver,
|
ActiveRequestMapProvider, EcssTcInVecConverter, MpscTcReceiver,
|
||||||
},
|
},
|
||||||
request::UniqueApidTargetId,
|
request::UniqueApidTargetId,
|
||||||
spacepackets::{
|
spacepackets::{
|
||||||
@ -761,6 +764,8 @@ pub(crate) mod tests {
|
|||||||
> {
|
> {
|
||||||
pub service: PusTargetedRequestService<
|
pub service: PusTargetedRequestService<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
|
MpscTmAsVecSender,
|
||||||
|
EcssTcInVecConverter,
|
||||||
TestVerificationReporter,
|
TestVerificationReporter,
|
||||||
RequestConverter,
|
RequestConverter,
|
||||||
ReplyHandler,
|
ReplyHandler,
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::mode_tree::{ModeNode, ModeParent};
|
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
|
||||||
use satrs_example::config::pus::PUS_MODE_SERVICE;
|
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::requests::GenericRequestRouter;
|
use crate::requests::GenericRequestRouter;
|
||||||
use crate::tmtc::sender::TmTcSender;
|
use satrs::pool::SharedStaticMemoryPool;
|
||||||
use satrs::pus::verification::VerificationReporter;
|
use satrs::pus::verification::VerificationReporter;
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver,
|
DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
|
||||||
PusPacketHandlingError, PusServiceHelper,
|
EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlingError,
|
||||||
|
PusServiceHelper,
|
||||||
};
|
};
|
||||||
use satrs::request::GenericMessage;
|
use satrs::request::GenericMessage;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
@ -34,6 +34,7 @@ use satrs::{
|
|||||||
},
|
},
|
||||||
ComponentId,
|
ComponentId,
|
||||||
};
|
};
|
||||||
|
use satrs_example::config::components::PUS_MODE_SERVICE;
|
||||||
use satrs_example::config::{mode_err, tmtc_err, CustomPusServiceId};
|
use satrs_example::config::{mode_err, tmtc_err, CustomPusServiceId};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -109,7 +110,6 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
|
|||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ModeReply::ModeInfo(_mode_and_submode) => (),
|
|
||||||
};
|
};
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@ -190,13 +190,7 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
|
|||||||
}
|
}
|
||||||
let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..])
|
let mode_and_submode = ModeAndSubmode::from_be_bytes(&tc.user_data()[4..])
|
||||||
.expect("mode and submode extraction failed");
|
.expect("mode and submode extraction failed");
|
||||||
Ok((
|
Ok((active_request, ModeRequest::SetMode(mode_and_submode)))
|
||||||
active_request,
|
|
||||||
ModeRequest::SetMode {
|
|
||||||
mode_and_submode,
|
|
||||||
forced: false,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)),
|
Subservice::TcReadMode => Ok((active_request, ModeRequest::ReadMode)),
|
||||||
Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)),
|
Subservice::TcAnnounceMode => Ok((active_request, ModeRequest::AnnounceMode)),
|
||||||
@ -208,20 +202,20 @@ impl PusTcToRequestConverter<ActivePusRequestStd, ModeRequest> for ModeRequestCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_mode_service(
|
pub fn create_mode_service_static(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: PacketSenderWithSharedPool,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_pool: SharedStaticMemoryPool,
|
||||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
mode_router: GenericRequestRouter,
|
mode_router: GenericRequestRouter,
|
||||||
reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>,
|
reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||||
) -> ModeServiceWrapper {
|
) -> ModeServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
|
||||||
let mode_request_handler = PusTargetedRequestService::new(
|
let mode_request_handler = PusTargetedRequestService::new(
|
||||||
PusServiceHelper::new(
|
PusServiceHelper::new(
|
||||||
PUS_MODE_SERVICE.id(),
|
PUS_MODE_SERVICE.id(),
|
||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid),
|
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid),
|
||||||
tc_in_mem_converter,
|
EcssTcInSharedStoreConverter::new(tc_pool, 2048),
|
||||||
),
|
),
|
||||||
ModeRequestConverter::default(),
|
ModeRequestConverter::default(),
|
||||||
DefaultActiveRequestMap::default(),
|
DefaultActiveRequestMap::default(),
|
||||||
@ -234,9 +228,36 @@ pub fn create_mode_service(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ModeServiceWrapper {
|
pub fn create_mode_service_dynamic(
|
||||||
|
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
|
||||||
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
|
mode_router: GenericRequestRouter,
|
||||||
|
reply_receiver: mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||||
|
) -> ModeServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
|
||||||
|
let mode_request_handler = PusTargetedRequestService::new(
|
||||||
|
PusServiceHelper::new(
|
||||||
|
PUS_MODE_SERVICE.id(),
|
||||||
|
pus_action_rx,
|
||||||
|
tm_funnel_tx,
|
||||||
|
create_verification_reporter(PUS_MODE_SERVICE.id(), PUS_MODE_SERVICE.apid),
|
||||||
|
EcssTcInVecConverter::default(),
|
||||||
|
),
|
||||||
|
ModeRequestConverter::default(),
|
||||||
|
DefaultActiveRequestMap::default(),
|
||||||
|
ModeReplyHandler::new(PUS_MODE_SERVICE.id()),
|
||||||
|
mode_router,
|
||||||
|
reply_receiver,
|
||||||
|
);
|
||||||
|
ModeServiceWrapper {
|
||||||
|
service: mode_request_handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ModeServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
|
||||||
pub(crate) service: PusTargetedRequestService<
|
pub(crate) service: PusTargetedRequestService<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
|
TmSender,
|
||||||
|
TcInMemConverter,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
ModeRequestConverter,
|
ModeRequestConverter,
|
||||||
ModeReplyHandler,
|
ModeReplyHandler,
|
||||||
@ -247,24 +268,9 @@ pub struct ModeServiceWrapper {
|
|||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModeNode for ModeServiceWrapper {
|
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
|
||||||
fn id(&self) -> ComponentId {
|
for ModeServiceWrapper<TmSender, TcInMemConverter>
|
||||||
self.service.service_helper.id()
|
{
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModeParent for ModeServiceWrapper {
|
|
||||||
type Sender = mpsc::SyncSender<GenericMessage<ModeRequest>>;
|
|
||||||
|
|
||||||
fn add_mode_child(&mut self, id: ComponentId, request_sender: Self::Sender) {
|
|
||||||
self.service
|
|
||||||
.request_router
|
|
||||||
.mode_router_map
|
|
||||||
.insert(id, request_sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TargetedPusService for ModeServiceWrapper {
|
|
||||||
const SERVICE_ID: u8 = CustomPusServiceId::Mode as u8;
|
const SERVICE_ID: u8 = CustomPusServiceId::Mode as u8;
|
||||||
const SERVICE_STR: &'static str = "mode";
|
const SERVICE_STR: &'static str = "mode";
|
||||||
|
|
||||||
@ -340,13 +346,7 @@ mod tests {
|
|||||||
let (_active_req, req) = testbench
|
let (_active_req, req) = testbench
|
||||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||||
.expect("conversion has failed");
|
.expect("conversion has failed");
|
||||||
assert_eq!(
|
assert_eq!(req, ModeRequest::SetMode(mode_and_submode));
|
||||||
req,
|
|
||||||
ModeRequest::SetMode {
|
|
||||||
mode_and_submode,
|
|
||||||
forced: false
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2,28 +2,28 @@ use std::sync::mpsc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::pus::create_verification_reporter;
|
use crate::pus::create_verification_reporter;
|
||||||
use crate::tmtc::sender::TmTcSender;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use satrs::pool::{PoolProvider, StaticMemoryPool};
|
use satrs::pool::{PoolProvider, StaticMemoryPool};
|
||||||
use satrs::pus::scheduler::{PusScheduler, TcInfo};
|
use satrs::pus::scheduler::{PusScheduler, TcInfo};
|
||||||
use satrs::pus::scheduler_srv::PusSchedServiceHandler;
|
use satrs::pus::scheduler_srv::PusSchedServiceHandler;
|
||||||
use satrs::pus::verification::VerificationReporter;
|
use satrs::pus::verification::VerificationReporter;
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, MpscTcReceiver,
|
DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter,
|
||||||
PartialPusHandlingError, PusServiceHelper,
|
EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver,
|
||||||
|
MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper,
|
||||||
};
|
};
|
||||||
use satrs::spacepackets::ecss::PusServiceId;
|
use satrs::spacepackets::ecss::PusServiceId;
|
||||||
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool};
|
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool};
|
||||||
use satrs::ComponentId;
|
use satrs::ComponentId;
|
||||||
use satrs_example::config::pus::PUS_SCHED_SERVICE;
|
use satrs_example::config::components::PUS_SCHED_SERVICE;
|
||||||
|
|
||||||
use super::{DirectPusService, HandlingStatus};
|
use super::{DirectPusService, HandlingStatus};
|
||||||
|
|
||||||
pub trait TcReleaseProvider {
|
pub trait TcReleaser {
|
||||||
fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool;
|
fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TcReleaseProvider for PacketSenderWithSharedPool {
|
impl TcReleaser for PacketSenderWithSharedPool {
|
||||||
fn release(
|
fn release(
|
||||||
&mut self,
|
&mut self,
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
@ -48,7 +48,7 @@ impl TcReleaseProvider for PacketSenderWithSharedPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TcReleaseProvider for mpsc::SyncSender<PacketAsVec> {
|
impl TcReleaser for mpsc::Sender<PacketAsVec> {
|
||||||
fn release(
|
fn release(
|
||||||
&mut self,
|
&mut self,
|
||||||
sender_id: ComponentId,
|
sender_id: ComponentId,
|
||||||
@ -65,35 +65,23 @@ impl TcReleaseProvider for mpsc::SyncSender<PacketAsVec> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub struct SchedulingServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
|
||||||
pub enum TcReleaser {
|
{
|
||||||
Static(PacketSenderWithSharedPool),
|
|
||||||
Heap(mpsc::SyncSender<PacketAsVec>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TcReleaseProvider for TcReleaser {
|
|
||||||
fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool {
|
|
||||||
match self {
|
|
||||||
TcReleaser::Static(sender) => sender.release(sender_id, enabled, info, tc),
|
|
||||||
TcReleaser::Heap(sender) => sender.release(sender_id, enabled, info, tc),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SchedulingServiceWrapper {
|
|
||||||
pub pus_11_handler: PusSchedServiceHandler<
|
pub pus_11_handler: PusSchedServiceHandler<
|
||||||
MpscTcReceiver,
|
MpscTcReceiver,
|
||||||
TmTcSender,
|
TmSender,
|
||||||
EcssTcInMemConverter,
|
TcInMemConverter,
|
||||||
VerificationReporter,
|
VerificationReporter,
|
||||||
PusScheduler,
|
PusScheduler,
|
||||||
>,
|
>,
|
||||||
pub sched_tc_pool: StaticMemoryPool,
|
pub sched_tc_pool: StaticMemoryPool,
|
||||||
pub releaser_buf: [u8; 4096],
|
pub releaser_buf: [u8; 4096],
|
||||||
pub tc_releaser: TcReleaser,
|
pub tc_releaser: Box<dyn TcReleaser + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectPusService for SchedulingServiceWrapper {
|
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
|
||||||
|
for SchedulingServiceWrapper<TmSender, TcInMemConverter>
|
||||||
|
{
|
||||||
const SERVICE_ID: u8 = PusServiceId::Verification as u8;
|
const SERVICE_ID: u8 = PusServiceId::Verification as u8;
|
||||||
|
|
||||||
const SERVICE_STR: &'static str = "verification";
|
const SERVICE_STR: &'static str = "verification";
|
||||||
@ -146,7 +134,9 @@ impl DirectPusService for SchedulingServiceWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SchedulingServiceWrapper {
|
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
|
||||||
|
SchedulingServiceWrapper<TmSender, TcInMemConverter>
|
||||||
|
{
|
||||||
pub fn release_tcs(&mut self) {
|
pub fn release_tcs(&mut self) {
|
||||||
let id = self.pus_11_handler.service_helper.id();
|
let id = self.pus_11_handler.service_helper.id();
|
||||||
let releaser = |enabled: bool, info: &TcInfo, tc: &[u8]| -> bool {
|
let releaser = |enabled: bool, info: &TcInfo, tc: &[u8]| -> bool {
|
||||||
@ -172,13 +162,12 @@ impl SchedulingServiceWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_scheduler_service(
|
pub fn create_scheduler_service_static(
|
||||||
tm_sender: TmTcSender,
|
tm_sender: PacketSenderWithSharedPool,
|
||||||
tc_in_mem_converter: EcssTcInMemConverter,
|
tc_releaser: PacketSenderWithSharedPool,
|
||||||
tc_releaser: TcReleaser,
|
|
||||||
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
sched_tc_pool: StaticMemoryPool,
|
sched_tc_pool: StaticMemoryPool,
|
||||||
) -> SchedulingServiceWrapper {
|
) -> SchedulingServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
|
||||||
let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
|
let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
|
||||||
.expect("Creating PUS Scheduler failed");
|
.expect("Creating PUS Scheduler failed");
|
||||||
let pus_11_handler = PusSchedServiceHandler::new(
|
let pus_11_handler = PusSchedServiceHandler::new(
|
||||||
@ -187,7 +176,7 @@ pub fn create_scheduler_service(
|
|||||||
pus_sched_rx,
|
pus_sched_rx,
|
||||||
tm_sender,
|
tm_sender,
|
||||||
create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid),
|
create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid),
|
||||||
tc_in_mem_converter,
|
EcssTcInSharedStoreConverter::new(tc_releaser.shared_packet_store().0.clone(), 2048),
|
||||||
),
|
),
|
||||||
scheduler,
|
scheduler,
|
||||||
);
|
);
|
||||||
@ -195,6 +184,34 @@ pub fn create_scheduler_service(
|
|||||||
pus_11_handler,
|
pus_11_handler,
|
||||||
sched_tc_pool,
|
sched_tc_pool,
|
||||||
releaser_buf: [0; 4096],
|
releaser_buf: [0; 4096],
|
||||||
tc_releaser,
|
tc_releaser: Box::new(tc_releaser),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_scheduler_service_dynamic(
|
||||||
|
tm_funnel_tx: mpsc::Sender<PacketAsVec>,
|
||||||
|
tc_source_sender: mpsc::Sender<PacketAsVec>,
|
||||||
|
pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
|
sched_tc_pool: StaticMemoryPool,
|
||||||
|
) -> SchedulingServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
|
||||||
|
//let sched_srv_receiver =
|
||||||
|
//MpscTcReceiver::new(PUS_SCHED_SERVICE.raw(), "PUS_11_TC_RECV", pus_sched_rx);
|
||||||
|
let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
|
||||||
|
.expect("Creating PUS Scheduler failed");
|
||||||
|
let pus_11_handler = PusSchedServiceHandler::new(
|
||||||
|
PusServiceHelper::new(
|
||||||
|
PUS_SCHED_SERVICE.id(),
|
||||||
|
pus_sched_rx,
|
||||||
|
tm_funnel_tx,
|
||||||
|
create_verification_reporter(PUS_SCHED_SERVICE.id(), PUS_SCHED_SERVICE.apid),
|
||||||
|
EcssTcInVecConverter::default(),
|
||||||
|
),
|
||||||
|
scheduler,
|
||||||
|
);
|
||||||
|
SchedulingServiceWrapper {
|
||||||
|
pus_11_handler,
|
||||||
|
sched_tc_pool,
|
||||||
|
releaser_buf: [0; 4096],
|
||||||
|
tc_releaser: Box::new(tc_source_sender),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user