Compare commits
194 Commits
satrs-v0.1
...
main
Author | SHA1 | Date | |
---|---|---|---|
1f2d6c9474 | |||
9960930339 | |||
cb270964a1 | |||
a45e634214 | |||
9e4132706c | |||
36d889a504 | |||
97a6510af7 | |||
2e5d6a5c41 | |||
29167736db | |||
a4c433a7be | |||
472a8ce0f9 | |||
e753319dac | |||
99dddf36f3 | |||
a819feeaa2 | |||
46ce3fc772 | |||
8d27bdf3bf | |||
3d2a46f044 | |||
1f192af262 | |||
3f78c200ad | |||
d73dfcdd67 | |||
5cae0f7036 | |||
832250d211 | |||
3c3b4349e8 | |||
acf73e93b1 | |||
0b2d4f6187 | |||
f7016b940a | |||
397ecd0c40 | |||
422f2c11ab | |||
37e945fd91 | |||
45379858f0 | |||
7c194ab543 | |||
bca1d7292a | |||
cdcb9cae1c | |||
9dcbd42862 | |||
da05efc16d | |||
e38e25a998 | |||
14b381cf4a | |||
3746e9ebb0 | |||
d2fc783562 | |||
282f799203 | |||
46dbb4309b | |||
42d1257e83 | |||
583f6ce4d2 | |||
408803fe86 | |||
9ffe4d0ae0 | |||
e37061dcf0 | |||
3a2ac11407 | |||
23327a7786 | |||
89d5a1022f | |||
a00c843698 | |||
c586fd7fef | |||
7e78e70a17 | |||
424dfc439c | |||
45eb2f1343 | |||
736eb74e66 | |||
29f71c2a57 | |||
f0d08b65a4 | |||
c7a74a844c | |||
9c60427f89 | |||
958ab9bab6 | |||
312849bddb | |||
b0159a3ba7 | |||
c477739f6d | |||
b7ce039406 | |||
4736d40997 | |||
5ec5124ea3 | |||
5e43259d4f | |||
bfaddd0ebb | |||
423a068736 | |||
8022af1bf2 | |||
acd2260dfd | |||
e5ee698dc4 | |||
e8907c74d4 | |||
536051e05b | |||
701db659e9 | |||
4b8e54b91b | |||
870d60cfd6 | |||
9e62e4292c | |||
b2e77fbc09 | |||
5371928496 | |||
31cddbd99b | |||
7c00e13e70 | |||
aa72063454 | |||
7b37b76695 | |||
ea5d95c12d | |||
c62adbb300 | |||
9242b8a607 | |||
4a27d2605d | |||
8195245481 | |||
f6f7519625 | |||
0f0fbc1a18 | |||
6e55e2ac95 | |||
2f96bfe992 | |||
52aafb3aab | |||
6ce9cb5ead | |||
273f79d1e6 | |||
622221835e | |||
e396ad2e7a | |||
772927d50b | |||
be9a45e55f | |||
eee8a69550 | |||
f7a6d3ce47 | |||
df97a3a93e | |||
42750e08c0 | |||
786671bbd7 | |||
63f37f0917 | |||
8cfe3b81e7 | |||
de50bec562 | |||
39ab9fa27b | |||
1dbc81a8f5 | |||
1ad74ee1d5 | |||
f96fe6bdc0 | |||
d43a8eb571 | |||
0bbada90ef | |||
3375780e00 | |||
de028ed827 | |||
d27ac5dfc9 | |||
c67b7cb93a | |||
f71ba3e8d8 | |||
975cd927f4 | |||
3cc9dd3c48 | |||
0fec994028 | |||
226a134aff | |||
aac59ec7c1 | |||
ce7eb8650f | |||
df2733a176 | |||
344fe6a4c0 | |||
a5941751d7 | |||
977e29894b | |||
dd1417a368 | |||
3195cf5111 | |||
8280c70682 | |||
19c5aa9b83 | |||
713b4e097b | |||
9039c1b59a | |||
746b31ec5d | |||
2318cd4293 | |||
a6b57d3eb9 | |||
bddd3132d4 | |||
6a6ffba754 | |||
d27a41e4de | |||
972bf19188 | |||
9d711d2b73 | |||
d0005cdd63 | |||
f00e6cf50c | |||
128df9a813 | |||
7387be3bc3 | |||
d3fb504545 | |||
ae8e39f626 | |||
ab3d907d4e | |||
3de5954898 | |||
5600aa576c | |||
88793cfa87 | |||
223b637eb8 | |||
cf9b115e1e | |||
eea9b11b39 | |||
f21ab0017e | |||
a7ca00317f | |||
75fda42f4f | |||
faf0f6f6c6 | |||
a690c7720d | |||
b48b5b8caa | |||
238c3a8d43 | |||
de8c0bc13e | |||
012eb82f42 | |||
d26f6cbe27 | |||
82d3215761 | |||
2b80244636 | |||
f1611cd5b8 | |||
808126ee41 | |||
05df24447b | |||
b229360233 | |||
52be26829b | |||
ca2c8aa359 | |||
ba03150178 | |||
4e45bfa7e6 | |||
93c01c8c22 | |||
2d062f3010 | |||
01d9a85976 | |||
fa7cd39f3e | |||
5bb37d8e87 | |||
813e221030 | |||
18cec8bcf0 | |||
d4a122e462 | |||
7af327d077 | |||
3a6cd6712d | |||
c4eba03043 | |||
e9e5c999ec | |||
f14a85cb84 | |||
c3902c2c06 | |||
24f82d4c5e | |||
44ff62e947 | |||
9cf80b44ea | |||
f4bc33aefa |
64
.github/workflows/ci.yml
vendored
Normal file
64
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
name: ci
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- run: cargo check --release
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Run Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install nextest
|
||||||
|
uses: taiki-e/install-action@nextest
|
||||||
|
- run: cargo nextest run --all-features
|
||||||
|
- run: cargo test --doc --all-features
|
||||||
|
|
||||||
|
cross-check:
|
||||||
|
name: Check Cross-Compilation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- armv7-unknown-linux-gnueabihf
|
||||||
|
- thumbv7em-none-eabihf
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
|
||||||
|
- run: cargo check -p satrs --release --target=${{matrix.target}} --no-default-features
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Check formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Check Documentation Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- run: cargo clippy -- -D warnings
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,5 +1,10 @@
|
|||||||
/target
|
target/
|
||||||
|
|
||||||
|
output.log
|
||||||
/Cargo.lock
|
/Cargo.lock
|
||||||
|
output.log
|
||||||
|
|
||||||
|
output.log
|
||||||
|
|
||||||
/.idea/*
|
/.idea/*
|
||||||
!/.idea/runConfigurations
|
!/.idea/runConfigurations
|
||||||
|
@ -4,10 +4,12 @@ members = [
|
|||||||
"satrs",
|
"satrs",
|
||||||
"satrs-mib",
|
"satrs-mib",
|
||||||
"satrs-example",
|
"satrs-example",
|
||||||
|
"satrs-minisim",
|
||||||
"satrs-shared",
|
"satrs-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"satrs-example-stm32f3-disco",
|
"embedded-examples/stm32f3-disco-rtic",
|
||||||
|
"embedded-examples/stm32h7-rtic",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
49
README.md
49
README.md
@ -1,13 +1,23 @@
|
|||||||
<p align="center"> <img src="misc/satrs-logo.png" width="40%"> </p>
|
<p align="center"> <img src="misc/satrs-logo-v2.png" width="40%"> </p>
|
||||||
|
|
||||||
|
[![sat-rs website](https://img.shields.io/badge/sat--rs-website-darkgreen?style=flat)](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/)
|
||||||
|
[![sat-rs book](https://img.shields.io/badge/sat--rs-book-darkgreen?style=flat)](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/)
|
||||||
|
[![Crates.io](https://img.shields.io/crates/v/satrs)](https://crates.io/crates/satrs)
|
||||||
|
[![docs.rs](https://img.shields.io/docsrs/satrs)](https://docs.rs/satrs)
|
||||||
|
|
||||||
sat-rs
|
sat-rs
|
||||||
=========
|
=========
|
||||||
|
|
||||||
This is the repository of the sat-rs framework. Its primary goal is to provide re-usable components
|
This is the repository of the sat-rs library. Its primary goal is to provide re-usable components
|
||||||
to write on-board software for remote systems like rovers or satellites. It is specifically written
|
to write on-board software for remote systems like rovers or satellites. It is specifically written
|
||||||
for the special requirements for these systems. You can find an overview of the project and the
|
for the special requirements for these systems. You can find an overview of the project and the
|
||||||
link to the [more high-level sat-rs book](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/)
|
link to the [more high-level sat-rs book](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/)
|
||||||
at the [IRS documentation website](https://absatsw.irs.uni-stuttgart.de/sat-rs.html).
|
at the [IRS software projects website](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/).
|
||||||
|
|
||||||
|
This is early-stage software. Important features are missing. New releases
|
||||||
|
with breaking changes are released regularly, with all changes documented inside respective
|
||||||
|
changelog files. You should only use this library if your are willing to work in this
|
||||||
|
environment.
|
||||||
|
|
||||||
A lot of the architecture and general design considerations are based on the
|
A lot of the architecture and general design considerations are based on the
|
||||||
[FSFW](https://egit.irs.uni-stuttgart.de/fsfw/fsfw) C++ framework which has flight heritage
|
[FSFW](https://egit.irs.uni-stuttgart.de/fsfw/fsfw) C++ framework which has flight heritage
|
||||||
@ -27,11 +37,19 @@ 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-example-stm32f3-disco`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example-stm32f3-disco):
|
* [`satrs-stm32f3-disco-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32f3-disco-rtic):
|
||||||
Example of a simple example on-board software using sat-rs components on a bare-metal system
|
Example of a simple example using low-level sat-rs components on a bare-metal system
|
||||||
with constrained resources.
|
with constrained resources. This example uses the [RTIC](https://github.com/rtic-rs/rtic)
|
||||||
|
framework on the STM32F3-Discovery device.
|
||||||
|
* [`satrs-stm32h-nucleo-rtic`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/embedded-examples/stm32h7-nucleo-rtic):
|
||||||
|
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`.
|
||||||
|
|
||||||
@ -41,9 +59,24 @@ Each project has its own `CHANGELOG.md`.
|
|||||||
|
|
||||||
* [`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
|
||||||
packet protocol implementations. This repository is re-exported in the
|
packet protocol implementations. This repository is re-exported in the
|
||||||
[`satrs-core`](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad/src/branch/main/satrs-core)
|
[`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
|
||||||
@ -54,5 +87,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-core` crate with coverage. You can
|
After that, you can simply run `coverage.py` to test the `satrs` 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.
|
||||||
|
1
automation/Jenkinsfile
vendored
1
automation/Jenkinsfile
vendored
@ -33,6 +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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Check with all features') {
|
stage('Check with all features') {
|
||||||
|
12
coverage.py
12
coverage.py
@ -18,15 +18,19 @@ def generate_cov_report(open_report: bool, format: str, package: str):
|
|||||||
out_path = "./target/debug/coverage"
|
out_path = "./target/debug/coverage"
|
||||||
if format == "lcov":
|
if format == "lcov":
|
||||||
out_path = "./target/debug/lcov.info"
|
out_path = "./target/debug/lcov.info"
|
||||||
os.system(
|
grcov_cmd = (
|
||||||
f"grcov . -s . --binary-path ./target/debug/ -t {format} --branch --ignore-not-existing "
|
f"grcov . -s . --binary-path ./target/debug/ -t {format} --branch --ignore-not-existing "
|
||||||
f"-o {out_path}"
|
f"-o {out_path}"
|
||||||
)
|
)
|
||||||
|
print(f"Running: {grcov_cmd}")
|
||||||
|
os.system(grcov_cmd)
|
||||||
if format == "lcov":
|
if format == "lcov":
|
||||||
os.system(
|
lcov_cmd = (
|
||||||
"genhtml -o ./target/debug/coverage/ --show-details --highlight --ignore-errors source "
|
"genhtml -o ./target/debug/coverage/ --show-details --highlight --ignore-errors source "
|
||||||
"--legend ./target/debug/lcov.info"
|
"--legend ./target/debug/lcov.info"
|
||||||
)
|
)
|
||||||
|
print(f"Running: {lcov_cmd}")
|
||||||
|
os.system(lcov_cmd)
|
||||||
if open_report:
|
if open_report:
|
||||||
coverage_report_path = os.path.abspath("./target/debug/coverage/index.html")
|
coverage_report_path = os.path.abspath("./target/debug/coverage/index.html")
|
||||||
webbrowser.open_new_tab(coverage_report_path)
|
webbrowser.open_new_tab(coverage_report_path)
|
||||||
@ -43,8 +47,8 @@ def main():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-p",
|
"-p",
|
||||||
"--package",
|
"--package",
|
||||||
choices=["satrs-core"],
|
choices=["satrs", "satrs-minisim", "satrs-example"],
|
||||||
default="satrs-core",
|
default="satrs",
|
||||||
help="Choose project to generate coverage for",
|
help="Choose project to generate coverage for",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
37
embedded-examples/stm32f3-disco-rtic/.cargo/def_config.toml
Normal file
37
embedded-examples/stm32f3-disco-rtic/.cargo/def_config.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# uncomment ONE of these three option to make `cargo run` start a GDB session
|
||||||
|
# which option to pick depends on your system
|
||||||
|
# You can also replace openocd.gdb by jlink.gdb when using a J-Link.
|
||||||
|
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
||||||
|
# runner = "gdb-multiarch -q -x openocd.gdb"
|
||||||
|
# runner = "gdb -q -x openocd.gdb"
|
||||||
|
runner = "probe-rs run --chip STM32F303VCTx"
|
||||||
|
|
||||||
|
rustflags = [
|
||||||
|
"-C", "linker=flip-link",
|
||||||
|
# LLD (shipped with the Rust toolchain) is used as the default linker
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
|
||||||
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
|
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
|
||||||
|
# if you run into problems with LLD switch to the GNU linker by commenting out
|
||||||
|
# this line
|
||||||
|
# "-C", "linker=arm-none-eabi-ld",
|
||||||
|
|
||||||
|
# if you need to link to pre-compiled C libraries provided by a C toolchain
|
||||||
|
# use GCC as the linker by commenting out both lines above and then
|
||||||
|
# uncommenting the three lines below
|
||||||
|
# "-C", "linker=arm-none-eabi-gcc",
|
||||||
|
# "-C", "link-arg=-Wl,-Tlink.x",
|
||||||
|
# "-C", "link-arg=-nostartfiles",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# comment out the following line if you intend to run unit tests on host machine
|
||||||
|
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
/itm.txt
|
/itm.txt
|
||||||
/.cargo/config*
|
/.cargo/config*
|
||||||
|
/.vscode
|
@ -13,18 +13,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-polyfill"
|
name = "atomic-polyfill"
|
||||||
version = "0.1.11"
|
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 = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28"
|
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"critical-section",
|
"critical-section",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.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 = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bare-metal"
|
name = "bare-metal"
|
||||||
@ -55,20 +55,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bxcan"
|
name = "bxcan"
|
||||||
version = "0.6.2"
|
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 = "4b13b4b2ea9ab2ba924063ebb86ad895cb79f4a79bf90f27949eb20c335b30f9"
|
checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"nb 1.0.0",
|
"embedded-hal 0.2.7",
|
||||||
|
"nb 1.1.0",
|
||||||
"vcell",
|
"vcell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.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 = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
@ -87,31 +88,42 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.23"
|
version = "0.4.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cobs"
|
||||||
version = "0.7.6"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70858629a458fdfd39f9675c4dc309411f2a3f83bede76988d81bf1a0ecee9e0"
|
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cobs"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"embedded-hal",
|
"critical-section",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
"volatile-register",
|
"volatile-register",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m-rt"
|
name = "cortex-m-rt"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6d3328b8b5534f0c90acd66b68950f2763b37e0173cac4d8b4937c4a80761f9"
|
checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m-rt-macros",
|
"cortex-m-rt-macros",
|
||||||
]
|
]
|
||||||
@ -124,48 +136,44 @@ checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m-rtic"
|
name = "cortex-m-semihosting"
|
||||||
version = "1.1.3"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6b82f1c39acd6c3a35c2013b6110c20f5bc534522791fabadeed49ccada2dce"
|
checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"cortex-m-rtic-macros",
|
|
||||||
"heapless",
|
|
||||||
"rtic-core",
|
|
||||||
"rtic-monotonic",
|
|
||||||
"version_check",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m-rtic-macros"
|
name = "crc"
|
||||||
version = "1.1.5"
|
version = "3.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e8e9645ef54bec1cf70ac33e9bf9566e6507ab5b41ae6baf3735662194e8607"
|
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-error",
|
"crc-catalog",
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rtic-syntax",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc-catalog"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "critical-section"
|
name = "critical-section"
|
||||||
version = "1.1.1"
|
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 = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52"
|
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.14.2"
|
version = "0.20.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"darling_macro",
|
"darling_macro",
|
||||||
@ -173,26 +181,113 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_core"
|
name = "darling_core"
|
||||||
version = "0.14.2"
|
version = "0.20.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.65",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.14.2"
|
version = "0.20.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.65",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"defmt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-brtt"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2f0ac3635d0c89d12b8101fcb44a7625f5f030a1c0491124b74467eb5a58a78"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"defmt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-macros"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
|
||||||
|
dependencies = [
|
||||||
|
"defmt-parser",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.65",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-parser"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-test"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290966e8c38f94b11884877242de876280d0eab934900e9642d58868e77c5df1"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt",
|
||||||
|
"cortex-m-semihosting",
|
||||||
|
"defmt",
|
||||||
|
"defmt-test-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-test-macros"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.65",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "delegate"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"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.65",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -214,6 +309,12 @@ dependencies = [
|
|||||||
"void",
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-time"
|
name = "embedded-time"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -225,25 +326,31 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enumset"
|
name = "enumset"
|
||||||
version = "1.0.12"
|
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 = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753"
|
checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enumset_derive",
|
"enumset_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enumset_derive"
|
name = "enumset_derive"
|
||||||
version = "0.6.1"
|
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 = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0"
|
checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.65",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@ -252,18 +359,42 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fugit"
|
name = "fugit"
|
||||||
version = "0.3.6"
|
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 = "7ab17bb279def6720d058cb6c052249938e7f99260ab534879281a95367a87e5"
|
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcd",
|
"gcd",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gcd"
|
name = "futures-core"
|
||||||
version = "2.2.0"
|
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 = "a4b1b088ad0a967aa29540456b82fc8903f854775d33f71e9709c4efb3dfbfd2"
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
|
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]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
@ -276,9 +407,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.6"
|
version = "0.14.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
@ -286,29 +417,26 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
version = "0.2.1"
|
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 = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.14.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heapless"
|
name = "heapless"
|
||||||
version = "0.7.16"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743"
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill",
|
|
||||||
"hash32",
|
"hash32",
|
||||||
"rustc_version 0.4.0",
|
|
||||||
"spin",
|
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -320,41 +448,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.2"
|
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 = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itm_logger"
|
|
||||||
version = "0.1.3-pre.0"
|
|
||||||
dependencies = [
|
|
||||||
"cortex-m",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lsm303dlhc"
|
name = "lsm303dlhc"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -362,7 +463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9e5d1a5c290951321d1b0d4a40edd828537de9889134a0e67c5146542ae57706"
|
checksum = "9e5d1a5c290951321d1b0d4a40edd828537de9889134a0e67c5146542ae57706"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cast",
|
"cast",
|
||||||
"embedded-hal",
|
"embedded-hal 0.2.7",
|
||||||
"generic-array 0.11.2",
|
"generic-array 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -372,7 +473,7 @@ version = "1.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc4010833aea396656c2f91ee704d51a6f1329ec2ab56ffd00bfd56f7481ea94"
|
checksum = "bc4010833aea396656c2f91ee704d51a6f1329ec2ab56ffd00bfd56f7481ea94"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array 0.14.6",
|
"generic-array 0.14.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -381,14 +482,14 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nb 1.0.0",
|
"nb 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
@ -414,19 +515,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.46"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-iter"
|
name = "num-iter"
|
||||||
version = "0.1.43"
|
version = "0.1.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
@ -446,27 +546,60 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "panic-itm"
|
name = "num_enum"
|
||||||
version = "0.4.2"
|
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 = "3d577d97d1b31268087b6dddf2470e6794ef5eee87d9dca7fcd0481695391a4c"
|
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||||
|
dependencies = [
|
||||||
|
"num_enum_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum_derive"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.65",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "panic-probe"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4047d9235d1423d66cc97da7d07eddb54d4f154d6c13805c6d0793956f4f25b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
|
"defmt",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.11"
|
version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
@ -477,7 +610,7 @@ dependencies = [
|
|||||||
"proc-macro-error-attr",
|
"proc-macro-error-attr",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -494,31 +627,54 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.49"
|
version = "1.0.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.23"
|
version = "1.0.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtcc"
|
name = "rtcc"
|
||||||
version = "0.3.0"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3623619ce77c09a7d87cf7c61c5c887b9c7dee8805f66af6c4aa5824be4d9930"
|
checksum = "95973c3a0274adc4f3c5b70d2b5b85618d6de9559a6737d3293ecae9a2fc0839"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"bare-metal 1.0.0",
|
||||||
|
"cortex-m",
|
||||||
|
"critical-section",
|
||||||
|
"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]]
|
[[package]]
|
||||||
name = "rtic-core"
|
name = "rtic-core"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -526,21 +682,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtic-monotonic"
|
name = "rtic-macros"
|
||||||
version = "1.0.0"
|
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 = "fb8b0b822d1a366470b9cea83a1d4e788392db763539dc4ba022bcc787fece82"
|
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtic-syntax"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3ad3ae243dd8d0a1b064615f664d4fa7e63929939074c564cbe5efdc4c503065"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.65",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-monotonics"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"cfg-if",
|
||||||
|
"cortex-m",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"fugit",
|
||||||
|
"rtic-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-time"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"futures-util",
|
||||||
|
"rtic-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -558,32 +734,59 @@ 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 = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver 1.0.16",
|
"semver 1.0.23",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sat-rs-example-stm32f-disco"
|
name = "satrs"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "866fcae3b683ccc37b5ad77982483a0ee01d5dc408dea5aad2117ad404b60fe1"
|
||||||
|
dependencies = [
|
||||||
|
"cobs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crc",
|
||||||
|
"defmt",
|
||||||
|
"delegate",
|
||||||
|
"derive-new",
|
||||||
|
"num-traits",
|
||||||
|
"num_enum",
|
||||||
|
"paste",
|
||||||
|
"satrs-shared",
|
||||||
|
"smallvec",
|
||||||
|
"spacepackets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "satrs-shared"
|
||||||
|
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.2.3 (git+https://github.com/robamu/cobs.rs.git?branch=all_features)",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"cortex-m-rtic",
|
"cortex-m-semihosting",
|
||||||
"embedded-hal",
|
"defmt",
|
||||||
|
"defmt-brtt",
|
||||||
|
"defmt-test",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
"enumset",
|
"enumset",
|
||||||
"heapless",
|
"heapless",
|
||||||
"itm_logger",
|
"panic-probe",
|
||||||
"panic-itm",
|
"rtic",
|
||||||
|
"rtic-monotonics",
|
||||||
|
"satrs",
|
||||||
"stm32f3-discovery",
|
"stm32f3-discovery",
|
||||||
"stm32f3xx-hal",
|
"stm32f3xx-hal",
|
||||||
"systick-monotonic",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -595,9 +798,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.16"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver-parser"
|
name = "semver-parser"
|
||||||
@ -607,17 +810,28 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slice-group-by"
|
name = "slice-group-by"
|
||||||
version = "0.3.0"
|
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 = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec"
|
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "smallvec"
|
||||||
version = "0.9.4"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spacepackets"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e85574d113a06312010c0ba51aadccd4ba2806231ebe9a49fc6473d0534d8696"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"crc",
|
||||||
|
"defmt",
|
||||||
|
"delegate",
|
||||||
|
"num-traits",
|
||||||
|
"num_enum",
|
||||||
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -639,9 +853,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32f3"
|
name = "stm32f3"
|
||||||
version = "0.14.0"
|
version = "0.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "265cda62ac13307414de4aca58dbbbd8038ddba85cffbb335823aa216f2e3200"
|
checksum = "b28b37228ef3fa47956af38c6abd756e912f244c1657f14e66d42fc8d74ea96f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
"bare-metal 1.0.0",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
@ -651,7 +865,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32f3-discovery"
|
name = "stm32f3-discovery"
|
||||||
version = "0.8.0-pre.0"
|
version = "0.8.0-alpha.0"
|
||||||
|
source = "git+https://github.com/robamu/stm32f3-discovery?branch=complete-dma-update-hal#5ccacae07ceff02d7d3649df67a6a0ba2a144752"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accelerometer",
|
"accelerometer",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
@ -663,20 +878,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32f3xx-hal"
|
name = "stm32f3xx-hal"
|
||||||
version = "0.9.1"
|
version = "0.11.0-alpha.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/robamu/stm32f3xx-hal?branch=complete-dma-update#04fc76b7912649c84b57bd0ab803ea3ccf2aadae"
|
||||||
checksum = "4e422c5c044e8f3a068b1e14b83c071449e27c9d4bc0e24f972b552d79f2be03"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
|
||||||
"bxcan",
|
"bxcan",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
|
"critical-section",
|
||||||
"embedded-dma",
|
"embedded-dma",
|
||||||
"embedded-hal",
|
"embedded-hal 0.2.7",
|
||||||
"embedded-time",
|
"embedded-time",
|
||||||
"enumset",
|
"enumset",
|
||||||
"nb 1.0.0",
|
"nb 1.1.0",
|
||||||
|
"num-traits",
|
||||||
"paste",
|
"paste",
|
||||||
"rtcc",
|
"rtcc",
|
||||||
"slice-group-by",
|
"slice-group-by",
|
||||||
@ -691,14 +906,14 @@ 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 = "90a4adc8cbd1726249b161898e48e0f3f1ce74d34dc784cbbc98fba4ed283fbf"
|
checksum = "90a4adc8cbd1726249b161898e48e0f3f1ce74d34dc784cbbc98fba4ed283fbf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-hal",
|
"embedded-hal 0.2.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.107"
|
version = "1.0.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -706,27 +921,47 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "systick-monotonic"
|
name = "syn"
|
||||||
version = "1.0.1"
|
version = "2.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67fb822d5c615a0ae3a4795ee5b1d06381c7faf488d861c0a4fa8e6a88d5ff84"
|
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cortex-m",
|
"proc-macro2",
|
||||||
"fugit",
|
"quote",
|
||||||
"rtic-monotonic",
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.65",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.6"
|
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 = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usb-device"
|
name = "usb-device"
|
||||||
@ -754,9 +989,30 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "volatile-register"
|
name = "volatile-register"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
|
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vcell",
|
"vcell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.65",
|
||||||
|
]
|
84
embedded-examples/stm32f3-disco-rtic/Cargo.toml
Normal file
84
embedded-examples/stm32f3-disco-rtic/Cargo.toml
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
[package]
|
||||||
|
name = "satrs-stm32f3-disco-rtic"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
default-run = "satrs-stm32f3-disco-rtic"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
defmt = "0.3"
|
||||||
|
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
||||||
|
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
|
embedded-hal = "0.2.7"
|
||||||
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
enumset = "1"
|
||||||
|
heapless = "0.8"
|
||||||
|
|
||||||
|
[dependencies.rtic]
|
||||||
|
version = "2"
|
||||||
|
features = ["thumbv7-backend"]
|
||||||
|
|
||||||
|
[dependencies.rtic-monotonics]
|
||||||
|
version = "1"
|
||||||
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
|
[dependencies.cobs]
|
||||||
|
git = "https://github.com/robamu/cobs.rs.git"
|
||||||
|
branch = "all_features"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.stm32f3xx-hal]
|
||||||
|
git = "https://github.com/robamu/stm32f3xx-hal"
|
||||||
|
version = "0.11.0-alpha.0"
|
||||||
|
features = ["stm32f303xc", "rt", "enumset"]
|
||||||
|
branch = "complete-dma-update"
|
||||||
|
# Can be used in workspace to develop and update HAL
|
||||||
|
# path = "../stm32f3xx-hal"
|
||||||
|
|
||||||
|
[dependencies.stm32f3-discovery]
|
||||||
|
git = "https://github.com/robamu/stm32f3-discovery"
|
||||||
|
version = "0.8.0-alpha.0"
|
||||||
|
branch = "complete-dma-update-hal"
|
||||||
|
# Can be used in workspace to develop and update BSP
|
||||||
|
# path = "../stm32f3-discovery"
|
||||||
|
|
||||||
|
[dependencies.satrs]
|
||||||
|
# path = "satrs"
|
||||||
|
version = "0.2"
|
||||||
|
default-features = false
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
defmt-test = "0.3"
|
||||||
|
|
||||||
|
# cargo test
|
||||||
|
[profile.test]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
opt-level = "s" # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = "s" # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
# cargo test --release
|
||||||
|
[profile.bench]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = "s" # <-
|
||||||
|
overflow-checks = false # <-
|
114
embedded-examples/stm32f3-disco-rtic/README.md
Normal file
114
embedded-examples/stm32f3-disco-rtic/README.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
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)
|
||||||
|
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 STM32F3-Discovery device was picked because it is a cheap Cortex-M4 based device which is also
|
||||||
|
used by the [Rust Embedded Book](https://docs.rust-embedded.org/book/intro/hardware.html) and the
|
||||||
|
[Rust Discovery](https://docs.rust-embedded.org/discovery/f3discovery/) book as an introduction
|
||||||
|
to embedded Rust.
|
||||||
|
|
||||||
|
## 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 STM32F303VCTx
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging with VS Code
|
||||||
|
|
||||||
|
The STM32F3-Discovery comes with an on-board ST-Link so all that is required to flash and debug
|
||||||
|
the board is a Mini-USB cable. The code in this repository was debugged using [`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.
|
38601
embedded-examples/stm32f3-disco-rtic/STM32F303.svd
Normal file
38601
embedded-examples/stm32f3-disco-rtic/STM32F303.svd
Normal file
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,9 @@ break main
|
|||||||
# # send captured ITM to the file itm.fifo
|
# # send captured ITM to the file itm.fifo
|
||||||
# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
|
# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
|
||||||
# # 8000000 must match the core clock frequency
|
# # 8000000 must match the core clock frequency
|
||||||
monitor tpiu config internal itm.txt uart off 8000000
|
# # 2000000 is the frequency of the SWO pin. This was added for newer
|
||||||
|
# openocd versions like v0.12.0.
|
||||||
|
# monitor tpiu config internal itm.txt uart off 8000000 2000000
|
||||||
|
|
||||||
# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
|
# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
|
||||||
# # 8000000 must match the core clock frequency
|
# # 8000000 must match the core clock frequency
|
||||||
@ -32,7 +34,7 @@ monitor tpiu config internal itm.txt uart off 8000000
|
|||||||
# monitor tpiu config external uart off 8000000 2000000
|
# monitor tpiu config external uart off 8000000 2000000
|
||||||
|
|
||||||
# # enable ITM port 0
|
# # enable ITM port 0
|
||||||
monitor itm port 0 on
|
# monitor itm port 0 on
|
||||||
|
|
||||||
load
|
load
|
||||||
|
|
@ -1,9 +1,8 @@
|
|||||||
__pycache__
|
|
||||||
|
|
||||||
/venv
|
/venv
|
||||||
|
/.tmtc-history.txt
|
||||||
/log
|
/log
|
||||||
/.idea/*
|
/.idea/*
|
||||||
!/.idea/runConfigurations
|
!/.idea/runConfigurations
|
||||||
|
|
||||||
/seqcnt.txt
|
/seqcnt.txt
|
||||||
/.tmtc-history.txt
|
/tmtc_conf.json
|
@ -1,20 +1,22 @@
|
|||||||
#!/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 struct
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from typing import Any, Optional, cast
|
||||||
from prompt_toolkit.history import History
|
from prompt_toolkit.history import FileHistory, History
|
||||||
from prompt_toolkit.history import FileHistory
|
from spacepackets.ecss.tm import CdsShortTimestamp
|
||||||
|
|
||||||
import tmtccmd
|
import tmtccmd
|
||||||
from spacepackets.ecss import PusTelemetry, PusVerificator
|
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusTm, 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
|
||||||
from spacepackets.ccsds.time import CdsShortTimestamp
|
|
||||||
|
|
||||||
from tmtccmd import TcHandlerBase, ProcedureParamsWrapper
|
from tmtccmd import TcHandlerBase, ProcedureParamsWrapper
|
||||||
from tmtccmd.core.base import BackendRequest
|
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.pus import VerificationWrapper
|
||||||
from tmtccmd.tmtc import CcsdsTmHandler, SpecificApidHandlerBase
|
from tmtccmd.tmtc import CcsdsTmHandler, SpecificApidHandlerBase
|
||||||
from tmtccmd.com import ComInterface
|
from tmtccmd.com import ComInterface
|
||||||
@ -25,8 +27,8 @@ from tmtccmd.config import (
|
|||||||
HookBase,
|
HookBase,
|
||||||
params_to_procedure_conversion,
|
params_to_procedure_conversion,
|
||||||
)
|
)
|
||||||
|
from tmtccmd.config.com import SerialCfgWrapper
|
||||||
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
||||||
from tmtccmd.logging import add_colorlog_console_logger
|
|
||||||
from tmtccmd.logging.pus import (
|
from tmtccmd.logging.pus import (
|
||||||
RegularTmtcLogWrapper,
|
RegularTmtcLogWrapper,
|
||||||
RawTmtcTimedLogWrapper,
|
RawTmtcTimedLogWrapper,
|
||||||
@ -39,21 +41,19 @@ from tmtccmd.tmtc import (
|
|||||||
FeedWrapper,
|
FeedWrapper,
|
||||||
SendCbParams,
|
SendCbParams,
|
||||||
DefaultPusQueueHelper,
|
DefaultPusQueueHelper,
|
||||||
QueueWrapper,
|
|
||||||
)
|
)
|
||||||
|
from tmtccmd.pus.s5_fsfw_event import Service5Tm
|
||||||
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
|
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
|
||||||
from tmtccmd.util.obj_id import ObjectIdDictT
|
from tmtccmd.util.obj_id import ObjectIdDictT
|
||||||
|
|
||||||
|
|
||||||
import pus_tc
|
|
||||||
from common import EXAMPLE_PUS_APID, TM_PACKET_IDS, EventU32
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger()
|
_LOGGER = logging.getLogger()
|
||||||
|
|
||||||
|
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=json_cfg_path)
|
super().__init__(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 (
|
||||||
@ -65,14 +65,20 @@ class SatRsConfigHook(HookBase):
|
|||||||
cfg = create_com_interface_cfg_default(
|
cfg = create_com_interface_cfg_default(
|
||||||
com_if_key=com_if_key,
|
com_if_key=com_if_key,
|
||||||
json_cfg_path=self.cfg_path,
|
json_cfg_path=self.cfg_path,
|
||||||
space_packet_ids=TM_PACKET_IDS,
|
space_packet_ids=None,
|
||||||
)
|
)
|
||||||
assert cfg is not 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)
|
return create_com_interface_default(cfg)
|
||||||
|
|
||||||
def get_command_definitions(self) -> CmdTreeNode:
|
def get_command_definitions(self) -> CmdTreeNode:
|
||||||
"""This function should return the root node of the command definition tree."""
|
"""This function should return the root node of the command definition tree."""
|
||||||
return pus_tc.create_cmd_definition_tree()
|
return create_cmd_definition_tree()
|
||||||
|
|
||||||
def get_cmd_history(self) -> Optional[History]:
|
def get_cmd_history(self) -> Optional[History]:
|
||||||
"""Optionlly return a history class for the past command paths which will be used
|
"""Optionlly return a history class for the past command paths which will be used
|
||||||
@ -85,6 +91,13 @@ class SatRsConfigHook(HookBase):
|
|||||||
return 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):
|
class PusHandler(SpecificApidHandlerBase):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -97,17 +110,20 @@ class PusHandler(SpecificApidHandlerBase):
|
|||||||
self.raw_logger = raw_logger
|
self.raw_logger = raw_logger
|
||||||
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):
|
||||||
try:
|
try:
|
||||||
pus_tm = PusTelemetry.unpack(packet, time_reader=CdsShortTimestamp.empty())
|
pus_tm = PusTm.unpack(
|
||||||
|
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}")
|
||||||
raise e
|
raise e
|
||||||
service = pus_tm.service
|
service = pus_tm.service
|
||||||
|
tm_packet = None
|
||||||
if service == 1:
|
if service == 1:
|
||||||
tm_packet = Service1Tm.unpack(
|
tm_packet = Service1Tm.unpack(
|
||||||
data=packet, params=UnpackParams(CdsShortTimestamp.empty(), 1, 2)
|
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 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:
|
||||||
@ -121,48 +137,39 @@ class PusHandler(SpecificApidHandlerBase):
|
|||||||
else:
|
else:
|
||||||
self.verif_wrapper.log_to_console(tm_packet, res)
|
self.verif_wrapper.log_to_console(tm_packet, res)
|
||||||
self.verif_wrapper.log_to_file(tm_packet, res)
|
self.verif_wrapper.log_to_file(tm_packet, res)
|
||||||
elif 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, time_reader=CdsShortTimestamp.empty())
|
pus_tm = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
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(json_str)
|
_LOGGER.info("received JSON string: " + json_str.decode("utf-8"))
|
||||||
elif service == 5:
|
if service == 5:
|
||||||
tm_packet = PusTelemetry.unpack(
|
tm_packet = Service5Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
packet, time_reader=CdsShortTimestamp.empty()
|
if service == 17:
|
||||||
)
|
tm_packet = Service17Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
src_data = tm_packet.source_data
|
|
||||||
event_u32 = EventU32.unpack(src_data)
|
|
||||||
_LOGGER.info(f"Received event packet. 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, time_reader=CdsShortTimestamp.empty()
|
|
||||||
)
|
|
||||||
if tm_packet.subservice == 2:
|
if tm_packet.subservice == 2:
|
||||||
self.file_logger.info("Received Ping Reply TM[17,2]")
|
|
||||||
_LOGGER.info("Received Ping Reply TM[17,2]")
|
_LOGGER.info("Received Ping Reply TM[17,2]")
|
||||||
else:
|
else:
|
||||||
self.file_logger.info(
|
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
|
||||||
)
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
||||||
)
|
)
|
||||||
else:
|
if tm_packet is None:
|
||||||
_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(
|
tm_packet = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
packet, time_reader=CdsShortTimestamp.empty()
|
|
||||||
)
|
|
||||||
self.raw_logger.log_tm(pus_tm)
|
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):
|
class TcHandler(TcHandlerBase):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -174,9 +181,9 @@ class TcHandler(TcHandlerBase):
|
|||||||
self.verif_wrapper = verif_wrapper
|
self.verif_wrapper = verif_wrapper
|
||||||
self.queue_helper = DefaultPusQueueHelper(
|
self.queue_helper = DefaultPusQueueHelper(
|
||||||
queue_wrapper=QueueWrapper.empty(),
|
queue_wrapper=QueueWrapper.empty(),
|
||||||
tc_sched_timestamp_len=CdsShortTimestamp.TIMESTAMP_SIZE,
|
tc_sched_timestamp_len=7,
|
||||||
seq_cnt_provider=seq_count_provider,
|
seq_cnt_provider=seq_count_provider,
|
||||||
pus_verificator=self.verif_wrapper.pus_verificator,
|
pus_verificator=verif_wrapper.pus_verificator,
|
||||||
default_pus_apid=EXAMPLE_PUS_APID,
|
default_pus_apid=EXAMPLE_PUS_APID,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -185,6 +192,10 @@ class TcHandler(TcHandlerBase):
|
|||||||
if entry_helper.is_tc:
|
if entry_helper.is_tc:
|
||||||
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
||||||
pus_tc_wrapper = entry_helper.to_pus_tc_entry()
|
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()
|
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
||||||
_LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
_LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
||||||
send_params.com_if.send(raw_tc)
|
send_params.com_if.send(raw_tc)
|
||||||
@ -193,17 +204,38 @@ 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.DEFAULT:
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
def_proc = info.to_def_procedure()
|
def_proc = info.to_tree_commanding_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.DEFAULT:
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
def_proc = info.to_def_procedure()
|
def_proc = info.to_tree_commanding_procedure()
|
||||||
assert def_proc.cmd_path is not None
|
cmd_path = def_proc.cmd_path
|
||||||
pus_tc.pack_pus_telecommands(q, 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():
|
def main():
|
@ -1,2 +1,2 @@
|
|||||||
tmtccmd == 4.0.0a0
|
tmtccmd == 8.0.1
|
||||||
# -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,17 +1,15 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
use satrs_stm32f3_disco_rtic as _;
|
||||||
|
|
||||||
extern crate panic_itm;
|
use stm32f3_discovery::leds::Leds;
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
|
||||||
|
|
||||||
use stm32f3_discovery::stm32f3xx_hal::delay::Delay;
|
use stm32f3_discovery::stm32f3xx_hal::delay::Delay;
|
||||||
use stm32f3_discovery::stm32f3xx_hal::{pac, prelude::*};
|
use stm32f3_discovery::stm32f3xx_hal::{pac, prelude::*};
|
||||||
use stm32f3_discovery::leds::Leds;
|
|
||||||
use stm32f3_discovery::switch_hal::{OutputSwitch, ToggleableOutputSwitch};
|
use stm32f3_discovery::switch_hal::{OutputSwitch, ToggleableOutputSwitch};
|
||||||
|
|
||||||
#[entry]
|
#[cortex_m_rt::entry]
|
||||||
fn main()-> ! {
|
fn main() -> ! {
|
||||||
|
defmt::println!("STM32F3 Discovery Blinky");
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
let mut rcc = dp.RCC.constrain();
|
let mut rcc = dp.RCC.constrain();
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
@ -30,49 +28,49 @@ fn main()-> ! {
|
|||||||
gpioe.pe14,
|
gpioe.pe14,
|
||||||
gpioe.pe15,
|
gpioe.pe15,
|
||||||
&mut gpioe.moder,
|
&mut gpioe.moder,
|
||||||
&mut gpioe.otyper
|
&mut gpioe.otyper,
|
||||||
);
|
);
|
||||||
let delay_ms = 200u16;
|
let delay_ms = 200u16;
|
||||||
loop {
|
loop {
|
||||||
leds.ld3.toggle().ok();
|
leds.ld3_n.toggle().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld3.toggle().ok();
|
leds.ld3_n.toggle().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
|
|
||||||
//explicit on/off
|
//explicit on/off
|
||||||
leds.ld4.on().ok();
|
leds.ld4_nw.on().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld4.off().ok();
|
leds.ld4_nw.off().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
|
|
||||||
leds.ld5.on().ok();
|
leds.ld5_ne.on().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld5.off().ok();
|
leds.ld5_ne.off().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
|
|
||||||
leds.ld6.on().ok();
|
leds.ld6_w.on().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld6.off().ok();
|
leds.ld6_w.off().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
|
|
||||||
leds.ld7.on().ok();
|
leds.ld7_e.on().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld7.off().ok();
|
leds.ld7_e.off().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
|
|
||||||
leds.ld8.on().ok();
|
leds.ld8_sw.on().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld8.off().ok();
|
leds.ld8_sw.off().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
|
|
||||||
leds.ld9.on().ok();
|
leds.ld9_se.on().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld9.off().ok();
|
leds.ld9_se.off().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
|
|
||||||
leds.ld10.on().ok();
|
leds.ld10_s.on().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
leds.ld10.off().ok();
|
leds.ld10_s.off().ok();
|
||||||
delay.delay_ms(delay_ms);
|
delay.delay_ms(delay_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
51
embedded-examples/stm32f3-disco-rtic/src/lib.rs
Normal file
51
embedded-examples/stm32f3-disco-rtic/src/lib.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
|
||||||
|
use defmt_brtt as _; // global logger
|
||||||
|
|
||||||
|
use stm32f3xx_hal as _; // memory layout
|
||||||
|
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||||
|
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||||
|
#[defmt::panic_handler]
|
||||||
|
fn panic() -> ! {
|
||||||
|
cortex_m::asm::udf()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Terminates the application and makes a semihosting-capable debug tool exit
|
||||||
|
/// with status code 0.
|
||||||
|
pub fn exit() -> ! {
|
||||||
|
loop {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardfault handler.
|
||||||
|
///
|
||||||
|
/// Terminates the application and makes a semihosting-capable debug tool exit
|
||||||
|
/// with an error. This seems better than the default, which is to spin in a
|
||||||
|
/// loop.
|
||||||
|
#[cortex_m_rt::exception]
|
||||||
|
unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||||
|
loop {
|
||||||
|
debug::exit(debug::EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used
|
||||||
|
// once within a crate. the module can be in any file but there can only be at most
|
||||||
|
// one `#[tests]` module in this library crate
|
||||||
|
#[cfg(test)]
|
||||||
|
#[defmt_test::tests]
|
||||||
|
mod unit_tests {
|
||||||
|
use defmt::assert;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert!(true)
|
||||||
|
}
|
||||||
|
}
|
684
embedded-examples/stm32f3-disco-rtic/src/main.rs
Normal file
684
embedded-examples/stm32f3-disco-rtic/src/main.rs
Normal file
@ -0,0 +1,684 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
use satrs::pus::verification::{
|
||||||
|
FailParams, TcStateAccepted, VerificationReportCreator, VerificationToken,
|
||||||
|
};
|
||||||
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
|
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
|
use satrs::spacepackets::ecss::EcssEnumU16;
|
||||||
|
use satrs::spacepackets::CcsdsPacket;
|
||||||
|
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
||||||
|
// global logger + panicking-behavior + memory layout
|
||||||
|
use satrs_stm32f3_disco_rtic as _;
|
||||||
|
|
||||||
|
use rtic::app;
|
||||||
|
|
||||||
|
use heapless::{mpmc::Q8, Vec};
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use rtic_monotonics::systick::fugit::{MillisDurationU32, TimerInstantU32};
|
||||||
|
use rtic_monotonics::systick::ExtU32;
|
||||||
|
use satrs::seq_count::SequenceCountProviderCore;
|
||||||
|
use satrs::spacepackets::{ecss::PusPacket, ecss::WritablePusPacket};
|
||||||
|
use stm32f3xx_hal::dma::dma1;
|
||||||
|
use stm32f3xx_hal::gpio::{PushPull, AF7, PA2, PA3};
|
||||||
|
use stm32f3xx_hal::pac::USART2;
|
||||||
|
use stm32f3xx_hal::serial::{Rx, RxEvent, Serial, SerialDmaRx, SerialDmaTx, Tx, TxEvent};
|
||||||
|
|
||||||
|
const UART_BAUD: u32 = 115200;
|
||||||
|
const DEFAULT_BLINK_FREQ_MS: u32 = 1000;
|
||||||
|
const TX_HANDLER_FREQ_MS: u32 = 20;
|
||||||
|
const MIN_DELAY_BETWEEN_TX_PACKETS_MS: u32 = 5;
|
||||||
|
const MAX_TC_LEN: usize = 128;
|
||||||
|
const MAX_TM_LEN: usize = 128;
|
||||||
|
pub const PUS_APID: u16 = 0x02;
|
||||||
|
|
||||||
|
type TxType = Tx<USART2, PA2<AF7<PushPull>>>;
|
||||||
|
type RxType = Rx<USART2, PA3<AF7<PushPull>>>;
|
||||||
|
type InstantFugit = TimerInstantU32<1000>;
|
||||||
|
type TxDmaTransferType = SerialDmaTx<&'static [u8], dma1::C7, TxType>;
|
||||||
|
type RxDmaTransferType = SerialDmaRx<&'static mut [u8], dma1::C6, RxType>;
|
||||||
|
|
||||||
|
// This is the predictable maximum overhead of the COBS encoding scheme.
|
||||||
|
// It is simply the maximum packet lenght dividied by 254 rounded up.
|
||||||
|
const COBS_TC_OVERHEAD: usize = (MAX_TC_LEN + 254 - 1) / 254;
|
||||||
|
const COBS_TM_OVERHEAD: usize = (MAX_TM_LEN + 254 - 1) / 254;
|
||||||
|
|
||||||
|
const TC_BUF_LEN: usize = MAX_TC_LEN + COBS_TC_OVERHEAD;
|
||||||
|
const TM_BUF_LEN: usize = MAX_TC_LEN + COBS_TM_OVERHEAD;
|
||||||
|
|
||||||
|
// This is a static buffer which should ONLY (!) be used as the TX DMA
|
||||||
|
// transfer buffer.
|
||||||
|
static mut DMA_TX_BUF: [u8; TM_BUF_LEN] = [0; TM_BUF_LEN];
|
||||||
|
// This is a static buffer which should ONLY (!) be used as the RX DMA
|
||||||
|
// transfer buffer.
|
||||||
|
static mut DMA_RX_BUF: [u8; TC_BUF_LEN] = [0; TC_BUF_LEN];
|
||||||
|
|
||||||
|
type TmPacket = Vec<u8, MAX_TM_LEN>;
|
||||||
|
type TcPacket = Vec<u8, MAX_TC_LEN>;
|
||||||
|
|
||||||
|
static TM_REQUESTS: Q8<TmPacket> = Q8::new();
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicU16, Ordering};
|
||||||
|
|
||||||
|
pub struct SeqCountProviderAtomicRef {
|
||||||
|
atomic: AtomicU16,
|
||||||
|
ordering: Ordering,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeqCountProviderAtomicRef {
|
||||||
|
pub const fn new(ordering: Ordering) -> Self {
|
||||||
|
Self {
|
||||||
|
atomic: AtomicU16::new(0),
|
||||||
|
ordering,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SequenceCountProviderCore<u16> for SeqCountProviderAtomicRef {
|
||||||
|
fn get(&self) -> u16 {
|
||||||
|
self.atomic.load(self.ordering)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increment(&self) {
|
||||||
|
self.atomic.fetch_add(1, self.ordering);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_and_increment(&self) -> u16 {
|
||||||
|
self.atomic.fetch_add(1, self.ordering)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SEQ_COUNT_PROVIDER: SeqCountProviderAtomicRef =
|
||||||
|
SeqCountProviderAtomicRef::new(Ordering::Relaxed);
|
||||||
|
|
||||||
|
pub struct TxIdle {
|
||||||
|
tx: TxType,
|
||||||
|
dma_channel: dma1::C7,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, defmt::Format)]
|
||||||
|
pub enum TmSendError {
|
||||||
|
ByteConversion(ByteConversionError),
|
||||||
|
Queue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ByteConversionError> for TmSendError {
|
||||||
|
fn from(value: ByteConversionError) -> Self {
|
||||||
|
Self::ByteConversion(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_tm(tm_creator: PusTmCreator) -> Result<(), TmSendError> {
|
||||||
|
if tm_creator.len_written() > MAX_TM_LEN {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall {
|
||||||
|
expected: tm_creator.len_written(),
|
||||||
|
found: MAX_TM_LEN,
|
||||||
|
}
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
let mut tm_vec = TmPacket::new();
|
||||||
|
tm_vec
|
||||||
|
.resize(tm_creator.len_written(), 0)
|
||||||
|
.expect("vec resize failed");
|
||||||
|
tm_creator.write_to_bytes(tm_vec.as_mut_slice())?;
|
||||||
|
defmt::info!(
|
||||||
|
"Sending TM[{},{}] with size {}",
|
||||||
|
tm_creator.service(),
|
||||||
|
tm_creator.subservice(),
|
||||||
|
tm_creator.len_written()
|
||||||
|
);
|
||||||
|
TM_REQUESTS
|
||||||
|
.enqueue(tm_vec)
|
||||||
|
.map_err(|_| TmSendError::Queue)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_tm_send_error(error: TmSendError) {
|
||||||
|
defmt::warn!("sending tm failed with error {}", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum UartTxState {
|
||||||
|
// Wrapped in an option because we need an owned type later.
|
||||||
|
Idle(Option<TxIdle>),
|
||||||
|
// Same as above
|
||||||
|
Transmitting(Option<TxDmaTransferType>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UartTxShared {
|
||||||
|
last_completed: Option<InstantFugit>,
|
||||||
|
state: UartTxState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RequestWithToken {
|
||||||
|
token: VerificationToken<TcStateAccepted>,
|
||||||
|
request: Request,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, defmt::Format)]
|
||||||
|
pub enum Request {
|
||||||
|
Ping,
|
||||||
|
ChangeBlinkFrequency(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, defmt::Format)]
|
||||||
|
pub enum RequestError {
|
||||||
|
InvalidApid = 1,
|
||||||
|
InvalidService = 2,
|
||||||
|
InvalidSubservice = 3,
|
||||||
|
NotEnoughAppData = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert_pus_tc_to_request(
|
||||||
|
tc: &PusTcReader,
|
||||||
|
verif_reporter: &mut VerificationReportCreator,
|
||||||
|
src_data_buf: &mut [u8],
|
||||||
|
timestamp: &[u8],
|
||||||
|
) -> Result<RequestWithToken, RequestError> {
|
||||||
|
defmt::info!(
|
||||||
|
"Found PUS TC [{},{}] with length {}",
|
||||||
|
tc.service(),
|
||||||
|
tc.subservice(),
|
||||||
|
tc.len_packed()
|
||||||
|
);
|
||||||
|
|
||||||
|
let token = verif_reporter.add_tc(tc);
|
||||||
|
if tc.apid() != PUS_APID {
|
||||||
|
defmt::warn!("Received tc with unknown APID {}", tc.apid());
|
||||||
|
let result = send_tm(
|
||||||
|
verif_reporter
|
||||||
|
.acceptance_failure(
|
||||||
|
src_data_buf,
|
||||||
|
token,
|
||||||
|
SEQ_COUNT_PROVIDER.get_and_increment(),
|
||||||
|
0,
|
||||||
|
FailParams::new(timestamp, &EcssEnumU16::new(0), &[]),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
if let Err(e) = result {
|
||||||
|
handle_tm_send_error(e);
|
||||||
|
}
|
||||||
|
return Err(RequestError::InvalidApid);
|
||||||
|
}
|
||||||
|
let (tm_creator, accepted_token) = verif_reporter
|
||||||
|
.acceptance_success(
|
||||||
|
src_data_buf,
|
||||||
|
token,
|
||||||
|
SEQ_COUNT_PROVIDER.get_and_increment(),
|
||||||
|
0,
|
||||||
|
timestamp,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Err(e) = send_tm(tm_creator) {
|
||||||
|
handle_tm_send_error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.service() == 17 && tc.subservice() == 1 {
|
||||||
|
if tc.subservice() == 1 {
|
||||||
|
return Ok(RequestWithToken {
|
||||||
|
request: Request::Ping,
|
||||||
|
token: accepted_token,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Err(RequestError::InvalidSubservice);
|
||||||
|
}
|
||||||
|
} else if tc.service() == 8 {
|
||||||
|
if tc.subservice() == 1 {
|
||||||
|
if tc.user_data().len() < 4 {
|
||||||
|
return Err(RequestError::NotEnoughAppData);
|
||||||
|
}
|
||||||
|
let new_freq_ms = u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap());
|
||||||
|
return Ok(RequestWithToken {
|
||||||
|
request: Request::ChangeBlinkFrequency(new_freq_ms),
|
||||||
|
token: accepted_token,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Err(RequestError::InvalidSubservice);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(RequestError::InvalidService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = stm32f3xx_hal::pac, peripherals = true)]
|
||||||
|
mod app {
|
||||||
|
use super::*;
|
||||||
|
use core::slice::Iter;
|
||||||
|
use rtic_monotonics::systick::Systick;
|
||||||
|
use rtic_monotonics::Monotonic;
|
||||||
|
use satrs::pus::verification::{TcStateStarted, VerificationReportCreator};
|
||||||
|
use satrs::spacepackets::{ecss::tc::PusTcReader, time::cds::P_FIELD_BASE};
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use stm32f3_discovery::leds::Direction;
|
||||||
|
use stm32f3_discovery::leds::Leds;
|
||||||
|
use stm32f3xx_hal::prelude::*;
|
||||||
|
|
||||||
|
use stm32f3_discovery::switch_hal::OutputSwitch;
|
||||||
|
use stm32f3xx_hal::Switch;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
blink_freq: MillisDurationU32,
|
||||||
|
tx_shared: UartTxShared,
|
||||||
|
rx_transfer: Option<RxDmaTransferType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
verif_reporter: VerificationReportCreator,
|
||||||
|
leds: Leds,
|
||||||
|
last_dir: Direction,
|
||||||
|
curr_dir: Iter<'static, Direction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
let mut rcc = cx.device.RCC.constrain();
|
||||||
|
|
||||||
|
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||||
|
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||||
|
Systick::start(cx.core.SYST, 8_000_000, systick_mono_token);
|
||||||
|
|
||||||
|
let mut flash = cx.device.FLASH.constrain();
|
||||||
|
let clocks = rcc
|
||||||
|
.cfgr
|
||||||
|
.use_hse(8.MHz())
|
||||||
|
.sysclk(8.MHz())
|
||||||
|
.pclk1(8.MHz())
|
||||||
|
.freeze(&mut flash.acr);
|
||||||
|
|
||||||
|
// Set up monotonic timer.
|
||||||
|
//let mono_timer = MonoTimer::new(cx.core.DWT, clocks, &mut cx.core.DCB);
|
||||||
|
|
||||||
|
defmt::info!("Starting sat-rs demo application for the STM32F3-Discovery");
|
||||||
|
let mut gpioe = cx.device.GPIOE.split(&mut rcc.ahb);
|
||||||
|
|
||||||
|
let leds = Leds::new(
|
||||||
|
gpioe.pe8,
|
||||||
|
gpioe.pe9,
|
||||||
|
gpioe.pe10,
|
||||||
|
gpioe.pe11,
|
||||||
|
gpioe.pe12,
|
||||||
|
gpioe.pe13,
|
||||||
|
gpioe.pe14,
|
||||||
|
gpioe.pe15,
|
||||||
|
&mut gpioe.moder,
|
||||||
|
&mut gpioe.otyper,
|
||||||
|
);
|
||||||
|
let mut gpioa = cx.device.GPIOA.split(&mut rcc.ahb);
|
||||||
|
// USART2 pins
|
||||||
|
let mut pins = (
|
||||||
|
// TX pin: PA2
|
||||||
|
gpioa
|
||||||
|
.pa2
|
||||||
|
.into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl),
|
||||||
|
// RX pin: PA3
|
||||||
|
gpioa
|
||||||
|
.pa3
|
||||||
|
.into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl),
|
||||||
|
);
|
||||||
|
pins.1.internal_pull_up(&mut gpioa.pupdr, true);
|
||||||
|
let mut usart2 = Serial::new(
|
||||||
|
cx.device.USART2,
|
||||||
|
pins,
|
||||||
|
UART_BAUD.Bd(),
|
||||||
|
clocks,
|
||||||
|
&mut rcc.apb1,
|
||||||
|
);
|
||||||
|
usart2.configure_rx_interrupt(RxEvent::Idle, Switch::On);
|
||||||
|
// This interrupt is enabled to re-schedule new transfers in the interrupt handler immediately.
|
||||||
|
usart2.configure_tx_interrupt(TxEvent::TransmissionComplete, Switch::On);
|
||||||
|
|
||||||
|
let dma1 = cx.device.DMA1.split(&mut rcc.ahb);
|
||||||
|
let (mut tx_serial, mut rx_serial) = usart2.split();
|
||||||
|
|
||||||
|
// This interrupt is immediately triggered, clear it. It will only be reset
|
||||||
|
// by the hardware when data is received on RX (RXNE event)
|
||||||
|
rx_serial.clear_event(RxEvent::Idle);
|
||||||
|
// For some reason, this is also immediately triggered..
|
||||||
|
tx_serial.clear_event(TxEvent::TransmissionComplete);
|
||||||
|
let rx_transfer = rx_serial.read_exact(unsafe { DMA_RX_BUF.as_mut_slice() }, dma1.ch6);
|
||||||
|
defmt::info!("Spawning tasks");
|
||||||
|
blink::spawn().unwrap();
|
||||||
|
serial_tx_handler::spawn().unwrap();
|
||||||
|
|
||||||
|
let verif_reporter = VerificationReportCreator::new(PUS_APID).unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
Shared {
|
||||||
|
blink_freq: MillisDurationU32::from_ticks(DEFAULT_BLINK_FREQ_MS),
|
||||||
|
tx_shared: UartTxShared {
|
||||||
|
last_completed: None,
|
||||||
|
state: UartTxState::Idle(Some(TxIdle {
|
||||||
|
tx: tx_serial,
|
||||||
|
dma_channel: dma1.ch7,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
rx_transfer: Some(rx_transfer),
|
||||||
|
},
|
||||||
|
Local {
|
||||||
|
verif_reporter,
|
||||||
|
leds,
|
||||||
|
last_dir: Direction::North,
|
||||||
|
curr_dir: Direction::iter(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(local = [leds, curr_dir, last_dir], shared=[blink_freq])]
|
||||||
|
async fn blink(mut cx: blink::Context) {
|
||||||
|
let blink::LocalResources {
|
||||||
|
leds,
|
||||||
|
curr_dir,
|
||||||
|
last_dir,
|
||||||
|
..
|
||||||
|
} = cx.local;
|
||||||
|
let mut toggle_leds = |dir: &Direction| {
|
||||||
|
let last_led = leds.for_direction(*last_dir);
|
||||||
|
last_led.off().ok();
|
||||||
|
let led = leds.for_direction(*dir);
|
||||||
|
led.on().ok();
|
||||||
|
*last_dir = *dir;
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
match curr_dir.next() {
|
||||||
|
Some(dir) => {
|
||||||
|
toggle_leds(dir);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
*curr_dir = Direction::iter();
|
||||||
|
toggle_leds(curr_dir.next().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let current_blink_freq = cx.shared.blink_freq.lock(|current| *current);
|
||||||
|
Systick::delay(current_blink_freq).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
shared = [tx_shared],
|
||||||
|
)]
|
||||||
|
async fn serial_tx_handler(mut cx: serial_tx_handler::Context) {
|
||||||
|
loop {
|
||||||
|
let is_idle = cx.shared.tx_shared.lock(|tx_shared| {
|
||||||
|
if let UartTxState::Idle(_) = tx_shared.state {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
|
if is_idle {
|
||||||
|
let last_completed = cx.shared.tx_shared.lock(|shared| shared.last_completed);
|
||||||
|
if let Some(last_completed) = last_completed {
|
||||||
|
let elapsed_ms = (Systick::now() - last_completed).to_millis();
|
||||||
|
if elapsed_ms < MIN_DELAY_BETWEEN_TX_PACKETS_MS {
|
||||||
|
Systick::delay((MIN_DELAY_BETWEEN_TX_PACKETS_MS - elapsed_ms).millis())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check for completion after 1 ms
|
||||||
|
Systick::delay(1.millis()).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(vec) = TM_REQUESTS.dequeue() {
|
||||||
|
cx.shared
|
||||||
|
.tx_shared
|
||||||
|
.lock(|tx_shared| match &mut tx_shared.state {
|
||||||
|
UartTxState::Idle(tx) => {
|
||||||
|
let encoded_len;
|
||||||
|
//debug!(target: "serial_tx_handler", "bytes: {:x?}", &buf[0..len]);
|
||||||
|
// Safety: We only copy the data into the TX DMA buffer in this task.
|
||||||
|
// If the DMA is active, another branch will be taken.
|
||||||
|
unsafe {
|
||||||
|
// 0 sentinel value as start marker
|
||||||
|
DMA_TX_BUF[0] = 0;
|
||||||
|
encoded_len =
|
||||||
|
cobs::encode(&vec[0..vec.len()], &mut DMA_TX_BUF[1..]);
|
||||||
|
// Should never panic, we accounted for the overhead.
|
||||||
|
// Write into transfer buffer directly, no need for intermediate
|
||||||
|
// encoding buffer.
|
||||||
|
// 0 end marker
|
||||||
|
DMA_TX_BUF[encoded_len + 1] = 0;
|
||||||
|
}
|
||||||
|
//debug!(target: "serial_tx_handler", "Sending {} bytes", encoded_len + 2);
|
||||||
|
//debug!("sent: {:x?}", &mut_tx_dma_buf[0..encoded_len + 2]);
|
||||||
|
let tx_idle = tx.take().unwrap();
|
||||||
|
// Transfer completion and re-scheduling of new TX transfers will be done
|
||||||
|
// by the IRQ handler.
|
||||||
|
// SAFETY: The DMA is the exclusive writer to the DMA buffer now.
|
||||||
|
let transfer = tx_idle.tx.write_all(
|
||||||
|
unsafe { &DMA_TX_BUF[0..encoded_len + 2] },
|
||||||
|
tx_idle.dma_channel,
|
||||||
|
);
|
||||||
|
tx_shared.state = UartTxState::Transmitting(Some(transfer));
|
||||||
|
// The memory block is automatically returned to the pool when it is dropped.
|
||||||
|
}
|
||||||
|
UartTxState::Transmitting(_) => (),
|
||||||
|
});
|
||||||
|
// Check for completion after 1 ms
|
||||||
|
Systick::delay(1.millis()).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Nothing to do, and we are idle.
|
||||||
|
Systick::delay(TX_HANDLER_FREQ_MS.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
local = [
|
||||||
|
verif_reporter,
|
||||||
|
decode_buf: [u8; MAX_TC_LEN] = [0; MAX_TC_LEN],
|
||||||
|
src_data_buf: [u8; MAX_TM_LEN] = [0; MAX_TM_LEN],
|
||||||
|
timestamp: [u8; 7] = [0; 7],
|
||||||
|
],
|
||||||
|
shared = [blink_freq]
|
||||||
|
)]
|
||||||
|
async fn serial_rx_handler(
|
||||||
|
mut cx: serial_rx_handler::Context,
|
||||||
|
received_packet: Vec<u8, MAX_TC_LEN>,
|
||||||
|
) {
|
||||||
|
cx.local.timestamp[0] = P_FIELD_BASE;
|
||||||
|
defmt::info!("Received packet with {} bytes", received_packet.len());
|
||||||
|
let decode_buf = cx.local.decode_buf;
|
||||||
|
let packet = received_packet.as_slice();
|
||||||
|
let mut start_idx = None;
|
||||||
|
for (idx, byte) in packet.iter().enumerate() {
|
||||||
|
if *byte != 0 {
|
||||||
|
start_idx = Some(idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start_idx.is_none() {
|
||||||
|
defmt::warn!("decoding error, can only process cobs encoded frames, data is all 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let start_idx = start_idx.unwrap();
|
||||||
|
match cobs::decode(&received_packet.as_slice()[start_idx..], decode_buf) {
|
||||||
|
Ok(len) => {
|
||||||
|
defmt::info!("Decoded packet length: {}", len);
|
||||||
|
let pus_tc = PusTcReader::new(decode_buf);
|
||||||
|
match pus_tc {
|
||||||
|
Ok((tc, _tc_len)) => {
|
||||||
|
match convert_pus_tc_to_request(
|
||||||
|
&tc,
|
||||||
|
cx.local.verif_reporter,
|
||||||
|
cx.local.src_data_buf,
|
||||||
|
cx.local.timestamp,
|
||||||
|
) {
|
||||||
|
Ok(request_with_token) => {
|
||||||
|
let started_token = handle_start_verification(
|
||||||
|
request_with_token.token,
|
||||||
|
cx.local.verif_reporter,
|
||||||
|
cx.local.src_data_buf,
|
||||||
|
cx.local.timestamp,
|
||||||
|
);
|
||||||
|
|
||||||
|
match request_with_token.request {
|
||||||
|
Request::Ping => {
|
||||||
|
handle_ping_request(cx.local.timestamp);
|
||||||
|
}
|
||||||
|
Request::ChangeBlinkFrequency(new_freq_ms) => {
|
||||||
|
defmt::info!("Received blink frequency change request with new frequncy {}", new_freq_ms);
|
||||||
|
cx.shared.blink_freq.lock(|blink_freq| {
|
||||||
|
*blink_freq =
|
||||||
|
MillisDurationU32::from_ticks(new_freq_ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_completion_verification(
|
||||||
|
started_token,
|
||||||
|
cx.local.verif_reporter,
|
||||||
|
cx.local.src_data_buf,
|
||||||
|
cx.local.timestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// TODO: Error handling: Send verification failure based on request error.
|
||||||
|
defmt::warn!("request error {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
defmt::warn!("Error unpacking PUS TC: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
defmt::warn!("decoding error, can only process cobs encoded frames")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_ping_request(timestamp: &[u8]) {
|
||||||
|
defmt::info!("Received PUS ping telecommand, sending ping reply TM[17,2]");
|
||||||
|
let sp_header =
|
||||||
|
SpHeader::new_for_unseg_tc(PUS_APID, SEQ_COUNT_PROVIDER.get_and_increment(), 0);
|
||||||
|
let sec_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
|
||||||
|
let ping_reply = PusTmCreator::new(sp_header, sec_header, &[], true);
|
||||||
|
let mut tm_packet = TmPacket::new();
|
||||||
|
tm_packet
|
||||||
|
.resize(ping_reply.len_written(), 0)
|
||||||
|
.expect("vec resize failed");
|
||||||
|
ping_reply.write_to_bytes(&mut tm_packet).unwrap();
|
||||||
|
if TM_REQUESTS.enqueue(tm_packet).is_err() {
|
||||||
|
defmt::warn!("TC queue full");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_start_verification(
|
||||||
|
accepted_token: VerificationToken<TcStateAccepted>,
|
||||||
|
verif_reporter: &mut VerificationReportCreator,
|
||||||
|
src_data_buf: &mut [u8],
|
||||||
|
timestamp: &[u8],
|
||||||
|
) -> VerificationToken<TcStateStarted> {
|
||||||
|
let (tm_creator, started_token) = verif_reporter
|
||||||
|
.start_success(
|
||||||
|
src_data_buf,
|
||||||
|
accepted_token,
|
||||||
|
SEQ_COUNT_PROVIDER.get(),
|
||||||
|
0,
|
||||||
|
×tamp,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let result = send_tm(tm_creator);
|
||||||
|
if let Err(e) = result {
|
||||||
|
handle_tm_send_error(e);
|
||||||
|
}
|
||||||
|
started_token
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_completion_verification(
|
||||||
|
started_token: VerificationToken<TcStateStarted>,
|
||||||
|
verif_reporter: &mut VerificationReportCreator,
|
||||||
|
src_data_buf: &mut [u8],
|
||||||
|
timestamp: &[u8],
|
||||||
|
) {
|
||||||
|
let result = send_tm(
|
||||||
|
verif_reporter
|
||||||
|
.completion_success(
|
||||||
|
src_data_buf,
|
||||||
|
started_token,
|
||||||
|
SEQ_COUNT_PROVIDER.get(),
|
||||||
|
0,
|
||||||
|
timestamp,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
if let Err(e) = result {
|
||||||
|
handle_tm_send_error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(binds = DMA1_CH6, shared = [rx_transfer])]
|
||||||
|
fn rx_dma_isr(mut cx: rx_dma_isr::Context) {
|
||||||
|
let mut tc_packet = TcPacket::new();
|
||||||
|
cx.shared.rx_transfer.lock(|rx_transfer| {
|
||||||
|
let rx_ref = rx_transfer.as_ref().unwrap();
|
||||||
|
if rx_ref.is_complete() {
|
||||||
|
let uart_rx_owned = rx_transfer.take().unwrap();
|
||||||
|
let (buf, c, rx) = uart_rx_owned.stop();
|
||||||
|
// The received data is transferred to another task now to avoid any processing overhead
|
||||||
|
// during the interrupt. There are multiple ways to do this, we use a stack allocaed vector here
|
||||||
|
// to do this.
|
||||||
|
tc_packet.resize(buf.len(), 0).expect("vec resize failed");
|
||||||
|
tc_packet.copy_from_slice(buf);
|
||||||
|
|
||||||
|
// Start the next transfer as soon as possible.
|
||||||
|
*rx_transfer = Some(rx.read_exact(buf, c));
|
||||||
|
|
||||||
|
// Send the vector to a regular task.
|
||||||
|
serial_rx_handler::spawn(tc_packet).expect("spawning rx handler task failed");
|
||||||
|
// If this happens, there is a high chance that the maximum packet length was
|
||||||
|
// exceeded. Circular mode is not used here, so data might be missed.
|
||||||
|
defmt::warn!(
|
||||||
|
"rx transfer with maximum length {}, might miss data",
|
||||||
|
TC_BUF_LEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(binds = USART2_EXTI26, shared = [rx_transfer, tx_shared])]
|
||||||
|
fn serial_isr(mut cx: serial_isr::Context) {
|
||||||
|
cx.shared
|
||||||
|
.tx_shared
|
||||||
|
.lock(|tx_shared| match &mut tx_shared.state {
|
||||||
|
UartTxState::Idle(_) => (),
|
||||||
|
UartTxState::Transmitting(transfer) => {
|
||||||
|
let transfer_ref = transfer.as_ref().unwrap();
|
||||||
|
if transfer_ref.is_complete() {
|
||||||
|
let transfer = transfer.take().unwrap();
|
||||||
|
let (_, dma_channel, mut tx) = transfer.stop();
|
||||||
|
tx.clear_event(TxEvent::TransmissionComplete);
|
||||||
|
tx_shared.state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
|
||||||
|
// We cache the last completed time to ensure that there is a minimum delay between consecutive
|
||||||
|
// transferred packets.
|
||||||
|
tx_shared.last_completed = Some(Systick::now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut tc_packet = TcPacket::new();
|
||||||
|
cx.shared.rx_transfer.lock(|rx_transfer| {
|
||||||
|
let rx_transfer_ref = rx_transfer.as_ref().unwrap();
|
||||||
|
// Received a partial packet.
|
||||||
|
if rx_transfer_ref.is_event_triggered(RxEvent::Idle) {
|
||||||
|
let rx_transfer_owned = rx_transfer.take().unwrap();
|
||||||
|
let (buf, ch, mut rx, rx_len) = rx_transfer_owned.stop_and_return_received_bytes();
|
||||||
|
// The received data is transferred to another task now to avoid any processing overhead
|
||||||
|
// during the interrupt. There are multiple ways to do this, we use a stack
|
||||||
|
// allocated vector to do this.
|
||||||
|
tc_packet
|
||||||
|
.resize(rx_len as usize, 0)
|
||||||
|
.expect("vec resize failed");
|
||||||
|
tc_packet[0..rx_len as usize].copy_from_slice(&buf[0..rx_len as usize]);
|
||||||
|
rx.clear_event(RxEvent::Idle);
|
||||||
|
serial_rx_handler::spawn(tc_packet).expect("spawning rx handler failed");
|
||||||
|
*rx_transfer = Some(rx.read_exact(buf, ch));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
// List of extensions which should be recommended for users of this workspace.
|
// List of extensions which should be recommended for users of this workspace.
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"rust-lang.rust",
|
"rust-lang.rust",
|
||||||
"marus25.cortex-debug",
|
"probe-rs.probe-rs-debugger"
|
||||||
],
|
],
|
||||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||||
"unwantedRecommendations": []
|
"unwantedRecommendations": []
|
22
embedded-examples/stm32f3-disco-rtic/vscode/launch.json
Normal file
22
embedded-examples/stm32f3-disco-rtic/vscode/launch.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"preLaunchTask": "${defaultBuildTask}",
|
||||||
|
"type": "probe-rs-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "probe-rs Debugging ",
|
||||||
|
"flashingConfig": {
|
||||||
|
"flashingEnabled": true
|
||||||
|
},
|
||||||
|
"chip": "STM32F303VCTx",
|
||||||
|
"coreConfigs": [
|
||||||
|
{
|
||||||
|
"programBinary": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/satrs-stm32f3-disco-rtic",
|
||||||
|
"rttEnabled": true,
|
||||||
|
"svdFile": "STM32F303.svd"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -11,7 +11,8 @@ proc CDSWOConfigure { CDCPUFreqHz CDSWOFreqHz CDSWOOutput } {
|
|||||||
# Alternative option: Pipe ITM output into itm.txt file
|
# Alternative option: Pipe ITM output into itm.txt file
|
||||||
# tpiu config internal itm.txt uart off $CDCPUFreqHz
|
# tpiu config internal itm.txt uart off $CDCPUFreqHz
|
||||||
|
|
||||||
# Default option so SWO display of VS code works.
|
# Default option so SWO display of VS code works. Please note that this might not be required
|
||||||
|
# anymore starting at openocd v0.12.0
|
||||||
tpiu config internal $CDSWOOutput uart off $CDCPUFreqHz $CDSWOFreqHz
|
tpiu config internal $CDSWOOutput uart off $CDCPUFreqHz $CDSWOFreqHz
|
||||||
itm port 0 on
|
itm port 0 on
|
||||||
}
|
}
|
29
embedded-examples/stm32h7-nucleo-rtic/.cargo/def_config.toml
Normal file
29
embedded-examples/stm32h7-nucleo-rtic/.cargo/def_config.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[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"
|
4
embedded-examples/stm32h7-nucleo-rtic/.gitignore
vendored
Normal file
4
embedded-examples/stm32h7-nucleo-rtic/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/target
|
||||||
|
/.cargo/config*
|
||||||
|
/.vscode
|
||||||
|
/app.map
|
881
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
Normal file
881
embedded-examples/stm32h7-nucleo-rtic/Cargo.lock
generated
Normal file
@ -0,0 +1,881 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-polyfill"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version 0.2.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||||
|
|
||||||
|
[[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.2.3"
|
||||||
|
source = "git+https://github.com/robamu/cobs.rs.git?branch=all_features#c70a7f30fd00a7cbdb7666dec12b437977385d40"
|
||||||
|
|
||||||
|
[[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.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt-macros"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-semihosting"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc"
|
||||||
|
version = "3.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.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"defmt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-brtt"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2f0ac3635d0c89d12b8101fcb44a7625f5f030a1c0491124b74467eb5a58a78"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"defmt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-macros"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb"
|
||||||
|
dependencies = [
|
||||||
|
"defmt-parser",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.64",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-parser"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-test"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290966e8c38f94b11884877242de876280d0eab934900e9642d58868e77c5df1"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt",
|
||||||
|
"cortex-m-semihosting",
|
||||||
|
"defmt",
|
||||||
|
"defmt-test-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-test-macros"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "984bc6eca246389726ac2826acc2488ca0fe5fcd6b8d9b48797021951d76a125"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.64",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "delegate"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"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.64",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-alloc"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"linked_list_allocator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
|
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.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.7.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"hash32 0.2.1",
|
||||||
|
"rustc_version 0.4.0",
|
||||||
|
"spin",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"defmt",
|
||||||
|
"hash32 0.3.1",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked_list_allocator"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "managed"
|
||||||
|
version = "0.8.0"
|
||||||
|
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.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||||
|
dependencies = [
|
||||||
|
"num_enum_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum_derive"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.64",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[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.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"bare-metal 1.0.0",
|
||||||
|
"cortex-m",
|
||||||
|
"critical-section",
|
||||||
|
"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.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.64",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-monotonics"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"cfg-if",
|
||||||
|
"cortex-m",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"fugit",
|
||||||
|
"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 0.8.0",
|
||||||
|
"portable-atomic",
|
||||||
|
"rtic-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-time"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"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 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
|
dependencies = [
|
||||||
|
"semver 1.0.23",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "satrs"
|
||||||
|
version = "0.2.1"
|
||||||
|
dependencies = [
|
||||||
|
"cobs",
|
||||||
|
"crc",
|
||||||
|
"defmt",
|
||||||
|
"delegate",
|
||||||
|
"derive-new",
|
||||||
|
"heapless 0.7.17",
|
||||||
|
"num-traits",
|
||||||
|
"num_enum",
|
||||||
|
"paste",
|
||||||
|
"satrs-shared",
|
||||||
|
"smallvec",
|
||||||
|
"spacepackets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "satrs-shared"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6042477018c2d43fffccaaa5099bc299a58485139b4d31c5b276889311e474f1"
|
||||||
|
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 = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
|
||||||
|
[[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 0.8.0",
|
||||||
|
"managed",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spacepackets"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e85574d113a06312010c0ba51aadccd4ba2806231ebe9a49fc6473d0534d8696"
|
||||||
|
dependencies = [
|
||||||
|
"crc",
|
||||||
|
"defmt",
|
||||||
|
"delegate",
|
||||||
|
"num-traits",
|
||||||
|
"num_enum",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "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 = "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.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.64",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcell"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
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.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.64",
|
||||||
|
]
|
85
embedded-examples/stm32h7-nucleo-rtic/Cargo.toml
Normal file
85
embedded-examples/stm32h7-nucleo-rtic/Cargo.toml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
[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.5"
|
||||||
|
rtic-sync = { version = "1", features = ["defmt-03"] }
|
||||||
|
|
||||||
|
[dependencies.smoltcp]
|
||||||
|
version = "0.11.0"
|
||||||
|
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 = "1"
|
||||||
|
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 # <-
|
201
embedded-examples/stm32h7-nucleo-rtic/LICENSE-APACHE
Normal file
201
embedded-examples/stm32h7-nucleo-rtic/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
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.
|
118
embedded-examples/stm32h7-nucleo-rtic/README.md
Normal file
118
embedded-examples/stm32h7-nucleo-rtic/README.md
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
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)
|
107782
embedded-examples/stm32h7-nucleo-rtic/STM32H743.svd
Normal file
107782
embedded-examples/stm32h7-nucleo-rtic/STM32H743.svd
Normal file
File diff suppressed because it is too large
Load Diff
BIN
embedded-examples/stm32h7-nucleo-rtic/docs/stm32h743bi.pdf
Normal file
BIN
embedded-examples/stm32h7-nucleo-rtic/docs/stm32h743bi.pdf
Normal file
Binary file not shown.
Binary file not shown.
119
embedded-examples/stm32h7-nucleo-rtic/memory.x
Normal file
119
embedded-examples/stm32h7-nucleo-rtic/memory.x
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/* 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,4 +1,5 @@
|
|||||||
/venv
|
/venv
|
||||||
|
/.tmtc-history.txt
|
||||||
/log
|
/log
|
||||||
/.idea/*
|
/.idea/*
|
||||||
!/.idea/runConfigurations
|
!/.idea/runConfigurations
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"com_if": "udp",
|
||||||
|
"tcpip_udp_port": 7301
|
||||||
|
}
|
212
satrs-example-stm32f3-disco/pyclient/main.py → embedded-examples/stm32h7-nucleo-rtic/pyclient/main.py
Normal file → Executable file
212
satrs-example-stm32f3-disco/pyclient/main.py → embedded-examples/stm32h7-nucleo-rtic/pyclient/main.py
Normal file → Executable file
@ -1,39 +1,40 @@
|
|||||||
#!/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 enum
|
|
||||||
import struct
|
import struct
|
||||||
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from typing import Optional, cast
|
from typing import Any, Optional, cast
|
||||||
|
from prompt_toolkit.history import FileHistory, History
|
||||||
|
from spacepackets.ecss.tm import CdsShortTimestamp
|
||||||
|
|
||||||
import tmtccmd
|
import tmtccmd
|
||||||
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusVerificator
|
from spacepackets.ecss import PusTelemetry, PusTelecommand, PusTm, 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
|
||||||
|
|
||||||
from tmtccmd import CcsdsTmtcBackend, TcHandlerBase, ProcedureParamsWrapper
|
from tmtccmd import TcHandlerBase, ProcedureParamsWrapper
|
||||||
from tmtccmd.core.base import BackendRequest
|
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.pus import VerificationWrapper
|
||||||
from tmtccmd.tm import CcsdsTmHandler, SpecificApidHandlerBase
|
from tmtccmd.tmtc import CcsdsTmHandler, SpecificApidHandlerBase
|
||||||
from tmtccmd.com_if import ComInterface
|
from tmtccmd.com import ComInterface
|
||||||
from tmtccmd.config import (
|
from tmtccmd.config import (
|
||||||
|
CmdTreeNode,
|
||||||
default_json_path,
|
default_json_path,
|
||||||
SetupParams,
|
SetupParams,
|
||||||
TmTcCfgHookBase,
|
HookBase,
|
||||||
TmtcDefinitionWrapper,
|
|
||||||
CoreServiceList,
|
|
||||||
OpCodeEntry,
|
|
||||||
params_to_procedure_conversion,
|
params_to_procedure_conversion,
|
||||||
)
|
)
|
||||||
from tmtccmd.config.com_if import SerialCfgWrapper
|
from tmtccmd.config.com import SerialCfgWrapper
|
||||||
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
||||||
from tmtccmd.logging import get_console_logger
|
|
||||||
from tmtccmd.logging.pus import (
|
from tmtccmd.logging.pus import (
|
||||||
RegularTmtcLogWrapper,
|
RegularTmtcLogWrapper,
|
||||||
RawTmtcTimedLogWrapper,
|
RawTmtcTimedLogWrapper,
|
||||||
TimedLogWhen,
|
TimedLogWhen,
|
||||||
)
|
)
|
||||||
from tmtccmd.tc import (
|
from tmtccmd.tmtc import (
|
||||||
TcQueueEntryType,
|
TcQueueEntryType,
|
||||||
ProcedureWrapper,
|
ProcedureWrapper,
|
||||||
TcProcedureType,
|
TcProcedureType,
|
||||||
@ -41,27 +42,26 @@ from tmtccmd.tc import (
|
|||||||
SendCbParams,
|
SendCbParams,
|
||||||
DefaultPusQueueHelper,
|
DefaultPusQueueHelper,
|
||||||
)
|
)
|
||||||
from tmtccmd.tm.pus_5_event import Service5Tm
|
from tmtccmd.pus.s5_fsfw_event import Service5Tm
|
||||||
from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider
|
from spacepackets.seqcount import FileSeqCountProvider, PusFileSeqCountProvider
|
||||||
from tmtccmd.util.obj_id import ObjectIdDictT
|
from tmtccmd.util.obj_id import ObjectIdDictT
|
||||||
|
|
||||||
from tmtccmd.util.tmtc_printer import FsfwTmTcPrinter
|
_LOGGER = logging.getLogger()
|
||||||
|
|
||||||
LOGGER = get_console_logger()
|
|
||||||
|
|
||||||
EXAMPLE_PUS_APID = 0x02
|
EXAMPLE_PUS_APID = 0x02
|
||||||
|
|
||||||
|
|
||||||
class SatRsConfigHook(TmTcCfgHookBase):
|
class SatRsConfigHook(HookBase):
|
||||||
def __init__(self, json_cfg_path: str):
|
def __init__(self, json_cfg_path: str):
|
||||||
super().__init__(json_cfg_path=json_cfg_path)
|
super().__init__(json_cfg_path)
|
||||||
|
|
||||||
def assign_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
def get_communication_interface(self, com_if_key: str) -> Optional[ComInterface]:
|
||||||
from tmtccmd.config.com_if import (
|
from tmtccmd.config.com import (
|
||||||
create_com_interface_default,
|
create_com_interface_default,
|
||||||
create_com_interface_cfg_default,
|
create_com_interface_cfg_default,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert self.cfg_path is not None
|
||||||
cfg = create_com_interface_cfg_default(
|
cfg = create_com_interface_cfg_default(
|
||||||
com_if_key=com_if_key,
|
com_if_key=com_if_key,
|
||||||
json_cfg_path=self.cfg_path,
|
json_cfg_path=self.cfg_path,
|
||||||
@ -76,35 +76,14 @@ class SatRsConfigHook(TmTcCfgHookBase):
|
|||||||
cfg.serial_cfg.serial_timeout = 0.5
|
cfg.serial_cfg.serial_timeout = 0.5
|
||||||
return create_com_interface_default(cfg)
|
return create_com_interface_default(cfg)
|
||||||
|
|
||||||
def get_tmtc_definitions(self) -> TmtcDefinitionWrapper:
|
def get_command_definitions(self) -> CmdTreeNode:
|
||||||
from tmtccmd.config.globals import get_default_tmtc_defs
|
"""This function should return the root node of the command definition tree."""
|
||||||
|
return create_cmd_definition_tree()
|
||||||
|
|
||||||
defs = get_default_tmtc_defs()
|
def get_cmd_history(self) -> Optional[History]:
|
||||||
srv_5 = OpCodeEntry()
|
"""Optionlly return a history class for the past command paths which will be used
|
||||||
srv_5.add("0", "Event Test")
|
when prompting a command path from the user in CLI mode."""
|
||||||
defs.add_service(
|
return FileHistory(".tmtc-history.txt")
|
||||||
name=CoreServiceList.SERVICE_5.value,
|
|
||||||
info="PUS Service 5 Event",
|
|
||||||
op_code_entry=srv_5,
|
|
||||||
)
|
|
||||||
srv_17 = OpCodeEntry()
|
|
||||||
srv_17.add("0", "Ping Test")
|
|
||||||
defs.add_service(
|
|
||||||
name=CoreServiceList.SERVICE_17_ALT,
|
|
||||||
info="PUS Service 17 Test",
|
|
||||||
op_code_entry=srv_17,
|
|
||||||
)
|
|
||||||
srv_3 = OpCodeEntry()
|
|
||||||
defs.add_service(
|
|
||||||
name=CoreServiceList.SERVICE_3,
|
|
||||||
info="PUS Service 3 Housekeeping",
|
|
||||||
op_code_entry=srv_3,
|
|
||||||
)
|
|
||||||
return defs
|
|
||||||
|
|
||||||
def perform_mode_operation(self, tmtc_backend: CcsdsTmtcBackend, mode: int):
|
|
||||||
LOGGER.info("Mode operation hook was called")
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_object_ids(self) -> ObjectIdDictT:
|
def get_object_ids(self) -> ObjectIdDictT:
|
||||||
from tmtccmd.config.objects import get_core_object_ids
|
from tmtccmd.config.objects import get_core_object_ids
|
||||||
@ -112,74 +91,77 @@ class SatRsConfigHook(TmTcCfgHookBase):
|
|||||||
return 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):
|
class PusHandler(SpecificApidHandlerBase):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
file_logger: logging.Logger,
|
||||||
verif_wrapper: VerificationWrapper,
|
verif_wrapper: VerificationWrapper,
|
||||||
printer: FsfwTmTcPrinter,
|
|
||||||
raw_logger: RawTmtcTimedLogWrapper,
|
raw_logger: RawTmtcTimedLogWrapper,
|
||||||
):
|
):
|
||||||
super().__init__(EXAMPLE_PUS_APID, None)
|
super().__init__(EXAMPLE_PUS_APID, None)
|
||||||
self.printer = printer
|
self.file_logger = file_logger
|
||||||
self.raw_logger = raw_logger
|
self.raw_logger = raw_logger
|
||||||
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):
|
||||||
try:
|
try:
|
||||||
tm_packet = PusTelemetry.unpack(packet)
|
pus_tm = PusTm.unpack(
|
||||||
|
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}")
|
||||||
raise e
|
raise e
|
||||||
service = tm_packet.service
|
service = pus_tm.service
|
||||||
dedicated_handler = False
|
tm_packet = None
|
||||||
if service == 1:
|
if service == 1:
|
||||||
tm_packet = Service1Tm.unpack(data=packet, params=UnpackParams(1, 2))
|
tm_packet = Service1Tm.unpack(
|
||||||
|
data=packet, params=UnpackParams(CdsShortTimestamp.TIMESTAMP_SIZE, 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:
|
||||||
LOGGER.info(
|
_LOGGER.info(
|
||||||
f"Received Verification TM[{tm_packet.service}, {tm_packet.subservice}] "
|
f"Received Verification TM[{tm_packet.service}, {tm_packet.subservice}] "
|
||||||
f"with Request ID {tm_packet.tc_req_id.as_u32():#08x}"
|
f"with Request ID {tm_packet.tc_req_id.as_u32():#08x}"
|
||||||
)
|
)
|
||||||
LOGGER.warning(
|
_LOGGER.warning(
|
||||||
f"No matching telecommand found for {tm_packet.tc_req_id}"
|
f"No matching telecommand found for {tm_packet.tc_req_id}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.verif_wrapper.log_to_console(tm_packet, res)
|
self.verif_wrapper.log_to_console(tm_packet, res)
|
||||||
self.verif_wrapper.log_to_file(tm_packet, res)
|
self.verif_wrapper.log_to_file(tm_packet, res)
|
||||||
dedicated_handler = True
|
|
||||||
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)
|
pus_tm = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
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:]
|
||||||
dedicated_handler = True
|
_LOGGER.info("received JSON string: " + json_str.decode("utf-8"))
|
||||||
if service == 5:
|
if service == 5:
|
||||||
tm_packet = Service5Tm.unpack(packet)
|
tm_packet = Service5Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
if service == 17:
|
if service == 17:
|
||||||
tm_packet = Service17Tm.unpack(packet)
|
tm_packet = Service17Tm.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
dedicated_handler = True
|
|
||||||
if tm_packet.subservice == 2:
|
if tm_packet.subservice == 2:
|
||||||
self.printer.file_logger.info("Received Ping Reply TM[17,2]")
|
_LOGGER.info("Received Ping Reply TM[17,2]")
|
||||||
LOGGER.info("Received Ping Reply TM[17,2]")
|
|
||||||
else:
|
else:
|
||||||
self.printer.file_logger.info(
|
_LOGGER.info(
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
|
||||||
)
|
|
||||||
LOGGER.info(
|
|
||||||
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
f"Received Test Packet with unknown subservice {tm_packet.subservice}"
|
||||||
)
|
)
|
||||||
if tm_packet is None:
|
if tm_packet is None:
|
||||||
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)
|
tm_packet = PusTelemetry.unpack(packet, CdsShortTimestamp.TIMESTAMP_SIZE)
|
||||||
self.raw_logger.log_tm(tm_packet)
|
self.raw_logger.log_tm(pus_tm)
|
||||||
if not dedicated_handler and tm_packet is not None:
|
|
||||||
self.printer.handle_long_tm_print(packet_if=tm_packet, info_if=tm_packet)
|
|
||||||
|
|
||||||
|
|
||||||
def make_addressable_id(target_id: int, unique_id: int) -> bytes:
|
def make_addressable_id(target_id: int, unique_id: int) -> bytes:
|
||||||
@ -198,8 +180,11 @@ class TcHandler(TcHandlerBase):
|
|||||||
self.seq_count_provider = seq_count_provider
|
self.seq_count_provider = seq_count_provider
|
||||||
self.verif_wrapper = verif_wrapper
|
self.verif_wrapper = verif_wrapper
|
||||||
self.queue_helper = DefaultPusQueueHelper(
|
self.queue_helper = DefaultPusQueueHelper(
|
||||||
queue_wrapper=None,
|
queue_wrapper=QueueWrapper.empty(),
|
||||||
|
tc_sched_timestamp_len=7,
|
||||||
seq_cnt_provider=seq_count_provider,
|
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):
|
def send_cb(self, send_params: SendCbParams):
|
||||||
@ -212,61 +197,74 @@ class TcHandler(TcHandlerBase):
|
|||||||
)
|
)
|
||||||
self.verif_wrapper.add_tc(pus_tc_wrapper.pus_tc)
|
self.verif_wrapper.add_tc(pus_tc_wrapper.pus_tc)
|
||||||
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
||||||
LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
_LOGGER.info(f"Sending {pus_tc_wrapper.pus_tc}")
|
||||||
send_params.com_if.send(raw_tc)
|
send_params.com_if.send(raw_tc)
|
||||||
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
||||||
log_entry = entry_helper.to_log_entry()
|
log_entry = entry_helper.to_log_entry()
|
||||||
LOGGER.info(log_entry.log_str)
|
_LOGGER.info(log_entry.log_str)
|
||||||
|
|
||||||
def queue_finished_cb(self, helper: ProcedureWrapper):
|
def queue_finished_cb(self, info: ProcedureWrapper):
|
||||||
if helper.proc_type == TcProcedureType.DEFAULT:
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
def_proc = helper.to_def_procedure()
|
def_proc = info.to_tree_commanding_procedure()
|
||||||
LOGGER.info(
|
_LOGGER.info(f"Queue handling finished for command {def_proc.cmd_path}")
|
||||||
f"Queue handling finished for service {def_proc.service} and "
|
|
||||||
f"op code {def_proc.op_code}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def feed_cb(self, helper: 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 helper.proc_type == TcProcedureType.DEFAULT:
|
if info.proc_type == TcProcedureType.TREE_COMMANDING:
|
||||||
def_proc = helper.to_def_procedure()
|
def_proc = info.to_tree_commanding_procedure()
|
||||||
service = def_proc.service
|
cmd_path = def_proc.cmd_path
|
||||||
op_code = def_proc.op_code
|
if cmd_path == "/ping":
|
||||||
if (
|
|
||||||
service == CoreServiceList.SERVICE_17
|
|
||||||
or service == CoreServiceList.SERVICE_17_ALT
|
|
||||||
):
|
|
||||||
q.add_log_cmd("Sending PUS ping telecommand")
|
q.add_log_cmd("Sending PUS ping telecommand")
|
||||||
return q.add_pus_tc(PusTelecommand(service=17, subservice=1))
|
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():
|
def main():
|
||||||
|
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()
|
||||||
parser_wrapper.add_def_proc_args()
|
parser_wrapper.add_def_proc_args()
|
||||||
post_args_wrapper = parser_wrapper.parse(hook_obj)
|
|
||||||
params = SetupParams()
|
params = SetupParams()
|
||||||
|
post_args_wrapper = parser_wrapper.parse(hook_obj, params)
|
||||||
proc_wrapper = ProcedureParamsWrapper()
|
proc_wrapper = ProcedureParamsWrapper()
|
||||||
if post_args_wrapper.use_gui:
|
if post_args_wrapper.use_gui:
|
||||||
post_args_wrapper.set_params_without_prompts(params, proc_wrapper)
|
post_args_wrapper.set_params_without_prompts(proc_wrapper)
|
||||||
else:
|
else:
|
||||||
post_args_wrapper.set_params_with_prompts(params, proc_wrapper)
|
post_args_wrapper.set_params_with_prompts(proc_wrapper)
|
||||||
params.apid = EXAMPLE_PUS_APID
|
params.apid = EXAMPLE_PUS_APID
|
||||||
setup_args = SetupWrapper(
|
setup_args = SetupWrapper(
|
||||||
hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_wrapper
|
hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_wrapper
|
||||||
)
|
)
|
||||||
# Create console logger helper and file loggers
|
# Create console logger helper and file loggers
|
||||||
tmtc_logger = RegularTmtcLogWrapper()
|
tmtc_logger = RegularTmtcLogWrapper()
|
||||||
printer = FsfwTmTcPrinter(tmtc_logger.logger)
|
file_logger = tmtc_logger.logger
|
||||||
raw_logger = RawTmtcTimedLogWrapper(when=TimedLogWhen.PER_HOUR, interval=1)
|
raw_logger = RawTmtcTimedLogWrapper(when=TimedLogWhen.PER_HOUR, interval=1)
|
||||||
verificator = PusVerificator()
|
verificator = PusVerificator()
|
||||||
verification_wrapper = VerificationWrapper(verificator, LOGGER, printer.file_logger)
|
verification_wrapper = VerificationWrapper(verificator, _LOGGER, file_logger)
|
||||||
# Create primary TM handler and add it to the CCSDS Packet Handler
|
# Create primary TM handler and add it to the CCSDS Packet Handler
|
||||||
tm_handler = PusHandler(verification_wrapper, printer, raw_logger)
|
tm_handler = PusHandler(file_logger, verification_wrapper, raw_logger)
|
||||||
ccsds_handler = CcsdsTmHandler(generic_handler=None)
|
ccsds_handler = CcsdsTmHandler(generic_handler=None)
|
||||||
ccsds_handler.add_apid_handler(tm_handler)
|
ccsds_handler.add_apid_handler(tm_handler)
|
||||||
|
|
||||||
@ -288,7 +286,7 @@ def main():
|
|||||||
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif state.request == BackendRequest.DELAY_IDLE:
|
elif state.request == BackendRequest.DELAY_IDLE:
|
||||||
LOGGER.info("TMTC Client in IDLE mode")
|
_LOGGER.info("TMTC Client in IDLE mode")
|
||||||
time.sleep(3.0)
|
time.sleep(3.0)
|
||||||
elif state.request == BackendRequest.DELAY_LISTENER:
|
elif state.request == BackendRequest.DELAY_LISTENER:
|
||||||
time.sleep(0.8)
|
time.sleep(0.8)
|
@ -0,0 +1,2 @@
|
|||||||
|
tmtccmd == 8.0.1
|
||||||
|
# -e git+https://github.com/robamu-org/tmtccmd.git@main#egg=tmtccmd
|
55
embedded-examples/stm32h7-nucleo-rtic/src/bin/blinky.rs
Normal file
55
embedded-examples/stm32h7-nucleo-rtic/src/bin/blinky.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//! 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();
|
||||||
|
}
|
||||||
|
}
|
11
embedded-examples/stm32h7-nucleo-rtic/src/bin/hello.rs
Normal file
11
embedded-examples/stm32h7-nucleo-rtic/src/bin/hello.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#![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()
|
||||||
|
}
|
52
embedded-examples/stm32h7-nucleo-rtic/src/lib.rs
Normal file
52
embedded-examples/stm32h7-nucleo-rtic/src/lib.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#![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)
|
||||||
|
}
|
||||||
|
}
|
528
embedded-examples/stm32h7-nucleo-rtic/src/main.rs
Normal file
528
embedded-examples/stm32h7-nucleo-rtic/src/main.rs
Normal file
@ -0,0 +1,528 @@
|
|||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use rtic::app;
|
||||||
|
use rtic_monotonics::systick::Systick;
|
||||||
|
use rtic_monotonics::Monotonic;
|
||||||
|
use satrs::pool::{PoolAddr, PoolProvider, StaticHeaplessMemoryPool};
|
||||||
|
use satrs::static_subpool;
|
||||||
|
// global logger + panicking-behavior + memory layout
|
||||||
|
use satrs_stm32h7_nucleo_rtic as _;
|
||||||
|
use smoltcp::socket::udp::UdpMetadata;
|
||||||
|
use smoltcp::socket::{dhcpv4, udp};
|
||||||
|
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
use embedded_alloc::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();
|
||||||
|
|
||||||
|
// 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((Systick::now() - Systick::ZERO).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 = Systick::now() - Systick::ZERO;
|
||||||
|
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::systick::fugit::MillisDurationU32;
|
||||||
|
use rtic_monotonics::systick::Systick;
|
||||||
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
|
use stm32h7xx_hal::ethernet::{EthernetMAC, PHY};
|
||||||
|
use stm32h7xx_hal::gpio::{Output, Pin};
|
||||||
|
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
|
||||||
|
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||||
|
Systick::start(
|
||||||
|
cx.core.SYST,
|
||||||
|
ccdr.clocks.sys_ck().to_Hz(),
|
||||||
|
systick_mono_token,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Those are used in the smoltcp of the stm32h7xx-hal , I am not fully sure what they are
|
||||||
|
// good for.
|
||||||
|
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(
|
||||||
|
unsafe { SUBPOOL_SMALL.assume_init_mut() },
|
||||||
|
unsafe { SUBPOOL_SMALL_SIZES.assume_init_mut() },
|
||||||
|
SUBPOOL_SMALL_NUM_BLOCKS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.expect("growing heapless memory pool failed");
|
||||||
|
shared_pool
|
||||||
|
.grow(
|
||||||
|
unsafe { SUBPOOL_MEDIUM.assume_init_mut() },
|
||||||
|
unsafe { SUBPOOL_MEDIUM_SIZES.assume_init_mut() },
|
||||||
|
SUBPOOL_MEDIUM_NUM_BLOCKS,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.expect("growing heapless memory pool failed");
|
||||||
|
shared_pool
|
||||||
|
.grow(
|
||||||
|
unsafe { SUBPOOL_LARGE.assume_init_mut() },
|
||||||
|
unsafe { SUBPOOL_LARGE_SIZES.assume_init_mut() },
|
||||||
|
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(HEAP_MEM.as_ptr() 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);
|
||||||
|
Systick::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");
|
||||||
|
}
|
||||||
|
Systick::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);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
Systick::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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
embedded-examples/stm32h7-nucleo-rtic/tests/integration.rs
Normal file
16
embedded-examples/stm32h7-nucleo-rtic/tests/integration.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#![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)
|
||||||
|
}
|
||||||
|
}
|
2
embedded-examples/stm32h7-nucleo-rtic/vscode/.gitignore
vendored
Normal file
2
embedded-examples/stm32h7-nucleo-rtic/vscode/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/settings.json
|
||||||
|
/.cortex-debug.*
|
12
embedded-examples/stm32h7-nucleo-rtic/vscode/extensions.json
Normal file
12
embedded-examples/stm32h7-nucleo-rtic/vscode/extensions.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
// 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": []
|
||||||
|
}
|
22
embedded-examples/stm32h7-nucleo-rtic/vscode/launch.json
Normal file
22
embedded-examples/stm32h7-nucleo-rtic/vscode/launch.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
20
embedded-examples/stm32h7-nucleo-rtic/vscode/tasks.json
Normal file
20
embedded-examples/stm32h7-nucleo-rtic/vscode/tasks.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
260
images/minisim-arch/minisim-arch.graphml
Normal file
260
images/minisim-arch/minisim-arch.graphml
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<?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>
|
BIN
images/minisim-arch/minisim-arch.png
Normal file
BIN
images/minisim-arch/minisim-arch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
650
images/mode-tree/mode-tree.graphml
Normal file
650
images/mode-tree/mode-tree.graphml
Normal file
@ -0,0 +1,650 @@
|
|||||||
|
<?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"/>
|
||||||
|
<node id="n0">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="157.79999999999998" width="452.0" x="959.3461887999997" y="585.7236400000005"/>
|
||||||
|
<y:Fill color2="#000000" 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="145.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="434.0" x="9.0" xml:space="preserve" y="6.399999999999977"><html>
|
||||||
|
<center>
|
||||||
|
<h4>ACS Mode Tree</h4>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
<table border="1">
|
||||||
|
<tr>
|
||||||
|
<th>Mode</th>
|
||||||
|
<th>MGMs</th>
|
||||||
|
<th>SUSs</th>
|
||||||
|
<th>STR</th>
|
||||||
|
<th>MGT</th>
|
||||||
|
<th>RWs</th>
|
||||||
|
<th>ACS CTRL</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>SAFE</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>OFF</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>SAFE</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>IDLE</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>IDLE</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</html><y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="-1.1102230246251565E-16" 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="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="134.60000000000014" width="452.0" x="959.3461887999997" y="757.2703199999999"/>
|
||||||
|
<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="120.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="428.0" x="12.0" xml:space="preserve" y="7.300000000000068"><html>
|
||||||
|
<center>
|
||||||
|
<h4>ACS IDLE Sequence</h4>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
<table border="1">
|
||||||
|
<tr>
|
||||||
|
<th>Step</th>
|
||||||
|
<th>MGMs</th>
|
||||||
|
<th>SUS</th>
|
||||||
|
<th>STR</th>
|
||||||
|
<th>MGT</th>
|
||||||
|
<th>RWs</th>
|
||||||
|
<th>ACS CTRL</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td>NORMAL</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>SAFE</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</html><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="n2">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="32.400000000000034" width="146.79999999999995" x="1128.4" y="313.94999999999993"/>
|
||||||
|
<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="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="113.94921875" x="16.42539062500009" xml:space="preserve" y="6.0515625000000455">ACS Subsystem<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="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="80.39999999999998" x="910.7200000000004" y="407.20000000000005"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="61.837890625" x="9.281054687499932" xml:space="preserve" y="4.03125">MGM
|
||||||
|
Assembly<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="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="32.400000000000034" width="146.79999999999995" x="1133.6000000000001" y="243.3579999999999"/>
|
||||||
|
<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="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.99609375" x="14.401953124999864" xml:space="preserve" y="6.051562499999989">Satellite System<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="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="72.40000000000009" x="1021.1200000000003" y="404.99560000000014"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="61.837890625" x="5.2810546875000455" xml:space="preserve" y="4.03125">SUS
|
||||||
|
Assembly<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="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="67.59999999999991" x="1124.92" y="404.99560000000014"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.599609375" x="5.5001953124999545" xml:space="preserve" y="4.03125">STR
|
||||||
|
Manager<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="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="67.59999999999991" x="1208.74" y="407.20000000000005"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.599609375" x="5.5001953124999545" xml:space="preserve" y="4.03125">MGT
|
||||||
|
Manager<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="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="72.40000000000009" x="1292.56" y="407.20000000000005"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="61.837890625" x="5.2810546875000455" xml:space="preserve" y="4.03125">RW
|
||||||
|
Assembly<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="n9">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="67.59999999999991" x="1381.18" y="407.20000000000005"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="34.732421875" x="16.433789062499955" xml:space="preserve" y="4.03125">ACS
|
||||||
|
CTRL<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="n10">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="946.4000000000004" y="463.3000000000002"/>
|
||||||
|
<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="41.640625" x="1.5396874999999" xml:space="preserve" y="1.0156250000000568">MGM0<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="n11">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="946.4000000000004" y="491.32480000000066"/>
|
||||||
|
<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="41.640625" x="1.5396874999999" xml:space="preserve" y="1.015625">MGM1<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="n12">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="946.4000000000004" y="519.3496000000011"/>
|
||||||
|
<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="41.640625" x="1.5396874999999" xml:space="preserve" y="1.015625">MGM2<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="n13">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="1055.2600000000007" y="463.3000000000002"/>
|
||||||
|
<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="35.65234375" x="4.5338281249999" xml:space="preserve" y="1.015625">SUS0<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="n14">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="1055.2600000000007" y="491.32480000000066"/>
|
||||||
|
<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="15.443359375" x="14.6383203124999" xml:space="preserve" y="1.015625">...<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="n15">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="1055.2600000000007" y="519.3496000000011"/>
|
||||||
|
<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="43.287109375" x="0.7164453124999" xml:space="preserve" y="1.015625">SUS12<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="n16">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="50.0" x="1129.9800000000005" y="482.79120000000023"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="45.0390625" x="2.48046875" xml:space="preserve" y="4.03125">STR
|
||||||
|
Device<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="n17">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="50.0" x="1216.4400000000005" y="482.7912000000002"/>
|
||||||
|
<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="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="45.0390625" x="2.48046875" xml:space="preserve" y="4.031249999999943">MGT
|
||||||
|
Device<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="n18">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="454.7884800000007"/>
|
||||||
|
<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="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW0<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="n19">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="482.3769600000013"/>
|
||||||
|
<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="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW1<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="n20">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="512.3769600000013"/>
|
||||||
|
<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="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW2<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="n21">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="542.3769600000013"/>
|
||||||
|
<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="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW3<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="n22">
|
||||||
|
<data key="d5"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="32.400000000000034" width="59.75999999999999" x="1305.2" y="313.94999999999993"/>
|
||||||
|
<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="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="17.3505859375" x="21.20470703125011" xml:space="preserve" y="6.051562499999989">...<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="n4" target="n2">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-5.199999999999818" sy="0.0" tx="0.0" ty="0.0"/>
|
||||||
|
<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="n2" target="n3">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="1200.63" y="379.7"/>
|
||||||
|
<y:Point x="950.9200000000003" y="379.7"/>
|
||||||
|
</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="n2" target="n5">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="1200.63" y="379.7"/>
|
||||||
|
<y:Point x="1057.3200000000004" y="379.7"/>
|
||||||
|
</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="e3" source="n2" target="n6">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="1200.63" y="379.7"/>
|
||||||
|
<y:Point x="1158.72" y="379.7"/>
|
||||||
|
</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="e4" source="n2" target="n7">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-1.1700000000000728" sy="0.0" tx="-11.29999999999859" ty="0.0">
|
||||||
|
<y:Point x="1200.63" y="379.7"/>
|
||||||
|
<y:Point x="1231.2400000000014" y="379.7"/>
|
||||||
|
</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="n2" target="n8">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="1200.63" y="379.7"/>
|
||||||
|
<y:Point x="1328.76" y="379.7"/>
|
||||||
|
</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="n9">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="1200.63" y="379.7"/>
|
||||||
|
<y:Point x="1414.98" y="379.7"/>
|
||||||
|
</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="e7" source="n3" target="n10">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-28.009798234586242" sy="0.0" tx="13.11999999999989" ty="0.0">
|
||||||
|
<y:Point x="922.9102017654141" y="473.3000000000002"/>
|
||||||
|
</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="e8" source="n3" target="n11">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-28.009798234586242" sy="17.706497359551577" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="922.9102017654141" y="501.32480000000066"/>
|
||||||
|
</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="e9" source="n3" target="n12">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-28.009798234586242" sy="12.053248679775777" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="922.9102017654141" y="529.3496000000011"/>
|
||||||
|
</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="e10" source="n5" target="n13">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-24.18711646917177" sy="11.599696230174459" tx="-20.855199999999513" ty="-1.9751999999995178">
|
||||||
|
<y:Point x="1033.1328835308286" y="471.32480000000066"/>
|
||||||
|
</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="e11" source="n5" target="n14">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-24.18711646917177" sy="0.0" tx="-20.855199999999513" ty="-3.024800000000596">
|
||||||
|
<y:Point x="1033.1328835308286" y="498.30000000000007"/>
|
||||||
|
</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="e12" source="n6" target="n16">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="0.0" sy="0.0" tx="3.7399999999995543" ty="0.0"/>
|
||||||
|
<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="e13" source="n7" target="n17">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="0.0" sy="0.0" tx="1.0999999999994543" ty="0.0"/>
|
||||||
|
<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="e14" source="n8" target="n18">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-23.248152938343992" sy="14.026599270174415" tx="-13.026188800000227" ty="0.33167708069714763">
|
||||||
|
<y:Point x="1305.511847061656" y="465.1201570806978"/>
|
||||||
|
</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="e15" source="n8" target="n19">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-23.248152938343992" sy="0.0" tx="-13.026188800000227" ty="0.0">
|
||||||
|
<y:Point x="1305.511847061656" y="492.3769600000013"/>
|
||||||
|
</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="e16" source="n8" target="n20">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-23.248152938343992" sy="0.0" tx="-13.026188800000227" ty="0.0">
|
||||||
|
<y:Point x="1305.511847061656" y="522.3769600000013"/>
|
||||||
|
</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="e17" source="n8" target="n21">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-23.248152938343992" sy="0.0" tx="-13.026188800000227" ty="-1.1368683772161603E-13">
|
||||||
|
<y:Point x="1305.511847061656" y="552.3769600000012"/>
|
||||||
|
</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="e18" source="n4" target="n22">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-5.199999999999818" sy="16.200000000000045" tx="0.0" ty="0.0">
|
||||||
|
<y:Point x="1201.8000000000002" y="290.43031999999994"/>
|
||||||
|
<y:Point x="1335.08" y="290.43031999999994"/>
|
||||||
|
</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="e19" source="n5" target="n15">
|
||||||
|
<data key="d9"/>
|
||||||
|
<data key="d10">
|
||||||
|
<y:PolyLineEdge>
|
||||||
|
<y:Path sx="-24.18711646917177" sy="0.0" tx="-20.855199999999513" ty="0.0">
|
||||||
|
<y:Point x="1033.1328835308286" y="529.3496000000011"/>
|
||||||
|
</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>
|
||||||
|
</graph>
|
||||||
|
<data key="d7">
|
||||||
|
<y:Resources/>
|
||||||
|
</data>
|
||||||
|
</graphml>
|
1062
images/mode-tree/mode-tree.pdf
Normal file
1062
images/mode-tree/mode-tree.pdf
Normal file
File diff suppressed because it is too large
Load Diff
285
images/satrs-example-goal/satrs-example-goal.graphml
Normal file
285
images/satrs-example-goal/satrs-example-goal.graphml
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
<?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="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="261.7582999999998" width="631.1152000000001" x="810.8848" y="142.00000000000003"/>
|
||||||
|
<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="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="296.09375" x="26.51035059931519" xml:space="preserve" y="9.42072533356739">satrs-example Component Structure<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="-0.5" nodeRatioX="-0.4579944349315068" nodeRatioY="-0.46400983146067415" 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" yfiles.foldertype="group">
|
||||||
|
<data key="d4" xml:space="preserve"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ProxyAutoBoundsNode>
|
||||||
|
<y:Realizers active="0">
|
||||||
|
<y:GroupNode>
|
||||||
|
<y:Geometry height="217.68500000000003" width="166.9147999999998" x="819.9999999999999" y="180.434265625"/>
|
||||||
|
<y:Fill hasColor="false" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="left" autoSizePolicy="node_width" borderDistance="5.0" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="166.9147999999998" x="13.181757989188668" xml:space="preserve" y="6.728692946816665">Application
|
||||||
|
Components<y:LabelModel><y:SmartNodeLabelModel distance="5.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.4210270270270303" labelRatioY="-0.5" nodeRatioX="0.5" nodeRatioY="-0.46908977216245173" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||||
|
<y:Shape type="rectangle"/>
|
||||||
|
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||||
|
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||||
|
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="39" topF="38.90500000000003"/>
|
||||||
|
</y:GroupNode>
|
||||||
|
<y:GroupNode>
|
||||||
|
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||||
|
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 3</y:NodeLabel>
|
||||||
|
<y:Shape type="roundrectangle"/>
|
||||||
|
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||||
|
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||||
|
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||||
|
</y:GroupNode>
|
||||||
|
</y:Realizers>
|
||||||
|
</y:ProxyAutoBoundsNode>
|
||||||
|
</data>
|
||||||
|
<graph edgedefault="directed" id="n1:">
|
||||||
|
<node id="n1::n0">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="136.9147999999998" x="834.9999999999999" y="234.33926562500002"/>
|
||||||
|
<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="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="113.94921875" x="11.482790624999893" xml:space="preserve" y="4.851562500000028">ACS Subsystem<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::n1">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="136.9147999999998" x="834.9999999999999" y="270.5687968750001"/>
|
||||||
|
<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="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="111.884765625" x="12.515017187499893" xml:space="preserve" y="4.8515625">EPS Subsystem<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::n2">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="136.9147999999998" x="834.9999999999999" y="306.84403125000006"/>
|
||||||
|
<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="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="112.923828125" x="11.995485937499893" xml:space="preserve" y="4.8515625">TCS Subsystem<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::n3">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="136.9147999999998" x="834.9999999999999" y="343.119265625"/>
|
||||||
|
<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="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.59375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.259765625" x="27.827517187499893" xml:space="preserve" y="1.703125">Payload
|
||||||
|
Subsystem<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>
|
||||||
|
</graph>
|
||||||
|
</node>
|
||||||
|
<node id="n2" yfiles.foldertype="group">
|
||||||
|
<data key="d4" xml:space="preserve"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ProxyAutoBoundsNode>
|
||||||
|
<y:Realizers active="0">
|
||||||
|
<y:GroupNode>
|
||||||
|
<y:Geometry height="261.7582999999998" width="498.25116669136776" x="971.9147999999997" y="151.36096562500023"/>
|
||||||
|
<y:Fill hasColor="false" transparent="false"/>
|
||||||
|
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="false" width="4.0" x="247.12558334568382" y="0.0"/>
|
||||||
|
<y:Shape type="roundrectangle"/>
|
||||||
|
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||||
|
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||||
|
<y:BorderInsets bottom="0" bottomF="0.0" left="7" leftF="7.00529999999992" right="3" rightF="3.313607121059931" top="14" topF="14.073299999999762"/>
|
||||||
|
</y:GroupNode>
|
||||||
|
<y:GroupNode>
|
||||||
|
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||||
|
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 4</y:NodeLabel>
|
||||||
|
<y:Shape type="roundrectangle"/>
|
||||||
|
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||||
|
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||||
|
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||||
|
</y:GroupNode>
|
||||||
|
</y:Realizers>
|
||||||
|
</y:ProxyAutoBoundsNode>
|
||||||
|
</data>
|
||||||
|
<graph edgedefault="directed" id="n2:">
|
||||||
|
<node id="n2::n0" yfiles.foldertype="group">
|
||||||
|
<data key="d4" xml:space="preserve"/>
|
||||||
|
<data key="d6">
|
||||||
|
<y:ProxyAutoBoundsNode>
|
||||||
|
<y:Realizers active="0">
|
||||||
|
<y:GroupNode>
|
||||||
|
<y:Geometry height="217.68500000000003" width="441.0158999999999" x="993.9200999999996" y="180.434265625"/>
|
||||||
|
<y:Fill hasColor="false" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="left" autoSizePolicy="node_width" borderDistance="5.0" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="441.0158999999999" x="16.916359570308032" xml:space="preserve" y="5.969557999968089">Generic Components<y:LabelModel><y:SmartNodeLabelModel distance="5.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.46164229096885634" labelRatioY="-0.5" nodeRatioX="0.5" nodeRatioY="-0.4725770815629552" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||||
|
<y:Shape type="rectangle"/>
|
||||||
|
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||||
|
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||||
|
<y:BorderInsets bottom="22" bottomF="22.006468749999954" left="0" leftF="0.0" right="0" rightF="0.0" top="28" topF="27.68500000000006"/>
|
||||||
|
</y:GroupNode>
|
||||||
|
<y:GroupNode>
|
||||||
|
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||||
|
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 2</y:NodeLabel>
|
||||||
|
<y:Shape type="roundrectangle"/>
|
||||||
|
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||||
|
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||||
|
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||||
|
</y:GroupNode>
|
||||||
|
</y:Realizers>
|
||||||
|
</y:ProxyAutoBoundsNode>
|
||||||
|
</data>
|
||||||
|
<graph edgedefault="directed" id="n2::n0:">
|
||||||
|
<node id="n2::n0::n0">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="125.0" x="1151.9280499999995" y="281.84403125000006"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="58.837890625" x="33.0810546875" xml:space="preserve" y="4.8515625">TM Sink<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="n2::n0::n1">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="125.0" x="1151.9280499999995" y="330.5687968750001"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="105.7119140625" x="9.64404296875" xml:space="preserve" y="4.8515625">TCP/IP Servers<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="n2::n0::n2">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="125.0" x="1294.9359999999995" y="281.84403125000006"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="75.1689453125" x="24.91552734375" xml:space="preserve" y="4.8515625">TC Source<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="n2::n0::n3">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="125.0" x="1008.9200999999996" y="223.11926562500005"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="109.9228515625" x="7.53857421875" xml:space="preserve" y="4.8515625">Event Manager<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="n2::n0::n4">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="125.0" x="1008.9200999999996" y="267.1160312500001"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.7021484375" x="3.64892578125" xml:space="preserve" y="4.8515625">PUS Distribution<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="n2::n0::n5">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="40.0" width="125.0" x="1151.9280499999995" y="223.11926562500005"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="dashed" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.59375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="84.1650390625" x="20.41748046875" xml:space="preserve" y="1.7031249999999716">Shared
|
||||||
|
TMTC Pools<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="n2::n0::n6">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="50.0" width="125.0" x="1008.9200999999996" y="311.1127968750001"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.59375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="78.12890625" x="23.435546875" xml:space="preserve" y="6.703125">Satellite
|
||||||
|
Mode Tree<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="n2::n0::n7">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="30.0" width="125.0" x="1294.9359999999995" y="223.11926562500005"/>
|
||||||
|
<y:Fill color="#CCFFFF" transparent="false"/>
|
||||||
|
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||||
|
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="74.7861328125" x="25.10693359375" xml:space="preserve" y="4.8515625"> PUS Stack<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>
|
||||||
|
</graph>
|
||||||
|
</node>
|
||||||
|
</graph>
|
||||||
|
</node>
|
||||||
|
<node id="n3">
|
||||||
|
<data key="d6">
|
||||||
|
<y:ShapeNode>
|
||||||
|
<y:Geometry height="57.265600000000006" width="631.1152" x="810.8847999999999" y="411.39428125"/>
|
||||||
|
<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="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="261.8125" x="166.89412267941418" xml:space="preserve" y="3.144146301369915">satrs-minisim
|
||||||
|
Simulator based on asynchronix<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" 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="50.0" width="631.1152000000002" x="810.8847999999998" y="476.2958625000002"/>
|
||||||
|
<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="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="374.8359375" x="110.3824039294143" xml:space="preserve" y="0.12842465753431043">pytmtc
|
||||||
|
Command-line interface based TMTC handling<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||||
|
<y:Shape type="rectangle"/>
|
||||||
|
</y:ShapeNode>
|
||||||
|
</data>
|
||||||
|
</node>
|
||||||
|
</graph>
|
||||||
|
<data key="d7">
|
||||||
|
<y:Resources/>
|
||||||
|
</data>
|
||||||
|
</graphml>
|
1029
images/satrs-example-goal/satrs-example-goal.pdf
Normal file
1029
images/satrs-example-goal/satrs-example-goal.pdf
Normal file
File diff suppressed because it is too large
Load Diff
BIN
misc/satrs-logo-v2.png
Normal file
BIN
misc/satrs-logo-v2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
@ -15,7 +15,7 @@ action commanding could look like.
|
|||||||
2. Target ID and Action String based. The target ID is the same as in the first proposal, but
|
2. Target ID and Action String based. The target ID is the same as in the first proposal, but
|
||||||
the unique action is identified by a string.
|
the unique action is identified by a string.
|
||||||
|
|
||||||
The framework provides an `ActionRequest` abstraction to model both of these cases.
|
The library provides an `ActionRequest` abstraction to model both of these cases.
|
||||||
|
|
||||||
## Commanding with ECSS PUS 8
|
## Commanding with ECSS PUS 8
|
||||||
|
|
||||||
|
@ -17,14 +17,14 @@ it is still centered around small packets. `sat-rs` provides support for these E
|
|||||||
standards and also attempts to fill the gap to the internet protocol by providing the following
|
standards and also attempts to fill the gap to the internet protocol by providing the following
|
||||||
components.
|
components.
|
||||||
|
|
||||||
1. [UDP TMTC Server](https://docs.rs/satrs-core/0.1.0-alpha.0/satrs_core/hal/host/udp_server/index.html).
|
1. [UDP TMTC Server](https://docs.rs/satrs/latest/satrs/hal/std/udp_server/index.html).
|
||||||
UDP is already packet based which makes it an excellent fit for exchanging space packets.
|
UDP is already packet based which makes it an excellent fit for exchanging space packets.
|
||||||
2. [TCP TMTC Server Components](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/index.html).
|
2. [TCP TMTC Server Components](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/index.html).
|
||||||
TCP is a stream based protocol, so the framework provides building blocks to parse telemetry
|
TCP is a stream based protocol, so the library provides building blocks to parse telemetry
|
||||||
from an arbitrary bytestream. Two concrete implementations are provided:
|
from an arbitrary bytestream. Two concrete implementations are provided:
|
||||||
- [TCP spacepackets server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
|
- [TCP spacepackets server](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
|
||||||
to parse tightly packed CCSDS Spacepackets.
|
to parse tightly packed CCSDS Spacepackets.
|
||||||
- [TCP COBS server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/struct.TcpTmtcInCobsServer.html)
|
- [TCP COBS server](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/struct.TcpTmtcInCobsServer.html)
|
||||||
to parse generic frames wrapped with the
|
to parse generic frames wrapped with the
|
||||||
[COBS protocol](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing).
|
[COBS protocol](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing).
|
||||||
|
|
||||||
@ -39,8 +39,12 @@ task might be to store all arriving telemetry persistently. This is especially i
|
|||||||
space systems which do not have permanent contact like low-earth-orbit (LEO) satellites.
|
space systems which do not have permanent contact like low-earth-orbit (LEO) satellites.
|
||||||
|
|
||||||
The most important task of a TC source is to deliver the telecommands to the correct recipients.
|
The most important task of a TC source is to deliver the telecommands to the correct recipients.
|
||||||
For modern component oriented software using message passing, this usually includes staged
|
For component oriented software using message passing, this usually includes staged demultiplexing
|
||||||
demultiplexing components to determine where a command needs to be sent.
|
components to determine where a command needs to be sent.
|
||||||
|
|
||||||
|
Using a generic concept of a TC source and a TM sink as part of the software design simplifies
|
||||||
|
the flexibility of the TMTC infrastructure: Newly added TM generators and TC receiver only have to
|
||||||
|
forward their generated or received packets to those handler objects.
|
||||||
|
|
||||||
# Low-level protocols and the bridge to the communcation subsystem
|
# Low-level protocols and the bridge to the communcation subsystem
|
||||||
|
|
||||||
|
@ -26,15 +26,17 @@ For example, a very small telecommand (TC) pool might look like this:
|
|||||||
![Example Pool](images/pools/static-pools.png)
|
![Example Pool](images/pools/static-pools.png)
|
||||||
|
|
||||||
The core of the pool abstractions is the
|
The core of the pool abstractions is the
|
||||||
[PoolProvider trait](https://docs.rs/satrs-core/0.1.0-alpha.3/satrs_core/pool/trait.PoolProvider.html).
|
[PoolProvider trait](https://docs.rs/satrs/latest/satrs/pool/trait.PoolProvider.html).
|
||||||
This trait specifies the general API a pool structure should have without making assumption
|
This trait specifies the general API a pool structure should have without making assumption
|
||||||
of how the data is stored.
|
of how the data is stored.
|
||||||
|
|
||||||
This trait is implemented by a static memory pool implementation.
|
This trait is implemented by a static memory pool implementation.
|
||||||
The code to generate this static pool would look like this:
|
The code to generate this static pool would look like this:
|
||||||
|
|
||||||
```rust
|
<!-- Would be nice to test this code sample, but need to wait
|
||||||
use satrs_core::pool::{StaticMemoryPool, StaticPoolConfig};
|
for https://github.com/rust-lang/mdBook/issues/706 to be merged.. -->
|
||||||
|
```rust, ignore
|
||||||
|
use satrs::pool::{StaticMemoryPool, StaticPoolConfig};
|
||||||
|
|
||||||
let tc_pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![
|
let tc_pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![
|
||||||
(6, 16),
|
(6, 16),
|
||||||
@ -52,8 +54,8 @@ inter-process communication (IPC) data.
|
|||||||
|
|
||||||
You can read
|
You can read
|
||||||
|
|
||||||
- [`StaticPoolConfig` API](https://docs.rs/satrs-core/0.1.0-alpha.3/satrs_core/pool/struct.StaticPoolConfig.html)
|
- [`StaticPoolConfig` API](https://docs.rs/satrs/latest/satrs/pool/struct.StaticPoolConfig.html)
|
||||||
- [`StaticMemoryPool` API](https://docs.rs/satrs-core/0.1.0-alpha.3/satrs_core/pool/struct.StaticMemoryPool.html)
|
- [`StaticMemoryPool` API](https://docs.rs/satrs/latest/satrs/pool/struct.StaticMemoryPool.html)
|
||||||
|
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
# Framework Design
|
# Library Design
|
||||||
|
|
||||||
Satellites and space systems in general are complex systems with a wide range of requirements for
|
Satellites and space systems in general are complex systems with a wide range of requirements for
|
||||||
both the hardware and the software. Consequently, the general design of the framework is centered
|
both the hardware and the software. Consequently, the general design of the library is centered
|
||||||
around many light-weight components which try to impose as few restrictions as possible on how to
|
around many light-weight components which try to impose as few restrictions as possible on how to
|
||||||
solve certain problems.
|
solve certain problems. This is also the reason why sat-rs is explicitely called a library
|
||||||
|
instead of a framework.
|
||||||
|
|
||||||
There are still a lot of common patterns and architectures across these systems where guidance
|
There are still a lot of common patterns and architectures across these systems where guidance
|
||||||
of how to solve a problem and a common structure would still be extremely useful to avoid pitfalls
|
of how to solve a problem and a common structure would still be extremely useful to avoid pitfalls
|
||||||
which were already solved and to avoid boilerplate code. This framework tries to provide this
|
which were already solved and to avoid boilerplate code. This library tries to provide this
|
||||||
structure and guidance the following way:
|
structure and guidance the following way:
|
||||||
|
|
||||||
1. Providing this book which explains the architecture and design patterns in respect to common
|
1. Providing this book which explains the architecture and design patterns in respect to common
|
||||||
@ -18,7 +19,7 @@ structure and guidance the following way:
|
|||||||
3. Providing a good test suite. This includes both unittests and integration tests. The integration
|
3. Providing a good test suite. This includes both unittests and integration tests. The integration
|
||||||
tests can also serve as smaller usage examples than the large `satrs-example` application.
|
tests can also serve as smaller usage examples than the large `satrs-example` application.
|
||||||
|
|
||||||
This framework has special support for standards used in the space industry. This especially
|
This library has special support for standards used in the space industry. This especially
|
||||||
includes standards provided by Consultative Committee for Space Data Systems (CCSDS) and European
|
includes standards provided by Consultative Committee for Space Data Systems (CCSDS) and European
|
||||||
Cooperation for Space Standardization (ECSS). It does not enforce using any of those standards,
|
Cooperation for Space Standardization (ECSS). It does not enforce using any of those standards,
|
||||||
but it is always recommended to use some sort of standard for interoperability.
|
but it is always recommended to use some sort of standard for interoperability.
|
||||||
@ -30,10 +31,10 @@ Flying Laptop Project by the University of Stuttgart with Airbus Defence and Spa
|
|||||||
It has flight heritage through the 2 mssions [FLP](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/flying-laptop/)
|
It has flight heritage through the 2 mssions [FLP](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/flying-laptop/)
|
||||||
and [EIVE](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/EIVE/).
|
and [EIVE](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/EIVE/).
|
||||||
Therefore, a lot of the design concepts were ported more or less unchanged to the `sat-rs`
|
Therefore, a lot of the design concepts were ported more or less unchanged to the `sat-rs`
|
||||||
framework.
|
library.
|
||||||
FLP is a medium-size small satellite with a higher budget and longer development time than EIVE,
|
FLP is a medium-size small satellite with a higher budget and longer development time than EIVE,
|
||||||
which allowed to build a highly reliable system while EIVE is a smaller 6U+ cubesat which had a
|
which allowed to build a highly reliable system while EIVE is a smaller 6U+ cubesat which had a
|
||||||
shorter development cycle and was built using cheaper COTS components. This framework also tries
|
shorter development cycle and was built using cheaper COTS components. This library also tries
|
||||||
to accumulate the knowledge of developing the OBSW and operating the satellite for both these
|
to accumulate the knowledge of developing the OBSW and operating the satellite for both these
|
||||||
different systems and provide a solution for a wider range of small satellite systems.
|
different systems and provide a solution for a wider range of small satellite systems.
|
||||||
|
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
# Events
|
# Events
|
||||||
|
|
||||||
Events can be an extremely important mechanism used for remote systems to monitor unexpected
|
Events are an important mechanism used for remote systems to monitor unexpected
|
||||||
or expected anomalies and events occuring on these systems. They are oftentimes tied to
|
or expected anomalies and events occuring on these systems.
|
||||||
|
|
||||||
|
One common use case for events on remote systems is to offer a light-weight publish-subscribe
|
||||||
|
mechanism and IPC mechanism for software and hardware events which are also packaged as telemetry
|
||||||
|
(TM) or can trigger a system response. They can also be tied to
|
||||||
Fault Detection, Isolation and Recovery (FDIR) operations, which need to happen autonomously.
|
Fault Detection, Isolation and Recovery (FDIR) operations, which need to happen autonomously.
|
||||||
|
|
||||||
Events can also be used as a convenient Inter-Process Communication (IPC) mechansism, which is
|
The PUS Service 5 standardizes how the ground interface for events might look like, but does not
|
||||||
also observable for the Ground segment. The PUS Service 5 standardizes how the ground interface
|
specify how other software components might react to those events. There is the PUS Service 19,
|
||||||
for events might look like, but does not specify how other software components might react
|
which might be used for that purpose, but the event components recommended by this framework do not
|
||||||
to those events. There is the PUS Service 19, which might be used for that purpose, but the
|
rely on the present of this service.
|
||||||
event components recommended by this framework do not really need this service.
|
|
||||||
|
|
||||||
The following images shows how the flow of events could look like in a system where components
|
The following images shows how the flow of events could look like in a system where components
|
||||||
can generate events, and where other system components might be interested in those events:
|
can generate events, and where other system components might be interested in those events:
|
||||||
|
|
||||||
![Event flow](images/events/event_man_arch.png)
|
![Event flow](images/events/event_man_arch.png)
|
||||||
|
|
||||||
|
For the concrete implementation of your own event management and/or event routing system, you
|
||||||
|
can have a look at the event management documentation inside the
|
||||||
|
[API documentation](https://docs.rs/satrs/latest/satrs/event_man/index.html) where you can also
|
||||||
|
find references to all examples.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# sat-rs Example Application
|
# sat-rs Example Application
|
||||||
|
|
||||||
The `sat-rs` framework includes a monolithic example application which can be found inside
|
The `sat-rs` library includes a monolithic example application which can be found inside
|
||||||
the [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
|
the [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
|
||||||
subdirectory of the repository. The primary purpose of this example application is to show how
|
subdirectory of the repository. The primary purpose of this example application is to show how
|
||||||
the various components of the sat-rs framework could be used as part of a larger on-board
|
the various components of the sat-rs framework could be used as part of a larger on-board
|
||||||
@ -23,11 +23,11 @@ Some additional explanation is provided for the various components.
|
|||||||
The example includes a UDP and TCP server to receive telecommands and poll telemetry from. This
|
The example includes a UDP and TCP server to receive telecommands and poll telemetry from. This
|
||||||
might be an optional component for an OBSW which is only used during the development phase on
|
might be an optional component for an OBSW which is only used during the development phase on
|
||||||
ground. The UDP server is strongly based on the
|
ground. The UDP server is strongly based on the
|
||||||
[UDP TC server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/udp_server/struct.UdpTcServer.html).
|
[UDP TC server](https://docs.rs/satrs/latest/satrs/hal/std/udp_server/struct.UdpTcServer.html).
|
||||||
This server component is wrapped by a TMTC server which handles all telemetry to the last connected
|
This server component is wrapped by a TMTC server which handles all telemetry to the last connected
|
||||||
client.
|
client.
|
||||||
|
|
||||||
The TCP server is based on the [TCP Spacepacket Server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
|
The TCP server is based on the [TCP Spacepacket Server](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
|
||||||
class. It parses space packets by using the CCSDS space packet ID as the packet
|
class. It parses space packets by using the CCSDS space packet ID as the packet
|
||||||
start delimiter. All available telemetry will be sent back to a client after having read all
|
start delimiter. All available telemetry will be sent back to a client after having read all
|
||||||
telecommands from the client.
|
telecommands from the client.
|
||||||
@ -51,13 +51,13 @@ services. This currently includes the following services:
|
|||||||
|
|
||||||
- Service 1 for telecommand verification. The verification handling is handled locally: Each
|
- Service 1 for telecommand verification. The verification handling is handled locally: Each
|
||||||
component which generates verification telemetry in some shape or form receives a
|
component which generates verification telemetry in some shape or form receives a
|
||||||
[reporter](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/pus/verification/struct.VerificationReporterWithSender.html)
|
[reporter](https://docs.rs/satrs/latest/satrs/pus/verification/struct.VerificationReporterWithSender.html)
|
||||||
object which can be used to send PUS 1 verification telemetry to the TM funnel.
|
object which can be used to send PUS 1 verification telemetry to the TM funnel.
|
||||||
- Service 3 for housekeeping telemetry handling.
|
- Service 3 for housekeeping telemetry handling.
|
||||||
- Service 5 for management and downlink of on-board events.
|
- Service 5 for management and downlink of on-board events.
|
||||||
- Service 8 for handling on-board actions.
|
- Service 8 for handling on-board actions.
|
||||||
- Service 11 for scheduling telecommands to be released at a specific time. This component
|
- Service 11 for scheduling telecommands to be released at a specific time. This component
|
||||||
uses the [PUS scheduler class](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/pus/scheduler/alloc_mod/struct.PusScheduler.html)
|
uses the [PUS scheduler class](https://docs.rs/satrs/latest/satrs/pus/scheduler/alloc_mod/struct.PusScheduler.html)
|
||||||
which performs the core logic of scheduling telecommands. All telecommands released by the
|
which performs the core logic of scheduling telecommands. All telecommands released by the
|
||||||
scheduler are sent to the central TC source using a message.
|
scheduler are sent to the central TC source using a message.
|
||||||
- Service 17 for test purposes like pings.
|
- Service 17 for test purposes like pings.
|
||||||
@ -65,10 +65,10 @@ services. This currently includes the following services:
|
|||||||
### Event Management Component
|
### Event Management Component
|
||||||
|
|
||||||
An event manager based on the sat-rs
|
An event manager based on the sat-rs
|
||||||
[event manager component](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/event_man/index.html)
|
[event manager component](https://docs.rs/satrs/latest/satrs/event_man/index.html)
|
||||||
is provided to handle the event IPC and FDIR mechanism. The event message are converted to PUS 5
|
is provided to handle the event IPC and FDIR mechanism. The event message are converted to PUS 5
|
||||||
telemetry by the
|
telemetry by the
|
||||||
[PUS event dispatcher](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/pus/event_man/alloc_mod/struct.PusEventDispatcher.html).
|
[PUS event dispatcher](https://docs.rs/satrs/latest/satrs/pus/event_man/alloc_mod/struct.PusEventDispatcher.html).
|
||||||
|
|
||||||
You can read the [events](./events.md) chapter for more in-depth information about event management.
|
You can read the [events](./events.md) chapter for more in-depth information about event management.
|
||||||
|
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
The sat-rs book
|
The sat-rs book
|
||||||
======
|
======
|
||||||
|
|
||||||
This book is the primary information resource for the [sat-rs framework](https://egit.irs.uni-stuttgart.de/rust/sat-rs)
|
This book is the primary information resource for the [sat-rs library](https://egit.irs.uni-stuttgart.de/rust/sat-rs)
|
||||||
in addition to the regular API documentation. It contains the following resources:
|
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
|
||||||
|
|
||||||
The primary goal of the sat-rs framework is to provide re-usable components
|
The primary goal of the sat-rs library is to provide re-usable components
|
||||||
to write on-board software for remote systems like rovers or satellites. It is specifically written
|
to write on-board software for remote systems like rovers or satellites. It is specifically written
|
||||||
for the special requirements for these systems.
|
for the special requirements for these systems.
|
||||||
|
|
||||||
|
It should be noted that sat-rs is early-stage software. Important features are missing. New releases
|
||||||
|
with breaking changes are released regularly, with all changes documented inside respective
|
||||||
|
changelog files. You should only use this library if your are willing to work in this
|
||||||
|
environment.
|
||||||
|
|
||||||
A lot of the architecture and general design considerations are based on the
|
A lot of the architecture and general design considerations are based on the
|
||||||
[FSFW](https://egit.irs.uni-stuttgart.de/fsfw/fsfw) C++ framework which has flight heritage
|
[FSFW](https://egit.irs.uni-stuttgart.de/fsfw/fsfw) C++ framework which has flight heritage
|
||||||
through the 2 missions [FLP](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/flying-laptop/)
|
through the 2 missions [FLP](https://www.irs.uni-stuttgart.de/en/research/satellitetechnology-and-instruments/smallsatelliteprogram/flying-laptop/)
|
||||||
@ -26,4 +29,21 @@ 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 example application. The [`satrs-minisim`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-minisim)
|
||||||
|
applicatin complements the example application and can be used to simulate some physical devices
|
||||||
|
for the `satrs-example` device handlers.
|
||||||
|
|
||||||
|
# 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,11 +1,11 @@
|
|||||||
# Modes
|
# Modes
|
||||||
|
|
||||||
Modes are an extremely useful concept for complex system in general. They also allow simplified
|
Modes are an extremely useful concept to model complex systems. They allow simplified
|
||||||
system reasoning for both system operators and OBSW developers. They model the behaviour of a
|
system reasoning for both system operators and OBSW developers. They also provide a way to alter
|
||||||
component and also provide observability of a system. A few examples of how to model
|
the behaviour of a component and also provide observability of a system. A few examples of how to
|
||||||
different components of a space system with modes will be given.
|
model the mode of different components within a space system with modes will be given.
|
||||||
|
|
||||||
## Modelling a pyhsical devices with modes
|
## Pyhsical device component with modes
|
||||||
|
|
||||||
The following simple mode scheme with the following three mode
|
The following simple mode scheme with the following three mode
|
||||||
|
|
||||||
@ -13,7 +13,8 @@ The following simple mode scheme with the following three mode
|
|||||||
- `ON`
|
- `ON`
|
||||||
- `NORMAL`
|
- `NORMAL`
|
||||||
|
|
||||||
can be applied to a large number of simpler devices of a remote system, for example sensors.
|
can be applied to a large number of simpler device controllers of a remote system, for example
|
||||||
|
sensors.
|
||||||
|
|
||||||
1. `OFF` means that a device is physically switched off, and the corresponding software component
|
1. `OFF` means that a device is physically switched off, and the corresponding software component
|
||||||
does not poll the device regularly.
|
does not poll the device regularly.
|
||||||
@ -31,7 +32,7 @@ for the majority of devices:
|
|||||||
2. `NORMAL` or `ON` to `OFF`: Any important shutdown configuration or handling must be performed
|
2. `NORMAL` or `ON` to `OFF`: Any important shutdown configuration or handling must be performed
|
||||||
before powering off the device.
|
before powering off the device.
|
||||||
|
|
||||||
## Modelling a controller with modes
|
## Controller components with modes
|
||||||
|
|
||||||
Controller components are not modelling physical devices, but a mode scheme is still the best
|
Controller components are not modelling physical devices, but a mode scheme is still the best
|
||||||
way to model most of these components.
|
way to model most of these components.
|
||||||
|
66
satrs-example-stm32f3-disco/.vscode/launch.json
vendored
66
satrs-example-stm32f3-disco/.vscode/launch.json
vendored
@ -1,66 +0,0 @@
|
|||||||
{
|
|
||||||
/*
|
|
||||||
* Requires the Rust Language Server (RLS) and Cortex-Debug extensions
|
|
||||||
* https://marketplace.visualstudio.com/items?itemName=rust-lang.rust
|
|
||||||
* https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug
|
|
||||||
*/
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
/* Launches debug session for currently open example */
|
|
||||||
"type": "cortex-debug",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug",
|
|
||||||
"servertype": "openocd",
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"preLaunchTask": "cargo build",
|
|
||||||
"runToEntryPoint": "true",
|
|
||||||
"executable": "./target/thumbv7em-none-eabihf/debug/satrs-example-stm32f3-disco",
|
|
||||||
"preLaunchCommands": ["break rust_begin_unwind"],
|
|
||||||
"device": "STM32F303VCT6",
|
|
||||||
"configFiles": [
|
|
||||||
"${workspaceRoot}/.vscode/openocd-helpers.tcl",
|
|
||||||
"interface/stlink.cfg",
|
|
||||||
"target/stm32f3x.cfg"
|
|
||||||
],
|
|
||||||
"svdFile": "${env:HOME}/.svd/STM32F303.svd",
|
|
||||||
"swoConfig": {
|
|
||||||
"enabled": true,
|
|
||||||
"cpuFrequency": 8000000,
|
|
||||||
"swoFrequency": 2000000,
|
|
||||||
"source": "probe",
|
|
||||||
"decoders": [
|
|
||||||
{ "type": "console", "label": "ITM", "port": 0 }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/* Launches debug session for currently open example */
|
|
||||||
"type": "cortex-debug",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Release",
|
|
||||||
"servertype": "openocd",
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"preLaunchTask": "cargo build",
|
|
||||||
"runToEntryPoint": "true",
|
|
||||||
"executable": "./target/thumbv7em-none-eabihf/release/satrs-example-stm32f3-disco",
|
|
||||||
"preLaunchCommands": ["break rust_begin_unwind"],
|
|
||||||
"device": "STM32F303VCT6",
|
|
||||||
"configFiles": [
|
|
||||||
"${workspaceRoot}/.vscode/openocd-helpers.tcl",
|
|
||||||
"interface/stlink.cfg",
|
|
||||||
"target/stm32f3x.cfg"
|
|
||||||
],
|
|
||||||
"svdFile": "${env:HOME}/.svd/STM32F303.svd",
|
|
||||||
"swoConfig": {
|
|
||||||
"enabled": true,
|
|
||||||
"cpuFrequency": 8000000,
|
|
||||||
"swoFrequency": 2000000,
|
|
||||||
"source": "probe",
|
|
||||||
"decoders": [
|
|
||||||
{ "type": "console", "label": "ITM", "port": 0 }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"cortex-debug.gdbPath.linux": "gdb-multiarch"
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "satrs-example-stm32f3-disco"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
cortex-m = "0.7"
|
|
||||||
cortex-m-rt = "0.7"
|
|
||||||
embedded-hal = "0.2.6"
|
|
||||||
cortex-m-rtic = "1.0"
|
|
||||||
enumset = "1.0"
|
|
||||||
heapless = "0.7"
|
|
||||||
systick-monotonic = "1.0"
|
|
||||||
|
|
||||||
[dependencies.cobs]
|
|
||||||
git = "https://github.com/robamu/cobs.rs.git"
|
|
||||||
branch = "all_features"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.panic-itm]
|
|
||||||
version = "0.4"
|
|
||||||
|
|
||||||
[dependencies.itm_logger]
|
|
||||||
git = "https://github.com/robamu/itm_logger.rs.git"
|
|
||||||
branch = "all_features"
|
|
||||||
version = "0.1.3-alpha.0"
|
|
||||||
|
|
||||||
[dependencies.stm32f3xx-hal]
|
|
||||||
git = "https://github.com/robamu/stm32f3xx-hal"
|
|
||||||
version = "0.10.0-alpha.0"
|
|
||||||
features = ["stm32f303xc", "rt", "enumset"]
|
|
||||||
branch = "all_features"
|
|
||||||
# Can be used in workspace to develop and update HAL
|
|
||||||
# path = "../stm32f3xx-hal"
|
|
||||||
|
|
||||||
[dependencies.stm32f3-discovery]
|
|
||||||
git = "https://github.com/robamu/stm32f3-discovery"
|
|
||||||
version = "0.8.0-alpha.0"
|
|
||||||
branch = "all_features"
|
|
||||||
# Can be used in workspace to develop and update BSP
|
|
||||||
# path = "../stm32f3-discovery"
|
|
||||||
|
|
||||||
[dependencies.satrs-core]
|
|
||||||
git = "https://egit.irs.uni-stuttgart.de/rust/satrs-core.git"
|
|
||||||
version = "0.1.0-alpha.0"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
# this lets you use `cargo fix`!
|
|
||||||
# [[bin]]
|
|
||||||
# name = "stm32f3-blinky"
|
|
||||||
# test = false
|
|
||||||
# bench = false
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
codegen-units = 1 # better optimizations
|
|
||||||
debug = true # symbols are nice and they don't increase the size on Flash
|
|
||||||
lto = true # better optimizations
|
|
@ -1,75 +0,0 @@
|
|||||||
sat-rs example for the STM32F3-Discovery board
|
|
||||||
=======
|
|
||||||
|
|
||||||
This example application shows how the [sat-rs framework](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad)
|
|
||||||
can be used on an embedded target. It also shows how a relatively simple OBSW could be built when no
|
|
||||||
standard runtime is available. It uses [RTIC](https://rtic.rs/1/book/en/) as the concurrency
|
|
||||||
framework.
|
|
||||||
|
|
||||||
The STM32F3-Discovery device was picked because it is a cheap Cortex-M4 based device which is also
|
|
||||||
used by the [Rust Embedded Book](https://docs.rust-embedded.org/book/intro/hardware.html) and the
|
|
||||||
[Rust Discovery](https://docs.rust-embedded.org/discovery/f3discovery/) book as an introduction
|
|
||||||
to embedded Rust.
|
|
||||||
|
|
||||||
## 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 and Debugging from the command line
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## Debugging with VS Code
|
|
||||||
|
|
||||||
The STM32F3-Discovery comes with an on-board ST-Link so all that is required to flash and debug
|
|
||||||
the board is a Mini-USB cable. The code in this repository was debugged using `openocd`
|
|
||||||
and the VS Code [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
|
||||||
configuration variables in your `settings.json`:
|
|
||||||
|
|
||||||
- `"cortex-debug.gdbPath"`
|
|
||||||
- `"cortex-debug.gdbPath.linux"`
|
|
||||||
- `"cortex-debug.gdbPath.windows"`
|
|
||||||
- `"cortex-debug.gdbPath.osx"`
|
|
||||||
|
|
||||||
## Commanding with Python
|
|
||||||
|
|
||||||
When the SW is running on the Discovery board, you can command the MCU via a serial interface,
|
|
||||||
using COBS encoded CCSDS packets.
|
|
||||||
|
|
||||||
TODO:
|
|
||||||
- How and where to connect serial interface on the MCU
|
|
||||||
- How to set up Python venv (or at least strongly recommend it) and install deps
|
|
||||||
- How to copy `def_tmtc_conf.json` to `tmtc_conf.json` and adapt it for custom serial port
|
|
@ -1,18 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Put the linker script somewhere the linker can find it
|
|
||||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
|
||||||
File::create(out.join("memory.x"))
|
|
||||||
.unwrap()
|
|
||||||
.write_all(include_bytes!("memory.x"))
|
|
||||||
.unwrap();
|
|
||||||
println!("cargo:rustc-link-search={}", out.display());
|
|
||||||
|
|
||||||
// Only re-run the build script when memory.x is changed,
|
|
||||||
// instead of when any part of the source code changes.
|
|
||||||
println!("cargo:rerun-if-changed=memory.x");
|
|
||||||
}
|
|
@ -1,604 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
extern crate panic_itm;
|
|
||||||
|
|
||||||
use rtic::app;
|
|
||||||
|
|
||||||
use heapless::{
|
|
||||||
mpmc::Q16,
|
|
||||||
pool,
|
|
||||||
pool::singleton::{Box, Pool},
|
|
||||||
};
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use itm_logger::{debug, info, logger_init, warn};
|
|
||||||
use satrs_core::spacepackets::{ecss::PusPacket, tm::PusTm};
|
|
||||||
use satrs_core::{
|
|
||||||
pus::{EcssTmErrorWithSend, EcssTmSenderCore},
|
|
||||||
seq_count::SequenceCountProviderCore,
|
|
||||||
};
|
|
||||||
use stm32f3xx_hal::dma::dma1;
|
|
||||||
use stm32f3xx_hal::gpio::{PushPull, AF7, PA2, PA3};
|
|
||||||
use stm32f3xx_hal::pac::USART2;
|
|
||||||
use stm32f3xx_hal::serial::{Rx, RxEvent, Serial, SerialDmaRx, SerialDmaTx, Tx, TxEvent};
|
|
||||||
use systick_monotonic::{fugit::Duration, Systick};
|
|
||||||
|
|
||||||
const UART_BAUD: u32 = 115200;
|
|
||||||
const BLINK_FREQ_MS: u64 = 1000;
|
|
||||||
const TX_HANDLER_FREQ_MS: u64 = 20;
|
|
||||||
const MIN_DELAY_BETWEEN_TX_PACKETS_MS: u16 = 5;
|
|
||||||
const MAX_TC_LEN: usize = 200;
|
|
||||||
const MAX_TM_LEN: usize = 200;
|
|
||||||
pub const PUS_APID: u16 = 0x02;
|
|
||||||
|
|
||||||
type TxType = Tx<USART2, PA2<AF7<PushPull>>>;
|
|
||||||
type RxType = Rx<USART2, PA3<AF7<PushPull>>>;
|
|
||||||
type MsDuration = Duration<u64, 1, 1000>;
|
|
||||||
type TxDmaTransferType = SerialDmaTx<&'static [u8], dma1::C7, TxType>;
|
|
||||||
type RxDmaTransferType = SerialDmaRx<&'static mut [u8], dma1::C6, RxType>;
|
|
||||||
|
|
||||||
// This is the predictable maximum overhead of the COBS encoding scheme.
|
|
||||||
// It is simply the maximum packet lenght dividied by 254 rounded up.
|
|
||||||
const COBS_TC_OVERHEAD: usize = (MAX_TC_LEN + 254 - 1) / 254;
|
|
||||||
const COBS_TM_OVERHEAD: usize = (MAX_TM_LEN + 254 - 1) / 254;
|
|
||||||
|
|
||||||
const TC_BUF_LEN: usize = MAX_TC_LEN + COBS_TC_OVERHEAD;
|
|
||||||
const TM_BUF_LEN: usize = MAX_TC_LEN + COBS_TM_OVERHEAD;
|
|
||||||
|
|
||||||
// This is a static buffer which should ONLY (!) be used as the TX DMA
|
|
||||||
// transfer buffer.
|
|
||||||
static mut DMA_TX_BUF: [u8; TM_BUF_LEN] = [0; TM_BUF_LEN];
|
|
||||||
// This is a static buffer which should ONLY (!) be used as the RX DMA
|
|
||||||
// transfer buffer.
|
|
||||||
static mut DMA_RX_BUF: [u8; TC_BUF_LEN] = [0; TC_BUF_LEN];
|
|
||||||
|
|
||||||
static TX_REQUESTS: Q16<(Box<poolmod::TM>, usize)> = Q16::new();
|
|
||||||
|
|
||||||
const TC_POOL_SLOTS: usize = 12;
|
|
||||||
const TM_POOL_SLOTS: usize = 12;
|
|
||||||
use core::sync::atomic::{AtomicU16, Ordering};
|
|
||||||
|
|
||||||
pub struct SeqCountProviderAtomicRef {
|
|
||||||
atomic: AtomicU16,
|
|
||||||
ordering: Ordering,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SeqCountProviderAtomicRef {
|
|
||||||
pub const fn new(ordering: Ordering) -> Self {
|
|
||||||
Self {
|
|
||||||
atomic: AtomicU16::new(0),
|
|
||||||
ordering,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SequenceCountProviderCore<u16> for SeqCountProviderAtomicRef {
|
|
||||||
fn get(&self) -> u16 {
|
|
||||||
self.atomic.load(self.ordering)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn increment(&self) {
|
|
||||||
self.atomic.fetch_add(1, self.ordering);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_and_increment(&self) -> u16 {
|
|
||||||
self.atomic.fetch_add(1, self.ordering)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static SEQ_COUNT_PROVIDER: SeqCountProviderAtomicRef =
|
|
||||||
SeqCountProviderAtomicRef::new(Ordering::Relaxed);
|
|
||||||
|
|
||||||
// Otherwise, warnings because of heapless pool macro.
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
mod poolmod {
|
|
||||||
use super::*;
|
|
||||||
// Must hold full TC length including COBS overhead.
|
|
||||||
pool!(TC: [u8; TC_BUF_LEN]);
|
|
||||||
// Only encoded at the end, so no need to account for COBS overhead.
|
|
||||||
pool!(TM: [u8; MAX_TM_LEN]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TxIdle {
|
|
||||||
tx: TxType,
|
|
||||||
dma_channel: dma1::C7,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum TmStoreError {
|
|
||||||
StoreFull,
|
|
||||||
StoreSlotsTooSmall,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TmStoreError> for EcssTmErrorWithSend<TmStoreError> {
|
|
||||||
fn from(value: TmStoreError) -> Self {
|
|
||||||
Self::SendError(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TmSender {
|
|
||||||
mem_block: Option<Box<poolmod::TM>>,
|
|
||||||
ctx: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TmSender {
|
|
||||||
pub fn new(mem_block: Box<poolmod::TM>, ctx: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
mem_block: Some(mem_block),
|
|
||||||
ctx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EcssTmSenderCore for TmSender {
|
|
||||||
type Error = TmStoreError;
|
|
||||||
|
|
||||||
fn send_tm(
|
|
||||||
&mut self,
|
|
||||||
tm: PusTm,
|
|
||||||
) -> Result<(), satrs_core::pus::EcssTmErrorWithSend<Self::Error>> {
|
|
||||||
let mem_block = self.mem_block.take();
|
|
||||||
if mem_block.is_none() {
|
|
||||||
panic!("send_tm should only be called once");
|
|
||||||
}
|
|
||||||
let mut mem_block = mem_block.unwrap();
|
|
||||||
if tm.len_packed() > MAX_TM_LEN {
|
|
||||||
return Err(EcssTmErrorWithSend::SendError(
|
|
||||||
TmStoreError::StoreSlotsTooSmall,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
tm.write_to_bytes(mem_block.as_mut_slice())
|
|
||||||
.map_err(|e| EcssTmErrorWithSend::EcssTmError(e.into()))?;
|
|
||||||
info!(target: self.ctx, "Sending TM[{},{}] with size {}", tm.service(), tm.subservice(), tm.len_packed());
|
|
||||||
TX_REQUESTS
|
|
||||||
.enqueue((mem_block, tm.len_packed()))
|
|
||||||
.map_err(|_| TmStoreError::StoreFull)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum UartTxState {
|
|
||||||
// Wrapped in an option because we need an owned type later.
|
|
||||||
Idle(Option<TxIdle>),
|
|
||||||
// Same as above
|
|
||||||
Transmitting(Option<TxDmaTransferType>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [TIM20_BRK, TIM20_UP, TIM20_TRG_COM])]
|
|
||||||
mod app {
|
|
||||||
use super::*;
|
|
||||||
use core::slice::Iter;
|
|
||||||
use cortex_m::iprintln;
|
|
||||||
use satrs_core::pus::verification::FailParams;
|
|
||||||
use satrs_core::pus::verification::VerificationReporterCore;
|
|
||||||
use satrs_core::spacepackets::{
|
|
||||||
ecss::EcssEnumU16,
|
|
||||||
tc::PusTc,
|
|
||||||
time::cds::P_FIELD_BASE,
|
|
||||||
tm::{PusTm, PusTmSecondaryHeader},
|
|
||||||
CcsdsPacket, SpHeader,
|
|
||||||
};
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use stm32f3_discovery::leds::Direction;
|
|
||||||
use stm32f3_discovery::leds::Leds;
|
|
||||||
use stm32f3xx_hal::prelude::*;
|
|
||||||
use stm32f3xx_hal::Toggle;
|
|
||||||
|
|
||||||
use stm32f3_discovery::switch_hal::OutputSwitch;
|
|
||||||
#[allow(dead_code)]
|
|
||||||
type SerialType = Serial<USART2, (PA2<AF7<PushPull>>, PA3<AF7<PushPull>>)>;
|
|
||||||
|
|
||||||
#[shared]
|
|
||||||
struct Shared {
|
|
||||||
tx_transfer: UartTxState,
|
|
||||||
rx_transfer: Option<RxDmaTransferType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[local]
|
|
||||||
struct Local {
|
|
||||||
leds: Leds,
|
|
||||||
last_dir: Direction,
|
|
||||||
verif_reporter: VerificationReporterCore,
|
|
||||||
curr_dir: Iter<'static, Direction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[monotonic(binds = SysTick, default = true)]
|
|
||||||
type MonoTimer = Systick<1000>;
|
|
||||||
|
|
||||||
#[init(local = [
|
|
||||||
tc_pool_mem: [u8; TC_BUF_LEN * TC_POOL_SLOTS] = [0; TC_BUF_LEN * TC_POOL_SLOTS],
|
|
||||||
tm_pool_mem: [u8; MAX_TM_LEN * TM_POOL_SLOTS] = [0; MAX_TM_LEN * TM_POOL_SLOTS]
|
|
||||||
])]
|
|
||||||
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) {
|
|
||||||
let mut rcc = cx.device.RCC.constrain();
|
|
||||||
|
|
||||||
let mono = Systick::new(cx.core.SYST, 8_000_000);
|
|
||||||
logger_init();
|
|
||||||
let mut flash = cx.device.FLASH.constrain();
|
|
||||||
let clocks = rcc
|
|
||||||
.cfgr
|
|
||||||
.use_hse(8.MHz())
|
|
||||||
.sysclk(8.MHz())
|
|
||||||
.pclk1(8.MHz())
|
|
||||||
.freeze(&mut flash.acr);
|
|
||||||
// setup ITM output
|
|
||||||
iprintln!(
|
|
||||||
&mut cx.core.ITM.stim[0],
|
|
||||||
"Starting sat-rs demo application for the STM32F3-Discovery"
|
|
||||||
);
|
|
||||||
let mut gpioe = cx.device.GPIOE.split(&mut rcc.ahb);
|
|
||||||
// Assign memory to the pools.
|
|
||||||
poolmod::TC::grow(cx.local.tc_pool_mem);
|
|
||||||
poolmod::TM::grow(cx.local.tm_pool_mem);
|
|
||||||
|
|
||||||
let verif_reporter = VerificationReporterCore::new(PUS_APID).unwrap();
|
|
||||||
|
|
||||||
let leds = Leds::new(
|
|
||||||
gpioe.pe8,
|
|
||||||
gpioe.pe9,
|
|
||||||
gpioe.pe10,
|
|
||||||
gpioe.pe11,
|
|
||||||
gpioe.pe12,
|
|
||||||
gpioe.pe13,
|
|
||||||
gpioe.pe14,
|
|
||||||
gpioe.pe15,
|
|
||||||
&mut gpioe.moder,
|
|
||||||
&mut gpioe.otyper,
|
|
||||||
);
|
|
||||||
let mut gpioa = cx.device.GPIOA.split(&mut rcc.ahb);
|
|
||||||
// USART2 pins
|
|
||||||
let mut pins = (
|
|
||||||
// TX pin: PA2
|
|
||||||
gpioa
|
|
||||||
.pa2
|
|
||||||
.into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl),
|
|
||||||
// RX pin: PA3
|
|
||||||
gpioa
|
|
||||||
.pa3
|
|
||||||
.into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl),
|
|
||||||
);
|
|
||||||
pins.1.internal_pull_up(&mut gpioa.pupdr, true);
|
|
||||||
let mut usart2 = Serial::new(
|
|
||||||
cx.device.USART2,
|
|
||||||
pins,
|
|
||||||
UART_BAUD.Bd(),
|
|
||||||
clocks,
|
|
||||||
&mut rcc.apb1,
|
|
||||||
);
|
|
||||||
usart2.configure_rx_interrupt(RxEvent::Idle, Toggle::On);
|
|
||||||
// This interrupt is enabled to re-schedule new transfers in the interrupt handler immediately.
|
|
||||||
usart2.configure_tx_interrupt(TxEvent::TransmissionComplete, Toggle::On);
|
|
||||||
|
|
||||||
let dma1 = cx.device.DMA1.split(&mut rcc.ahb);
|
|
||||||
let (tx_serial, mut rx_serial) = usart2.split();
|
|
||||||
|
|
||||||
// This interrupt is immediately triggered, clear it. It will only be reset
|
|
||||||
// by the hardware when data is received on RX (RXNE event)
|
|
||||||
rx_serial.clear_event(RxEvent::Idle);
|
|
||||||
let rx_transfer = rx_serial.read_exact(unsafe { DMA_RX_BUF.as_mut_slice() }, dma1.ch6);
|
|
||||||
info!(target: "init", "Spawning tasks");
|
|
||||||
blink::spawn().unwrap();
|
|
||||||
serial_tx_handler::spawn().unwrap();
|
|
||||||
(
|
|
||||||
Shared {
|
|
||||||
tx_transfer: UartTxState::Idle(Some(TxIdle {
|
|
||||||
tx: tx_serial,
|
|
||||||
dma_channel: dma1.ch7,
|
|
||||||
})),
|
|
||||||
rx_transfer: Some(rx_transfer),
|
|
||||||
},
|
|
||||||
Local {
|
|
||||||
leds,
|
|
||||||
last_dir: Direction::North,
|
|
||||||
curr_dir: Direction::iter(),
|
|
||||||
verif_reporter,
|
|
||||||
},
|
|
||||||
init::Monotonics(mono),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(local = [leds, curr_dir, last_dir])]
|
|
||||||
fn blink(cx: blink::Context) {
|
|
||||||
let toggle_leds = |dir: &Direction| {
|
|
||||||
let leds = cx.local.leds;
|
|
||||||
let last_led = leds.for_direction(*cx.local.last_dir);
|
|
||||||
last_led.off().ok();
|
|
||||||
let led = leds.for_direction(*dir);
|
|
||||||
led.on().ok();
|
|
||||||
*cx.local.last_dir = *dir;
|
|
||||||
};
|
|
||||||
|
|
||||||
match cx.local.curr_dir.next() {
|
|
||||||
Some(dir) => {
|
|
||||||
toggle_leds(dir);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
*cx.local.curr_dir = Direction::iter();
|
|
||||||
toggle_leds(cx.local.curr_dir.next().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
blink::spawn_after(MsDuration::from_ticks(BLINK_FREQ_MS)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(
|
|
||||||
shared = [tx_transfer],
|
|
||||||
local = []
|
|
||||||
)]
|
|
||||||
fn serial_tx_handler(mut cx: serial_tx_handler::Context) {
|
|
||||||
if let Some((buf, len)) = TX_REQUESTS.dequeue() {
|
|
||||||
cx.shared.tx_transfer.lock(|tx_state| match tx_state {
|
|
||||||
UartTxState::Idle(tx) => {
|
|
||||||
//debug!(target: "serial_tx_handler", "bytes: {:x?}", &buf[0..len]);
|
|
||||||
// Safety: We only copy the data into the TX DMA buffer in this task.
|
|
||||||
// If the DMA is active, another branch will be taken.
|
|
||||||
let mut_tx_dma_buf = unsafe { &mut DMA_TX_BUF };
|
|
||||||
// 0 sentinel value as start marker
|
|
||||||
mut_tx_dma_buf[0] = 0;
|
|
||||||
// Should never panic, we accounted for the overhead.
|
|
||||||
// Write into transfer buffer directly, no need for intermediate
|
|
||||||
// encoding buffer.
|
|
||||||
let encoded_len = cobs::encode(&buf[0..len], &mut mut_tx_dma_buf[1..]);
|
|
||||||
// 0 end marker
|
|
||||||
mut_tx_dma_buf[encoded_len + 1] = 0;
|
|
||||||
//debug!(target: "serial_tx_handler", "Sending {} bytes", encoded_len + 2);
|
|
||||||
//debug!("sent: {:x?}", &mut_tx_dma_buf[0..encoded_len + 2]);
|
|
||||||
let tx_idle = tx.take().unwrap();
|
|
||||||
// Transfer completion and re-scheduling of new TX transfers will be done
|
|
||||||
// by the IRQ handler.
|
|
||||||
let transfer = tx_idle
|
|
||||||
.tx
|
|
||||||
.write_all(&mut_tx_dma_buf[0..encoded_len + 2], tx_idle.dma_channel);
|
|
||||||
*tx_state = UartTxState::Transmitting(Some(transfer));
|
|
||||||
// The memory block is automatically returned to the pool when it is dropped.
|
|
||||||
}
|
|
||||||
UartTxState::Transmitting(_) => {
|
|
||||||
// This is a SW configuration error. Only the ISR which
|
|
||||||
// detects transfer completion should be able to spawn a new
|
|
||||||
// task, and that ISR should set the state to IDLE.
|
|
||||||
panic!("invalid internal tx state detected")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
cx.shared.tx_transfer.lock(|tx_state| {
|
|
||||||
if let UartTxState::Idle(_) = tx_state {
|
|
||||||
serial_tx_handler::spawn_after(MsDuration::from_ticks(TX_HANDLER_FREQ_MS))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(
|
|
||||||
local = [
|
|
||||||
stamp_buf: [u8; 7] = [0; 7],
|
|
||||||
decode_buf: [u8; MAX_TC_LEN] = [0; MAX_TC_LEN],
|
|
||||||
src_data_buf: [u8; MAX_TM_LEN] = [0; MAX_TM_LEN],
|
|
||||||
verif_reporter
|
|
||||||
],
|
|
||||||
)]
|
|
||||||
fn serial_rx_handler(
|
|
||||||
cx: serial_rx_handler::Context,
|
|
||||||
received_packet: Box<poolmod::TC>,
|
|
||||||
rx_len: usize,
|
|
||||||
) {
|
|
||||||
let tgt: &'static str = "serial_rx_handler";
|
|
||||||
cx.local.stamp_buf[0] = P_FIELD_BASE;
|
|
||||||
info!(target: tgt, "Received packet with {} bytes", rx_len);
|
|
||||||
let decode_buf = cx.local.decode_buf;
|
|
||||||
let packet = received_packet.as_slice();
|
|
||||||
let mut start_idx = None;
|
|
||||||
for (idx, byte) in packet.iter().enumerate() {
|
|
||||||
if *byte != 0 {
|
|
||||||
start_idx = Some(idx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if start_idx.is_none() {
|
|
||||||
warn!(
|
|
||||||
target: tgt,
|
|
||||||
"decoding error, can only process cobs encoded frames, data is all 0"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let start_idx = start_idx.unwrap();
|
|
||||||
match cobs::decode(&received_packet.as_slice()[start_idx..], decode_buf) {
|
|
||||||
Ok(len) => {
|
|
||||||
info!(target: tgt, "Decoded packet length: {}", len);
|
|
||||||
let pus_tc = PusTc::from_bytes(decode_buf);
|
|
||||||
let verif_reporter = cx.local.verif_reporter;
|
|
||||||
match pus_tc {
|
|
||||||
Ok((tc, tc_len)) => handle_tc(
|
|
||||||
tc,
|
|
||||||
tc_len,
|
|
||||||
verif_reporter,
|
|
||||||
cx.local.src_data_buf,
|
|
||||||
cx.local.stamp_buf,
|
|
||||||
tgt,
|
|
||||||
),
|
|
||||||
Err(e) => {
|
|
||||||
warn!(target: tgt, "Error unpacking PUS TC: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
warn!(
|
|
||||||
target: tgt,
|
|
||||||
"decoding error, can only process cobs encoded frames"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_tc(
|
|
||||||
tc: PusTc,
|
|
||||||
tc_len: usize,
|
|
||||||
verif_reporter: &mut VerificationReporterCore,
|
|
||||||
src_data_buf: &mut [u8; MAX_TM_LEN],
|
|
||||||
stamp_buf: &[u8; 7],
|
|
||||||
tgt: &'static str,
|
|
||||||
) {
|
|
||||||
info!(
|
|
||||||
target: tgt,
|
|
||||||
"Found PUS TC [{},{}] with length {}",
|
|
||||||
tc.service(),
|
|
||||||
tc.subservice(),
|
|
||||||
tc_len
|
|
||||||
);
|
|
||||||
|
|
||||||
let token = verif_reporter.add_tc(&tc);
|
|
||||||
if tc.apid() != PUS_APID {
|
|
||||||
warn!(target: tgt, "Received tc with unknown APID {}", tc.apid());
|
|
||||||
let sendable = verif_reporter
|
|
||||||
.acceptance_failure(
|
|
||||||
src_data_buf,
|
|
||||||
token,
|
|
||||||
&SEQ_COUNT_PROVIDER,
|
|
||||||
FailParams::new(stamp_buf, &EcssEnumU16::new(0), None),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
|
||||||
let mut sender = TmSender::new(mem_block, tgt);
|
|
||||||
if let Err(e) =
|
|
||||||
verif_reporter.send_acceptance_failure(sendable, &SEQ_COUNT_PROVIDER, &mut sender)
|
|
||||||
{
|
|
||||||
warn!(target: tgt, "Sending acceptance failure failed: {:?}", e.0);
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let sendable = verif_reporter
|
|
||||||
.acceptance_success(src_data_buf, token, &SEQ_COUNT_PROVIDER, stamp_buf)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
|
||||||
let mut sender = TmSender::new(mem_block, tgt);
|
|
||||||
let accepted_token = match verif_reporter.send_acceptance_success(
|
|
||||||
sendable,
|
|
||||||
&SEQ_COUNT_PROVIDER,
|
|
||||||
&mut sender,
|
|
||||||
) {
|
|
||||||
Ok(token) => token,
|
|
||||||
Err(e) => {
|
|
||||||
warn!(target: "serial_rx_handler", "Sending acceptance success failed: {:?}", e.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if tc.service() == 17 {
|
|
||||||
if tc.subservice() == 1 {
|
|
||||||
let sendable = verif_reporter
|
|
||||||
.start_success(src_data_buf, accepted_token, &SEQ_COUNT_PROVIDER, stamp_buf)
|
|
||||||
.unwrap();
|
|
||||||
let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
|
||||||
let mut sender = TmSender::new(mem_block, tgt);
|
|
||||||
let started_token = match verif_reporter.send_start_success(
|
|
||||||
sendable,
|
|
||||||
&SEQ_COUNT_PROVIDER,
|
|
||||||
&mut sender,
|
|
||||||
) {
|
|
||||||
Ok(token) => token,
|
|
||||||
Err(e) => {
|
|
||||||
warn!(target: tgt, "Sending acceptance success failed: {:?}", e.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
info!(
|
|
||||||
target: tgt,
|
|
||||||
"Received PUS ping telecommand, sending ping reply TM[17,2]"
|
|
||||||
);
|
|
||||||
let mut sp_header =
|
|
||||||
SpHeader::tc_unseg(PUS_APID, SEQ_COUNT_PROVIDER.get(), 0).unwrap();
|
|
||||||
let sec_header = PusTmSecondaryHeader::new_simple(17, 2, stamp_buf);
|
|
||||||
let ping_reply = PusTm::new(&mut sp_header, sec_header, None, true);
|
|
||||||
let mut mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
|
||||||
let reply_len = ping_reply.write_to_bytes(mem_block.as_mut_slice()).unwrap();
|
|
||||||
if TX_REQUESTS.enqueue((mem_block, reply_len)).is_err() {
|
|
||||||
warn!(target: tgt, "TC queue full");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SEQ_COUNT_PROVIDER.increment();
|
|
||||||
let sendable = verif_reporter
|
|
||||||
.completion_success(src_data_buf, started_token, &SEQ_COUNT_PROVIDER, stamp_buf)
|
|
||||||
.unwrap();
|
|
||||||
let mem_block = poolmod::TM::alloc().unwrap().init([0u8; MAX_TM_LEN]);
|
|
||||||
let mut sender = TmSender::new(mem_block, tgt);
|
|
||||||
if let Err(e) = verif_reporter.send_step_or_completion_success(
|
|
||||||
sendable,
|
|
||||||
&SEQ_COUNT_PROVIDER,
|
|
||||||
&mut sender,
|
|
||||||
) {
|
|
||||||
warn!(target: tgt, "Sending completion success failed: {:?}", e.0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: Invalid subservice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(binds = DMA1_CH6, shared = [rx_transfer])]
|
|
||||||
fn rx_dma_isr(mut cx: rx_dma_isr::Context) {
|
|
||||||
cx.shared.rx_transfer.lock(|rx_transfer| {
|
|
||||||
let rx_ref = rx_transfer.as_ref().unwrap();
|
|
||||||
if rx_ref.is_complete() {
|
|
||||||
let uart_rx_owned = rx_transfer.take().unwrap();
|
|
||||||
let (buf, c, rx) = uart_rx_owned.stop();
|
|
||||||
// The received data is transferred to another task now to avoid any processing overhead
|
|
||||||
// during the interrupt. There are multiple ways to do this, we use a memory pool here
|
|
||||||
// to do this.
|
|
||||||
let mut mem_block = poolmod::TC::alloc()
|
|
||||||
.expect("allocating memory block for rx failed")
|
|
||||||
.init([0u8; TC_BUF_LEN]);
|
|
||||||
// Copy data into memory pool.
|
|
||||||
mem_block.copy_from_slice(buf);
|
|
||||||
*rx_transfer = Some(rx.read_exact(buf, c));
|
|
||||||
// Only send owning pointer to pool memory and the received packet length.
|
|
||||||
serial_rx_handler::spawn(mem_block, TC_BUF_LEN)
|
|
||||||
.expect("spawning rx handler task failed");
|
|
||||||
// If this happens, there is a high chance that the maximum packet length was
|
|
||||||
// exceeded. Circular mode is not used here, so data might be missed.
|
|
||||||
warn!(
|
|
||||||
"rx transfer with maximum length {}, might miss data",
|
|
||||||
TC_BUF_LEN
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(binds = USART2_EXTI26, shared = [rx_transfer, tx_transfer])]
|
|
||||||
fn serial_isr(mut cx: serial_isr::Context) {
|
|
||||||
cx.shared.tx_transfer.lock(|tx_state| match tx_state {
|
|
||||||
UartTxState::Idle(_) => (),
|
|
||||||
UartTxState::Transmitting(transfer) => {
|
|
||||||
let transfer_ref = transfer.as_ref().unwrap();
|
|
||||||
if transfer_ref.is_complete() {
|
|
||||||
let transfer = transfer.take().unwrap();
|
|
||||||
let (_, dma_channel, tx) = transfer.stop();
|
|
||||||
*tx_state = UartTxState::Idle(Some(TxIdle { tx, dma_channel }));
|
|
||||||
serial_tx_handler::spawn_after(MsDuration::from_ticks(
|
|
||||||
MIN_DELAY_BETWEEN_TX_PACKETS_MS.into(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cx.shared.rx_transfer.lock(|rx_transfer| {
|
|
||||||
let rx_transfer_ref = rx_transfer.as_ref().unwrap();
|
|
||||||
// Received a partial packet.
|
|
||||||
if rx_transfer_ref.is_event_triggered(RxEvent::Idle) {
|
|
||||||
let rx_transfer_owned = rx_transfer.take().unwrap();
|
|
||||||
let (buf, ch, mut rx, rx_len) = rx_transfer_owned.stop_and_return_received_bytes();
|
|
||||||
// The received data is transferred to another task now to avoid any processing overhead
|
|
||||||
// during the interrupt. There are multiple ways to do this, we use a memory pool here
|
|
||||||
// to do this.
|
|
||||||
let mut mem_block = poolmod::TC::alloc()
|
|
||||||
.expect("allocating memory block for rx failed")
|
|
||||||
.init([0u8; TC_BUF_LEN]);
|
|
||||||
// Copy data into memory pool.
|
|
||||||
mem_block[0..rx_len as usize].copy_from_slice(&buf[0..rx_len as usize]);
|
|
||||||
rx.clear_event(RxEvent::Idle);
|
|
||||||
// Only send owning pointer to pool memory and the received packet length.
|
|
||||||
serial_rx_handler::spawn(mem_block, rx_len as usize)
|
|
||||||
.expect("spawning rx handler task failed");
|
|
||||||
*rx_transfer = Some(rx.read_exact(buf, ch));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,3 +7,13 @@ 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
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "satrs-example"
|
name = "satrs-example"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
default-run = "satrs-example"
|
default-run = "satrs-example"
|
||||||
@ -17,16 +17,26 @@ zerocopy = "0.6"
|
|||||||
csv = "1"
|
csv = "1"
|
||||||
num_enum = "0.7"
|
num_enum = "0.7"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
lazy_static = "1"
|
||||||
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
derive-new = "0.5"
|
derive-new = "0.5"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
[dependencies.satrs]
|
[dependencies.satrs]
|
||||||
# version = "0.1.0-alpha.3"
|
|
||||||
path = "../satrs"
|
path = "../satrs"
|
||||||
|
features = ["test_util"]
|
||||||
|
|
||||||
|
[dependencies.satrs-minisim]
|
||||||
|
path = "../satrs-minisim"
|
||||||
|
|
||||||
[dependencies.satrs-mib]
|
[dependencies.satrs-mib]
|
||||||
# version = "0.1.0-alpha.2"
|
version = "0.1.1"
|
||||||
path = "../satrs-mib"
|
path = "../satrs-mib"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dyn_tmtc = []
|
dyn_tmtc = []
|
||||||
default = ["dyn_tmtc"]
|
default = ["dyn_tmtc"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger = "0.11"
|
||||||
|
@ -48,16 +48,17 @@ 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:
|
dependency interactively:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install -r requirements.txt
|
pip install -e .
|
||||||
```
|
```
|
||||||
|
|
||||||
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]
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -72,3 +73,22 @@ 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`.
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from spacepackets.ccsds import CdsShortTimestamp
|
|
||||||
from spacepackets.ecss import PusTelecommand
|
|
||||||
from tmtccmd.config import CmdTreeNode
|
|
||||||
from tmtccmd.tmtc import DefaultPusQueueHelper
|
|
||||||
from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd
|
|
||||||
from tmtccmd.pus.tc.s3_fsfw_hk import create_request_one_hk_command
|
|
||||||
|
|
||||||
from common import (
|
|
||||||
EXAMPLE_PUS_APID,
|
|
||||||
make_addressable_id,
|
|
||||||
RequestTargetId,
|
|
||||||
AcsHkIds,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def create_cmd_definition_tree() -> CmdTreeNode:
|
|
||||||
|
|
||||||
root_node = CmdTreeNode.root_node()
|
|
||||||
|
|
||||||
test_node = CmdTreeNode("test", "Test Node")
|
|
||||||
test_node.add_child(CmdTreeNode("ping", "Send PUS ping TC"))
|
|
||||||
test_node.add_child(CmdTreeNode("trigger_event", "Send PUS test to trigger event"))
|
|
||||||
root_node.add_child(test_node)
|
|
||||||
|
|
||||||
scheduler_node = CmdTreeNode("scheduler", "Scheduler Node")
|
|
||||||
scheduler_node.add_child(
|
|
||||||
CmdTreeNode(
|
|
||||||
"schedule_ping_10_secs_ahead", "Schedule Ping to execute in 10 seconds"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
root_node.add_child(scheduler_node)
|
|
||||||
|
|
||||||
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
|
||||||
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
|
||||||
mgm_node.add_child(CmdTreeNode("one_shot_hk", "Request one shot HK"))
|
|
||||||
acs_node.add_child(mgm_node)
|
|
||||||
root_node.add_child(acs_node)
|
|
||||||
|
|
||||||
return root_node
|
|
||||||
|
|
||||||
|
|
||||||
def pack_pus_telecommands(q: DefaultPusQueueHelper, cmd_path: str):
|
|
||||||
# It should always be at least the root path "/", so we split of the empty portion left of it.
|
|
||||||
cmd_path_list = cmd_path.split("/")[1:]
|
|
||||||
if len(cmd_path_list) == 0:
|
|
||||||
_LOGGER.warning("empty command path")
|
|
||||||
return
|
|
||||||
if cmd_path_list[0] == "test":
|
|
||||||
assert len(cmd_path_list) >= 2
|
|
||||||
if cmd_path_list[1] == "ping":
|
|
||||||
q.add_log_cmd("Sending PUS ping telecommand")
|
|
||||||
return q.add_pus_tc(PusTelecommand(service=17, subservice=1))
|
|
||||||
elif cmd_path_list[1] == "trigger_event":
|
|
||||||
q.add_log_cmd("Triggering test event")
|
|
||||||
return q.add_pus_tc(PusTelecommand(service=17, subservice=128))
|
|
||||||
if cmd_path_list[0] == "scheduler":
|
|
||||||
assert len(cmd_path_list) >= 2
|
|
||||||
if cmd_path_list[1] == "schedule_ping_10_secs_ahead":
|
|
||||||
q.add_log_cmd("Sending PUS scheduled TC telecommand")
|
|
||||||
crt_time = CdsShortTimestamp.from_now()
|
|
||||||
time_stamp = crt_time + datetime.timedelta(seconds=10)
|
|
||||||
time_stamp = time_stamp.pack()
|
|
||||||
return q.add_pus_tc(
|
|
||||||
create_time_tagged_cmd(
|
|
||||||
time_stamp,
|
|
||||||
PusTelecommand(service=17, subservice=1),
|
|
||||||
apid=EXAMPLE_PUS_APID,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if cmd_path_list[0] == "acs":
|
|
||||||
assert len(cmd_path_list) >= 2
|
|
||||||
if cmd_path_list[1] == "mgm":
|
|
||||||
assert len(cmd_path_list) >= 3
|
|
||||||
if cmd_path_list[2] == "one_shot_hk":
|
|
||||||
q.add_log_cmd("Sending HK one shot request")
|
|
||||||
q.add_pus_tc(
|
|
||||||
create_request_one_hk_command(
|
|
||||||
make_addressable_id(RequestTargetId.ACS, AcsHkIds.MGM_SET)
|
|
||||||
)
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
tmtccmd == 8.0.0rc1
|
|
||||||
# -e git+https://github.com/robamu-org/tmtccmd@97e5e51101a08b21472b3ddecc2063359f7e307a#egg=tmtccmd
|
|
@ -1,38 +0,0 @@
|
|||||||
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
|
|
143
satrs-example/pytmtc/.gitignore
vendored
Normal file
143
satrs-example/pytmtc/.gitignore
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/tmtc_conf.json
|
||||||
|
__pycache__
|
||||||
|
|
||||||
|
/venv
|
||||||
|
/log
|
||||||
|
/.idea/*
|
||||||
|
!/.idea/runConfigurations
|
||||||
|
|
||||||
|
/seqcnt.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
|
102
satrs-example/pytmtc/main.py
Executable file
102
satrs-example/pytmtc/main.py
Executable file
@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Example client for the sat-rs example application"""
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import tmtccmd
|
||||||
|
from spacepackets.ecss import PusVerificator
|
||||||
|
|
||||||
|
from tmtccmd import ProcedureParamsWrapper
|
||||||
|
from tmtccmd.core.base import BackendRequest
|
||||||
|
from tmtccmd.pus import VerificationWrapper
|
||||||
|
from tmtccmd.tmtc import CcsdsTmHandler
|
||||||
|
from tmtccmd.config import (
|
||||||
|
default_json_path,
|
||||||
|
SetupParams,
|
||||||
|
params_to_procedure_conversion,
|
||||||
|
)
|
||||||
|
from tmtccmd.config import PreArgsParsingWrapper, SetupWrapper
|
||||||
|
from tmtccmd.logging import add_colorlog_console_logger
|
||||||
|
from tmtccmd.logging.pus import (
|
||||||
|
RegularTmtcLogWrapper,
|
||||||
|
RawTmtcTimedLogWrapper,
|
||||||
|
TimedLogWhen,
|
||||||
|
)
|
||||||
|
from spacepackets.seqcount import PusFileSeqCountProvider
|
||||||
|
|
||||||
|
|
||||||
|
from pytmtc.config import SatrsConfigHook
|
||||||
|
from pytmtc.pus_tc import TcHandler
|
||||||
|
from pytmtc.pus_tm import PusHandler
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
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=tm_handler)
|
||||||
|
# TODO: We could add the CFDP handler for the CFDP APID at a later stage.
|
||||||
|
# ccsds_handler.add_apid_handler(tm_handler)
|
||||||
|
|
||||||
|
# Create TC handler
|
||||||
|
seq_count_provider = PusFileSeqCountProvider()
|
||||||
|
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:
|
||||||
|
tmtc_backend.close_com_if()
|
||||||
|
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:
|
||||||
|
tmtc_backend.close_com_if()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
27
satrs-example/pytmtc/pyproject.toml
Normal file
27
satrs-example/pytmtc/pyproject.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[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.0",
|
||||||
|
"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"]
|
@ -4,11 +4,13 @@ import dataclasses
|
|||||||
import enum
|
import enum
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from spacepackets.ecss.tc import PacketId, PacketType
|
|
||||||
|
|
||||||
EXAMPLE_PUS_APID = 0x02
|
class Apid(enum.IntEnum):
|
||||||
EXAMPLE_PUS_PACKET_ID_TM = PacketId(PacketType.TM, True, EXAMPLE_PUS_APID)
|
SCHED = 1
|
||||||
TM_PACKET_IDS = [EXAMPLE_PUS_PACKET_ID_TM]
|
GENERIC_PUS = 2
|
||||||
|
ACS = 3
|
||||||
|
CFDP = 4
|
||||||
|
TMTC = 5
|
||||||
|
|
||||||
|
|
||||||
class EventSeverity(enum.IntEnum):
|
class EventSeverity(enum.IntEnum):
|
||||||
@ -36,8 +38,8 @@ class EventU32:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RequestTargetId(enum.IntEnum):
|
class AcsId(enum.IntEnum):
|
||||||
ACS = 1
|
MGM_0 = 0
|
||||||
|
|
||||||
|
|
||||||
class AcsHkIds(enum.IntEnum):
|
class AcsHkIds(enum.IntEnum):
|
47
satrs-example/pytmtc/pytmtc/config.py
Normal file
47
satrs-example/pytmtc/pytmtc/config.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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=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()
|
42
satrs-example/pytmtc/pytmtc/hk.py
Normal file
42
satrs-example/pytmtc/pytmtc/hk.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
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.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"
|
||||||
|
)
|
16
satrs-example/pytmtc/pytmtc/hk_common.py
Normal file
16
satrs-example/pytmtc/pytmtc/hk_common.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
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,
|
||||||
|
)
|
45
satrs-example/pytmtc/pytmtc/mgms.py
Normal file
45
satrs-example/pytmtc/pytmtc/mgms.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
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
|
31
satrs-example/pytmtc/pytmtc/mode.py
Normal file
31
satrs-example/pytmtc/pytmtc/mode.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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))
|
151
satrs-example/pytmtc/pytmtc/pus_tc.py
Normal file
151
satrs-example/pytmtc/pytmtc/pus_tc.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from spacepackets.ccsds import CdsShortTimestamp
|
||||||
|
from spacepackets.ecss import PusTelecommand
|
||||||
|
from spacepackets.seqcount import FileSeqCountProvider
|
||||||
|
from tmtccmd import ProcedureWrapper, TcHandlerBase
|
||||||
|
from tmtccmd.config import CmdTreeNode
|
||||||
|
from tmtccmd.pus import VerificationWrapper
|
||||||
|
from tmtccmd.tmtc import (
|
||||||
|
DefaultPusQueueHelper,
|
||||||
|
FeedWrapper,
|
||||||
|
QueueWrapper,
|
||||||
|
SendCbParams,
|
||||||
|
TcProcedureType,
|
||||||
|
TcQueueEntryType,
|
||||||
|
)
|
||||||
|
from tmtccmd.pus.s11_tc_sched import create_time_tagged_cmd
|
||||||
|
|
||||||
|
from pytmtc.common import Apid
|
||||||
|
from pytmtc.mgms import create_mgm_cmds
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
pack_pus_telecommands(q, def_proc.cmd_path)
|
||||||
|
|
||||||
|
|
||||||
|
def create_cmd_definition_tree() -> CmdTreeNode:
|
||||||
|
|
||||||
|
root_node = CmdTreeNode.root_node()
|
||||||
|
|
||||||
|
hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True)
|
||||||
|
hk_node.add_child(CmdTreeNode("one_shot_hk", "Request One Shot HK set"))
|
||||||
|
hk_node.add_child(
|
||||||
|
CmdTreeNode("enable", "Enable periodic housekeeping data generation")
|
||||||
|
)
|
||||||
|
hk_node.add_child(
|
||||||
|
CmdTreeNode("disable", "Disable periodic housekeeping data generation")
|
||||||
|
)
|
||||||
|
|
||||||
|
mode_node = CmdTreeNode("mode", "Mode Node", hide_children_for_print=True)
|
||||||
|
set_mode_node = CmdTreeNode(
|
||||||
|
"set_mode", "Set Node", hide_children_which_are_leaves=True
|
||||||
|
)
|
||||||
|
set_mode_node.add_child(CmdTreeNode("off", "Set OFF Mode"))
|
||||||
|
set_mode_node.add_child(CmdTreeNode("on", "Set ON Mode"))
|
||||||
|
set_mode_node.add_child(CmdTreeNode("normal", "Set NORMAL Mode"))
|
||||||
|
mode_node.add_child(set_mode_node)
|
||||||
|
mode_node.add_child(CmdTreeNode("read_mode", "Read Mode"))
|
||||||
|
|
||||||
|
test_node = CmdTreeNode("test", "Test Node")
|
||||||
|
test_node.add_child(CmdTreeNode("ping", "Send PUS ping TC"))
|
||||||
|
test_node.add_child(CmdTreeNode("trigger_event", "Send PUS test to trigger event"))
|
||||||
|
root_node.add_child(test_node)
|
||||||
|
|
||||||
|
scheduler_node = CmdTreeNode("scheduler", "Scheduler Node")
|
||||||
|
scheduler_node.add_child(
|
||||||
|
CmdTreeNode(
|
||||||
|
"schedule_ping_10_secs_ahead", "Schedule Ping to execute in 10 seconds"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
root_node.add_child(scheduler_node)
|
||||||
|
|
||||||
|
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
||||||
|
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
||||||
|
mgm_node.add_child(mode_node)
|
||||||
|
mgm_node.add_child(hk_node)
|
||||||
|
|
||||||
|
acs_node.add_child(mgm_node)
|
||||||
|
root_node.add_child(acs_node)
|
||||||
|
|
||||||
|
return root_node
|
||||||
|
|
||||||
|
|
||||||
|
def pack_pus_telecommands(q: DefaultPusQueueHelper, cmd_path: str):
|
||||||
|
# It should always be at least the root path "/", so we split of the empty portion left of it.
|
||||||
|
cmd_path_list = cmd_path.split("/")[1:]
|
||||||
|
if len(cmd_path_list) == 0:
|
||||||
|
_LOGGER.warning("empty command path")
|
||||||
|
return
|
||||||
|
if cmd_path_list[0] == "test":
|
||||||
|
assert len(cmd_path_list) >= 2
|
||||||
|
if cmd_path_list[1] == "ping":
|
||||||
|
q.add_log_cmd("Sending PUS ping telecommand")
|
||||||
|
return q.add_pus_tc(
|
||||||
|
PusTelecommand(apid=Apid.GENERIC_PUS, service=17, subservice=1)
|
||||||
|
)
|
||||||
|
elif cmd_path_list[1] == "trigger_event":
|
||||||
|
q.add_log_cmd("Triggering test event")
|
||||||
|
return q.add_pus_tc(
|
||||||
|
PusTelecommand(apid=Apid.GENERIC_PUS, service=17, subservice=128)
|
||||||
|
)
|
||||||
|
if cmd_path_list[0] == "scheduler":
|
||||||
|
assert len(cmd_path_list) >= 2
|
||||||
|
if cmd_path_list[1] == "schedule_ping_10_secs_ahead":
|
||||||
|
q.add_log_cmd("Sending PUS scheduled TC telecommand")
|
||||||
|
crt_time = CdsShortTimestamp.from_now()
|
||||||
|
time_stamp = crt_time + datetime.timedelta(seconds=10)
|
||||||
|
time_stamp = time_stamp.pack()
|
||||||
|
return q.add_pus_tc(
|
||||||
|
create_time_tagged_cmd(
|
||||||
|
time_stamp,
|
||||||
|
PusTelecommand(service=17, subservice=1),
|
||||||
|
apid=Apid.SCHED,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if cmd_path_list[0] == "acs":
|
||||||
|
assert len(cmd_path_list) >= 2
|
||||||
|
if cmd_path_list[1] == "mgms":
|
||||||
|
create_mgm_cmds(q, cmd_path_list)
|
93
satrs-example/pytmtc/pytmtc/pus_tm.py
Normal file
93
satrs-example/pytmtc/pytmtc/pus_tm.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
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
satrs-example/pytmtc/requirements.txt
Normal file
1
satrs-example/pytmtc/requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
.
|
0
satrs-example/pytmtc/tests/__init__.py
Normal file
0
satrs-example/pytmtc/tests/__init__.py
Normal file
48
satrs-example/pytmtc/tests/test_tc_mods.py
Normal file
48
satrs-example/pytmtc/tests/test_tc_mods.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
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,117 +0,0 @@
|
|||||||
use std::sync::mpsc::{self, TryRecvError};
|
|
||||||
|
|
||||||
use log::{info, warn};
|
|
||||||
use satrs::pus::verification::VerificationReporterWithSender;
|
|
||||||
use satrs::pus::{EcssTmSender, PusTmWrapper};
|
|
||||||
use satrs::spacepackets::ecss::hk::Subservice as HkSubservice;
|
|
||||||
use satrs::{
|
|
||||||
hk::HkRequest,
|
|
||||||
spacepackets::{
|
|
||||||
ecss::tm::{PusTmCreator, PusTmSecondaryHeader},
|
|
||||||
time::cds::{DaysLen16Bits, TimeProvider},
|
|
||||||
SequenceFlags, SpHeader,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use satrs_example::config::{RequestTargetId, PUS_APID};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
hk::{AcsHkIds, HkUniqueId},
|
|
||||||
requests::{Request, RequestWithToken},
|
|
||||||
update_time,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct AcsTask {
|
|
||||||
timestamp: [u8; 7],
|
|
||||||
time_provider: TimeProvider<DaysLen16Bits>,
|
|
||||||
verif_reporter: VerificationReporterWithSender,
|
|
||||||
tm_sender: Box<dyn EcssTmSender>,
|
|
||||||
request_rx: mpsc::Receiver<RequestWithToken>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AcsTask {
|
|
||||||
pub fn new(
|
|
||||||
tm_sender: impl EcssTmSender,
|
|
||||||
request_rx: mpsc::Receiver<RequestWithToken>,
|
|
||||||
verif_reporter: VerificationReporterWithSender,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
timestamp: [0; 7],
|
|
||||||
time_provider: TimeProvider::new_with_u16_days(0, 0),
|
|
||||||
verif_reporter,
|
|
||||||
tm_sender: Box::new(tm_sender),
|
|
||||||
request_rx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_hk_request(&mut self, target_id: u32, unique_id: u32) {
|
|
||||||
assert_eq!(target_id, RequestTargetId::AcsSubsystem as u32);
|
|
||||||
if unique_id == AcsHkIds::TestMgmSet as u32 {
|
|
||||||
let mut sp_header = SpHeader::tm(PUS_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
|
||||||
let sec_header = PusTmSecondaryHeader::new_simple(
|
|
||||||
3,
|
|
||||||
HkSubservice::TmHkPacket as u8,
|
|
||||||
&self.timestamp,
|
|
||||||
);
|
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
|
||||||
let hk_id = HkUniqueId::new(target_id, unique_id);
|
|
||||||
hk_id.write_to_be_bytes(&mut buf).unwrap();
|
|
||||||
let pus_tm = PusTmCreator::new(&mut sp_header, sec_header, &buf, true);
|
|
||||||
self.tm_sender
|
|
||||||
.send_tm(PusTmWrapper::Direct(pus_tm))
|
|
||||||
.expect("Sending HK TM failed");
|
|
||||||
}
|
|
||||||
// TODO: Verification failure for invalid unique IDs.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_reading_one_request(&mut self) -> bool {
|
|
||||||
match self.request_rx.try_recv() {
|
|
||||||
Ok(request) => {
|
|
||||||
info!(
|
|
||||||
"ACS thread: Received HK request {:?}",
|
|
||||||
request.targeted_request
|
|
||||||
);
|
|
||||||
match request.targeted_request.request {
|
|
||||||
Request::Hk(hk_req) => match hk_req {
|
|
||||||
HkRequest::OneShot(unique_id) => self.handle_hk_request(
|
|
||||||
request.targeted_request.target_id_with_apid.target_id(),
|
|
||||||
unique_id,
|
|
||||||
),
|
|
||||||
HkRequest::Enable(_) => {}
|
|
||||||
HkRequest::Disable(_) => {}
|
|
||||||
HkRequest::ModifyCollectionInterval(_, _) => {}
|
|
||||||
},
|
|
||||||
Request::Mode(_mode_req) => {
|
|
||||||
warn!("mode request handling not implemented yet")
|
|
||||||
}
|
|
||||||
Request::Action(_action_req) => {
|
|
||||||
warn!("action request handling not implemented yet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let started_token = self
|
|
||||||
.verif_reporter
|
|
||||||
.start_success(request.token, Some(&self.timestamp))
|
|
||||||
.expect("Sending start success failed");
|
|
||||||
self.verif_reporter
|
|
||||||
.completion_success(started_token, Some(&self.timestamp))
|
|
||||||
.expect("Sending completion success failed");
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Err(e) => match e {
|
|
||||||
TryRecvError::Empty => false,
|
|
||||||
TryRecvError::Disconnected => {
|
|
||||||
warn!("ACS thread: Message Queue TX disconnected!");
|
|
||||||
false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn periodic_operation(&mut self) {
|
|
||||||
update_time(&mut self.time_provider, &mut self.timestamp);
|
|
||||||
loop {
|
|
||||||
if !self.try_reading_one_request() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
654
satrs-example/src/acs/mgm.rs
Normal file
654
satrs-example/src/acs/mgm.rs
Normal file
@ -0,0 +1,654 @@
|
|||||||
|
use derive_new::new;
|
||||||
|
use satrs::hk::{HkRequest, HkRequestVariant};
|
||||||
|
use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender};
|
||||||
|
use satrs::queue::{GenericSendError, GenericTargetedMessagingError};
|
||||||
|
use satrs_example::{DeviceMode, TimestampHelper};
|
||||||
|
use satrs_minisim::acs::lis3mdl::{
|
||||||
|
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::{Arc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use satrs::mode::{
|
||||||
|
ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequest, ModeRequestHandler,
|
||||||
|
};
|
||||||
|
use satrs::pus::{EcssTmSender, PusTmVariant};
|
||||||
|
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId};
|
||||||
|
use satrs_example::config::components::{NO_SENDER, PUS_MODE_SERVICE};
|
||||||
|
|
||||||
|
use crate::hk::PusHkHelper;
|
||||||
|
use crate::pus::hk::{HkReply, HkReplyVariant};
|
||||||
|
use crate::requests::CompositeRequest;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub const NR_OF_DATA_AND_CFG_REGISTERS: usize = 14;
|
||||||
|
|
||||||
|
// Register adresses to access various bytes from the raw reply.
|
||||||
|
pub const X_LOWBYTE_IDX: usize = 9;
|
||||||
|
pub const Y_LOWBYTE_IDX: usize = 11;
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SpiInterface {
|
||||||
|
type Error: Debug;
|
||||||
|
fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SpiDummyInterface {
|
||||||
|
pub dummy_values: MgmLis3RawValues,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpiInterface for SpiDummyInterface {
|
||||||
|
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.dummy_values.x.to_le_bytes());
|
||||||
|
rx[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.y.to_be_bytes());
|
||||||
|
rx[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.z.to_be_bytes());
|
||||||
|
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)]
|
||||||
|
pub struct MgmData {
|
||||||
|
pub valid: bool,
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MpscModeLeafInterface {
|
||||||
|
pub request_rx: mpsc::Receiver<GenericMessage<ModeRequest>>,
|
||||||
|
pub reply_to_pus_tx: mpsc::Sender<GenericMessage<ModeReply>>,
|
||||||
|
pub reply_to_parent_tx: mpsc::SyncSender<GenericMessage<ModeReply>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BufWrapper {
|
||||||
|
tx_buf: [u8; 32],
|
||||||
|
rx_buf: [u8; 32],
|
||||||
|
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.
|
||||||
|
#[derive(new)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub struct MgmHandlerLis3Mdl<
|
||||||
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> {
|
||||||
|
id: UniqueApidTargetId,
|
||||||
|
dev_str: &'static str,
|
||||||
|
mode_interface: MpscModeLeafInterface,
|
||||||
|
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||||
|
hk_reply_tx: mpsc::Sender<GenericMessage<HkReply>>,
|
||||||
|
switch_helper: SwitchHelper,
|
||||||
|
tm_sender: TmSender,
|
||||||
|
pub com_interface: ComInterface,
|
||||||
|
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||||
|
#[new(value = "PusHkHelper::new(id)")]
|
||||||
|
hk_helper: PusHkHelper,
|
||||||
|
#[new(default)]
|
||||||
|
mode_helpers: ModeHelpers,
|
||||||
|
#[new(default)]
|
||||||
|
bufs: BufWrapper,
|
||||||
|
#[new(default)]
|
||||||
|
stamp_helper: TimestampHelper,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
|
||||||
|
{
|
||||||
|
pub fn periodic_operation(&mut self) {
|
||||||
|
self.stamp_helper.update_from_now();
|
||||||
|
// Handle requests.
|
||||||
|
self.handle_composite_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 {
|
||||||
|
log::trace!("polling LIS3MDL sensor {}", self.dev_str);
|
||||||
|
self.poll_sensor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 => {
|
||||||
|
let mgm_snapshot = *self.shared_mgm_set.lock().unwrap();
|
||||||
|
if let Ok(hk_tm) = self.hk_helper.generate_hk_report_packet(
|
||||||
|
self.stamp_helper.stamp(),
|
||||||
|
SetId::SensorData as u32,
|
||||||
|
&mut |hk_buf| {
|
||||||
|
hk_buf[0] = mgm_snapshot.valid as u8;
|
||||||
|
hk_buf[1..5].copy_from_slice(&mgm_snapshot.x.to_be_bytes());
|
||||||
|
hk_buf[5..9].copy_from_slice(&mgm_snapshot.y.to_be_bytes());
|
||||||
|
hk_buf[9..13].copy_from_slice(&mgm_snapshot.z.to_be_bytes());
|
||||||
|
Ok(13)
|
||||||
|
},
|
||||||
|
&mut self.bufs.tm_buf,
|
||||||
|
) {
|
||||||
|
// TODO: If sending the TM fails, we should also send a failure reply.
|
||||||
|
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");
|
||||||
|
} 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::DisablePeriodic => todo!(),
|
||||||
|
HkRequestVariant::ModifyCollectionInterval(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_mode_requests(&mut self) {
|
||||||
|
loop {
|
||||||
|
// TODO: Only allow one set mode request per cycle?
|
||||||
|
match self.mode_interface.request_rx.try_recv() {
|
||||||
|
Ok(msg) => {
|
||||||
|
let result = self.handle_mode_request(msg);
|
||||||
|
// TODO: Trigger event?
|
||||||
|
if result.is_err() {
|
||||||
|
log::warn!(
|
||||||
|
"{}: mode request failed with error {:?}",
|
||||||
|
self.dev_str,
|
||||||
|
result.err().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if e != mpsc::TryRecvError::Empty {
|
||||||
|
log::warn!("{}: failed to receive mode request: {:?}", self.dev_str, e);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<
|
||||||
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> ModeProvider for MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
|
||||||
|
{
|
||||||
|
fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||||
|
self.mode_helpers.current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> ModeRequestHandler for MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
|
||||||
|
{
|
||||||
|
type Error = ModeError;
|
||||||
|
|
||||||
|
fn start_transition(
|
||||||
|
&mut self,
|
||||||
|
requestor: MessageMetadata,
|
||||||
|
mode_and_submode: ModeAndSubmode,
|
||||||
|
) -> Result<(), satrs::mode::ModeError> {
|
||||||
|
log::info!(
|
||||||
|
"{}: transitioning to mode {:?}",
|
||||||
|
self.dev_str,
|
||||||
|
mode_and_submode
|
||||||
|
);
|
||||||
|
self.mode_helpers.current = mode_and_submode;
|
||||||
|
if mode_and_submode.mode() == DeviceMode::Off as u32 {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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.mode_helpers.target = None;
|
||||||
|
self.announce_mode(requestor, false);
|
||||||
|
if let Some(requestor) = requestor {
|
||||||
|
if requestor.sender_id() == NO_SENDER {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
||||||
|
log::warn!(
|
||||||
|
"can not send back mode reply to sender {:x}",
|
||||||
|
requestor.sender_id()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_mode_reply(
|
||||||
|
&self,
|
||||||
|
requestor: MessageMetadata,
|
||||||
|
reply: ModeReply,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
if requestor.sender_id() != PUS_MODE_SERVICE.id() {
|
||||||
|
log::warn!(
|
||||||
|
"can not send back mode reply to sender {}",
|
||||||
|
requestor.sender_id()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.mode_interface
|
||||||
|
.reply_to_pus_tx
|
||||||
|
.send(GenericMessage::new(requestor, reply))
|
||||||
|
.map_err(|_| GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_mode_info(
|
||||||
|
&mut self,
|
||||||
|
_requestor_info: MessageMetadata,
|
||||||
|
_info: ModeAndSubmode,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::{mpsc, Arc};
|
||||||
|
|
||||||
|
use satrs::{
|
||||||
|
mode::{ModeReply, ModeRequest},
|
||||||
|
power::SwitchStateBinary,
|
||||||
|
request::{GenericMessage, UniqueApidTargetId},
|
||||||
|
tmtc::PacketAsVec,
|
||||||
|
};
|
||||||
|
use satrs_example::config::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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MgmTestbench {
|
||||||
|
pub mode_request_tx: mpsc::Sender<GenericMessage<ModeRequest>>,
|
||||||
|
pub mode_reply_rx_to_pus: mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||||
|
pub mode_reply_rx_to_parent: mpsc::Receiver<GenericMessage<ModeReply>>,
|
||||||
|
pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
|
||||||
|
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
|
||||||
|
pub tm_rx: mpsc::Receiver<PacketAsVec>,
|
||||||
|
pub handler:
|
||||||
|
MgmHandlerLis3Mdl<TestSpiInterface, mpsc::Sender<PacketAsVec>, TestSwitchHelper>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MgmTestbench {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (request_tx, request_rx) = mpsc::channel();
|
||||||
|
let (reply_tx_to_pus, reply_rx_to_pus) = mpsc::channel();
|
||||||
|
let (reply_tx_to_parent, reply_rx_to_parent) = mpsc::sync_channel(5);
|
||||||
|
let mode_interface = MpscModeLeafInterface {
|
||||||
|
request_rx,
|
||||||
|
reply_to_pus_tx: reply_tx_to_pus,
|
||||||
|
reply_to_parent_tx: reply_tx_to_parent,
|
||||||
|
};
|
||||||
|
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||||
|
let (hk_reply_tx, hk_reply_rx) = mpsc::channel();
|
||||||
|
let (tm_tx, tm_rx) = mpsc::channel::<PacketAsVec>();
|
||||||
|
let shared_mgm_set = Arc::default();
|
||||||
|
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,
|
||||||
|
tm_rx,
|
||||||
|
hk_reply_rx,
|
||||||
|
handler: MgmHandlerLis3Mdl::new(
|
||||||
|
UniqueApidTargetId::new(Apid::Acs as u16, 1),
|
||||||
|
"TEST_MGM",
|
||||||
|
mode_interface,
|
||||||
|
composite_request_rx,
|
||||||
|
hk_reply_tx,
|
||||||
|
TestSwitchHelper::default(),
|
||||||
|
tm_tx,
|
||||||
|
TestSpiInterface::default(),
|
||||||
|
shared_mgm_set,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)),
|
||||||
|
))
|
||||||
|
.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(ModeAndSubmode::new(DeviceMode::Normal as u32, 0)),
|
||||||
|
))
|
||||||
|
.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
satrs-example/src/acs/mod.rs
Normal file
1
satrs-example/src/acs/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod mgm;
|
@ -12,8 +12,7 @@ use std::time::Duration;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut buf = [0; 32];
|
let mut buf = [0; 32];
|
||||||
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||||
let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap();
|
let pus_tc = PusTcCreator::new_simple(SpHeader::new_from_apid(0x02), 17, 1, &[], true);
|
||||||
let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
|
|
||||||
let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed");
|
let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed");
|
||||||
let tc_req_id = RequestId::new(&pus_tc);
|
let tc_req_id = RequestId::new(&pus_tc);
|
||||||
println!("Packing and sending PUS ping command TC[17,1] with request ID {tc_req_id}");
|
println!("Packing and sending PUS ping command TC[17,1] with request ID {tc_req_id}");
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user