Compare commits

...

94 Commits

Author SHA1 Message Date
muellerr ec952b771c Merge pull request 'add length check for CCSDS reader' (#213) from add-len-check-ccsds-reader into main
Reviewed-on: #213
2026-05-19 16:10:41 +02:00
Robin Mueller 01f61310fe add length check for CCSDS reader 2026-05-19 16:10:04 +02:00
muellerr d1e8015ded Merge pull request 'bump bitbybit dependency' (#212) from bump-bitbybit-dep into main
Reviewed-on: #212
2026-05-10 23:52:32 +02:00
Robin Mueller e48df492f0 bump bitbybit dependency 2026-05-10 23:52:19 +02:00
muellerr e83bb26f00 Merge pull request 'check in Cargo.lock file' (#211) from check-in-cargo-lock into main
Reviewed-on: #211
2026-05-10 23:51:05 +02:00
Robin Mueller 03cb93a874 check in Cargo.lock file 2026-05-10 23:50:34 +02:00
muellerr 215f663247 Merge pull request 'clippy fix' (#210) from clippy-fix into main
Reviewed-on: http://localhost:3000/rust/spacepackets/pulls/210
2026-05-04 22:44:24 +02:00
Robin Mueller 2342f87823 clippy fix 2026-05-04 22:42:00 +02:00
muellerr 8c5f1539f8 Merge pull request 'fix: unexpected panic in PusTmReader constructor' (#209) from merge-changes into main
Reviewed-on: http://localhost:3000/rust/spacepackets/pulls/209
2026-05-04 22:32:21 +02:00
Robin Mueller bc653685b1 Merge pull request #12 from koloiyolo/fix/unwrap_in_pus_tm_reader_constructor
fix: unexpected panic in PusTmReader constructor
2026-05-04 22:04:27 +02:00
Jakub Kołodziej d9453d46fe fix: unexpected panic in PusTmReader constructor 2026-05-04 11:32:32 +02:00
muellerr 52d45384ab Merge pull request 'clippy fix' (#208) from clippy-fix into main
Reviewed-on: #208
2026-03-09 10:18:18 +01:00
Robin Mueller 8374fddd13 clippy fix 2026-03-09 10:16:52 +01:00
muellerr 55b547ada4 Merge pull request 'patch some missing USLP protocol IDs' (#207) from patch-missing-uslp-protocol-ids into main
Reviewed-on: #207
2026-03-09 10:11:22 +01:00
Robin Mueller ff8fb65151 patch some missing USLP protocol IDs 2026-03-09 10:09:18 +01:00
muellerr a27d8b3634 Merge pull request 'Add CCSDS zero copy writer' (#206) from ccsds-zero-copy-writer into main
Reviewed-on: #206
2026-01-19 09:40:39 +01:00
Robin Mueller 1d971cc4d8 Add CCSDS zero copy writer 2026-01-16 15:10:43 +01:00
muellerr 547a093f28 Merge pull request 'creator with reserved data field' (#205) from add-creator-with-reserved-data-field into main
Reviewed-on: #205
2025-12-10 23:35:58 +01:00
Robin Mueller b9d18ae0a3 add creator with reserved datafield 2025-12-10 23:33:07 +01:00
muellerr 64d53ea48e Merge pull request 'infallible ctor for primary header' (#204) from infallible-primary-header-ctor into main
Reviewed-on: #204
2025-12-10 23:28:38 +01:00
Robin Mueller c07144a193 infallible ctor for primary header 2025-12-10 23:27:56 +01:00
muellerr 5db91e608f Merge pull request 'USLP reader getter functions' (#203) from additional-uslp-getter-functions into main
Reviewed-on: #203
2025-12-10 11:41:27 +01:00
Robin Mueller 369b03f279 USLP reader getter functions 2025-12-10 11:34:28 +01:00
muellerr 66201a6f6d Merge pull request 'USLP raw buffer caching' (#202) from uslp-raw-buffer-caching into main
Reviewed-on: #202
2025-12-10 10:20:04 +01:00
Robin Mueller bb2258e177 USLP raw buffer caching 2025-12-10 10:19:32 +01:00
muellerr 99aa85c701 Merge pull request 'mutability fix' (#201) from vec-ctors-uslp into main
Reviewed-on: #201
2025-12-10 10:18:59 +01:00
Robin Mueller 7f6bbc27e5 mutability fix 2025-12-10 10:05:18 +01:00
muellerr 5eab255562 Merge pull request 'add to_vec writers for USLP' (#200) from vec-ctors-uslp into main
Reviewed-on: #200
2025-12-10 10:00:22 +01:00
Robin Mueller 337007f006 add to_vec writers for USLP 2025-12-10 09:59:52 +01:00
muellerr 6e2b4c87db Merge pull request 'minor tweaks' (#199) from missing-uslp-ctor into main
Reviewed-on: #199
2025-12-10 09:41:55 +01:00
Robin Mueller f5393aabe0 minor tweaks 2025-12-10 09:41:43 +01:00
muellerr adcf62833d Merge pull request 'added missing ctor' (#198) from missing-uslp-ctor into main
Reviewed-on: #198
2025-12-10 09:39:46 +01:00
Robin Mueller 42f87dad99 added missing ctor 2025-12-09 15:18:02 +01:00
muellerr e5b165def3 Merge pull request 'Add USLP frame creator' (#197) from add-uslp-frame-creator into main
Reviewed-on: #197
2025-12-09 15:02:02 +01:00
Robin Mueller 9882d3cdc1 add tests 2025-12-09 15:00:52 +01:00
Robin Mueller d8fb5e1ca5 add USLP frame creator 2025-12-08 20:49:17 +01:00
muellerr 9035ecd139 Merge pull request 'USLP type improvements' (#196) from uslp-type-improvements into main
Reviewed-on: #196
2025-12-08 18:53:48 +01:00
Robin Mueller 2a3e280d96 USLP type improvements 2025-12-08 18:52:06 +01:00
muellerr cf9306d992 Merge pull request 'more precise error handling for CCSDS support' (#195) from more-precise-error-handling into main
Reviewed-on: #195
2025-11-27 17:36:37 +01:00
Robin Mueller 73b11d0ae2 more precise error handling for CCSDS support 2025-11-27 17:34:51 +01:00
muellerr b7efa0378d Merge pull request 'fix docs CI' (#194) from fix-docs-ci into main
Reviewed-on: #194
2025-11-27 12:47:56 +01:00
Robin Mueller c6d10422d5 fix docs CI 2025-11-27 12:44:26 +01:00
muellerr 416a89b807 Merge pull request 'added raw data getter function' (#193) from ccsds-raw-data-getter into main
Reviewed-on: #193
2025-11-19 17:39:17 +01:00
Robin Mueller 8136f554b5 added raw data getter function 2025-11-19 17:28:11 +01:00
muellerr 665cb3b107 Merge pull request 'sequence counter update' (#192) from seq-counter-update into main
Reviewed-on: #192
2025-11-19 17:11:11 +01:00
Robin Mueller 989ace786e Update sequence counter impl 2025-11-19 17:10:02 +01:00
muellerr 402331c725 Merge pull request 'added distinction between CCSDS packet and user data' (#191) from ccsds-reader-user-data into main
Reviewed-on: #191
2025-11-19 17:02:33 +01:00
muellerr 3e3f33a7d1 added distiction between CCSDS packet and user data 2025-11-19 16:43:39 +01:00
muellerr f001234025 Merge pull request 'clippy' (#190) from clippy into main
Reviewed-on: #190
2025-11-06 13:43:48 +01:00
Robin Mueller e5a7839901 clippy 2025-11-06 13:43:20 +01:00
muellerr 5707c6322a Merge pull request 'changelog' (#189) from prep-v0.17.0 into main
Reviewed-on: #189
2025-11-06 13:27:09 +01:00
Robin Mueller 093f82ae86 changelog 2025-11-06 13:26:46 +01:00
muellerr e68d1ade48 Merge pull request 'Finish full crate docs' (#188) from renaming-docs-for-ecss into main
Reviewed-on: #188
2025-11-05 20:20:35 +01:00
Robin Mueller fbdc325d0d Finish full crate docs 2025-11-05 20:18:31 +01:00
muellerr 4bc0219cb2 Merge pull request 'docs and minor cfdp change' (#187) from cfdp-update-docs into main
Reviewed-on: #187
2025-11-04 18:54:33 +01:00
muellerr 3f4f76849f docs and minor cfdp change 2025-11-04 18:50:53 +01:00
muellerr fb1e2fc583 Merge pull request 'added missing derives' (#186) from add-missing-derives into main
Reviewed-on: #186
2025-11-04 15:57:46 +01:00
Robin Mueller 96e5851864 added missing derives 2025-11-04 15:57:09 +01:00
muellerr b4d00c26c5 Merge pull request 'add direct APID getter' (#185) from add-direct-ccsds-apid-getter into main
Reviewed-on: #185
2025-11-04 15:40:10 +01:00
Robin Mueller a8b64f2fef add direct APID getter 2025-11-04 15:39:37 +01:00
muellerr e7cb6f2a7a Merge pull request 'error reporting bugfix' (#184) from ccsds-packet-reader-error-reporting-fix into main
Reviewed-on: #184
2025-11-04 15:34:54 +01:00
Robin Mueller 973ba4d3c4 error reporting bugfix 2025-11-04 15:34:15 +01:00
muellerr 8789e34c14 Merge pull request 'added missing function to reader' (#183) from add-ccsds-id-function-to-reader into main
Reviewed-on: #183
2025-10-31 16:56:12 +01:00
Robin Mueller a68e82a825 added missing function to reader 2025-10-31 16:55:45 +01:00
muellerr 0b46fa785b Merge pull request 'better naming' (#182) from naming-improvement into main
Reviewed-on: #182
2025-10-31 16:03:40 +01:00
Robin Mueller c57ee3e131 better naming 2025-10-31 16:03:08 +01:00
muellerr 6ac84c3dca Merge pull request 'add option to ignore checksum for CCSDS' (#181) from add-option-to-ignore-checksum into main
Reviewed-on: #181
2025-10-31 16:01:11 +01:00
Robin Mueller 374f39f13b add option to ignore checksum for CCSDS 2025-10-31 15:59:41 +01:00
muellerr 2bc6167710 Merge pull request 'less confusing naming' (#180) from less-confusing-naming into main
Reviewed-on: #180
2025-10-31 12:55:53 +01:00
Robin Mueller cfe0937afe less confusing naming 2025-10-31 12:55:07 +01:00
muellerr e1c693cb29 Merge pull request 'update ECSS PUS naming convention' (#179) from update-ecss-pus-naming-convention into main
Reviewed-on: #179
2025-10-31 12:39:39 +01:00
Robin Mueller 38165420b7 update ECSS PUS naming convention 2025-10-31 12:38:59 +01:00
muellerr 0d09ff7825 Merge pull request 'add docs and minor changes' (#178) from add-docs-minor-changes into main
Reviewed-on: #178
2025-10-31 11:45:06 +01:00
Robin Mueller 8f2096ca35 add docs and minor changes 2025-10-31 11:39:23 +01:00
muellerr 3f35e9dba9 Merge pull request 'add owned CCSDS packet creator' (#177) from add-owned-ccsds-packet-creator into main
Reviewed-on: #177
2025-10-31 10:10:50 +01:00
Robin Mueller ea96099f55 add owned CCSDS packet creator 2025-10-31 10:10:11 +01:00
muellerr e117239852 Merge pull request 'add useful functions' (#176) from add-ccsds-id-functions into main
Reviewed-on: #176
2025-10-30 18:58:27 +01:00
Robin Mueller 844c517a94 add useful functions 2025-10-30 18:58:10 +01:00
muellerr 0ae2ac149b Merge pull request 'add CCSDS packet ID' (#175) from add-ccsds-packet-id into main
Reviewed-on: #175
2025-10-29 21:58:56 +01:00
Robin Mueller 2b41f9754d add CCSDS packet ID 2025-10-29 21:45:07 +01:00
muellerr 8e2e0ce632 Merge pull request 'fix portable atomic support' (#174) from fix-portable-atomic-support into main
Reviewed-on: #174
2025-10-29 16:13:09 +01:00
Robin Mueller 14d935ac2a fix portable atomic support 2025-10-29 16:05:11 +01:00
muellerr 756a803213 Merge pull request 'prepare v0.17.0' (#173) from prepare-v0.17.0 into main
Reviewed-on: #173
2025-10-29 16:04:19 +01:00
Robin Mueller 937bdeaf54 prepare v0.17.0 2025-10-29 15:48:29 +01:00
muellerr bc30143d61 Merge pull request 'start adding improved CCSDS packet support' (#172) from add-better-ccsds-packet-support into main
Reviewed-on: #172
2025-10-29 15:44:38 +01:00
Robin Mueller 549e323211 start adding improved CCSDS packet support 2025-10-29 15:28:25 +01:00
muellerr 82c3e06ac0 Merge pull request 'feature gate all core atomics' (#171) from feature-gate-all-core-atomics into main
Reviewed-on: #171
2025-10-29 11:26:57 +01:00
Robin Mueller 750add26ef feature gate all core atomics 2025-10-29 11:24:05 +01:00
muellerr c3ff947fb0 Merge pull request 'move some modules' (#170) from clean-up-cds-time-mod into main
Reviewed-on: #170
2025-10-15 15:26:39 +02:00
Robin Mueller 8d86ecc8ee move some modules 2025-10-15 15:26:06 +02:00
muellerr 4b2bebb8cb Merge pull request 'simplified CDS short impl' (#169) from simplify-cds-timestamp-impl into main
Reviewed-on: #169
2025-10-15 15:01:06 +02:00
Robin Mueller e0b7a6a6bb simplified CDS short impl 2025-10-15 11:57:44 +02:00
muellerr 49983a5d6c Merge pull request 'update for docs generation' (#168) from doc-generation-update into main
Reviewed-on: #168
2025-10-02 09:45:45 +02:00
Robin Mueller 04c864d6a2 update for docs generation 2025-10-01 00:20:47 +02:00
36 changed files with 6226 additions and 1539 deletions
+3 -2
View File
@@ -39,12 +39,13 @@ jobs:
matrix: matrix:
target: target:
- armv7-unknown-linux-gnueabihf - armv7-unknown-linux-gnueabihf
- thumbv6m-none-eabi
- thumbv7em-none-eabihf - thumbv7em-none-eabihf
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf" targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf, thumbv6m-none-eabi"
- run: cargo check --release --target=${{matrix.target}} --no-default-features - run: cargo check --release --target=${{matrix.target}} --no-default-features
fmt: fmt:
@@ -63,7 +64,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/rust-toolchain@nightly
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features - run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features --no-deps
clippy: clippy:
name: Clippy name: Clippy
-1
View File
@@ -1,6 +1,5 @@
# Rust # Rust
/target /target
/Cargo.lock
# CLion # CLion
/.idea/* /.idea/*
+54 -1
View File
@@ -8,6 +8,58 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.18.0] ?
## Changed
- Added distinction between `CcsdsPacketReader::user_data` and `CcsdsPacketReader::packet_data`.
- Added distinction between `CcsdsPacketCreatorWithReservedData::user_data` and
`CcsdsPacketCreatorWithReservedData::packet_data`, including mutable variants as well.
- `SequenceCounter::MAX_BIT_WIDTH` is now a regular trait method `SequenceCounter::max_bit_width`
to allow dyn compatibility and easier usage in trait objects.
- Improved type level support in USLP module by using VC ID type `u6` and MAP ID type `u4`.
## Added
- `checksum` getter for `CcsdsPacketReader`.
- Added `SequenceCounterOnFile` which persists the sequence counter by writing it to a file.
- Added `SequenceCounter::set` method which allows manually setting an initial value.
- Added `CcsdsPacketReader::raw_data` full data getter.
- `UslpFrameCreator` constructor
- Additional reader methods on `UslpFrameReader`
## Removed
- `SequenceCounter::increment_mut` and `SequenceCounter::get_and_increment_mut`
# [v0.17.0] 2025-11-06
## Changed
- `CdsCommon` renamed to `CdsBase`
- cfdp: Removed `FileDirectiveType` variant `*Pdu` suffix
- ecss: Renamed `Subservice` to `MessageSubtypeId`
- Simplified CDS short timestamp, contains one less field which reduced serialization length.
- Renamed `UnsignedEnum::value` to `UnsignedEnum::value_raw`, `value` is reserved for the `const`
value getter.
- Renamed `CcsdsPrimaryHeader::from_composite_fields` to
`CcsdsPrimaryHeader::new_from_composite_fields`
- Renamed `PusPacket::service` to `PusPacket::service_type_id` and `PusPacket::subservice` to
`PusPacket::message_subtype_id`. Also added `PusPacket::message_type_id`. Performed the same
change for the ECSS PUS C secondary header traits.
## Added
- Added `CcsdsPacketCreator`, `CcsdsPacketReader`, `CcsdsPacketCreatorWithReservedData` and
`CcsdsPacketCreatorOwned` which simplify the process of creating full CCSDS space packets.
- Added new optional `portable-atomic` because portable atomics might not work on every
architecture in addition to requiring atomic CAS support enabled inside for the crate.
## Fixed
- All `core::sync::Atomic?` usages are feature gated properly to allow compilation on systems
without atomic CAS.
# [v0.16.1] 2025-09-26 # [v0.16.1] 2025-09-26
## Fixed ## Fixed
@@ -650,7 +702,8 @@ The timestamp of `PusTm` is now optional. See Added and Changed section for deta
Initial release with CCSDS Space Packet Primary Header implementation and basic PUS TC and TM Initial release with CCSDS Space Packet Primary Header implementation and basic PUS TC and TM
implementations. implementations.
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.16.1...HEAD [unreleased]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.17.0...HEAD
[v0.17.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.16.1...v0.17.0
[v0.16.1]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.16.0...v0.16.1 [v0.16.1]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.16.0...v0.16.1
[v0.16.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.15.0...v0.16.0 [v0.16.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.15.0...v0.16.0
[v0.15.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.14.0...v0.15.0 [v0.15.0]: https://egit.irs.uni-stuttgart.de/rust/spacepackets/compare/v0.14.0...v0.15.0
Generated
+777
View File
@@ -0,0 +1,777 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "arbitrary-int"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c858caffa49edfc4ecc45a4bec37abd3e88041a2903816f10f990b7b41abc281"
dependencies = [
"defmt 0.3.100",
"serde",
]
[[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.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitbybit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d2a3353d70ac1091a33cbf31fc7e77b19091538a7e306e3740712af19807ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crc"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "defmt"
version = "0.3.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
dependencies = [
"defmt 1.0.1",
]
[[package]]
name = "defmt"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
dependencies = [
"bitflags 1.3.2",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
dependencies = [
"defmt-parser",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror",
]
[[package]]
name = "delegate"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "deranged"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [
"powerfmt",
]
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32",
"rustc_version",
"serde",
"spin",
"stable_deref_trait",
]
[[package]]
name = "iana-time-zone"
version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "js-sys"
version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
dependencies = [
"num_enum_derive",
"rustversion",
]
[[package]]
name = "num_enum_derive"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "postcard"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
dependencies = [
"cobs",
"embedded-io 0.4.0",
"embedded-io 0.6.1",
"heapless",
"serde",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags 2.10.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "spacepackets"
version = "0.17.0"
dependencies = [
"arbitrary-int",
"bitbybit",
"chrono",
"crc",
"defmt 1.0.1",
"delegate",
"num-traits",
"num_enum",
"paste",
"portable-atomic",
"postcard",
"serde",
"tempfile",
"thiserror",
"time",
"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.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "thiserror"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "zerocopy"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+7 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "spacepackets" name = "spacepackets"
version = "0.16.1" version = "0.17.0"
edition = "2021" edition = "2021"
rust-version = "1.83" rust-version = "1.83"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
@@ -14,16 +14,16 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
[dependencies] [dependencies]
crc = "3" crc = "3"
delegate = ">=0.8, <=0.13" delegate = "0.13"
paste = "1" paste = "1"
zerocopy = { version = "0.8", features = ["derive"] } zerocopy = { version = "0.8", features = ["derive"] }
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
num_enum = { version = ">0.5, <=0.7", default-features = false } num_enum = { version = "0.7", default-features = false }
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
serde = { version = "1", optional = true, default-features = false, features = ["derive"] } serde = { version = "1", optional = true, default-features = false, features = ["derive"] }
arbitrary-int = { version = "2" } arbitrary-int = { version = "2" }
portable-atomic = "1" portable-atomic = { version = "1", optional = true }
bitbybit = "1.4" bitbybit = "2"
time = { version = "0.3", default-features = false, optional = true } time = { version = "0.3", default-features = false, optional = true }
chrono = { version = "0.4", default-features = false, optional = true } chrono = { version = "0.4", default-features = false, optional = true }
@@ -32,6 +32,7 @@ defmt = { version = "1", default-features = false, optional = true }
[features] [features]
default = ["std"] default = ["std"]
std = ["alloc", "chrono/std", "chrono/clock", "thiserror/std"] std = ["alloc", "chrono/std", "chrono/clock", "thiserror/std"]
portable-atomic = ["dep:portable-atomic", "portable-atomic/require-cas"]
defmt = ["dep:defmt", "arbitrary-int/defmt"] defmt = ["dep:defmt", "arbitrary-int/defmt"]
serde = ["dep:serde", "chrono?/serde", "arbitrary-int/serde"] serde = ["dep:serde", "chrono?/serde", "arbitrary-int/serde"]
alloc = ["chrono?/alloc", "defmt?/alloc", "serde?/alloc"] alloc = ["chrono?/alloc", "defmt?/alloc", "serde?/alloc"]
@@ -40,6 +41,7 @@ timelib = ["dep:time"]
[dev-dependencies] [dev-dependencies]
postcard = { version = "1", features = ["alloc"] } postcard = { version = "1", features = ["alloc"] }
chrono = "0.4" chrono = "0.4"
tempfile = "3"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
+3
View File
@@ -43,6 +43,9 @@ Currently, this includes the following components:
- [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library. - [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library.
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the - [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
[`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types. [`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
- [`portable-atomic`](https://github.com/taiki-e/portable-atomic): Basic support for `portable-atomic`
crate in addition to the support for core atomic types. This support requires atomic CAS support
enabled in the portable atomic crate.
# Examples # Examples
+7 -5
View File
@@ -1,9 +1,12 @@
all: check build embedded test clippy fmt docs coverage all: check build clippy embedded test check-fmt docs coverage
clippy: clippy:
cargo clippy -- -D warnings cargo clippy -- -D warnings
fmt: fmt:
cargo fmt --all
check-fmt:
cargo fmt --all -- --check cargo fmt --all -- --check
check: check:
@@ -11,6 +14,7 @@ check:
embedded: embedded:
cargo build --target thumbv7em-none-eabihf --no-default-features cargo build --target thumbv7em-none-eabihf --no-default-features
cargo build --target thumbv6m-none-eabi --no-default-features
test: test:
cargo nextest r --all-features cargo nextest r --all-features
@@ -20,12 +24,10 @@ build:
cargo build --all-features cargo build --all-features
docs: docs:
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" RUSTDOCFLAGS="--cfg docsrs -Z unstable-options --generate-link-to-definition" cargo +nightly doc --all-features --no-deps
cargo +nightly doc --all-features
docs-html: docs-html:
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" RUSTDOCFLAGS="--cfg docsrs -Z unstable-options --generate-link-to-definition" cargo +nightly doc --all-features --open
cargo +nightly doc --all-features --open
coverage: coverage:
cargo llvm-cov nextest cargo llvm-cov nextest
+5 -1
View File
@@ -8,6 +8,7 @@ use std::string::String;
use super::TlvLvDataTooLargeError; use super::TlvLvDataTooLargeError;
/// Minmum length of a CFDP length-value structure in bytes.
pub const MIN_LV_LEN: usize = 1; pub const MIN_LV_LEN: usize = 1;
/// Generic CFDP length-value (LV) abstraction as specified in CFDP 5.1.8. /// Generic CFDP length-value (LV) abstraction as specified in CFDP 5.1.8.
@@ -63,8 +64,10 @@ pub(crate) fn generic_len_check_deserialization(
} }
impl<'data> Lv<'data> { impl<'data> Lv<'data> {
/// Minimum length of a LV structure in bytes.
pub const MIN_LEN: usize = MIN_LV_LEN; pub const MIN_LEN: usize = MIN_LV_LEN;
/// Generic constructor.
#[inline] #[inline]
pub fn new(data: &[u8]) -> Result<Lv<'_>, TlvLvDataTooLargeError> { pub fn new(data: &[u8]) -> Result<Lv<'_>, TlvLvDataTooLargeError> {
if data.len() > u8::MAX as usize { if data.len() > u8::MAX as usize {
@@ -118,6 +121,7 @@ impl<'data> Lv<'data> {
self.data.len() == 0 self.data.len() == 0
} }
/// Raw value part of the LV.
#[inline] #[inline]
pub fn value(&self) -> &[u8] { pub fn value(&self) -> &[u8] {
self.data self.data
@@ -179,7 +183,7 @@ impl<'data> Lv<'data> {
} }
#[cfg(test)] #[cfg(test)]
pub mod tests { mod tests {
use alloc::string::ToString; use alloc::string::ToString;
use super::*; use super::*;
+47 -8
View File
@@ -13,43 +13,55 @@ pub const CFDP_VERSION_2_NAME: &str = "CCSDS 727.0-B-5";
/// Currently, only this version is supported. /// Currently, only this version is supported.
pub const CFDP_VERSION_2: u8 = 0b001; pub const CFDP_VERSION_2: u8 = 0b001;
/// PDU type.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)] #[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum PduType { pub enum PduType {
/// File directive PDU.
FileDirective = 0, FileDirective = 0,
/// File data PDU.
FileData = 1, FileData = 1,
} }
/// PDU direction.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)] #[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum Direction { pub enum Direction {
/// Going towards the file receiver.
TowardsReceiver = 0, TowardsReceiver = 0,
/// Going towards the file sender.
TowardsSender = 1, TowardsSender = 1,
} }
/// PDU transmission mode.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)] #[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum TransmissionMode { pub enum TransmissionMode {
/// Acknowledged (class 1) transfer.
Acknowledged = 0, Acknowledged = 0,
/// Unacknowledged (class 2) transfer.
Unacknowledged = 1, Unacknowledged = 1,
} }
/// CRC flag.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)] #[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum CrcFlag { pub enum CrcFlag {
/// No CRC for the packet.
NoCrc = 0, NoCrc = 0,
/// Packet has CRC.
WithCrc = 1, WithCrc = 1,
} }
@@ -78,7 +90,9 @@ impl From<CrcFlag> for bool {
#[bitbybit::bitenum(u1, exhaustive = true)] #[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum SegmentMetadataFlag { pub enum SegmentMetadataFlag {
/// Segment metadata not present.
NotPresent = 0, NotPresent = 0,
/// Segment metadata present.
Present = 1, Present = 1,
} }
@@ -89,22 +103,30 @@ pub enum SegmentMetadataFlag {
#[bitbybit::bitenum(u1, exhaustive = true)] #[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum SegmentationControl { pub enum SegmentationControl {
/// No record boundary preservation.
NoRecordBoundaryPreservation = 0, NoRecordBoundaryPreservation = 0,
/// With record boundary preservation.
WithRecordBoundaryPreservation = 1, WithRecordBoundaryPreservation = 1,
} }
/// Fault handler codes according to the CFDP standard.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u3, exhaustive = false)] #[bitbybit::bitenum(u3, exhaustive = false)]
#[repr(u8)] #[repr(u8)]
pub enum FaultHandlerCode { pub enum FaultHandlerCode {
/// Notice of cancellation fault handler code.
NoticeOfCancellation = 0b0001, NoticeOfCancellation = 0b0001,
/// Notice of suspension fault handler code.
NoticeOfSuspension = 0b0010, NoticeOfSuspension = 0b0010,
/// Ignore error fault handler code.
IgnoreError = 0b0011, IgnoreError = 0b0011,
/// Abandon transaction fault handler code.
AbandonTransaction = 0b0100, AbandonTransaction = 0b0100,
} }
/// CFDP condition codes.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -113,15 +135,25 @@ pub enum FaultHandlerCode {
pub enum ConditionCode { pub enum ConditionCode {
/// This is not an error condition for which a faulty handler override can be specified /// This is not an error condition for which a faulty handler override can be specified
NoError = 0b0000, NoError = 0b0000,
/// Positive acknowledgement limit reached.
PositiveAckLimitReached = 0b0001, PositiveAckLimitReached = 0b0001,
/// Keep-alive limit reached.
KeepAliveLimitReached = 0b0010, KeepAliveLimitReached = 0b0010,
/// Invalid transmission mode.
InvalidTransmissionMode = 0b0011, InvalidTransmissionMode = 0b0011,
/// Filestore rejection.
FilestoreRejection = 0b0100, FilestoreRejection = 0b0100,
/// File checksum error.
FileChecksumFailure = 0b0101, FileChecksumFailure = 0b0101,
/// File size error.
FileSizeError = 0b0110, FileSizeError = 0b0110,
/// NAK limit reached.
NakLimitReached = 0b0111, NakLimitReached = 0b0111,
/// Inactivity detected.
InactivityDetected = 0b1000, InactivityDetected = 0b1000,
/// Check limit reached.
CheckLimitReached = 0b1010, CheckLimitReached = 0b1010,
/// Unsupported checksum type.
UnsupportedChecksumType = 0b1011, UnsupportedChecksumType = 0b1011,
/// Not an actual fault condition for which fault handler overrides can be specified /// Not an actual fault condition for which fault handler overrides can be specified
SuspendRequestReceived = 0b1110, SuspendRequestReceived = 0b1110,
@@ -129,6 +161,7 @@ pub enum ConditionCode {
CancelRequestReceived = 0b1111, CancelRequestReceived = 0b1111,
} }
/// Large file flag.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -151,6 +184,7 @@ pub enum TransactionStatus {
/// Transaction is not currently active and the CFDP implementation does not retain a /// Transaction is not currently active and the CFDP implementation does not retain a
/// transaction history. /// transaction history.
Undefined = 0b00, Undefined = 0b00,
/// Transaction is currently active.
Active = 0b01, Active = 0b01,
/// Transaction was active in the past and was terminated. /// Transaction was active in the past and was terminated.
Terminated = 0b10, Terminated = 0b10,
@@ -161,28 +195,28 @@ pub enum TransactionStatus {
/// Checksum types according to the /// Checksum types according to the
/// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/) /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/)
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum ChecksumType { pub enum ChecksumType {
/// Modular legacy checksum /// Modular legacy checksum
Modular = 0, Modular = 0,
/// CRC32 Proximity-1.
Crc32Proximity1 = 1, Crc32Proximity1 = 1,
/// CRC32C.
Crc32C = 2, Crc32C = 2,
/// Polynomial: 0x4C11DB7. Preferred checksum for now. /// CRC32. Polynomial: 0x4C11DB7. Preferred checksum for now.
Crc32 = 3, Crc32 = 3,
/// Null checksum (no checksum).
#[default]
NullChecksum = 15, NullChecksum = 15,
} }
impl Default for ChecksumType { /// Raw null checksum.
fn default() -> Self {
Self::NullChecksum
}
}
pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4]; pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4];
/// TLV or LV data larger than allowed [u8::MAX].
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -199,16 +233,21 @@ pub struct InvalidTlvTypeFieldError {
expected: Option<u8>, expected: Option<u8>,
} }
/// Generic TLV/LV error.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TlvLvError { pub enum TlvLvError {
/// Data too large error.
#[error("{0}")] #[error("{0}")]
DataTooLarge(#[from] TlvLvDataTooLargeError), DataTooLarge(#[from] TlvLvDataTooLargeError),
/// Byte conversion error.
#[error("byte conversion error: {0}")] #[error("byte conversion error: {0}")]
ByteConversion(#[from] ByteConversionError), ByteConversion(#[from] ByteConversionError),
/// Invalid TLV type field error.
#[error("{0}")] #[error("{0}")]
InvalidTlvTypeField(#[from] InvalidTlvTypeFieldError), InvalidTlvTypeField(#[from] InvalidTlvTypeFieldError),
/// Invalid value length.
#[error("invalid value length {0}")] #[error("invalid value length {0}")]
InvalidValueLength(usize), InvalidValueLength(usize),
/// Only applies to filestore requests and responses. Second name was missing where one is /// Only applies to filestore requests and responses. Second name was missing where one is
+36 -24
View File
@@ -1,3 +1,4 @@
//! # Acknowledgement (ACK) PDU packet implementation.
use crate::{ use crate::{
cfdp::{ConditionCode, CrcFlag, Direction, TransactionStatus}, cfdp::{ConditionCode, CrcFlag, Direction, TransactionStatus},
ByteConversionError, ByteConversionError,
@@ -10,6 +11,7 @@ use super::{
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Invalid [FileDirectiveType] of the acknowledged PDU error.
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("invalid directive code of acknowledged PDU")] #[error("invalid directive code of acknowledged PDU")]
pub struct InvalidAckedDirectiveCodeError(pub FileDirectiveType); pub struct InvalidAckedDirectiveCodeError(pub FileDirectiveType);
@@ -28,15 +30,16 @@ pub struct AckPdu {
} }
impl AckPdu { impl AckPdu {
/// Constructor.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
directive_code_of_acked_pdu: FileDirectiveType, directive_code_of_acked_pdu: FileDirectiveType,
condition_code: ConditionCode, condition_code: ConditionCode,
transaction_status: TransactionStatus, transaction_status: TransactionStatus,
) -> Result<Self, InvalidAckedDirectiveCodeError> { ) -> Result<Self, InvalidAckedDirectiveCodeError> {
if directive_code_of_acked_pdu == FileDirectiveType::EofPdu { if directive_code_of_acked_pdu == FileDirectiveType::Eof {
pdu_header.pdu_conf.direction = Direction::TowardsSender; pdu_header.pdu_conf.direction = Direction::TowardsSender;
} else if directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu { } else if directive_code_of_acked_pdu == FileDirectiveType::Finished {
pdu_header.pdu_conf.direction = Direction::TowardsReceiver; pdu_header.pdu_conf.direction = Direction::TowardsReceiver;
} else { } else {
return Err(InvalidAckedDirectiveCodeError(directive_code_of_acked_pdu)); return Err(InvalidAckedDirectiveCodeError(directive_code_of_acked_pdu));
@@ -52,6 +55,9 @@ impl AckPdu {
Ok(ack_pdu) Ok(ack_pdu)
} }
/// Constructor for an ACK PDU acknowledging an EOF PDU.
///
/// Relevant for the file receiver.
pub fn new_for_eof_pdu( pub fn new_for_eof_pdu(
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -60,13 +66,16 @@ impl AckPdu {
// Unwrap okay here, [new] can only fail on invalid directive codes. // Unwrap okay here, [new] can only fail on invalid directive codes.
Self::new( Self::new(
pdu_header, pdu_header,
FileDirectiveType::EofPdu, FileDirectiveType::Eof,
condition_code, condition_code,
transaction_status, transaction_status,
) )
.unwrap() .unwrap()
} }
/// Constructor for an ACK PDU acknowledging a Finished PDU.
///
/// Relevant for the file sender.
pub fn new_for_finished_pdu( pub fn new_for_finished_pdu(
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -75,28 +84,32 @@ impl AckPdu {
// Unwrap okay here, [new] can only fail on invalid directive codes. // Unwrap okay here, [new] can only fail on invalid directive codes.
Self::new( Self::new(
pdu_header, pdu_header,
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
condition_code, condition_code,
transaction_status, transaction_status,
) )
.unwrap() .unwrap()
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Directive code of the acknowledged PDU.
#[inline] #[inline]
pub fn directive_code_of_acked_pdu(&self) -> FileDirectiveType { pub fn directive_code_of_acked_pdu(&self) -> FileDirectiveType {
self.directive_code_of_acked_pdu self.directive_code_of_acked_pdu
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// Transaction status.
#[inline] #[inline]
pub fn transaction_status(&self) -> TransactionStatus { pub fn transaction_status(&self) -> TransactionStatus {
self.transaction_status self.transaction_status
@@ -110,6 +123,7 @@ impl AckPdu {
3 3
} }
/// Construct [Self] from the provided byte slice.
pub fn from_bytes(buf: &[u8]) -> Result<AckPdu, PduError> { pub fn from_bytes(buf: &[u8]) -> Result<AckPdu, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -117,13 +131,13 @@ impl AckPdu {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::AckPdu), expected: Some(FileDirectiveType::Ack),
} }
})?; })?;
if directive_type != FileDirectiveType::AckPdu { if directive_type != FileDirectiveType::Ack {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::AckPdu, expected: FileDirectiveType::Ack,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -134,8 +148,8 @@ impl AckPdu {
expected: None, expected: None,
} }
})?; })?;
if acked_directive_type != FileDirectiveType::EofPdu if acked_directive_type != FileDirectiveType::Eof
&& acked_directive_type != FileDirectiveType::FinishedPdu && acked_directive_type != FileDirectiveType::Finished
{ {
return Err(PduError::InvalidDirectiveType { return Err(PduError::InvalidDirectiveType {
found: acked_directive_type as u8, found: acked_directive_type as u8,
@@ -167,11 +181,11 @@ impl AckPdu {
.into()); .into());
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::AckPdu as u8; buf[current_idx] = FileDirectiveType::Ack as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = (self.directive_code_of_acked_pdu as u8) << 4; buf[current_idx] = (self.directive_code_of_acked_pdu as u8) << 4;
if self.directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu { if self.directive_code_of_acked_pdu == FileDirectiveType::Finished {
// This is the directive subtype code. It needs to be set to 0b0001 if the ACK PDU // This is the directive subtype code. It needs to be set to 0b0001 if the ACK PDU
// acknowledges a Finished PDU, and to 0b0000 otherwise. // acknowledges a Finished PDU, and to 0b0000 otherwise.
buf[current_idx] |= 0b0001; buf[current_idx] |= 0b0001;
@@ -185,6 +199,7 @@ impl AckPdu {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -198,7 +213,7 @@ impl CfdpPdu for AckPdu {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::AckPdu) Some(FileDirectiveType::Ack)
} }
} }
@@ -230,10 +245,7 @@ mod tests {
assert_eq!(ack_pdu.crc_flag(), expected_crc_flag); assert_eq!(ack_pdu.crc_flag(), expected_crc_flag);
assert_eq!(ack_pdu.file_flag(), LargeFileFlag::Normal); assert_eq!(ack_pdu.file_flag(), LargeFileFlag::Normal);
assert_eq!(ack_pdu.pdu_type(), PduType::FileDirective); assert_eq!(ack_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(ack_pdu.file_directive_type(), Some(FileDirectiveType::Ack));
ack_pdu.file_directive_type(),
Some(FileDirectiveType::AckPdu)
);
assert_eq!(ack_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(ack_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(ack_pdu.direction(), expected_dir); assert_eq!(ack_pdu.direction(), expected_dir);
assert_eq!(ack_pdu.source_id(), TEST_SRC_ID.into()); assert_eq!(ack_pdu.source_id(), TEST_SRC_ID.into());
@@ -247,14 +259,14 @@ mod tests {
let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0); let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
let ack_pdu = AckPdu::new( let ack_pdu = AckPdu::new(
pdu_header, pdu_header,
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
ConditionCode::NoError, ConditionCode::NoError,
TransactionStatus::Active, TransactionStatus::Active,
) )
.expect("creating ACK PDU failed"); .expect("creating ACK PDU failed");
assert_eq!( assert_eq!(
ack_pdu.directive_code_of_acked_pdu(), ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
verify_state(&ack_pdu, CrcFlag::NoCrc, Direction::TowardsReceiver); verify_state(&ack_pdu, CrcFlag::NoCrc, Direction::TowardsReceiver);
} }
@@ -273,8 +285,8 @@ mod tests {
assert_eq!(written, ack_pdu.len_written()); assert_eq!(written, ack_pdu.len_written());
verify_raw_header(ack_pdu.pdu_header(), &buf); verify_raw_header(ack_pdu.pdu_header(), &buf);
assert_eq!(buf[7], FileDirectiveType::AckPdu as u8); assert_eq!(buf[7], FileDirectiveType::Ack as u8);
assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::FinishedPdu as u8); assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::Finished as u8);
assert_eq!(buf[8] & 0b1111, 0b0001); assert_eq!(buf[8] & 0b1111, 0b0001);
assert_eq!(buf[9] >> 4 & 0b1111, condition_code as u8); assert_eq!(buf[9] >> 4 & 0b1111, condition_code as u8);
assert_eq!(buf[9] & 0b11, transaction_status as u8); assert_eq!(buf[9] & 0b11, transaction_status as u8);
@@ -292,7 +304,7 @@ mod tests {
let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0); let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
let ack_pdu = AckPdu::new( let ack_pdu = AckPdu::new(
pdu_header, pdu_header,
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
ConditionCode::NoError, ConditionCode::NoError,
TransactionStatus::Active, TransactionStatus::Active,
) )
@@ -320,12 +332,12 @@ mod tests {
assert_eq!( assert_eq!(
AckPdu::new( AckPdu::new(
pdu_header, pdu_header,
FileDirectiveType::MetadataPdu, FileDirectiveType::Metadata,
ConditionCode::NoError, ConditionCode::NoError,
TransactionStatus::Active, TransactionStatus::Active,
) )
.unwrap_err(), .unwrap_err(),
InvalidAckedDirectiveCodeError(FileDirectiveType::MetadataPdu) InvalidAckedDirectiveCodeError(FileDirectiveType::Metadata)
); );
} }
@@ -372,7 +384,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
ack_pdu.directive_code_of_acked_pdu(), ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::EofPdu FileDirectiveType::Eof
); );
verify_state(&ack_pdu, CrcFlag::WithCrc, Direction::TowardsSender); verify_state(&ack_pdu, CrcFlag::WithCrc, Direction::TowardsSender);
} }
+16 -10
View File
@@ -1,3 +1,4 @@
//! # End-of-File (EOF) PDU packet implementation.
use crate::cfdp::pdu::{ use crate::cfdp::pdu::{
add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field, add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
FileDirectiveType, PduError, PduHeader, FileDirectiveType, PduError, PduHeader,
@@ -25,6 +26,7 @@ pub struct EofPdu {
} }
impl EofPdu { impl EofPdu {
/// Constructor.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -45,6 +47,7 @@ impl EofPdu {
eof_pdu eof_pdu
} }
/// Constructor for no error EOF PDUs.
pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self { pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self {
Self::new( Self::new(
pdu_header, pdu_header,
@@ -55,21 +58,25 @@ impl EofPdu {
) )
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// File checksum.
#[inline] #[inline]
pub fn file_checksum(&self) -> u32 { pub fn file_checksum(&self) -> u32 {
self.file_checksum self.file_checksum
} }
/// File size.
#[inline] #[inline]
pub fn file_size(&self) -> u64 { pub fn file_size(&self) -> u64 {
self.file_size self.file_size
@@ -90,6 +97,7 @@ impl EofPdu {
len len
} }
/// Construct [Self] from the provided byte slice.
pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> { pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -102,13 +110,13 @@ impl EofPdu {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::EofPdu), expected: Some(FileDirectiveType::Eof),
} }
})?; })?;
if directive_type != FileDirectiveType::EofPdu { if directive_type != FileDirectiveType::Eof {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::EofPdu, expected: FileDirectiveType::Eof,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -145,7 +153,7 @@ impl EofPdu {
.into()); .into());
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::EofPdu as u8; buf[current_idx] = FileDirectiveType::Eof as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = (self.condition_code as u8) << 4; buf[current_idx] = (self.condition_code as u8) << 4;
current_idx += 1; current_idx += 1;
@@ -165,6 +173,7 @@ impl EofPdu {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -178,7 +187,7 @@ impl CfdpPdu for EofPdu {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::EofPdu) Some(FileDirectiveType::Eof)
} }
} }
@@ -221,10 +230,7 @@ mod tests {
assert_eq!(eof_pdu.crc_flag(), crc_flag); assert_eq!(eof_pdu.crc_flag(), crc_flag);
assert_eq!(eof_pdu.file_flag(), file_flag); assert_eq!(eof_pdu.file_flag(), file_flag);
assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective); assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(eof_pdu.file_directive_type(), Some(FileDirectiveType::Eof));
eof_pdu.file_directive_type(),
Some(FileDirectiveType::EofPdu)
);
assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver); assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver);
assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into()); assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into());
@@ -253,7 +259,7 @@ mod tests {
assert_eq!(written, eof_pdu.len_written()); assert_eq!(written, eof_pdu.len_written());
verify_raw_header(eof_pdu.pdu_header(), &buf); verify_raw_header(eof_pdu.pdu_header(), &buf);
let mut current_idx = eof_pdu.pdu_header().header_len(); let mut current_idx = eof_pdu.pdu_header().header_len();
buf[current_idx] = FileDirectiveType::EofPdu as u8; buf[current_idx] = FileDirectiveType::Eof as u8;
current_idx += 1; current_idx += 1;
assert_eq!( assert_eq!(
(buf[current_idx] >> 4) & 0b1111, (buf[current_idx] >> 4) & 0b1111,
+35 -14
View File
@@ -1,3 +1,4 @@
//! # File Data PDU packet implementation
use crate::cfdp::pdu::{ use crate::cfdp::pdu::{
add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field, add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
PduError, PduHeader, PduError, PduHeader,
@@ -10,18 +11,24 @@ use serde::{Deserialize, Serialize};
use super::{CfdpPdu, FileDirectiveType, WritablePduPacket}; use super::{CfdpPdu, FileDirectiveType, WritablePduPacket};
/// Record continuation state for segment metadata.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u2, exhaustive = true)] #[bitbybit::bitenum(u2, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum RecordContinuationState { pub enum RecordContinuationState {
/// No start and no end.
NoStartNoEnd = 0b00, NoStartNoEnd = 0b00,
/// Start without end.
StartWithoutEnd = 0b01, StartWithoutEnd = 0b01,
/// End without start.
EndWithoutStart = 0b10, EndWithoutStart = 0b10,
/// Start and end.
StartAndEnd = 0b11, StartAndEnd = 0b11,
} }
/// Segment metadata structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SegmentMetadata<'seg_meta> { pub struct SegmentMetadata<'seg_meta> {
@@ -30,6 +37,7 @@ pub struct SegmentMetadata<'seg_meta> {
} }
impl<'seg_meta> SegmentMetadata<'seg_meta> { impl<'seg_meta> SegmentMetadata<'seg_meta> {
/// Constructor.
pub fn new( pub fn new(
record_continuation_state: RecordContinuationState, record_continuation_state: RecordContinuationState,
metadata: Option<&'seg_meta [u8]>, metadata: Option<&'seg_meta [u8]>,
@@ -45,16 +53,19 @@ impl<'seg_meta> SegmentMetadata<'seg_meta> {
}) })
} }
/// Record continuation state.
#[inline] #[inline]
pub fn record_continuation_state(&self) -> RecordContinuationState { pub fn record_continuation_state(&self) -> RecordContinuationState {
self.record_continuation_state self.record_continuation_state
} }
/// Raw metadata slice.
#[inline] #[inline]
pub fn metadata(&self) -> Option<&'seg_meta [u8]> { pub fn metadata(&self) -> Option<&'seg_meta [u8]> {
self.metadata self.metadata
} }
/// Length of the written segment metadata structure.
#[inline] #[inline]
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
// Map empty metadata to 0 and slice to its length. // Map empty metadata to 0 and slice to its length.
@@ -124,8 +135,8 @@ impl FdPduBase<'_> {
if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large { if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
len += 4; len += 4;
} }
if self.segment_metadata.is_some() { if let Some(segment_metadata) = self.segment_metadata {
len += self.segment_metadata.as_ref().unwrap().len_written() len += segment_metadata.len_written()
} }
len += file_data_len as usize; len += file_data_len as usize;
if self.crc_flag() == CrcFlag::WithCrc { if self.crc_flag() == CrcFlag::WithCrc {
@@ -136,12 +147,8 @@ impl FdPduBase<'_> {
fn write_common_fields_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> { fn write_common_fields_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
if self.segment_metadata.is_some() { if let Some(segment_metadata) = self.segment_metadata {
current_idx += self current_idx += segment_metadata.write_to_bytes(&mut buf[current_idx..])?;
.segment_metadata
.as_ref()
.unwrap()
.write_to_bytes(&mut buf[current_idx..])?;
} }
current_idx += write_fss_field( current_idx += write_fss_field(
self.pdu_header.common_pdu_conf().file_flag, self.pdu_header.common_pdu_conf().file_flag,
@@ -169,24 +176,27 @@ pub struct FileDataPdu<'seg_meta, 'file_data> {
} }
impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
/// Constructor for a file data PDU including segment metadata.
pub fn new_with_seg_metadata( pub fn new_with_seg_metadata(
pdu_header: PduHeader, pdu_header: PduHeader,
segment_metadata: SegmentMetadata<'seg_meta>, segment_metadata: SegmentMetadata<'seg_meta>,
offset: u64, offset: u64,
file_data: &'file_data [u8], file_data: &'file_data [u8],
) -> Self { ) -> Self {
Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data) Self::new(pdu_header, Some(segment_metadata), offset, file_data)
} }
/// Constructor for a file data PDU without segment metadata.
pub fn new_no_seg_metadata( pub fn new_no_seg_metadata(
pdu_header: PduHeader, pdu_header: PduHeader,
offset: u64, offset: u64,
file_data: &'file_data [u8], file_data: &'file_data [u8],
) -> Self { ) -> Self {
Self::new_generic(pdu_header, None, offset, file_data) Self::new(pdu_header, None, offset, file_data)
} }
pub fn new_generic( /// Generic constructor for a file data PDU.
pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
segment_metadata: Option<SegmentMetadata<'seg_meta>>, segment_metadata: Option<SegmentMetadata<'seg_meta>>,
offset: u64, offset: u64,
@@ -213,26 +223,31 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
.calc_pdu_datafield_len(self.file_data.len() as u64) .calc_pdu_datafield_len(self.file_data.len() as u64)
} }
/// Optional segment metadata.
#[inline] #[inline]
pub fn segment_metadata(&self) -> Option<&SegmentMetadata<'_>> { pub fn segment_metadata(&self) -> Option<&SegmentMetadata<'_>> {
self.common.segment_metadata.as_ref() self.common.segment_metadata.as_ref()
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
self.common.pdu_header() self.common.pdu_header()
} }
/// File data offset.
#[inline] #[inline]
pub fn offset(&self) -> u64 { pub fn offset(&self) -> u64 {
self.common.offset self.common.offset
} }
/// File data.
#[inline] #[inline]
pub fn file_data(&self) -> &'file_data [u8] { pub fn file_data(&self) -> &'file_data [u8] {
self.file_data self.file_data
} }
/// Read [Self] from the provided buffer.
pub fn from_bytes<'buf: 'seg_meta + 'file_data>(buf: &'buf [u8]) -> Result<Self, PduError> { pub fn from_bytes<'buf: 'seg_meta + 'file_data>(buf: &'buf [u8]) -> Result<Self, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -281,6 +296,7 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.common.pdu_header.header_len() + self.calc_pdu_datafield_len() self.common.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -323,20 +339,23 @@ pub struct FileDataPduCreatorWithReservedDatafield<'seg_meta> {
} }
impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> { impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> {
/// Constructor for a file data PDU including segment metadata.
pub fn new_with_seg_metadata( pub fn new_with_seg_metadata(
pdu_header: PduHeader, pdu_header: PduHeader,
segment_metadata: SegmentMetadata<'seg_meta>, segment_metadata: SegmentMetadata<'seg_meta>,
offset: u64, offset: u64,
file_data_len: u64, file_data_len: u64,
) -> Self { ) -> Self {
Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data_len) Self::new(pdu_header, Some(segment_metadata), offset, file_data_len)
} }
/// Constructor for a file data PDU without segment metadata.
pub fn new_no_seg_metadata(pdu_header: PduHeader, offset: u64, file_data_len: u64) -> Self { pub fn new_no_seg_metadata(pdu_header: PduHeader, offset: u64, file_data_len: u64) -> Self {
Self::new_generic(pdu_header, None, offset, file_data_len) Self::new(pdu_header, None, offset, file_data_len)
} }
pub fn new_generic( /// Generic constructor.
pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
segment_metadata: Option<SegmentMetadata<'seg_meta>>, segment_metadata: Option<SegmentMetadata<'seg_meta>>,
offset: u64, offset: u64,
@@ -362,6 +381,7 @@ impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> {
self.common.calc_pdu_datafield_len(self.file_data_len) self.common.calc_pdu_datafield_len(self.file_data_len)
} }
/// Length of the written PDU.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.common.pdu_header.header_len() + self.calc_pdu_datafield_len() self.common.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -423,6 +443,7 @@ pub struct FileDataPduCreatorWithUnwrittenData<'buf> {
} }
impl FileDataPduCreatorWithUnwrittenData<'_> { impl FileDataPduCreatorWithUnwrittenData<'_> {
/// Mutable access to the file data field.
pub fn file_data_field_mut(&mut self) -> &mut [u8] { pub fn file_data_field_mut(&mut self) -> &mut [u8] {
&mut self.write_buf[self.file_data_offset as usize &mut self.write_buf[self.file_data_offset as usize
..self.file_data_offset as usize + self.file_data_len as usize] ..self.file_data_offset as usize + self.file_data_len as usize]
+35 -10
View File
@@ -1,3 +1,4 @@
//! # Finished PDU packet implementation.
use crate::cfdp::pdu::{ use crate::cfdp::pdu::{
add_pdu_crc, generic_length_checks_pdu_deserialization, FileDirectiveType, PduError, PduHeader, add_pdu_crc, generic_length_checks_pdu_deserialization, FileDirectiveType, PduError, PduHeader,
}; };
@@ -13,23 +14,31 @@ use serde::{Deserialize, Serialize};
use super::tlv::ReadableTlv; use super::tlv::ReadableTlv;
use super::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket}; use super::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket};
/// Delivery code enumeration.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum DeliveryCode { pub enum DeliveryCode {
/// Completed delivery.
Complete = 0, Complete = 0,
/// Incomplete delivery.
Incomplete = 1, Incomplete = 1,
} }
/// File status enumeration.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FileStatus { pub enum FileStatus {
/// File was discarded deliberately.
DiscardDeliberately = 0b00, DiscardDeliberately = 0b00,
/// File was rejected by the filestore.
DiscardedFsRejection = 0b01, DiscardedFsRejection = 0b01,
/// File was retained (but not necesarilly complete).
Retained = 0b10, Retained = 0b10,
/// Unreported file status.
Unreported = 0b11, Unreported = 0b11,
} }
@@ -65,6 +74,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
) )
} }
/// Constructor where the fault location is provided.
pub fn new_with_error( pub fn new_with_error(
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -82,6 +92,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
) )
} }
/// Generic constructor.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -109,32 +120,37 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
finished_pdu finished_pdu
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// Delivery code.
#[inline] #[inline]
pub fn delivery_code(&self) -> DeliveryCode { pub fn delivery_code(&self) -> DeliveryCode {
self.delivery_code self.delivery_code
} }
/// File status.
#[inline] #[inline]
pub fn file_status(&self) -> FileStatus { pub fn file_status(&self) -> FileStatus {
self.file_status self.file_status
} }
// If there are no filestore responses, an empty slice will be returned. /// Filestore responses as a slice.
#[inline] #[inline]
pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] { pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] {
self.fs_responses self.fs_responses
} }
/// Optional fault location [EntityIdTlv].
#[inline] #[inline]
pub fn fault_location(&self) -> Option<EntityIdTlv> { pub fn fault_location(&self) -> Option<EntityIdTlv> {
self.fault_location self.fault_location
@@ -166,7 +182,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::FinishedPdu as u8; buf[current_idx] = FileDirectiveType::Finished as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = ((self.condition_code as u8) << 4) buf[current_idx] = ((self.condition_code as u8) << 4)
| ((self.delivery_code as u8) << 2) | ((self.delivery_code as u8) << 2)
@@ -184,6 +200,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -197,7 +214,7 @@ impl CfdpPdu for FinishedPduCreator<'_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
} }
} }
@@ -242,6 +259,7 @@ impl<'buf> Iterator for FilestoreResponseIterator<'buf> {
} }
} }
/// Fnished PDU reader structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -269,13 +287,13 @@ impl<'buf> FinishedPduReader<'buf> {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::FinishedPdu), expected: Some(FileDirectiveType::Finished),
} }
})?; })?;
if directive_type != FileDirectiveType::FinishedPdu { if directive_type != FileDirectiveType::Finished {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::FinishedPdu, expected: FileDirectiveType::Finished,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -297,11 +315,13 @@ impl<'buf> FinishedPduReader<'buf> {
}) })
} }
/// Raw filestore responses.
#[inline] #[inline]
pub fn fs_responses_raw(&self) -> &[u8] { pub fn fs_responses_raw(&self) -> &[u8] {
self.fs_responses_raw self.fs_responses_raw
} }
/// Iterator over the filestore responses.
#[inline] #[inline]
pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> { pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> {
FilestoreResponseIterator { FilestoreResponseIterator {
@@ -310,26 +330,31 @@ impl<'buf> FinishedPduReader<'buf> {
} }
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// Delivery code.
#[inline] #[inline]
pub fn delivery_code(&self) -> DeliveryCode { pub fn delivery_code(&self) -> DeliveryCode {
self.delivery_code self.delivery_code
} }
/// File status.
#[inline] #[inline]
pub fn file_status(&self) -> FileStatus { pub fn file_status(&self) -> FileStatus {
self.file_status self.file_status
} }
/// Optional fault location [EntityIdTlv].
#[inline] #[inline]
pub fn fault_location(&self) -> Option<EntityIdTlv> { pub fn fault_location(&self) -> Option<EntityIdTlv> {
self.fault_location self.fault_location
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
@@ -399,7 +424,7 @@ impl CfdpPdu for FinishedPduReader<'_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
} }
} }
@@ -468,7 +493,7 @@ mod tests {
assert_eq!(finished_pdu.pdu_type(), PduType::FileDirective); assert_eq!(finished_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(
finished_pdu.file_directive_type(), finished_pdu.file_directive_type(),
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
); );
assert_eq!( assert_eq!(
finished_pdu.transmission_mode(), finished_pdu.transmission_mode(),
@@ -500,7 +525,7 @@ mod tests {
); );
verify_raw_header(finished_pdu.pdu_header(), &buf); verify_raw_header(finished_pdu.pdu_header(), &buf);
let mut current_idx = finished_pdu.pdu_header().header_len(); let mut current_idx = finished_pdu.pdu_header().header_len();
assert_eq!(buf[current_idx], FileDirectiveType::FinishedPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Finished as u8);
current_idx += 1; current_idx += 1;
assert_eq!( assert_eq!(
(buf[current_idx] >> 4) & 0b1111, (buf[current_idx] >> 4) & 0b1111,
@@ -626,7 +651,7 @@ mod tests {
assert_eq!(finished_pdu_vec.len(), 12); assert_eq!(finished_pdu_vec.len(), 12);
assert_eq!(finished_pdu_vec[9], TlvType::EntityId.into()); assert_eq!(finished_pdu_vec[9], TlvType::EntityId.into());
assert_eq!(finished_pdu_vec[10], 1); assert_eq!(finished_pdu_vec[10], 1);
assert_eq!(finished_pdu_vec[11], TEST_DEST_ID.value_typed()); assert_eq!(finished_pdu_vec[11], TEST_DEST_ID.value());
assert_eq!( assert_eq!(
finished_pdu.fault_location().unwrap().entity_id(), finished_pdu.fault_location().unwrap().entity_id(),
&TEST_DEST_ID.into() &TEST_DEST_ID.into()
+40 -15
View File
@@ -1,3 +1,4 @@
//! # Metadata PDU packet implementation.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use super::tlv::TlvOwned; use super::tlv::TlvOwned;
use crate::cfdp::lv::Lv; use crate::cfdp::lv::Lv;
@@ -16,16 +17,21 @@ use serde::{Deserialize, Serialize};
use super::tlv::ReadableTlv; use super::tlv::ReadableTlv;
use super::{CfdpPdu, WritablePduPacket}; use super::{CfdpPdu, WritablePduPacket};
/// Generic metadata parameters.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MetadataGenericParams { pub struct MetadataGenericParams {
/// Closure requested flag.
pub closure_requested: bool, pub closure_requested: bool,
/// Checksum type.
pub checksum_type: ChecksumType, pub checksum_type: ChecksumType,
/// File size.
pub file_size: u64, pub file_size: u64,
} }
impl MetadataGenericParams { impl MetadataGenericParams {
/// Constructor.
pub fn new(closure_requested: bool, checksum_type: ChecksumType, file_size: u64) -> Self { pub fn new(closure_requested: bool, checksum_type: ChecksumType, file_size: u64) -> Self {
Self { Self {
closure_requested, closure_requested,
@@ -35,6 +41,7 @@ impl MetadataGenericParams {
} }
} }
/// Build the metadata options from a slice of [Tlv]s
pub fn build_metadata_opts_from_slice( pub fn build_metadata_opts_from_slice(
buf: &mut [u8], buf: &mut [u8],
tlvs: &[Tlv], tlvs: &[Tlv],
@@ -46,6 +53,7 @@ pub fn build_metadata_opts_from_slice(
Ok(written) Ok(written)
} }
/// Build the metadata options from a vector of [Tlv]s
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn build_metadata_opts_from_vec( pub fn build_metadata_opts_from_vec(
buf: &mut [u8], buf: &mut [u8],
@@ -54,6 +62,7 @@ pub fn build_metadata_opts_from_vec(
build_metadata_opts_from_slice(buf, tlvs.as_slice()) build_metadata_opts_from_slice(buf, tlvs.as_slice())
} }
/// Build the metadata options from a slice of [TlvOwned]s
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn build_metadata_opts_from_owned_slice(tlvs: &[TlvOwned]) -> Vec<u8> { pub fn build_metadata_opts_from_owned_slice(tlvs: &[TlvOwned]) -> Vec<u8> {
let mut sum_vec = Vec::new(); let mut sum_vec = Vec::new();
@@ -77,6 +86,7 @@ pub struct MetadataPduCreator<'src_name, 'dest_name, 'opts> {
} }
impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'opts> { impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'opts> {
/// Constructor for a metadata PDU without options.
pub fn new_no_opts( pub fn new_no_opts(
pdu_header: PduHeader, pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,
@@ -92,6 +102,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
) )
} }
/// Constructor for a metadata PDU with options.
pub fn new_with_opts( pub fn new_with_opts(
pdu_header: PduHeader, pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,
@@ -108,6 +119,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
) )
} }
/// Generic constructor for a metadata PDU.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,
@@ -128,26 +140,31 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
pdu pdu
} }
/// Metadata generic parameters.
#[inline] #[inline]
pub fn metadata_params(&self) -> &MetadataGenericParams { pub fn metadata_params(&self) -> &MetadataGenericParams {
&self.metadata_params &self.metadata_params
} }
/// Source file name as a [Lv].
#[inline] #[inline]
pub fn src_file_name(&self) -> Lv<'src_name> { pub fn src_file_name(&self) -> Lv<'src_name> {
self.src_file_name self.src_file_name
} }
/// Destination file name as a [Lv].
#[inline] #[inline]
pub fn dest_file_name(&self) -> Lv<'dest_name> { pub fn dest_file_name(&self) -> Lv<'dest_name> {
self.dest_file_name self.dest_file_name
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Raw options.
#[inline] #[inline]
pub fn options(&self) -> &'opts [u8] { pub fn options(&self) -> &'opts [u8] {
self.options self.options
@@ -191,7 +208,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::MetadataPdu as u8; buf[current_idx] = FileDirectiveType::Metadata as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 6) buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 6)
| (self.metadata_params.checksum_type as u8); | (self.metadata_params.checksum_type as u8);
@@ -215,6 +232,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -228,7 +246,7 @@ impl CfdpPdu for MetadataPduCreator<'_, '_, '_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::MetadataPdu) Some(FileDirectiveType::Metadata)
} }
} }
@@ -291,10 +309,12 @@ pub struct MetadataPduReader<'buf> {
} }
impl<'raw> MetadataPduReader<'raw> { impl<'raw> MetadataPduReader<'raw> {
/// Constructor from raw bytes.
pub fn new(buf: &'raw [u8]) -> Result<Self, PduError> { pub fn new(buf: &'raw [u8]) -> Result<Self, PduError> {
Self::from_bytes(buf) Self::from_bytes(buf)
} }
/// Constructor from raw bytes.
pub fn from_bytes(buf: &'raw [u8]) -> Result<Self, PduError> { pub fn from_bytes(buf: &'raw [u8]) -> Result<Self, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -308,13 +328,13 @@ impl<'raw> MetadataPduReader<'raw> {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::MetadataPdu), expected: Some(FileDirectiveType::Metadata),
} }
})?; })?;
if directive_type != FileDirectiveType::MetadataPdu { if directive_type != FileDirectiveType::Metadata {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::MetadataPdu, expected: FileDirectiveType::Metadata,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -350,26 +370,31 @@ impl<'raw> MetadataPduReader<'raw> {
}) })
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Raw options.
#[inline] #[inline]
pub fn options(&self) -> &'raw [u8] { pub fn options(&self) -> &'raw [u8] {
self.options self.options
} }
/// Generic metadata parameters.
#[inline] #[inline]
pub fn metadata_params(&self) -> &MetadataGenericParams { pub fn metadata_params(&self) -> &MetadataGenericParams {
&self.metadata_params &self.metadata_params
} }
/// Source file name as a [Lv].
#[inline] #[inline]
pub fn src_file_name(&self) -> Lv<'_> { pub fn src_file_name(&self) -> Lv<'_> {
self.src_file_name self.src_file_name
} }
/// Destination file name as a [Lv].
#[inline] #[inline]
pub fn dest_file_name(&self) -> Lv<'_> { pub fn dest_file_name(&self) -> Lv<'_> {
self.dest_file_name self.dest_file_name
@@ -383,12 +408,12 @@ impl CfdpPdu for MetadataPduReader<'_> {
} }
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::MetadataPdu) Some(FileDirectiveType::Metadata)
} }
} }
#[cfg(test)] #[cfg(test)]
pub mod tests { mod tests {
use alloc::string::ToString; use alloc::string::ToString;
use crate::cfdp::lv::Lv; use crate::cfdp::lv::Lv;
@@ -471,7 +496,7 @@ pub mod tests {
); );
assert_eq!( assert_eq!(
metadata_pdu.file_directive_type(), metadata_pdu.file_directive_type(),
Some(FileDirectiveType::MetadataPdu) Some(FileDirectiveType::Metadata)
); );
assert_eq!( assert_eq!(
metadata_pdu.transmission_mode(), metadata_pdu.transmission_mode(),
@@ -502,7 +527,7 @@ pub mod tests {
+ expected_src_filename.len_full() + expected_src_filename.len_full()
+ expected_dest_filename.len_full() + expected_dest_filename.len_full()
); );
assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8); assert_eq!(buf[7], FileDirectiveType::Metadata as u8);
assert_eq!(buf[8] >> 6, closure_requested as u8); assert_eq!(buf[8] >> 6, closure_requested as u8);
assert_eq!(buf[8] & 0b1111, checksum_type as u8); assert_eq!(buf[8] & 0b1111, checksum_type as u8);
assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010); assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010);
@@ -807,10 +832,10 @@ pub mod tests {
let error = metadata_error.unwrap_err(); let error = metadata_error.unwrap_err();
if let PduError::InvalidDirectiveType { found, expected } = error { if let PduError::InvalidDirectiveType { found, expected } = error {
assert_eq!(found, 0xff); assert_eq!(found, 0xff);
assert_eq!(expected, Some(FileDirectiveType::MetadataPdu)); assert_eq!(expected, Some(FileDirectiveType::Metadata));
assert_eq!( assert_eq!(
error.to_string(), error.to_string(),
"invalid directive type, found 255, expected Some(MetadataPdu)" "invalid directive type, found 255, expected Some(Metadata)"
); );
} else { } else {
panic!("Expected InvalidDirectiveType error, got {:?}", error); panic!("Expected InvalidDirectiveType error, got {:?}", error);
@@ -827,16 +852,16 @@ pub mod tests {
&[], &[],
); );
let mut metadata_vec = metadata_pdu.to_vec().unwrap(); let mut metadata_vec = metadata_pdu.to_vec().unwrap();
metadata_vec[7] = FileDirectiveType::EofPdu as u8; metadata_vec[7] = FileDirectiveType::Eof as u8;
let metadata_error = MetadataPduReader::from_bytes(&metadata_vec); let metadata_error = MetadataPduReader::from_bytes(&metadata_vec);
assert!(metadata_error.is_err()); assert!(metadata_error.is_err());
let error = metadata_error.unwrap_err(); let error = metadata_error.unwrap_err();
if let PduError::WrongDirectiveType { found, expected } = error { if let PduError::WrongDirectiveType { found, expected } = error {
assert_eq!(found, FileDirectiveType::EofPdu); assert_eq!(found, FileDirectiveType::Eof);
assert_eq!(expected, FileDirectiveType::MetadataPdu); assert_eq!(expected, FileDirectiveType::Metadata);
assert_eq!( assert_eq!(
error.to_string(), error.to_string(),
"wrong directive type, found EofPdu, expected MetadataPdu" "wrong directive type, found Eof, expected Metadata"
); );
} else { } else {
panic!("Expected InvalidDirectiveType error, got {:?}", error); panic!("Expected InvalidDirectiveType error, got {:?}", error);
+65 -7
View File
@@ -15,24 +15,34 @@ pub mod finished;
pub mod metadata; pub mod metadata;
pub mod nak; pub mod nak;
/// File directive type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FileDirectiveType { pub enum FileDirectiveType {
EofPdu = 0x04, /// EOF.
FinishedPdu = 0x05, Eof = 0x04,
AckPdu = 0x06, /// Finished.
MetadataPdu = 0x07, Finished = 0x05,
NakPdu = 0x08, /// ACK.
PromptPdu = 0x09, Ack = 0x06,
KeepAlivePdu = 0x0c, /// Metadata.
Metadata = 0x07,
/// NAK.
Nak = 0x08,
/// Prompt.
Prompt = 0x09,
/// Keep Alive.
KeepAlive = 0x0c,
} }
/// PDU error.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PduError { pub enum PduError {
/// Byte conversion error.
#[error("byte conversion error: {0}")] #[error("byte conversion error: {0}")]
ByteConversion(#[from] ByteConversionError), ByteConversion(#[from] ByteConversionError),
/// Found version ID invalid, not equal to [super::CFDP_VERSION_2]. /// Found version ID invalid, not equal to [super::CFDP_VERSION_2].
@@ -44,27 +54,35 @@ pub enum PduError {
/// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported. /// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
#[error("invalid transaction ID length {0}")] #[error("invalid transaction ID length {0}")]
InvalidTransactionSeqNumLen(u8), InvalidTransactionSeqNumLen(u8),
/// Source and destination entity ID lengths do not match.
#[error( #[error(
"missmatch of PDU source ID length {src_id_len} and destination ID length {dest_id_len}" "missmatch of PDU source ID length {src_id_len} and destination ID length {dest_id_len}"
)] )]
SourceDestIdLenMissmatch { SourceDestIdLenMissmatch {
/// Source ID length.
src_id_len: usize, src_id_len: usize,
/// Destination ID length.
dest_id_len: usize, dest_id_len: usize,
}, },
/// Wrong directive type, for example when parsing the directive field for a file directive /// Wrong directive type, for example when parsing the directive field for a file directive
/// PDU. /// PDU.
#[error("wrong directive type, found {found:?}, expected {expected:?}")] #[error("wrong directive type, found {found:?}, expected {expected:?}")]
WrongDirectiveType { WrongDirectiveType {
/// Found directive type.
found: FileDirectiveType, found: FileDirectiveType,
/// Expected directive type.
expected: FileDirectiveType, expected: FileDirectiveType,
}, },
/// The directive type field contained a value not in the range of permitted values. This can /// The directive type field contained a value not in the range of permitted values. This can
/// also happen if an invalid value is passed to the ACK PDU reader. /// also happen if an invalid value is passed to the ACK PDU reader.
#[error("invalid directive type, found {found:?}, expected {expected:?}")] #[error("invalid directive type, found {found:?}, expected {expected:?}")]
InvalidDirectiveType { InvalidDirectiveType {
/// Found raw directive type.
found: u8, found: u8,
/// Expected raw directive type if applicable.
expected: Option<FileDirectiveType>, expected: Option<FileDirectiveType>,
}, },
/// Invalid start or end of scope for a NAK PDU.
#[error("nak pdu: {0}")] #[error("nak pdu: {0}")]
InvalidStartOrEndOfScope(#[from] InvalidStartOrEndOfScopeError), InvalidStartOrEndOfScope(#[from] InvalidStartOrEndOfScopeError),
/// Invalid condition code. Contains the raw detected value. /// Invalid condition code. Contains the raw detected value.
@@ -74,6 +92,7 @@ pub enum PduError {
/// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/). /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/).
#[error("invalid checksum type {0}")] #[error("invalid checksum type {0}")]
InvalidChecksumType(u8), InvalidChecksumType(u8),
/// File size is too large.
#[error("file size {0} too large")] #[error("file size {0} too large")]
FileSizeTooLarge(u64), FileSizeTooLarge(u64),
/// If the CRC flag for a PDU is enabled and the checksum check fails. Contains raw 16-bit CRC. /// If the CRC flag for a PDU is enabled and the checksum check fails. Contains raw 16-bit CRC.
@@ -96,9 +115,15 @@ impl From<InvalidAckedDirectiveCodeError> for PduError {
} }
} }
/// Generic trait for a PDU which can be written to bytes.
pub trait WritablePduPacket { pub trait WritablePduPacket {
/// Length when written to bytes.
fn len_written(&self) -> usize; fn len_written(&self) -> usize;
/// Write the PDU to a raw buffer, returning the written length.
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError>; fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError>;
/// Convert the PDU to an owned vector of bytes.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<Vec<u8>, PduError> { fn to_vec(&self) -> Result<Vec<u8>, PduError> {
// This is the correct way to do this. See // This is the correct way to do this. See
@@ -112,48 +137,58 @@ pub trait WritablePduPacket {
/// Abstraction trait for fields and properties common for all PDUs. /// Abstraction trait for fields and properties common for all PDUs.
pub trait CfdpPdu { pub trait CfdpPdu {
/// PDU header.
fn pdu_header(&self) -> &PduHeader; fn pdu_header(&self) -> &PduHeader;
/// Source ID (file sender).
#[inline] #[inline]
fn source_id(&self) -> UnsignedByteField { fn source_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().source_entity_id self.pdu_header().common_pdu_conf().source_entity_id
} }
/// Destination ID (file sender).
#[inline] #[inline]
fn dest_id(&self) -> UnsignedByteField { fn dest_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().dest_entity_id self.pdu_header().common_pdu_conf().dest_entity_id
} }
/// Transaction sequence number.
#[inline] #[inline]
fn transaction_seq_num(&self) -> UnsignedByteField { fn transaction_seq_num(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().transaction_seq_num self.pdu_header().common_pdu_conf().transaction_seq_num
} }
/// Transmission mode.
#[inline] #[inline]
fn transmission_mode(&self) -> TransmissionMode { fn transmission_mode(&self) -> TransmissionMode {
self.pdu_header().common_pdu_conf().trans_mode self.pdu_header().common_pdu_conf().trans_mode
} }
/// Direction.
#[inline] #[inline]
fn direction(&self) -> Direction { fn direction(&self) -> Direction {
self.pdu_header().common_pdu_conf().direction self.pdu_header().common_pdu_conf().direction
} }
/// CRC flag.
#[inline] #[inline]
fn crc_flag(&self) -> CrcFlag { fn crc_flag(&self) -> CrcFlag {
self.pdu_header().common_pdu_conf().crc_flag self.pdu_header().common_pdu_conf().crc_flag
} }
/// File flag.
#[inline] #[inline]
fn file_flag(&self) -> LargeFileFlag { fn file_flag(&self) -> LargeFileFlag {
self.pdu_header().common_pdu_conf().file_flag self.pdu_header().common_pdu_conf().file_flag
} }
/// PDU type.
#[inline] #[inline]
fn pdu_type(&self) -> PduType { fn pdu_type(&self) -> PduType {
self.pdu_header().pdu_type() self.pdu_header().pdu_type()
} }
/// File directive type when applicable.
fn file_directive_type(&self) -> Option<FileDirectiveType>; fn file_directive_type(&self) -> Option<FileDirectiveType>;
} }
@@ -169,15 +204,21 @@ pub trait CfdpPdu {
pub struct CommonPduConfig { pub struct CommonPduConfig {
source_entity_id: UnsignedByteField, source_entity_id: UnsignedByteField,
dest_entity_id: UnsignedByteField, dest_entity_id: UnsignedByteField,
/// Transaction sequence number.
pub transaction_seq_num: UnsignedByteField, pub transaction_seq_num: UnsignedByteField,
/// Transmission mode.
pub trans_mode: TransmissionMode, pub trans_mode: TransmissionMode,
/// File flag.
pub file_flag: LargeFileFlag, pub file_flag: LargeFileFlag,
/// CRC flag.
pub crc_flag: CrcFlag, pub crc_flag: CrcFlag,
/// Direction.
pub direction: Direction, pub direction: Direction,
} }
// TODO: Builder pattern might be applicable here.. // TODO: Builder pattern might be applicable here..
impl CommonPduConfig { impl CommonPduConfig {
/// Generic constructor.
#[inline] #[inline]
pub fn new( pub fn new(
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
@@ -210,6 +251,7 @@ impl CommonPduConfig {
}) })
} }
/// Constructor for custom byte field with default field values for the other fields.
#[inline] #[inline]
pub fn new_with_byte_fields( pub fn new_with_byte_fields(
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
@@ -227,6 +269,7 @@ impl CommonPduConfig {
) )
} }
/// Source ID (file sender).
#[inline] #[inline]
pub fn source_id(&self) -> UnsignedByteField { pub fn source_id(&self) -> UnsignedByteField {
self.source_entity_id self.source_entity_id
@@ -255,6 +298,7 @@ impl CommonPduConfig {
Ok((source_id, dest_id)) Ok((source_id, dest_id))
} }
/// Set the source and destination ID field.
#[inline] #[inline]
pub fn set_source_and_dest_id( pub fn set_source_and_dest_id(
&mut self, &mut self,
@@ -267,6 +311,7 @@ impl CommonPduConfig {
Ok(()) Ok(())
} }
/// Destination ID (file receiver).
#[inline] #[inline]
pub fn dest_id(&self) -> UnsignedByteField { pub fn dest_id(&self) -> UnsignedByteField {
self.dest_entity_id self.dest_entity_id
@@ -305,6 +350,7 @@ impl PartialEq for CommonPduConfig {
} }
} }
/// Fixed header length of the PDU header.
pub const FIXED_HEADER_LEN: usize = 4; pub const FIXED_HEADER_LEN: usize = 4;
/// Abstraction for the PDU header common to all CFDP PDUs. /// Abstraction for the PDU header common to all CFDP PDUs.
@@ -322,8 +368,10 @@ pub struct PduHeader {
} }
impl PduHeader { impl PduHeader {
/// Fixed length of the PDU header when written to a raw buffer.
pub const FIXED_LEN: usize = FIXED_HEADER_LEN; pub const FIXED_LEN: usize = FIXED_HEADER_LEN;
/// Constructor for a File Data PDU header.
#[inline] #[inline]
pub fn new_for_file_data( pub fn new_for_file_data(
pdu_conf: CommonPduConfig, pdu_conf: CommonPduConfig,
@@ -340,6 +388,7 @@ impl PduHeader {
) )
} }
/// Constructor for a file data PDU.
#[inline] #[inline]
pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic( Self::new_generic(
@@ -351,6 +400,7 @@ impl PduHeader {
) )
} }
/// Constructor for a file directive PDU.
#[inline] #[inline]
pub fn new_for_file_directive(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { pub fn new_for_file_directive(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic( Self::new_generic(
@@ -362,6 +412,7 @@ impl PduHeader {
) )
} }
/// Constructor from a given [CommonPduConfig] and for a file directive PDU.
#[inline] #[inline]
pub fn from_pdu_conf_for_file_directive(pdu_conf: CommonPduConfig) -> Self { pub fn from_pdu_conf_for_file_directive(pdu_conf: CommonPduConfig) -> Self {
Self::new_generic( Self::new_generic(
@@ -373,6 +424,7 @@ impl PduHeader {
) )
} }
/// Generic constructor.
#[inline] #[inline]
pub fn new_generic( pub fn new_generic(
pdu_type: PduType, pdu_type: PduType,
@@ -399,6 +451,7 @@ impl PduHeader {
+ self.pdu_conf.dest_entity_id.size() + self.pdu_conf.dest_entity_id.size()
} }
/// PDU data field length.
#[inline] #[inline]
pub fn pdu_datafield_len(&self) -> usize { pub fn pdu_datafield_len(&self) -> usize {
self.pdu_datafield_len.into() self.pdu_datafield_len.into()
@@ -411,6 +464,7 @@ impl PduHeader {
self.header_len() + self.pdu_datafield_len as usize self.header_len() + self.pdu_datafield_len as usize
} }
/// Write the header to a raw buffer, returning the written length on success.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
// The API does not allow passing entity IDs with different sizes, so this should // The API does not allow passing entity IDs with different sizes, so this should
// never happen. // never happen.
@@ -578,21 +632,25 @@ impl PduHeader {
)) ))
} }
/// PDU type.
#[inline] #[inline]
pub fn pdu_type(&self) -> PduType { pub fn pdu_type(&self) -> PduType {
self.pdu_type self.pdu_type
} }
/// Common PDU configuration fields.
#[inline] #[inline]
pub fn common_pdu_conf(&self) -> &CommonPduConfig { pub fn common_pdu_conf(&self) -> &CommonPduConfig {
&self.pdu_conf &self.pdu_conf
} }
/// Segment metadata flag.
#[inline] #[inline]
pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag { pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag {
self.seg_metadata_flag self.seg_metadata_flag
} }
/// Segmentation Control.
#[inline] #[inline]
pub fn seg_ctrl(&self) -> SegmentationControl { pub fn seg_ctrl(&self) -> SegmentationControl {
self.seg_ctrl self.seg_ctrl
+40 -19
View File
@@ -1,3 +1,4 @@
//! # NAK PDU packet implementation.
use crate::{ use crate::{
cfdp::{CrcFlag, Direction, LargeFileFlag}, cfdp::{CrcFlag, Direction, LargeFileFlag},
ByteConversionError, ByteConversionError,
@@ -8,6 +9,7 @@ use super::{
PduHeader, WritablePduPacket, PduHeader, WritablePduPacket,
}; };
/// Invalid start or end of scope value.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[error("invalid start or end of scope value for NAK PDU")] #[error("invalid start or end of scope value for NAK PDU")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -52,6 +54,7 @@ fn write_start_and_end_of_scope(
current_index current_index
} }
/// Buffer is too small to even hold a PDU header.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[error("packet buffer too small for PDU header")] #[error("packet buffer too small for PDU header")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -94,11 +97,14 @@ pub fn calculate_max_segment_requests(
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SegmentRequests<'a> { pub enum SegmentRequests<'a> {
/// Segment requests for normal file sizes bounded by [u32::MAX].
U32Pairs(&'a [(u32, u32)]), U32Pairs(&'a [(u32, u32)]),
/// Segment requests for large file sizes bounded by [u64::MAX].
U64Pairs(&'a [(u64, u64)]), U64Pairs(&'a [(u64, u64)]),
} }
impl SegmentRequests<'_> { impl SegmentRequests<'_> {
/// Wrapped segment requests are empty.
#[inline] #[inline]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
match self { match self {
@@ -152,6 +158,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
) )
} }
/// Constructor for large file sizes.
pub fn new_large_file_size( pub fn new_large_file_size(
pdu_header: PduHeader, pdu_header: PduHeader,
start_of_scope: u64, start_of_scope: u64,
@@ -206,21 +213,25 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
Ok(nak_pdu) Ok(nak_pdu)
} }
/// Start of scope of the NAK PDU.
#[inline] #[inline]
pub fn start_of_scope(&self) -> u64 { pub fn start_of_scope(&self) -> u64 {
self.start_of_scope self.start_of_scope
} }
/// End of scope of the NAK PDU.
#[inline] #[inline]
pub fn end_of_scope(&self) -> u64 { pub fn end_of_scope(&self) -> u64 {
self.end_of_scope self.end_of_scope
} }
/// Semgent requests.
#[inline] #[inline]
pub fn segment_requests(&self) -> Option<&SegmentRequests<'_>> { pub fn segment_requests(&self) -> Option<&SegmentRequests<'_>> {
self.segment_requests.as_ref() self.segment_requests.as_ref()
} }
/// Number of segment requests.
#[inline] #[inline]
pub fn num_segment_reqs(&self) -> usize { pub fn num_segment_reqs(&self) -> usize {
match &self.segment_requests { match &self.segment_requests {
@@ -232,6 +243,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
} }
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
@@ -252,7 +264,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
.into()); .into());
} }
let mut current_index = self.pdu_header.write_to_bytes(buf)?; let mut current_index = self.pdu_header.write_to_bytes(buf)?;
buf[current_index] = FileDirectiveType::NakPdu as u8; buf[current_index] = FileDirectiveType::Nak as u8;
current_index += 1; current_index += 1;
current_index = write_start_and_end_of_scope( current_index = write_start_and_end_of_scope(
@@ -309,7 +321,7 @@ impl CfdpPdu for NakPduCreator<'_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::NakPdu) Some(FileDirectiveType::Nak)
} }
} }
@@ -337,6 +349,8 @@ pub struct NakPduCreatorWithReservedSeqReqsBuf<'buf> {
} }
impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> { impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
/// Calculartes the maximum amount of segment requests which fit into a packet with the
/// provided parameters.
pub fn calculate_max_segment_requests( pub fn calculate_max_segment_requests(
max_packet_size: usize, max_packet_size: usize,
pdu_header: &PduHeader, pdu_header: &PduHeader,
@@ -344,6 +358,7 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
calculate_max_segment_requests(max_packet_size, pdu_header) calculate_max_segment_requests(max_packet_size, pdu_header)
} }
/// Constructor.
pub fn new( pub fn new(
buf: &'buf mut [u8], buf: &'buf mut [u8],
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
@@ -371,11 +386,13 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
} }
impl NakPduCreatorWithReservedSeqReqsBuf<'_> { impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Number of segment requests.
#[inline] #[inline]
pub fn num_segment_reqs(&self) -> usize { pub fn num_segment_reqs(&self) -> usize {
self.num_segment_reqs self.num_segment_reqs
@@ -407,6 +424,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
Ok(()) Ok(())
} }
/// Length when written to a buffer.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -427,6 +445,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
&self.buf[segment_req_buf_offset..segment_req_buf_offset + len] &self.buf[segment_req_buf_offset..segment_req_buf_offset + len]
} }
/// Iterator over all segment requests.
#[inline] #[inline]
pub fn segment_request_iter(&self) -> SegmentRequestIter<'_> { pub fn segment_request_iter(&self) -> SegmentRequestIter<'_> {
SegmentRequestIter::new( SegmentRequestIter::new(
@@ -447,7 +466,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
) -> Result<usize, InvalidStartOrEndOfScopeError> { ) -> Result<usize, InvalidStartOrEndOfScopeError> {
self.set_start_and_end_of_scope(start_of_scope, end_of_scope)?; self.set_start_and_end_of_scope(start_of_scope, end_of_scope)?;
let mut current_idx = self.pdu_header.write_to_bytes(self.buf).unwrap(); let mut current_idx = self.pdu_header.write_to_bytes(self.buf).unwrap();
self.buf[current_idx] = FileDirectiveType::NakPdu as u8; self.buf[current_idx] = FileDirectiveType::Nak as u8;
current_idx += 1; current_idx += 1;
if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large { if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
@@ -613,15 +632,17 @@ impl CfdpPdu for NakPduReader<'_> {
} }
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::NakPdu) Some(FileDirectiveType::Nak)
} }
} }
impl<'seg_reqs> NakPduReader<'seg_reqs> { impl<'seg_reqs> NakPduReader<'seg_reqs> {
/// Constructor from a raw bytestream.
pub fn new(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> { pub fn new(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> {
Self::from_bytes(buf) Self::from_bytes(buf)
} }
/// Constructor from a raw bytestream.
pub fn from_bytes(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> { pub fn from_bytes(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -631,13 +652,13 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::NakPdu), expected: Some(FileDirectiveType::Nak),
} }
})?; })?;
if directive_type != FileDirectiveType::NakPdu { if directive_type != FileDirectiveType::Nak {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::AckPdu, expected: FileDirectiveType::Ack,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -682,14 +703,17 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
}) })
} }
/// Start of scope.
pub fn start_of_scope(&self) -> u64 { pub fn start_of_scope(&self) -> u64 {
self.start_of_scope self.start_of_scope
} }
/// End of scope.
pub fn end_of_scope(&self) -> u64 { pub fn end_of_scope(&self) -> u64 {
self.end_of_scope self.end_of_scope
} }
/// Number of segment requests.
pub fn num_segment_reqs(&self) -> usize { pub fn num_segment_reqs(&self) -> usize {
if self.seg_reqs_raw.is_empty() { if self.seg_reqs_raw.is_empty() {
return 0; return 0;
@@ -747,10 +771,7 @@ mod tests {
assert_eq!(nak_pdu.crc_flag(), CrcFlag::NoCrc); assert_eq!(nak_pdu.crc_flag(), CrcFlag::NoCrc);
assert_eq!(nak_pdu.file_flag(), LargeFileFlag::Normal); assert_eq!(nak_pdu.file_flag(), LargeFileFlag::Normal);
assert_eq!(nak_pdu.pdu_type(), PduType::FileDirective); assert_eq!(nak_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(nak_pdu.file_directive_type(), Some(FileDirectiveType::Nak),);
nak_pdu.file_directive_type(),
Some(FileDirectiveType::NakPdu),
);
assert_eq!(nak_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(nak_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(nak_pdu.direction(), Direction::TowardsSender); assert_eq!(nak_pdu.direction(), Direction::TowardsSender);
assert_eq!(nak_pdu.source_id(), TEST_SRC_ID.into()); assert_eq!(nak_pdu.source_id(), TEST_SRC_ID.into());
@@ -794,7 +815,7 @@ mod tests {
verify_raw_header(nak_pdu.pdu_header(), &buf); verify_raw_header(nak_pdu.pdu_header(), &buf);
let mut current_idx = nak_pdu.pdu_header().header_len(); let mut current_idx = nak_pdu.pdu_header().header_len();
assert_eq!(current_idx + 9, nak_pdu.len_written()); assert_eq!(current_idx + 9, nak_pdu.len_written());
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -821,7 +842,7 @@ mod tests {
verify_raw_header(nak_pdu.pdu_header(), &buf); verify_raw_header(nak_pdu.pdu_header(), &buf);
let mut current_idx = nak_pdu.pdu_header().header_len(); let mut current_idx = nak_pdu.pdu_header().header_len();
assert_eq!(current_idx + 9 + 16, nak_pdu.len_written()); assert_eq!(current_idx + 9 + 16, nak_pdu.len_written());
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1034,7 +1055,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 9, len_written); assert_eq!(current_idx + 9, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1060,7 +1081,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 8 + 2, len_written); assert_eq!(current_idx + 1 + 8 + 2, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1087,7 +1108,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 16, len_written); assert_eq!(current_idx + 1 + 16, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());
@@ -1131,7 +1152,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 16, len_written); assert_eq!(current_idx + 1 + 16, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());
@@ -1171,7 +1192,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 8 + num_segments * 8, len_written); assert_eq!(current_idx + 1 + 8 + num_segments * 8, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1227,7 +1248,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 16 + num_segments * 16, len_written); assert_eq!(current_idx + 1 + 16 + num_segments * 16, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());
+72
View File
@@ -19,9 +19,12 @@ use super::{InvalidTlvTypeFieldError, TlvLvDataTooLargeError};
pub mod msg_to_user; pub mod msg_to_user;
/// Minimum length of a type-length-value structure, including type and length fields.
pub const MIN_TLV_LEN: usize = 2; pub const MIN_TLV_LEN: usize = 2;
/// Trait for generic TLV structures.
pub trait GenericTlv { pub trait GenericTlv {
/// TLV type field.
fn tlv_type_field(&self) -> TlvTypeField; fn tlv_type_field(&self) -> TlvTypeField;
/// Checks whether the type field contains one of the standard types specified in the CFDP /// Checks whether the type field contains one of the standard types specified in the CFDP
@@ -45,7 +48,9 @@ pub trait GenericTlv {
} }
} }
/// Readable TLV structure trait.
pub trait ReadableTlv { pub trait ReadableTlv {
/// Value field of the TLV.
fn value(&self) -> &[u8]; fn value(&self) -> &[u8];
/// Checks whether the value field is empty. /// Checks whether the value field is empty.
@@ -68,9 +73,15 @@ pub trait ReadableTlv {
} }
} }
/// Writable TLV structure trait.
pub trait WritableTlv { pub trait WritableTlv {
/// Write the TLV to bytes.
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>; fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
/// Length of the written TLV.
fn len_written(&self) -> usize; fn len_written(&self) -> usize;
/// Convenience method to write the TLV to an owned [alloc::vec::Vec].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn to_vec(&self) -> Vec<u8> { fn to_vec(&self) -> Vec<u8> {
let mut buf = vec![0; self.len_written()]; let mut buf = vec![0; self.len_written()];
@@ -79,34 +90,50 @@ pub trait WritableTlv {
} }
} }
/// TLV type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum TlvType { pub enum TlvType {
/// Filestore request.
FilestoreRequest = 0x00, FilestoreRequest = 0x00,
/// Filestore response.
FilestoreResponse = 0x01, FilestoreResponse = 0x01,
/// Message to user.
MsgToUser = 0x02, MsgToUser = 0x02,
/// Fault handler.
FaultHandler = 0x04, FaultHandler = 0x04,
/// Flow label.
FlowLabel = 0x05, FlowLabel = 0x05,
/// Entity ID.
EntityId = 0x06, EntityId = 0x06,
} }
/// TLV type field variants.
///
/// This allows specifying custom variants as well.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TlvTypeField { pub enum TlvTypeField {
/// Standard TLV types.
Standard(TlvType), Standard(TlvType),
/// Custom TLV type.
Custom(u8), Custom(u8),
} }
/// Filestore action codes as specified in the standard.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FilestoreActionCode { pub enum FilestoreActionCode {
/// Create file.
CreateFile = 0b0000, CreateFile = 0b0000,
/// Delete file.
DeleteFile = 0b0001, DeleteFile = 0b0001,
/// Rename file.
RenameFile = 0b0010, RenameFile = 0b0010,
/// This operation appends one file to another. The first specified name will form the first /// This operation appends one file to another. The first specified name will form the first
/// part of the new file and the name of the new file. This function can be used to get /// part of the new file and the name of the new file. This function can be used to get
@@ -115,9 +142,13 @@ pub enum FilestoreActionCode {
/// This operation replaces the content of the first specified file with the content of /// This operation replaces the content of the first specified file with the content of
/// the secondly specified file. /// the secondly specified file.
ReplaceFile = 0b0100, ReplaceFile = 0b0100,
/// Create directory.
CreateDirectory = 0b0101, CreateDirectory = 0b0101,
/// Remove directory.
RemoveDirectory = 0b0110, RemoveDirectory = 0b0110,
/// Deny file.
DenyFile = 0b0111, DenyFile = 0b0111,
/// Deny directory.
DenyDirectory = 0b1000, DenyDirectory = 0b1000,
} }
@@ -158,8 +189,10 @@ pub struct Tlv<'data> {
} }
impl<'data> Tlv<'data> { impl<'data> Tlv<'data> {
/// Minimum length of a TLV structure, including type and length fields.
pub const MIN_LEN: usize = MIN_TLV_LEN; pub const MIN_LEN: usize = MIN_TLV_LEN;
/// Generic constructor for a TLV structure.
pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv<'_>, TlvLvDataTooLargeError> { pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv<'_>, TlvLvDataTooLargeError> {
Ok(Tlv { Ok(Tlv {
tlv_type_field: TlvTypeField::Standard(tlv_type), tlv_type_field: TlvTypeField::Standard(tlv_type),
@@ -167,6 +200,7 @@ impl<'data> Tlv<'data> {
}) })
} }
/// Constructor for a TLV with a custom type field.
pub fn new_with_custom_type( pub fn new_with_custom_type(
tlv_type: u8, tlv_type: u8,
data: &[u8], data: &[u8],
@@ -208,6 +242,7 @@ impl<'data> Tlv<'data> {
self.lv.raw_data() self.lv.raw_data()
} }
/// Converts to an owned TLV variant, allocating memory for the value field.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_owned(&self) -> TlvOwned { pub fn to_owned(&self) -> TlvOwned {
TlvOwned { TlvOwned {
@@ -251,6 +286,7 @@ impl GenericTlv for Tlv<'_> {
} }
} }
/// Component of the TLV module which require [alloc] support.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod alloc_mod { pub mod alloc_mod {
use super::*; use super::*;
@@ -266,6 +302,7 @@ pub mod alloc_mod {
} }
impl TlvOwned { impl TlvOwned {
/// Generic constructor.
pub fn new(tlv_type: TlvType, data: &[u8]) -> Self { pub fn new(tlv_type: TlvType, data: &[u8]) -> Self {
Self { Self {
tlv_type_field: TlvTypeField::Standard(tlv_type), tlv_type_field: TlvTypeField::Standard(tlv_type),
@@ -273,6 +310,7 @@ pub mod alloc_mod {
} }
} }
/// Generic constructor with a custom TLV type.
pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Self { pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Self {
Self { Self {
tlv_type_field: TlvTypeField::Custom(tlv_type), tlv_type_field: TlvTypeField::Custom(tlv_type),
@@ -288,6 +326,7 @@ pub mod alloc_mod {
} }
} }
/// Write to a byte slice.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
generic_len_check_data_serialization(buf, self.data.len(), MIN_TLV_LEN)?; generic_len_check_data_serialization(buf, self.data.len(), MIN_TLV_LEN)?;
buf[0] = self.tlv_type_field.into(); buf[0] = self.tlv_type_field.into();
@@ -301,6 +340,7 @@ pub mod alloc_mod {
self.data.len() + 2 self.data.len() + 2
} }
/// Convert to [Tlv]
pub fn as_tlv(&self) -> Tlv<'_> { pub fn as_tlv(&self) -> Tlv<'_> {
Tlv { Tlv {
tlv_type_field: self.tlv_type_field, tlv_type_field: self.tlv_type_field,
@@ -349,6 +389,7 @@ pub mod alloc_mod {
} }
} }
/// Entity ID TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -357,6 +398,7 @@ pub struct EntityIdTlv {
} }
impl EntityIdTlv { impl EntityIdTlv {
/// Constructor.
#[inline] #[inline]
pub fn new(entity_id: UnsignedByteField) -> Self { pub fn new(entity_id: UnsignedByteField) -> Self {
Self { entity_id } Self { entity_id }
@@ -372,21 +414,25 @@ impl EntityIdTlv {
Ok(()) Ok(())
} }
/// Entity ID.
#[inline] #[inline]
pub fn entity_id(&self) -> &UnsignedByteField { pub fn entity_id(&self) -> &UnsignedByteField {
&self.entity_id &self.entity_id
} }
/// Length of the value field.
#[inline] #[inline]
pub fn len_value(&self) -> usize { pub fn len_value(&self) -> usize {
self.entity_id.size() self.entity_id.size()
} }
/// Full length of the TLV, including type and length fields.
#[inline] #[inline]
pub fn len_full(&self) -> usize { pub fn len_full(&self) -> usize {
2 + self.entity_id.size() 2 + self.entity_id.size()
} }
/// Create from a raw bytestream.
pub fn from_bytes(buf: &[u8]) -> Result<Self, TlvLvError> { pub fn from_bytes(buf: &[u8]) -> Result<Self, TlvLvError> {
Self::len_check(buf)?; Self::len_check(buf)?;
verify_tlv_type(buf[0], TlvType::EntityId)?; verify_tlv_type(buf[0], TlvType::EntityId)?;
@@ -478,6 +524,7 @@ impl TryFrom<Tlv<'_>> for EntityIdTlv {
} }
} }
/// Does the [FilestoreActionCode] have a second filename?
#[inline] #[inline]
pub fn fs_request_has_second_filename(action_code: FilestoreActionCode) -> bool { pub fn fs_request_has_second_filename(action_code: FilestoreActionCode) -> bool {
if action_code == FilestoreActionCode::RenameFile if action_code == FilestoreActionCode::RenameFile
@@ -511,6 +558,7 @@ impl FilestoreTlvBase<'_, '_> {
} }
} }
/// Filestore request TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FilestoreRequestTlv<'first_name, 'second_name> { pub struct FilestoreRequestTlv<'first_name, 'second_name> {
@@ -519,14 +567,17 @@ pub struct FilestoreRequestTlv<'first_name, 'second_name> {
} }
impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> { impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
/// Constructor for file creation.
pub fn new_create_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_create_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::CreateFile, file_name, None) Self::new(FilestoreActionCode::CreateFile, file_name, None)
} }
/// Constructor for file deletion.
pub fn new_delete_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_delete_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DeleteFile, file_name, None) Self::new(FilestoreActionCode::DeleteFile, file_name, None)
} }
/// Constructor for file renaming.
pub fn new_rename_file( pub fn new_rename_file(
source_name: Lv<'first_name>, source_name: Lv<'first_name>,
target_name: Lv<'second_name>, target_name: Lv<'second_name>,
@@ -566,18 +617,22 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
) )
} }
/// Constructor for directory creation.
pub fn new_create_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_create_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::CreateDirectory, dir_name, None) Self::new(FilestoreActionCode::CreateDirectory, dir_name, None)
} }
/// Constructor for directory removal.
pub fn new_remove_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_remove_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::RemoveDirectory, dir_name, None) Self::new(FilestoreActionCode::RemoveDirectory, dir_name, None)
} }
/// Constructor for file denial.
pub fn new_deny_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_deny_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DenyFile, file_name, None) Self::new(FilestoreActionCode::DenyFile, file_name, None)
} }
/// Constructor for directory denial.
pub fn new_deny_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_deny_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DenyDirectory, dir_name, None) Self::new(FilestoreActionCode::DenyDirectory, dir_name, None)
} }
@@ -611,31 +666,37 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
}) })
} }
/// Action code.
#[inline] #[inline]
pub fn action_code(&self) -> FilestoreActionCode { pub fn action_code(&self) -> FilestoreActionCode {
self.base.action_code self.base.action_code
} }
/// First name as [Lv].
#[inline] #[inline]
pub fn first_name(&self) -> Lv<'first_name> { pub fn first_name(&self) -> Lv<'first_name> {
self.base.first_name self.base.first_name
} }
/// First name as optional [Lv].
#[inline] #[inline]
pub fn second_name(&self) -> Option<Lv<'second_name>> { pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.base.second_name self.base.second_name
} }
/// Length of the value field.
#[inline] #[inline]
pub fn len_value(&self) -> usize { pub fn len_value(&self) -> usize {
self.base.base_len_value() self.base.base_len_value()
} }
/// Full TLV length.
#[inline] #[inline]
pub fn len_full(&self) -> usize { pub fn len_full(&self) -> usize {
2 + self.len_value() 2 + self.len_value()
} }
/// Construct from a raw bytestream.
pub fn from_bytes<'longest: 'first_name + 'second_name>( pub fn from_bytes<'longest: 'first_name + 'second_name>(
buf: &'longest [u8], buf: &'longest [u8],
) -> Result<Self, TlvLvError> { ) -> Result<Self, TlvLvError> {
@@ -720,6 +781,7 @@ impl GenericTlv for FilestoreRequestTlv<'_, '_> {
} }
} }
/// Filestore response TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -751,6 +813,8 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco
Lv::new_empty(), Lv::new_empty(),
) )
} }
/// Generic constructor.
pub fn new( pub fn new(
action_code: FilestoreActionCode, action_code: FilestoreActionCode,
status_code: u8, status_code: u8,
@@ -779,6 +843,7 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco
}) })
} }
/// Check whether this response has a second filename.
pub fn has_second_filename(action_code: FilestoreActionCode) -> bool { pub fn has_second_filename(action_code: FilestoreActionCode) -> bool {
if action_code == FilestoreActionCode::RenameFile if action_code == FilestoreActionCode::RenameFile
|| action_code == FilestoreActionCode::AppendFile || action_code == FilestoreActionCode::AppendFile
@@ -789,36 +854,43 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco
false false
} }
/// Action code.
#[inline] #[inline]
pub fn action_code(&self) -> FilestoreActionCode { pub fn action_code(&self) -> FilestoreActionCode {
self.base.action_code self.base.action_code
} }
/// Status code.
#[inline] #[inline]
pub fn status_code(&self) -> u8 { pub fn status_code(&self) -> u8 {
self.status_code self.status_code
} }
/// First name as [Lv].
#[inline] #[inline]
pub fn first_name(&self) -> Lv<'first_name> { pub fn first_name(&self) -> Lv<'first_name> {
self.base.first_name self.base.first_name
} }
/// Optional second name as [Lv].
#[inline] #[inline]
pub fn second_name(&self) -> Option<Lv<'second_name>> { pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.base.second_name self.base.second_name
} }
/// Length of the value field.
#[inline] #[inline]
pub fn len_value(&self) -> usize { pub fn len_value(&self) -> usize {
self.base.base_len_value() + self.filestore_message.len_full() self.base.base_len_value() + self.filestore_message.len_full()
} }
/// Full length of the TLV.
#[inline] #[inline]
pub fn len_full(&self) -> usize { pub fn len_full(&self) -> usize {
2 + self.len_value() 2 + self.len_value()
} }
/// Construct from a raw bytestream.
pub fn from_bytes<'buf: 'first_name + 'second_name + 'fs_msg>( pub fn from_bytes<'buf: 'first_name + 'second_name + 'fs_msg>(
buf: &'buf [u8], buf: &'buf [u8],
) -> Result<Self, TlvLvError> { ) -> Result<Self, TlvLvError> {
+12 -3
View File
@@ -8,8 +8,10 @@ use crate::{
}; };
use delegate::delegate; use delegate::delegate;
/// Message To User TLV structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MsgToUserTlv<'data> { pub struct MsgToUserTlv<'data> {
/// Wrapped generic TLV structure.
pub tlv: Tlv<'data>, pub tlv: Tlv<'data>,
} }
@@ -23,7 +25,9 @@ impl<'data> MsgToUserTlv<'data> {
delegate! { delegate! {
to self.tlv { to self.tlv {
/// Value field of the TLV.
pub fn value(&self) -> &[u8]; pub fn value(&self) -> &[u8];
/// Helper method to retrieve the length of the value. Simply calls the [slice::len] method of /// Helper method to retrieve the length of the value. Simply calls the [slice::len] method of
/// [Self::value] /// [Self::value]
pub fn len_value(&self) -> usize; pub fn len_value(&self) -> usize;
@@ -37,14 +41,16 @@ impl<'data> MsgToUserTlv<'data> {
} }
} }
/// Is this a standard TLV?
#[inline] #[inline]
pub fn is_standard_tlv(&self) -> bool { pub fn is_standard_tlv(&self) -> bool {
true true
} }
/// TLV type field.
#[inline] #[inline]
pub fn tlv_type(&self) -> Option<TlvType> { pub fn tlv_type(&self) -> TlvType {
Some(TlvType::MsgToUser) TlvType::MsgToUser
} }
/// Check whether this message is a reserved CFDP message like a Proxy Operation Message. /// Check whether this message is a reserved CFDP message like a Proxy Operation Message.
@@ -85,11 +91,13 @@ impl<'data> MsgToUserTlv<'data> {
Ok(msg_to_user) Ok(msg_to_user)
} }
/// Convert to a generic [Tlv].
#[inline] #[inline]
pub fn to_tlv(&self) -> Tlv<'data> { pub fn to_tlv(&self) -> Tlv<'data> {
self.tlv self.tlv
} }
/// Convert to an [TlvOwned].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_owned(&self) -> TlvOwned { pub fn to_owned(&self) -> TlvOwned {
self.tlv.to_owned() self.tlv.to_owned()
@@ -102,6 +110,7 @@ impl<'data> MsgToUserTlv<'data> {
delegate!( delegate!(
to self.tlv { to self.tlv {
/// Write the TLV to a byte buffer.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>; pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
} }
); );
@@ -142,7 +151,7 @@ mod tests {
assert!(msg_to_user.is_ok()); assert!(msg_to_user.is_ok());
let msg_to_user = msg_to_user.unwrap(); let msg_to_user = msg_to_user.unwrap();
assert!(msg_to_user.is_standard_tlv()); assert!(msg_to_user.is_standard_tlv());
assert_eq!(msg_to_user.tlv_type().unwrap(), TlvType::MsgToUser); assert_eq!(msg_to_user.tlv_type(), TlvType::MsgToUser);
assert_eq!( assert_eq!(
msg_to_user.tlv_type_field(), msg_to_user.tlv_type_field(),
TlvTypeField::Standard(TlvType::MsgToUser) TlvTypeField::Standard(TlvType::MsgToUser)
+4
View File
@@ -1,3 +1,7 @@
//! # CRC checksum support.
//!
//! Thin wrapper around the [crc] crate.
/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard, using /// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard, using
/// a [crc::NoTable] as the CRC implementation. /// a [crc::NoTable] as the CRC implementation.
pub const CRC_CCITT_FALSE_NO_TABLE: crc::Crc<u16, crc::NoTable> = pub const CRC_CCITT_FALSE_NO_TABLE: crc::Crc<u16, crc::NoTable> =
+14 -5
View File
@@ -3,17 +3,26 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Event service subtype ID.
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)] #[repr(u8)]
pub enum Subservice { pub enum MessageSubtypeId {
/// Telemetry - Info report.
TmInfoReport = 1, TmInfoReport = 1,
/// Telemetry - Low severity report.
TmLowSeverityReport = 2, TmLowSeverityReport = 2,
/// Telemetry - Medium severity report.
TmMediumSeverityReport = 3, TmMediumSeverityReport = 3,
/// Telemetry - High severity report.
TmHighSeverityReport = 4, TmHighSeverityReport = 4,
/// Telecommand - Enable event generation.
TcEnableEventGeneration = 5, TcEnableEventGeneration = 5,
/// Telecommand - Disable event generation.
TcDisableEventGeneration = 6, TcDisableEventGeneration = 6,
/// Telecommand - Report disabled list.
TcReportDisabledList = 7, TcReportDisabledList = 7,
/// Telemetry - Disabled events report.
TmDisabledEventsReport = 8, TmDisabledEventsReport = 8,
} }
@@ -23,19 +32,19 @@ mod tests {
#[test] #[test]
fn test_conv_into_u8() { fn test_conv_into_u8() {
let subservice: u8 = Subservice::TmLowSeverityReport.into(); let subservice: u8 = MessageSubtypeId::TmLowSeverityReport.into();
assert_eq!(subservice, 2); assert_eq!(subservice, 2);
} }
#[test] #[test]
fn test_conv_from_u8() { fn test_conv_from_u8() {
let subservice: Subservice = 2.try_into().unwrap(); let subservice: MessageSubtypeId = 2.try_into().unwrap();
assert_eq!(subservice, Subservice::TmLowSeverityReport); assert_eq!(subservice, MessageSubtypeId::TmLowSeverityReport);
} }
#[test] #[test]
fn test_conv_fails() { fn test_conv_fails() {
let conversion = Subservice::try_from(9); let conversion = MessageSubtypeId::try_from(9);
assert!(conversion.is_err()); assert!(conversion.is_err());
let err = conversion.unwrap_err(); let err = conversion.unwrap_err();
assert_eq!(err.number, 9); assert_eq!(err.number, 9);
+28 -9
View File
@@ -3,31 +3,49 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Housekeeping service subtype ID.
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum Subservice { pub enum MessageSubtypeId {
// Regular HK // Regular HK
/// Telecommand - Create Housekeeping Report Structure.
TcCreateHkReportStructure = 1, TcCreateHkReportStructure = 1,
/// Telecommand - Delete HK report structures.
TcDeleteHkReportStructures = 3, TcDeleteHkReportStructures = 3,
/// Telecommand - Enable HK generation.
TcEnableHkGeneration = 5, TcEnableHkGeneration = 5,
/// Telecommand - Disable HK generation.
TcDisableHkGeneration = 6, TcDisableHkGeneration = 6,
/// Telecommand - Report HK report structures.
TcReportHkReportStructures = 9, TcReportHkReportStructures = 9,
/// Telemetry - HK report.
TmHkPacket = 25, TmHkPacket = 25,
/// Telecommand - Generate one-shot report.
TcGenerateOneShotHk = 27, TcGenerateOneShotHk = 27,
/// Telecommand - Modify collection interval.
TcModifyHkCollectionInterval = 31, TcModifyHkCollectionInterval = 31,
// Diagnostics HK /// Telecommand - Create diagnostics report structures.
TcCreateDiagReportStructure = 2, TcCreateDiagReportStructure = 2,
/// Telecommand - Delete diagnostics report structures.
TcDeleteDiagReportStructures = 4, TcDeleteDiagReportStructures = 4,
/// Telecommand - Enable diagnostics generation.
TcEnableDiagGeneration = 7, TcEnableDiagGeneration = 7,
/// Telecommand - Disable diagnostics generation.
TcDisableDiagGeneration = 8, TcDisableDiagGeneration = 8,
/// Telemetry - HK structures report.
TmHkStructuresReport = 10, TmHkStructuresReport = 10,
/// Telecommand - Report diagnostics report structures.
TcReportDiagReportStructures = 11, TcReportDiagReportStructures = 11,
/// Telemetry - Diagnostics report structures.
TmDiagStructuresReport = 12, TmDiagStructuresReport = 12,
/// Telemetry - Diagnostics packet.
TmDiagPacket = 26, TmDiagPacket = 26,
/// Telecommand - Generate one-shot diagnostics report.
TcGenerateOneShotDiag = 28, TcGenerateOneShotDiag = 28,
/// Telecommand - Modify diagnostics interval report.
TcModifyDiagCollectionInterval = 32, TcModifyDiagCollectionInterval = 32,
} }
@@ -37,25 +55,26 @@ mod tests {
#[test] #[test]
fn test_try_from_u8() { fn test_try_from_u8() {
let hk_report_subservice_raw = 25; let hk_report_subservice_raw = 25;
let hk_report: Subservice = Subservice::try_from(hk_report_subservice_raw).unwrap(); let hk_report: MessageSubtypeId =
assert_eq!(hk_report, Subservice::TmHkPacket); MessageSubtypeId::try_from(hk_report_subservice_raw).unwrap();
assert_eq!(hk_report, MessageSubtypeId::TmHkPacket);
} }
#[test] #[test]
fn test_into_u8() { fn test_into_u8() {
let hk_report_raw: u8 = Subservice::TmHkPacket.into(); let hk_report_raw: u8 = MessageSubtypeId::TmHkPacket.into();
assert_eq!(hk_report_raw, 25); assert_eq!(hk_report_raw, 25);
} }
#[test] #[test]
fn test_partial_eq() { fn test_partial_eq() {
let hk_report_raw = Subservice::TmHkPacket; let hk_report_raw = MessageSubtypeId::TmHkPacket;
assert_ne!(hk_report_raw, Subservice::TcGenerateOneShotHk); assert_ne!(hk_report_raw, MessageSubtypeId::TcGenerateOneShotHk);
assert_eq!(hk_report_raw, Subservice::TmHkPacket); assert_eq!(hk_report_raw, MessageSubtypeId::TmHkPacket);
} }
#[test] #[test]
fn test_copy_clone() { fn test_copy_clone() {
let hk_report = Subservice::TmHkPacket; let hk_report = MessageSubtypeId::TmHkPacket;
let hk_report_copy = hk_report; let hk_report_copy = hk_report;
assert_eq!(hk_report, hk_report_copy); assert_eq!(hk_report, hk_report_copy);
} }
+121 -39
View File
@@ -25,53 +25,55 @@ pub mod tm;
pub mod tm_pus_a; pub mod tm_pus_a;
pub mod verification; pub mod verification;
/// Type alias for the CRC16 type.
pub type CrcType = u16; pub type CrcType = u16;
/// Standard PUS service IDs.
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
#[non_exhaustive] #[non_exhaustive]
pub enum PusServiceId { pub enum PusServiceId {
/// Service 1 /// Service 1 Verification
Verification = 1, Verification = 1,
/// Service 2 /// Service 2 Device Access
DeviceAccess = 2, DeviceAccess = 2,
/// Service 3 /// Service 3 Housekeeping
Housekeeping = 3, Housekeeping = 3,
/// Service 4 /// Service 4 Parameter Statistics
ParameterStatistics = 4, ParameterStatistics = 4,
/// Service 5 /// Service 5 Event
Event = 5, Event = 5,
/// Service 6 /// Service 6 Memory Management
MemoryManagement = 6, MemoryManagement = 6,
/// Service 8 /// Service 8 Action
Action = 8, Action = 8,
/// Service 9 /// Service 9 Time Management
TimeManagement = 9, TimeManagement = 9,
/// Service 11 /// Service 11 Scheduling
Scheduling = 11, Scheduling = 11,
/// Service 12 /// Service 12 On-Board Monitoring
OnBoardMonitoring = 12, OnBoardMonitoring = 12,
/// Service 13 /// Service 13 Large Packet Transfer
LargePacketTransfer = 13, LargePacketTransfer = 13,
/// Service 14 /// Service 14 Real-Time Forwarding Control
RealTimeForwardingControl = 14, RealTimeForwardingControl = 14,
/// Service 15 /// Service 15 Storage And Retrival
StorageAndRetrival = 15, StorageAndRetrival = 15,
/// Service 17 /// Service 17 Test
Test = 17, Test = 17,
/// Service 18 /// Service 18 Operations And Procedures
OpsAndProcedures = 18, OpsAndProcedures = 18,
/// Service 19 /// Service 19 Event Action
EventAction = 19, EventAction = 19,
/// Service 20 /// Service 20 Parameter
Parameter = 20, Parameter = 20,
/// Service 21 /// Service 21 Request Sequencing
RequestSequencing = 21, RequestSequencing = 21,
/// Service 22 /// Service 22 Position Based Scheduling
PositionBasedScheduling = 22, PositionBasedScheduling = 22,
/// Service 23 /// Service 23 File Management
FileManagement = 23, FileManagement = 23,
} }
@@ -83,8 +85,11 @@ pub enum PusServiceId {
#[repr(u8)] #[repr(u8)]
#[non_exhaustive] #[non_exhaustive]
pub enum PusVersion { pub enum PusVersion {
/// ESA PUS
EsaPus = 0, EsaPus = 0,
/// PUS A
PusA = 1, PusA = 1,
/// PUS C
PusC = 2, PusC = 2,
} }
@@ -107,20 +112,33 @@ impl TryFrom<u4> for PusVersion {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum PacketTypeCodes { pub enum PacketTypeCodes {
/// Boolean.
Boolean = 1, Boolean = 1,
/// Enumerated.
Enumerated = 2, Enumerated = 2,
/// Unsigned Integer.
UnsignedInt = 3, UnsignedInt = 3,
/// Signed Integer.
SignedInt = 4, SignedInt = 4,
/// Real (floating point).
Real = 5, Real = 5,
/// Bit string.
BitString = 6, BitString = 6,
/// Octet (byte) string.
OctetString = 7, OctetString = 7,
/// Character string.
CharString = 8, CharString = 8,
/// Absolute time.
AbsoluteTime = 9, AbsoluteTime = 9,
/// Relative time.
RelativeTime = 10, RelativeTime = 10,
/// Deduced.
Deduced = 11, Deduced = 11,
/// Packet.
Packet = 12, Packet = 12,
} }
/// Type alias for the ECSS Packet Type Codes (PTC)s.
pub type Ptc = PacketTypeCodes; pub type Ptc = PacketTypeCodes;
/// ECSS Packet Field Codes (PFC)s for the unsigned [Ptc]. /// ECSS Packet Field Codes (PFC)s for the unsigned [Ptc].
@@ -129,15 +147,25 @@ pub type Ptc = PacketTypeCodes;
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum PfcUnsigned { pub enum PfcUnsigned {
/// 1 byte.
OneByte = 4, OneByte = 4,
/// 12 bits.
TwelveBits = 8, TwelveBits = 8,
/// 2 bytes.
TwoBytes = 12, TwoBytes = 12,
/// 3 bytes.
ThreeBytes = 13, ThreeBytes = 13,
/// 4 bytes.
FourBytes = 14, FourBytes = 14,
/// 6 bytes.
SixBytes = 15, SixBytes = 15,
/// 8 bytes.
EightBytes = 16, EightBytes = 16,
/// 1 bit.
OneBit = 17, OneBit = 17,
/// 2 bits.
TwoBits = 18, TwoBits = 18,
/// 3 bits.
ThreeBits = 19, ThreeBits = 19,
} }
@@ -157,12 +185,15 @@ pub enum PfcReal {
DoubleMilStd = 4, DoubleMilStd = 4,
} }
/// Generic PUS error.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PusError { pub enum PusError {
/// PUS version is not supported.
#[error("PUS version {0:?} not supported")] #[error("PUS version {0:?} not supported")]
VersionNotSupported(u4), VersionNotSupported(u4),
/// Checksum failure.
#[error("checksum verification for crc16 {0:#06x} failed")] #[error("checksum verification for crc16 {0:#06x} failed")]
ChecksumFailure(u16), ChecksumFailure(u16),
/// CRC16 needs to be calculated first /// CRC16 needs to be calculated first
@@ -172,15 +203,54 @@ pub enum PusError {
ByteConversion(#[from] ByteConversionError), ByteConversion(#[from] ByteConversionError),
} }
/// Message type ID field.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MessageTypeId {
/// Service type ID.
pub type_id: u8,
/// Subtype ID.
pub subtype_id: u8,
}
impl MessageTypeId {
/// Generic constructor.
pub const fn new(type_id: u8, subtype_id: u8) -> Self {
Self {
type_id,
subtype_id,
}
}
}
/// Generic trait to describe common attributes for both PUS Telecommands (TC) and PUS Telemetry /// Generic trait to describe common attributes for both PUS Telecommands (TC) and PUS Telemetry
/// (TM) packets. All PUS packets are also a special type of [CcsdsPacket]s. /// (TM) packets. All PUS packets are also a special type of [CcsdsPacket]s.
pub trait PusPacket: CcsdsPacket { pub trait PusPacket: CcsdsPacket {
/// PUS version.
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
fn service(&self) -> u8;
fn subservice(&self) -> u8; /// Message type ID.
fn message_type_id(&self) -> MessageTypeId;
/// Service type ID.
#[inline]
fn service_type_id(&self) -> u8 {
self.message_type_id().type_id
}
/// Message subtype ID.
#[inline]
fn message_subtype_id(&self) -> u8 {
self.message_type_id().subtype_id
}
/// User data field.
fn user_data(&self) -> &[u8]; fn user_data(&self) -> &[u8];
/// CRC-16-CCITT checksum. /// CRC-16-CCITT checksum.
fn checksum(&self) -> Option<u16>; fn checksum(&self) -> Option<u16>;
/// The presence of the CRC-16-CCITT checksum is optional. /// The presence of the CRC-16-CCITT checksum is optional.
fn has_checksum(&self) -> bool { fn has_checksum(&self) -> bool {
self.checksum().is_some() self.checksum().is_some()
@@ -226,6 +296,7 @@ pub(crate) fn user_data_from_raw(
} }
} }
/// Verify the CRC16 of a raw packet and return a [PusError] on failure.
pub fn verify_crc16_ccitt_false_from_raw_to_pus_error( pub fn verify_crc16_ccitt_false_from_raw_to_pus_error(
raw_data: &[u8], raw_data: &[u8],
crc16: u16, crc16: u16,
@@ -235,6 +306,8 @@ pub fn verify_crc16_ccitt_false_from_raw_to_pus_error(
.ok_or(PusError::ChecksumFailure(crc16)) .ok_or(PusError::ChecksumFailure(crc16))
} }
/// Verify the CRC16 of a raw packet using a table-less implementation and return a [PusError] on
/// failure.
pub fn verify_crc16_ccitt_false_from_raw_to_pus_error_no_table( pub fn verify_crc16_ccitt_false_from_raw_to_pus_error_no_table(
raw_data: &[u8], raw_data: &[u8],
crc16: u16, crc16: u16,
@@ -267,10 +340,15 @@ pub fn verify_crc16_ccitt_false_from_raw_no_table(raw_data: &[u8]) -> bool {
macro_rules! sp_header_impls { macro_rules! sp_header_impls {
() => { () => {
delegate!(to self.sp_header { delegate!(to self.sp_header {
/// Set the CCSDS APID.
#[inline] #[inline]
pub fn set_apid(&mut self, apid: u11); pub fn set_apid(&mut self, apid: u11);
/// Set the CCSDS sequence count.
#[inline] #[inline]
pub fn set_seq_count(&mut self, seq_count: u14); pub fn set_seq_count(&mut self, seq_count: u14);
/// Set the CCSDS sequence flags.
#[inline] #[inline]
pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags); pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags);
}); });
@@ -289,27 +367,28 @@ pub trait EcssEnumeration: UnsignedEnum {
fn pfc(&self) -> u8; fn pfc(&self) -> u8;
} }
/// Extension trait for [EcssEnumeration] which adds common trait bounds.
pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {} pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {}
/// ECSS enumerated type wrapper.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GenericEcssEnumWrapper<TYPE: Copy + Into<u64>> { pub struct GenericEcssEnumWrapper<TYPE: Copy + Into<u64>>(GenericUnsignedByteField<TYPE>);
field: GenericUnsignedByteField<TYPE>,
}
impl<TYPE: Copy + Into<u64>> GenericEcssEnumWrapper<TYPE> { impl<TYPE: Copy + Into<u64>> GenericEcssEnumWrapper<TYPE> {
/// Returns [PacketTypeCodes::Enumerated].
pub const fn ptc() -> PacketTypeCodes { pub const fn ptc() -> PacketTypeCodes {
PacketTypeCodes::Enumerated PacketTypeCodes::Enumerated
} }
pub const fn value_typed(&self) -> TYPE { /// Value.
self.field.value_typed() pub const fn value(&self) -> TYPE {
self.0.value()
} }
pub fn new(val: TYPE) -> Self { /// Generic constructor.
Self { pub const fn new(val: TYPE) -> Self {
field: GenericUnsignedByteField::new(val), Self(GenericUnsignedByteField::new(val))
}
} }
} }
@@ -319,11 +398,11 @@ impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericEcssEnumWrapper
} }
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
self.field.write_to_be_bytes(buf) self.0.write_to_be_bytes(buf)
} }
fn value(&self) -> u64 { fn value_raw(&self) -> u64 {
self.field.value() self.0.value().into()
} }
} }
@@ -347,11 +426,12 @@ impl<T: Copy + Into<u64>> From<T> for GenericEcssEnumWrapper<T> {
macro_rules! generic_ecss_enum_typedefs_and_from_impls { macro_rules! generic_ecss_enum_typedefs_and_from_impls {
($($ty:ty => $Enum:ident),*) => { ($($ty:ty => $Enum:ident),*) => {
$( $(
/// Type alias for ECSS enumeration wrapper around `$ty`
pub type $Enum = GenericEcssEnumWrapper<$ty>; pub type $Enum = GenericEcssEnumWrapper<$ty>;
impl From<$Enum> for $ty { impl From<$Enum> for $ty {
fn from(value: $Enum) -> Self { fn from(value: $Enum) -> Self {
value.value_typed() value.value()
} }
} }
)* )*
@@ -412,6 +492,7 @@ pub trait WritablePusPacket {
Ok(curr_idx) Ok(curr_idx)
} }
/// Converts the packet into an owned [alloc::vec::Vec].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<Vec<u8>, PusError> { fn to_vec(&self) -> Result<Vec<u8>, PusError> {
// This is the correct way to do this. See // This is the correct way to do this. See
@@ -423,6 +504,7 @@ pub trait WritablePusPacket {
} }
} }
/// PUS packet creator configuration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -465,7 +547,7 @@ mod tests {
.expect("To byte conversion of u8 failed"); .expect("To byte conversion of u8 failed");
assert_eq!(buf[1], 1); assert_eq!(buf[1], 1);
assert_eq!(my_enum.value(), 1); assert_eq!(my_enum.value(), 1);
assert_eq!(my_enum.value_typed(), 1); assert_eq!(my_enum.value(), 1);
let enum_as_u8: u8 = my_enum.into(); let enum_as_u8: u8 = my_enum.into();
assert_eq!(enum_as_u8, 1); assert_eq!(enum_as_u8, 1);
let vec = my_enum.to_vec(); let vec = my_enum.to_vec();
@@ -484,7 +566,7 @@ mod tests {
assert_eq!(buf[1], 0x1f); assert_eq!(buf[1], 0x1f);
assert_eq!(buf[2], 0x2f); assert_eq!(buf[2], 0x2f);
assert_eq!(my_enum.value(), 0x1f2f); assert_eq!(my_enum.value(), 0x1f2f);
assert_eq!(my_enum.value_typed(), 0x1f2f); assert_eq!(my_enum.value(), 0x1f2f);
let enum_as_raw: u16 = my_enum.into(); let enum_as_raw: u16 = my_enum.into();
assert_eq!(enum_as_raw, 0x1f2f); assert_eq!(enum_as_raw, 0x1f2f);
let vec = my_enum.to_vec(); let vec = my_enum.to_vec();
@@ -521,7 +603,7 @@ mod tests {
assert_eq!(buf[3], 0x3f); assert_eq!(buf[3], 0x3f);
assert_eq!(buf[4], 0x4f); assert_eq!(buf[4], 0x4f);
assert_eq!(my_enum.value(), 0x1f2f3f4f); assert_eq!(my_enum.value(), 0x1f2f3f4f);
assert_eq!(my_enum.value_typed(), 0x1f2f3f4f); assert_eq!(my_enum.value(), 0x1f2f3f4f);
let enum_as_raw: u32 = my_enum.into(); let enum_as_raw: u32 = my_enum.into();
assert_eq!(enum_as_raw, 0x1f2f3f4f); assert_eq!(enum_as_raw, 0x1f2f3f4f);
let vec = my_enum.to_vec(); let vec = my_enum.to_vec();
@@ -559,7 +641,7 @@ mod tests {
assert_eq!(buf[6], 0x4f); assert_eq!(buf[6], 0x4f);
assert_eq!(buf[7], 0x5f); assert_eq!(buf[7], 0x5f);
assert_eq!(my_enum.value(), 0x1f2f3f4f5f); assert_eq!(my_enum.value(), 0x1f2f3f4f5f);
assert_eq!(my_enum.value_typed(), 0x1f2f3f4f5f); assert_eq!(my_enum.value(), 0x1f2f3f4f5f);
let enum_as_raw: u64 = my_enum.into(); let enum_as_raw: u64 = my_enum.into();
assert_eq!(enum_as_raw, 0x1f2f3f4f5f); assert_eq!(enum_as_raw, 0x1f2f3f4f5f);
assert_eq!(u64::from_be_bytes(buf), 0x1f2f3f4f5f); assert_eq!(u64::from_be_bytes(buf), 0x1f2f3f4f5f);
+39 -5
View File
@@ -3,46 +3,74 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Scheduling service subtype ID.
#[derive(Debug, PartialEq, Eq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, PartialEq, Eq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum Subservice { pub enum MessageSubtypeId {
// Core subservices // Core subservices
/// Telecommand - Enable scheduling.
TcEnableScheduling = 1, TcEnableScheduling = 1,
/// Telecommand - Disable scheduling.
TcDisableScheduling = 2, TcDisableScheduling = 2,
/// Telecommand - Reset scheduling.
TcResetScheduling = 3, TcResetScheduling = 3,
/// Telecommand - Insert activity.
TcInsertActivity = 4, TcInsertActivity = 4,
/// Telecommand - Delete activity by request ID.
TcDeleteActivityByRequestId = 5, TcDeleteActivityByRequestId = 5,
/// Telecommand - Delete activity by filter.
TcDeleteActivitiesByFilter = 6, TcDeleteActivitiesByFilter = 6,
// Time shift subservices // Time shift subservices
/// Telecommand - Time shift activity by request ID.
TcTimeShiftActivityWithRequestId = 7, TcTimeShiftActivityWithRequestId = 7,
/// Telecommand - Time shift activity by filter.
TcTimeShiftActivitiesByFilter = 8, TcTimeShiftActivitiesByFilter = 8,
/// Telecommand - Time shift all.
TcTimeShiftAll = 15, TcTimeShiftAll = 15,
// Reporting subservices // Reporting subservices
/// Telecommand - Detail report by request ID.
TcDetailReportByRequestId = 9, TcDetailReportByRequestId = 9,
/// Telemetry - Detail report.
TmDetailReport = 10, TmDetailReport = 10,
/// Telecommand - Detail report by filter.
TcDetailReportByFilter = 11, TcDetailReportByFilter = 11,
/// Telecommand - Summary report by request ID.
TcSummaryReportByRequestId = 12, TcSummaryReportByRequestId = 12,
/// Telemetry - Summary report.
TmSummaryReport = 13, TmSummaryReport = 13,
/// Telecommand - Summary report by filter.
TcSummaryReportByFilter = 14, TcSummaryReportByFilter = 14,
/// Telecommand - Detail report all.
TcDetailReportAll = 16, TcDetailReportAll = 16,
/// Telecommand - Summary report all.
TcSummaryReportAll = 17, TcSummaryReportAll = 17,
// Subschedule subservices // Subschedule subservices
/// Telecommand - Report subschedule status.
TcReportSubscheduleStatus = 18, TcReportSubscheduleStatus = 18,
/// Telemetry - Subschedule status report.
TmReportSubscheduleStatus = 19, TmReportSubscheduleStatus = 19,
/// Telecommand - Enable subschedule.
TcEnableSubschedule = 20, TcEnableSubschedule = 20,
/// Telecommand - Disable subschedule.
TcDisableSubschedule = 21, TcDisableSubschedule = 21,
// Group subservices // Group subservices
/// Telecommand - Create schedule group.
TcCreateScheduleGroup = 22, TcCreateScheduleGroup = 22,
/// Telecommand - Delete schedule group.
TcDeleteScheduleGroup = 23, TcDeleteScheduleGroup = 23,
/// Telecommand - Enable schedule group.
TcEnableScheduleGroup = 24, TcEnableScheduleGroup = 24,
/// Telecommand - Disable schedule group.
TcDisableScheduleGroup = 25, TcDisableScheduleGroup = 25,
/// Telecommand - Report all group status.
TcReportAllGroupsStatus = 26, TcReportAllGroupsStatus = 26,
/// Telemetry - All group status report.
TmReportAllGroupsStatus = 27, TmReportAllGroupsStatus = 27,
} }
@@ -51,7 +79,9 @@ pub enum Subservice {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SchedStatus { pub enum SchedStatus {
/// Scheduling disabled.
Disabled = 0, Disabled = 0,
/// Scheduling enabled.
Enabled = 1, Enabled = 1,
} }
@@ -71,9 +101,13 @@ impl From<bool> for SchedStatus {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TimeWindowType { pub enum TimeWindowType {
/// Select all.
SelectAll = 0, SelectAll = 0,
/// From time tag to time tag.
TimeTagToTimeTag = 1, TimeTagToTimeTag = 1,
/// Starting from a time tag.
FromTimeTag = 2, FromTimeTag = 2,
/// Until a time tag.
ToTimeTag = 3, ToTimeTag = 3,
} }
@@ -99,20 +133,20 @@ mod tests {
#[test] #[test]
fn test_conv_into_u8() { fn test_conv_into_u8() {
let subservice: u8 = Subservice::TcCreateScheduleGroup.into(); let subservice: u8 = MessageSubtypeId::TcCreateScheduleGroup.into();
assert_eq!(subservice, 22); assert_eq!(subservice, 22);
} }
#[test] #[test]
fn test_conv_from_u8() { fn test_conv_from_u8() {
let subservice: Subservice = 22u8.try_into().unwrap(); let subservice: MessageSubtypeId = 22u8.try_into().unwrap();
assert_eq!(subservice, Subservice::TcCreateScheduleGroup); assert_eq!(subservice, MessageSubtypeId::TcCreateScheduleGroup);
} }
#[test] #[test]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
fn test_serde_subservice_id() { fn test_serde_subservice_id() {
generic_serde_test(Subservice::TcEnableScheduling); generic_serde_test(MessageSubtypeId::TcEnableScheduling);
} }
#[test] #[test]
+160 -83
View File
@@ -5,18 +5,18 @@
//! //!
//! ```rust //! ```rust
//! use spacepackets::SpHeader; //! use spacepackets::SpHeader;
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader, CreatorConfig}; //! use spacepackets::ecss::tc::{MessageTypeId, PusTcCreator, PusTcReader, PusTcSecondaryHeader, CreatorConfig};
//! use arbitrary_int::u11; //! use arbitrary_int::u11;
//! //!
//! // Create a ping telecommand with no user application data //! // Create a ping telecommand with no user application data
//! let pus_tc = PusTcCreator::new_no_app_data( //! let pus_tc = PusTcCreator::new_no_app_data(
//! SpHeader::new_from_apid(u11::new(0x02)), //! SpHeader::new_from_apid(u11::new(0x02)),
//! PusTcSecondaryHeader::new_simple(17, 1), //! PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1)),
//! CreatorConfig::default() //! CreatorConfig::default()
//! ); //! );
//! println!("{:?}", pus_tc); //! println!("{:?}", pus_tc);
//! assert_eq!(pus_tc.service(), 17); //! assert_eq!(pus_tc.service_type_id(), 17);
//! assert_eq!(pus_tc.subservice(), 1); //! assert_eq!(pus_tc.message_subtype_id(), 1);
//! assert_eq!(pus_tc.apid().value(), 0x02); //! assert_eq!(pus_tc.apid().value(), 0x02);
//! //!
//! // Serialize TC into a raw buffer //! // Serialize TC into a raw buffer
@@ -29,25 +29,25 @@
//! //!
//! // Deserialize from the raw byte representation //! // Deserialize from the raw byte representation
//! let pus_tc_deserialized = PusTcReader::new(&test_buf).expect("Deserialization failed"); //! let pus_tc_deserialized = PusTcReader::new(&test_buf).expect("Deserialization failed");
//! assert_eq!(pus_tc.service(), 17); //! assert_eq!(pus_tc.service_type_id(), 17);
//! assert_eq!(pus_tc.subservice(), 1); //! assert_eq!(pus_tc.message_subtype_id(), 1);
//! assert_eq!(pus_tc.apid().value(), 0x02); //! assert_eq!(pus_tc.apid().value(), 0x02);
//! //!
//! // Alternative builder API //! // Alternative builder API
//! let pus_tc_by_builder = PusTcCreator::builder() //! let pus_tc_by_builder = PusTcCreator::builder()
//! .with_service(17) //! .with_service_type_id(17)
//! .with_subservice(1) //! .with_message_subtype_id(1)
//! .with_apid(u11::new(0x02)) //! .with_apid(u11::new(0x02))
//! .build(); //! .build();
//! assert_eq!(pus_tc_by_builder, pus_tc); //! assert_eq!(pus_tc_by_builder, pus_tc);
//! ``` //! ```
use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE};
pub use crate::ecss::CreatorConfig;
use crate::ecss::{ use crate::ecss::{
crc_from_raw_data, sp_header_impls, user_data_from_raw, crc_from_raw_data, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion, verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion,
WritablePusPacket, WritablePusPacket,
}; };
pub use crate::ecss::{CreatorConfig, MessageTypeId};
use crate::{ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, CCSDS_HEADER_LEN}; use crate::{ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, CCSDS_HEADER_LEN};
use crate::{PacketId, PacketSequenceControl, SpHeader}; use crate::{PacketId, PacketSequenceControl, SpHeader};
use arbitrary_int::{u11, u14, u3, u4}; use arbitrary_int::{u11, u14, u3, u4};
@@ -57,10 +57,6 @@ use delegate::delegate;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use zerocopy::{FromBytes, IntoBytes}; use zerocopy::{FromBytes, IntoBytes};
// Is necessary for some reason, possibly bug.
#[cfg(feature = "defmt")]
use arbitrary_int::traits::Integer;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -68,26 +64,33 @@ use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table;
/// PUS C secondary header length is fixed /// PUS C secondary header length is fixed
pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::<zc::PusTcSecondaryHeader>(); pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::<zc::PusTcSecondaryHeader>();
/// Minimum PUS C secondary header length without application data.
pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize = CCSDS_HEADER_LEN + PUC_TC_SECONDARY_HEADER_LEN; pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize = CCSDS_HEADER_LEN + PUC_TC_SECONDARY_HEADER_LEN;
const PUS_VERSION: PusVersion = PusVersion::PusC; const PUS_VERSION: PusVersion = PusVersion::PusC;
/// Marker trait for PUS telecommand structures. /// Marker trait for PUS telecommand structures.
pub trait IsPusTelecommand {} pub trait IsPusTelecommand {}
/// Acknowledgement flags for PUS telecommands.
#[bitbybit::bitfield(u4, default = 0b0000, debug, defmt_bitfields(feature = "defmt"))] #[bitbybit::bitfield(u4, default = 0b0000, debug, defmt_bitfields(feature = "defmt"))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub struct AckFlags { pub struct AckFlags {
/// Acceptance must be acknowledged.
#[bit(3, rw)] #[bit(3, rw)]
acceptance: bool, acceptance: bool,
/// Start must be acknowledged.
#[bit(2, rw)] #[bit(2, rw)]
start: bool, start: bool,
/// Progress must be acknowledged.
#[bit(1, rw)] #[bit(1, rw)]
progress: bool, progress: bool,
/// Completion must be acknowledged.
#[bit(0, rw)] #[bit(0, rw)]
completion: bool, completion: bool,
} }
/// Constant to acknowledge all TC handling phases.
pub const ACK_ALL: AckFlags = AckFlags::builder() pub const ACK_ALL: AckFlags = AckFlags::builder()
.with_acceptance(true) .with_acceptance(true)
.with_start(true) .with_start(true)
@@ -96,30 +99,49 @@ pub const ACK_ALL: AckFlags = AckFlags::builder()
.build(); .build();
impl AckFlags { impl AckFlags {
/// Constant to acknowledge all TC handling phases.
pub const ALL: Self = ACK_ALL; pub const ALL: Self = ACK_ALL;
} }
/// Generic trait for PUS TC secondary header access.
pub trait GenericPusTcSecondaryHeader { pub trait GenericPusTcSecondaryHeader {
/// PUS version.
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
/// Acknowledgement flags.
fn ack_flags(&self) -> AckFlags; fn ack_flags(&self) -> AckFlags;
fn service(&self) -> u8; /// Message type ID.
fn subservice(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
/// Source ID.
fn source_id(&self) -> u16; fn source_id(&self) -> u16;
/// Service type ID.
#[inline]
fn service_type_id(&self) -> u8 {
self.message_type_id().type_id
}
/// Message subtype ID.
#[inline]
fn message_subtype_id(&self) -> u8 {
self.message_type_id().subtype_id
}
} }
/// [zerocopy] support.
pub mod zc { pub mod zc {
use crate::ecss::tc::{AckFlags, GenericPusTcSecondaryHeader}; use crate::ecss::tc::{AckFlags, GenericPusTcSecondaryHeader};
use crate::ecss::{PusError, PusVersion}; use crate::ecss::{MessageTypeId, PusError, PusVersion};
use arbitrary_int::traits::Integer; use arbitrary_int::traits::Integer;
use arbitrary_int::u4; use arbitrary_int::u4;
use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16}; use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16};
/// PUS TC secondary header.
#[derive(FromBytes, IntoBytes, Immutable, Unaligned)] #[derive(FromBytes, IntoBytes, Immutable, Unaligned)]
#[repr(C)] #[repr(C)]
pub struct PusTcSecondaryHeader { pub struct PusTcSecondaryHeader {
version_ack: u8, version_ack: u8,
service: u8, service_type_id: u8,
subservice: u8, message_subtype_id: u8,
source_id: U16<NetworkEndian>, source_id: U16<NetworkEndian>,
} }
@@ -131,8 +153,8 @@ pub mod zc {
} }
Ok(PusTcSecondaryHeader { Ok(PusTcSecondaryHeader {
version_ack: ((value.version as u8) << 4) | value.ack_flags.raw_value().as_u8(), version_ack: ((value.version as u8) << 4) | value.ack_flags.raw_value().as_u8(),
service: value.service, service_type_id: value.service_type_id(),
subservice: value.subservice, message_subtype_id: value.message_subtype_id(),
source_id: U16::from(value.source_id), source_id: U16::from(value.source_id),
}) })
} }
@@ -150,13 +172,22 @@ pub mod zc {
} }
#[inline] #[inline]
fn service(&self) -> u8 { /// Message type ID.
self.service fn message_type_id(&self) -> MessageTypeId {
MessageTypeId {
type_id: self.service_type_id,
subtype_id: self.message_subtype_id,
}
} }
#[inline] #[inline]
fn subservice(&self) -> u8 { fn service_type_id(&self) -> u8 {
self.subservice self.service_type_id
}
#[inline]
fn message_subtype_id(&self) -> u8 {
self.message_subtype_id
} }
#[inline] #[inline]
@@ -166,14 +197,18 @@ pub mod zc {
} }
} }
/// PUS C secondary header for telecommands.
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTcSecondaryHeader { pub struct PusTcSecondaryHeader {
pub service: u8, /// Message type ID.
pub subservice: u8, pub message_type_id: MessageTypeId,
/// Source ID.
pub source_id: u16, pub source_id: u16,
/// Acknowledgement flags.
pub ack_flags: AckFlags, pub ack_flags: AckFlags,
/// PUS version.
pub version: PusVersion, pub version: PusVersion,
} }
@@ -189,13 +224,8 @@ impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
} }
#[inline] #[inline]
fn service(&self) -> u8 { fn message_type_id(&self) -> MessageTypeId {
self.service self.message_type_id
}
#[inline]
fn subservice(&self) -> u8 {
self.subservice
} }
#[inline] #[inline]
@@ -209,8 +239,7 @@ impl TryFrom<zc::PusTcSecondaryHeader> for PusTcSecondaryHeader {
fn try_from(value: zc::PusTcSecondaryHeader) -> Result<Self, Self::Error> { fn try_from(value: zc::PusTcSecondaryHeader) -> Result<Self, Self::Error> {
Ok(PusTcSecondaryHeader { Ok(PusTcSecondaryHeader {
service: value.service(), message_type_id: value.message_type_id(),
subservice: value.subservice(),
source_id: value.source_id(), source_id: value.source_id(),
ack_flags: value.ack_flags(), ack_flags: value.ack_flags(),
version: PUS_VERSION, version: PUS_VERSION,
@@ -219,24 +248,25 @@ impl TryFrom<zc::PusTcSecondaryHeader> for PusTcSecondaryHeader {
} }
impl PusTcSecondaryHeader { impl PusTcSecondaryHeader {
/// Header length constant.
pub const HEADER_LEN: usize = PUC_TC_SECONDARY_HEADER_LEN; pub const HEADER_LEN: usize = PUC_TC_SECONDARY_HEADER_LEN;
/// Simple constructor which only requires the [MessageTypeId].
#[inline] #[inline]
pub fn new_simple(service: u8, subservice: u8) -> Self { pub fn new_simple(message_type_id: MessageTypeId) -> Self {
PusTcSecondaryHeader { PusTcSecondaryHeader {
service, message_type_id,
subservice,
ack_flags: ACK_ALL, ack_flags: ACK_ALL,
source_id: 0, source_id: 0,
version: PusVersion::PusC, version: PusVersion::PusC,
} }
} }
/// General constructor.
#[inline] #[inline]
pub fn new(service: u8, subservice: u8, ack_flags: AckFlags, source_id: u16) -> Self { pub fn new(message_type_id: MessageTypeId, ack_flags: AckFlags, source_id: u16) -> Self {
PusTcSecondaryHeader { PusTcSecondaryHeader {
service, message_type_id,
subservice,
ack_flags, ack_flags,
source_id, source_id,
version: PusVersion::PusC, version: PusVersion::PusC,
@@ -257,6 +287,7 @@ impl PusTcSecondaryHeader {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTcCreator<'app_data> { pub struct PusTcCreator<'app_data> {
sp_header: SpHeader, sp_header: SpHeader,
/// Secondary header.
pub sec_header: PusTcSecondaryHeader, pub sec_header: PusTcSecondaryHeader,
app_data: &'app_data [u8], app_data: &'app_data [u8],
has_checksum: bool, has_checksum: bool,
@@ -299,19 +330,19 @@ impl<'app_data> PusTcCreator<'app_data> {
#[inline] #[inline]
pub fn new_simple( pub fn new_simple(
sph: SpHeader, sph: SpHeader,
service: u8, message_type_id: MessageTypeId,
subservice: u8,
app_data: &'app_data [u8], app_data: &'app_data [u8],
packet_config: CreatorConfig, packet_config: CreatorConfig,
) -> Self { ) -> Self {
Self::new( Self::new(
sph, sph,
PusTcSecondaryHeader::new(service, subservice, ACK_ALL, 0), PusTcSecondaryHeader::new(message_type_id, ACK_ALL, 0),
app_data, app_data,
packet_config, packet_config,
) )
} }
/// Constructor for a PUS TC packet without application data.
#[inline] #[inline]
pub fn new_no_app_data( pub fn new_no_app_data(
sp_header: SpHeader, sp_header: SpHeader,
@@ -321,40 +352,48 @@ impl<'app_data> PusTcCreator<'app_data> {
Self::new(sp_header, sec_header, &[], packet_config) Self::new(sp_header, sec_header, &[], packet_config)
} }
/// Get a builder whih allows building [Self] step-by-step.
pub fn builder<'a>() -> PusTcBuilder<'a> { pub fn builder<'a>() -> PusTcBuilder<'a> {
PusTcBuilder::default() PusTcBuilder::default()
} }
/// Space packet header.
#[inline] #[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
/// Mutable access to the space packet header.
#[inline] #[inline]
pub fn sp_header_mut(&mut self) -> &mut SpHeader { pub fn sp_header_mut(&mut self) -> &mut SpHeader {
&mut self.sp_header &mut self.sp_header
} }
/// Service type ID.
#[inline] #[inline]
pub fn service(&self) -> u8 { pub fn service_type_id(&self) -> u8 {
self.sec_header.service self.sec_header.service_type_id()
} }
/// Message subtype ID.
#[inline] #[inline]
pub fn subservice(&self) -> u8 { pub fn message_subtype_id(&self) -> u8 {
self.sec_header.subservice self.sec_header.message_subtype_id()
} }
/// Application Process ID (APID).
#[inline] #[inline]
pub fn apid(&self) -> u11 { pub fn apid(&self) -> u11 {
self.sp_header.packet_id.apid self.sp_header.packet_id.apid
} }
/// Set acknowledgement flags.
#[inline] #[inline]
pub fn set_ack_flags(&mut self, ack_flags: AckFlags) { pub fn set_ack_flags(&mut self, ack_flags: AckFlags) {
self.sec_header.ack_flags = ack_flags; self.sec_header.ack_flags = ack_flags;
} }
/// Set the source ID.
#[inline] #[inline]
pub fn set_source_id(&mut self, source_id: u16) { pub fn set_source_id(&mut self, source_id: u16) {
self.sec_header.source_id = source_id; self.sec_header.source_id = source_id;
@@ -396,11 +435,13 @@ impl<'app_data> PusTcCreator<'app_data> {
digest.finalize() digest.finalize()
} }
/// Current packet has a checksum.
#[inline] #[inline]
pub fn has_checksum(&self) -> bool { pub fn has_checksum(&self) -> bool {
self.has_checksum self.has_checksum
} }
/// Append the raw PUS byte representation to a provided vector.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize { pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize {
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
@@ -513,9 +554,11 @@ impl PusPacket for PusTcCreator<'_> {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
#[inline] #[inline]
fn service(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
#[inline] #[inline]
fn subservice(&self) -> u8; fn service_type_id(&self) -> u8;
#[inline]
fn message_subtype_id(&self) -> u8;
}); });
#[inline] #[inline]
@@ -541,9 +584,11 @@ impl GenericPusTcSecondaryHeader for PusTcCreator<'_> {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
#[inline] #[inline]
fn service(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
#[inline] #[inline]
fn subservice(&self) -> u8; fn service_type_id(&self) -> u8;
#[inline]
fn message_subtype_id(&self) -> u8;
#[inline] #[inline]
fn source_id(&self) -> u16; fn source_id(&self) -> u16;
#[inline] #[inline]
@@ -638,6 +683,7 @@ impl<'buf> PusTcCreatorWithReservedAppData<'buf> {
}) })
} }
/// Length of the full packet written into a buffer.
#[inline] #[inline]
pub const fn len_written(&self) -> usize { pub const fn len_written(&self) -> usize {
self.full_len self.full_len
@@ -665,6 +711,7 @@ impl<'buf> PusTcCreatorWithReservedAppData<'buf> {
&self.buf[self.app_data_offset..end_index] &self.buf[self.app_data_offset..end_index]
} }
/// Length of the application data.
#[inline] #[inline]
pub fn app_data_len(&self) -> usize { pub fn app_data_len(&self) -> usize {
let mut len = self.full_len - self.app_data_offset; let mut len = self.full_len - self.app_data_offset;
@@ -727,6 +774,7 @@ pub struct PusTcBuilder<'a> {
} }
impl PusTcBuilder<'_> { impl PusTcBuilder<'_> {
/// Constructor.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
sp_header: SpHeader::new( sp_header: SpHeader::new(
@@ -734,12 +782,13 @@ impl PusTcBuilder<'_> {
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0)), PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0)),
0, 0,
), ),
sec_header: PusTcSecondaryHeader::new(0, 0, ACK_ALL, 0), sec_header: PusTcSecondaryHeader::new(MessageTypeId::new(0, 0), ACK_ALL, 0),
app_data: &[], app_data: &[],
has_checksum: true, has_checksum: true,
} }
} }
/// Set the packet ID.
#[inline] #[inline]
pub fn with_packet_id(mut self, mut packet_id: PacketId) -> Self { pub fn with_packet_id(mut self, mut packet_id: PacketId) -> Self {
packet_id.packet_type = PacketType::Tc; packet_id.packet_type = PacketType::Tc;
@@ -747,48 +796,63 @@ impl PusTcBuilder<'_> {
self self
} }
/// Set the packet sequence control.
#[inline] #[inline]
pub fn with_packet_sequence_control(mut self, psc: PacketSequenceControl) -> Self { pub fn with_packet_sequence_control(mut self, psc: PacketSequenceControl) -> Self {
self.sp_header.psc = psc; self.sp_header.psc = psc;
self self
} }
/// Set the sequence count.
#[inline] #[inline]
pub fn with_sequence_count(mut self, seq_count: u14) -> Self { pub fn with_sequence_count(mut self, seq_count: u14) -> Self {
self.sp_header.psc.seq_count = seq_count; self.sp_header.psc.seq_count = seq_count;
self self
} }
/// Set the message type ID.
#[inline] #[inline]
pub fn with_service(mut self, service: u8) -> Self { pub fn with_message_type_id(mut self, message_type: MessageTypeId) -> Self {
self.sec_header.service = service; self.sec_header.message_type_id = message_type;
self self
} }
/// Set the service type ID.
#[inline] #[inline]
pub fn with_subservice(mut self, service: u8) -> Self { pub fn with_service_type_id(mut self, service: u8) -> Self {
self.sec_header.subservice = service; self.sec_header.message_type_id.type_id = service;
self self
} }
/// Set the message subtype ID.
#[inline]
pub fn with_message_subtype_id(mut self, subtype_id: u8) -> Self {
self.sec_header.message_type_id.subtype_id = subtype_id;
self
}
/// Set the source ID.
#[inline] #[inline]
pub fn with_source_id(mut self, source_id: u16) -> Self { pub fn with_source_id(mut self, source_id: u16) -> Self {
self.sec_header.source_id = source_id; self.sec_header.source_id = source_id;
self self
} }
/// Set the acknowledgmenet flags.
#[inline] #[inline]
pub fn with_ack_flags(mut self, ack_flags: AckFlags) -> Self { pub fn with_ack_flags(mut self, ack_flags: AckFlags) -> Self {
self.sec_header.ack_flags = ack_flags; self.sec_header.ack_flags = ack_flags;
self self
} }
/// Set the Application Process ID (APID).
#[inline] #[inline]
pub fn with_apid(mut self, apid: u11) -> Self { pub fn with_apid(mut self, apid: u11) -> Self {
self.sp_header.packet_id.set_apid(apid); self.sp_header.packet_id.set_apid(apid);
self self
} }
/// Enable or disable the checksum.
#[inline] #[inline]
pub fn with_checksum(mut self, has_checksum: bool) -> Self { pub fn with_checksum(mut self, has_checksum: bool) -> Self {
self.has_checksum = has_checksum; self.has_checksum = has_checksum;
@@ -803,6 +867,7 @@ impl Default for PusTcBuilder<'_> {
} }
impl<'a> PusTcBuilder<'a> { impl<'a> PusTcBuilder<'a> {
/// Constructor.
pub fn build(self) -> PusTcCreator<'a> { pub fn build(self) -> PusTcCreator<'a> {
PusTcCreator::new( PusTcCreator::new(
self.sp_header, self.sp_header,
@@ -815,6 +880,7 @@ impl<'a> PusTcBuilder<'a> {
) )
} }
/// Application data slice.
#[inline] #[inline]
pub fn with_app_data(mut self, app_data: &'a [u8]) -> Self { pub fn with_app_data(mut self, app_data: &'a [u8]) -> Self {
self.app_data = app_data; self.app_data = app_data;
@@ -923,26 +989,31 @@ impl<'raw_data> PusTcReader<'raw_data> {
}) })
} }
/// Application data slice.
#[inline] #[inline]
pub fn app_data(&self) -> &[u8] { pub fn app_data(&self) -> &[u8] {
self.user_data() self.user_data()
} }
/// Full raw data slice.
#[inline] #[inline]
pub fn raw_data(&self) -> &[u8] { pub fn raw_data(&self) -> &[u8] {
self.raw_data self.raw_data
} }
/// Length of the packed data.
#[inline] #[inline]
pub fn len_packed(&self) -> usize { pub fn len_packed(&self) -> usize {
self.sp_header.packet_len() self.sp_header.packet_len()
} }
/// Space packet header.
#[inline] #[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
/// CRC16 checksum if present.
#[inline] #[inline]
pub fn crc16(&self) -> Option<u16> { pub fn crc16(&self) -> Option<u16> {
self.crc16 self.crc16
@@ -974,9 +1045,11 @@ impl PusPacket for PusTcReader<'_> {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
#[inline] #[inline]
fn service(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
#[inline] #[inline]
fn subservice(&self) -> u8; fn service_type_id(&self) -> u8;
#[inline]
fn message_subtype_id(&self) -> u8;
}); });
fn has_checksum(&self) -> bool { fn has_checksum(&self) -> bool {
@@ -999,9 +1072,11 @@ impl GenericPusTcSecondaryHeader for PusTcReader<'_> {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
#[inline] #[inline]
fn service(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
#[inline] #[inline]
fn subservice(&self) -> u8; fn service_type_id(&self) -> u8;
#[inline]
fn message_subtype_id(&self) -> u8;
#[inline] #[inline]
fn source_id(&self) -> u16; fn source_id(&self) -> u16;
#[inline] #[inline]
@@ -1042,13 +1117,13 @@ mod tests {
fn base_ping_tc_full_ctor() -> PusTcCreator<'static> { fn base_ping_tc_full_ctor() -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0); let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
let tc_header = PusTcSecondaryHeader::new_simple(17, 1); let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
PusTcCreator::new_no_app_data(sph, tc_header, CreatorConfig::default()) PusTcCreator::new_no_app_data(sph, tc_header, CreatorConfig::default())
} }
fn base_ping_tc_full_ctor_no_checksum() -> PusTcCreator<'static> { fn base_ping_tc_full_ctor_no_checksum() -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0); let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
let tc_header = PusTcSecondaryHeader::new_simple(17, 1); let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
PusTcCreator::new_no_app_data( PusTcCreator::new_no_app_data(
sph, sph,
tc_header, tc_header,
@@ -1061,21 +1136,26 @@ mod tests {
fn base_ping_tc_simple_ctor() -> PusTcCreator<'static> { fn base_ping_tc_simple_ctor() -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0); let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default()) PusTcCreator::new_simple(
sph,
MessageTypeId::new(17, 1),
&[],
CreatorConfig::default(),
)
} }
fn base_ping_tc_with_builder(alt_api: bool) -> PusTcCreator<'static> { fn base_ping_tc_with_builder(alt_api: bool) -> PusTcCreator<'static> {
if alt_api { if alt_api {
return PusTcCreator::builder() return PusTcCreator::builder()
.with_service(17) .with_service_type_id(17)
.with_subservice(1) .with_message_subtype_id(1)
.with_apid(u11::new(0x02)) .with_apid(u11::new(0x02))
.with_sequence_count(u14::new(0x34)) .with_sequence_count(u14::new(0x34))
.build(); .build();
} }
PusTcBuilder::new() PusTcBuilder::new()
.with_service(17) .with_service_type_id(17)
.with_subservice(1) .with_message_subtype_id(1)
.with_apid(u11::new(0x02)) .with_apid(u11::new(0x02))
.with_sequence_count(u14::new(0x34)) .with_sequence_count(u14::new(0x34))
.build() .build()
@@ -1085,8 +1165,7 @@ mod tests {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0); let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
PusTcCreator::new_simple( PusTcCreator::new_simple(
sph, sph,
17, MessageTypeId::new(17, 1),
1,
&[], &[],
CreatorConfig { CreatorConfig {
set_ccsds_len: true, set_ccsds_len: true,
@@ -1102,8 +1181,7 @@ mod tests {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0); let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
PusTcCreator::new_simple( PusTcCreator::new_simple(
sph, sph,
17, MessageTypeId::new(17, 1),
1,
app_data, app_data,
CreatorConfig { CreatorConfig {
set_ccsds_len: true, set_ccsds_len: true,
@@ -1234,7 +1312,7 @@ mod tests {
#[test] #[test]
fn test_deserialization_alt_ctor() { fn test_deserialization_alt_ctor() {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0); let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
let tc_header = PusTcSecondaryHeader::new_simple(17, 1); let tc_header = PusTcSecondaryHeader::new_simple(MessageTypeId::new(17, 1));
let mut test_buf: [u8; 32] = [0; 32]; let mut test_buf: [u8; 32] = [0; 32];
let mut pus_tc = let mut pus_tc =
PusTcCreatorWithReservedAppData::new(&mut test_buf, sph, tc_header, 0, true).unwrap(); PusTcCreatorWithReservedAppData::new(&mut test_buf, sph, tc_header, 0, true).unwrap();
@@ -1289,8 +1367,7 @@ mod tests {
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0); let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
let mut tc = PusTcCreator::new_simple( let mut tc = PusTcCreator::new_simple(
sph, sph,
17, MessageTypeId::new(17, 1),
1,
&[], &[],
CreatorConfig { CreatorConfig {
set_ccsds_len: false, set_ccsds_len: false,
@@ -1517,10 +1594,10 @@ mod tests {
} }
fn verify_test_tc_generic(tc: &(impl PusPacket + GenericPusTcSecondaryHeader)) { fn verify_test_tc_generic(tc: &(impl PusPacket + GenericPusTcSecondaryHeader)) {
assert_eq!(PusPacket::service(tc), 17); assert_eq!(PusPacket::service_type_id(tc), 17);
assert_eq!(GenericPusTcSecondaryHeader::service(tc), 17); assert_eq!(GenericPusTcSecondaryHeader::service_type_id(tc), 17);
assert_eq!(PusPacket::subservice(tc), 1); assert_eq!(PusPacket::message_subtype_id(tc), 1);
assert_eq!(GenericPusTcSecondaryHeader::subservice(tc), 1); assert_eq!(GenericPusTcSecondaryHeader::message_subtype_id(tc), 1);
assert!(tc.sec_header_flag()); assert!(tc.sec_header_flag());
assert_eq!(PusPacket::pus_version(tc).unwrap(), PusC); assert_eq!(PusPacket::pus_version(tc).unwrap(), PusC);
assert_eq!(tc.seq_count().value(), 0x34); assert_eq!(tc.seq_count().value(), 0x34);
@@ -1650,8 +1727,8 @@ mod tests {
SequenceFlags::Unsegmented, SequenceFlags::Unsegmented,
u14::new(0x34), u14::new(0x34),
)) ))
.with_service(17) .with_service_type_id(17)
.with_subservice(1) .with_message_subtype_id(1)
.with_ack_flags(AckFlags::new_with_raw_value(u4::new(0b1010))) .with_ack_flags(AckFlags::new_with_raw_value(u4::new(0b1010)))
.with_source_id(0x2f2f) .with_source_id(0x2f2f)
.with_checksum(false) .with_checksum(false)
@@ -1660,7 +1737,7 @@ mod tests {
assert_eq!(tc.sequence_flags(), SequenceFlags::Unsegmented); assert_eq!(tc.sequence_flags(), SequenceFlags::Unsegmented);
assert_eq!(tc.apid().value(), 0x02); assert_eq!(tc.apid().value(), 0x02);
assert_eq!(tc.packet_type(), PacketType::Tc); assert_eq!(tc.packet_type(), PacketType::Tc);
assert_eq!(tc.service(), 17); assert_eq!(tc.service_type_id(), 17);
assert_eq!(tc.subservice(), 1); assert_eq!(tc.message_subtype_id(), 1);
} }
} }
+79 -15
View File
@@ -15,8 +15,8 @@
//! true //! true
//! ); //! );
//! println!("{:?}", pus_tc); //! println!("{:?}", pus_tc);
//! assert_eq!(pus_tc.service(), 17); //! assert_eq!(pus_tc.service_type_id(), 17);
//! assert_eq!(pus_tc.subservice(), 1); //! assert_eq!(pus_tc.message_subtype_id(), 1);
//! assert_eq!(pus_tc.apid().value(), 0x02); //! assert_eq!(pus_tc.apid().value(), 0x02);
//! //!
//! // Serialize TC into a raw buffer //! // Serialize TC into a raw buffer
@@ -29,15 +29,15 @@
//! //!
//! // Deserialize from the raw byte representation. No source ID, 0 spare bytes. //! // Deserialize from the raw byte representation. No source ID, 0 spare bytes.
//! let pus_tc_deserialized = PusTcReader::new(&test_buf, None, 0).expect("Deserialization failed"); //! let pus_tc_deserialized = PusTcReader::new(&test_buf, None, 0).expect("Deserialization failed");
//! assert_eq!(pus_tc.service(), 17); //! assert_eq!(pus_tc.service_type_id(), 17);
//! assert_eq!(pus_tc.subservice(), 1); //! assert_eq!(pus_tc.message_subtype_id(), 1);
//! assert_eq!(pus_tc.apid().value(), 0x02); //! assert_eq!(pus_tc.apid().value(), 0x02);
//! ``` //! ```
use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE};
use crate::ecss::tc::{AckFlags, ACK_ALL}; use crate::ecss::tc::{AckFlags, ACK_ALL};
use crate::ecss::{ use crate::ecss::{
crc_from_raw_data, sp_header_impls, user_data_from_raw, crc_from_raw_data, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion, verify_crc16_ccitt_false_from_raw_to_pus_error, MessageTypeId, PusError, PusPacket, PusVersion,
WritablePusPacket, WritablePusPacket,
}; };
use crate::util::{UnsignedByteField, UnsignedEnum}; use crate::util::{UnsignedByteField, UnsignedEnum};
@@ -74,33 +74,49 @@ enum AckOpts {
/// Assuming 8 bytes of source ID and 7 bytes of spare. /// Assuming 8 bytes of source ID and 7 bytes of spare.
pub const MAX_SEC_HEADER_LEN: usize = 18; pub const MAX_SEC_HEADER_LEN: usize = 18;
/// Maximum allowed number of space bytes.
pub const MAX_SPARE_BYTES: usize = 7; pub const MAX_SPARE_BYTES: usize = 7;
/// Invalid number of spare bytes which must be between 0 and 7.
#[derive(Debug, Eq, PartialEq, Copy, Clone, thiserror::Error)] #[derive(Debug, Eq, PartialEq, Copy, Clone, thiserror::Error)]
#[error("invalid number of spare bytes, must be between 0 and 7")] #[error("invalid number of spare bytes, must be between 0 and 7")]
pub struct InvalidNumberOfSpareBytesError; pub struct InvalidNumberOfSpareBytesError;
/// PUS version error.
#[derive(Debug, Eq, PartialEq, Copy, Clone, thiserror::Error)] #[derive(Debug, Eq, PartialEq, Copy, Clone, thiserror::Error)]
#[error("invalid version, expected PUS A (1), got {0}")] #[error("invalid version, expected PUS A (1), got {0}")]
pub struct VersionError(pub u8); pub struct VersionError(pub u8);
/// Trait for accessing PUS Telecommand secondary header fields.
pub trait GenericPusTcSecondaryHeader { pub trait GenericPusTcSecondaryHeader {
/// PUS version number.
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
/// Acknowledgment flags.
fn ack_flags(&self) -> AckFlags; fn ack_flags(&self) -> AckFlags;
/// Service type identifier.
fn service(&self) -> u8; fn service(&self) -> u8;
/// Subservice type identifier.
fn subservice(&self) -> u8; fn subservice(&self) -> u8;
/// Source ID.
fn source_id(&self) -> Option<UnsignedByteField>; fn source_id(&self) -> Option<UnsignedByteField>;
/// Number of spare bytes.
fn spare_bytes(&self) -> usize; fn spare_bytes(&self) -> usize;
} }
/// PUS TC secondary header.
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTcSecondaryHeader { pub struct PusTcSecondaryHeader {
/// Service type identifier.
pub service: u8, pub service: u8,
/// Subservice type identifier.
pub subservice: u8, pub subservice: u8,
/// Source ID.
pub source_id: Option<UnsignedByteField>, pub source_id: Option<UnsignedByteField>,
/// Acknowledgment flags.
pub ack: AckFlags, pub ack: AckFlags,
/// PUS version.
pub version: PusVersion, pub version: PusVersion,
spare_bytes: usize, spare_bytes: usize,
} }
@@ -138,6 +154,8 @@ impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
} }
impl PusTcSecondaryHeader { impl PusTcSecondaryHeader {
/// Constructor which allows modifying the service and subservice and sets
/// default values for the other fields.
#[inline] #[inline]
pub fn new_simple(service: u8, subservice: u8) -> Self { pub fn new_simple(service: u8, subservice: u8) -> Self {
PusTcSecondaryHeader { PusTcSecondaryHeader {
@@ -150,6 +168,7 @@ impl PusTcSecondaryHeader {
} }
} }
/// Generic constructor.
#[inline] #[inline]
pub fn new( pub fn new(
service: u8, service: u8,
@@ -176,6 +195,7 @@ impl PusTcSecondaryHeader {
self.spare_bytes = spare_bytes; self.spare_bytes = spare_bytes;
} }
/// Length of the written secondary header in bytes.
pub fn written_len(&self) -> usize { pub fn written_len(&self) -> usize {
let mut len = 3 + self.spare_bytes; let mut len = 3 + self.spare_bytes;
if let Some(source_id) = self.source_id { if let Some(source_id) = self.source_id {
@@ -184,6 +204,7 @@ impl PusTcSecondaryHeader {
len len
} }
/// Converts the secondary header into a vector of bytes.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_vec(&self) -> Result<Vec<u8>, ByteConversionError> { pub fn to_vec(&self) -> Result<Vec<u8>, ByteConversionError> {
let mut buf = alloc::vec![0; self.written_len()]; let mut buf = alloc::vec![0; self.written_len()];
@@ -191,6 +212,7 @@ impl PusTcSecondaryHeader {
Ok(buf) Ok(buf)
} }
/// Write the secondary header to a provided buffer.
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
if buf.len() < self.written_len() { if buf.len() < self.written_len() {
return Err(ByteConversionError::ToSliceTooSmall { return Err(ByteConversionError::ToSliceTooSmall {
@@ -216,6 +238,7 @@ impl PusTcSecondaryHeader {
Ok(current_idx) Ok(current_idx)
} }
/// Read the secondary header from a raw byte slice.
pub fn from_bytes( pub fn from_bytes(
data: &[u8], data: &[u8],
source_id_size: Option<usize>, source_id_size: Option<usize>,
@@ -268,6 +291,7 @@ impl PusTcSecondaryHeader {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTcCreator<'app_data> { pub struct PusTcCreator<'app_data> {
sp_header: SpHeader, sp_header: SpHeader,
/// Secondary header.
pub sec_header: PusTcSecondaryHeader, pub sec_header: PusTcSecondaryHeader,
app_data: &'app_data [u8], app_data: &'app_data [u8],
} }
@@ -323,6 +347,7 @@ impl<'app_data> PusTcCreator<'app_data> {
) )
} }
/// Constructor without application data.
#[inline] #[inline]
pub fn new_no_app_data( pub fn new_no_app_data(
sp_header: SpHeader, sp_header: SpHeader,
@@ -332,26 +357,31 @@ impl<'app_data> PusTcCreator<'app_data> {
Self::new(sp_header, sec_header, &[], set_ccsds_len) Self::new(sp_header, sec_header, &[], set_ccsds_len)
} }
/// Space packet header.
#[inline] #[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
/// Mutable access to the space packet header.
#[inline] #[inline]
pub fn sp_header_mut(&mut self) -> &mut SpHeader { pub fn sp_header_mut(&mut self) -> &mut SpHeader {
&mut self.sp_header &mut self.sp_header
} }
/// Set the acknowledgment flags.
#[inline] #[inline]
pub fn set_ack_field(&mut self, ack: AckFlags) { pub fn set_ack_field(&mut self, ack: AckFlags) {
self.sec_header.ack = ack; self.sec_header.ack = ack;
} }
/// Set the source ID.
#[inline] #[inline]
pub fn set_source_id(&mut self, source_id: Option<UnsignedByteField>) { pub fn set_source_id(&mut self, source_id: Option<UnsignedByteField>) {
self.sec_header.source_id = source_id; self.sec_header.source_id = source_id;
} }
/// Application data slice.
#[inline] #[inline]
pub fn app_data(&'app_data self) -> &'app_data [u8] { pub fn app_data(&'app_data self) -> &'app_data [u8] {
self.user_data() self.user_data()
@@ -397,6 +427,7 @@ impl<'app_data> PusTcCreator<'app_data> {
digest.finalize() digest.finalize()
} }
/// Append the raw PUS byte representation to a provided vector.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize { pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize {
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
@@ -499,11 +530,23 @@ impl PusPacket for PusTcCreator<'_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
#[inline]
fn service(&self) -> u8;
#[inline]
fn subservice(&self) -> u8;
}); });
#[inline]
fn message_type_id(&self) -> MessageTypeId {
MessageTypeId {
type_id: self.service_type_id(),
subtype_id: self.message_subtype_id(),
}
}
#[inline]
fn service_type_id(&self) -> u8 {
self.sec_header.service
}
#[inline]
fn message_subtype_id(&self) -> u8 {
self.sec_header.subservice
}
#[inline] #[inline]
fn user_data(&self) -> &[u8] { fn user_data(&self) -> &[u8] {
@@ -604,6 +647,7 @@ impl<'buf> PusTcCreatorWithReservedAppData<'buf> {
}) })
} }
/// Length of the written TC packet.
#[inline] #[inline]
pub const fn len_written(&self) -> usize { pub const fn len_written(&self) -> usize {
self.full_len self.full_len
@@ -621,6 +665,7 @@ impl<'buf> PusTcCreatorWithReservedAppData<'buf> {
&self.buf[self.app_data_offset..self.full_len - 2] &self.buf[self.app_data_offset..self.full_len - 2]
} }
/// Application data length.
#[inline] #[inline]
pub fn app_data_len(&self) -> usize { pub fn app_data_len(&self) -> usize {
self.full_len - 2 - self.app_data_offset self.full_len - 2 - self.app_data_offset
@@ -745,26 +790,31 @@ impl<'raw_data> PusTcReader<'raw_data> {
}) })
} }
/// Application data slice.
#[inline] #[inline]
pub fn app_data(&self) -> &[u8] { pub fn app_data(&self) -> &[u8] {
self.user_data() self.user_data()
} }
/// Full raw data slice.
#[inline] #[inline]
pub fn raw_data(&self) -> &[u8] { pub fn raw_data(&self) -> &[u8] {
self.raw_data self.raw_data
} }
/// Length of the packed PUS TC packet in bytes.
#[inline] #[inline]
pub fn len_packed(&self) -> usize { pub fn len_packed(&self) -> usize {
self.sp_header.packet_len() self.sp_header.packet_len()
} }
/// Space packet header.
#[inline] #[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
/// CRC16 checksum value.
#[inline] #[inline]
pub fn crc16(&self) -> u16 { pub fn crc16(&self) -> u16 {
self.crc16 self.crc16
@@ -793,14 +843,28 @@ impl CcsdsPacket for PusTcReader<'_> {
impl PusPacket for PusTcReader<'_> { impl PusPacket for PusTcReader<'_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline]
fn service(&self) -> u8;
#[inline]
fn subservice(&self) -> u8;
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
}); });
#[inline]
fn message_type_id(&self) -> MessageTypeId {
MessageTypeId {
type_id: self.service_type_id(),
subtype_id: self.message_subtype_id(),
}
}
#[inline]
fn service_type_id(&self) -> u8 {
self.sec_header.service
}
#[inline]
fn message_subtype_id(&self) -> u8 {
self.sec_header.subservice
}
#[inline] #[inline]
fn has_checksum(&self) -> bool { fn has_checksum(&self) -> bool {
true true
@@ -1229,9 +1293,9 @@ mod tests {
} }
fn verify_test_tc_generic(tc: &(impl PusPacket + GenericPusTcSecondaryHeader)) { fn verify_test_tc_generic(tc: &(impl PusPacket + GenericPusTcSecondaryHeader)) {
assert_eq!(PusPacket::service(tc), 17); assert_eq!(PusPacket::service_type_id(tc), 17);
assert_eq!(GenericPusTcSecondaryHeader::service(tc), 17); assert_eq!(GenericPusTcSecondaryHeader::service(tc), 17);
assert_eq!(PusPacket::subservice(tc), 1); assert_eq!(PusPacket::message_subtype_id(tc), 1);
assert_eq!(GenericPusTcSecondaryHeader::subservice(tc), 1); assert_eq!(GenericPusTcSecondaryHeader::subservice(tc), 1);
assert!(tc.sec_header_flag()); assert!(tc.sec_header_flag());
assert_eq!(PusPacket::pus_version(tc).unwrap(), PusVersion::PusA); assert_eq!(PusPacket::pus_version(tc).unwrap(), PusVersion::PusA);
+263 -124
View File
@@ -6,7 +6,7 @@
//! ```rust //! ```rust
//! use spacepackets::time::cds::CdsTime; //! use spacepackets::time::cds::CdsTime;
//! use spacepackets::SpHeader; //! use spacepackets::SpHeader;
//! use spacepackets::ecss::tm::{PusTmCreator, PusTmReader, PusTmSecondaryHeader, CreatorConfig}; //! use spacepackets::ecss::tm::{MessageTypeId, PusTmCreator, PusTmReader, PusTmSecondaryHeader, CreatorConfig};
//! use arbitrary_int::u11; //! use arbitrary_int::u11;
//! //!
//! let mut time_buf: [u8; 7] = [0; 7]; //! let mut time_buf: [u8; 7] = [0; 7];
@@ -17,12 +17,12 @@
//! // Create a ping telemetry with no user source data //! // Create a ping telemetry with no user source data
//! let ping_tm = PusTmCreator::new_no_source_data( //! let ping_tm = PusTmCreator::new_no_source_data(
//! SpHeader::new_from_apid(u11::new(0x02)), //! SpHeader::new_from_apid(u11::new(0x02)),
//! PusTmSecondaryHeader::new_simple(17, 2, &time_buf), //! PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), &time_buf),
//! CreatorConfig::default() //! CreatorConfig::default()
//! ); //! );
//! println!("{:?}", ping_tm); //! println!("{:?}", ping_tm);
//! assert_eq!(ping_tm.service(), 17); //! assert_eq!(ping_tm.service_type_id(), 17);
//! assert_eq!(ping_tm.subservice(), 2); //! assert_eq!(ping_tm.message_subtype_id(), 2);
//! assert_eq!(ping_tm.apid().value(), 0x02); //! assert_eq!(ping_tm.apid().value(), 0x02);
//! //!
//! // Serialize TM into a raw buffer //! // Serialize TM into a raw buffer
@@ -36,27 +36,27 @@
//! // Deserialize from the raw byte representation //! // Deserialize from the raw byte representation
//! let ping_tm_reader = PusTmReader::new(&test_buf, 7).expect("Deserialization failed"); //! let ping_tm_reader = PusTmReader::new(&test_buf, 7).expect("Deserialization failed");
//! assert_eq!(written_size, ping_tm_reader.packet_len()); //! assert_eq!(written_size, ping_tm_reader.packet_len());
//! assert_eq!(ping_tm_reader.service(), 17); //! assert_eq!(ping_tm_reader.service_type_id(), 17);
//! assert_eq!(ping_tm_reader.subservice(), 2); //! assert_eq!(ping_tm_reader.message_subtype_id(), 2);
//! assert_eq!(ping_tm_reader.apid().value(), 0x02); //! assert_eq!(ping_tm_reader.apid().value(), 0x02);
//! assert_eq!(ping_tm_reader.timestamp(), &time_buf); //! assert_eq!(ping_tm_reader.timestamp(), &time_buf);
//! //!
//! // Alternative builder API //! // Alternative builder API
//! let pus_tm_by_builder = PusTmCreator::builder() //! let pus_tm_by_builder = PusTmCreator::builder()
//! .with_service(17) //! .with_service_type_id(17)
//! .with_subservice(2) //! .with_message_subtype_id(2)
//! .with_apid(u11::new(0x02)) //! .with_apid(u11::new(0x02))
//! .with_timestamp(&time_buf) //! .with_timestamp(&time_buf)
//! .build(); //! .build();
//! assert_eq!(pus_tm_by_builder, ping_tm); //! assert_eq!(pus_tm_by_builder, ping_tm);
//! ``` //! ```
use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE};
pub use crate::ecss::CreatorConfig;
use crate::ecss::{ use crate::ecss::{
calc_pus_crc16, crc_from_raw_data, sp_header_impls, user_data_from_raw, calc_pus_crc16, crc_from_raw_data, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion, verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, PusVersion,
WritablePusPacket, WritablePusPacket,
}; };
pub use crate::ecss::{CreatorConfig, MessageTypeId};
use crate::{ use crate::{
ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl, PacketType, SequenceFlags, ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl, PacketType, SequenceFlags,
SpHeader, CCSDS_HEADER_LEN, MAX_APID, SpHeader, CCSDS_HEADER_LEN, MAX_APID,
@@ -78,40 +78,63 @@ use self::zc::PusTmSecHeaderWithoutTimestamp;
use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table; use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table;
/// Marker trait for PUS telemetry packets.
pub trait IsPusTelemetry {} pub trait IsPusTelemetry {}
/// Length without timestamp /// Length without timestamp
pub const PUS_TM_MIN_SEC_HEADER_LEN: usize = 7; pub const PUS_TM_MIN_SEC_HEADER_LEN: usize = 7;
/// Minimum length of a PUS telemetry packet without source data and timestamp
pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize = CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN; pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize = CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN;
/// Generic properties of a PUS TM secondary header.
pub trait GenericPusTmSecondaryHeader { pub trait GenericPusTmSecondaryHeader {
/// PUS version.
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
/// Spacecraft time reference status.
fn sc_time_ref_status(&self) -> u4; fn sc_time_ref_status(&self) -> u4;
fn service(&self) -> u8; /// Message type ID.
fn subservice(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
fn msg_counter(&self) -> u16; /// Message counter for the message type.
fn msg_type_counter(&self) -> u16;
/// Destination ID.
fn dest_id(&self) -> u16; fn dest_id(&self) -> u16;
/// Service type ID.
#[inline]
fn service_type_id(&self) -> u8 {
self.message_type_id().type_id
}
/// Message subtype ID.
#[inline]
fn message_subtype_id(&self) -> u8 {
self.message_type_id().subtype_id
}
} }
/// [zerocopy] support module.
pub mod zc { pub mod zc {
use super::GenericPusTmSecondaryHeader; use super::GenericPusTmSecondaryHeader;
use crate::ecss::{PusError, PusVersion}; use crate::ecss::{MessageTypeId, PusError, PusVersion};
use arbitrary_int::{traits::Integer as _, u4}; use arbitrary_int::{traits::Integer as _, u4};
use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16}; use zerocopy::{FromBytes, Immutable, IntoBytes, NetworkEndian, Unaligned, U16};
/// PUS TM secondary header without a timestamp.
#[derive(FromBytes, IntoBytes, Immutable, Unaligned)] #[derive(FromBytes, IntoBytes, Immutable, Unaligned)]
#[repr(C)] #[repr(C)]
pub struct PusTmSecHeaderWithoutTimestamp { pub struct PusTmSecHeaderWithoutTimestamp {
pus_version_and_sc_time_ref_status: u8, pus_version_and_sc_time_ref_status: u8,
service: u8, service_type_id: u8,
subservice: u8, message_subtype_id: u8,
msg_counter: U16<NetworkEndian>, msg_counter: U16<NetworkEndian>,
dest_id: U16<NetworkEndian>, dest_id: U16<NetworkEndian>,
} }
pub struct PusTmSecHeader<'slice> { /// PUS TM secondary header with timestamp.
pub struct PusTmSecHeader<'time> {
pub(crate) zc_header: PusTmSecHeaderWithoutTimestamp, pub(crate) zc_header: PusTmSecHeaderWithoutTimestamp,
pub(crate) timestamp: &'slice [u8], pub(crate) timestamp: &'time [u8],
} }
impl TryFrom<crate::ecss::tm::PusTmSecondaryHeader<'_>> for PusTmSecHeaderWithoutTimestamp { impl TryFrom<crate::ecss::tm::PusTmSecondaryHeader<'_>> for PusTmSecHeaderWithoutTimestamp {
@@ -125,8 +148,8 @@ pub mod zc {
Ok(PusTmSecHeaderWithoutTimestamp { Ok(PusTmSecHeaderWithoutTimestamp {
pus_version_and_sc_time_ref_status: ((header.pus_version as u8) << 4) pus_version_and_sc_time_ref_status: ((header.pus_version as u8) << 4)
| header.sc_time_ref_status.as_u8(), | header.sc_time_ref_status.as_u8(),
service: header.service, service_type_id: header.service_type_id(),
subservice: header.subservice, message_subtype_id: header.message_subtype_id(),
msg_counter: U16::from(header.msg_counter), msg_counter: U16::from(header.msg_counter),
dest_id: U16::from(header.dest_id), dest_id: U16::from(header.dest_id),
}) })
@@ -146,18 +169,27 @@ pub mod zc {
u4::new(self.pus_version_and_sc_time_ref_status & 0b1111) u4::new(self.pus_version_and_sc_time_ref_status & 0b1111)
} }
/// Message type ID.
#[inline] #[inline]
fn service(&self) -> u8 { fn message_type_id(&self) -> MessageTypeId {
self.service MessageTypeId {
type_id: self.service_type_id,
subtype_id: self.message_subtype_id,
}
} }
#[inline] #[inline]
fn subservice(&self) -> u8 { fn service_type_id(&self) -> u8 {
self.subservice self.service_type_id
} }
#[inline] #[inline]
fn msg_counter(&self) -> u16 { fn message_subtype_id(&self) -> u8 {
self.message_subtype_id
}
#[inline]
fn msg_type_counter(&self) -> u16 {
self.msg_counter.get() self.msg_counter.get()
} }
@@ -168,35 +200,42 @@ pub mod zc {
} }
} }
/// PUS TM secondary header.
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTmSecondaryHeader<'stamp> { pub struct PusTmSecondaryHeader<'stamp> {
pus_version: PusVersion, pus_version: PusVersion,
/// Spacecraft time reference status.
pub sc_time_ref_status: u4, pub sc_time_ref_status: u4,
pub service: u8, /// Message type ID.
pub subservice: u8, pub message_type_id: MessageTypeId,
/// Message counter for the message service type ID.
pub msg_counter: u16, pub msg_counter: u16,
/// Destination ID.
pub dest_id: u16, pub dest_id: u16,
/// Raw timestamp slice.
pub timestamp: &'stamp [u8], pub timestamp: &'stamp [u8],
} }
impl<'stamp> PusTmSecondaryHeader<'stamp> { impl<'stamp> PusTmSecondaryHeader<'stamp> {
/// Constructor where only the message type ID and timestamp are specified. The other fields
/// are set to default values.
#[inline] #[inline]
pub fn new_simple(service: u8, subservice: u8, timestamp: &'stamp [u8]) -> Self { pub fn new_simple(message_type_id: MessageTypeId, timestamp: &'stamp [u8]) -> Self {
Self::new(service, subservice, 0, 0, timestamp) Self::new(message_type_id, 0, 0, timestamp)
} }
/// Like [Self::new_simple] but without a timestamp. /// Like [Self::new_simple] but without a timestamp.
#[inline] #[inline]
pub fn new_simple_no_timestamp(service: u8, subservice: u8) -> Self { pub fn new_simple_no_timestamp(message_type_id: MessageTypeId) -> Self {
Self::new(service, subservice, 0, 0, &[]) Self::new(message_type_id, 0, 0, &[])
} }
/// Generic constructor.
#[inline] #[inline]
pub fn new( pub fn new(
service: u8, message_type_id: MessageTypeId,
subservice: u8,
msg_counter: u16, msg_counter: u16,
dest_id: u16, dest_id: u16,
timestamp: &'stamp [u8], timestamp: &'stamp [u8],
@@ -204,8 +243,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
PusTmSecondaryHeader { PusTmSecondaryHeader {
pus_version: PusVersion::PusC, pus_version: PusVersion::PusC,
sc_time_ref_status: u4::new(0), sc_time_ref_status: u4::new(0),
service, message_type_id,
subservice,
msg_counter, msg_counter,
dest_id, dest_id,
timestamp, timestamp,
@@ -225,17 +263,12 @@ impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
} }
#[inline] #[inline]
fn service(&self) -> u8 { fn message_type_id(&self) -> MessageTypeId {
self.service self.message_type_id
} }
#[inline] #[inline]
fn subservice(&self) -> u8 { fn msg_type_counter(&self) -> u16 {
self.subservice
}
#[inline]
fn msg_counter(&self) -> u16 {
self.msg_counter self.msg_counter
} }
@@ -257,9 +290,8 @@ impl<'slice> TryFrom<zc::PusTmSecHeader<'slice>> for PusTmSecondaryHeader<'slice
Ok(PusTmSecondaryHeader { Ok(PusTmSecondaryHeader {
pus_version: version.unwrap(), pus_version: version.unwrap(),
sc_time_ref_status: sec_header.zc_header.sc_time_ref_status(), sc_time_ref_status: sec_header.zc_header.sc_time_ref_status(),
service: sec_header.zc_header.service(), message_type_id: sec_header.zc_header.message_type_id(),
subservice: sec_header.zc_header.subservice(), msg_counter: sec_header.zc_header.msg_type_counter(),
msg_counter: sec_header.zc_header.msg_counter(),
dest_id: sec_header.zc_header.dest_id(), dest_id: sec_header.zc_header.dest_id(),
timestamp: sec_header.timestamp, timestamp: sec_header.timestamp,
}) })
@@ -283,7 +315,9 @@ impl<'slice> TryFrom<zc::PusTmSecHeader<'slice>> for PusTmSecondaryHeader<'slice
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTmCreator<'time, 'src_data> { pub struct PusTmCreator<'time, 'src_data> {
/// Space packet header.
pub sp_header: SpHeader, pub sp_header: SpHeader,
/// Secondary header.
#[cfg_attr(feature = "serde", serde(borrow))] #[cfg_attr(feature = "serde", serde(borrow))]
pub sec_header: PusTmSecondaryHeader<'time>, pub sec_header: PusTmSecondaryHeader<'time>,
source_data: &'src_data [u8], source_data: &'src_data [u8],
@@ -326,11 +360,12 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
pus_tm pus_tm
} }
/// Simple constructor which builds the [PusTmSecondaryHeader] internally from the
/// provided arguments.
#[inline] #[inline]
pub fn new_simple( pub fn new_simple(
sp_header: SpHeader, sp_header: SpHeader,
service: u8, message_type_id: MessageTypeId,
subservice: u8,
time_provider: &impl TimeWriter, time_provider: &impl TimeWriter,
stamp_buf: &'time mut [u8], stamp_buf: &'time mut [u8],
source_data: &'src_data [u8], source_data: &'src_data [u8],
@@ -338,10 +373,11 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
) -> Result<Self, TimestampError> { ) -> Result<Self, TimestampError> {
let stamp_size = time_provider.write_to_bytes(stamp_buf)?; let stamp_size = time_provider.write_to_bytes(stamp_buf)?;
let sec_header = let sec_header =
PusTmSecondaryHeader::new_simple(service, subservice, &stamp_buf[0..stamp_size]); PusTmSecondaryHeader::new_simple(message_type_id, &stamp_buf[0..stamp_size]);
Ok(Self::new(sp_header, sec_header, source_data, packet_config)) Ok(Self::new(sp_header, sec_header, source_data, packet_config))
} }
/// Constructor for PUS TM packets without source data.
#[inline] #[inline]
pub fn new_no_source_data( pub fn new_no_source_data(
sp_header: SpHeader, sp_header: SpHeader,
@@ -351,50 +387,60 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
Self::new(sp_header, sec_header, &[], packet_config) Self::new(sp_header, sec_header, &[], packet_config)
} }
/// Builder API.
pub fn builder() -> PusTmBuilder<'time, 'src_data> { pub fn builder() -> PusTmBuilder<'time, 'src_data> {
PusTmBuilder::new() PusTmBuilder::new()
} }
/// Does the packet have a checksum?
#[inline] #[inline]
pub fn has_checksum(&self) -> bool { pub fn has_checksum(&self) -> bool {
self.has_checksum self.has_checksum
} }
/// Raw timestamp slice.
#[inline] #[inline]
pub fn timestamp(&self) -> &[u8] { pub fn timestamp(&self) -> &[u8] {
self.sec_header.timestamp self.sec_header.timestamp
} }
/// Raw source data slice.
#[inline] #[inline]
pub fn source_data(&self) -> &[u8] { pub fn source_data(&self) -> &[u8] {
self.source_data self.source_data
} }
/// Servcie type ID.
#[inline] #[inline]
pub fn service(&self) -> u8 { pub fn service_type_id(&self) -> u8 {
self.sec_header.service self.sec_header.service_type_id()
} }
/// Message subtype ID.
#[inline] #[inline]
pub fn subservice(&self) -> u8 { pub fn message_subtype_id(&self) -> u8 {
self.sec_header.subservice self.sec_header.message_subtype_id()
} }
/// Application Process Identifier (APID).
#[inline] #[inline]
pub fn apid(&self) -> u11 { pub fn apid(&self) -> u11 {
self.sp_header.packet_id.apid self.sp_header.packet_id.apid
} }
/// Set the destination ID.
#[inline] #[inline]
pub fn set_dest_id(&mut self, dest_id: u16) { pub fn set_dest_id(&mut self, dest_id: u16) {
self.sec_header.dest_id = dest_id; self.sec_header.dest_id = dest_id;
} }
/// Set the message counter for the service type ID.
#[inline] #[inline]
pub fn set_msg_counter(&mut self, msg_counter: u16) { pub fn set_msg_counter(&mut self, msg_counter: u16) {
self.sec_header.msg_counter = msg_counter self.sec_header.msg_counter = msg_counter
} }
/// Set the spacecraft time reference status.
#[inline] #[inline]
pub fn set_sc_time_ref_status(&mut self, sc_time_ref_status: u4) { pub fn set_sc_time_ref_status(&mut self, sc_time_ref_status: u4) {
self.sec_header.sc_time_ref_status = sc_time_ref_status; self.sec_header.sc_time_ref_status = sc_time_ref_status;
@@ -567,9 +613,7 @@ impl PusPacket for PusTmCreator<'_, '_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline] #[inline]
fn service(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
#[inline]
fn subservice(&self) -> u8;
}); });
#[inline] #[inline]
@@ -590,14 +634,21 @@ impl GenericPusTmSecondaryHeader for PusTmCreator<'_, '_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
/// Message type ID.
fn message_type_id(&self) -> MessageTypeId;
/// Service type ID.
#[inline] #[inline]
fn service(&self) -> u8; fn service_type_id(&self) -> u8;
/// Message subtype ID.
#[inline] #[inline]
fn subservice(&self) -> u8; fn message_subtype_id(&self) -> u8;
#[inline] #[inline]
fn dest_id(&self) -> u16; fn dest_id(&self) -> u16;
#[inline] #[inline]
fn msg_counter(&self) -> u16; fn msg_type_counter(&self) -> u16;
#[inline] #[inline]
fn sc_time_ref_status(&self) -> u4; fn sc_time_ref_status(&self) -> u4;
}); });
@@ -605,6 +656,7 @@ impl GenericPusTmSecondaryHeader for PusTmCreator<'_, '_> {
impl IsPusTelemetry for PusTmCreator<'_, '_> {} impl IsPusTelemetry for PusTmCreator<'_, '_> {}
/// PUS TM bulder API.
#[derive(Debug)] #[derive(Debug)]
pub struct PusTmBuilder<'time, 'src_data> { pub struct PusTmBuilder<'time, 'src_data> {
sp_header: SpHeader, sp_header: SpHeader,
@@ -620,6 +672,7 @@ impl Default for PusTmBuilder<'_, '_> {
} }
impl PusTmBuilder<'_, '_> { impl PusTmBuilder<'_, '_> {
/// Constructor.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
sp_header: SpHeader::new( sp_header: SpHeader::new(
@@ -627,18 +680,28 @@ impl PusTmBuilder<'_, '_> {
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0)), PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0)),
0, 0,
), ),
sec_header: PusTmSecondaryHeader::new(0, 0, 0, 0, &[]), sec_header: PusTmSecondaryHeader::new(
MessageTypeId {
type_id: 0,
subtype_id: 0,
},
0,
0,
&[],
),
source_data: &[], source_data: &[],
has_checksum: true, has_checksum: true,
} }
} }
/// Set the application process identifier (APID).
#[inline] #[inline]
pub fn with_apid(mut self, apid: u11) -> Self { pub fn with_apid(mut self, apid: u11) -> Self {
self.sp_header.packet_id.set_apid(apid); self.sp_header.packet_id.set_apid(apid);
self self
} }
/// Set the packet ID.
#[inline] #[inline]
pub fn with_packet_id(mut self, mut packet_id: PacketId) -> Self { pub fn with_packet_id(mut self, mut packet_id: PacketId) -> Self {
packet_id.packet_type = PacketType::Tc; packet_id.packet_type = PacketType::Tc;
@@ -646,48 +709,63 @@ impl PusTmBuilder<'_, '_> {
self self
} }
/// Set the packet sequence control.
#[inline] #[inline]
pub fn with_packet_sequence_control(mut self, psc: PacketSequenceControl) -> Self { pub fn with_packet_sequence_control(mut self, psc: PacketSequenceControl) -> Self {
self.sp_header.psc = psc; self.sp_header.psc = psc;
self self
} }
/// Set the sequence count.
#[inline] #[inline]
pub fn with_sequence_count(mut self, seq_count: u14) -> Self { pub fn with_sequence_count(mut self, seq_count: u14) -> Self {
self.sp_header.psc.seq_count = seq_count; self.sp_header.psc.seq_count = seq_count;
self self
} }
/// Set the message type ID.
#[inline] #[inline]
pub fn with_service(mut self, service: u8) -> Self { pub fn with_message_type_id(mut self, message_type_id: MessageTypeId) -> Self {
self.sec_header.service = service; self.sec_header.message_type_id = message_type_id;
self self
} }
/// Set the service type ID.
#[inline] #[inline]
pub fn with_subservice(mut self, service: u8) -> Self { pub fn with_service_type_id(mut self, type_id: u8) -> Self {
self.sec_header.subservice = service; self.sec_header.message_type_id.type_id = type_id;
self self
} }
/// Set the message subtype ID.
#[inline]
pub fn with_message_subtype_id(mut self, subtype_id: u8) -> Self {
self.sec_header.message_type_id.subtype_id = subtype_id;
self
}
/// Set the destination ID.
#[inline] #[inline]
pub fn with_dest_id(mut self, dest_id: u16) -> Self { pub fn with_dest_id(mut self, dest_id: u16) -> Self {
self.sec_header.dest_id = dest_id; self.sec_header.dest_id = dest_id;
self self
} }
/// Set the message counter for the service type ID.
#[inline] #[inline]
pub fn with_msg_counter(mut self, msg_counter: u16) -> Self { pub fn with_msg_counter(mut self, msg_counter: u16) -> Self {
self.sec_header.msg_counter = msg_counter; self.sec_header.msg_counter = msg_counter;
self self
} }
/// Set the spacecraft time reference status.
#[inline] #[inline]
pub fn with_sc_time_ref_status(mut self, sc_time_ref_status: u4) -> Self { pub fn with_sc_time_ref_status(mut self, sc_time_ref_status: u4) -> Self {
self.sec_header.sc_time_ref_status = sc_time_ref_status; self.sec_header.sc_time_ref_status = sc_time_ref_status;
self self
} }
/// Enable or disable checksum generation.
#[inline] #[inline]
pub fn with_checksum(mut self, has_checksum: bool) -> Self { pub fn with_checksum(mut self, has_checksum: bool) -> Self {
self.has_checksum = has_checksum; self.has_checksum = has_checksum;
@@ -696,6 +774,7 @@ impl PusTmBuilder<'_, '_> {
} }
impl<'src_data> PusTmBuilder<'_, 'src_data> { impl<'src_data> PusTmBuilder<'_, 'src_data> {
/// Set the source data.
#[inline] #[inline]
pub fn with_source_data(mut self, source_data: &'src_data [u8]) -> Self { pub fn with_source_data(mut self, source_data: &'src_data [u8]) -> Self {
self.source_data = source_data; self.source_data = source_data;
@@ -704,6 +783,7 @@ impl<'src_data> PusTmBuilder<'_, 'src_data> {
} }
impl<'time> PusTmBuilder<'time, '_> { impl<'time> PusTmBuilder<'time, '_> {
/// Set the timestamp.
#[inline] #[inline]
pub fn with_timestamp(mut self, timestamp: &'time [u8]) -> Self { pub fn with_timestamp(mut self, timestamp: &'time [u8]) -> Self {
self.sec_header.timestamp = timestamp; self.sec_header.timestamp = timestamp;
@@ -712,6 +792,7 @@ impl<'time> PusTmBuilder<'time, '_> {
} }
impl<'time, 'src_data> PusTmBuilder<'time, 'src_data> { impl<'time, 'src_data> PusTmBuilder<'time, 'src_data> {
/// Constructor.
pub fn build(self) -> PusTmCreator<'time, 'src_data> { pub fn build(self) -> PusTmCreator<'time, 'src_data> {
PusTmCreator::new( PusTmCreator::new(
self.sp_header, self.sp_header,
@@ -812,6 +893,7 @@ impl<'buf> PusTmCreatorWithReservedSourceData<'buf> {
}) })
} }
/// Length of the full packet written to the buffer.
#[inline] #[inline]
pub const fn len_written(&self) -> usize { pub const fn len_written(&self) -> usize {
self.full_len self.full_len
@@ -837,6 +919,7 @@ impl<'buf> PusTmCreatorWithReservedSourceData<'buf> {
} }
} }
/// Length of the source data.
#[inline] #[inline]
pub fn source_data_len(&self) -> usize { pub fn source_data_len(&self) -> usize {
let mut len = self.full_len - self.source_data_offset; let mut len = self.full_len - self.source_data_offset;
@@ -886,11 +969,16 @@ impl<'buf> PusTmCreatorWithReservedSourceData<'buf> {
} }
} }
/// Configuration options for the [PusTmReader].
///
/// This includes managed parameters which can not be deduced from the raw byte slice itself.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ReaderConfig { pub struct ReaderConfig {
/// Expected timestamp length.
pub timestamp_len: usize, pub timestamp_len: usize,
/// Does the packet have a checksum?
pub has_checksum: bool, pub has_checksum: bool,
} }
@@ -910,7 +998,9 @@ pub struct ReaderConfig {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTmReader<'raw_data> { pub struct PusTmReader<'raw_data> {
/// Space packet header.
pub sp_header: SpHeader, pub sp_header: SpHeader,
/// PUS TM secondary header.
pub sec_header: PusTmSecondaryHeader<'raw_data>, pub sec_header: PusTmSecondaryHeader<'raw_data>,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "serde", serde(skip))]
raw_data: &'raw_data [u8], raw_data: &'raw_data [u8],
@@ -968,6 +1058,7 @@ impl<'raw_data> PusTmReader<'raw_data> {
) )
} }
/// Constructor which does not perform checksum verification.
pub fn new_no_checksum_verification( pub fn new_no_checksum_verification(
slice: &'raw_data [u8], slice: &'raw_data [u8],
reader_config: ReaderConfig, reader_config: ReaderConfig,
@@ -1017,7 +1108,7 @@ impl<'raw_data> PusTmReader<'raw_data> {
} }
Ok(Self { Ok(Self {
sp_header, sp_header,
sec_header: PusTmSecondaryHeader::try_from(zc_sec_header_wrapper).unwrap(), sec_header: PusTmSecondaryHeader::try_from(zc_sec_header_wrapper)?,
raw_data, raw_data,
source_data: user_data_from_raw( source_data: user_data_from_raw(
current_idx, current_idx,
@@ -1029,41 +1120,49 @@ impl<'raw_data> PusTmReader<'raw_data> {
}) })
} }
/// Length of the full packed PUS TM packet.
#[inline] #[inline]
pub fn len_packed(&self) -> usize { pub fn len_packed(&self) -> usize {
self.sp_header.packet_len() self.sp_header.packet_len()
} }
/// Source data slice.
#[inline] #[inline]
pub fn source_data(&self) -> &[u8] { pub fn source_data(&self) -> &[u8] {
self.user_data() self.user_data()
} }
/// Service type ID.
#[inline] #[inline]
pub fn service(&self) -> u8 { pub fn service_type_id(&self) -> u8 {
self.sec_header.service self.sec_header.service_type_id()
} }
/// Message subtype ID.
#[inline] #[inline]
pub fn subservice(&self) -> u8 { pub fn message_subtype_id(&self) -> u8 {
self.sec_header.subservice self.sec_header.message_subtype_id()
} }
/// Full packet length.
#[inline] #[inline]
pub fn packet_len(&self) -> usize { pub fn packet_len(&self) -> usize {
self.sp_header.packet_len() self.sp_header.packet_len()
} }
/// Application Process Identifier (APID).
#[inline] #[inline]
pub fn apid(&self) -> u11 { pub fn apid(&self) -> u11 {
self.sp_header.packet_id.apid self.sp_header.packet_id.apid
} }
/// Raw timestamp slice.
#[inline] #[inline]
pub fn timestamp(&self) -> &[u8] { pub fn timestamp(&self) -> &[u8] {
self.sec_header.timestamp self.sec_header.timestamp
} }
/// Does the packet have a checksum?
#[inline] #[inline]
pub fn checksum(&self) -> Option<u16> { pub fn checksum(&self) -> Option<u16> {
self.checksum self.checksum
@@ -1102,10 +1201,15 @@ impl PusPacket for PusTmReader<'_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
#[inline] #[inline]
fn service(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
#[inline] #[inline]
fn subservice(&self) -> u8; fn service_type_id(&self) -> u8;
#[inline]
fn message_subtype_id(&self) -> u8;
}); });
#[inline] #[inline]
@@ -1129,13 +1233,15 @@ impl GenericPusTmSecondaryHeader for PusTmReader<'_> {
#[inline] #[inline]
fn pus_version(&self) -> Result<PusVersion, u4>; fn pus_version(&self) -> Result<PusVersion, u4>;
#[inline] #[inline]
fn service(&self) -> u8; fn message_type_id(&self) -> MessageTypeId;
#[inline] #[inline]
fn subservice(&self) -> u8; fn service_type_id(&self) -> u8;
#[inline]
fn message_subtype_id(&self) -> u8;
#[inline] #[inline]
fn dest_id(&self) -> u16; fn dest_id(&self) -> u16;
#[inline] #[inline]
fn msg_counter(&self) -> u16; fn msg_type_counter(&self) -> u16;
#[inline] #[inline]
fn sc_time_ref_status(&self) -> u4; fn sc_time_ref_status(&self) -> u4;
}); });
@@ -1202,6 +1308,7 @@ impl<'raw> PusTmZeroCopyWriter<'raw> {
Some(writer) Some(writer)
} }
/// Set the application process identifier (APID).
#[inline] #[inline]
pub fn set_apid(&mut self, apid: u11) { pub fn set_apid(&mut self, apid: u11) {
// Clear APID part of the raw packet ID // Clear APID part of the raw packet ID
@@ -1241,6 +1348,7 @@ impl<'raw> PusTmZeroCopyWriter<'raw> {
.unwrap() .unwrap()
} }
/// Set the sequence count in the CCSDS packet header.
#[inline] #[inline]
pub fn set_seq_count(&mut self, seq_count: u14) { pub fn set_seq_count(&mut self, seq_count: u14) {
let new_psc = (u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000) let new_psc = (u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000)
@@ -1257,6 +1365,24 @@ impl<'raw> PusTmZeroCopyWriter<'raw> {
self.raw_tm[slice_len - 2..].copy_from_slice(&crc16.to_be_bytes()); self.raw_tm[slice_len - 2..].copy_from_slice(&crc16.to_be_bytes());
} }
} }
#[inline]
fn service_type_id(&self) -> u8 {
self.raw_tm[7]
}
#[inline]
fn message_subtype_id(&self) -> u8 {
self.raw_tm[8]
}
#[inline]
fn message_type_id(&self) -> MessageTypeId {
MessageTypeId {
type_id: self.service_type_id(),
subtype_id: self.message_subtype_id(),
}
}
} }
impl CcsdsPacket for PusTmZeroCopyWriter<'_> { impl CcsdsPacket for PusTmZeroCopyWriter<'_> {
@@ -1287,19 +1413,24 @@ impl PusPacket for PusTmZeroCopyWriter<'_> {
self.sec_header_without_timestamp().pus_version() self.sec_header_without_timestamp().pus_version()
} }
#[inline]
fn service(&self) -> u8 {
self.raw_tm[7]
}
#[inline] #[inline]
fn has_checksum(&self) -> bool { fn has_checksum(&self) -> bool {
self.has_checksum self.has_checksum
} }
#[inline] #[inline]
fn subservice(&self) -> u8 { fn service_type_id(&self) -> u8 {
self.raw_tm[8] self.service_type_id()
}
#[inline]
fn message_subtype_id(&self) -> u8 {
self.message_subtype_id()
}
#[inline]
fn message_type_id(&self) -> MessageTypeId {
self.message_type_id()
} }
#[inline] #[inline]
@@ -1334,20 +1465,25 @@ impl GenericPusTmSecondaryHeader for PusTmZeroCopyWriter<'_> {
#[inline] #[inline]
fn sc_time_ref_status(&self) -> u4; fn sc_time_ref_status(&self) -> u4;
#[inline] #[inline]
fn msg_counter(&self) -> u16; fn msg_type_counter(&self) -> u16;
#[inline] #[inline]
fn dest_id(&self) -> u16; fn dest_id(&self) -> u16;
} }
} }
#[inline] #[inline]
fn service(&self) -> u8 { fn service_type_id(&self) -> u8 {
PusPacket::service(self) self.service_type_id()
} }
#[inline] #[inline]
fn subservice(&self) -> u8 { fn message_subtype_id(&self) -> u8 {
PusPacket::subservice(self) self.message_subtype_id()
}
#[inline]
fn message_type_id(&self) -> MessageTypeId {
self.message_type_id()
} }
} }
@@ -1367,7 +1503,7 @@ mod tests {
fn base_ping_reply_full_ctor<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> { fn base_ping_reply_full_ctor<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> {
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); let tm_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), timestamp);
PusTmCreator::new_no_source_data(sph, tm_header, CreatorConfig::default()) PusTmCreator::new_no_source_data(sph, tm_header, CreatorConfig::default())
} }
@@ -1379,23 +1515,23 @@ mod tests {
return PusTmCreator::builder() return PusTmCreator::builder()
.with_apid(u11::new(0x123)) .with_apid(u11::new(0x123))
.with_sequence_count(u14::new(0x234)) .with_sequence_count(u14::new(0x234))
.with_service(17) .with_service_type_id(17)
.with_subservice(2) .with_message_subtype_id(2)
.with_timestamp(timestamp) .with_timestamp(timestamp)
.build(); .build();
} }
PusTmBuilder::new() PusTmBuilder::new()
.with_apid(u11::new(0x123)) .with_apid(u11::new(0x123))
.with_sequence_count(u14::new(0x234)) .with_sequence_count(u14::new(0x234))
.with_service(17) .with_service_type_id(17)
.with_subservice(2) .with_message_subtype_id(2)
.with_timestamp(timestamp) .with_timestamp(timestamp)
.build() .build()
} }
fn base_ping_reply_full_ctor_no_checksum<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> { fn base_ping_reply_full_ctor_no_checksum<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> {
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); let tm_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), timestamp);
PusTmCreator::new_no_source_data( PusTmCreator::new_no_source_data(
sph, sph,
tm_header, tm_header,
@@ -1407,13 +1543,13 @@ mod tests {
} }
fn ping_reply_with_data<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> { fn ping_reply_with_data<'a, 'b>(timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> {
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); let tm_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), timestamp);
PusTmCreator::new(sph, tm_header, DUMMY_DATA, CreatorConfig::default()) PusTmCreator::new(sph, tm_header, DUMMY_DATA, CreatorConfig::default())
} }
fn base_hk_reply<'a, 'b>(timestamp: &'a [u8], src_data: &'b [u8]) -> PusTmCreator<'a, 'b> { fn base_hk_reply<'a, 'b>(timestamp: &'a [u8], src_data: &'b [u8]) -> PusTmCreator<'a, 'b> {
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tc_header = PusTmSecondaryHeader::new_simple(3, 5, timestamp); let tc_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(3, 5), timestamp);
PusTmCreator::new(sph, tc_header, src_data, CreatorConfig::default()) PusTmCreator::new(sph, tc_header, src_data, CreatorConfig::default())
} }
@@ -1422,7 +1558,7 @@ mod tests {
src_data: &'b [u8], src_data: &'b [u8],
) -> PusTmCreator<'a, 'b> { ) -> PusTmCreator<'a, 'b> {
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tc_header = PusTmSecondaryHeader::new_simple(3, 5, timestamp); let tc_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(3, 5), timestamp);
PusTmCreator::new( PusTmCreator::new(
sph, sph,
tc_header, tc_header,
@@ -1459,8 +1595,7 @@ mod tests {
let mut stamp_buf: [u8; 8] = [0; 8]; let mut stamp_buf: [u8; 8] = [0; 8];
let pus_tm = PusTmCreator::new_simple( let pus_tm = PusTmCreator::new_simple(
sph, sph,
17, MessageTypeId::new(17, 2),
2,
&time_provider, &time_provider,
&mut stamp_buf, &mut stamp_buf,
&[], &[],
@@ -1486,7 +1621,7 @@ mod tests {
fn test_serialization_no_source_data_alt_ctor() { fn test_serialization_no_source_data_alt_ctor() {
let timestamp = dummy_timestamp(); let timestamp = dummy_timestamp();
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); let tm_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), timestamp);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let mut pus_tm = let mut pus_tm =
PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0, true).unwrap(); PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0, true).unwrap();
@@ -1502,7 +1637,7 @@ mod tests {
fn test_serialization_no_source_data_alt_ctor_no_checksum_verification() { fn test_serialization_no_source_data_alt_ctor_no_checksum_verification() {
let timestamp = dummy_timestamp(); let timestamp = dummy_timestamp();
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); let tm_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), timestamp);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let mut pus_tm = let mut pus_tm =
PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0, true).unwrap(); PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0, true).unwrap();
@@ -1520,7 +1655,7 @@ mod tests {
fn test_serialization_no_source_data_alt_ctor_no_checksum() { fn test_serialization_no_source_data_alt_ctor_no_checksum() {
let timestamp = dummy_timestamp(); let timestamp = dummy_timestamp();
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); let tm_header = PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), timestamp);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let mut pus_tm = let mut pus_tm =
PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0, false).unwrap(); PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0, false).unwrap();
@@ -1596,7 +1731,8 @@ mod tests {
let src_data = &[1, 2, 3]; let src_data = &[1, 2, 3];
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tc_header = PusTmSecondaryHeader::new_simple(3, 5, dummy_timestamp()); let tc_header =
PusTmSecondaryHeader::new_simple(MessageTypeId::new(3, 5), dummy_timestamp());
let mut hk_reply_unwritten = let mut hk_reply_unwritten =
PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tc_header, 3, true).unwrap(); PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tc_header, 3, true).unwrap();
assert_eq!(hk_reply_unwritten.source_data_len(), 3); assert_eq!(hk_reply_unwritten.source_data_len(), 3);
@@ -1616,7 +1752,8 @@ mod tests {
let src_data = &[1, 2, 3]; let src_data = &[1, 2, 3];
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tc_header = PusTmSecondaryHeader::new_simple(3, 5, dummy_timestamp()); let tc_header =
PusTmSecondaryHeader::new_simple(MessageTypeId::new(3, 5), dummy_timestamp());
let mut hk_reply_unwritten = let mut hk_reply_unwritten =
PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tc_header, 3, true).unwrap(); PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tc_header, 3, true).unwrap();
assert_eq!(hk_reply_unwritten.source_data_len(), 3); assert_eq!(hk_reply_unwritten.source_data_len(), 3);
@@ -1640,7 +1777,7 @@ mod tests {
pus_tm.set_msg_counter(0x1f1f); pus_tm.set_msg_counter(0x1f1f);
assert_eq!(pus_tm.sc_time_ref_status().value(), 0b1010); assert_eq!(pus_tm.sc_time_ref_status().value(), 0b1010);
assert_eq!(pus_tm.dest_id(), 0x7fff); assert_eq!(pus_tm.dest_id(), 0x7fff);
assert_eq!(pus_tm.msg_counter(), 0x1f1f); assert_eq!(pus_tm.msg_type_counter(), 0x1f1f);
pus_tm.set_apid(u11::new(0x7ff)); pus_tm.set_apid(u11::new(0x7ff));
assert_eq!(pus_tm.apid().value(), 0x7ff); assert_eq!(pus_tm.apid().value(), 0x7ff);
} }
@@ -1772,7 +1909,8 @@ mod tests {
#[test] #[test]
fn test_manual_field_update() { fn test_manual_field_update() {
let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0); let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_timestamp()); let tc_header =
PusTmSecondaryHeader::new_simple(MessageTypeId::new(17, 2), dummy_timestamp());
let mut tm = PusTmCreator::new_no_source_data( let mut tm = PusTmCreator::new_no_source_data(
sph, sph,
tc_header, tc_header,
@@ -1928,10 +2066,10 @@ mod tests {
exp_full_len: usize, exp_full_len: usize,
) { ) {
assert!(tm.is_tm()); assert!(tm.is_tm());
assert_eq!(PusPacket::service(tm), 17); assert_eq!(PusPacket::service_type_id(tm), 17);
assert_eq!(GenericPusTmSecondaryHeader::service(tm), 17); assert_eq!(GenericPusTmSecondaryHeader::service_type_id(tm), 17);
assert_eq!(PusPacket::subservice(tm), 2); assert_eq!(PusPacket::message_subtype_id(tm), 2);
assert_eq!(GenericPusTmSecondaryHeader::subservice(tm), 2); assert_eq!(GenericPusTmSecondaryHeader::message_subtype_id(tm), 2);
assert!(tm.sec_header_flag()); assert!(tm.sec_header_flag());
if has_user_data { if has_user_data {
assert!(!tm.user_data().is_empty()); assert!(!tm.user_data().is_empty());
@@ -1946,7 +2084,7 @@ mod tests {
); );
assert_eq!(tm.data_len(), exp_full_len as u16 - 7); assert_eq!(tm.data_len(), exp_full_len as u16 - 7);
assert_eq!(tm.dest_id(), 0x0000); assert_eq!(tm.dest_id(), 0x0000);
assert_eq!(tm.msg_counter(), 0x0000); assert_eq!(tm.msg_type_counter(), 0x0000);
assert_eq!(tm.sc_time_ref_status().value(), 0b0000); assert_eq!(tm.sc_time_ref_status().value(), 0b0000);
} }
@@ -1984,7 +2122,7 @@ mod tests {
// This performs all necessary checks, including the CRC check. // This performs all necessary checks, including the CRC check.
let tm_read_back = PusTmReader::new(&buf, 7).expect("Re-creating PUS TM failed"); let tm_read_back = PusTmReader::new(&buf, 7).expect("Re-creating PUS TM failed");
assert_eq!(tm_read_back.packet_len(), tm_size); assert_eq!(tm_read_back.packet_len(), tm_size);
assert_eq!(tm_read_back.msg_counter(), 100); assert_eq!(tm_read_back.msg_type_counter(), 100);
assert_eq!(tm_read_back.dest_id(), 55); assert_eq!(tm_read_back.dest_id(), 55);
assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT); assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT);
assert_eq!(tm_read_back.apid(), MAX_APID); assert_eq!(tm_read_back.apid(), MAX_APID);
@@ -2003,8 +2141,8 @@ mod tests {
writer.set_msg_count(100); writer.set_msg_count(100);
writer.set_seq_count(MAX_SEQ_COUNT); writer.set_seq_count(MAX_SEQ_COUNT);
writer.set_apid(MAX_APID); writer.set_apid(MAX_APID);
assert_eq!(PusPacket::service(&writer), 17); assert_eq!(PusPacket::service_type_id(&writer), 17);
assert_eq!(PusPacket::subservice(&writer), 2); assert_eq!(PusPacket::message_subtype_id(&writer), 2);
assert_eq!(writer.apid(), MAX_APID); assert_eq!(writer.apid(), MAX_APID);
assert_eq!(writer.seq_count(), MAX_SEQ_COUNT); assert_eq!(writer.seq_count(), MAX_SEQ_COUNT);
} }
@@ -2023,12 +2161,15 @@ mod tests {
writer.set_msg_count(100); writer.set_msg_count(100);
writer.set_seq_count(MAX_SEQ_COUNT); writer.set_seq_count(MAX_SEQ_COUNT);
writer.set_apid(MAX_APID); writer.set_apid(MAX_APID);
assert_eq!(PusPacket::service(&writer), 17); assert_eq!(PusPacket::service_type_id(&writer), 17);
assert_eq!(PusPacket::subservice(&writer), 2); assert_eq!(PusPacket::message_subtype_id(&writer), 2);
assert_eq!(writer.dest_id(), 55); assert_eq!(writer.dest_id(), 55);
assert_eq!(writer.msg_counter(), 100); assert_eq!(writer.msg_type_counter(), 100);
assert_eq!(writer.sec_header_without_timestamp().dest_id(), 55); assert_eq!(writer.sec_header_without_timestamp().dest_id(), 55);
assert_eq!(writer.sec_header_without_timestamp().msg_counter(), 100); assert_eq!(
writer.sec_header_without_timestamp().msg_type_counter(),
100
);
assert_eq!(writer.user_data(), DUMMY_DATA); assert_eq!(writer.user_data(), DUMMY_DATA);
// Need to check crc16 before finish, because finish will update the CRC. // Need to check crc16 before finish, because finish will update the CRC.
let crc16 = writer.checksum(); let crc16 = writer.checksum();
@@ -2039,7 +2180,7 @@ mod tests {
#[test] #[test]
fn test_sec_header_without_stamp() { fn test_sec_header_without_stamp() {
let sec_header = PusTmSecondaryHeader::new_simple_no_timestamp(17, 1); let sec_header = PusTmSecondaryHeader::new_simple_no_timestamp(MessageTypeId::new(17, 1));
assert_eq!(sec_header.timestamp, &[]); assert_eq!(sec_header.timestamp, &[]);
} }
@@ -2105,8 +2246,7 @@ mod tests {
let mut stamp_buf: [u8; 8] = [0; 8]; let mut stamp_buf: [u8; 8] = [0; 8];
let pus_tm = PusTmCreator::new_simple( let pus_tm = PusTmCreator::new_simple(
sph, sph,
17, MessageTypeId::new(17, 2),
2,
&time_provider, &time_provider,
&mut stamp_buf, &mut stamp_buf,
&[], &[],
@@ -2127,8 +2267,7 @@ mod tests {
let mut stamp_buf: [u8; 8] = [0; 8]; let mut stamp_buf: [u8; 8] = [0; 8];
let pus_tm = PusTmCreator::new_simple( let pus_tm = PusTmCreator::new_simple(
sph, sph,
17, MessageTypeId::new(17, 2),
2,
&time_provider, &time_provider,
&mut stamp_buf, &mut stamp_buf,
&[], &[],
@@ -2166,8 +2305,8 @@ mod tests {
SequenceFlags::Unsegmented, SequenceFlags::Unsegmented,
u14::new(0x34), u14::new(0x34),
)) ))
.with_service(17) .with_service_type_id(17)
.with_subservice(2) .with_message_subtype_id(2)
.with_dest_id(0x2f2f) .with_dest_id(0x2f2f)
.with_checksum(false) .with_checksum(false)
.build(); .build();
@@ -2175,7 +2314,7 @@ mod tests {
assert_eq!(tm.sequence_flags(), SequenceFlags::Unsegmented); assert_eq!(tm.sequence_flags(), SequenceFlags::Unsegmented);
assert_eq!(tm.apid().value(), 0x02); assert_eq!(tm.apid().value(), 0x02);
assert_eq!(tm.packet_type(), PacketType::Tm); assert_eq!(tm.packet_type(), PacketType::Tm);
assert_eq!(tm.service(), 17); assert_eq!(tm.service_type_id(), 17);
assert_eq!(tm.subservice(), 2); assert_eq!(tm.message_subtype_id(), 2);
} }
} }
+123 -38
View File
@@ -27,8 +27,8 @@
//! true //! true
//! ); //! );
//! println!("{:?}", ping_tm); //! println!("{:?}", ping_tm);
//! assert_eq!(ping_tm.service(), 17); //! assert_eq!(ping_tm.service_type_id(), 17);
//! assert_eq!(ping_tm.subservice(), 2); //! assert_eq!(ping_tm.message_subtype_id(), 2);
//! assert_eq!(ping_tm.apid().value(), 0x02); //! assert_eq!(ping_tm.apid().value(), 0x02);
//! //!
//! // Serialize TM into a raw buffer //! // Serialize TM into a raw buffer
@@ -42,16 +42,17 @@
//! // Deserialize from the raw byte representation //! // Deserialize from the raw byte representation
//! let ping_tm_reader = PusTmReader::new(&test_buf, &SecondaryHeaderParameters::new_minimal(7)).expect("deserialization failed"); //! let ping_tm_reader = PusTmReader::new(&test_buf, &SecondaryHeaderParameters::new_minimal(7)).expect("deserialization failed");
//! assert_eq!(written_size, ping_tm_reader.packet_len()); //! assert_eq!(written_size, ping_tm_reader.packet_len());
//! assert_eq!(ping_tm_reader.service(), 17); //! assert_eq!(ping_tm_reader.service_type_id(), 17);
//! assert_eq!(ping_tm_reader.subservice(), 2); //! assert_eq!(ping_tm_reader.message_subtype_id(), 2);
//! assert_eq!(ping_tm_reader.apid().value(), 0x02); //! assert_eq!(ping_tm_reader.apid().value(), 0x02);
//! assert_eq!(ping_tm_reader.timestamp(), &time_buf); //! assert_eq!(ping_tm_reader.timestamp(), &time_buf);
//! ``` //! ```
use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE}; use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE};
use crate::ecss::tm::IsPusTelemetry;
use crate::ecss::{ use crate::ecss::{
calc_pus_crc16, crc_from_raw_data, sp_header_impls, user_data_from_raw, calc_pus_crc16, crc_from_raw_data, sp_header_impls, user_data_from_raw,
verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion, verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, MessageTypeId, PusError, PusPacket,
WritablePusPacket, PusVersion, WritablePusPacket,
}; };
use crate::util::{UnsignedByteField, UnsignedEnum}; use crate::util::{UnsignedByteField, UnsignedEnum};
use crate::{ use crate::{
@@ -73,55 +74,78 @@ use crate::time::{TimeWriter, TimestampError};
use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table; use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table;
pub trait IsPusTelemetry {}
/// Length without timestamp /// Length without timestamp
pub const PUS_TM_MIN_SEC_HEADER_LEN: usize = 3; pub const PUS_TM_MIN_SEC_HEADER_LEN: usize = 3;
/// Minimal length of a PUS TM packet without source data.
pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize = pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>(); CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
/// Generic properties of a PUS-A PUS TM secondary header.
pub trait GenericPusTmSecondaryHeader { pub trait GenericPusTmSecondaryHeader {
/// PUS version field.
fn pus_version(&self) -> PusVersion; fn pus_version(&self) -> PusVersion;
/// Service field.
fn service(&self) -> u8; fn service(&self) -> u8;
/// Subservice field.
fn subservice(&self) -> u8; fn subservice(&self) -> u8;
/// Message counter field for the service ID.
fn msg_counter(&self) -> Option<u8>; fn msg_counter(&self) -> Option<u8>;
/// Destination ID.
fn dest_id(&self) -> Option<UnsignedByteField>; fn dest_id(&self) -> Option<UnsignedByteField>;
/// Number of spare bytes.
fn spare_bytes(&self) -> usize; fn spare_bytes(&self) -> usize;
} }
/// Managed parameters for the secondary header which can not be deduced from the packet itself.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SecondaryHeaderParameters { pub struct SecondaryHeaderParameters {
/// Timestamp length in bytes.
pub timestamp_len: usize, pub timestamp_len: usize,
/// Does the packet have a message counter?
pub has_msg_counter: bool, pub has_msg_counter: bool,
pub dest_id_len: Option<usize>, /// Present and length of the destination ID.
pub opt_dest_id_len: Option<usize>,
/// Number of spare bytes.
pub spare_bytes: usize, pub spare_bytes: usize,
} }
impl SecondaryHeaderParameters { impl SecondaryHeaderParameters {
/// Minimal constructor.
pub const fn new_minimal(timestamp_len: usize) -> Self { pub const fn new_minimal(timestamp_len: usize) -> Self {
Self { Self {
timestamp_len, timestamp_len,
has_msg_counter: false, has_msg_counter: false,
dest_id_len: None, opt_dest_id_len: None,
spare_bytes: 0, spare_bytes: 0,
} }
} }
} }
/// PUS TM secondary header.
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTmSecondaryHeader<'stamp> { pub struct PusTmSecondaryHeader<'stamp> {
pus_version: PusVersion, pus_version: PusVersion,
/// Service field.
pub service: u8, pub service: u8,
/// Subservice field.
pub subservice: u8, pub subservice: u8,
/// Service message counter.
pub msg_counter: Option<u8>, pub msg_counter: Option<u8>,
/// Destination ID field.
pub dest_id: Option<UnsignedByteField>, pub dest_id: Option<UnsignedByteField>,
/// Timestamp slice.
pub timestamp: &'stamp [u8], pub timestamp: &'stamp [u8],
/// Number of spare bytes to add after the timestamp.
pub spare_bytes: usize, pub spare_bytes: usize,
} }
impl<'stamp> PusTmSecondaryHeader<'stamp> { impl<'stamp> PusTmSecondaryHeader<'stamp> {
/// Simple constructor.
///
/// Allows setting the service, subservice and timestamp and sets default values for the
/// other fields.
#[inline] #[inline]
pub fn new_simple(service: u8, subservice: u8, timestamp: &'stamp [u8]) -> Self { pub fn new_simple(service: u8, subservice: u8, timestamp: &'stamp [u8]) -> Self {
Self::new(service, subservice, None, None, timestamp, 0) Self::new(service, subservice, None, None, timestamp, 0)
@@ -133,6 +157,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
Self::new(service, subservice, None, None, &[], 0) Self::new(service, subservice, None, None, &[], 0)
} }
/// Generic constructor.
#[inline] #[inline]
pub fn new( pub fn new(
service: u8, service: u8,
@@ -153,6 +178,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
} }
} }
/// Construct the secondary header from a raw byte slice.
pub fn from_bytes( pub fn from_bytes(
buf: &'stamp [u8], buf: &'stamp [u8],
params: &SecondaryHeaderParameters, params: &SecondaryHeaderParameters,
@@ -180,7 +206,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
current_idx += 1; current_idx += 1;
} }
let mut dest_id = None; let mut dest_id = None;
if let Some(dest_id_len) = params.dest_id_len { if let Some(dest_id_len) = params.opt_dest_id_len {
dest_id = Some( dest_id = Some(
UnsignedByteField::new_from_be_bytes( UnsignedByteField::new_from_be_bytes(
dest_id_len, dest_id_len,
@@ -201,6 +227,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
}) })
} }
/// Write the PUS TM secondary header to a provided buffer.
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
let written_len = self.written_len(); let written_len = self.written_len();
if buf.len() < written_len { if buf.len() < written_len {
@@ -229,6 +256,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
Ok(written_len) Ok(written_len)
} }
/// Convert the PUS TM packet secondary header to a [alloc::vec::Vec].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_vec(&self) -> Vec<u8> { pub fn to_vec(&self) -> Vec<u8> {
let mut vec = alloc::vec![0; self.written_len()]; let mut vec = alloc::vec![0; self.written_len()];
@@ -236,6 +264,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
vec vec
} }
/// Length of the secondary header when written to bytes.
pub fn written_len(&self) -> usize { pub fn written_len(&self) -> usize {
let mut len = PUS_TM_MIN_SEC_HEADER_LEN + self.timestamp.len() + self.spare_bytes; let mut len = PUS_TM_MIN_SEC_HEADER_LEN + self.timestamp.len() + self.spare_bytes;
if let Some(dest_id) = self.dest_id { if let Some(dest_id) = self.dest_id {
@@ -247,9 +276,10 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
len len
} }
/// Length for the provided packet parameters.
pub fn len_for_params(params: &SecondaryHeaderParameters) -> usize { pub fn len_for_params(params: &SecondaryHeaderParameters) -> usize {
let mut len = PUS_TM_MIN_SEC_HEADER_LEN + params.timestamp_len + params.spare_bytes; let mut len = PUS_TM_MIN_SEC_HEADER_LEN + params.timestamp_len + params.spare_bytes;
if let Some(dest_id) = params.dest_id_len { if let Some(dest_id) = params.opt_dest_id_len {
len += dest_id; len += dest_id;
} }
if params.has_msg_counter { if params.has_msg_counter {
@@ -308,7 +338,9 @@ impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTmCreator<'time, 'src_data> { pub struct PusTmCreator<'time, 'src_data> {
/// Space packet header.
pub sp_header: SpHeader, pub sp_header: SpHeader,
/// PUS TM secondary header.
#[cfg_attr(feature = "serde", serde(borrow))] #[cfg_attr(feature = "serde", serde(borrow))]
pub sec_header: PusTmSecondaryHeader<'time>, pub sec_header: PusTmSecondaryHeader<'time>,
source_data: &'src_data [u8], source_data: &'src_data [u8],
@@ -351,6 +383,8 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
pus_tm pus_tm
} }
/// Simple constructor which builds the [PusTmSecondaryHeader] internally from the
/// provided arguments.
#[inline] #[inline]
pub fn new_simple( pub fn new_simple(
sp_header: SpHeader, sp_header: SpHeader,
@@ -367,6 +401,7 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
Ok(Self::new(sp_header, sec_header, source_data, set_ccsds_len)) Ok(Self::new(sp_header, sec_header, source_data, set_ccsds_len))
} }
/// Constructor for PUS TM packets without source data.
#[inline] #[inline]
pub fn new_no_source_data( pub fn new_no_source_data(
sp_header: SpHeader, sp_header: SpHeader,
@@ -376,21 +411,25 @@ impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
Self::new(sp_header, sec_header, &[], set_ccsds_len) Self::new(sp_header, sec_header, &[], set_ccsds_len)
} }
/// Raw timestamp slice.
#[inline] #[inline]
pub fn timestamp(&self) -> &[u8] { pub fn timestamp(&self) -> &[u8] {
self.sec_header.timestamp self.sec_header.timestamp
} }
/// Raw source data slice.
#[inline] #[inline]
pub fn source_data(&self) -> &[u8] { pub fn source_data(&self) -> &[u8] {
self.source_data self.source_data
} }
/// Set the destination ID.
#[inline] #[inline]
pub fn set_dest_id(&mut self, dest_id: Option<UnsignedByteField>) { pub fn set_dest_id(&mut self, dest_id: Option<UnsignedByteField>) {
self.sec_header.dest_id = dest_id; self.sec_header.dest_id = dest_id;
} }
/// Set the message counter for the current service ID.
#[inline] #[inline]
pub fn set_msg_counter(&mut self, msg_counter: Option<u8>) { pub fn set_msg_counter(&mut self, msg_counter: Option<u8>) {
self.sec_header.msg_counter = msg_counter self.sec_header.msg_counter = msg_counter
@@ -559,12 +598,22 @@ impl PusPacket for PusTmCreator<'_, '_> {
Ok(self.sec_header.pus_version) Ok(self.sec_header.pus_version)
} }
delegate!(to self.sec_header { #[inline]
#[inline] fn message_type_id(&self) -> super::MessageTypeId {
fn service(&self) -> u8; super::MessageTypeId {
#[inline] type_id: self.sec_header.service,
fn subservice(&self) -> u8; subtype_id: self.sec_header.subservice,
}); }
}
#[inline]
fn service_type_id(&self) -> u8 {
self.sec_header.service
}
#[inline]
fn message_subtype_id(&self) -> u8 {
self.sec_header.subservice
}
#[inline] #[inline]
fn user_data(&self) -> &[u8] { fn user_data(&self) -> &[u8] {
@@ -665,6 +714,7 @@ impl<'buf> PusTmCreatorWithReservedSourceData<'buf> {
}) })
} }
/// Length of the full packet when written to bytes.
#[inline] #[inline]
pub const fn len_written(&self) -> usize { pub const fn len_written(&self) -> usize {
self.full_len self.full_len
@@ -682,6 +732,7 @@ impl<'buf> PusTmCreatorWithReservedSourceData<'buf> {
&self.buf[self.source_data_offset..self.full_len - 2] &self.buf[self.source_data_offset..self.full_len - 2]
} }
/// Source data length.
#[inline] #[inline]
pub fn source_data_len(&self) -> usize { pub fn source_data_len(&self) -> usize {
self.full_len - 2 - self.source_data_offset self.full_len - 2 - self.source_data_offset
@@ -735,7 +786,9 @@ impl<'buf> PusTmCreatorWithReservedSourceData<'buf> {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTmReader<'raw_data> { pub struct PusTmReader<'raw_data> {
/// Space packet header.
pub sp_header: SpHeader, pub sp_header: SpHeader,
/// PUS TM secondary header.
pub sec_header: PusTmSecondaryHeader<'raw_data>, pub sec_header: PusTmSecondaryHeader<'raw_data>,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "serde", serde(skip))]
raw_data: &'raw_data [u8], raw_data: &'raw_data [u8],
@@ -769,6 +822,7 @@ impl<'raw_data> PusTmReader<'raw_data> {
Ok(tc) Ok(tc)
} }
/// New constructor which does not check the CRC16.
pub fn new_no_crc_check( pub fn new_no_crc_check(
slice: &'raw_data [u8], slice: &'raw_data [u8],
sec_header_params: &SecondaryHeaderParameters, sec_header_params: &SecondaryHeaderParameters,
@@ -812,21 +866,25 @@ impl<'raw_data> PusTmReader<'raw_data> {
}) })
} }
/// Length of the full packet as packed in the raw data.
#[inline] #[inline]
pub fn len_packed(&self) -> usize { pub fn len_packed(&self) -> usize {
self.sp_header.packet_len() self.sp_header.packet_len()
} }
/// Raw source data slice.
#[inline] #[inline]
pub fn source_data(&self) -> &[u8] { pub fn source_data(&self) -> &[u8] {
self.user_data() self.user_data()
} }
/// Raw timestamp slice.
#[inline] #[inline]
pub fn timestamp(&self) -> &[u8] { pub fn timestamp(&self) -> &[u8] {
self.sec_header.timestamp self.sec_header.timestamp
} }
/// CRC16 checksum of the packet.
#[inline] #[inline]
pub fn crc16(&self) -> u16 { pub fn crc16(&self) -> u16 {
self.crc16 self.crc16
@@ -867,12 +925,23 @@ impl PusPacket for PusTmReader<'_> {
Ok(self.sec_header.pus_version) Ok(self.sec_header.pus_version)
} }
delegate!(to self.sec_header { #[inline]
#[inline] fn message_type_id(&self) -> super::MessageTypeId {
fn service(&self) -> u8; MessageTypeId {
#[inline] type_id: self.sec_header.service,
fn subservice(&self) -> u8; subtype_id: self.sec_header.subservice,
}); }
}
#[inline]
fn service_type_id(&self) -> u8 {
self.sec_header.service
}
#[inline]
fn message_subtype_id(&self) -> u8 {
self.sec_header.subservice
}
#[inline] #[inline]
fn user_data(&self) -> &[u8] { fn user_data(&self) -> &[u8] {
@@ -920,16 +989,21 @@ impl PartialEq<PusTmReader<'_>> for PusTmCreator<'_, '_> {
} }
} }
/// Secondary header field not present error.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[error("this field is not present in the secondary header")] #[error("this field is not present in the secondary header")]
pub struct SecondaryHeaderFieldNotPresentError; pub struct SecondaryHeaderFieldNotPresentError;
/// Destination ID operation error.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum DestIdOperationError { pub enum DestIdOperationError {
/// Field is not present in the secondary header.
#[error("this field is not present in the secondary header")] #[error("this field is not present in the secondary header")]
FieldNotPresent(#[from] SecondaryHeaderFieldNotPresentError), FieldNotPresent(#[from] SecondaryHeaderFieldNotPresentError),
/// Invalid field length.
#[error("invalid byte field length")] #[error("invalid byte field length")]
InvalidFieldLen, InvalidFieldLen,
/// Bzyte conversion error.
#[error("byte conversion error")] #[error("byte conversion error")]
ByteConversionError(#[from] ByteConversionError), ByteConversionError(#[from] ByteConversionError),
} }
@@ -987,15 +1061,16 @@ impl<'raw> PusTmZeroCopyWriter<'raw> {
self.raw_tm[0..2].copy_from_slice(&updated_apid.to_be_bytes()); self.raw_tm[0..2].copy_from_slice(&updated_apid.to_be_bytes());
} }
/// Destination ID field if present.
pub fn dest_id(&self) -> Result<Option<UnsignedByteField>, ByteConversionError> { pub fn dest_id(&self) -> Result<Option<UnsignedByteField>, ByteConversionError> {
if self.sec_header_params.dest_id_len.is_none() { if self.sec_header_params.opt_dest_id_len.is_none() {
return Ok(None); return Ok(None);
} }
let mut base_idx = 10; let mut base_idx = 10;
if self.sec_header_params.has_msg_counter { if self.sec_header_params.has_msg_counter {
base_idx += 1; base_idx += 1;
} }
let dest_id_len = self.sec_header_params.dest_id_len.unwrap(); let dest_id_len = self.sec_header_params.opt_dest_id_len.unwrap();
if self.raw_tm.len() < base_idx + dest_id_len { if self.raw_tm.len() < base_idx + dest_id_len {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
found: self.raw_tm.len(), found: self.raw_tm.len(),
@@ -1011,6 +1086,7 @@ impl<'raw> PusTmZeroCopyWriter<'raw> {
)) ))
} }
/// Message counter if present.
pub fn msg_counter(&self) -> Option<u8> { pub fn msg_counter(&self) -> Option<u8> {
if !self.sec_header_params.has_msg_counter { if !self.sec_header_params.has_msg_counter {
return None; return None;
@@ -1040,10 +1116,10 @@ impl<'raw> PusTmZeroCopyWriter<'raw> {
&mut self, &mut self,
dest_id: UnsignedByteField, dest_id: UnsignedByteField,
) -> Result<(), DestIdOperationError> { ) -> Result<(), DestIdOperationError> {
if self.sec_header_params.dest_id_len.is_none() { if self.sec_header_params.opt_dest_id_len.is_none() {
return Err(SecondaryHeaderFieldNotPresentError.into()); return Err(SecondaryHeaderFieldNotPresentError.into());
} }
let dest_id_len = self.sec_header_params.dest_id_len.unwrap(); let dest_id_len = self.sec_header_params.opt_dest_id_len.unwrap();
if dest_id.size() != dest_id_len { if dest_id.size() != dest_id_len {
return Err(DestIdOperationError::InvalidFieldLen); return Err(DestIdOperationError::InvalidFieldLen);
} }
@@ -1072,6 +1148,7 @@ impl<'raw> PusTmZeroCopyWriter<'raw> {
crate::zc::SpHeader::read_from_bytes(&self.raw_tm[0..CCSDS_HEADER_LEN]).unwrap() crate::zc::SpHeader::read_from_bytes(&self.raw_tm[0..CCSDS_HEADER_LEN]).unwrap()
} }
/// Set the sequence count.
#[inline] #[inline]
pub fn set_seq_count(&mut self, seq_count: u14) { pub fn set_seq_count(&mut self, seq_count: u14) {
let new_psc = (u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000) let new_psc = (u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000)
@@ -1117,12 +1194,20 @@ impl PusPacket for PusTmZeroCopyWriter<'_> {
} }
#[inline] #[inline]
fn service(&self) -> u8 { fn message_type_id(&self) -> super::MessageTypeId {
MessageTypeId {
type_id: self.service_type_id(),
subtype_id: self.message_subtype_id(),
}
}
#[inline]
fn service_type_id(&self) -> u8 {
self.raw_tm[7] self.raw_tm[7]
} }
#[inline] #[inline]
fn subservice(&self) -> u8 { fn message_subtype_id(&self) -> u8 {
self.raw_tm[8] self.raw_tm[8]
} }
@@ -1494,7 +1579,7 @@ mod tests {
&SecondaryHeaderParameters { &SecondaryHeaderParameters {
timestamp_len: 7, timestamp_len: 7,
has_msg_counter: msg_counter.is_some(), has_msg_counter: msg_counter.is_some(),
dest_id_len: dest_id.as_ref().map(|id| id.size()), opt_dest_id_len: dest_id.as_ref().map(|id| id.size()),
spare_bytes: 0, spare_bytes: 0,
}, },
) )
@@ -1774,9 +1859,9 @@ mod tests {
msg_counter: Option<u8>, msg_counter: Option<u8>,
) { ) {
assert!(tm.is_tm()); assert!(tm.is_tm());
assert_eq!(PusPacket::service(tm), 17); assert_eq!(PusPacket::service_type_id(tm), 17);
assert_eq!(GenericPusTmSecondaryHeader::service(tm), 17); assert_eq!(GenericPusTmSecondaryHeader::service(tm), 17);
assert_eq!(PusPacket::subservice(tm), 2); assert_eq!(PusPacket::message_subtype_id(tm), 2);
assert_eq!(GenericPusTmSecondaryHeader::subservice(tm), 2); assert_eq!(GenericPusTmSecondaryHeader::subservice(tm), 2);
assert!(tm.sec_header_flag()); assert!(tm.sec_header_flag());
if has_user_data { if has_user_data {
@@ -1849,7 +1934,7 @@ mod tests {
&SecondaryHeaderParameters { &SecondaryHeaderParameters {
timestamp_len: dummy_timestamp().len(), timestamp_len: dummy_timestamp().len(),
has_msg_counter: false, has_msg_counter: false,
dest_id_len: Some(2), opt_dest_id_len: Some(2),
spare_bytes: 0, spare_bytes: 0,
}, },
) )
@@ -1859,8 +1944,8 @@ mod tests {
writer writer
.set_destination_id(UnsignedByteFieldU16::new(0xf1f1).into()) .set_destination_id(UnsignedByteFieldU16::new(0xf1f1).into())
.unwrap(); .unwrap();
assert_eq!(PusPacket::service(&writer), 17); assert_eq!(PusPacket::service_type_id(&writer), 17);
assert_eq!(PusPacket::subservice(&writer), 2); assert_eq!(PusPacket::message_subtype_id(&writer), 2);
assert_eq!( assert_eq!(
writer.dest_id().unwrap().unwrap(), writer.dest_id().unwrap().unwrap(),
UnsignedByteFieldU16::new(0xf1f1).into() UnsignedByteFieldU16::new(0xf1f1).into()
@@ -1881,8 +1966,8 @@ mod tests {
.expect("Creating zero copy writer failed"); .expect("Creating zero copy writer failed");
writer.set_seq_count(MAX_SEQ_COUNT); writer.set_seq_count(MAX_SEQ_COUNT);
writer.set_apid(MAX_APID); writer.set_apid(MAX_APID);
assert_eq!(PusPacket::service(&writer), 17); assert_eq!(PusPacket::service_type_id(&writer), 17);
assert_eq!(PusPacket::subservice(&writer), 2); assert_eq!(PusPacket::message_subtype_id(&writer), 2);
assert!(writer.dest_id().unwrap().is_none()); assert!(writer.dest_id().unwrap().is_none());
assert!(writer.msg_counter().is_none()); assert!(writer.msg_counter().is_none());
if let Err(err) = writer.set_destination_id(UnsignedByteFieldU16::new(0xf1f1).into()) { if let Err(err) = writer.set_destination_id(UnsignedByteFieldU16::new(0xf1f1).into()) {
+13 -4
View File
@@ -3,18 +3,27 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Message subtype ID.
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum Subservice { pub enum MessageSubtypeId {
/// Telemetry - Acceptance success.
TmAcceptanceSuccess = 1, TmAcceptanceSuccess = 1,
/// Telemetry - Acceptance failure.
TmAcceptanceFailure = 2, TmAcceptanceFailure = 2,
/// Telemetry - Start success.
TmStartSuccess = 3, TmStartSuccess = 3,
/// Telemetry - Start failure.
TmStartFailure = 4, TmStartFailure = 4,
/// Telemetry - Step success.
TmStepSuccess = 5, TmStepSuccess = 5,
/// Telemetry - Step failure.
TmStepFailure = 6, TmStepFailure = 6,
/// Telemetry - Completion success.
TmCompletionSuccess = 7, TmCompletionSuccess = 7,
/// Telemetry - Completion failure.
TmCompletionFailure = 8, TmCompletionFailure = 8,
} }
@@ -24,13 +33,13 @@ mod tests {
#[test] #[test]
fn test_conv_into_u8() { fn test_conv_into_u8() {
let subservice: u8 = Subservice::TmCompletionSuccess.into(); let subservice: u8 = MessageSubtypeId::TmCompletionSuccess.into();
assert_eq!(subservice, 7); assert_eq!(subservice, 7);
} }
#[test] #[test]
fn test_conv_from_u8() { fn test_conv_from_u8() {
let subservice: Subservice = 7.try_into().unwrap(); let subservice: MessageSubtypeId = 7.try_into().unwrap();
assert_eq!(subservice, Subservice::TmCompletionSuccess); assert_eq!(subservice, MessageSubtypeId::TmCompletionSuccess);
} }
} }
+1998 -46
View File
File diff suppressed because it is too large Load Diff
+423 -32
View File
@@ -1,3 +1,8 @@
//! # Sequence counter module.
//!
//! CCSDS and ECSS packet standard oftentimes use sequence counters, for example to allow detecting
//! packet gaps. This module provides basic abstractions and helper components to implement
//! sequence counters.
use crate::MAX_SEQ_COUNT; use crate::MAX_SEQ_COUNT;
use arbitrary_int::traits::Integer; use arbitrary_int::traits::Integer;
use core::cell::Cell; use core::cell::Cell;
@@ -9,28 +14,33 @@ use paste::paste;
/// static structs when using the interior mutability pattern. This can be achieved by using /// static structs when using the interior mutability pattern. This can be achieved by using
/// [Cell], [core::cell::RefCell] or atomic types. /// [Cell], [core::cell::RefCell] or atomic types.
pub trait SequenceCounter { pub trait SequenceCounter {
/// Raw type of the counter.
type Raw: Into<u64>; type Raw: Into<u64>;
const MAX_BIT_WIDTH: usize;
/// Bit width of the counter.
fn max_bit_width(&self) -> usize;
/// Get the current sequence count value.
fn get(&self) -> Self::Raw; fn get(&self) -> Self::Raw;
/// Increment the sequence count by one.
fn increment(&self); fn increment(&self);
fn increment_mut(&mut self) { /// Get the current sequence count value and increment the counter by one.
self.increment();
}
fn get_and_increment(&self) -> Self::Raw { fn get_and_increment(&self) -> Self::Raw {
let val = self.get(); let val = self.get();
self.increment(); self.increment();
val val
} }
fn get_and_increment_mut(&mut self) -> Self::Raw { /// Set the sequence counter.
self.get_and_increment() ///
} /// This should not be required by default but can be used to reset the counter
/// or initialize it with a custom value.
fn set(&self, value: Self::Raw);
} }
/// Simple sequence counter which wraps at ´T::MAX´.
#[derive(Clone)] #[derive(Clone)]
pub struct SequenceCounterSimple<T: Copy> { pub struct SequenceCounterSimple<T: Copy> {
seq_count: Cell<T>, seq_count: Cell<T>,
@@ -43,13 +53,16 @@ macro_rules! impl_for_primitives {
$( $(
paste! { paste! {
impl SequenceCounterSimple<$ty> { impl SequenceCounterSimple<$ty> {
pub fn [<new_custom_max_val_ $ty>](max_val: $ty) -> Self { /// Constructor with a custom maximum value.
pub const fn [<new_custom_max_val_ $ty>](max_val: $ty) -> Self {
Self { Self {
seq_count: Cell::new(0), seq_count: Cell::new(0),
max_val, max_val,
} }
} }
pub fn [<new_ $ty>]() -> Self {
/// Generic constructor.
pub const fn [<new_ $ty>]() -> Self {
Self { Self {
seq_count: Cell::new(0), seq_count: Cell::new(0),
max_val: $ty::MAX max_val: $ty::MAX
@@ -65,16 +78,23 @@ macro_rules! impl_for_primitives {
impl SequenceCounter for SequenceCounterSimple<$ty> { impl SequenceCounter for SequenceCounterSimple<$ty> {
type Raw = $ty; type Raw = $ty;
const MAX_BIT_WIDTH: usize = core::mem::size_of::<Self::Raw>() * 8;
#[inline]
fn max_bit_width(&self) -> usize {
core::mem::size_of::<Self::Raw>() * 8
}
#[inline]
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.seq_count.get() self.seq_count.get()
} }
#[inline]
fn increment(&self) { fn increment(&self) {
self.get_and_increment(); self.get_and_increment();
} }
#[inline]
fn get_and_increment(&self) -> Self::Raw { fn get_and_increment(&self) -> Self::Raw {
let curr_count = self.seq_count.get(); let curr_count = self.seq_count.get();
@@ -85,6 +105,11 @@ macro_rules! impl_for_primitives {
} }
curr_count curr_count
} }
#[inline]
fn set(&self, value: Self::Raw) {
self.seq_count.set(value);
}
} }
} }
)+ )+
@@ -100,6 +125,7 @@ pub struct SequenceCounterCcsdsSimple {
} }
impl Default for SequenceCounterCcsdsSimple { impl Default for SequenceCounterCcsdsSimple {
#[inline]
fn default() -> Self { fn default() -> Self {
Self { Self {
provider: SequenceCounterSimple::new_custom_max_val_u16(MAX_SEQ_COUNT.as_u16()), provider: SequenceCounterSimple::new_custom_max_val_u16(MAX_SEQ_COUNT.as_u16()),
@@ -109,7 +135,6 @@ impl Default for SequenceCounterCcsdsSimple {
impl SequenceCounter for SequenceCounterCcsdsSimple { impl SequenceCounter for SequenceCounterCcsdsSimple {
type Raw = u16; type Raw = u16;
const MAX_BIT_WIDTH: usize = core::mem::size_of::<Self::Raw>() * 8;
delegate::delegate! { delegate::delegate! {
to self.provider { to self.provider {
fn get(&self) -> u16; fn get(&self) -> u16;
@@ -117,134 +142,252 @@ impl SequenceCounter for SequenceCounterCcsdsSimple {
fn get_and_increment(&self) -> u16; fn get_and_increment(&self) -> u16;
} }
} }
#[inline]
fn set(&self, value: u16) {
if value > MAX_SEQ_COUNT.as_u16() {
return;
}
self.provider.set(value);
}
#[inline]
fn max_bit_width(&self) -> usize {
Self::MAX_BIT_WIDTH
}
} }
impl SequenceCounterCcsdsSimple {
/// Maximum bit width for CCSDS packet sequence counter is 14 bits.
pub const MAX_BIT_WIDTH: usize = 14;
/// Create a new sequence counter specifically for the sequence count of CCSDS packets.
///
/// It has a [Self::MAX_BIT_WIDTH] of 14.
pub const fn new() -> Self {
Self {
provider: SequenceCounterSimple::new_custom_max_val_u16(MAX_SEQ_COUNT.value()),
}
}
}
#[cfg(target_has_atomic = "8")]
impl SequenceCounter for core::sync::atomic::AtomicU8 { impl SequenceCounter for core::sync::atomic::AtomicU8 {
type Raw = u8; type Raw = u8;
const MAX_BIT_WIDTH: usize = 8; #[inline]
fn max_bit_width(&self) -> usize {
8
}
#[inline]
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(core::sync::atomic::Ordering::Relaxed)
} }
#[inline]
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
} }
#[inline]
fn set(&self, value: u8) {
self.store(value, core::sync::atomic::Ordering::Relaxed);
}
} }
#[cfg(target_has_atomic = "16")]
impl SequenceCounter for core::sync::atomic::AtomicU16 { impl SequenceCounter for core::sync::atomic::AtomicU16 {
type Raw = u16; type Raw = u16;
const MAX_BIT_WIDTH: usize = 16; #[inline]
fn max_bit_width(&self) -> usize {
16
}
#[inline]
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(core::sync::atomic::Ordering::Relaxed)
} }
#[inline]
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
} }
#[inline]
fn set(&self, value: u16) {
self.store(value, core::sync::atomic::Ordering::Relaxed);
}
} }
#[cfg(target_has_atomic = "32")]
impl SequenceCounter for core::sync::atomic::AtomicU32 { impl SequenceCounter for core::sync::atomic::AtomicU32 {
type Raw = u32; type Raw = u32;
const MAX_BIT_WIDTH: usize = 32; #[inline]
fn max_bit_width(&self) -> usize {
32
}
#[inline]
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(core::sync::atomic::Ordering::Relaxed)
} }
#[inline]
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
} }
#[inline]
fn set(&self, value: u32) {
self.store(value, core::sync::atomic::Ordering::Relaxed);
}
} }
#[cfg(target_has_atomic = "64")] #[cfg(target_has_atomic = "64")]
impl SequenceCounter for core::sync::atomic::AtomicU64 { impl SequenceCounter for core::sync::atomic::AtomicU64 {
type Raw = u64; type Raw = u64;
const MAX_BIT_WIDTH: usize = 64; #[inline]
fn max_bit_width(&self) -> usize {
64
}
#[inline]
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(core::sync::atomic::Ordering::Relaxed)
} }
#[inline]
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
} }
#[inline]
fn set(&self, value: u64) {
self.store(value, core::sync::atomic::Ordering::Relaxed);
}
} }
#[cfg(feature = "portable-atomic")]
impl SequenceCounter for portable_atomic::AtomicU8 { impl SequenceCounter for portable_atomic::AtomicU8 {
type Raw = u8; type Raw = u8;
const MAX_BIT_WIDTH: usize = 8; #[inline]
fn max_bit_width(&self) -> usize {
8
}
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(portable_atomic::Ordering::Relaxed)
} }
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, portable_atomic::Ordering::Relaxed);
}
fn set(&self, value: Self::Raw) {
self.store(value, portable_atomic::Ordering::Relaxed);
} }
} }
#[cfg(feature = "portable-atomic")]
impl SequenceCounter for portable_atomic::AtomicU16 { impl SequenceCounter for portable_atomic::AtomicU16 {
type Raw = u16; type Raw = u16;
const MAX_BIT_WIDTH: usize = 16; #[inline]
fn max_bit_width(&self) -> usize {
16
}
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(portable_atomic::Ordering::Relaxed)
} }
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, portable_atomic::Ordering::Relaxed);
}
fn set(&self, value: Self::Raw) {
self.store(value, portable_atomic::Ordering::Relaxed);
} }
} }
#[cfg(feature = "portable-atomic")]
impl SequenceCounter for portable_atomic::AtomicU32 { impl SequenceCounter for portable_atomic::AtomicU32 {
type Raw = u32; type Raw = u32;
const MAX_BIT_WIDTH: usize = 32; #[inline]
fn max_bit_width(&self) -> usize {
32
}
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(portable_atomic::Ordering::Relaxed)
} }
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, portable_atomic::Ordering::Relaxed);
}
fn set(&self, value: Self::Raw) {
self.store(value, portable_atomic::Ordering::Relaxed);
} }
} }
#[cfg(feature = "portable-atomic")]
impl SequenceCounter for portable_atomic::AtomicU64 { impl SequenceCounter for portable_atomic::AtomicU64 {
type Raw = u64; type Raw = u64;
const MAX_BIT_WIDTH: usize = 64; #[inline]
fn max_bit_width(&self) -> usize {
64
}
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed) self.load(portable_atomic::Ordering::Relaxed)
} }
fn increment(&self) { fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.fetch_add(1, portable_atomic::Ordering::Relaxed);
}
fn set(&self, value: Self::Raw) {
self.store(value, portable_atomic::Ordering::Relaxed);
} }
} }
impl<T: SequenceCounter + ?Sized> SequenceCounter for &T { impl<T: SequenceCounter + ?Sized> SequenceCounter for &T {
type Raw = T::Raw; type Raw = T::Raw;
const MAX_BIT_WIDTH: usize = T::MAX_BIT_WIDTH;
#[inline]
fn max_bit_width(&self) -> usize {
(**self).max_bit_width()
}
#[inline]
fn get(&self) -> Self::Raw { fn get(&self) -> Self::Raw {
(**self).get() (**self).get()
} }
#[inline]
fn increment(&self) { fn increment(&self) {
(**self).increment() (**self).increment()
} }
fn set(&self, value: Self::Raw) {
(**self).set(value);
}
} }
#[cfg(any(
target_has_atomic = "8",
target_has_atomic = "16",
target_has_atomic = "32",
target_has_atomic = "64"
))]
macro_rules! sync_clonable_seq_counter_impl { macro_rules! sync_clonable_seq_counter_impl {
($ty: ident) => { ($ty: ident) => {
paste::paste! { paste::paste! {
@@ -257,6 +400,7 @@ macro_rules! sync_clonable_seq_counter_impl {
} }
impl [<SequenceCounterSyncCustomWrap $ty:upper>] { impl [<SequenceCounterSyncCustomWrap $ty:upper>] {
/// Generic constructor.
pub fn new(max_val: $ty) -> Self { pub fn new(max_val: $ty) -> Self {
Self { Self {
seq_count: core::sync::atomic::[<Atomic $ty:upper>]::new(0), seq_count: core::sync::atomic::[<Atomic $ty:upper>]::new(0),
@@ -267,7 +411,10 @@ macro_rules! sync_clonable_seq_counter_impl {
impl SequenceCounter for [<SequenceCounterSyncCustomWrap $ty:upper>] { impl SequenceCounter for [<SequenceCounterSyncCustomWrap $ty:upper>] {
type Raw = $ty; type Raw = $ty;
const MAX_BIT_WIDTH: usize = core::mem::size_of::<Self::Raw>() * 8;
fn max_bit_width(&self) -> usize {
core::mem::size_of::<Self::Raw>() * 8
}
fn get(&self) -> $ty { fn get(&self) -> $ty {
self.seq_count.load(core::sync::atomic::Ordering::Relaxed) self.seq_count.load(core::sync::atomic::Ordering::Relaxed)
@@ -288,6 +435,10 @@ macro_rules! sync_clonable_seq_counter_impl {
}, },
).unwrap() ).unwrap()
} }
fn set(&self, value: $ty) {
self.seq_count.store(value, core::sync::atomic::Ordering::Relaxed);
}
} }
} }
}; };
@@ -302,9 +453,187 @@ sync_clonable_seq_counter_impl!(u32);
#[cfg(target_has_atomic = "64")] #[cfg(target_has_atomic = "64")]
sync_clonable_seq_counter_impl!(u64); sync_clonable_seq_counter_impl!(u64);
/// Modules relying on [std] support.
#[cfg(feature = "std")]
pub mod std_mod {
use super::*;
use core::str::FromStr;
use std::path::{Path, PathBuf};
use std::string::ToString as _;
use std::{fs, io};
/// A persistent file-backed sequence counter that can wrap any other [SequenceCounter]
/// implementation which is non-persistent.
///
/// In the default configuration, the underlying [SequenceCounter] is initialized from the file
/// content, and the file content will only be updated on a manual [Self::save] or on drop.
#[derive(Debug, PartialEq, Eq)]
pub struct SequenceCounterOnFile<
Inner: SequenceCounter<Raw = RawTy>,
RawTy: core::fmt::Debug
+ Copy
+ Clone
+ Into<u64>
+ TryFrom<u64>
+ FromStr
+ Default
+ PartialEq
+ Eq,
> {
path: PathBuf,
inner: Inner,
/// Configures whether the counter value is saved to disk when the object is dropped.
///
/// If this is set to [true] which is the default, the sequence counter will only be stored
/// to disk if the [Self::save] method is used or the object is dropped. Otherwise, the
/// counter will be saved to disk on every [Self::increment] or [Self::set].
pub save_on_drop: bool,
}
impl<
Inner: SequenceCounter<Raw = RawTy>,
RawTy: core::fmt::Debug
+ Copy
+ Clone
+ Into<u64>
+ TryFrom<u64>
+ FromStr
+ Default
+ PartialEq
+ Eq,
> SequenceCounterOnFile<Inner, RawTy>
{
/// Initialize a new persistent sequence counter using a file at the given path and
/// any non persistent inner [SequenceCounter] implementation.
pub fn new<P: AsRef<Path>>(path: P, inner: Inner) -> io::Result<Self> {
let path = path.as_ref().to_path_buf();
let value = Self::load_from_path(&path);
inner.set(value);
Ok(Self {
path,
inner,
save_on_drop: true,
})
}
fn load_from_path(path: &Path) -> RawTy {
let bytes = match fs::read(path) {
Ok(b) => b,
Err(_) => return Default::default(),
};
// Trim optional single trailing newline (Unix/Windows)
let trimmed = match bytes.last() {
Some(&b'\n') => &bytes[..bytes.len() - 1],
_ => &bytes,
};
// Reject non-ASCII
if !trimmed.is_ascii() {
return Default::default();
}
// Parse
std::str::from_utf8(trimmed)
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or_default()
}
/// Persist the current value to disk (best-effort).
pub fn save(&self) -> io::Result<()> {
let value = self.inner.get();
std::fs::write(&self.path, value.into().to_string())
}
}
impl<
Inner: SequenceCounter<Raw = RawTy>,
RawTy: core::fmt::Debug
+ Copy
+ Clone
+ Into<u64>
+ TryFrom<u64, Error: core::fmt::Debug>
+ FromStr
+ Default
+ PartialEq
+ Eq,
> SequenceCounter for SequenceCounterOnFile<Inner, RawTy>
{
type Raw = RawTy;
fn max_bit_width(&self) -> usize {
self.inner.max_bit_width()
}
fn get(&self) -> RawTy {
self.inner.get()
}
fn increment(&self) {
self.inner.increment();
if !self.save_on_drop {
// persist (ignore I/O errors here; caller can call `save` explicitly)
let _ = self.save();
}
}
fn set(&self, value: RawTy) {
self.inner.set(value);
if !self.save_on_drop {
// persist (ignore I/O errors here; caller can call `save` explicitly)
let _ = self.save();
}
}
}
impl<
Inner: SequenceCounter<Raw = RawTy>,
RawTy: core::fmt::Debug
+ Copy
+ Clone
+ Into<u64>
+ TryFrom<u64>
+ FromStr
+ Default
+ PartialEq
+ Eq,
> Drop for SequenceCounterOnFile<Inner, RawTy>
{
fn drop(&mut self) {
if self.save_on_drop {
let _ = self.save();
}
}
}
/// Type alisas for a CCSDS sequence counter stored on file.
pub type SequenceCounterCcsdsOnFile = SequenceCounterOnFile<SequenceCounterCcsdsSimple, u16>;
impl SequenceCounterCcsdsOnFile {
/// Open or create the counter file at `path`.
pub fn new_ccsds_counter<P: AsRef<Path>>(path: P) -> io::Result<Self> {
SequenceCounterOnFile::new(path, SequenceCounterCcsdsSimple::default())
}
}
/// Type alisas for a [u16] sequence counter stored on file.
pub type SequenceCounterU16OnFile = SequenceCounterOnFile<SequenceCounterSimple<u16>, u16>;
impl SequenceCounterU16OnFile {
/// Open or create the counter file at `path`.
pub fn new_u16_counter<P: AsRef<Path>>(path: P) -> io::Result<Self> {
SequenceCounterOnFile::new(path, SequenceCounterSimple::<u16>::default())
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8}; use core::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8};
use std::boxed::Box;
use crate::seq_count::{ use crate::seq_count::{
SequenceCounter, SequenceCounterCcsdsSimple, SequenceCounterSimple, SequenceCounter, SequenceCounterCcsdsSimple, SequenceCounterSimple,
@@ -316,6 +645,7 @@ mod tests {
fn test_u8_counter() { fn test_u8_counter() {
let u8_counter = SequenceCounterSimple::<u8>::default(); let u8_counter = SequenceCounterSimple::<u8>::default();
assert_eq!(u8_counter.get(), 0); assert_eq!(u8_counter.get(), 0);
assert_eq!(u8_counter.max_bit_width(), 8);
assert_eq!(u8_counter.get_and_increment(), 0); assert_eq!(u8_counter.get_and_increment(), 0);
assert_eq!(u8_counter.get_and_increment(), 1); assert_eq!(u8_counter.get_and_increment(), 1);
assert_eq!(u8_counter.get(), 2); assert_eq!(u8_counter.get(), 2);
@@ -353,55 +683,63 @@ mod tests {
assert_eq!(seq_counter.get_and_increment().into(), 0); assert_eq!(seq_counter.get_and_increment().into(), 0);
assert_eq!(seq_counter.get_and_increment().into(), 1); assert_eq!(seq_counter.get_and_increment().into(), 1);
assert_eq!(seq_counter.get().into(), 2); assert_eq!(seq_counter.get().into(), 2);
seq_counter.increment_mut(); seq_counter.increment();
assert_eq!(seq_counter.get().into(), 3); assert_eq!(seq_counter.get().into(), 3);
assert_eq!(seq_counter.get_and_increment_mut().into(), 3); assert_eq!(seq_counter.get_and_increment().into(), 3);
assert_eq!(seq_counter.get().into(), 4); assert_eq!(seq_counter.get().into(), 4);
} }
#[test] #[test]
fn test_atomic_counter_u8() { fn test_atomic_counter_u8() {
let mut sync_u8_counter = AtomicU8::new(0); let mut sync_u8_counter = AtomicU8::new(0);
assert_eq!(sync_u8_counter.max_bit_width(), 8);
common_counter_test(&mut sync_u8_counter); common_counter_test(&mut sync_u8_counter);
} }
#[test] #[test]
fn test_atomic_counter_u16() { fn test_atomic_counter_u16() {
let mut sync_u16_counter = AtomicU16::new(0); let mut sync_u16_counter = AtomicU16::new(0);
assert_eq!(sync_u16_counter.max_bit_width(), 16);
common_counter_test(&mut sync_u16_counter); common_counter_test(&mut sync_u16_counter);
} }
#[test] #[test]
fn test_atomic_counter_u32() { fn test_atomic_counter_u32() {
let mut sync_u32_counter = AtomicU32::new(0); let mut sync_u32_counter = AtomicU32::new(0);
assert_eq!(sync_u32_counter.max_bit_width(), 32);
common_counter_test(&mut sync_u32_counter); common_counter_test(&mut sync_u32_counter);
} }
#[test] #[test]
fn test_atomic_counter_u64() { fn test_atomic_counter_u64() {
let mut sync_u64_counter = AtomicU64::new(0); let mut sync_u64_counter = AtomicU64::new(0);
assert_eq!(sync_u64_counter.max_bit_width(), 64);
common_counter_test(&mut sync_u64_counter); common_counter_test(&mut sync_u64_counter);
} }
#[test] #[test]
#[cfg(feature = "portable-atomic")]
fn test_portable_atomic_counter_u8() { fn test_portable_atomic_counter_u8() {
let mut sync_u8_counter = portable_atomic::AtomicU8::new(0); let mut sync_u8_counter = portable_atomic::AtomicU8::new(0);
common_counter_test(&mut sync_u8_counter); common_counter_test(&mut sync_u8_counter);
} }
#[test] #[test]
#[cfg(feature = "portable-atomic")]
fn test_portable_atomic_counter_u16() { fn test_portable_atomic_counter_u16() {
let mut sync_u16_counter = portable_atomic::AtomicU16::new(0); let mut sync_u16_counter = portable_atomic::AtomicU16::new(0);
common_counter_test(&mut sync_u16_counter); common_counter_test(&mut sync_u16_counter);
} }
#[test] #[test]
#[cfg(feature = "portable-atomic")]
fn test_portable_atomic_counter_u32() { fn test_portable_atomic_counter_u32() {
let mut sync_u32_counter = portable_atomic::AtomicU32::new(0); let mut sync_u32_counter = portable_atomic::AtomicU32::new(0);
common_counter_test(&mut sync_u32_counter); common_counter_test(&mut sync_u32_counter);
} }
#[test] #[test]
#[cfg(feature = "portable-atomic")]
fn test_portable_atomic_counter_u64() { fn test_portable_atomic_counter_u64() {
let mut sync_u64_counter = portable_atomic::AtomicU64::new(0); let mut sync_u64_counter = portable_atomic::AtomicU64::new(0);
common_counter_test(&mut sync_u64_counter); common_counter_test(&mut sync_u64_counter);
@@ -421,6 +759,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "portable-atomic")]
fn test_portable_atomic_u8_counter_overflow() { fn test_portable_atomic_u8_counter_overflow() {
let sync_u8_counter = portable_atomic::AtomicU8::new(0); let sync_u8_counter = portable_atomic::AtomicU8::new(0);
common_overflow_test_u8(&sync_u8_counter); common_overflow_test_u8(&sync_u8_counter);
@@ -434,4 +773,56 @@ mod tests {
} }
assert_eq!(sync_u8_counter.get(), 0); assert_eq!(sync_u8_counter.get(), 0);
} }
#[test]
fn test_dyn_compatible() {
let counter: Box<dyn SequenceCounter<Raw = u16>> =
Box::new(SequenceCounterCcsdsSimple::default());
assert_eq!(counter.get(), 0);
assert_eq!(counter.max_bit_width(), 14);
counter.increment();
assert_eq!(counter.get(), 1);
assert_eq!(counter.get_and_increment(), 1);
assert_eq!(counter.get(), 2);
}
#[test]
fn test_persistent_counter() {
let tempdir = tempfile::tempdir().expect("failed to create temp dir");
let path = tempdir.path().join("seq_count.txt");
let mut persistent_counter =
crate::seq_count::std_mod::SequenceCounterCcsdsOnFile::new_ccsds_counter(&path)
.unwrap();
assert_eq!(persistent_counter.get(), 0);
assert_eq!(persistent_counter.get_and_increment(), 0);
drop(persistent_counter);
assert!(path.exists());
persistent_counter =
crate::seq_count::std_mod::SequenceCounterCcsdsOnFile::new_ccsds_counter(
tempdir.path().join("seq_count.txt"),
)
.unwrap();
assert_eq!(persistent_counter.get(), 1);
}
#[test]
fn test_persistent_couter_manual_save() {
let tempdir = tempfile::tempdir().expect("failed to create temp dir");
let path = tempdir.path().join("seq_count.txt");
let mut persistent_counter =
crate::seq_count::std_mod::SequenceCounterCcsdsOnFile::new_ccsds_counter(&path)
.unwrap();
assert_eq!(persistent_counter.get(), 0);
assert_eq!(persistent_counter.get_and_increment(), 0);
persistent_counter.save().unwrap();
assert!(path.exists());
std::mem::forget(persistent_counter);
persistent_counter =
crate::seq_count::std_mod::SequenceCounterCcsdsOnFile::new_ccsds_counter(
tempdir.path().join("seq_count.txt"),
)
.unwrap();
assert_eq!(persistent_counter.get(), 1);
}
} }
+1
View File
@@ -31,6 +31,7 @@ pub const FMT_STR_CODE_B_WITH_SIZE: (&str, usize) = ("%Y-%jT%T%.3f", 21);
/// Three digits are used for the decimal fraction and a terminator is added at the end. /// Three digits are used for the decimal fraction and a terminator is added at the end.
pub const FMT_STR_CODE_B_TERMINATED_WITH_SIZE: (&str, usize) = ("%Y-%jT%T%.3fZ", 22); pub const FMT_STR_CODE_B_TERMINATED_WITH_SIZE: (&str, usize) = ("%Y-%jT%T%.3fZ", 22);
/// Functions requiring both [chrono] and [alloc] support.
#[cfg(all(feature = "alloc", feature = "chrono"))] #[cfg(all(feature = "alloc", feature = "chrono"))]
pub mod alloc_mod_chrono { pub mod alloc_mod_chrono {
use super::*; use super::*;
+842 -843
View File
File diff suppressed because it is too large Load Diff
+43 -3
View File
@@ -32,6 +32,7 @@ pub const P_FIELD_BASE: u8 = (CcsdsTimeCode::CucCcsdsEpoch as u8) << 4;
/// Maximum length if the preamble field is not extended. /// Maximum length if the preamble field is not extended.
pub const MAX_CUC_LEN_SMALL_PREAMBLE: usize = 8; pub const MAX_CUC_LEN_SMALL_PREAMBLE: usize = 8;
/// Fractional resolution for the fractional part of the CUC time code.
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -70,13 +71,14 @@ pub fn convert_fractional_part_to_ns(fractional_part: FractionalPart) -> u64 {
10_u64.pow(9) * fractional_part.counter as u64 / div as u64 10_u64.pow(9) * fractional_part.counter as u64 / div as u64
} }
/// Convert the fractional resolution to the divisor used to calculate the fractional part.
#[inline(always)] #[inline(always)]
pub const fn fractional_res_to_div(res: FractionalResolution) -> u32 { pub const fn fractional_res_to_div(res: FractionalResolution) -> u32 {
// We do not use the full possible range for a given resolution. This is because if we did // We do not use the full possible range for a given resolution. This is because if we did
// that, the largest value would be equal to the counter being incremented by one. Thus, the // that, the largest value would be equal to the counter being incremented by one. Thus, the
// smallest allowed fractions value is 0 while the largest allowed fractions value is the // smallest allowed fractions value is 0 while the largest allowed fractions value is the
// closest fractions value to the next counter increment. // closest fractions value to the next counter increment.
2_u32.pow(8 * res as u32) - 1 (1u32 << (8 * res as u32)) - 1
} }
/// Calculate the fractional part for a given resolution and subsecond nanoseconds. /// Calculate the fractional part for a given resolution and subsecond nanoseconds.
@@ -101,22 +103,34 @@ pub fn fractional_part_from_subsec_ns(res: FractionalResolution, ns: u64) -> Fra
} }
} }
/// CUC error.
#[derive(Copy, Clone, PartialEq, Eq, Debug, thiserror::Error)] #[derive(Copy, Clone, PartialEq, Eq, Debug, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CucError { pub enum CucError {
/// Invalid CUC counter width.
#[error("invalid cuc counter byte width {0}")] #[error("invalid cuc counter byte width {0}")]
InvalidCounterWidth(u8), InvalidCounterWidth(u8),
/// Invalid counter supplied. /// Invalid counter supplied.
#[error("invalid cuc counter {counter} for width {width}")] #[error("invalid cuc counter {counter} for width {width}")]
InvalidCounter { width: u8, counter: u64 }, InvalidCounter {
/// Width.
width: u8,
/// Counter.
counter: u64,
},
/// Invalid fractions.
#[error("invalid cuc fractional part {value} for resolution {resolution:?}")] #[error("invalid cuc fractional part {value} for resolution {resolution:?}")]
InvalidFractions { InvalidFractions {
/// Resolution.
resolution: FractionalResolution, resolution: FractionalResolution,
/// Value.
value: u64, value: u64,
}, },
/// Error while correcting for leap seconds.
#[error("error while correcting for leap seconds")] #[error("error while correcting for leap seconds")]
LeapSecondCorrectionError, LeapSecondCorrectionError,
/// Data is before the CCSDS epoch.
#[error("date before ccsds epoch: {0}")] #[error("date before ccsds epoch: {0}")]
DateBeforeCcsdsEpoch(#[from] DateBeforeCcsdsEpochError), DateBeforeCcsdsEpoch(#[from] DateBeforeCcsdsEpochError),
} }
@@ -127,14 +141,20 @@ pub enum CucError {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct WidthCounterPair(pub u8, pub u32); pub struct WidthCounterPair(pub u8, pub u32);
/// Fractional part of the CUC time code.
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FractionalPart { pub struct FractionalPart {
/// Resolution.
pub resolution: FractionalResolution, pub resolution: FractionalResolution,
/// Counter.
pub counter: u32, pub counter: u32,
} }
impl FractionalPart { impl FractionalPart {
/// Generic constructor.
///
/// This function will panic if the counter is smaller than the calculated divisor.
#[inline] #[inline]
pub const fn new(resolution: FractionalResolution, counter: u32) -> Self { pub const fn new(resolution: FractionalResolution, counter: u32) -> Self {
let div = fractional_res_to_div(resolution); let div = fractional_res_to_div(resolution);
@@ -157,6 +177,7 @@ impl FractionalPart {
Self::new_with_seconds_resolution() Self::new_with_seconds_resolution()
} }
/// Check constructor which verifies that the counter is larger than the divisor.
#[inline] #[inline]
pub fn new_checked(resolution: FractionalResolution, counter: u32) -> Option<Self> { pub fn new_checked(resolution: FractionalResolution, counter: u32) -> Option<Self> {
let div = fractional_res_to_div(resolution); let div = fractional_res_to_div(resolution);
@@ -169,16 +190,19 @@ impl FractionalPart {
}) })
} }
/// Fractional resolution.
#[inline] #[inline]
pub fn resolution(&self) -> FractionalResolution { pub fn resolution(&self) -> FractionalResolution {
self.resolution self.resolution
} }
/// Counter value.
#[inline] #[inline]
pub fn counter(&self) -> u32 { pub fn counter(&self) -> u32 {
self.counter self.counter
} }
/// Check whether the timestamp does not have a fractional part.
#[inline] #[inline]
pub fn no_fractional_part(&self) -> bool { pub fn no_fractional_part(&self) -> bool {
self.resolution == FractionalResolution::Seconds self.resolution == FractionalResolution::Seconds
@@ -245,17 +269,21 @@ pub struct CucTime {
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CucTimeWithLeapSecs { pub struct CucTimeWithLeapSecs {
/// CUC time.
pub time: CucTime, pub time: CucTime,
/// Leap seconds.
pub leap_seconds: u32, pub leap_seconds: u32,
} }
impl CucTimeWithLeapSecs { impl CucTimeWithLeapSecs {
/// Generic constructor.
#[inline] #[inline]
pub fn new(time: CucTime, leap_seconds: u32) -> Self { pub fn new(time: CucTime, leap_seconds: u32) -> Self {
Self { time, leap_seconds } Self { time, leap_seconds }
} }
} }
/// p-field length.
#[inline] #[inline]
pub fn pfield_len(pfield: u8) -> usize { pub fn pfield_len(pfield: u8) -> usize {
if ((pfield >> 7) & 0b1) == 1 { if ((pfield >> 7) & 0b1) == 1 {
@@ -381,6 +409,7 @@ impl CucTime {
Ok(()) Ok(())
} }
/// Creates a CUC timestamp from a Chrono DateTime object.
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn from_chrono_date_time( pub fn from_chrono_date_time(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
@@ -423,7 +452,7 @@ impl CucTime {
.ok_or(CucError::LeapSecondCorrectionError)?; .ok_or(CucError::LeapSecondCorrectionError)?;
let fractions = let fractions =
fractional_part_from_subsec_ns(res, unix_time.subsec_millis() as u64 * 10_u64.pow(6)); fractional_part_from_subsec_ns(res, unix_time.subsec_millis() as u64 * 10_u64.pow(6));
Self::new_generic(WidthCounterPair(4, counter as u32), fractions) Self::new_generic(WidthCounterPair(4, counter), fractions)
} }
/// Most generic constructor which allows full configurability for the counter and for the /// Most generic constructor which allows full configurability for the counter and for the
@@ -448,21 +477,25 @@ impl CucTime {
}) })
} }
/// CCSDS time code.
#[inline] #[inline]
pub fn ccsds_time_code(&self) -> CcsdsTimeCode { pub fn ccsds_time_code(&self) -> CcsdsTimeCode {
CcsdsTimeCode::CucCcsdsEpoch CcsdsTimeCode::CucCcsdsEpoch
} }
/// Width and counter pair.
#[inline] #[inline]
pub fn width_counter_pair(&self) -> WidthCounterPair { pub fn width_counter_pair(&self) -> WidthCounterPair {
self.counter self.counter
} }
/// Counter width.
#[inline] #[inline]
pub fn counter_width(&self) -> u8 { pub fn counter_width(&self) -> u8 {
self.counter.0 self.counter.0
} }
/// Counter value.
#[inline] #[inline]
pub fn counter(&self) -> u32 { pub fn counter(&self) -> u32 {
self.counter.1 self.counter.1
@@ -474,11 +507,13 @@ impl CucTime {
self.fractions self.fractions
} }
/// Convert to the leap seconds helper.
#[inline] #[inline]
pub fn to_leap_sec_helper(&self, leap_seconds: u32) -> CucTimeWithLeapSecs { pub fn to_leap_sec_helper(&self, leap_seconds: u32) -> CucTimeWithLeapSecs {
CucTimeWithLeapSecs::new(*self, leap_seconds) CucTimeWithLeapSecs::new(*self, leap_seconds)
} }
/// Set the fractional part.
#[inline] #[inline]
pub fn set_fractions(&mut self, fractions: FractionalPart) -> Result<(), CucError> { pub fn set_fractions(&mut self, fractions: FractionalPart) -> Result<(), CucError> {
Self::verify_fractions_value(fractions)?; Self::verify_fractions_value(fractions)?;
@@ -525,16 +560,19 @@ impl CucTime {
self.pfield |= self.fractions.resolution() as u8; self.pfield |= self.fractions.resolution() as u8;
} }
/// Length of the counter from the p-field.
#[inline] #[inline]
pub fn len_cntr_from_pfield(pfield: u8) -> u8 { pub fn len_cntr_from_pfield(pfield: u8) -> u8 {
((pfield >> 2) & 0b11) + 1 ((pfield >> 2) & 0b11) + 1
} }
/// Length of the fractional part from the p-field.
#[inline] #[inline]
pub fn len_fractions_from_pfield(pfield: u8) -> u8 { pub fn len_fractions_from_pfield(pfield: u8) -> u8 {
pfield & 0b11 pfield & 0b11
} }
/// UNIX seconds.
#[inline] #[inline]
pub fn unix_secs(&self, leap_seconds: u32) -> i64 { pub fn unix_secs(&self, leap_seconds: u32) -> i64 {
ccsds_epoch_to_unix_epoch(self.counter.1 as i64) ccsds_epoch_to_unix_epoch(self.counter.1 as i64)
@@ -542,6 +580,7 @@ impl CucTime {
.unwrap() .unwrap()
} }
/// Subsecond milliseconds part of the CUC time.
#[inline] #[inline]
pub fn subsec_millis(&self) -> u16 { pub fn subsec_millis(&self) -> u16 {
(self.subsec_nanos() / 1_000_000) as u16 (self.subsec_nanos() / 1_000_000) as u16
@@ -564,6 +603,7 @@ impl CucTime {
) )
} }
/// Packed length from the raw p-field.
#[inline] #[inline]
pub fn len_packed_from_pfield(pfield: u8) -> usize { pub fn len_packed_from_pfield(pfield: u8) -> usize {
let mut base_len: usize = 1; let mut base_len: usize = 1;
+63 -4
View File
@@ -22,19 +22,29 @@ pub mod ascii;
pub mod cds; pub mod cds;
pub mod cuc; pub mod cuc;
/// Conversion constant for converting CCSDS days to UNIX days.
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383; pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
/// Seconds per day.
pub const SECONDS_PER_DAY: u32 = 86400; pub const SECONDS_PER_DAY: u32 = 86400;
/// Milliseconds per day.
pub const MS_PER_DAY: u32 = SECONDS_PER_DAY * 1000; pub const MS_PER_DAY: u32 = SECONDS_PER_DAY * 1000;
/// Nanoseconds per second.
pub const NANOS_PER_SECOND: u32 = 1_000_000_000; pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
/// CCSDS time code identifiers.
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CcsdsTimeCode { pub enum CcsdsTimeCode {
/// CUC with a CCSDS epoch (1958-01-01T00:00:00+00:00).
CucCcsdsEpoch = 0b001, CucCcsdsEpoch = 0b001,
/// CUC with a custom agency epoch.
CucAgencyEpoch = 0b010, CucAgencyEpoch = 0b010,
/// CDS time code.
Cds = 0b100, Cds = 0b100,
/// CCS time code.
Ccs = 0b101, Ccs = 0b101,
/// Agency defined time code.
AgencyDefined = 0b110, AgencyDefined = 0b110,
} }
@@ -60,44 +70,61 @@ pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result<CcsdsTimeCode, u8> {
CcsdsTimeCode::try_from(raw_bits).map_err(|_| raw_bits) CcsdsTimeCode::try_from(raw_bits).map_err(|_| raw_bits)
} }
/// Date is before the CCSDS epoch.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("date before ccsds epoch: {0:?}")] #[error("date before ccsds epoch: {0:?}")]
pub struct DateBeforeCcsdsEpochError(UnixTime); pub struct DateBeforeCcsdsEpochError(UnixTime);
/// Generic timestamp error.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive] #[non_exhaustive]
pub enum TimestampError { pub enum TimestampError {
/// Invalid time code.
#[error("invalid time code, expected {expected:?}, found {found}")] #[error("invalid time code, expected {expected:?}, found {found}")]
InvalidTimeCode { expected: CcsdsTimeCode, found: u8 }, InvalidTimeCode {
/// Expected time code.
expected: CcsdsTimeCode,
/// Found raw time code.
found: u8,
},
/// Byte conversion error.
#[error("time stamp: byte conversion error: {0}")] #[error("time stamp: byte conversion error: {0}")]
ByteConversion(#[from] ByteConversionError), ByteConversion(#[from] ByteConversionError),
/// CDS timestamp error.
#[error("CDS error: {0}")] #[error("CDS error: {0}")]
Cds(#[from] cds::CdsError), Cds(#[from] cds::CdsError),
/// CUC timestamp error.
#[error("CUC error: {0}")] #[error("CUC error: {0}")]
Cuc(#[from] cuc::CucError), Cuc(#[from] cuc::CucError),
/// Custom epoch is not supported.
#[error("custom epoch not supported")] #[error("custom epoch not supported")]
CustomEpochNotSupported, CustomEpochNotSupported,
} }
/// [std] module.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub mod std_mod { pub mod std_mod {
use crate::time::TimestampError; use crate::time::TimestampError;
use std::time::SystemTimeError; use std::time::SystemTimeError;
use thiserror::Error; use thiserror::Error;
/// [std] timestamp error.
#[derive(Debug, Clone, Error)] #[derive(Debug, Clone, Error)]
pub enum StdTimestampError { pub enum StdTimestampError {
/// System time error.
#[error("system time error: {0:?}")] #[error("system time error: {0:?}")]
SystemTime(#[from] SystemTimeError), SystemTime(#[from] SystemTimeError),
/// Generic timestamp error.
#[error("timestamp error: {0}")] #[error("timestamp error: {0}")]
Timestamp(#[from] TimestampError), Timestamp(#[from] TimestampError),
} }
} }
/// Seconds since epoch for the current system time.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn seconds_since_epoch() -> f64 { pub fn seconds_since_epoch() -> f64 {
SystemTime::now() SystemTime::now()
@@ -131,16 +158,19 @@ pub const fn unix_epoch_to_ccsds_epoch(unix_epoch: i64) -> i64 {
unix_epoch - (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64) unix_epoch - (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
} }
/// Convert CCSDS epoch to UNIX epoch.
#[inline] #[inline]
pub const fn ccsds_epoch_to_unix_epoch(ccsds_epoch: i64) -> i64 { pub const fn ccsds_epoch_to_unix_epoch(ccsds_epoch: i64) -> i64 {
ccsds_epoch + (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64) ccsds_epoch + (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
} }
/// Milliseconds of day for the current system time.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn ms_of_day_using_sysclock() -> u32 { pub fn ms_of_day_using_sysclock() -> u32 {
ms_of_day(seconds_since_epoch()) ms_of_day(seconds_since_epoch())
} }
/// Milliseconds for the given seconds since epoch.
pub fn ms_of_day(seconds_since_epoch: f64) -> u32 { pub fn ms_of_day(seconds_since_epoch: f64) -> u32 {
let fraction_ms = seconds_since_epoch - seconds_since_epoch.floor(); let fraction_ms = seconds_since_epoch - seconds_since_epoch.floor();
let ms_of_day: u32 = (((seconds_since_epoch.floor() as u32 % SECONDS_PER_DAY) * 1000) as f64 let ms_of_day: u32 = (((seconds_since_epoch.floor() as u32 % SECONDS_PER_DAY) * 1000) as f64
@@ -149,13 +179,16 @@ pub fn ms_of_day(seconds_since_epoch: f64) -> u32 {
ms_of_day ms_of_day
} }
/// Generic writable timestamp trait.
pub trait TimeWriter { pub trait TimeWriter {
/// Written length.
fn len_written(&self) -> usize; fn len_written(&self) -> usize;
/// Generic function to convert write a timestamp into a raw buffer. /// Generic function to convert write a timestamp into a raw buffer.
/// Returns the number of written bytes on success. /// Returns the number of written bytes on success.
fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError>; fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError>;
/// Convert to a owned [alloc::vec::Vec].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<alloc::vec::Vec<u8>, TimestampError> { fn to_vec(&self) -> Result<alloc::vec::Vec<u8>, TimestampError> {
let mut vec = alloc::vec![0; self.len_written()]; let mut vec = alloc::vec![0; self.len_written()];
@@ -164,7 +197,9 @@ pub trait TimeWriter {
} }
} }
/// Genmeric readable timestamp trait.
pub trait TimeReader: Sized { pub trait TimeReader: Sized {
/// Create a timestamp from a raw byte buffer.
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>; fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>;
} }
@@ -174,6 +209,7 @@ pub trait TimeReader: Sized {
/// practical because they are a very common and simple exchange format for time information. /// practical because they are a very common and simple exchange format for time information.
/// Therefore, it was decided to keep them in this trait as well. /// Therefore, it was decided to keep them in this trait as well.
pub trait CcsdsTimeProvider { pub trait CcsdsTimeProvider {
/// Length when written to bytes.
fn len_as_bytes(&self) -> usize; fn len_as_bytes(&self) -> usize;
/// Returns the pfield of the time provider. The pfield can have one or two bytes depending /// Returns the pfield of the time provider. The pfield can have one or two bytes depending
@@ -181,24 +217,37 @@ pub trait CcsdsTimeProvider {
/// entry denotes the length of the pfield and the second entry is the value of the pfield /// entry denotes the length of the pfield and the second entry is the value of the pfield
/// in big endian format. /// in big endian format.
fn p_field(&self) -> (usize, [u8; 2]); fn p_field(&self) -> (usize, [u8; 2]);
/// CCSDS time code field.
fn ccdsd_time_code(&self) -> CcsdsTimeCode; fn ccdsd_time_code(&self) -> CcsdsTimeCode;
fn unix_secs(&self) -> i64; /// UNIX time as seconds.
fn subsec_nanos(&self) -> u32; fn unix_secs(&self) -> i64 {
self.unix_time().secs
}
/// Subsecond nanoseconds.
fn subsec_nanos(&self) -> u32 {
self.unix_time().subsec_nanos
}
/// Subsecond milliseconds.
fn subsec_millis(&self) -> u16 { fn subsec_millis(&self) -> u16 {
(self.subsec_nanos() / 1_000_000) as u16 (self.subsec_nanos() / 1_000_000) as u16
} }
/// UNIX time.
fn unix_time(&self) -> UnixTime { fn unix_time(&self) -> UnixTime {
UnixTime::new(self.unix_secs(), self.subsec_nanos()) UnixTime::new(self.unix_secs(), self.subsec_nanos())
} }
/// [chrono] date time.
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> { fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
chrono::Utc.timestamp_opt(self.unix_secs(), self.subsec_nanos()) chrono::Utc.timestamp_opt(self.unix_secs(), self.subsec_nanos())
} }
/// [time] library date] library date time.
#[cfg(feature = "timelib")] #[cfg(feature = "timelib")]
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> { fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
Ok(time::OffsetDateTime::from_unix_timestamp(self.unix_secs())? Ok(time::OffsetDateTime::from_unix_timestamp(self.unix_secs())?
@@ -280,6 +329,7 @@ impl UnixTime {
} }
} }
/// New UNIX time with only seconds, subseconds set to zero.
pub fn new_only_secs(unix_seconds: i64) -> Self { pub fn new_only_secs(unix_seconds: i64) -> Self {
Self { Self {
secs: unix_seconds, secs: unix_seconds,
@@ -287,15 +337,18 @@ impl UnixTime {
} }
} }
/// Sub-second milliseconds.
#[inline] #[inline]
pub fn subsec_millis(&self) -> u16 { pub fn subsec_millis(&self) -> u16 {
(self.subsec_nanos / 1_000_000) as u16 (self.subsec_nanos / 1_000_000) as u16
} }
/// Sub-second nanoseconds.
pub fn subsec_nanos(&self) -> u32 { pub fn subsec_nanos(&self) -> u32 {
self.subsec_nanos self.subsec_nanos
} }
/// Create a UNIX timestamp from the current system time.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now() -> Result<Self, SystemTimeError> { pub fn now() -> Result<Self, SystemTimeError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
@@ -303,27 +356,31 @@ impl UnixTime {
Ok(Self::new(epoch as i64, now.subsec_nanos())) Ok(Self::new(epoch as i64, now.subsec_nanos()))
} }
/// UNIX timestamp as a floating point number in seconds.
#[inline] #[inline]
pub fn unix_secs_f64(&self) -> f64 { pub fn unix_secs_f64(&self) -> f64 {
self.secs as f64 + (self.subsec_nanos as f64 / 1_000_000_000.0) self.secs as f64 + (self.subsec_nanos as f64 / 1_000_000_000.0)
} }
/// UNIX timestamp as seconds, discards the sub-second part.
pub fn as_secs(&self) -> i64 { pub fn as_secs(&self) -> i64 {
self.secs self.secs
} }
/// UNIX timestamp as [chrono] date time.
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> { pub fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
Utc.timestamp_opt(self.secs, self.subsec_nanos) Utc.timestamp_opt(self.secs, self.subsec_nanos)
} }
/// UNIX timestamp as [time] library date time.
#[cfg(feature = "timelib")] #[cfg(feature = "timelib")]
pub fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> { pub fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
Ok(time::OffsetDateTime::from_unix_timestamp(self.as_secs())? Ok(time::OffsetDateTime::from_unix_timestamp(self.as_secs())?
+ time::Duration::nanoseconds(self.subsec_nanos().into())) + time::Duration::nanoseconds(self.subsec_nanos().into()))
} }
// Calculate the difference in milliseconds between two UnixTimestamps /// Calculate the difference in milliseconds between two UnixTimestamps
pub fn diff_in_millis(&self, other: &UnixTime) -> Option<i64> { pub fn diff_in_millis(&self, other: &UnixTime) -> Option<i64> {
let seconds_difference = self.secs.checked_sub(other.secs)?; let seconds_difference = self.secs.checked_sub(other.secs)?;
// Convert seconds difference to milliseconds // Convert seconds difference to milliseconds
@@ -393,7 +450,9 @@ impl Ord for UnixTime {
/// so the sign information is supplied separately. /// so the sign information is supplied separately.
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub struct StampDiff { pub struct StampDiff {
/// Positive duration flag.
pub positive_duration: bool, pub positive_duration: bool,
/// Absolute duration.
pub duration_absolute: Duration, pub duration_absolute: Duration,
} }
+713 -151
View File
File diff suppressed because it is too large Load Diff
+45 -13
View File
@@ -1,12 +1,16 @@
//! # Utility module.
use crate::ByteConversionError; use crate::ByteConversionError;
use core::fmt::Debug; use core::fmt::Debug;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Helper traits for types which can be converted to a byte array.
pub trait ToBeBytes { pub trait ToBeBytes {
/// Concrete byte array type.
type ByteArray: AsRef<[u8]>; type ByteArray: AsRef<[u8]>;
/// Length when written to big endian bytes. /// Length when written to big endian bytes.
fn written_len(&self) -> usize; fn written_len(&self) -> usize;
/// Convert to big endian byte array.
fn to_be_bytes(&self) -> Self::ByteArray; fn to_be_bytes(&self) -> Self::ByteArray;
} }
@@ -80,14 +84,17 @@ impl ToBeBytes for u64 {
} }
} }
/// Helper trait for unsigned enumerations.
pub trait UnsignedEnum { pub trait UnsignedEnum {
/// Size of the unsigned enumeration in bytes. /// Size of the unsigned enumeration in bytes.
fn size(&self) -> usize; fn size(&self) -> usize;
/// Write the unsigned enumeration to a raw buffer. Returns the written size on success. /// Write the unsigned enumeration to a raw buffer. Returns the written size on success.
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>; fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
fn value(&self) -> u64; /// Type-erased raw value.
fn value_raw(&self) -> u64;
/// Convert to a [alloc::vec::Vec].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn to_vec(&self) -> alloc::vec::Vec<u8> { fn to_vec(&self) -> alloc::vec::Vec<u8> {
let mut buf = alloc::vec![0; self.size()]; let mut buf = alloc::vec![0; self.size()];
@@ -96,22 +103,32 @@ pub trait UnsignedEnum {
} }
} }
/// Extension trait for unsigned enumerations.
pub trait UnsignedEnumExt: UnsignedEnum + Debug + Copy + Clone + PartialEq + Eq {} pub trait UnsignedEnumExt: UnsignedEnum + Debug + Copy + Clone + PartialEq + Eq {}
/// Unsigned byte field errors.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UnsignedByteFieldError { pub enum UnsignedByteFieldError {
/// Value is too large for specified width of byte field. /// Value is too large for specified width of byte field.
#[error("value {value} too large for width {width}")] #[error("value {value} too large for width {width}")]
ValueTooLargeForWidth { width: usize, value: u64 }, ValueTooLargeForWidth {
/// Width in bytes.
width: usize,
/// Value.
value: u64,
},
/// Only 1, 2, 4 and 8 are allow width values. Optionally contains the expected width if /// Only 1, 2, 4 and 8 are allow width values. Optionally contains the expected width if
/// applicable, for example for conversions. /// applicable, for example for conversions.
#[error("invalid width {found}, expected {expected:?}")] #[error("invalid width {found}, expected {expected:?}")]
InvalidWidth { InvalidWidth {
/// Found width.
found: usize, found: usize,
/// Expected width.
expected: Option<usize>, expected: Option<usize>,
}, },
/// Error during byte conversion.
#[error("byte conversion error: {0}")] #[error("byte conversion error: {0}")]
ByteConversionError(#[from] ByteConversionError), ByteConversionError(#[from] ByteConversionError),
} }
@@ -126,16 +143,19 @@ pub struct UnsignedByteField {
} }
impl UnsignedByteField { impl UnsignedByteField {
/// Generic constructor.
#[inline] #[inline]
pub const fn new(width: usize, value: u64) -> Self { pub const fn new(width: usize, value: u64) -> Self {
Self { width, value } Self { width, value }
} }
/// Type-erased raw value.
#[inline] #[inline]
pub const fn value_const(&self) -> u64 { pub const fn value(&self) -> u64 {
self.value self.value
} }
/// Construct from raw bytes, assuming big-endian byte order.
#[inline] #[inline]
pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result<Self, UnsignedByteFieldError> { pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result<Self, UnsignedByteFieldError> {
if width > buf.len() { if width > buf.len() {
@@ -175,8 +195,8 @@ impl UnsignedEnum for UnsignedByteField {
} }
#[inline] #[inline]
fn value(&self) -> u64 { fn value_raw(&self) -> u64 {
self.value_const() self.value()
} }
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
@@ -212,6 +232,7 @@ impl UnsignedEnum for UnsignedByteField {
} }
} }
/// Generic type erased unsigned byte field.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -220,11 +241,13 @@ pub struct GenericUnsignedByteField<TYPE: Copy + Into<u64>> {
} }
impl<TYPE: Copy + Into<u64>> GenericUnsignedByteField<TYPE> { impl<TYPE: Copy + Into<u64>> GenericUnsignedByteField<TYPE> {
/// Generic constructor.
pub const fn new(val: TYPE) -> Self { pub const fn new(val: TYPE) -> Self {
Self { value: val } Self { value: val }
} }
pub const fn value_typed(&self) -> TYPE { /// Raw value.
pub const fn value(&self) -> TYPE {
self.value self.value
} }
} }
@@ -247,20 +270,29 @@ impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericUnsignedByteFie
} }
#[inline] #[inline]
fn value(&self) -> u64 { fn value_raw(&self) -> u64 {
self.value_typed().into() self.value().into()
} }
} }
/// Alias for [GenericUnsignedByteField] with [()] generic.
pub type UnsignedByteFieldEmpty = GenericUnsignedByteField<()>; pub type UnsignedByteFieldEmpty = GenericUnsignedByteField<()>;
/// Alias for [GenericUnsignedByteField] with [u8] generic.
pub type UnsignedByteFieldU8 = GenericUnsignedByteField<u8>; pub type UnsignedByteFieldU8 = GenericUnsignedByteField<u8>;
/// Alias for [GenericUnsignedByteField] with [u16] generic.
pub type UnsignedByteFieldU16 = GenericUnsignedByteField<u16>; pub type UnsignedByteFieldU16 = GenericUnsignedByteField<u16>;
/// Alias for [GenericUnsignedByteField] with [u32] generic.
pub type UnsignedByteFieldU32 = GenericUnsignedByteField<u32>; pub type UnsignedByteFieldU32 = GenericUnsignedByteField<u32>;
/// Alias for [GenericUnsignedByteField] with [u64] generic.
pub type UnsignedByteFieldU64 = GenericUnsignedByteField<u64>; pub type UnsignedByteFieldU64 = GenericUnsignedByteField<u64>;
/// Alias for [UnsignedByteFieldU8]
pub type UbfU8 = UnsignedByteFieldU8; pub type UbfU8 = UnsignedByteFieldU8;
/// Alias for [UnsignedByteFieldU16]
pub type UbfU16 = UnsignedByteFieldU16; pub type UbfU16 = UnsignedByteFieldU16;
/// Alias for [UnsignedByteFieldU32]
pub type UbfU32 = UnsignedByteFieldU32; pub type UbfU32 = UnsignedByteFieldU32;
/// Alias for [UnsignedByteFieldU64]
pub type UbfU64 = UnsignedByteFieldU64; pub type UbfU64 = UnsignedByteFieldU64;
impl From<UnsignedByteFieldU8> for UnsignedByteField { impl From<UnsignedByteFieldU8> for UnsignedByteField {
@@ -351,7 +383,7 @@ impl TryFrom<UnsignedByteField> for UnsignedByteFieldU64 {
} }
#[cfg(test)] #[cfg(test)]
pub mod tests { mod tests {
use crate::util::{ use crate::util::{
UnsignedByteField, UnsignedByteFieldError, UnsignedByteFieldU16, UnsignedByteFieldU32, UnsignedByteField, UnsignedByteFieldError, UnsignedByteFieldU16, UnsignedByteFieldU32,
UnsignedByteFieldU64, UnsignedByteFieldU8, UnsignedEnum, UnsignedByteFieldU64, UnsignedByteFieldU8, UnsignedEnum,
@@ -372,7 +404,7 @@ pub mod tests {
for val in buf.iter().skip(1) { for val in buf.iter().skip(1) {
assert_eq!(*val, 0); assert_eq!(*val, 0);
} }
assert_eq!(u8.value_typed(), 5); assert_eq!(u8.value_raw(), 5);
assert_eq!(u8.value(), 5); assert_eq!(u8.value(), 5);
} }
@@ -390,7 +422,7 @@ pub mod tests {
for val in buf.iter().skip(2) { for val in buf.iter().skip(2) {
assert_eq!(*val, 0); assert_eq!(*val, 0);
} }
assert_eq!(u16.value_typed(), 3823); assert_eq!(u16.value_raw(), 3823);
assert_eq!(u16.value(), 3823); assert_eq!(u16.value(), 3823);
} }
@@ -408,7 +440,7 @@ pub mod tests {
(4..8).for_each(|i| { (4..8).for_each(|i| {
assert_eq!(buf[i], 0); assert_eq!(buf[i], 0);
}); });
assert_eq!(u32.value_typed(), 80932); assert_eq!(u32.value_raw(), 80932);
assert_eq!(u32.value(), 80932); assert_eq!(u32.value(), 80932);
} }
@@ -423,7 +455,7 @@ pub mod tests {
assert_eq!(len, 8); assert_eq!(len, 8);
let raw_val = u64::from_be_bytes(buf[0..8].try_into().unwrap()); let raw_val = u64::from_be_bytes(buf[0..8].try_into().unwrap());
assert_eq!(raw_val, 5999999); assert_eq!(raw_val, 5999999);
assert_eq!(u64.value_typed(), 5999999); assert_eq!(u64.value_raw(), 5999999);
assert_eq!(u64.value(), 5999999); assert_eq!(u64.value(), 5999999);
} }