Merge tag 'v0.1.0' into master am: b48675a926 am: 368be876f2 am: ebfc1360c4
Original change: https://android-review.googlesource.com/c/platform/external/rust/pica/+/2367694
Change-Id: I3e843faa358dc134d824eeccabcc90b40e66450b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e5c3b28
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement (CLA). You (or your employer) retain the copyright to your
+contribution; this simply gives us permission to use and redistribute your
+contributions as part of the project. Head over to
+<https://cla.developers.google.com/> to see your current agreements on file or
+to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code Reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..37936d2
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,753 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bytes"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+
+[[package]]
+name = "futures-task"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+
+[[package]]
+name = "futures-util"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "glam"
+version = "0.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3412e74893a912839a67e975aca0c88561e20e5461d2d358a5fa6d3b229fae59"
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "http"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "hyper"
+version = "0.14.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+
+[[package]]
+name = "lock_api"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "mio"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
+dependencies = [
+ "libc",
+ "log",
+ "miow",
+ "ntapi",
+ "wasi",
+ "winapi",
+]
+
+[[package]]
+name = "miow"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys",
+]
+
+[[package]]
+name = "pica"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "glam",
+ "hex",
+ "hyper",
+ "num-derive",
+ "num-traits",
+ "serde",
+ "serde_json",
+ "structopt",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+
+[[package]]
+name = "socket2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "structopt"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
+dependencies = [
+ "clap",
+ "lazy_static",
+ "structopt-derive",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
+dependencies = [
+ "bytes",
+ "libc",
+ "memchr",
+ "mio",
+ "num_cpus",
+ "once_cell",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "winapi",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower-service"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
+
+[[package]]
+name = "tracing"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "want"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..da3611e
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "pica"
+authors = [
+ "Adrien Larbanet",
+ "Charlie Boutier",
+ "David Duarte",
+ "Henri Chataing",
+]
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tokio = { version = "1.17.0", features = ["full"] }
+tokio-stream = { version = "0.1.8", features = ["sync"] }
+bytes = "1"
+anyhow = "1.0.56"
+num-derive = "*"
+num-traits = "*"
+thiserror = "*"
+glam = "0.20.3"
+hyper = { version = "0.14", features = ["server", "stream", "http1", "tcp"] }
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+structopt = "0.3.23"
+hex = "0.4.3"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e6e77b0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8a0ca22
--- /dev/null
+++ b/README.md
@@ -0,0 +1,147 @@
+# Pica
+
+Pica is a virtual UWB Controller implementing the FiRa UCI specification.
+It has been designed for testing UWB ranging capabilities.
+Pica supports the following features:
+
+- Pica keeps an internal representation of a 3-D scene.
+- Pica lets multiple clients connect through TCP sockets.
+ Each new connection spawns an attached UWB subsystem. Connected hosts can
+ interact together as if they existed in a single 3-D scene.
+- Pica implements a nice GUI through a web server.
+- Pica provides HTTP commands to interact with the scene directly such as create and destroy
+ virtual anchors.
+
+# Build and run
+
+```bash
+$> git clone https://github.com/google/pica.git
+$> cd pica/
+$> cargo run
+```
+
+You should have the following output:
+
+```
+Pica: Listening on: 7000
+Pica: Web server started on http://0.0.0.0:3000
+```
+
+You can now open the web interface at `http://0.0.0.0:3000` and the HTTP commands documentation
+at `http://0.0.0.0:3000/openapi`. The scene should be empty and look like this:
+
+![Pica empty scene](./res/empty_scene.png)
+
+# Command line
+
+A command line tool is available to trigger some action such as creating an anchor.
+Run pica in a terminal then open a new one and do:
+```
+$> cd pica/
+$> python3 scripts/console.py
+```
+
+If you hit `Enter`, the console will list you all the available commands:
+```
+device_reset Reset the UWBS.
+get_device_info Retrieve the device information like (UCI version and other vendor specific info).
+get_caps_info Get the capability of the UWBS.
+session_init Initialize the session
+session_deinit Deinitialize the session
+session_set_app_config set APP Configuration Parameters for the requested UWB session.
+session_get_app_config retrieve the current APP Configuration Parameters of the requested UWB session.
+session_get_count Retrieve number of UWB sessions in the UWBS.
+session_get_state Query the current state of the UWB session.
+range_start start a UWB session.
+range_stop Stop a UWB session.
+get_ranging_count Get the number of times ranging has been attempted during the ranging session..
+pica_create_anchor Create a Pica anchor
+pica_destroy_anchor Destroy a Pica anchor
+pica_get_state Return the internal Pica state
+pica_init_uci_device Initialize an uci device
+pica_set_position Set the position of a Device
+```
+
+If you wish to create a virtual anchor:
+
+```bash
+$> cd pica/ && python3 scripts/console.py # If the console is not started yet
+$> --> pica_create_anchor 00:00 # pica_create_anchor <mac_address>
+$> --> pica_create_anchor 00:01 # Create another one
+```
+# Architecture
+
+- *Device* UWB subsystem created for a connected host.
+- *Session* UWB ranging session opened by a connected host.
+- *Anchor* virtual UWB host, responding to ranging requests from
+ connected hosts.
+
+```
+ ┌────────────────────┐
+ │ Web │
+ │ │
+ └─────┬─────────▲────┘
+ │ │ HTTP localhost:3000
+ ┌────────────────────▼─────────┴───────┐
+ │ │
+ │ Pica │
+ │ │
+ │ ┌────────┐ ┌────────┐ ┌────────┐ │
+ │ │Anchor1 │ │Device1 │ │Device2 │ │
+ │ ├────────┤ │ │ │ │ │
+ │ │Anchor2 │ ├────────┤ ├────────┤ │
+ │ ├────────┤ │Session1│ │Session1│ │
+ │ │... │ ├────────┤ ├────────┤ │
+ │ │ │ │Session2│ │Session2│ │
+ │ └────────┘ └──▲──┬──┘ └──▲──┬──┘ │
+ │ │ │ │ │ │
+ └─────────────────┼──┼────────┼──┼─────┘
+ │ │ │ │ TCP localhost:7000
+ ┌──┴──▼──┐ ┌──┴──▼──┐
+ │Client1 │ │Client2 │
+ │ │ │ │
+ ├────────┤ ├────────┤
+ │VirtIO │ │ │
+ ├────────┤ │ │
+ │UWB HAL │ │ │
+ ├────────┤ │Python │
+ │Cuttle │ │console │
+ │fish │ │ │
+ └────────┘ └────────┘
+```
+
+# Http commands
+
+Pica also implements HTTP commands, the documentation is available at `http://0.0.0.0:3000/openapi`.
+The set of HTTP commands let the user interact with Pica amd modify its scene.
+
+# Regenerate uci_packets.rs
+If you haven't use bluetooth_packetgen before, it is a tool from Android. You can build it and use it
+and build it that way:
+```bash
+# Build bluetooth_packetgen
+cd $AOSP_DIR
+source build/envsetup.sh
+lunch <target> # Use target 1 if in doubt
+m bluetooth_packetgen
+export PATH=$PATH:${AOSP_DIR}/out/host/linux-x86/bin/
+
+# Generate the source
+cd $PICA_DIR
+bluetooth_packetgen \
+ --rust \
+ --include=src/ \
+ --out=src/ \
+ src/uci_packets.pdl
+```
+
+Then edit the uci_packet.rs to add clippy guards
+
+```
+#![allow(clippy::all)]
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(unused)]
+#![allow(missing_docs)]
+```
diff --git a/res/empty_scene.png b/res/empty_scene.png
new file mode 100644
index 0000000..cc4456e
--- /dev/null
+++ b/res/empty_scene.png
Binary files differ
diff --git a/scripts/__pycache__/uci_packets.cpython-310.pyc b/scripts/__pycache__/uci_packets.cpython-310.pyc
new file mode 100644
index 0000000..9487b58
--- /dev/null
+++ b/scripts/__pycache__/uci_packets.cpython-310.pyc
Binary files differ
diff --git a/scripts/console.py b/scripts/console.py
new file mode 100755
index 0000000..644b6ff
--- /dev/null
+++ b/scripts/console.py
@@ -0,0 +1,453 @@
+#!/usr/bin/env python3
+
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import inspect
+import json
+import random
+import readline
+import socket
+import sys
+import time
+import requests
+import struct
+import asyncio
+from concurrent.futures import ThreadPoolExecutor
+import uci_packets
+
+MAX_PAYLOAD_SIZE = 1024
+
+
+def encode_position(x: int, y: int, z: int, yaw: int, pitch: int, roll: int) -> bytes:
+ return (struct.pack('<h', x)
+ + struct.pack('<h', y)
+ + struct.pack('<h', z)
+ + struct.pack('<h', yaw)
+ + struct.pack('<b', pitch)
+ + struct.pack('<h', roll))
+
+
+def encode_session_id(session_id: str) -> bytes:
+ return int(session_id).to_bytes(4, byteorder='little')
+
+
+def encode_short_mac_address(mac_address: str) -> bytes:
+ return int(mac_address).to_bytes(2, byteorder='little')
+
+
+def encode_mac_address(mac_address: str) -> bytes:
+ return int(mac_address).to_bytes(8, byteorder='little')
+
+
+def encode_tlv(typ: int, value: bytes):
+ return bytes([typ, len(value)]) + value if len(value) > 0 else bytes([])
+
+
+class TLV:
+ def __init__(self):
+ self.count = 0
+ self.buffer = bytes()
+
+ def append(self, tag: int, value: bytes):
+ if len(value) > 0:
+ self.count += 1
+ self.buffer += encode_tlv(tag, value)
+
+ def len(self) -> int:
+ return len(self.buffer) + 1
+
+ def encode(self) -> bytes:
+ return bytes([self.count]) + self.buffer
+
+
+class Device:
+ def __init__(self, reader, writer, http_address):
+ self.reader = reader
+ self.writer = writer
+ self.http_address = http_address
+
+ def pica_get_state(
+ self,
+ **kargs):
+ """List the UCI devices and anchors currently existing within the Pica
+ virtual environment"""
+ r = requests.get(f'{self.http_address}/get-state')
+ print(f'{r.status_code}:\n{json.dumps(r.json(), indent=2)}')
+
+ def pica_init_uci_device(
+ self,
+ x: str = "0",
+ y: str = "0",
+ z: str = "0",
+ yaw: str = "0",
+ pitch: str = "0",
+ roll: str = "0",
+ **kargs):
+ """Init Pica device"""
+ r = requests.post(f'{self.http_address}/init-uci-device/{mac_address}',
+ data=json.dumps({
+ 'x': int(x), 'y': int(y), 'z': int(z),
+ 'yaw': int(yaw), 'pitch': int(pitch), 'roll': int(roll)
+ }))
+ print(f'{r.status_code}: {r.text}')
+
+ def pica_create_anchor(
+ self,
+ mac_address: str = "00:00",
+ x: str = "0",
+ y: str = "0",
+ z: str = "0",
+ yaw: str = "0",
+ pitch: str = "0",
+ roll: str = "0",
+ **kargs):
+ """Create a Pica anchor"""
+ r = requests.post(f'{self.http_address}/create-anchor/{mac_address}',
+ data=json.dumps({
+ 'x': int(x), 'y': int(y), 'z': int(z),
+ 'yaw': int(yaw), 'pitch': int(pitch), 'roll': int(roll)
+ }))
+ print(f'{r.status_code}: {r.text}')
+
+ def pica_destroy_anchor(
+ self,
+ mac_address: str = "00:00",
+ **kargs):
+ """Destroy a Pica anchor"""
+ r = requests.post(f'{self.http_address}/destroy-anchor/{mac_address}')
+ print(f'{r.status_code}: {r.text}')
+
+ def pica_set_position(
+ self,
+ mac_address: str = "00:00",
+ x: str = "0",
+ y: str = "0",
+ z: str = "0",
+ yaw: str = "0",
+ pitch: str = "0",
+ roll: str = "0",
+ **kargs):
+ """Set Pica UCI device or anchor position"""
+ r = requests.post(f'{self.http_address}/set-position/{mac_address}',
+ data=json.dumps({
+ 'x': int(x), 'y': int(y), 'z': int(z),
+ 'yaw': int(yaw), 'pitch': int(pitch), 'roll': int(roll)
+ }))
+ print(f'{r.status_code}: {r.text}')
+
+ def _send_command(self, group_id: int, opcode_id: int, payload: bytes):
+ """Sends a UCI command without fragmentation"""
+ command = bytes([0x20 | group_id, opcode_id,
+ 0, len(payload)]) + payload
+ self.writer.write(command)
+
+ def raw(self,
+ group_id: str = "0",
+ opcode_id: str = "0",
+ payload_size: str = "0",
+ **kargs):
+ """Send raw commands with random payload."""
+ self._send_command(
+ int(group_id), int(opcode_id),
+ random.randbytes(int(payload_size)))
+
+ def device_reset(self, **kargs):
+ """Reset the UWBS."""
+ self._send_command(0, 0, bytes([0x0]))
+
+ def get_device_info(self, **kargs):
+ """Retrieve the device information like (UCI version and other vendor specific info)."""
+ self._send_command(0, 2, bytes([]))
+
+ def get_caps_info(self, **kargs):
+ """Get the capability of the UWBS."""
+ self._send_command(0, 3, bytes([]))
+
+ def set_config(self, low_power_mode: str = '0', **kargs):
+ """Set the configuration parameters on the UWBS."""
+ self._send_command(0, 4, bytes([1, 1, 1, int(low_power_mode)]))
+
+ def get_config(self, **kargs):
+ """Retrieve the current configuration parameter(s) of the UWBS."""
+ self._send_command(0, 5, bytes([2, 0, 1]))
+
+ def session_init(self, session_id: str = '0', **kargs):
+ """Initialize the session"""
+ self._send_command(1, 0,
+ int(session_id).to_bytes(4, byteorder='little') + bytes([0x0]))
+
+ def session_deinit(self, session_id: str = '0', **kargs):
+ """Deinitialize the session"""
+ self._send_command(1, 1, encode_session_id(session_id))
+
+ def session_set_app_config(
+ self,
+ session_id: str = '0',
+ ranging_interval: str = '200',
+ dst_mac_addresses: str = '',
+ **kargs):
+ """set APP Configuration Parameters for the requested UWB session."""
+ encoded_dst_mac_addresses = bytes()
+ dst_mac_addresses_count = 0
+ for mac_address in dst_mac_addresses.split(','):
+ if len(mac_address) > 0:
+ dst_mac_addresses_count += 1
+ encoded_dst_mac_addresses += int(
+ mac_address).to_bytes(8, byteorder='little')
+
+ configs = TLV()
+ configs.append(0x26, bytes([0x2])) # MAC Address Mode #2
+ configs.append(0x5, bytes([dst_mac_addresses_count]))
+ configs.append(0x9, int(ranging_interval).to_bytes(
+ 4, byteorder='little'))
+ configs.append(0x7, encoded_dst_mac_addresses)
+
+ self._send_command(1, 3,
+ encode_session_id(session_id) +
+ configs.encode())
+
+ def session_get_app_config(self, session_id: str = '0', **kargs):
+ """retrieve the current APP Configuration Parameters of the requested UWB session."""
+ self._send_command(1, 4,
+ encode_session_id(session_id) +
+ bytes([1, 0x9]))
+
+ def session_get_count(self, **kargs):
+ """Retrieve number of UWB sessions in the UWBS."""
+ self._send_command(1, 5, bytes([]))
+
+ def session_get_state(self, session_id: str = '0', **kargs):
+ """Query the current state of the UWB session."""
+ self._send_command(1, 6, encode_session_id(session_id))
+
+ def session_update_controller_multicast_list(
+ self,
+ session_id: str = '0',
+ action: str = 'add',
+ mac_address: str = '0',
+ subsession_id: str = '0',
+ **kargs):
+ """Update the controller multicast list."""
+
+ if action == 'add':
+ encoded_action = bytes([0])
+ elif action == 'remove':
+ encoded_action = bytes([1])
+ else:
+ print(f"Unexpected action: '{action}', expected add or remove")
+ return
+
+ self._send_command(1, 7,
+ encode_session_id(session_id) +
+ encoded_action +
+ bytes([1]) +
+ encode_short_mac_address(mac_address) +
+ encode_session_id(subsession_id))
+
+ def range_start(self, session_id: str = '0', **kargs):
+ """start a UWB session."""
+ self._send_command(2, 0, encode_session_id(session_id))
+
+ def range_stop(self, session_id: str = '0', **kargs):
+ """Stop a UWB session."""
+ self._send_command(2, 1, encode_session_id(session_id))
+
+ def get_ranging_count(self, session_id: str = '0', **kargs):
+ """Get the number of times ranging has been attempted during the ranging session.."""
+ self._send_command(2, 3, encode_session_id(session_id))
+
+ async def read_responses_and_notifications(self):
+ def chunks(l, n):
+ for i in range(0, len(l), n):
+ yield l[i:i + n]
+
+ packet = bytes()
+ buffer = bytes()
+ expect = 4
+ have_header = False
+
+ while True:
+ chunk = await self.reader.read(expect)
+
+ # Disconnected from Pica
+ if len(chunk) == 0:
+ break
+
+ # Waiting for more bytes
+ buffer += chunk
+ if len(buffer) < expect:
+ continue
+
+ packet += buffer
+ buffer = bytes()
+ if not have_header:
+ have_header = True
+ expect = packet[3]
+ else:
+ # Format and print raw response data
+ txt = '\n '.join([
+ ' '.join(['{:02x}'.format(b) for b in shard]) for
+ shard in chunks(packet, 16)])
+
+ command_buffer = readline.get_line_buffer()
+ print('\r', end='')
+ print(f'Received UCI packet [{len(packet)}]:')
+ print(f' {txt}')
+ uci_packet = uci_packets.UciPacket.parse_all(packet)
+ uci_packet.show()
+ print(f'--> {command_buffer}', end='', flush=True)
+
+ packet = bytes()
+ have_header = False
+ expect = 4
+
+
+async def ainput(prompt: str = ''):
+ with ThreadPoolExecutor(1, 'ainput') as executor:
+ return (await asyncio.get_event_loop().run_in_executor(executor, input, prompt)).rstrip()
+
+
+async def get_stream_reader(pipe) -> asyncio.StreamReader:
+ loop = asyncio.get_event_loop()
+ reader = asyncio.StreamReader(loop=loop)
+ protocol = asyncio.StreamReaderProtocol(reader)
+ await loop.connect_read_pipe(lambda: protocol, pipe)
+ return reader
+
+
+async def command_line(device: Device):
+ commands = {
+ 'pica_get_state': device.pica_get_state,
+ 'pica_init_uci_device': device.pica_init_uci_device,
+ 'pica_create_anchor': device.pica_create_anchor,
+ 'pica_destroy_anchor': device.pica_destroy_anchor,
+ 'pica_set_position': device.pica_set_position,
+ 'device_reset': device.device_reset,
+ 'get_device_info': device.get_device_info,
+ 'get_config': device.get_config,
+ 'set_config': device.set_config,
+ 'get_caps_info': device.get_caps_info,
+ 'session_init': device.session_init,
+ 'session_deinit': device.session_deinit,
+ 'session_set_app_config': device.session_set_app_config,
+ 'session_get_app_config': device.session_get_app_config,
+ 'session_get_count': device.session_get_count,
+ 'session_get_state': device.session_get_state,
+ 'session_update_controller_multicast_list': device.session_update_controller_multicast_list,
+ 'range_start': device.range_start,
+ 'range_stop': device.range_stop,
+ 'get_ranging_count': device.get_ranging_count,
+ 'raw': device.raw,
+ }
+
+ def usage():
+ for (cmd, func) in commands.items():
+ print(f' {cmd.ljust(32)}{func.__doc__}')
+
+ def complete(text, state):
+ tokens = readline.get_line_buffer().split()
+ if not tokens or readline.get_line_buffer()[-1] == ' ':
+ tokens.append('')
+
+ # Writing a command name, complete to ' '
+ if len(tokens) == 1:
+ results = [cmd + ' ' for cmd in commands.keys() if
+ cmd.startswith(text)]
+
+ # Writing a keyword argument, no completion
+ elif '=' in tokens[-1]:
+ results = []
+
+ # Writing a keyword name, but unknown command, no completion
+ elif tokens[0] not in commands:
+ results = []
+
+ # Writing a keyword name, complete to '='
+ else:
+ sig = inspect.signature(commands[tokens[0]])
+ names = [name for (name, p) in sig.parameters.items()
+ if (p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD or
+ p.kind == inspect.Parameter.KEYWORD_ONLY)]
+ results = [
+ name + '=' for name in names if name.startswith(tokens[-1])]
+
+ results += [None]
+ return results[state]
+
+ # Configure readline
+ readline.parse_and_bind("tab: complete")
+ readline.set_completer(complete)
+
+ while True:
+ cmd = await ainput('--> ')
+ [cmd, *params] = cmd.split(' ')
+ args = []
+ kargs = dict()
+ for param in params:
+ if len(param) == 0:
+ continue
+ elif '=' in param:
+ [key, value] = param.split('=')
+ kargs[key] = value
+ else:
+ args.append(param)
+
+ if cmd in ['quit', 'q']:
+ break
+ if cmd not in commands:
+ print(f'Undefined command {cmd}')
+ usage()
+ continue
+ commands[cmd](*args, **kargs)
+
+
+async def run(address: str, uci_port: int, http_port: int):
+ try:
+ # Connect to Pica
+ reader, writer = await asyncio.open_connection(address, uci_port)
+ except Exception as exn:
+ print(
+ f'Failed to connect to Pica server at address {address}:{uci_port}\n' +
+ 'Make sure the server is running')
+ exit(1)
+
+ # Start input and receive loops
+ device = Device(reader, writer, f'http://{address}:{http_port}')
+ loop = asyncio.get_event_loop()
+ loop.create_task(device.read_responses_and_notifications())
+ await command_line(device)
+
+
+def main():
+ """Start a Pica interactive console."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--address',
+ type=str,
+ default='127.0.0.1',
+ help='Select the pica server address')
+ parser.add_argument('--uci-port',
+ type=int,
+ default=7000,
+ help='Select the pica TCP UCI port')
+ parser.add_argument('--http-port',
+ type=int,
+ default=3000,
+ help='Select the pica HTTP port')
+ asyncio.run(run(**vars(parser.parse_args())))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/uci_packets.py b/scripts/uci_packets.py
new file mode 100644
index 0000000..ab757b5
--- /dev/null
+++ b/scripts/uci_packets.py
@@ -0,0 +1,2353 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# File generated from uci_packets.json, with the command:
+# ./scripts/generate_python_backend.py --input uci_packets.json
+# /!\ Do not edit by hand.
+from dataclasses import dataclass, field, fields
+from typing import Optional, List, Tuple
+import enum
+import inspect
+import math
+
+
+@dataclass
+class Packet:
+ payload: Optional[bytes] = field(repr=False)
+
+ @classmethod
+ def parse_all(cls, span: bytes) -> 'Packet':
+ packet, remain = getattr(cls, 'parse')(span)
+ if len(remain) > 0:
+ raise Exception('Unexpected parsing remainder')
+ return packet
+
+ def show(self, prefix: str = ''):
+ print(f'{self.__class__.__name__}')
+
+ def print_val(p: str, pp: str, name: str, align: int, typ, val):
+ if name == 'payload':
+ pass
+
+ # Scalar fields.
+ elif typ is int:
+ print(f'{p}{name:{align}} = {val} (0x{val:x})')
+
+ # Byte fields.
+ elif typ is bytes:
+ print(f'{p}{name:{align}} = [', end='')
+ line = ''
+ n_pp = ''
+ for (idx, b) in enumerate(val):
+ if idx > 0 and idx % 8 == 0:
+ print(f'{n_pp}{line}')
+ line = ''
+ n_pp = pp + (' ' * (align + 4))
+ line += f' {b:02x}'
+ print(f'{n_pp}{line} ]')
+
+ # Enum fields.
+ elif inspect.isclass(typ) and issubclass(typ, enum.IntEnum):
+ print(f'{p}{name:{align}} = {typ.__name__}::{val.name} (0x{val:x})')
+
+ # Struct fields.
+ elif inspect.isclass(typ) and issubclass(typ, globals().get('Packet')):
+ print(f'{p}{name:{align}} = ', end='')
+ val.show(prefix=pp)
+
+ # Array fields.
+ elif typ.__origin__ == list:
+ print(f'{p}{name:{align}}')
+ last = len(val) - 1
+ align = 5
+ for (idx, elt) in enumerate(val):
+ n_p = pp + ('├── ' if idx != last else '└── ')
+ n_pp = pp + ('│ ' if idx != last else ' ')
+ print_val(n_p, n_pp, f'[{idx}]',
+ align, typ.__args__[0], val[idx])
+
+ else:
+ print(f'{p}{name:{align}} = ##{typ}##')
+
+ last = len(fields(self)) - 1
+ align = max(len(f.name) for f in fields(self) if f.name != 'payload')
+
+ for (idx, f) in enumerate(fields(self)):
+ p = prefix + ('├── ' if idx != last else '└── ')
+ pp = prefix + ('│ ' if idx != last else ' ')
+ val = getattr(self, f.name)
+
+ print_val(p, pp, f.name, align, f.type, val)
+
+
+class PacketBoundaryFlag(enum.IntEnum):
+ COMPLETE = 0x0
+ NOT_COMPLETE = 0x1
+
+
+class GroupId(enum.IntEnum):
+ CORE = 0x0
+ SESSION_CONFIG = 0x1
+ RANGING_SESSION_CONTROL = 0x2
+ DATA_CONTROL = 0x3
+ TEST = 0xd
+ VENDOR_PICA = 0x9
+ VENDOR_RESERVED_A = 0xa
+ VENDOR_RESERVED_B = 0xb
+ VENDOR_ANDROID = 0xe
+ VENDOR_RESERVED_E = 0xc
+ VENDOR_RESERVED_F = 0xf
+
+
+class CoreOpCode(enum.IntEnum):
+ CORE_DEVICE_RESET = 0x0
+ CORE_DEVICE_STATUS_NTF = 0x1
+ CORE_GET_DEVICE_INFO = 0x2
+ CORE_GET_CAPS_INFO = 0x3
+ CORE_SET_CONFIG = 0x4
+ CORE_GET_CONFIG = 0x5
+ CORE_DEVICE_SUSPEND = 0x6
+ CORE_GENERIC_ERROR_NTF = 0x7
+
+
+class SessionOpCode(enum.IntEnum):
+ SESSION_INIT = 0x0
+ SESSION_DEINIT = 0x1
+ SESSION_STATUS_NTF = 0x2
+ SESSION_SET_APP_CONFIG = 0x3
+ SESSION_GET_APP_CONFIG = 0x4
+ SESSION_GET_COUNT = 0x5
+ SESSION_GET_STATE = 0x6
+ SESSION_UPDATE_CONTROLLER_MULTICAST_LIST = 0x7
+
+
+class RangeOpCode(enum.IntEnum):
+ RANGE_START = 0x0
+ RANGE_STOP = 0x1
+ RANGE_INTERVAL_UPDATE_REQ = 0x2
+ RANGE_GET_RANGING_COUNT = 0x3
+
+
+class AppDataOpCode(enum.IntEnum):
+ APP_DATA_TX = 0x0
+ APP_DATA_RX = 0x1
+
+
+class PicaOpCode(enum.IntEnum):
+ PICA_INIT_DEVICE = 0x0
+ PICA_SET_DEVICE_POSITION = 0x1
+ PICA_CREATE_BEACON = 0x2
+ PICA_SET_BEACON_POSITION = 0x3
+ PICA_DESTROY_BEACON = 0x4
+
+
+class AndroidOpCode(enum.IntEnum):
+ ANDROID_GET_POWER_STATS = 0x0
+ ANDROID_SET_COUNTRY_CODE = 0x1
+
+
+class StatusCode(enum.IntEnum):
+ UCI_STATUS_OK = 0x0
+ UCI_STATUS_REJECTED = 0x1
+ UCI_STATUS_FAILED = 0x2
+ UCI_STATUS_SYNTAX_ERROR = 0x3
+ UCI_STATUS_INVALID_PARAM = 0x4
+ UCI_STATUS_INVALID_RANGE = 0x5
+ UCI_STATUS_INVALID_MSG_SIZE = 0x6
+ UCI_STATUS_UNKNOWN_GID = 0x7
+ UCI_STATUS_UNKNOWN_OID = 0x8
+ UCI_STATUS_READ_ONLY = 0x9
+ UCI_STATUS_COMMAND_RETRY = 0xa
+ UCI_STATUS_SESSION_NOT_EXIST = 0x11
+ UCI_STATUS_SESSION_DUPLICATE = 0x12
+ UCI_STATUS_SESSION_ACTIVE = 0x13
+ UCI_STATUS_MAX_SESSIONS_EXCEEDED = 0x14
+ UCI_STATUS_SESSION_NOT_CONFIGURED = 0x15
+ UCI_STATUS_ACTIVE_SESSION_ONGOING = 0x16
+ UCI_STATUS_MULTICAST_LIST_FULL = 0x17
+ UCI_STATUS_ADDRESS_NOT_FOUND = 0x18
+ UCI_STATUS_ADDRESS_ALREADY_PRESENT = 0x19
+ UCI_STATUS_RANGING_TX_FAILED = 0x20
+ UCI_STATUS_RANGING_RX_TIMEOUT = 0x21
+ UCI_STATUS_RANGING_RX_PHY_DEC_FAILED = 0x22
+ UCI_STATUS_RANGING_RX_PHY_TOA_FAILED = 0x23
+ UCI_STATUS_RANGING_RX_PHY_STS_FAILED = 0x24
+ UCI_STATUS_RANGING_RX_MAC_DEC_FAILED = 0x25
+ UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED = 0x26
+ UCI_STATUS_RANGING_RX_MAC_IE_MISSING = 0x27
+ UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED = 0x30
+ UCI_STATUS_DATA_RX_CRC_ERROR = 0x31
+ UCI_STATUS_ERROR_CCC_SE_BUSY = 0x50
+ UCI_STATUS_ERROR_CCC_LIFECYCLE = 0x51
+
+
+class ResetConfig(enum.IntEnum):
+ UWBS_RESET = 0x0
+
+
+class DeviceConfigId(enum.IntEnum):
+ DEVICE_STATE = 0x0
+ LOW_POWER_MODE = 0x1
+
+
+class AppConfigTlvType(enum.IntEnum):
+ DEVICE_TYPE = 0x0
+ RANGING_ROUND_USAGE = 0x1
+ STS_CONFIG = 0x2
+ MULTI_NODE_MODE = 0x3
+ CHANNEL_NUMBER = 0x4
+ NO_OF_CONTROLEE = 0x5
+ DEVICE_MAC_ADDRESS = 0x6
+ DST_MAC_ADDRESS = 0x7
+ SLOT_DURATION = 0x8
+ RANGING_INTERVAL = 0x9
+ STS_INDEX = 0xa
+ MAC_FCS_TYPE = 0xb
+ RANGING_ROUND_CONTROL = 0xc
+ AOA_RESULT_REQ = 0xd
+ RNG_DATA_NTF = 0xe
+ RNG_DATA_NTF_PROXIMITY_NEAR = 0xf
+ RNG_DATA_NTF_PROXIMITY_FAR = 0x10
+ DEVICE_ROLE = 0x11
+ RFRAME_CONFIG = 0x12
+ PREAMBLE_CODE_INDEX = 0x14
+ SFD_ID = 0x15
+ PSDU_DATA_RATE = 0x16
+ PREAMBLE_DURATION = 0x17
+ RANGING_TIME_STRUCT = 0x1a
+ SLOTS_PER_RR = 0x1b
+ TX_ADAPTIVE_PAYLOAD_POWER = 0x1c
+ RESPONDER_SLOT_INDEX = 0x1e
+ PRF_MODE = 0x1f
+ SCHEDULED_MODE = 0x22
+ KEY_ROTATION = 0x23
+ KEY_ROTATION_RATE = 0x24
+ SESSION_PRIORITY = 0x25
+ MAC_ADDRESS_MODE = 0x26
+ VENDOR_ID = 0x27
+ STATIC_STS_IV = 0x28
+ NUMBER_OF_STS_SEGMENTS = 0x29
+ MAX_RR_RETRY = 0x2a
+ UWB_INITIATION_TIME = 0x2b
+ HOPPING_MODE = 0x2c
+ BLOCK_STRIDE_LENGTH = 0x2d
+ RESULT_REPORT_CONFIG = 0x2e
+ IN_BAND_TERMINATION_ATTEMPT_COUNT = 0x2f
+ SUB_SESSION_ID = 0x30
+ BPRF_PHR_DATA_RATE = 0x31
+ MAX_NUMBER_OF_MEASUREMENTS = 0x32
+ STS_LENGTH = 0x35
+ CCC_HOP_MODE_KEY = 0xa0
+ CCC_UWB_TIME0 = 0xa1
+ CCC_RANGING_PROTOCOL_VER = 0xa3
+ CCC_UWB_CONFIG_ID = 0xa4
+ CCC_PULSESHAPE_COMBO = 0xa5
+ CCC_URSK_TTL = 0xa6
+ NB_OF_RANGE_MEASUREMENTS = 0xe3
+ NB_OF_AZIMUTH_MEASUREMENTS = 0xe4
+ NB_OF_ELEVATION_MEASUREMENTS = 0xe5
+
+
+class CapTlvType(enum.IntEnum):
+ SUPPORTED_FIRA_PHY_VERSION_RANGE = 0x0
+ SUPPORTED_FIRA_MAC_VERSION_RANGE = 0x1
+ SUPPORTED_DEVICE_ROLES = 0x2
+ SUPPORTED_RANGING_METHOD = 0x3
+ SUPPORTED_STS_CONFIG = 0x4
+ SUPPORTED_MULTI_NODE_MODES = 0x5
+ SUPPORTED_RANGING_TIME_STRUCT = 0x6
+ SUPPORTED_SCHEDULED_MODE = 0x7
+ SUPPORTED_HOPPING_MODE = 0x8
+ SUPPORTED_BLOCK_STRIDING = 0x9
+ SUPPORTED_UWB_INITIATION_TIME = 0xa
+ SUPPORTED_CHANNELS = 0xb
+ SUPPORTED_RFRAME_CONFIG = 0xc
+ SUPPORTED_CC_CONSTRAINT_LENGTH = 0xd
+ SUPPORTED_BPRF_PARAMETER_SETS = 0xe
+ SUPPORTED_HPRF_PARAMETER_SETS = 0xf
+ SUPPORTED_AOA = 0x10
+ SUPPORTED_EXTENDED_MAC_ADDRESS = 0x11
+ SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xe3
+ SUPPORTED_POWER_STATS = 0xc0
+ CCC_SUPPORTED_CHAPS_PER_SLOT = 0xa0
+ CCC_SUPPORTED_SYNC_CODES = 0xa1
+ CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES = 0xa2
+ CCC_SUPPORTED_CHANNELS = 0xa3
+ CCC_SUPPORTED_VERSIONS = 0xa4
+ CCC_SUPPORTED_UWB_CONFIGS = 0xa5
+ CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 0xa6
+ CCC_SUPPORTED_RAN_MULTIPLIER = 0xa7
+
+
+class AoaResultReqType(enum.IntEnum):
+ AOA_DISABLE = 0x0
+ AOA_ENABLE = 0x1
+ AOA_ENABLE_AZIMUTH = 0x2
+ AOA_ENABLE_ELEVATION = 0x3
+ AOA_ENABLE_INTERLEAVED = 0xf0
+
+
+class DeviceState(enum.IntEnum):
+ DEVICE_STATE_READY = 0x1
+ DEVICE_STATE_ACTIVE = 0x2
+ DEVICE_STATE_ERROR = 0xff
+
+
+class SessionState(enum.IntEnum):
+ SESSION_STATE_INIT = 0x0
+ SESSION_STATE_DEINIT = 0x1
+ SESSION_STATE_ACTIVE = 0x2
+ SESSION_STATE_IDLE = 0x3
+
+
+class ReasonCode(enum.IntEnum):
+ STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS = 0x0
+ MAX_RANGING_ROUND_RETRY_COUNT_REACHED = 0x1
+ MAX_NUMBER_OF_MEASUREMENTS_REACHED = 0x2
+ ERROR_SLOT_LENGTH_NOT_SUPPORTED = 0x20
+ ERROR_INSUFFICIENT_SLOTS_PER_RR = 0x21
+ ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED = 0x22
+ ERROR_INVALID_RANGING_INTERVAL = 0x23
+ ERROR_INVALID_STS_CONFIG = 0x24
+ ERROR_INVALID_RFRAME_CONFIG = 0x25
+
+
+class MulticastUpdateStatusCode(enum.IntEnum):
+ STATUS_OK_MULTICAST_LIST_UPDATE = 0x0
+ STATUS_ERROR_MULTICAST_LIST_FULL = 0x1
+ STATUS_ERROR_KEY_FETCH_FAIL = 0x2
+ STATUS_ERROR_SUB_SESSION_ID_NOT_FOUND = 0x3
+
+
+class MacAddressIndicator(enum.IntEnum):
+ SHORT_ADDRESS = 0x0
+ EXTENDED_ADDRESS = 0x1
+
+
+class SessionType(enum.IntEnum):
+ FIRA_RANGING_SESSION = 0x0
+ FIRA_DATA_TRANSFER = 0x1
+ CCC = 0xa0
+
+
+class MessageType(enum.IntEnum):
+ COMMAND = 0x1
+ RESPONSE = 0x2
+ NOTIFICATION = 0x3
+
+
+@dataclass
+class UciPacket(Packet):
+ group_id: GroupId
+ packet_boundary_flag: PacketBoundaryFlag
+ message_type: MessageType
+ opcode: int
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['UciPacket', bytes]:
+ fields = {'payload': None}
+ if len(span) < 4:
+ raise Exception('Invalid packet size')
+ fields['group_id'] = GroupId((span[0] >> 0) & 0xf)
+ fields['packet_boundary_flag'] = PacketBoundaryFlag(
+ (span[0] >> 4) & 0x1)
+ fields['message_type'] = MessageType((span[0] >> 5) & 0x7)
+ fields['opcode'] = (span[1] >> 0) & 0x3f
+ _payload__size = span[3]
+ span = span[4:]
+ if len(span) < _payload__size:
+ raise Exception('Invalid packet size')
+ payload = span[:_payload__size]
+ fields['payload'] = payload
+ if fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x0:
+ return DeviceResetCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x2:
+ return GetDeviceInfoCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x3:
+ return GetCapsInfoCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x4:
+ return SetConfigCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x5:
+ return GetConfigCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x0:
+ return SessionInitCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x1:
+ return SessionDeinitCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x3:
+ return SessionSetAppConfigCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x4:
+ return SessionGetAppConfigCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x5:
+ return SessionGetCountCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x6:
+ return SessionGetStateCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.RANGING_SESSION_CONTROL:
+ return RangingCommand.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x0:
+ return PicaInitDeviceCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x1:
+ return PicaSetDevicePositionCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x2:
+ return PicaCreateBeaconCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x3:
+ return PicaSetBeaconPositionCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x4:
+ return PicaDestroyBeaconCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x0:
+ return AndroidGetPowerStatsCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.COMMAND and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x1:
+ return AndroidSetCountryCodeCmd.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x0:
+ return DeviceResetRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x2:
+ return GetDeviceInfoRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x3:
+ return GetCapsInfoRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x4:
+ return SetConfigRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x5:
+ return GetConfigRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x0:
+ return SessionInitRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x1:
+ return SessionDeinitRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x3:
+ return SessionSetAppConfigRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x4:
+ return SessionGetAppConfigRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x5:
+ return SessionGetCountRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x6:
+ return SessionGetStateRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x0:
+ return RangeStartRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x1:
+ return RangeStopRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x3:
+ return RangeGetRangingCountRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x0:
+ return PicaInitDeviceRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x1:
+ return PicaSetDevicePositionRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x2:
+ return PicaCreateBeaconRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x3:
+ return PicaSetBeaconPositionRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x4:
+ return PicaDestroyBeaconRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x0:
+ return AndroidGetPowerStatsRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.RESPONSE and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x1:
+ return AndroidSetCountryCodeRsp.parse(fields, payload)
+ elif fields['message_type'] == MessageType.NOTIFICATION and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x1:
+ return DeviceStatusNtf.parse(fields, payload)
+ elif fields['message_type'] == MessageType.NOTIFICATION and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x7:
+ return GenericError.parse(fields, payload)
+ elif fields['message_type'] == MessageType.NOTIFICATION and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x2:
+ return SessionStatusNtf.parse(fields, payload)
+ elif fields['message_type'] == MessageType.NOTIFICATION and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListNtf.parse(fields, payload)
+ elif fields['message_type'] == MessageType.NOTIFICATION and fields['packet_boundary_flag'] == PacketBoundaryFlag.COMPLETE and fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x0:
+ return RangeDataNtf.parse(fields, payload)
+ else:
+ return UciPacket(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciCommand(UciPacket):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciCommand', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x0:
+ return DeviceResetCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x2:
+ return GetDeviceInfoCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x3:
+ return GetCapsInfoCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x4:
+ return SetConfigCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x5:
+ return GetConfigCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x0:
+ return SessionInitCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x1:
+ return SessionDeinitCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x3:
+ return SessionSetAppConfigCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x4:
+ return SessionGetAppConfigCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x5:
+ return SessionGetCountCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x6:
+ return SessionGetStateCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.RANGING_SESSION_CONTROL:
+ return RangingCommand.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x0:
+ return PicaInitDeviceCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x1:
+ return PicaSetDevicePositionCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x2:
+ return PicaCreateBeaconCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x3:
+ return PicaSetBeaconPositionCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x4:
+ return PicaDestroyBeaconCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x0:
+ return AndroidGetPowerStatsCmd.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x1:
+ return AndroidSetCountryCodeCmd.parse(fields, payload)
+ else:
+ return UciCommand(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciResponse(UciPacket):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciResponse', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x0:
+ return DeviceResetRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x2:
+ return GetDeviceInfoRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x3:
+ return GetCapsInfoRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x4:
+ return SetConfigRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x5:
+ return GetConfigRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x0:
+ return SessionInitRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x1:
+ return SessionDeinitRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x3:
+ return SessionSetAppConfigRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x4:
+ return SessionGetAppConfigRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x5:
+ return SessionGetCountRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x6:
+ return SessionGetStateRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x0:
+ return RangeStartRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x1:
+ return RangeStopRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x3:
+ return RangeGetRangingCountRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x0:
+ return PicaInitDeviceRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x1:
+ return PicaSetDevicePositionRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x2:
+ return PicaCreateBeaconRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x3:
+ return PicaSetBeaconPositionRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_PICA and fields['opcode'] == 0x4:
+ return PicaDestroyBeaconRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x0:
+ return AndroidGetPowerStatsRsp.parse(fields, payload)
+ elif fields['group_id'] == GroupId.VENDOR_ANDROID and fields['opcode'] == 0x1:
+ return AndroidSetCountryCodeRsp.parse(fields, payload)
+ else:
+ return UciResponse(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciNotification(UciPacket):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciNotification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x1:
+ return DeviceStatusNtf.parse(fields, payload)
+ elif fields['group_id'] == GroupId.CORE and fields['opcode'] == 0x7:
+ return GenericError.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x2:
+ return SessionStatusNtf.parse(fields, payload)
+ elif fields['group_id'] == GroupId.SESSION_CONFIG and fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListNtf.parse(fields, payload)
+ elif fields['group_id'] == GroupId.RANGING_SESSION_CONTROL and fields['opcode'] == 0x0:
+ return RangeDataNtf.parse(fields, payload)
+ else:
+ return UciNotification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class CoreCommand(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['CoreCommand', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return DeviceResetCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x2:
+ return GetDeviceInfoCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return GetCapsInfoCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x4:
+ return SetConfigCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x5:
+ return GetConfigCmd.parse(fields, payload)
+ else:
+ return CoreCommand(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class CoreResponse(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['CoreResponse', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return DeviceResetRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x2:
+ return GetDeviceInfoRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return GetCapsInfoRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x4:
+ return SetConfigRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x5:
+ return GetConfigRsp.parse(fields, payload)
+ else:
+ return CoreResponse(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class CoreNotification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['CoreNotification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x1:
+ return DeviceStatusNtf.parse(fields, payload)
+ elif fields['opcode'] == 0x7:
+ return GenericError.parse(fields, payload)
+ else:
+ return CoreNotification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionCommand(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionCommand', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return SessionInitCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return SessionDeinitCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return SessionSetAppConfigCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x4:
+ return SessionGetAppConfigCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x5:
+ return SessionGetCountCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x6:
+ return SessionGetStateCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListCmd.parse(fields, payload)
+ else:
+ return SessionCommand(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionResponse(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionResponse', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return SessionInitRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return SessionDeinitRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return SessionSetAppConfigRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x4:
+ return SessionGetAppConfigRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x5:
+ return SessionGetCountRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x6:
+ return SessionGetStateRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListRsp.parse(fields, payload)
+ else:
+ return SessionResponse(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionNotification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionNotification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x2:
+ return SessionStatusNtf.parse(fields, payload)
+ elif fields['opcode'] == 0x7:
+ return SessionUpdateControllerMulticastListNtf.parse(fields, payload)
+ else:
+ return SessionNotification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangingCommand(UciCommand):
+ session_id: int
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangingCommand', bytes]:
+ if len(span) < 4:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ span = span[4:]
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return RangeStartCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return RangeStopCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return RangeGetRangingCountCmd.parse(fields, payload)
+ else:
+ return RangingCommand(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangingResponse(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangingResponse', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return RangeStartRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return RangeStopRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return RangeGetRangingCountRsp.parse(fields, payload)
+ else:
+ return RangingResponse(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangingNotification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangingNotification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return RangeDataNtf.parse(fields, payload)
+ else:
+ return RangingNotification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaCommand(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaCommand', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return PicaInitDeviceCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return PicaSetDevicePositionCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x2:
+ return PicaCreateBeaconCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return PicaSetBeaconPositionCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x4:
+ return PicaDestroyBeaconCmd.parse(fields, payload)
+ else:
+ return PicaCommand(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaResponse(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaResponse', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return PicaInitDeviceRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return PicaSetDevicePositionRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x2:
+ return PicaCreateBeaconRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x3:
+ return PicaSetBeaconPositionRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x4:
+ return PicaDestroyBeaconRsp.parse(fields, payload)
+ else:
+ return PicaResponse(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AndroidCommand(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['AndroidCommand', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return AndroidGetPowerStatsCmd.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return AndroidSetCountryCodeCmd.parse(fields, payload)
+ else:
+ return AndroidCommand(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AndroidResponse(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['AndroidResponse', bytes]:
+ payload = span
+ fields['payload'] = payload
+ if fields['opcode'] == 0x0:
+ return AndroidGetPowerStatsRsp.parse(fields, payload)
+ elif fields['opcode'] == 0x1:
+ return AndroidSetCountryCodeRsp.parse(fields, payload)
+ else:
+ return AndroidResponse(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AndroidNotification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['AndroidNotification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return AndroidNotification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class DeviceResetCmd(CoreCommand):
+ reset_config: ResetConfig
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['DeviceResetCmd', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['reset_config'] = ResetConfig(span[0])
+ span = span[1:]
+ return DeviceResetCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class DeviceResetRsp(CoreResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['DeviceResetRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return DeviceResetRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class DeviceStatusNtf(CoreNotification):
+ device_state: DeviceState
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['DeviceStatusNtf', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['device_state'] = DeviceState(span[0])
+ span = span[1:]
+ return DeviceStatusNtf(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class GetDeviceInfoCmd(CoreCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['GetDeviceInfoCmd', bytes]:
+ return GetDeviceInfoCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class GetDeviceInfoRsp(CoreResponse):
+ status: StatusCode
+ uci_version: int
+ mac_version: int
+ phy_version: int
+ uci_test_version: int
+ vendor_spec_info: bytes
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['GetDeviceInfoRsp', bytes]:
+ if len(span) < 10:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ value_ = int.from_bytes(span[1:3], byteorder='little')
+ fields['uci_version'] = value_
+ value_ = int.from_bytes(span[3:5], byteorder='little')
+ fields['mac_version'] = value_
+ value_ = int.from_bytes(span[5:7], byteorder='little')
+ fields['phy_version'] = value_
+ value_ = int.from_bytes(span[7:9], byteorder='little')
+ fields['uci_test_version'] = value_
+ vendor_spec_info_count = span[9]
+ span = span[10:]
+ if len(span) < vendor_spec_info_count:
+ raise Exception('Invalid packet size')
+ vendor_spec_info = []
+ for n in range(vendor_spec_info_count):
+ vendor_spec_info.append(int.from_bytes(
+ span[n:n + 1], byteorder='little'))
+ fields['vendor_spec_info'] = vendor_spec_info
+ span = span[vendor_spec_info_count:]
+ return GetDeviceInfoRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class GetCapsInfoCmd(CoreCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['GetCapsInfoCmd', bytes]:
+ return GetCapsInfoCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class CapTlv(Packet):
+ t: CapTlvType
+ v: bytes
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['CapTlv', bytes]:
+ fields = {'payload': None}
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['t'] = CapTlvType(span[0])
+ v_count = span[1]
+ span = span[2:]
+ if len(span) < v_count:
+ raise Exception('Invalid packet size')
+ v = []
+ for n in range(v_count):
+ v.append(int.from_bytes(span[n:n + 1], byteorder='little'))
+ fields['v'] = v
+ span = span[v_count:]
+ return CapTlv(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class GetCapsInfoRsp(CoreResponse):
+ status: StatusCode
+ tlvs: List[CapTlv]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['GetCapsInfoRsp', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ tlvs_count = span[1]
+ span = span[2:]
+ tlvs = []
+ for n in range(tlvs_count):
+ element, span = CapTlv.parse(span)
+ tlvs.append(element)
+ fields['tlvs'] = tlvs
+ return GetCapsInfoRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class DeviceParameter(Packet):
+ id: int
+ value: bytes
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['DeviceParameter', bytes]:
+ fields = {'payload': None}
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['id'] = span[0]
+ value_count = span[1]
+ span = span[2:]
+ if len(span) < value_count:
+ raise Exception('Invalid packet size')
+ value = []
+ for n in range(value_count):
+ value.append(int.from_bytes(span[n:n + 1], byteorder='little'))
+ fields['value'] = value
+ span = span[value_count:]
+ return DeviceParameter(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SetConfigCmd(CoreCommand):
+ parameters: List[DeviceParameter]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SetConfigCmd', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ parameters_count = span[0]
+ span = span[1:]
+ parameters = []
+ for n in range(parameters_count):
+ element, span = DeviceParameter.parse(span)
+ parameters.append(element)
+ fields['parameters'] = parameters
+ return SetConfigCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class DeviceConfigStatus(Packet):
+ parameter_id: int
+ status: StatusCode
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['DeviceConfigStatus', bytes]:
+ fields = {'payload': None}
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['parameter_id'] = span[0]
+ fields['status'] = StatusCode(span[1])
+ span = span[2:]
+ return DeviceConfigStatus(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SetConfigRsp(CoreResponse):
+ status: StatusCode
+ parameters: List[DeviceConfigStatus]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SetConfigRsp', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ parameters_count = span[1]
+ span = span[2:]
+ if len(span) < parameters_count * 2:
+ raise Exception('Invalid packet size')
+ parameters = []
+ for n in range(parameters_count):
+ parameters.append(DeviceConfigStatus.parse_all(
+ span[n * 2:(n + 1) * 2]))
+ fields['parameters'] = parameters
+ span = span[parameters_count * 2:]
+ return SetConfigRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class GetConfigCmd(CoreCommand):
+ parameter_ids: bytes
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['GetConfigCmd', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ parameter_ids_count = span[0]
+ span = span[1:]
+ if len(span) < parameter_ids_count:
+ raise Exception('Invalid packet size')
+ parameter_ids = []
+ for n in range(parameter_ids_count):
+ parameter_ids.append(int.from_bytes(
+ span[n:n + 1], byteorder='little'))
+ fields['parameter_ids'] = parameter_ids
+ span = span[parameter_ids_count:]
+ return GetConfigCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class GetConfigRsp(CoreResponse):
+ status: StatusCode
+ parameters: List[DeviceParameter]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['GetConfigRsp', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ parameters_count = span[1]
+ span = span[2:]
+ parameters = []
+ for n in range(parameters_count):
+ element, span = DeviceParameter.parse(span)
+ parameters.append(element)
+ fields['parameters'] = parameters
+ return GetConfigRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class GenericError(CoreNotification):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['GenericError', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return GenericError(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionInitCmd(SessionCommand):
+ session_id: int
+ session_type: SessionType
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionInitCmd', bytes]:
+ if len(span) < 5:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ fields['session_type'] = SessionType(span[4])
+ span = span[5:]
+ return SessionInitCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionInitRsp(SessionResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionInitRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return SessionInitRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionDeinitCmd(SessionCommand):
+ session_id: int
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionDeinitCmd', bytes]:
+ if len(span) < 4:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ span = span[4:]
+ return SessionDeinitCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionDeinitRsp(SessionResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionDeinitRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return SessionDeinitRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionStatusNtf(SessionNotification):
+ session_id: int
+ session_state: SessionState
+ reason_code: ReasonCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionStatusNtf', bytes]:
+ if len(span) < 6:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ fields['session_state'] = SessionState(span[4])
+ fields['reason_code'] = ReasonCode(span[5])
+ span = span[6:]
+ return SessionStatusNtf(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AppConfigParameter(Packet):
+ id: int
+ value: bytes
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['AppConfigParameter', bytes]:
+ fields = {'payload': None}
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['id'] = span[0]
+ value_count = span[1]
+ span = span[2:]
+ if len(span) < value_count:
+ raise Exception('Invalid packet size')
+ value = []
+ for n in range(value_count):
+ value.append(int.from_bytes(span[n:n + 1], byteorder='little'))
+ fields['value'] = value
+ span = span[value_count:]
+ return AppConfigParameter(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionSetAppConfigCmd(SessionCommand):
+ session_id: int
+ parameters: List[AppConfigParameter]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionSetAppConfigCmd', bytes]:
+ if len(span) < 5:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ parameters_count = span[4]
+ span = span[5:]
+ parameters = []
+ for n in range(parameters_count):
+ element, span = AppConfigParameter.parse(span)
+ parameters.append(element)
+ fields['parameters'] = parameters
+ return SessionSetAppConfigCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AppConfigStatus(Packet):
+ config_id: int
+ status: StatusCode
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['AppConfigStatus', bytes]:
+ fields = {'payload': None}
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['config_id'] = span[0]
+ fields['status'] = StatusCode(span[1])
+ span = span[2:]
+ return AppConfigStatus(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionSetAppConfigRsp(SessionResponse):
+ status: StatusCode
+ parameters: List[AppConfigStatus]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionSetAppConfigRsp', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ parameters_count = span[1]
+ span = span[2:]
+ if len(span) < parameters_count * 2:
+ raise Exception('Invalid packet size')
+ parameters = []
+ for n in range(parameters_count):
+ parameters.append(AppConfigStatus.parse_all(
+ span[n * 2:(n + 1) * 2]))
+ fields['parameters'] = parameters
+ span = span[parameters_count * 2:]
+ return SessionSetAppConfigRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionGetAppConfigCmd(SessionCommand):
+ session_id: int
+ parameters: bytes
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionGetAppConfigCmd', bytes]:
+ if len(span) < 5:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ parameters_count = span[4]
+ span = span[5:]
+ if len(span) < parameters_count:
+ raise Exception('Invalid packet size')
+ parameters = []
+ for n in range(parameters_count):
+ parameters.append(int.from_bytes(
+ span[n:n + 1], byteorder='little'))
+ fields['parameters'] = parameters
+ span = span[parameters_count:]
+ return SessionGetAppConfigCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionGetAppConfigRsp(SessionResponse):
+ status: StatusCode
+ parameters: List[AppConfigParameter]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionGetAppConfigRsp', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ parameters_count = span[1]
+ span = span[2:]
+ parameters = []
+ for n in range(parameters_count):
+ element, span = AppConfigParameter.parse(span)
+ parameters.append(element)
+ fields['parameters'] = parameters
+ return SessionGetAppConfigRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionGetCountCmd(SessionCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionGetCountCmd', bytes]:
+ return SessionGetCountCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionGetCountRsp(SessionResponse):
+ status: StatusCode
+ session_count: int
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionGetCountRsp', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ fields['session_count'] = span[1]
+ span = span[2:]
+ return SessionGetCountRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionGetStateCmd(SessionCommand):
+ session_id: int
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionGetStateCmd', bytes]:
+ if len(span) < 4:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ span = span[4:]
+ return SessionGetStateCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionGetStateRsp(SessionResponse):
+ status: StatusCode
+ session_state: SessionState
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionGetStateRsp', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ fields['session_state'] = SessionState(span[1])
+ span = span[2:]
+ return SessionGetStateRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class Controlee(Packet):
+ short_address: int
+ subsession_id: int
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['Controlee', bytes]:
+ fields = {'payload': None}
+ if len(span) < 6:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:2], byteorder='little')
+ fields['short_address'] = value_
+ value_ = int.from_bytes(span[2:6], byteorder='little')
+ fields['subsession_id'] = value_
+ span = span[6:]
+ return Controlee(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionUpdateControllerMulticastListCmd(SessionCommand):
+ session_id: int
+ action: int
+ controlees: List[Controlee]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateControllerMulticastListCmd', bytes]:
+ if len(span) < 6:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ fields['action'] = span[4]
+ controlees_count = span[5]
+ span = span[6:]
+ if len(span) < controlees_count * 6:
+ raise Exception('Invalid packet size')
+ controlees = []
+ for n in range(controlees_count):
+ controlees.append(Controlee.parse_all(span[n * 6:(n + 1) * 6]))
+ fields['controlees'] = controlees
+ span = span[controlees_count * 6:]
+ return SessionUpdateControllerMulticastListCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionUpdateControllerMulticastListRsp(SessionResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateControllerMulticastListRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return SessionUpdateControllerMulticastListRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class ControleeStatus(Packet):
+ mac_address: int
+ subsession_id: int
+ status: int
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['ControleeStatus', bytes]:
+ fields = {'payload': None}
+ if len(span) < 7:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:2], byteorder='little')
+ fields['mac_address'] = value_
+ value_ = int.from_bytes(span[2:6], byteorder='little')
+ fields['subsession_id'] = value_
+ fields['status'] = span[6]
+ span = span[7:]
+ return ControleeStatus(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class SessionUpdateControllerMulticastListNtf(SessionNotification):
+ session_id: int
+ remaining_multicast_list_size: int
+ controlee_status: List[ControleeStatus]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateControllerMulticastListNtf', bytes]:
+ if len(span) < 6:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['session_id'] = value_
+ fields['remaining_multicast_list_size'] = span[4]
+ controlee_status_count = span[5]
+ span = span[6:]
+ if len(span) < controlee_status_count * 7:
+ raise Exception('Invalid packet size')
+ controlee_status = []
+ for n in range(controlee_status_count):
+ controlee_status.append(
+ ControleeStatus.parse_all(span[n * 7:(n + 1) * 7]))
+ fields['controlee_status'] = controlee_status
+ span = span[controlee_status_count * 7:]
+ return SessionUpdateControllerMulticastListNtf(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangeStartCmd(RangingCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangeStartCmd', bytes]:
+ return RangeStartCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangeStartRsp(RangingResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangeStartRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return RangeStartRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class ShortAddressTwoWayRangingMeasurement(Packet):
+ mac_address: int
+ status: StatusCode
+ nlos: int
+ distance: int
+ aoa_azimuth: int
+ aoa_azimuth_fom: int
+ aoa_elevation: int
+ aoa_elevation_fom: int
+ aoa_destination_azimuth: int
+ aoa_destination_azimuth_fom: int
+ aoa_destination_elevation: int
+ aoa_destination_elevation_fom: int
+ slot_index: int
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['ShortAddressTwoWayRangingMeasurement', bytes]:
+ fields = {'payload': None}
+ if len(span) < 31:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:2], byteorder='little')
+ fields['mac_address'] = value_
+ fields['status'] = StatusCode(span[2])
+ fields['nlos'] = span[3]
+ value_ = int.from_bytes(span[4:6], byteorder='little')
+ fields['distance'] = value_
+ value_ = int.from_bytes(span[6:8], byteorder='little')
+ fields['aoa_azimuth'] = value_
+ fields['aoa_azimuth_fom'] = span[8]
+ value_ = int.from_bytes(span[9:11], byteorder='little')
+ fields['aoa_elevation'] = value_
+ fields['aoa_elevation_fom'] = span[11]
+ value_ = int.from_bytes(span[12:14], byteorder='little')
+ fields['aoa_destination_azimuth'] = value_
+ fields['aoa_destination_azimuth_fom'] = span[14]
+ value_ = int.from_bytes(span[15:17], byteorder='little')
+ fields['aoa_destination_elevation'] = value_
+ fields['aoa_destination_elevation_fom'] = span[17]
+ fields['slot_index'] = span[18]
+ value_ = int.from_bytes(span[19:31], byteorder='little')
+ span = span[31:]
+ return ShortAddressTwoWayRangingMeasurement(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class ExtendedAddressTwoWayRangingMeasurement(Packet):
+ mac_address: int
+ status: StatusCode
+ nlos: int
+ distance: int
+ aoa_azimuth: int
+ aoa_azimuth_fom: int
+ aoa_elevation: int
+ aoa_elevation_fom: int
+ aoa_destination_azimuth: int
+ aoa_destination_azimuth_fom: int
+ aoa_destination_elevation: int
+ aoa_destination_elevation_fom: int
+ slot_index: int
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['ExtendedAddressTwoWayRangingMeasurement', bytes]:
+ fields = {'payload': None}
+ if len(span) < 31:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:8], byteorder='little')
+ fields['mac_address'] = value_
+ fields['status'] = StatusCode(span[8])
+ fields['nlos'] = span[9]
+ value_ = int.from_bytes(span[10:12], byteorder='little')
+ fields['distance'] = value_
+ value_ = int.from_bytes(span[12:14], byteorder='little')
+ fields['aoa_azimuth'] = value_
+ fields['aoa_azimuth_fom'] = span[14]
+ value_ = int.from_bytes(span[15:17], byteorder='little')
+ fields['aoa_elevation'] = value_
+ fields['aoa_elevation_fom'] = span[17]
+ value_ = int.from_bytes(span[18:20], byteorder='little')
+ fields['aoa_destination_azimuth'] = value_
+ fields['aoa_destination_azimuth_fom'] = span[20]
+ value_ = int.from_bytes(span[21:23], byteorder='little')
+ fields['aoa_destination_elevation'] = value_
+ fields['aoa_destination_elevation_fom'] = span[23]
+ fields['slot_index'] = span[24]
+ value_ = int.from_bytes(span[25:31], byteorder='little')
+ span = span[31:]
+ return ExtendedAddressTwoWayRangingMeasurement(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+class RangingMeasurementType(enum.IntEnum):
+ ONE_WAY = 0x0
+ TWO_WAY = 0x1
+
+
+@dataclass
+class RangeDataNtf(RangingNotification):
+ sequence_number: int
+ session_id: int
+ rcr_indicator: int
+ current_ranging_interval: int
+ ranging_measurement_type: RangingMeasurementType
+ mac_address_indicator: MacAddressIndicator
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangeDataNtf', bytes]:
+ if len(span) < 24:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:4], byteorder='little')
+ fields['sequence_number'] = value_
+ value_ = int.from_bytes(span[4:8], byteorder='little')
+ fields['session_id'] = value_
+ fields['rcr_indicator'] = span[8]
+ value_ = int.from_bytes(span[9:13], byteorder='little')
+ fields['current_ranging_interval'] = value_
+ fields['ranging_measurement_type'] = RangingMeasurementType(span[13])
+ fields['mac_address_indicator'] = MacAddressIndicator(span[15])
+ value_ = int.from_bytes(span[16:24], byteorder='little')
+ span = span[24:]
+ payload = span
+ fields['payload'] = payload
+ if fields['ranging_measurement_type'] == RangingMeasurementType.TWO_WAY and fields['mac_address_indicator'] == MacAddressIndicator.SHORT_ADDRESS:
+ return ShortMacTwoWayRangeDataNtf.parse(fields, payload)
+ elif fields['ranging_measurement_type'] == RangingMeasurementType.TWO_WAY and fields['mac_address_indicator'] == MacAddressIndicator.EXTENDED_ADDRESS:
+ return ExtendedMacTwoWayRangeDataNtf.parse(fields, payload)
+ else:
+ return RangeDataNtf(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class ShortMacTwoWayRangeDataNtf(RangeDataNtf):
+ two_way_ranging_measurements: List[ShortAddressTwoWayRangingMeasurement]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['ShortMacTwoWayRangeDataNtf', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ two_way_ranging_measurements_count = span[0]
+ span = span[1:]
+ if len(span) < two_way_ranging_measurements_count * 31:
+ raise Exception('Invalid packet size')
+ two_way_ranging_measurements = []
+ for n in range(two_way_ranging_measurements_count):
+ two_way_ranging_measurements.append(
+ ShortAddressTwoWayRangingMeasurement.parse_all(span[n * 31:(n + 1) * 31]))
+ fields['two_way_ranging_measurements'] = two_way_ranging_measurements
+ span = span[two_way_ranging_measurements_count * 31:]
+ return ShortMacTwoWayRangeDataNtf(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class ExtendedMacTwoWayRangeDataNtf(RangeDataNtf):
+ two_way_ranging_measurements: List[ExtendedAddressTwoWayRangingMeasurement]
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['ExtendedMacTwoWayRangeDataNtf', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ two_way_ranging_measurements_count = span[0]
+ span = span[1:]
+ if len(span) < two_way_ranging_measurements_count * 31:
+ raise Exception('Invalid packet size')
+ two_way_ranging_measurements = []
+ for n in range(two_way_ranging_measurements_count):
+ two_way_ranging_measurements.append(
+ ExtendedAddressTwoWayRangingMeasurement.parse_all(span[n * 31:(n + 1) * 31]))
+ fields['two_way_ranging_measurements'] = two_way_ranging_measurements
+ span = span[two_way_ranging_measurements_count * 31:]
+ return ExtendedMacTwoWayRangeDataNtf(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangeStopCmd(RangingCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangeStopCmd', bytes]:
+ return RangeStopCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangeStopRsp(RangingResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangeStopRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return RangeStopRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangeGetRangingCountCmd(RangingCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangeGetRangingCountCmd', bytes]:
+ return RangeGetRangingCountCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class RangeGetRangingCountRsp(RangingResponse):
+ status: StatusCode
+ count: int
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['RangeGetRangingCountRsp', bytes]:
+ if len(span) < 5:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ value_ = int.from_bytes(span[1:5], byteorder='little')
+ fields['count'] = value_
+ span = span[5:]
+ return RangeGetRangingCountRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaPosition(Packet):
+ x: int
+ y: int
+ z: int
+ yaw: int
+ pitch: int
+ roll: int
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['PicaPosition', bytes]:
+ fields = {'payload': None}
+ if len(span) < 11:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:2], byteorder='little')
+ fields['x'] = value_
+ value_ = int.from_bytes(span[2:4], byteorder='little')
+ fields['y'] = value_
+ value_ = int.from_bytes(span[4:6], byteorder='little')
+ fields['z'] = value_
+ value_ = int.from_bytes(span[6:8], byteorder='little')
+ fields['yaw'] = value_
+ fields['pitch'] = span[8]
+ value_ = int.from_bytes(span[9:11], byteorder='little')
+ fields['roll'] = value_
+ span = span[11:]
+ return PicaPosition(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaInitDeviceCmd(PicaCommand):
+ mac_address: int
+ position: PicaPosition
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaInitDeviceCmd', bytes]:
+ if len(span) < 19:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:8], byteorder='little')
+ fields['mac_address'] = value_
+ fields['position'] = PicaPosition.parse_all(span[8:19])
+ span = span[19:]
+ return PicaInitDeviceCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaInitDeviceRsp(PicaResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaInitDeviceRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return PicaInitDeviceRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaSetDevicePositionCmd(PicaCommand):
+ position: PicaPosition
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaSetDevicePositionCmd', bytes]:
+ if len(span) < 11:
+ raise Exception('Invalid packet size')
+ fields['position'] = PicaPosition.parse_all(span[0:11])
+ span = span[11:]
+ return PicaSetDevicePositionCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaSetDevicePositionRsp(PicaResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaSetDevicePositionRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return PicaSetDevicePositionRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaCreateBeaconCmd(PicaCommand):
+ mac_address: int
+ position: PicaPosition
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaCreateBeaconCmd', bytes]:
+ if len(span) < 19:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:8], byteorder='little')
+ fields['mac_address'] = value_
+ fields['position'] = PicaPosition.parse_all(span[8:19])
+ span = span[19:]
+ return PicaCreateBeaconCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaCreateBeaconRsp(PicaResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaCreateBeaconRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return PicaCreateBeaconRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaSetBeaconPositionCmd(PicaCommand):
+ mac_address: int
+ position: PicaPosition
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaSetBeaconPositionCmd', bytes]:
+ if len(span) < 19:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:8], byteorder='little')
+ fields['mac_address'] = value_
+ fields['position'] = PicaPosition.parse_all(span[8:19])
+ span = span[19:]
+ return PicaSetBeaconPositionCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaSetBeaconPositionRsp(PicaResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaSetBeaconPositionRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return PicaSetBeaconPositionRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaDestroyBeaconCmd(PicaCommand):
+ mac_address: int
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaDestroyBeaconCmd', bytes]:
+ if len(span) < 8:
+ raise Exception('Invalid packet size')
+ value_ = int.from_bytes(span[0:8], byteorder='little')
+ fields['mac_address'] = value_
+ span = span[8:]
+ return PicaDestroyBeaconCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PicaDestroyBeaconRsp(PicaResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['PicaDestroyBeaconRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return PicaDestroyBeaconRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AndroidGetPowerStatsCmd(AndroidCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['AndroidGetPowerStatsCmd', bytes]:
+ return AndroidGetPowerStatsCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class PowerStats(Packet):
+ status: StatusCode
+ idle_time_ms: int
+ tx_time_ms: int
+ rx_time_ms: int
+ total_wake_count: int
+
+ @staticmethod
+ def parse(span: bytes) -> Tuple['PowerStats', bytes]:
+ fields = {'payload': None}
+ if len(span) < 17:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ value_ = int.from_bytes(span[1:5], byteorder='little')
+ fields['idle_time_ms'] = value_
+ value_ = int.from_bytes(span[5:9], byteorder='little')
+ fields['tx_time_ms'] = value_
+ value_ = int.from_bytes(span[9:13], byteorder='little')
+ fields['rx_time_ms'] = value_
+ value_ = int.from_bytes(span[13:17], byteorder='little')
+ fields['total_wake_count'] = value_
+ span = span[17:]
+ return PowerStats(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AndroidGetPowerStatsRsp(AndroidResponse):
+ stats: PowerStats
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['AndroidGetPowerStatsRsp', bytes]:
+ if len(span) < 17:
+ raise Exception('Invalid packet size')
+ fields['stats'] = PowerStats.parse_all(span[0:17])
+ span = span[17:]
+ return AndroidGetPowerStatsRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AndroidSetCountryCodeCmd(AndroidCommand):
+ country_code: bytes
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['AndroidSetCountryCodeCmd', bytes]:
+ if len(span) < 2:
+ raise Exception('Invalid packet size')
+ country_code = []
+ for n in range(2):
+ country_code.append(int.from_bytes(
+ span[n:n + 1], byteorder='little'))
+ fields['country_code'] = country_code
+ span = span[2:]
+ return AndroidSetCountryCodeCmd(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class AndroidSetCountryCodeRsp(AndroidResponse):
+ status: StatusCode
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['AndroidSetCountryCodeRsp', bytes]:
+ if len(span) < 1:
+ raise Exception('Invalid packet size')
+ fields['status'] = StatusCode(span[0])
+ span = span[1:]
+ return AndroidSetCountryCodeRsp(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_A_Command(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_A_Command', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_A_Command(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_B_Command(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_B_Command', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_B_Command(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_E_Command(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_E_Command', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_E_Command(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_F_Command(UciCommand):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_F_Command', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_F_Command(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_A_Response(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_A_Response', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_A_Response(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_B_Response(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_B_Response', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_B_Response(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_E_Response(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_E_Response', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_E_Response(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_F_Response(UciResponse):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_F_Response', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_F_Response(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_A_Notification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_A_Notification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_A_Notification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_B_Notification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_B_Notification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_B_Notification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_E_Notification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_E_Notification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_E_Notification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
+
+
+@dataclass
+class UciVendor_F_Notification(UciNotification):
+
+ @staticmethod
+ def parse(fields: dict, span: bytes) -> Tuple['UciVendor_F_Notification', bytes]:
+ payload = span
+ fields['payload'] = payload
+ return UciVendor_F_Notification(**fields), span
+
+ def serialize(self) -> bytes:
+ pass
diff --git a/src/device.rs b/src/device.rs
new file mode 100644
index 0000000..3d2f27d
--- /dev/null
+++ b/src/device.rs
@@ -0,0 +1,515 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::position::Position;
+use crate::uci_packets::*;
+use crate::MacAddress;
+use crate::PicaCommand;
+
+use std::collections::HashMap;
+use std::iter::Extend;
+
+use tokio::sync::mpsc;
+
+use num_traits::FromPrimitive;
+
+use super::session::{Session, MAX_SESSION};
+
+pub const MAX_DEVICE: usize = 4;
+const UCI_VERSION: u16 = 0x1001; // Version 1.1.0
+const MAC_VERSION: u16 = 0x3001; // Version 1.3.0
+const PHY_VERSION: u16 = 0x3001; // Version 1.3.0
+const TEST_VERSION: u16 = 0x1001; // Version 1.1
+
+// Capabilities are vendor defined, Android parses capabilities
+// according to these definitions:
+// /android/packages/modules/Uwb/service/java/com/android/server/uwb/config/CapabilityParam.java
+pub const DEFAULT_CAPS_INFO: &[(CapTlvType, &[u8])] = &[
+ // Fira params
+ (CapTlvType::SupportedFiraPhyVersionRange, &[1, 1, 1, 3]), // 1.1 - 1.3
+ (CapTlvType::SupportedFiraMacVersionRange, &[1, 1, 1, 3]), // 1.1 - 1.3
+ (CapTlvType::SupportedDeviceRoles, &[0x3]), // INTIATOR | RESPONDER
+ (CapTlvType::SupportedRangingMethod, &[0x1f]), // DS_TWR_NON_DEFERRED | SS_TWR_NON_DEFERRED | DS_TWR_DEFERRED | SS_TWR_DEFERRED | OWR
+ (CapTlvType::SupportedStsConfig, &[0x7]), // STATIC_STS | DYNAMIC_STS | DYNAMIC_STS_RESPONDER_SPECIFIC_SUBSESSION_KEY
+ (CapTlvType::SupportedMultiNodeModes, &[0xff]),
+ (CapTlvType::SupportedBlockStriding, &[0x1]),
+ (CapTlvType::SupportedUwbInitiationTime, &[0x01]),
+ (CapTlvType::SupportedChannels, &[0xff]),
+ (CapTlvType::SupportedRframeConfig, &[0xff]),
+ (CapTlvType::SupportedBprfParameterSets, &[0xff]),
+ (CapTlvType::SupportedHprfParameterSets, &[0xff]),
+ (CapTlvType::SupportedCcConstraintLength, &[0xff]),
+ (CapTlvType::SupportedAoa, &[0xff]),
+ (CapTlvType::SupportedAoaResultReqAntennaInterleaving, &[0x1]),
+ (CapTlvType::SupportedExtendedMacAddress, &[0x1]),
+ // CCC params
+ (CapTlvType::CccSupportedVersions, &[1, 0]),
+ (CapTlvType::CccSupportedUwbConfigs, &[0]),
+ (CapTlvType::CccSupportedPulseShapeCombos, &[0]),
+ (CapTlvType::CccSupportedRanMultiplier, &[0, 0, 0, 0]),
+ (CapTlvType::CccSupportedChapsPerSlot, &[0xff]),
+ (CapTlvType::CccSupportedSyncCodes, &[0xff, 0xff, 0xff, 0xff]),
+ (CapTlvType::CccSupportedChannels, &[0xff]),
+ (
+ CapTlvType::CccSupportedHoppingConfigModesAndSequences,
+ &[0xff],
+ ),
+];
+
+pub struct Device {
+ handle: usize,
+ pub mac_address: MacAddress,
+ pub position: Position,
+ /// [UCI] 5. UWBS Device State Machine
+ state: DeviceState,
+ sessions: HashMap<u32, Session>,
+ pub tx: mpsc::Sender<UciPacketPacket>,
+ pica_tx: mpsc::Sender<PicaCommand>,
+ config: HashMap<u8, Vec<u8>>,
+ country_code: [u8; 2],
+
+ n_active_sessions: usize,
+}
+
+impl Device {
+ pub fn new(
+ device_handle: usize,
+ tx: mpsc::Sender<UciPacketPacket>,
+ pica_tx: mpsc::Sender<PicaCommand>,
+ ) -> Self {
+ let mac_address = {
+ let handle = device_handle as u16;
+ MacAddress::Short(handle.to_be_bytes())
+ };
+ Device {
+ handle: device_handle,
+ mac_address,
+ position: Position::default(),
+ state: DeviceState::DeviceStateError, // Will be overwitten
+ sessions: Default::default(),
+ tx,
+ pica_tx,
+ config: HashMap::new(),
+ country_code: Default::default(),
+ n_active_sessions: 0,
+ }
+ }
+
+ fn set_state(&mut self, device_state: DeviceState) {
+ // No transition: ignore
+ if device_state == self.state {
+ return;
+ }
+
+ // Send status notification
+ self.state = device_state;
+ let tx = self.tx.clone();
+ tokio::spawn(async move {
+ tx.send(DeviceStatusNtfBuilder { device_state }.build().into())
+ .await
+ .unwrap()
+ });
+ }
+
+ pub fn init(&mut self) {
+ self.set_state(DeviceState::DeviceStateReady);
+ }
+
+ pub fn get_session(&self, session_id: u32) -> Option<&Session> {
+ self.sessions.get(&session_id)
+ }
+
+ pub fn get_session_mut(&mut self, session_id: u32) -> Option<&mut Session> {
+ self.sessions.get_mut(&session_id)
+ }
+
+ // The fira norm specify to send a response, then reset, then
+ // send a notification once the reset is done
+ fn command_device_reset(&mut self, cmd: DeviceResetCmdPacket) -> DeviceResetRspPacket {
+ let reset_config = cmd.get_reset_config();
+ println!("[{}] DeviceReset", self.handle);
+ println!(" reset_config={}", reset_config);
+
+ let status = match reset_config {
+ ResetConfig::UwbsReset => StatusCode::UciStatusOk,
+ };
+
+ *self = Device::new(self.handle, self.tx.clone(), self.pica_tx.clone());
+
+ DeviceResetRspBuilder { status }.build()
+ }
+
+ fn command_get_device_info(&self, _cmd: GetDeviceInfoCmdPacket) -> GetDeviceInfoRspPacket {
+ // TODO: Implement a fancy build time state machine instead of crash at runtime
+ println!("[{}] GetDeviceInfo", self.handle);
+ assert_eq!(self.state, DeviceState::DeviceStateReady);
+ GetDeviceInfoRspBuilder {
+ status: StatusCode::UciStatusOk,
+ uci_version: UCI_VERSION,
+ mac_version: MAC_VERSION,
+ phy_version: PHY_VERSION,
+ uci_test_version: TEST_VERSION,
+ vendor_spec_info: Vec::new(),
+ }
+ .build()
+ }
+
+ pub fn command_get_caps_info(&self, cmd: GetCapsInfoCmdPacket) -> GetCapsInfoRspPacket {
+ println!("[{}] GetCapsInfo", self.handle);
+ assert_eq!(
+ cmd.get_packet_boundary_flag(),
+ PacketBoundaryFlag::Complete,
+ "Boundary flag is true, implement fragmentation"
+ );
+
+ let caps = DEFAULT_CAPS_INFO
+ .iter()
+ .map(|(id, value)| CapTlv {
+ t: *id,
+ v: (*value).into(),
+ })
+ .collect();
+
+ GetCapsInfoRspBuilder {
+ status: StatusCode::UciStatusOk,
+ tlvs: caps,
+ }
+ .build()
+ }
+
+ pub fn command_set_config(&mut self, cmd: SetConfigCmdPacket) -> SetConfigRspPacket {
+ println!("[{}] SetConfig", self.handle);
+ assert_eq!(self.state, DeviceState::DeviceStateReady); // UCI 6.3
+ assert_eq!(
+ cmd.get_packet_boundary_flag(),
+ PacketBoundaryFlag::Complete,
+ "Boundary flag is true, implement fragmentation"
+ );
+
+ let (valid_parameters, invalid_config_status) = cmd.get_parameters().iter().fold(
+ (HashMap::new(), Vec::new()),
+ |(mut valid_parameters, mut invalid_config_status), param| {
+ let id = param.id;
+ match DeviceConfigId::from_u8(id) {
+ Some(_) => {
+ // TODO: DeviceState is a read only parameter
+ valid_parameters.insert(param.id, param.value.clone());
+ }
+ None => {
+ // TODO: silently ignore vendor parameter
+ invalid_config_status.push(DeviceConfigStatus {
+ parameter_id: id,
+ status: StatusCode::UciStatusInvalidParam,
+ })
+ }
+ };
+ (valid_parameters, invalid_config_status)
+ },
+ );
+
+ let (status, parameters) = if invalid_config_status.is_empty() {
+ self.config.extend(valid_parameters.into_iter());
+ (StatusCode::UciStatusOk, Vec::new())
+ } else {
+ (StatusCode::UciStatusInvalidParam, invalid_config_status)
+ };
+
+ SetConfigRspBuilder { status, parameters }.build()
+ }
+
+ pub fn command_get_config(&self, cmd: GetConfigCmdPacket) -> GetConfigRspPacket {
+ println!("[{}] GetConfig", self.handle);
+ assert_eq!(
+ cmd.get_packet_boundary_flag(),
+ PacketBoundaryFlag::Complete,
+ "Boundary flag is true, implement fragmentation"
+ );
+ let ids = cmd.get_parameter_ids();
+
+ // TODO: do this config shall be set on device reset
+ let (valid_parameters, invalid_parameters) = ids.iter().fold(
+ (Vec::new(), Vec::new()),
+ |(mut valid_parameters, mut invalid_parameters), id| {
+ // UCI Core Section 6.3.2 Table 8
+ // UCI Core Section 6.3.2 - Return the Configuration
+ // If the status code is ok, return the params
+ // If there is at least one invalid param, return the list of invalid params
+ // If the ID is not present in our config, return the Type with length = 0
+ match self.config.get(id) {
+ Some(value) => valid_parameters.push(DeviceParameter {
+ id: *id,
+ value: value.clone(),
+ }),
+ None => invalid_parameters.push(DeviceParameter {
+ id: *id,
+ value: Vec::new(),
+ }),
+ }
+
+ (valid_parameters, invalid_parameters)
+ },
+ );
+
+ let (status, parameters) = if invalid_parameters.is_empty() {
+ (StatusCode::UciStatusOk, valid_parameters)
+ } else {
+ (StatusCode::UciStatusInvalidParam, invalid_parameters)
+ };
+
+ GetConfigRspBuilder { status, parameters }.build()
+ }
+
+ fn command_session_init(&mut self, cmd: SessionInitCmdPacket) -> SessionInitRspPacket {
+ let session_id = cmd.get_session_id();
+ let session_type = cmd.get_session_type();
+
+ println!("[{}] Session init", self.handle);
+ println!(" session_id=0x{:x}", session_id);
+ println!(" session_type={}", session_type);
+
+ let status = if self.sessions.len() >= MAX_SESSION {
+ StatusCode::UciStatusMaxSessionsExceeded
+ } else {
+ match self.sessions.insert(
+ session_id,
+ Session::new(
+ session_id,
+ session_type,
+ self.handle,
+ self.tx.clone(),
+ self.pica_tx.clone(),
+ ),
+ ) {
+ Some(_) => StatusCode::UciStatusSessionDuplicate,
+ None => {
+ // Should not fail
+ self.get_session_mut(session_id).unwrap().init();
+ StatusCode::UciStatusOk
+ }
+ }
+ };
+
+ SessionInitRspBuilder { status }.build()
+ }
+
+ fn command_session_deinit(&mut self, cmd: SessionDeinitCmdPacket) -> SessionDeinitRspPacket {
+ let session_id = cmd.get_session_id();
+ println!("[{}] Session deinit", self.handle);
+ println!(" session_id=0x{:x}", session_id);
+
+ let status = if self.sessions.remove(&session_id).is_some() {
+ StatusCode::UciStatusOk
+ } else {
+ StatusCode::UciStatusSessionNotExist
+ };
+
+ SessionDeinitRspBuilder { status }.build()
+ }
+
+ fn command_session_get_count(
+ &self,
+ _cmd: SessionGetCountCmdPacket,
+ ) -> SessionGetCountRspPacket {
+ println!("[{}] Session get count", self.handle);
+
+ SessionGetCountRspBuilder {
+ status: StatusCode::UciStatusOk,
+ session_count: self.sessions.len() as u8,
+ }
+ .build()
+ }
+
+ fn command_set_country_code(
+ &mut self,
+ cmd: AndroidSetCountryCodeCmdPacket,
+ ) -> AndroidSetCountryCodeRspPacket {
+ let country_code = *cmd.get_country_code();
+ println!("[{}] Set country code", self.handle);
+ println!(" country_code={},{}", country_code[0], country_code[1]);
+
+ self.country_code = country_code;
+ AndroidSetCountryCodeRspBuilder {
+ status: StatusCode::UciStatusOk,
+ }
+ .build()
+ }
+
+ fn command_get_power_stats(
+ &mut self,
+ _cmd: AndroidGetPowerStatsCmdPacket,
+ ) -> AndroidGetPowerStatsRspPacket {
+ println!("[{}] Get power stats", self.handle);
+
+ // TODO
+ AndroidGetPowerStatsRspBuilder {
+ stats: PowerStats {
+ status: StatusCode::UciStatusOk,
+ idle_time_ms: 0,
+ tx_time_ms: 0,
+ rx_time_ms: 0,
+ total_wake_count: 0,
+ },
+ }
+ .build()
+ }
+
+ pub fn command(&mut self, cmd: UciCommandPacket) -> UciResponsePacket {
+ match cmd.specialize() {
+ // Handle commands for this device
+ UciCommandChild::CoreCommand(core_command) => match core_command.specialize() {
+ CoreCommandChild::DeviceResetCmd(cmd) => self.command_device_reset(cmd).into(),
+ CoreCommandChild::GetDeviceInfoCmd(cmd) => self.command_get_device_info(cmd).into(),
+ CoreCommandChild::GetCapsInfoCmd(cmd) => self.command_get_caps_info(cmd).into(),
+ CoreCommandChild::SetConfigCmd(cmd) => self.command_set_config(cmd).into(),
+ CoreCommandChild::GetConfigCmd(cmd) => self.command_get_config(cmd).into(),
+ _ => panic!("Unsupported core command"),
+ },
+ // Handle commands for session management
+ UciCommandChild::SessionCommand(session_command) => {
+ // Session commands directly handled at Device level
+ match session_command.specialize() {
+ SessionCommandChild::SessionInitCmd(cmd) => {
+ return self.command_session_init(cmd).into();
+ }
+ SessionCommandChild::SessionDeinitCmd(cmd) => {
+ return self.command_session_deinit(cmd).into();
+ }
+ SessionCommandChild::SessionGetCountCmd(cmd) => {
+ return self.command_session_get_count(cmd).into();
+ }
+ _ => {}
+ }
+
+ // Common code for retrieving the session_id in the command
+ let session_id = match session_command.specialize() {
+ SessionCommandChild::SessionSetAppConfigCmd(cmd) => cmd.get_session_id(),
+ SessionCommandChild::SessionGetAppConfigCmd(cmd) => cmd.get_session_id(),
+ SessionCommandChild::SessionGetStateCmd(cmd) => cmd.get_session_id(),
+ SessionCommandChild::SessionUpdateControllerMulticastListCmd(cmd) => {
+ cmd.get_session_id()
+ }
+ _ => panic!("Unsupported session command type"),
+ };
+
+ if let Some(session) = self.get_session_mut(session_id) {
+ // There is a session matching the session_id in the command
+ // Pass the command through
+ match session_command.specialize() {
+ SessionCommandChild::SessionSetAppConfigCmd(_)
+ | SessionCommandChild::SessionGetAppConfigCmd(_)
+ | SessionCommandChild::SessionGetStateCmd(_)
+ | SessionCommandChild::SessionUpdateControllerMulticastListCmd(_) => {
+ session.session_command(session_command).into()
+ }
+ _ => panic!("Unsupported session command"),
+ }
+ } else {
+ // There is no session matching the session_id in the command
+ let status = StatusCode::UciStatusSessionNotExist;
+ match session_command.specialize() {
+ SessionCommandChild::SessionSetAppConfigCmd(_) => {
+ SessionSetAppConfigRspBuilder {
+ status,
+ parameters: Vec::new(),
+ }
+ .build()
+ .into()
+ }
+ SessionCommandChild::SessionGetAppConfigCmd(_) => {
+ SessionGetAppConfigRspBuilder {
+ status,
+ parameters: Vec::new(),
+ }
+ .build()
+ .into()
+ }
+ SessionCommandChild::SessionGetStateCmd(_) => SessionGetStateRspBuilder {
+ status,
+ session_state: SessionState::SessionStateDeinit,
+ }
+ .build()
+ .into(),
+ SessionCommandChild::SessionUpdateControllerMulticastListCmd(_) => {
+ SessionUpdateControllerMulticastListRspBuilder { status }
+ .build()
+ .into()
+ }
+ _ => panic!("Unsupported session command"),
+ }
+ }
+ }
+ UciCommandChild::RangingCommand(ranging_command) => {
+ let session_id = ranging_command.get_session_id();
+ if let Some(session) = self.get_session_mut(session_id) {
+ // Forward to the proper session
+ let response = session.ranging_command(ranging_command);
+ match response.specialize() {
+ RangingResponseChild::RangeStartRsp(rsp)
+ if rsp.get_status() == StatusCode::UciStatusOk =>
+ {
+ self.n_active_sessions += 1;
+ self.set_state(DeviceState::DeviceStateActive);
+ }
+ RangingResponseChild::RangeStopRsp(rsp)
+ if rsp.get_status() == StatusCode::UciStatusOk =>
+ {
+ assert!(self.n_active_sessions > 0);
+ self.n_active_sessions -= 1;
+ if self.n_active_sessions == 0 {
+ self.set_state(DeviceState::DeviceStateReady);
+ }
+ }
+ _ => {}
+ }
+ response.into()
+ } else {
+ let status = StatusCode::UciStatusSessionNotExist;
+ match ranging_command.specialize() {
+ RangingCommandChild::RangeStartCmd(_) => {
+ RangeStartRspBuilder { status }.build().into()
+ }
+ RangingCommandChild::RangeStopCmd(_) => {
+ RangeStopRspBuilder { status }.build().into()
+ }
+ RangingCommandChild::RangeGetRangingCountCmd(_) => {
+ RangeGetRangingCountRspBuilder { status, count: 0 }
+ .build()
+ .into()
+ }
+ _ => panic!("Unsupported ranging command"),
+ }
+ }
+ }
+
+ UciCommandChild::AndroidCommand(android_command) => {
+ match android_command.specialize() {
+ AndroidCommandChild::AndroidSetCountryCodeCmd(cmd) => {
+ self.command_set_country_code(cmd).into()
+ }
+ AndroidCommandChild::AndroidGetPowerStatsCmd(cmd) => {
+ self.command_get_power_stats(cmd).into()
+ }
+ _ => panic!("Unsupported Android command"),
+ }
+ }
+ // TODO: Handle properly without panic
+ _ => UciResponseBuilder {
+ group_id: GroupId::Core,
+ opcode: 0,
+ payload: None,
+ }
+ .build(),
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..66e07f7
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,658 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use anyhow::Result;
+use bytes::{Bytes, BytesMut};
+use serde::Serialize;
+use std::collections::HashMap;
+use std::fmt::Display;
+use std::path::PathBuf;
+use thiserror::Error;
+use tokio::io::AsyncReadExt;
+use tokio::net::TcpStream;
+use tokio::sync::{broadcast, mpsc, oneshot};
+
+use num_traits::{FromPrimitive, ToPrimitive};
+
+mod pcapng;
+
+mod position;
+use position::Position;
+
+mod uci_packets;
+use uci_packets::StatusCode as UciStatusCode;
+use uci_packets::*;
+
+mod device;
+use device::{Device, MAX_DEVICE};
+
+mod session;
+use session::MAX_SESSION;
+
+pub mod web;
+use web::Category;
+
+pub mod mac_address;
+use mac_address::MacAddress;
+
+const MAX_PAYLOAD_SIZE: usize = 4096;
+
+struct Connection {
+ socket: TcpStream,
+ buffer: BytesMut,
+ pcapng_file: Option<pcapng::File>,
+}
+
+impl Connection {
+ fn new(socket: TcpStream, pcapng_file: Option<pcapng::File>) -> Self {
+ Connection {
+ socket,
+ buffer: BytesMut::with_capacity(MAX_PAYLOAD_SIZE),
+ pcapng_file,
+ }
+ }
+
+ async fn read(&mut self) -> Result<Option<BytesMut>> {
+ let len = self.socket.read_buf(&mut self.buffer).await?;
+ if len == 0 {
+ return Ok(None);
+ }
+
+ if let Some(ref mut pcapng_file) = self.pcapng_file {
+ pcapng_file
+ .write(&self.buffer, pcapng::Direction::Tx)
+ .await?
+ }
+
+ let bytes = self.buffer.split_to(self.buffer.len());
+ Ok(Some(bytes))
+ }
+
+ async fn write(&mut self, packet: Bytes) -> Result<()> {
+ if let Some(ref mut pcapng_file) = self.pcapng_file {
+ pcapng_file.write(&packet, pcapng::Direction::Rx).await?
+ }
+
+ let _ = self.socket.try_write(&packet)?;
+ Ok(())
+ }
+}
+
+pub type PicaCommandStatus = Result<(), PicaCommandError>;
+
+#[derive(Error, Debug, Clone, PartialEq, Eq)]
+pub enum PicaCommandError {
+ #[error("Device already exists: {0}")]
+ DeviceAlreadyExists(MacAddress),
+ #[error("Device not found: {0}")]
+ DeviceNotFound(MacAddress),
+}
+
+#[derive(Debug)]
+pub enum PicaCommand {
+ // Connect a new device.
+ Connect(TcpStream),
+ // Disconnect the selected device.
+ Disconnect(usize),
+ // Execute ranging command for selected device and session.
+ Ranging(usize, u32),
+ // Execute UCI command received for selected device.
+ Command(usize, UciCommandPacket),
+ // Init Uci Device
+ InitUciDevice(MacAddress, Position, oneshot::Sender<PicaCommandStatus>),
+ // Set Position
+ SetPosition(MacAddress, Position, oneshot::Sender<PicaCommandStatus>),
+ // Create Anchor
+ CreateAnchor(MacAddress, Position, oneshot::Sender<PicaCommandStatus>),
+ // Destroy Anchor
+ DestroyAnchor(MacAddress, oneshot::Sender<PicaCommandStatus>),
+ // Get State
+ GetState(oneshot::Sender<Vec<web::Device>>),
+}
+
+impl Display for PicaCommand {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let cmd = match self {
+ PicaCommand::Connect(_) => "Connect",
+ PicaCommand::Disconnect(_) => "Disconnect",
+ PicaCommand::Ranging(_, _) => "Ranging",
+ PicaCommand::Command(_, _) => "Command",
+ PicaCommand::InitUciDevice(_, _, _) => "InitUciDevice",
+ PicaCommand::SetPosition(_, _, _) => "SetPosition",
+ PicaCommand::CreateAnchor(_, _, _) => "CreateAnchor",
+ PicaCommand::DestroyAnchor(_, _) => "DestroyAnchor",
+ PicaCommand::GetState(_) => "GetState",
+ };
+ write!(f, "{}", cmd)
+ }
+}
+
+#[derive(Clone, Debug, Serialize)]
+#[serde(untagged)]
+pub enum PicaEvent {
+ // A Device was added
+ DeviceAdded {
+ device: web::Device,
+ },
+ // A Device was removed
+ DeviceRemoved {
+ device: web::Device,
+ },
+ // A Device position has changed
+ DeviceUpdated {
+ device: web::Device,
+ },
+ NeighborUpdated {
+ source_device: web::Device,
+ destination_device: web::Device,
+ distance: u16,
+ azimuth: i16,
+ elevation: i8,
+ },
+}
+
+#[derive(Debug, Clone, Copy)]
+struct Anchor {
+ mac_address: MacAddress,
+ position: Position,
+}
+
+pub struct Pica {
+ devices: HashMap<usize, Device>,
+ anchors: HashMap<MacAddress, Anchor>,
+ counter: usize,
+ rx: mpsc::Receiver<PicaCommand>,
+ tx: mpsc::Sender<PicaCommand>,
+ event_tx: broadcast::Sender<PicaEvent>,
+ pcapng_dir: Option<PathBuf>,
+}
+
+/// Result of UCI packet parsing.
+enum UciParseResult {
+ Ok(UciCommandPacket),
+ Err(Bytes),
+ Skip,
+}
+
+/// Parse incoming UCI packets.
+/// Handle parsing errors by crafting a suitable error response packet.
+fn parse_uci_packet(bytes: &[u8]) -> UciParseResult {
+ match UciPacketPacket::parse(bytes) {
+ // Parsing error. Determine what error response should be
+ // returned to the host:
+ // - response and notifications are ignored, no response
+ // - if the group id is not known, STATUS_UNKNOWN_GID,
+ // - otherwise, and to simplify the code, STATUS_UNKNOWN_OID is
+ // always returned. That means that malformed commands
+ // get the same status code, instead of
+ // STATUS_SYNTAX_ERROR.
+ Err(_) => {
+ let message_type = (bytes[0] >> 5) & 0x7;
+ let group_id = bytes[0] & 0xf;
+ let opcode_id = bytes[1] & 0x3f;
+
+ let status = match (
+ MessageType::from_u8(message_type),
+ GroupId::from_u8(group_id),
+ ) {
+ (Some(MessageType::Command), Some(_)) => UciStatusCode::UciStatusUnknownOid,
+ (Some(MessageType::Command), None) => UciStatusCode::UciStatusUnknownGid,
+ _ => return UciParseResult::Skip,
+ };
+ // The PDL generated code cannot be used to generate
+ // responses with invalid group identifiers.
+ let response = vec![
+ (MessageType::Response.to_u8().unwrap() << 5) | group_id,
+ opcode_id,
+ 0,
+ 1,
+ status.to_u8().unwrap(),
+ ];
+ UciParseResult::Err(response.into())
+ }
+
+ // Parsing success, ignore non command packets.
+ Ok(packet) => {
+ if let Ok(cmd) = packet.try_into() {
+ UciParseResult::Ok(cmd)
+ } else {
+ UciParseResult::Skip
+ }
+ }
+ }
+}
+
+impl Pica {
+ pub fn new(event_tx: broadcast::Sender<PicaEvent>, pcapng_dir: Option<PathBuf>) -> Self {
+ let (tx, rx) = mpsc::channel(MAX_SESSION * MAX_DEVICE);
+ Pica {
+ devices: HashMap::new(),
+ anchors: HashMap::new(),
+ counter: 0,
+ rx,
+ tx,
+ event_tx,
+ pcapng_dir,
+ }
+ }
+
+ pub fn tx(&self) -> mpsc::Sender<PicaCommand> {
+ self.tx.clone()
+ }
+
+ fn get_device_mut(&mut self, device_handle: usize) -> Option<&mut Device> {
+ self.devices.get_mut(&device_handle)
+ }
+
+ fn get_device(&self, device_handle: usize) -> Option<&Device> {
+ self.devices.get(&device_handle)
+ }
+
+ fn get_category(&self, mac_address: &MacAddress) -> Option<Category> {
+ if self.anchors.contains_key(mac_address) {
+ Some(Category::Anchor)
+ } else if self
+ .devices
+ .iter()
+ .any(|(_, device)| device.mac_address == *mac_address)
+ {
+ Some(Category::Uci)
+ } else {
+ None
+ }
+ }
+
+ fn get_device_mut_by_mac(&mut self, mac_address: MacAddress) -> Option<&mut Device> {
+ self.devices
+ .values_mut()
+ .find(|d| d.mac_address == mac_address)
+ }
+
+ fn send_event(&self, event: PicaEvent) {
+ // An error here means that we have
+ // no receivers, so ignore it
+ let _ = self.event_tx.send(event);
+ }
+
+ async fn connect(&mut self, stream: TcpStream) {
+ let (packet_tx, mut packet_rx) = mpsc::channel(MAX_SESSION);
+ let device_handle = self.counter;
+ let pica_tx = self.tx.clone();
+ let pcapng_dir = self.pcapng_dir.clone();
+
+ println!("[{}] Connecting device", device_handle);
+
+ self.counter += 1;
+ let mut device = Device::new(device_handle, packet_tx, self.tx.clone());
+ device.init();
+
+ self.send_event(PicaEvent::DeviceAdded {
+ device: web::Device::new(Category::Uci, device.mac_address, device.position),
+ });
+
+ self.devices.insert(device_handle, device);
+
+ // Spawn and detach the connection handling task.
+ // The task notifies pica when exiting to let it clean
+ // the state.
+ tokio::spawn(async move {
+ let pcapng_file: Option<pcapng::File> = if let Some(dir) = pcapng_dir {
+ let full_path = dir.join(format!("device-{}.pcapng", device_handle));
+ println!("Recording pcapng to file {}", full_path.as_path().display());
+ Some(pcapng::File::create(full_path).await.unwrap())
+ } else {
+ None
+ };
+
+ let mut connection = Connection::new(stream, pcapng_file);
+ 'outer: loop {
+ tokio::select! {
+ // Read command packet sent from connected UWB host.
+ // Run associated command.
+ result = connection.read() =>
+ match result {
+ Ok(Some(packet)) =>
+ match parse_uci_packet(&packet) {
+ UciParseResult::Ok(cmd) =>
+ pica_tx.send(PicaCommand::Command(device_handle, cmd)).await.unwrap(),
+ UciParseResult::Err(response) =>
+ connection.write(response).await.unwrap(),
+ UciParseResult::Skip => (),
+ },
+ Ok(None) | Err(_) => break 'outer
+ },
+
+ // Send response packets to the connected UWB host.
+ Some(packet) = packet_rx.recv() =>
+ if connection.write(packet.to_bytes()).await.is_err() {
+ break 'outer
+ }
+ }
+ }
+ pica_tx
+ .send(PicaCommand::Disconnect(device_handle))
+ .await
+ .unwrap()
+ });
+ }
+
+ fn disconnect(&mut self, device_handle: usize) {
+ println!("[{}] Disconnecting device", device_handle);
+
+ match self
+ .devices
+ .get(&device_handle)
+ .ok_or_else(|| PicaCommandError::DeviceNotFound(device_handle.into()))
+ {
+ Ok(device) => {
+ self.send_event(PicaEvent::DeviceRemoved {
+ device: web::Device::new(Category::Uci, device.mac_address, device.position),
+ });
+ self.devices.remove(&device_handle);
+ }
+ Err(err) => println!("{}", err),
+ }
+ }
+
+ async fn ranging(&mut self, device_handle: usize, session_id: u32) {
+ println!("[{}] Ranging event", device_handle);
+ println!(" session_id={}", session_id);
+
+ let device = self.get_device(device_handle).unwrap();
+ let session = device.get_session(session_id).unwrap();
+
+ let mut measurements = Vec::new();
+ session
+ .get_dst_mac_addresses()
+ .iter()
+ .for_each(|mac_address| {
+ if let Some(anchor) = self.anchors.get(mac_address) {
+ let local = device
+ .position
+ .compute_range_azimuth_elevation(&anchor.position);
+ let remote = anchor
+ .position
+ .compute_range_azimuth_elevation(&device.position);
+
+ assert!(local.0 == remote.0);
+
+ // TODO: support extended address
+ match mac_address {
+ MacAddress::Short(address) => {
+ measurements.push(ShortAddressTwoWayRangingMeasurement {
+ mac_address: u16::from_be_bytes(*address),
+ status: UciStatusCode::UciStatusOk,
+ nlos: 0, // in Line Of Sight
+ distance: local.0,
+ aoa_azimuth: local.1 as u16,
+ aoa_azimuth_fom: 100, // Yup, pretty sure about this
+ aoa_elevation: local.2 as u16,
+ aoa_elevation_fom: 100, // Yup, pretty sure about this
+ aoa_destination_azimuth: remote.1 as u16,
+ aoa_destination_azimuth_fom: 100,
+ aoa_destination_elevation: remote.2 as u16,
+ aoa_destination_elevation_fom: 100,
+ slot_index: 0,
+ })
+ }
+ MacAddress::Extend(_) => unimplemented!(),
+ }
+ }
+ });
+
+ device
+ .tx
+ .send(
+ // TODO: support extended address
+ ShortMacTwoWayRangeDataNtfBuilder {
+ sequence_number: session.sequence_number,
+ session_id: session_id as u32,
+ rcr_indicator: 0, //TODO
+ current_ranging_interval: 0, //TODO
+ two_way_ranging_measurements: measurements,
+ }
+ .build()
+ .into(),
+ )
+ .await
+ .unwrap();
+
+ let device = self.get_device_mut(device_handle).unwrap();
+ let session = device.get_session_mut(session_id).unwrap();
+
+ session.sequence_number += 1;
+ }
+
+ async fn command(&mut self, device_handle: usize, cmd: UciCommandPacket) {
+ // TODO: implement fragmentation support
+ assert_eq!(
+ cmd.get_packet_boundary_flag(),
+ PacketBoundaryFlag::Complete,
+ "Boundary flag is true, implement fragmentation"
+ );
+
+ match self
+ .get_device_mut(device_handle)
+ .ok_or_else(|| PicaCommandError::DeviceNotFound(device_handle.into()))
+ {
+ Ok(device) => {
+ let response = device.command(cmd).into();
+ device.tx.send(response).await.unwrap_or_else(|err| {
+ println!("Failed to send UCI command response: {}", err)
+ });
+ }
+ Err(err) => println!("{}", err),
+ }
+ }
+
+ pub async fn run(&mut self) -> Result<()> {
+ loop {
+ use PicaCommand::*;
+ match self.rx.recv().await {
+ Some(Connect(stream)) => {
+ self.connect(stream).await;
+ }
+ Some(Disconnect(device_handle)) => self.disconnect(device_handle),
+ Some(Ranging(device_handle, session_id)) => {
+ self.ranging(device_handle, session_id).await;
+ }
+ Some(Command(device_handle, cmd)) => self.command(device_handle, cmd).await,
+ Some(SetPosition(mac_address, position, pica_cmd_rsp_tx)) => {
+ self.set_position(mac_address, position, pica_cmd_rsp_tx)
+ }
+ Some(CreateAnchor(mac_address, position, pica_cmd_rsp_tx)) => {
+ self.create_anchor(mac_address, position, pica_cmd_rsp_tx)
+ }
+ Some(DestroyAnchor(mac_address, pica_cmd_rsp_tx)) => {
+ self.destroy_anchor(mac_address, pica_cmd_rsp_tx)
+ }
+ Some(GetState(state_tx)) => self.get_state(state_tx),
+ Some(InitUciDevice(mac_address, position, pica_cmd_rsp_tx)) => {
+ self.init_uci_device(mac_address, position, pica_cmd_rsp_tx);
+ }
+ None => (),
+ };
+ }
+ }
+
+ // TODO: Assign a reserved range of mac addresses for UCI devices
+ // to protect against conflicts with user defined Anchor addresses
+ // b/246000641
+ fn init_uci_device(
+ &mut self,
+ mac_address: MacAddress,
+ position: Position,
+ pica_cmd_rsp_tx: oneshot::Sender<PicaCommandStatus>,
+ ) {
+ println!("[_] Init device");
+ println!(" mac_address: {}", mac_address);
+ println!(" position={:?}", position);
+
+ let status = self
+ .get_device_mut_by_mac(mac_address)
+ .ok_or(PicaCommandError::DeviceNotFound(mac_address))
+ .map(|uci_device| {
+ uci_device.mac_address = mac_address;
+ uci_device.position = position;
+ });
+
+ pica_cmd_rsp_tx.send(status).unwrap_or_else(|err| {
+ println!("Failed to send init-uci-device command response: {:?}", err)
+ });
+ }
+
+ fn set_position(
+ &mut self,
+ mac_address: MacAddress,
+ position: Position,
+ pica_cmd_rsp_tx: oneshot::Sender<PicaCommandStatus>,
+ ) {
+ let mut status = if let Some(uci_device) = self.get_device_mut_by_mac(mac_address) {
+ uci_device.position = position;
+ Ok(())
+ } else if let Some(anchor) = self.anchors.get_mut(&mac_address) {
+ anchor.position = position;
+ Ok(())
+ } else {
+ Err(PicaCommandError::DeviceNotFound(mac_address))
+ };
+
+ if status.is_ok() {
+ status = self.update_position(mac_address, position)
+ }
+
+ pica_cmd_rsp_tx.send(status).unwrap_or_else(|err| {
+ println!("Failed to send set-position command response: {:?}", err)
+ });
+ }
+
+ fn update_position(
+ &self,
+ mac_address: MacAddress,
+ position: Position,
+ ) -> Result<(), PicaCommandError> {
+ let category = match self.get_category(&mac_address) {
+ Some(category) => category,
+ None => {
+ return Err(PicaCommandError::DeviceNotFound(mac_address));
+ }
+ };
+ self.send_event(PicaEvent::DeviceUpdated {
+ device: web::Device::new(category, mac_address, position),
+ });
+
+ let devices = self.devices.values().map(|d| (d.mac_address, d.position));
+ let anchors = self.anchors.values().map(|b| (b.mac_address, b.position));
+
+ let update_neighbors = |category, device_mac_address, device_position| {
+ if mac_address != device_mac_address {
+ let local = position.compute_range_azimuth_elevation(&device_position);
+ let remote = device_position.compute_range_azimuth_elevation(&position);
+
+ assert!(local.0 == remote.0);
+
+ self.send_event(PicaEvent::NeighborUpdated {
+ source_device: web::Device::new(category, mac_address, position),
+ destination_device: web::Device::new(
+ category,
+ device_mac_address,
+ device_position,
+ ),
+ distance: local.0,
+ azimuth: local.1,
+ elevation: local.2,
+ });
+
+ self.send_event(PicaEvent::NeighborUpdated {
+ source_device: web::Device::new(category, device_mac_address, device_position),
+ destination_device: web::Device::new(category, mac_address, position),
+ distance: remote.0,
+ azimuth: remote.1,
+ elevation: remote.2,
+ });
+ }
+ };
+
+ devices.for_each(|device| update_neighbors(Category::Uci, device.0, device.1));
+ anchors.for_each(|anchor| update_neighbors(Category::Anchor, anchor.0, anchor.1));
+ Ok(())
+ }
+
+ #[allow(clippy::map_entry)]
+ fn create_anchor(
+ &mut self,
+ mac_address: MacAddress,
+ position: Position,
+ pica_cmd_rsp_tx: oneshot::Sender<PicaCommandStatus>,
+ ) {
+ println!("Create anchor: {} {}", mac_address, position);
+ let status = if self.get_category(&mac_address).is_some() {
+ Err(PicaCommandError::DeviceAlreadyExists(mac_address))
+ } else {
+ self.send_event(PicaEvent::DeviceAdded {
+ device: web::Device::new(Category::Anchor, mac_address, position),
+ });
+ assert!(self
+ .anchors
+ .insert(
+ mac_address,
+ Anchor {
+ mac_address,
+ position,
+ },
+ )
+ .is_none());
+ Ok(())
+ };
+
+ pica_cmd_rsp_tx.send(status).unwrap_or_else(|err| {
+ println!("Failed to send create-anchor command response: {:?}", err)
+ })
+ }
+
+ fn destroy_anchor(
+ &mut self,
+ mac_address: MacAddress,
+ pica_cmd_rsp_tx: oneshot::Sender<PicaCommandStatus>,
+ ) {
+ println!("[_] Destroy anchor");
+ println!(" mac_address: {}", mac_address);
+
+ let status = if self.anchors.remove(&mac_address).is_none() {
+ Err(PicaCommandError::DeviceNotFound(mac_address))
+ } else {
+ self.send_event(PicaEvent::DeviceRemoved {
+ device: web::Device::new(Category::Anchor, mac_address, Position::default()),
+ });
+ Ok(())
+ };
+ pica_cmd_rsp_tx.send(status).unwrap_or_else(|err| {
+ println!("Failed to send destroy-anchor command response: {:?}", err)
+ })
+ }
+
+ fn get_state(&self, state_tx: oneshot::Sender<Vec<web::Device>>) {
+ println!("[_] Get State");
+ let web_devices: Vec<web::Device> = self
+ .anchors
+ .iter()
+ .map(|(_, anchor)| web::Device::from(*anchor))
+ .chain(self.devices.iter().map(|(_, uci_device)| {
+ web::Device::new(Category::Uci, uci_device.mac_address, uci_device.position)
+ }))
+ .collect();
+
+ state_tx.send(web_devices).unwrap();
+ }
+}
diff --git a/src/mac_address.rs b/src/mac_address.rs
new file mode 100644
index 0000000..768bb6d
--- /dev/null
+++ b/src/mac_address.rs
@@ -0,0 +1,125 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Display;
+
+use hex::FromHex;
+use thiserror::Error;
+
+const SHORT_MAC_ADDRESS_SIZE: usize = 2;
+const STRING_SHORT_MAC_ADDRESS_SIZE: usize = 2 * SHORT_MAC_ADDRESS_SIZE;
+
+const EXTEND_MAC_ADDRESS_SIZE: usize = 8;
+const STRING_EXTEND_MAC_ADDRESS_SIZE: usize = 2 * EXTEND_MAC_ADDRESS_SIZE;
+
+#[derive(Error, Debug)]
+pub enum Error {
+ #[error("MacAddress has the wrong format: 0")]
+ MacAddressWrongFormat(String),
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum MacAddress {
+ Short([u8; SHORT_MAC_ADDRESS_SIZE]),
+ Extend([u8; EXTEND_MAC_ADDRESS_SIZE]),
+}
+
+impl MacAddress {
+ pub fn new(mac_address: String) -> Result<Self, Error> {
+ let mac_address = mac_address.replace(':', "");
+ let mac_address = mac_address.replace("%3A", "");
+ let uwb_mac_address = match mac_address.len() {
+ STRING_SHORT_MAC_ADDRESS_SIZE => MacAddress::Short(
+ <[u8; SHORT_MAC_ADDRESS_SIZE]>::from_hex(mac_address)
+ .map_err(|err| Error::MacAddressWrongFormat(err.to_string()))?,
+ ),
+ STRING_EXTEND_MAC_ADDRESS_SIZE => MacAddress::Extend(
+ <[u8; EXTEND_MAC_ADDRESS_SIZE]>::from_hex(mac_address)
+ .map_err(|err| Error::MacAddressWrongFormat(err.to_string()))?,
+ ),
+ _ => return Err(Error::MacAddressWrongFormat(mac_address)),
+ };
+ Ok(uwb_mac_address)
+ }
+}
+
+impl From<usize> for MacAddress {
+ fn from(device_handle: usize) -> Self {
+ MacAddress::Extend(device_handle.to_be_bytes())
+ }
+}
+
+impl Display for MacAddress {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let to_string = |addr: &[u8]| -> String {
+ let mac_address: Vec<_> = addr.iter().map(|byte| format!("{:02X}:", byte)).collect();
+ let s = mac_address
+ .iter()
+ .flat_map(|byte| byte.chars())
+ .collect::<String>();
+ s.trim_end_matches(':').into()
+ };
+ match *self {
+ MacAddress::Short(address) => write!(f, "{}", to_string(&address)),
+ MacAddress::Extend(address) => write!(f, "{}", to_string(&address)),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn valid_mac_address() {
+ let valid_mac_address = "00:11";
+ assert_eq!(
+ MacAddress::new(valid_mac_address.into()).unwrap(),
+ MacAddress::Short([0x00, 0x11])
+ );
+ let valid_mac_address = "FF:77:AA:DD:EE:BB:CC:10";
+ assert_eq!(
+ MacAddress::new(valid_mac_address.into()).unwrap(),
+ MacAddress::Extend([0xFF, 0x77, 0xAA, 0xDD, 0xEE, 0xBB, 0xCC, 0x10])
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ fn invalid_mac_address_short() {
+ let invalid_mac_address = "00:11:22";
+ MacAddress::new(invalid_mac_address.into()).unwrap();
+ }
+
+ #[test]
+ #[should_panic]
+ fn invalid_mac_address_extend() {
+ let invalid_mac_address = "00:11:22:33:44:55:66";
+ MacAddress::new(invalid_mac_address.into()).unwrap();
+ }
+
+ #[test]
+ fn display_mac_address() {
+ let extend_mac_address = "00:FF:77:AA:DD:EE:CC:45";
+ let short_mac_address = "00:FF";
+ assert_eq!(
+ format!("{}", MacAddress::new(extend_mac_address.into()).unwrap()),
+ extend_mac_address
+ );
+ assert_eq!(
+ format!("{}", MacAddress::new(short_mac_address.into()).unwrap()),
+ short_mac_address
+ );
+ assert_eq!(extend_mac_address.to_string(), extend_mac_address);
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..f789d57
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,69 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+extern crate bytes;
+extern crate num_derive;
+extern crate num_traits;
+extern crate thiserror;
+
+use pica::{web, Pica, PicaCommand};
+use std::path::PathBuf;
+
+use anyhow::Result;
+use std::net::{Ipv4Addr, SocketAddrV4};
+use structopt::StructOpt;
+use tokio::net::TcpListener;
+use tokio::sync::{broadcast, mpsc};
+use tokio::try_join;
+
+const UCI_PORT: u16 = 7000;
+
+async fn accept_incoming(tx: mpsc::Sender<PicaCommand>) -> Result<()> {
+ let uci_socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, UCI_PORT);
+ let uci_listener = TcpListener::bind(uci_socket).await?;
+ println!("Pica: Listening on: {}", UCI_PORT);
+
+ loop {
+ let (socket, addr) = uci_listener.accept().await?;
+ println!("Uwb host addr: {}", addr);
+ tx.send(PicaCommand::Connect(socket)).await?
+ }
+}
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "pica", about = "Virtual UWB subsystem")]
+struct Opts {
+ /// Output directory for storing .pcapng traces.
+ /// If provided, .pcapng traces of client connections are automatically
+ /// saved under the name `device-{handle}.pcapng`.
+ #[structopt(short, long, parse(from_os_str))]
+ pcapng_dir: Option<PathBuf>,
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+ let opts = Opts::from_args();
+ let (event_tx, _) = broadcast::channel(16);
+
+ let mut pica = Pica::new(event_tx.clone(), opts.pcapng_dir);
+ let pica_tx = pica.tx();
+
+ try_join!(
+ accept_incoming(pica_tx.clone()),
+ pica.run(),
+ web::serve(pica_tx, event_tx)
+ )?;
+
+ Ok(())
+}
diff --git a/src/pcapng.rs b/src/pcapng.rs
new file mode 100644
index 0000000..fc2d882
--- /dev/null
+++ b/src/pcapng.rs
@@ -0,0 +1,87 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(clippy::unused_io_amount)]
+
+use std::path::Path;
+use std::time::Instant;
+use tokio::io::AsyncWriteExt;
+
+pub struct File {
+ file: tokio::fs::File,
+ start_time: Instant,
+}
+
+pub enum Direction {
+ Rx,
+ Tx,
+}
+
+impl File {
+ pub async fn create<P: AsRef<Path>>(path: P) -> std::io::Result<File> {
+ let mut file = tokio::fs::File::create(path).await?;
+
+ // PCAPng files must start with a Section Header Block.
+ file.write(&u32::to_le_bytes(0x0A0D0D0A)).await?; // Block Type
+ file.write(&u32::to_le_bytes(28)).await?; // Block Total Length
+ file.write(&u32::to_le_bytes(0x1A2B3C4D)).await?; // Byte-Order Magic
+ file.write(&u16::to_le_bytes(1)).await?; // Major Version
+ file.write(&u16::to_le_bytes(0)).await?; // Minor Version
+ file.write(&u64::to_le_bytes(0xFFFFFFFFFFFFFFFF)).await?; // Section Length (not specified)
+ file.write(&u32::to_le_bytes(28)).await?; // Block Total Length
+
+ // Write the Interface Description Block used for all
+ // UCI records.
+ file.write(&u32::to_le_bytes(0x00000001)).await?; // Block Type
+ file.write(&u32::to_le_bytes(20)).await?; // Block Total Length
+ file.write(&u16::to_le_bytes(293)).await?; // LinkType
+ file.write(&u16::to_le_bytes(0)).await?; // Reserved
+ file.write(&u32::to_le_bytes(0)).await?; // SnapLen (no limit)
+ file.write(&u32::to_le_bytes(20)).await?; // Block Total Length
+
+ Ok(File {
+ file,
+ start_time: Instant::now(),
+ })
+ }
+
+ pub async fn write(&mut self, packet: &[u8], _dir: Direction) -> std::io::Result<()> {
+ let packet_data_padding: usize = 4 - packet.len() % 4;
+ let block_total_length: u32 = packet.len() as u32 + packet_data_padding as u32 + 32;
+ let timestamp = self.start_time.elapsed().as_micros();
+
+ // Wrap the packet inside an Enhanced Packet Block.
+ self.file.write(&u32::to_le_bytes(0x00000006)).await?; // Block Type
+ self.file
+ .write(&u32::to_le_bytes(block_total_length))
+ .await?;
+ self.file.write(&u32::to_le_bytes(0)).await?; // Interface ID
+ self.file
+ .write(&u32::to_le_bytes((timestamp >> 32) as u32))
+ .await?; // Timestamp (High)
+ self.file.write(&u32::to_le_bytes(timestamp as u32)).await?; // Timestamp (Low)
+ self.file
+ .write(&u32::to_le_bytes(packet.len() as u32))
+ .await?; // Captured Packet Length
+ self.file
+ .write(&u32::to_le_bytes(packet.len() as u32))
+ .await?; // Original Packet Length
+ self.file.write(packet).await?;
+ self.file.write(&vec![0; packet_data_padding]).await?;
+ self.file
+ .write(&u32::to_le_bytes(block_total_length))
+ .await?; // Block Total Length
+ Ok(())
+ }
+}
diff --git a/src/position.rs b/src/position.rs
new file mode 100644
index 0000000..b54fa9b
--- /dev/null
+++ b/src/position.rs
@@ -0,0 +1,293 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::uci_packets::PicaPosition;
+use glam::{EulerRot, Quat, Vec3};
+use serde::ser::{Serialize, SerializeStruct, Serializer};
+use std::convert::From;
+use std::default::Default;
+use std::fmt::Display;
+
+#[derive(Debug, Clone, Copy)]
+pub struct Position {
+ position: Vec3,
+ rotation: Quat,
+}
+
+impl Display for Position {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let (roll, pitch, yaw) = self.rotation.to_euler(EulerRot::ZXY);
+ write!(
+ f,
+ "Position: {}, {}, {} Rotation: {}, {}, {}",
+ self.position.x,
+ self.position.y,
+ self.position.z,
+ yaw.to_degrees().round(),
+ pitch.to_degrees().round(),
+ roll.to_degrees().round(),
+ )
+ }
+}
+
+impl Serialize for Position {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut state = serializer.serialize_struct("Position", 5)?;
+ state.serialize_field("x", &(self.position.x as i16))?;
+ state.serialize_field("y", &(self.position.y as i16))?;
+ state.serialize_field("z", &(self.position.z as i16))?;
+
+ let (roll, pitch, yaw) = self.rotation.to_euler(EulerRot::ZXY);
+
+ state.serialize_field("yaw", &(yaw.to_degrees().round() as i16))?;
+ state.serialize_field("pitch", &(pitch.to_degrees().round() as i8))?;
+ state.serialize_field("roll", &(roll.to_degrees().round() as i16))?;
+ state.end()
+ }
+}
+
+fn checked_div(num: f32, den: f32) -> Option<f32> {
+ if den == 0. {
+ None
+ } else {
+ Some(num / den)
+ }
+}
+
+fn azimuth(delta: Vec3) -> f32 {
+ checked_div(delta.x, delta.z).map_or(
+ if delta.x == 0. {
+ 0.
+ } else {
+ delta.x.signum() * std::f32::consts::FRAC_PI_2
+ },
+ f32::atan,
+ ) + if delta.z >= 0. {
+ 0.
+ } else {
+ delta.x.signum() * std::f32::consts::PI
+ }
+}
+
+fn elevation(delta: Vec3) -> f32 {
+ checked_div(delta.y, f32::sqrt(delta.x.powi(2) + delta.z.powi(2)))
+ .map_or(delta.y.signum() * std::f32::consts::FRAC_PI_2, f32::atan)
+}
+
+impl Position {
+ pub fn new(x: i16, y: i16, z: i16, yaw: i16, pitch: i8, roll: i16) -> Self {
+ Self {
+ position: Vec3::new(x as f32, y as f32, z as f32),
+ rotation: Quat::from_euler(
+ EulerRot::ZXY, // Rotation performed from right to left order
+ (roll as f32).to_radians(),
+ (pitch as f32).to_radians(),
+ (yaw as f32).to_radians(),
+ ),
+ }
+ }
+
+ pub fn compute_range_azimuth_elevation(&self, other: &Position) -> (u16, i16, i8) {
+ let delta = other.position - self.position;
+
+ let distance = delta.length();
+ let direction = self.rotation.mul_vec3(delta);
+
+ let azimuth = azimuth(direction).to_degrees().round();
+ let elevation = elevation(direction).to_degrees().round();
+
+ assert!((-180. ..=180.).contains(&azimuth));
+ assert!((-90. ..=90.).contains(&elevation));
+
+ (
+ f32::min(distance, u16::MAX as f32) as u16,
+ azimuth as i16,
+ elevation as i8,
+ )
+ }
+}
+
+impl Default for Position {
+ fn default() -> Self {
+ Self::new(0, 0, 0, 0, 0, 0)
+ }
+}
+
+impl From<&PicaPosition> for Position {
+ fn from(other: &PicaPosition) -> Self {
+ Self::new(
+ other.x as i16,
+ other.y as i16,
+ other.z as i16,
+ other.yaw as i16,
+ other.pitch as i8,
+ other.roll as i16,
+ )
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Position;
+
+ #[test]
+ fn range() {
+ let position_a = Position::new(0, 0, 0, 0, 0, 0);
+ {
+ let position_b = Position::new(10, 0, 0, 0, 0, 0);
+ let (range, _, _) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(range == 10);
+ }
+ {
+ let position_b = Position::new(-10, 0, 0, 0, 0, 0);
+ let (range, _, _) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(range == 10);
+ }
+ {
+ let position_b = Position::new(10, 10, 0, 0, 0, 0);
+ let (range, _, _) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(range == f32::sqrt(10. * 10. + 10. * 10.).round() as u16);
+ }
+ {
+ let position_b = Position::new(-10, -10, -10, 0, 0, 0);
+ let (range, _, _) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(range == f32::sqrt(10. * 10. + 10. * 10. + 10. * 10.).round() as u16);
+ }
+ }
+
+ #[test]
+ fn azimuth_without_rotation() {
+ let position_a = Position::new(0, 0, 0, 0, 0, 0);
+ {
+ let position_b = Position::new(10, 0, 10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 45);
+ assert!(elevation == 0);
+ }
+ {
+ let position_b = Position::new(-10, 0, 10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == -45);
+ assert!(elevation == 0);
+ }
+ {
+ let position_b = Position::new(10, 0, -10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 135);
+ assert!(elevation == 0);
+ }
+ {
+ let position_b = Position::new(-10, 0, -10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == -135);
+ assert!(elevation == 0);
+ }
+ }
+
+ #[test]
+ fn elevation_without_rotation() {
+ let position_a = Position::new(0, 0, 0, 0, 0, 0);
+ {
+ let position_b = Position::new(0, 10, 10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 0);
+ assert!(elevation == 45);
+ }
+ {
+ let position_b = Position::new(0, -10, 10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 0);
+ assert!(elevation == -45);
+ }
+ {
+ let position_b = Position::new(0, 10, -10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 180 || azimuth == -180);
+ assert!(elevation == 45);
+ }
+ {
+ let position_b = Position::new(0, -10, -10, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 180 || azimuth == -180);
+ assert!(elevation == -45);
+ }
+ }
+
+ #[test]
+ fn rotation_only() {
+ let position_b = Position::new(0, 0, 10, 0, 0, 0);
+ {
+ let position_a = Position::new(0, 0, 0, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 0);
+ assert!(elevation == 0);
+ }
+ {
+ let position_a = Position::new(0, 0, 0, 45, 0, 0); // <=> azimuth = -45deg
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 45);
+ assert!(elevation == 0);
+ }
+ {
+ let position_a = Position::new(0, 0, 0, 0, 45, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 0);
+ assert!(elevation == -45);
+ }
+ {
+ let position_a = Position::new(0, 0, 0, 0, 0, 45);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 0);
+ assert!(elevation == 0);
+ }
+ }
+
+ #[test]
+ fn rotation_only_complex_position() {
+ let position_b = Position::new(10, 10, 10, 0, 0, 0);
+ {
+ let position_a = Position::new(0, 0, 0, 0, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 45);
+ assert!(elevation == 35);
+ }
+ {
+ let position_a = Position::new(0, 0, 0, 90, 0, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 90 + 45);
+ assert!(elevation == 35);
+ }
+ {
+ let position_a = Position::new(0, 0, 0, 0, 90, 0);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 45);
+ assert!(elevation == -35);
+ }
+ {
+ let position_a = Position::new(0, 0, 0, 0, 0, 90);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == -45);
+ assert!(elevation == 35);
+ }
+ {
+ let position_a = Position::new(0, 0, 0, -45, 35, 42);
+ let (_, azimuth, elevation) = position_a.compute_range_azimuth_elevation(&position_b);
+ assert!(azimuth == 0);
+ assert!(elevation == 0);
+ }
+ }
+}
diff --git a/src/session.rs b/src/session.rs
new file mode 100644
index 0000000..a4258d4
--- /dev/null
+++ b/src/session.rs
@@ -0,0 +1,546 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! ## Specifications
+//! - [MAC] FiRa Consortium UWB MAC Technical Requirements
+//! - [UCI] FiRa Consortium UWB Command Interface Generic Technical specification
+
+use crate::uci_packets::AppConfigTlvType;
+use crate::uci_packets::*;
+use crate::{MacAddress, PicaCommand};
+use std::collections::HashMap;
+use std::time::Duration;
+use tokio::sync::mpsc;
+use tokio::task::JoinHandle;
+use tokio::time;
+
+use num_derive::{FromPrimitive, ToPrimitive};
+use num_traits::FromPrimitive;
+
+pub const MAX_SESSION: usize = 255;
+pub const DEFAULT_RANGING_INTERVAL: Duration = time::Duration::from_millis(200);
+pub const DEFAULT_SLOT_DURATION: u16 = 2400; // RTSU unit
+/// cf. [UCI] 8.3 Table 29
+pub const MAX_NUMBER_OF_CONTROLEES: usize = 8;
+
+#[derive(Copy, Clone, FromPrimitive, PartialEq, Eq)]
+pub enum DeviceType {
+ /// [MAC] 5.1.1 Device controlling the ranging features through Control Messages
+ Controller,
+ /// [MAC] 5.1.2 Device utilizing the ranging features set through Control Messages
+ Controlee,
+}
+
+#[derive(Copy, Clone, FromPrimitive)]
+pub enum DeviceRole {
+ /// [MAC] 5.1.3 Device initiating a ranging exchange with a ranging initiation message
+ Initiator,
+ /// [MAC] 5.1.4 Device responding to ranging initiation messages
+ Responder,
+}
+
+/// cf. [UCI] 8.4 Table 29
+#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq, Eq)]
+#[repr(u8)]
+pub enum MacAddressMode {
+ /// MAC address is 2 bytes and 2 bytes to be used in MAC header
+ AddressMode0 = 0x00,
+ /// Not Supported: MAC address is 8 bytes and 2 bytes to be used in MAC header
+ AddressMode1 = 0x01,
+ /// MAC address is 8 bytes and 8 bytes to be used in MAC header
+ AddressMode2 = 0x02,
+}
+
+/// cf. [UCI] 8.3 Table 29
+#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq, Eq)]
+#[repr(u8)]
+pub enum ChannelNumber {
+ ChannelNumber5 = 0x05,
+ ChannelNumber6 = 0x06,
+ ChannelNumber8 = 0x08,
+ ChannelNumber9 = 0x09,
+ ChannelNumber10 = 0x0a,
+ ChannelNumber12 = 0x0c,
+ ChannelNumber13 = 0x0d,
+ ChannelNumber14 = 0x0e,
+}
+
+const DEFAULT_CHANNEL_NUMBER: ChannelNumber = ChannelNumber::ChannelNumber9;
+
+/// cf. [UCI] 8.3 Table 29
+#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)]
+#[repr(u8)]
+enum MultiNodeMode {
+ /// Single device to single device
+ Unicast = 0x00,
+ OneToMany = 0x01,
+ ManyToMany = 0x02,
+}
+
+/// cf. [UCI] 7.7
+#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)]
+#[repr(u8)]
+enum UpdateMulticastListAction {
+ Add = 0x00,
+ Delete = 0x01,
+}
+
+/// cf. [UCI] 8.3 Table 29
+#[derive(Clone)]
+pub struct AppConfig {
+ /// Copy of the valid App Configuration parameters provided by host
+ raw: HashMap<AppConfigTlvType, Vec<u8>>,
+
+ device_type: DeviceType,
+ _device_role: DeviceRole,
+ mac_address_mode: MacAddressMode,
+ device_mac_address: MacAddress,
+ number_of_controlees: usize,
+ dst_mac_addresses: Vec<MacAddress>,
+ ranging_interval: time::Duration,
+ slot_duration: u16,
+ channel_number: ChannelNumber,
+ multi_node_mode: MultiNodeMode,
+}
+
+impl Default for AppConfig {
+ fn default() -> Self {
+ AppConfig {
+ raw: HashMap::new(),
+ mac_address_mode: MacAddressMode::AddressMode0,
+ _device_role: DeviceRole::Responder,
+ device_type: DeviceType::Controlee,
+ ranging_interval: DEFAULT_RANGING_INTERVAL,
+ slot_duration: DEFAULT_SLOT_DURATION,
+ channel_number: DEFAULT_CHANNEL_NUMBER,
+ device_mac_address: MacAddress::Short([0x00, 0x00]),
+ number_of_controlees: 0,
+ dst_mac_addresses: Vec::new(),
+ multi_node_mode: MultiNodeMode::Unicast,
+ }
+ }
+}
+
+fn app_config_has_mandatory_parameters(configs: &[AppConfigParameter]) -> bool {
+ const MANDATORY_PARAMETERS: [AppConfigTlvType; 6] = [
+ AppConfigTlvType::DeviceRole,
+ AppConfigTlvType::MultiNodeMode,
+ AppConfigTlvType::NoOfControlee,
+ AppConfigTlvType::DeviceMacAddress,
+ AppConfigTlvType::DstMacAddress,
+ AppConfigTlvType::DeviceType,
+ ];
+
+ MANDATORY_PARAMETERS.iter().all(|&mparam| {
+ configs
+ .iter()
+ .any(|param| mparam == AppConfigTlvType::from_u8(param.id).unwrap())
+ })
+}
+
+impl AppConfig {
+ fn set_config(
+ &mut self,
+ id: AppConfigTlvType,
+ value: &[u8],
+ ) -> std::result::Result<(), StatusCode> {
+ match id {
+ AppConfigTlvType::MacAddressMode => {
+ let mode = MacAddressMode::from_u8(value[0]).unwrap();
+ if mode == MacAddressMode::AddressMode1 {
+ return Err(StatusCode::UciStatusInvalidParam);
+ }
+ self.mac_address_mode = mode;
+ }
+ AppConfigTlvType::RangingInterval => {
+ let interval = u32::from_le_bytes(value[..].try_into().unwrap());
+ self.ranging_interval = time::Duration::from_millis(interval as u64)
+ }
+ AppConfigTlvType::SlotDuration => {
+ self.slot_duration = u16::from_le_bytes(value[..].try_into().unwrap())
+ }
+ AppConfigTlvType::ChannelNumber => {
+ self.channel_number = ChannelNumber::from_u8(value[0]).unwrap()
+ }
+ AppConfigTlvType::DeviceMacAddress => {
+ self.device_mac_address = match self.mac_address_mode {
+ MacAddressMode::AddressMode0 => {
+ MacAddress::Short(value[..].try_into().unwrap())
+ }
+ MacAddressMode::AddressMode2 => {
+ MacAddress::Extend(value[..].try_into().unwrap())
+ }
+ _ => panic!("Unexpected MAC Address Mode"),
+ };
+ }
+ AppConfigTlvType::NoOfControlee => {
+ assert!(value[0] as usize <= MAX_NUMBER_OF_CONTROLEES);
+ self.number_of_controlees = value[0] as usize;
+ }
+ AppConfigTlvType::DstMacAddress => {
+ let mac_address_size = match self.mac_address_mode {
+ MacAddressMode::AddressMode0 => 2,
+ MacAddressMode::AddressMode2 => 8,
+ _ => panic!("Unexpected MAC Address Mode"),
+ };
+ if value.len() % mac_address_size != 0
+ || (value.len() / mac_address_size) != self.number_of_controlees
+ {
+ return Err(StatusCode::UciStatusInvalidParam);
+ }
+ self.dst_mac_addresses = value
+ .chunks(mac_address_size)
+ .map(|c| match self.mac_address_mode {
+ MacAddressMode::AddressMode0 => MacAddress::Short(c.try_into().unwrap()),
+ MacAddressMode::AddressMode2 => MacAddress::Extend(c.try_into().unwrap()),
+ _ => panic!("Unexpected MAC Address Mode"),
+ })
+ .collect();
+ assert_eq!(self.dst_mac_addresses.len(), self.number_of_controlees);
+ }
+ AppConfigTlvType::MultiNodeMode => {
+ self.multi_node_mode = MultiNodeMode::from_u8(value[0]).unwrap()
+ }
+ id => {
+ println!("Ignored AppConfig parameter {}", id);
+ return Err(StatusCode::UciStatusInvalidParam);
+ }
+ };
+
+ self.raw.insert(id, value.to_vec());
+
+ Ok(())
+ }
+
+ fn get_config(&self, id: AppConfigTlvType) -> Option<Vec<u8>> {
+ self.raw.get(&id).cloned()
+ }
+
+ fn extend(&mut self, configs: &[AppConfigParameter]) -> Vec<AppConfigStatus> {
+ if !app_config_has_mandatory_parameters(configs) {
+ // TODO: What shall we do in this situation?
+ }
+
+ configs
+ .iter()
+ .fold(Vec::new(), |mut invalid_parameters, config| {
+ match AppConfigTlvType::from_u8(config.id) {
+ Some(id) => match self.set_config(id, &config.value) {
+ Ok(_) => (),
+ Err(status) => invalid_parameters.push(AppConfigStatus {
+ config_id: config.id,
+ status,
+ }),
+ },
+ None => invalid_parameters.push(AppConfigStatus {
+ config_id: config.id,
+ status: StatusCode::UciStatusInvalidParam,
+ }),
+ };
+ invalid_parameters
+ })
+ }
+}
+
+pub struct Session {
+ /// cf. [UCI] 7.1
+ state: SessionState,
+ /// cf. [UCI] 7.2 Table 13: 4 octets unique random number generated by application
+ id: u32,
+ device_handle: usize,
+
+ session_type: SessionType,
+ pub sequence_number: u32,
+ app_config: AppConfig,
+ ranging_task: Option<JoinHandle<()>>,
+ tx: mpsc::Sender<UciPacketPacket>,
+ pica_tx: mpsc::Sender<PicaCommand>,
+}
+
+impl Session {
+ pub fn new(
+ id: u32,
+ session_type: SessionType,
+ device_handle: usize,
+ tx: mpsc::Sender<UciPacketPacket>,
+ pica_tx: mpsc::Sender<PicaCommand>,
+ ) -> Self {
+ Self {
+ state: SessionState::SessionStateDeinit,
+ id,
+ device_handle,
+ session_type,
+ sequence_number: 0,
+ app_config: AppConfig::default(),
+ ranging_task: None,
+ tx,
+ pica_tx,
+ }
+ }
+
+ fn set_state(&mut self, session_state: SessionState) {
+ // No transition: ignore
+ if session_state == self.state {
+ return;
+ }
+
+ // Send status notification
+ self.state = session_state;
+ let tx = self.tx.clone();
+ let session_id = self.id;
+ tokio::spawn(async move {
+ tx.send(
+ SessionStatusNtfBuilder {
+ session_id,
+ session_state,
+ reason_code: ReasonCode::StateChangeWithSessionManagementCommands,
+ }
+ .build()
+ .into(),
+ )
+ .await
+ .unwrap()
+ });
+ }
+
+ pub fn get_dst_mac_addresses(&self) -> &Vec<MacAddress> {
+ &self.app_config.dst_mac_addresses
+ }
+
+ pub fn init(&mut self) {
+ self.set_state(SessionState::SessionStateInit);
+ }
+
+ fn command_set_app_config(
+ &mut self,
+ cmd: SessionSetAppConfigCmdPacket,
+ ) -> SessionSetAppConfigRspPacket {
+ // TODO properly handle these asserts
+ println!(
+ "[{}:0x{:x}] Session Set App Config",
+ self.device_handle, self.id
+ );
+ assert_eq!(self.id, cmd.get_session_id());
+ assert_eq!(self.session_type, SessionType::FiraRangingSession);
+
+ let (status, invalid_parameters) = if self.state != SessionState::SessionStateInit {
+ (StatusCode::UciStatusRejected, Vec::new())
+ } else {
+ let mut app_config = self.app_config.clone();
+ let invalid_parameters = app_config.extend(cmd.get_parameters());
+ if invalid_parameters.is_empty() {
+ self.app_config = app_config;
+ self.set_state(SessionState::SessionStateIdle);
+ (StatusCode::UciStatusOk, invalid_parameters)
+ } else {
+ (StatusCode::UciStatusInvalidParam, invalid_parameters)
+ }
+ };
+
+ SessionSetAppConfigRspBuilder {
+ status,
+ parameters: invalid_parameters,
+ }
+ .build()
+ }
+
+ fn command_get_app_config(
+ &self,
+ cmd: SessionGetAppConfigCmdPacket,
+ ) -> SessionGetAppConfigRspPacket {
+ println!(
+ "[{}:0x{:x}] Session Get App Config",
+ self.device_handle, self.id
+ );
+ assert_eq!(self.id, cmd.get_session_id());
+
+ let (status, valid_parameters) = {
+ let (valid_parameters, invalid_parameters) = cmd.get_parameters().iter().fold(
+ (Vec::new(), Vec::new()),
+ |(mut valid_parameters, mut invalid_parameters), config_id| {
+ match AppConfigTlvType::from_u8(*config_id)
+ .and_then(|id| self.app_config.get_config(id))
+ {
+ Some(value) => valid_parameters.push(AppConfigParameter {
+ id: *config_id,
+ value,
+ }),
+ None => invalid_parameters.push(AppConfigParameter {
+ id: *config_id,
+ value: Vec::new(),
+ }),
+ }
+ (valid_parameters, invalid_parameters)
+ },
+ );
+ if invalid_parameters.is_empty() {
+ (StatusCode::UciStatusOk, valid_parameters)
+ } else {
+ (StatusCode::UciStatusFailed, Vec::new())
+ }
+ };
+ SessionGetAppConfigRspBuilder {
+ status,
+ parameters: valid_parameters,
+ }
+ .build()
+ }
+
+ fn command_get_state(&self, cmd: SessionGetStateCmdPacket) -> SessionGetStateRspPacket {
+ println!("[{}:0x{:x}] Session Get State", self.device_handle, self.id);
+ assert_eq!(self.id, cmd.get_session_id());
+ SessionGetStateRspBuilder {
+ status: StatusCode::UciStatusOk,
+ session_state: self.state,
+ }
+ .build()
+ }
+
+ fn command_update_controller_multicast_list(
+ &mut self,
+ cmd: SessionUpdateControllerMulticastListCmdPacket,
+ ) -> SessionUpdateControllerMulticastListRspPacket {
+ println!(
+ "[{}:0x{:x}] Session Update Controller Multicast List",
+ self.device_handle, self.id
+ );
+ assert_eq!(self.id, cmd.get_session_id());
+ let status = {
+ if (self.state != SessionState::SessionStateActive
+ && self.state != SessionState::SessionStateIdle)
+ || self.app_config.device_type != DeviceType::Controller
+ || (self.app_config.multi_node_mode != MultiNodeMode::OneToMany
+ && self.app_config.multi_node_mode != MultiNodeMode::ManyToMany)
+ {
+ StatusCode::UciStatusRejected
+ } else {
+ let action = UpdateMulticastListAction::from_u8(cmd.get_action()).unwrap();
+ let controlees = cmd.get_controlees();
+
+ if action == UpdateMulticastListAction::Add
+ && (controlees.len() + self.app_config.number_of_controlees)
+ > MAX_NUMBER_OF_CONTROLEES
+ {
+ StatusCode::UciStatusMulticastListFull
+ } else {
+ // TODO properly Add/Remove the controlees and send notif (?)
+ StatusCode::UciStatusOk
+ }
+ }
+ };
+ SessionUpdateControllerMulticastListRspBuilder { status }.build()
+ }
+
+ fn command_range_start(&mut self, cmd: RangeStartCmdPacket) -> RangeStartRspPacket {
+ println!("[{}:0x{:x}] Range Start", self.device_handle, self.id);
+ assert_eq!(self.id, cmd.get_session_id());
+
+ let status = if self.state != SessionState::SessionStateIdle {
+ StatusCode::UciStatusSessionNotConfigured
+ } else {
+ assert!(self.ranging_task.is_none());
+ assert_eq!(self.state, SessionState::SessionStateIdle);
+
+ let session_id = self.id;
+ let ranging_interval = self.app_config.ranging_interval;
+ let device_handle = self.device_handle;
+ let tx = self.pica_tx.clone();
+ self.ranging_task = Some(tokio::spawn(async move {
+ loop {
+ time::sleep(ranging_interval).await;
+ tx.send(PicaCommand::Ranging(device_handle, session_id))
+ .await
+ .unwrap();
+ }
+ }));
+ self.set_state(SessionState::SessionStateActive);
+ StatusCode::UciStatusOk
+ };
+ RangeStartRspBuilder { status }.build()
+ }
+
+ fn stop_ranging_task(&mut self) {
+ if let Some(handle) = &self.ranging_task {
+ handle.abort();
+ self.ranging_task = None;
+ }
+ }
+ fn command_range_stop(&mut self, cmd: RangeStopCmdPacket) -> RangeStopRspPacket {
+ println!("[{}:0x{:x}] Range Stop", self.device_handle, self.id);
+ assert_eq!(self.id, cmd.get_session_id());
+
+ let status = if self.state != SessionState::SessionStateActive {
+ StatusCode::UciStatusSessionActive
+ } else {
+ self.stop_ranging_task();
+ self.set_state(SessionState::SessionStateIdle);
+ StatusCode::UciStatusOk
+ };
+ RangeStopRspBuilder { status }.build()
+ }
+
+ fn command_get_ranging_count(
+ &self,
+ cmd: RangeGetRangingCountCmdPacket,
+ ) -> RangeGetRangingCountRspPacket {
+ println!(
+ "[{}:0x{:x}] Range Get Ranging Count",
+ self.device_handle, self.id
+ );
+ assert_eq!(self.id, cmd.get_session_id());
+
+ RangeGetRangingCountRspBuilder {
+ status: StatusCode::UciStatusOk,
+ count: self.sequence_number,
+ }
+ .build()
+ }
+
+ pub fn session_command(&mut self, cmd: SessionCommandPacket) -> SessionResponsePacket {
+ match cmd.specialize() {
+ SessionCommandChild::SessionSetAppConfigCmd(cmd) => {
+ self.command_set_app_config(cmd).into()
+ }
+ SessionCommandChild::SessionGetAppConfigCmd(cmd) => {
+ self.command_get_app_config(cmd).into()
+ }
+ SessionCommandChild::SessionGetStateCmd(cmd) => self.command_get_state(cmd).into(),
+ SessionCommandChild::SessionUpdateControllerMulticastListCmd(cmd) => {
+ self.command_update_controller_multicast_list(cmd).into()
+ }
+ _ => panic!("Unsupported session command"),
+ }
+ }
+
+ pub fn ranging_command(&mut self, cmd: RangingCommandPacket) -> RangingResponsePacket {
+ match cmd.specialize() {
+ RangingCommandChild::RangeStartCmd(cmd) => self.command_range_start(cmd).into(),
+ RangingCommandChild::RangeStopCmd(cmd) => self.command_range_stop(cmd).into(),
+ RangingCommandChild::RangeGetRangingCountCmd(cmd) => {
+ self.command_get_ranging_count(cmd).into()
+ }
+ _ => panic!("Unsupported ranging command"),
+ }
+ }
+}
+
+impl Drop for Session {
+ fn drop(&mut self) {
+ // Make sure to abort the ranging task when dropping the session,
+ // the default behaviour when dropping a task handle is to detach
+ // the task, which is undesirable.
+ self.stop_ranging_task();
+ self.set_state(SessionState::SessionStateDeinit);
+ }
+}
diff --git a/src/uci_packets.pdl b/src/uci_packets.pdl
new file mode 100644
index 0000000..e2271f4
--- /dev/null
+++ b/src/uci_packets.pdl
@@ -0,0 +1,871 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+little_endian_packets
+
+enum PacketBoundaryFlag : 1 {
+ COMPLETE = 0x00,
+ NOT_COMPLETE = 0x01,
+}
+
+enum GroupId : 4 {
+ CORE = 0x00,
+ SESSION_CONFIG = 0x01,
+ RANGING_SESSION_CONTROL = 0x02,
+ DATA_CONTROL = 0x03,
+ TEST = 0x0d,
+ VENDOR_RESERVED_9 = 0x09,
+ VENDOR_RESERVED_A = 0x0a,
+ VENDOR_RESERVED_B = 0x0b,
+ VENDOR_ANDROID = 0x0c,
+ VENDOR_RESERVED_E = 0x0e,
+ VENDOR_RESERVED_F = 0x0f,
+}
+
+enum CoreOpCode : 6 {
+ CORE_DEVICE_RESET = 0x00,
+ CORE_DEVICE_STATUS_NTF = 0x01,
+ CORE_GET_DEVICE_INFO = 0x02,
+ CORE_GET_CAPS_INFO = 0x03,
+ CORE_SET_CONFIG = 0x04,
+ CORE_GET_CONFIG = 0x05,
+ CORE_DEVICE_SUSPEND = 0x06,
+ CORE_GENERIC_ERROR_NTF = 0x07,
+}
+
+enum SessionOpCode : 6 {
+ SESSION_INIT = 0x00,
+ SESSION_DEINIT = 0x01,
+ SESSION_STATUS_NTF = 0x02,
+ SESSION_SET_APP_CONFIG = 0x03,
+ SESSION_GET_APP_CONFIG = 0x04,
+ SESSION_GET_COUNT = 0x05,
+ SESSION_GET_STATE = 0x06,
+ SESSION_UPDATE_CONTROLLER_MULTICAST_LIST = 0x07,
+}
+
+enum RangeOpCode : 6 {
+ RANGE_START = 0x00,
+ RANGE_STOP = 0x01,
+ RANGE_INTERVAL_UPDATE_REQ = 0x02,
+ RANGE_GET_RANGING_COUNT = 0x03,
+}
+
+enum AppDataOpCode : 6 {
+ APP_DATA_TX = 0x00,
+ APP_DATA_RX = 0x01,
+}
+
+enum PicaOpCode : 6 {
+ PICA_INIT_DEVICE = 0x00,
+ PICA_SET_DEVICE_POSITION = 0x01,
+ PICA_CREATE_BEACON = 0x02,
+ PICA_SET_BEACON_POSITION = 0x03,
+ PICA_DESTROY_BEACON = 0x04,
+}
+
+// Android vendor commands
+enum AndroidOpCode : 6 {
+ ANDROID_GET_POWER_STATS = 0x0,
+ ANDROID_SET_COUNTRY_CODE = 0x1,
+}
+
+enum StatusCode : 8 {
+ // Generic Status Codes
+ UCI_STATUS_OK = 0x00,
+ UCI_STATUS_REJECTED = 0x01,
+ UCI_STATUS_FAILED = 0x02,
+ UCI_STATUS_SYNTAX_ERROR = 0x03,
+ UCI_STATUS_INVALID_PARAM = 0x04,
+ UCI_STATUS_INVALID_RANGE = 0x05,
+ UCI_STATUS_INVALID_MSG_SIZE = 0x06,
+ UCI_STATUS_UNKNOWN_GID = 0x07,
+ UCI_STATUS_UNKNOWN_OID = 0x08,
+ UCI_STATUS_READ_ONLY = 0x09,
+ UCI_STATUS_COMMAND_RETRY = 0x0A,
+
+ // UWB Session Specific Status Codes
+ UCI_STATUS_SESSION_NOT_EXIST = 0x11,
+ UCI_STATUS_SESSION_DUPLICATE = 0x12,
+ UCI_STATUS_SESSION_ACTIVE = 0x13,
+ UCI_STATUS_MAX_SESSIONS_EXCEEDED = 0x14,
+ UCI_STATUS_SESSION_NOT_CONFIGURED = 0x15,
+ UCI_STATUS_ACTIVE_SESSION_ONGOING = 0x16,
+ UCI_STATUS_MULTICAST_LIST_FULL = 0x17,
+ UCI_STATUS_ADDRESS_NOT_FOUND = 0x18,
+ UCI_STATUS_ADDRESS_ALREADY_PRESENT = 0x19,
+
+ // UWB Ranging Session Specific Status Codes
+ UCI_STATUS_RANGING_TX_FAILED = 0x20,
+ UCI_STATUS_RANGING_RX_TIMEOUT = 0x21,
+ UCI_STATUS_RANGING_RX_PHY_DEC_FAILED = 0x22,
+ UCI_STATUS_RANGING_RX_PHY_TOA_FAILED = 0x23,
+ UCI_STATUS_RANGING_RX_PHY_STS_FAILED = 0x24,
+ UCI_STATUS_RANGING_RX_MAC_DEC_FAILED = 0x25,
+ UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED = 0x26,
+ UCI_STATUS_RANGING_RX_MAC_IE_MISSING = 0x27,
+
+ // UWB Data Session Specific Status Codes
+ UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED = 0x30,
+ UCI_STATUS_DATA_RX_CRC_ERROR = 0x31,
+
+ // Vendor Specific Status Codes
+ UCI_STATUS_ERROR_CCC_SE_BUSY = 0x50,
+ UCI_STATUS_ERROR_CCC_LIFECYCLE = 0x51,
+}
+
+enum ResetConfig : 8 {
+ UWBS_RESET = 0x00,
+}
+
+enum DeviceConfigId : 8 {
+ DEVICE_STATE = 0x00,
+ LOW_POWER_MODE = 0x01,
+}
+
+enum AppConfigTlvType : 8 {
+ DEVICE_TYPE = 0x00,
+ RANGING_ROUND_USAGE = 0x01,
+ STS_CONFIG = 0x02,
+ MULTI_NODE_MODE = 0x03,
+ CHANNEL_NUMBER = 0x04,
+ NO_OF_CONTROLEE = 0x05,
+ DEVICE_MAC_ADDRESS = 0x06,
+ DST_MAC_ADDRESS = 0x07,
+ SLOT_DURATION = 0x08,
+ RANGING_INTERVAL = 0x09,
+ STS_INDEX = 0x0A,
+ MAC_FCS_TYPE = 0x0B,
+ RANGING_ROUND_CONTROL = 0x0C,
+ AOA_RESULT_REQ = 0x0D,
+ RNG_DATA_NTF = 0x0E,
+ RNG_DATA_NTF_PROXIMITY_NEAR = 0x0F,
+ RNG_DATA_NTF_PROXIMITY_FAR = 0x10,
+ DEVICE_ROLE = 0x11,
+ RFRAME_CONFIG = 0x12,
+ PREAMBLE_CODE_INDEX = 0x14,
+ SFD_ID = 0x15,
+ PSDU_DATA_RATE = 0x16,
+ PREAMBLE_DURATION = 0x17,
+ RANGING_TIME_STRUCT = 0x1A,
+ SLOTS_PER_RR = 0x1B,
+ TX_ADAPTIVE_PAYLOAD_POWER = 0x1C,
+ RESPONDER_SLOT_INDEX = 0x1E,
+ PRF_MODE = 0x1F,
+ SCHEDULED_MODE = 0x22,
+ KEY_ROTATION = 0x23,
+ KEY_ROTATION_RATE = 0x24,
+ SESSION_PRIORITY = 0x25,
+ MAC_ADDRESS_MODE = 0x26,
+ VENDOR_ID = 0x27,
+ STATIC_STS_IV = 0x28,
+ NUMBER_OF_STS_SEGMENTS = 0x29,
+ MAX_RR_RETRY = 0x2A,
+ UWB_INITIATION_TIME = 0x2B,
+ HOPPING_MODE = 0x2C,
+ BLOCK_STRIDE_LENGTH = 0x2D,
+ RESULT_REPORT_CONFIG = 0x2E,
+ IN_BAND_TERMINATION_ATTEMPT_COUNT = 0x2F,
+ SUB_SESSION_ID = 0x30,
+ BPRF_PHR_DATA_RATE = 0x31,
+ MAX_NUMBER_OF_MEASUREMENTS = 0x32,
+ STS_LENGTH = 0x35,
+
+ // CCC specific
+ CCC_HOP_MODE_KEY = 0xA0,
+ CCC_UWB_TIME0 = 0xA1,
+ CCC_RANGING_PROTOCOL_VER = 0xA3,
+ CCC_UWB_CONFIG_ID = 0xA4,
+ CCC_PULSESHAPE_COMBO = 0xA5,
+ CCC_URSK_TTL = 0xA6,
+
+ // Interleaving ratio if AOA_RESULT_REQ is set to 0xF0.
+ NB_OF_RANGE_MEASUREMENTS = 0xE3,
+ NB_OF_AZIMUTH_MEASUREMENTS = 0xE4,
+ NB_OF_ELEVATION_MEASUREMENTS = 0xE5,
+}
+
+enum CapTlvType : 8 {
+ SUPPORTED_FIRA_PHY_VERSION_RANGE = 0x0,
+ SUPPORTED_FIRA_MAC_VERSION_RANGE = 0x1,
+ SUPPORTED_DEVICE_ROLES = 0x2,
+ SUPPORTED_RANGING_METHOD = 0x3,
+ SUPPORTED_STS_CONFIG = 0x4,
+ SUPPORTED_MULTI_NODE_MODES = 0x5,
+ SUPPORTED_RANGING_TIME_STRUCT = 0x6,
+ SUPPORTED_SCHEDULED_MODE = 0x7,
+ SUPPORTED_HOPPING_MODE = 0x8,
+ SUPPORTED_BLOCK_STRIDING = 0x9,
+ SUPPORTED_UWB_INITIATION_TIME = 0x0A,
+ SUPPORTED_CHANNELS = 0x0B,
+ SUPPORTED_RFRAME_CONFIG = 0x0C,
+ SUPPORTED_CC_CONSTRAINT_LENGTH = 0x0D,
+ SUPPORTED_BPRF_PARAMETER_SETS = 0x0E,
+ SUPPORTED_HPRF_PARAMETER_SETS = 0x0F,
+ SUPPORTED_AOA = 0x10,
+ SUPPORTED_EXTENDED_MAC_ADDRESS = 0x11,
+ SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xE3,
+ SUPPORTED_POWER_STATS = 0xC0,
+
+ // CCC specific
+ CCC_SUPPORTED_CHAPS_PER_SLOT = 0xA0,
+ CCC_SUPPORTED_SYNC_CODES = 0xA1,
+ CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES = 0xA2,
+ CCC_SUPPORTED_CHANNELS = 0xA3,
+ CCC_SUPPORTED_VERSIONS = 0xA4,
+ CCC_SUPPORTED_UWB_CONFIGS = 0xA5,
+ CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 0xA6,
+ CCC_SUPPORTED_RAN_MULTIPLIER = 0xA7,
+}
+
+
+// AOA result request type.
+// Values set for AOA_RESULT_REQ config ID.
+enum AoaResultReqType : 8 {
+ AOA_DISABLE = 0x0,
+ AOA_ENABLE = 0x01,
+ AOA_ENABLE_AZIMUTH = 0x02,
+ AOA_ENABLE_ELEVATION = 0x03,
+ AOA_ENABLE_INTERLEAVED = 0xF0,
+}
+
+enum DeviceState : 8 {
+ DEVICE_STATE_READY = 0x01,
+ DEVICE_STATE_ACTIVE = 0x02,
+ DEVICE_STATE_ERROR = 0xff,
+}
+
+enum SessionState : 8 {
+ SESSION_STATE_INIT = 0x00,
+ SESSION_STATE_DEINIT = 0x01,
+ SESSION_STATE_ACTIVE = 0x02,
+ SESSION_STATE_IDLE = 0x03,
+}
+
+enum ReasonCode : 8 {
+ STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS = 0x00,
+ MAX_RANGING_ROUND_RETRY_COUNT_REACHED = 0x01,
+ MAX_NUMBER_OF_MEASUREMENTS_REACHED = 0x02,
+ ERROR_SLOT_LENGTH_NOT_SUPPORTED = 0x20,
+ ERROR_INSUFFICIENT_SLOTS_PER_RR = 0x21,
+ ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED = 0x22,
+ ERROR_INVALID_RANGING_INTERVAL = 0x23,
+ ERROR_INVALID_STS_CONFIG = 0x24,
+ ERROR_INVALID_RFRAME_CONFIG = 0x25,
+}
+
+enum MulticastUpdateStatusCode : 8 {
+ STATUS_OK_MULTICAST_LIST_UPDATE = 0x00,
+ STATUS_ERROR_MULTICAST_LIST_FULL = 0x01,
+ STATUS_ERROR_KEY_FETCH_FAIL = 0x02,
+ STATUS_ERROR_SUB_SESSION_ID_NOT_FOUND = 0x03,
+}
+
+enum MacAddressIndicator : 8 {
+ SHORT_ADDRESS = 0x00,
+ EXTENDED_ADDRESS = 0x01,
+}
+
+enum SessionType: 8 {
+ FIRA_RANGING_SESSION = 0x00,
+ FIRA_DATA_TRANSFER = 0x01,
+ CCC = 0xA0,
+}
+
+enum MessageType: 3 {
+ COMMAND = 0x01,
+ RESPONSE = 0x02,
+ NOTIFICATION = 0x03,
+}
+
+packet UciPacket {
+ group_id: GroupId,
+ packet_boundary_flag: PacketBoundaryFlag,
+ message_type: MessageType,
+ opcode: 6,
+ _reserved_: 2,
+ _reserved_: 8,
+ _size_(_payload_): 8,
+ _payload_,
+}
+
+// TODO(b/202760099): Handle fragmentation of packets if the size exceed max allowed.
+packet UciCommand : UciPacket (message_type = COMMAND, packet_boundary_flag = COMPLETE) {
+ _payload_,
+}
+
+packet UciResponse : UciPacket (message_type = RESPONSE, packet_boundary_flag = COMPLETE) {
+ _payload_,
+}
+
+packet UciNotification : UciPacket (message_type = NOTIFICATION, packet_boundary_flag = COMPLETE) {
+ _payload_,
+}
+
+packet CoreCommand : UciCommand (group_id = CORE) {
+ _body_,
+}
+
+packet CoreResponse : UciResponse (group_id = CORE) {
+ _body_,
+}
+
+packet CoreNotification : UciNotification (group_id = CORE) {
+ _body_,
+}
+
+packet SessionCommand : UciCommand (group_id = SESSION_CONFIG) {
+ _body_,
+}
+
+packet SessionResponse : UciResponse (group_id = SESSION_CONFIG) {
+ _body_,
+}
+
+packet SessionNotification : UciNotification (group_id = SESSION_CONFIG) {
+ _body_,
+}
+
+packet RangingCommand : UciCommand (group_id = RANGING_SESSION_CONTROL) {
+ session_id: 32,
+ _body_,
+}
+
+packet RangingResponse : UciResponse (group_id = RANGING_SESSION_CONTROL) {
+ _body_,
+}
+
+packet RangingNotification : UciNotification (group_id = RANGING_SESSION_CONTROL) {
+ _body_,
+}
+
+packet AndroidCommand : UciCommand (group_id = VENDOR_ANDROID) {
+ _body_,
+}
+
+packet AndroidResponse : UciResponse (group_id = VENDOR_ANDROID) {
+ _body_,
+}
+
+packet AndroidNotification : UciNotification (group_id = VENDOR_ANDROID) {
+ _body_,
+}
+
+// TODO: b/202760099: Use the correspnding opcode enum instead of the raw value in the |opcode| field.
+packet DeviceResetCmd : CoreCommand (opcode = 0x0) { //CORE_DEVICE_RESET
+ reset_config: ResetConfig,
+}
+
+test DeviceResetCmd {
+ "\x20\x00\x00\x01\x00",
+}
+
+packet DeviceResetRsp : CoreResponse (opcode = 0x0) { //CORE_DEVICE_RESET
+ status: StatusCode,
+}
+
+test DeviceResetRsp {
+ "\x40\x00\x00\x01\x00",
+}
+
+packet DeviceStatusNtf : CoreNotification (opcode = 0x1) { //CORE_DEVICE_STATUS_NTF
+ device_state: DeviceState,
+}
+
+test DeviceStatusNtf {
+ "\x60\x01\x00\x01\x01",
+}
+
+packet GetDeviceInfoCmd : CoreCommand (opcode = 0x2) { //CORE_GET_DEVICE_INFO
+}
+
+test GetDeviceInfoCmd {
+ "\x20\x02\x00\x00",
+}
+
+packet GetDeviceInfoRsp : CoreResponse (opcode = 0x2) { //CORE_GET_DEVICE_INFO
+ status: StatusCode,
+ uci_version: 16,
+ mac_version: 16,
+ phy_version: 16,
+ uci_test_version: 16,
+ _count_(vendor_spec_info): 8,
+ vendor_spec_info: 8[],
+}
+
+test GetDeviceInfoRsp {
+ "\x40\x02\x00\x0b\x01\x01\x00\x02\x00\x03\x00\x04\x00\x01\x0a",
+}
+
+packet GetCapsInfoCmd : CoreCommand (opcode = 0x3) { //CORE_GET_CAPS_INFO
+}
+
+test GetCapsInfoCmd {
+ "\x20\x03\x00\x00",
+}
+
+struct CapTlv {
+ t: CapTlvType,
+ _count_(v): 8,
+ v: 8[],
+}
+
+
+packet GetCapsInfoRsp : CoreResponse (opcode = 0x3) { //CORE_GET_CAPS_INFO
+ status: StatusCode,
+ _count_(tlvs): 8,
+ tlvs: CapTlv[],
+}
+
+test GetCapsInfoRsp {
+ "\x40\x03\x00\x05\x00\x01\x00\x01\x01",
+}
+
+struct DeviceParameter {
+ id: 8,
+ _count_(value): 8,
+ value: 8[],
+}
+
+packet SetConfigCmd : CoreCommand (opcode = 0x4) { //CORE_SET_CONFIG
+ _count_(parameters): 8,
+ parameters: DeviceParameter[],
+}
+
+test SetConfigCmd {
+ "\x20\x04\x00\x03\x01\x01\x00",
+}
+
+struct DeviceConfigStatus {
+ parameter_id: 8,
+ status: StatusCode,
+}
+
+packet SetConfigRsp : CoreResponse (opcode = 0x4) { //CORE_SET_CONFIG
+ status: StatusCode,
+ _count_(parameters): 8,
+ parameters: DeviceConfigStatus[],
+}
+
+test SetConfigRsp {
+ "\x40\x04\x00\x04\x01\x01\x01\x01",
+}
+
+packet GetConfigCmd : CoreCommand (opcode = 0x5) { //CORE_GET_CONFIG
+ _count_(parameter_ids): 8,
+ parameter_ids: 8[],
+}
+
+test GetConfigCmd {
+ "\x20\x05\x00\x02\x01\x01",
+}
+
+packet GetConfigRsp : CoreResponse (opcode = 0x5) { //CORE_GET_CONFIG
+ status: StatusCode,
+ _count_(parameters): 8,
+ parameters: DeviceParameter[]
+}
+
+test GetConfigRsp {
+ "\x40\x05\x00\x05\x01\x01\x00\x01\x01",
+}
+
+packet GenericError : CoreNotification (opcode = 0x7) { //CORE_GENERIC_ERROR_NTF
+ status: StatusCode,
+}
+
+test GenericError {
+ "\x60\x07\x00\x01\x01",
+}
+
+packet SessionInitCmd : SessionCommand (opcode = 0x0) { //SESSION_INIT
+ session_id: 32,
+ session_type: SessionType,
+}
+
+test SessionInitCmd {
+ "\x22\x00\x00\x05\x01\x02\x03\x04\x01",
+}
+
+packet SessionInitRsp : SessionResponse (opcode = 0x0) { //SESSION_INIT
+ status: StatusCode,
+}
+
+test SessionInitRsp {
+ "\x41\x00\x00\x01\x11",
+}
+
+packet SessionDeinitCmd : SessionCommand (opcode = 0x1) { //SESSION_DEINIT
+ session_id: 32,
+}
+
+test SessionDeinitCmd {
+ "\x21\x01\x00\x04\x01\x02\x03\x04",
+}
+
+packet SessionDeinitRsp : SessionResponse (opcode = 0x1) { //SESSION_DEINIT
+ status: StatusCode,
+}
+
+test SessionDeinitRsp {
+ "\x41\x01\x00\x01\x00",
+}
+
+packet SessionStatusNtf : SessionNotification (opcode = 0x2) { //SESSION_STATUS_NTF
+ session_id: 32,
+ session_state: SessionState,
+ reason_code: ReasonCode,
+}
+
+test SessionStatusNtf {
+ "\x61\x02\x00\x06\x01\x02\x03\x04\x02\x21",
+}
+
+struct AppConfigParameter {
+ id: 8,
+ _count_(value): 8,
+ value: 8[],
+}
+
+packet SessionSetAppConfigCmd : SessionCommand (opcode = 0x3) { //SESSION_SET_APP_CONFIG
+ session_id: 32,
+ _count_(parameters): 8,
+ parameters: AppConfigParameter[]
+}
+
+test SessionSetAppConfigCmd {
+ "\x21\x03\x00\x05\x01\x02\x03\x04\x00",
+}
+
+struct AppConfigStatus {
+ config_id: 8,
+ status: StatusCode,
+}
+
+packet SessionSetAppConfigRsp : SessionResponse (opcode = 0x3) { //SESSION_SET_APP_CONFIG
+ status: StatusCode,
+ _count_(parameters): 8,
+ parameters: AppConfigStatus[],
+}
+
+test SessionSetAppConfigRsp {
+ "\x41\x03\x00\x04\x01\x01\x01\x00",
+}
+
+packet SessionGetAppConfigCmd : SessionCommand (opcode = 0x4) { //SESSION_GET_APP_CONFIG
+ session_id: 32,
+ _count_(parameters): 8,
+ parameters: 8[],
+}
+
+test SessionGetAppConfigCmd {
+ "\x21\x04\x00\x05\x01\x02\x03\x04\x00",
+}
+
+packet SessionGetAppConfigRsp : SessionResponse (opcode = 0x4) { //SESSION_GET_APP_CONFIG
+ status: StatusCode,
+ _count_(parameters): 8,
+ parameters: AppConfigParameter[],
+}
+
+test SessionGetAppConfigRsp {
+ "\x41\x04\x00\x02\x01\x00",
+}
+
+packet SessionGetCountCmd : SessionCommand (opcode = 0x5) { //SESSION_GET_COUNT
+}
+
+test SessionGetCountCmd {
+ "\x21\x05\x00\x00",
+}
+
+packet SessionGetCountRsp : SessionResponse (opcode = 0x5) { //SESSION_GET_COUNT
+ status: StatusCode,
+ session_count: 8,
+}
+
+test SessionGetCountRsp {
+ "\x41\x05\x00\x02\x00\x01",
+}
+
+packet SessionGetStateCmd : SessionCommand (opcode = 0x6) { //SESSION_GET_STATE
+ session_id: 32,
+}
+
+test SessionGetStateCmd {
+ "\x21\x06\x00\x04\x00\x01\x02\x03",
+}
+
+packet SessionGetStateRsp : SessionResponse (opcode = 0x6) { //SESSION_GET_STATE
+ status: StatusCode,
+ session_state: SessionState,
+}
+
+test SessionGetStateRsp {
+ "\x41\x06\x00\x02\x00\x01",
+}
+
+struct Controlee {
+ short_address: 16,
+ subsession_id: 32,
+}
+
+packet SessionUpdateControllerMulticastListCmd : SessionCommand (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST
+ session_id: 32,
+ action: 8,
+ _count_(controlees): 8,
+ controlees: Controlee[],
+}
+
+test SessionUpdateControllerMulticastListCmd {
+ "\x21\x07\x00\x06\x00\x01\x02\x03\x04\x00",
+}
+
+packet SessionUpdateControllerMulticastListRsp : SessionResponse (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST
+ status: StatusCode,
+}
+
+test SessionUpdateControllerMulticastListRsp {
+ "\x41\x07\x00\x01\x00",
+}
+
+struct ControleeStatus {
+ mac_address: 16,
+ subsession_id: 32,
+ status: 8,
+}
+
+packet SessionUpdateControllerMulticastListNtf : SessionNotification (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST
+ session_id: 32,
+ remaining_multicast_list_size: 8,
+ _count_(controlee_status): 8,
+ controlee_status: ControleeStatus[],
+}
+
+test SessionUpdateControllerMulticastListNtf {
+ "\x61\x07\x00\x06\x00\x01\x02\x03\x04\x00",
+}
+
+
+packet RangeStartCmd : RangingCommand (opcode = 0x0) { //RANGE_START
+}
+
+test RangeStartCmd {
+ "\x22\x00\x00\x04\x00\x01\x02\x03",
+}
+
+packet RangeStartRsp : RangingResponse (opcode = 0x0) { //RANGE_START
+ status: StatusCode,
+}
+
+test RangeStartRsp {
+ "\x42\x00\x00\x01\x00",
+}
+
+struct ShortAddressTwoWayRangingMeasurement {
+ mac_address: 16,
+ status: StatusCode,
+ nlos: 8,
+ distance: 16,
+ aoa_azimuth: 16,
+ aoa_azimuth_fom: 8,
+ aoa_elevation: 16,
+ aoa_elevation_fom: 8,
+ aoa_destination_azimuth: 16,
+ aoa_destination_azimuth_fom: 8,
+ aoa_destination_elevation: 16,
+ aoa_destination_elevation_fom: 8,
+ slot_index: 8,
+ _reserved_: 96,
+}
+
+struct ExtendedAddressTwoWayRangingMeasurement {
+ mac_address: 64,
+ status: StatusCode,
+ nlos: 8,
+ distance: 16,
+ aoa_azimuth: 16,
+ aoa_azimuth_fom: 8,
+ aoa_elevation: 16,
+ aoa_elevation_fom: 8,
+ aoa_destination_azimuth: 16,
+ aoa_destination_azimuth_fom: 8,
+ aoa_destination_elevation: 16,
+ aoa_destination_elevation_fom: 8,
+ slot_index: 8,
+ _reserved_: 48,
+}
+
+enum RangingMeasurementType : 8 {
+ ONE_WAY = 0x0,
+ TWO_WAY = 0x1,
+}
+
+packet RangeDataNtf : RangingNotification (opcode = 0x0) { //RANGE_START
+ sequence_number: 32,
+ session_id: 32,
+ rcr_indicator: 8,
+ current_ranging_interval: 32,
+ ranging_measurement_type: RangingMeasurementType,
+ _reserved_: 8,
+ mac_address_indicator: MacAddressIndicator,
+ _reserved_: 64,
+ _body_,
+}
+
+packet ShortMacTwoWayRangeDataNtf : RangeDataNtf (ranging_measurement_type = TWO_WAY, mac_address_indicator = SHORT_ADDRESS) {
+ _count_(two_way_ranging_measurements) : 8,
+ two_way_ranging_measurements : ShortAddressTwoWayRangingMeasurement[],
+}
+
+test ShortMacTwoWayRangeDataNtf {
+ "\x62\x00\x00\x19\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+}
+
+packet ExtendedMacTwoWayRangeDataNtf : RangeDataNtf (ranging_measurement_type = TWO_WAY, mac_address_indicator = EXTENDED_ADDRESS) {
+ _count_(two_way_ranging_measurements) : 8,
+ two_way_ranging_measurements : ExtendedAddressTwoWayRangingMeasurement[],
+}
+
+test ExtendedMacTwoWayRangeDataNtf {
+ "\x62\x00\x00\x19\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+}
+
+packet RangeStopCmd : RangingCommand (opcode = 0x1) { //RANGE_STOP
+}
+
+test RangeStopCmd {
+ "\x22\x01\x00\x04\x00\x02\x03\x04",
+}
+
+packet RangeStopRsp : RangingResponse (opcode = 0x1) { //RANGE_STOP
+ status: StatusCode,
+}
+
+test RangeStopRsp {
+ "\x42\x01\x00\x01\x00",
+}
+
+packet RangeGetRangingCountCmd : RangingCommand (opcode = 0x3) { //RANGE_GET_RANGING_COUNT
+}
+
+test RangeGetRangingCountCmd {
+ "\x22\x03\x00\x04\x00\x02\x03\x04",
+}
+
+packet RangeGetRangingCountRsp : RangingResponse (opcode = 0x3) { //RANGE_GET_RANGING_COUNT
+ status: StatusCode,
+ count: 32,
+}
+
+test RangeGetRangingCountRsp {
+ "\x42\x03\x00\x05\x00\x02\x03\x04\x05",
+}
+
+struct PicaPosition {
+ x: 16,
+ y: 16,
+ z: 16,
+ yaw: 16,
+ pitch: 8,
+ roll: 16,
+}
+
+packet AndroidGetPowerStatsCmd: AndroidCommand (opcode = 0x0) { //ANDROID_GET_POWER_STATS
+}
+
+test AndroidGetPowerStatsCmd {
+ "\x2e\x00\x00\x00",
+}
+
+struct PowerStats {
+ status: StatusCode,
+ idle_time_ms: 32,
+ tx_time_ms: 32,
+ rx_time_ms: 32,
+ total_wake_count:32,
+}
+
+packet AndroidGetPowerStatsRsp : AndroidResponse (opcode = 0x0) { //ANDROID_GET_POWER_STATS
+ stats: PowerStats,
+}
+
+test AndroidGetPowerStatsRsp {
+ "\x4e\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+}
+
+packet AndroidSetCountryCodeCmd: AndroidCommand (opcode = 0x1) { //ANDROID_SET_COUNTRY_CODE
+ country_code : 8[2],
+}
+
+// Set country code to US.
+test AndroidSetCountryCodeCmd {
+ "\x2e\x01\x00\x02\x55\x53",
+}
+
+packet AndroidSetCountryCodeRsp : AndroidResponse (opcode = 0x1) { //ANDROID_SET_COUNTRY_CODE
+ status: StatusCode,
+}
+
+test AndroidSetCountryCodeRsp {
+ "\x4e\x01\x00\x01\x00",
+}
+
+packet UciVendor_A_Command : UciCommand (group_id = VENDOR_RESERVED_A) {
+ _payload_,
+}
+
+packet UciVendor_B_Command : UciCommand (group_id = VENDOR_RESERVED_B) {
+ _payload_,
+}
+
+packet UciVendor_E_Command : UciCommand (group_id = VENDOR_RESERVED_E) {
+ _payload_,
+}
+
+packet UciVendor_F_Command : UciCommand (group_id = VENDOR_RESERVED_F) {
+ _payload_,
+}
+
+packet UciVendor_A_Response : UciResponse (group_id = VENDOR_RESERVED_A) {
+ _payload_,
+}
+
+packet UciVendor_B_Response : UciResponse (group_id = VENDOR_RESERVED_B) {
+ _payload_,
+}
+
+packet UciVendor_E_Response : UciResponse (group_id = VENDOR_RESERVED_E) {
+ _payload_,
+}
+
+packet UciVendor_F_Response : UciResponse (group_id = VENDOR_RESERVED_F) {
+ _payload_,
+}
+
+packet UciVendor_A_Notification : UciNotification (group_id = VENDOR_RESERVED_A) {
+ _payload_,
+}
+
+packet UciVendor_B_Notification : UciNotification (group_id = VENDOR_RESERVED_B) {
+ _payload_,
+}
+
+packet UciVendor_E_Notification : UciNotification (group_id = VENDOR_RESERVED_E) {
+ _payload_,
+}
+
+packet UciVendor_F_Notification : UciNotification (group_id = VENDOR_RESERVED_F) {
+ _payload_,
+}
diff --git a/src/uci_packets.rs b/src/uci_packets.rs
new file mode 100644
index 0000000..f66e79d
--- /dev/null
+++ b/src/uci_packets.rs
@@ -0,0 +1,15846 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// @generated rust packets from uci_packets.pdl
+
+#![allow(clippy::all)]
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(unused)]
+#![allow(missing_docs)]
+
+use bytes::{BufMut, Bytes, BytesMut};
+use num_derive::{FromPrimitive, ToPrimitive};
+use num_traits::{FromPrimitive, ToPrimitive};
+use std::convert::{TryFrom, TryInto};
+use std::fmt;
+use std::sync::Arc;
+use thiserror::Error;
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug, Error)]
+pub enum Error {
+ #[error("Packet parsing failed")]
+ InvalidPacketError,
+ #[error("{field} was {value:x}, which is not known")]
+ ConstraintOutOfBounds { field: String, value: u64 },
+ #[error("when parsing {obj}.{field} needed length of {wanted} but got {got}")]
+ InvalidLengthError {
+ obj: String,
+ field: String,
+ wanted: usize,
+ got: usize,
+ },
+ #[error("Due to size restrictions a struct could not be parsed.")]
+ ImpossibleStructError,
+ #[error("when parsing field {obj}.{field}, {value} is not a valid {type_} value")]
+ InvalidEnumValueError {
+ obj: String,
+ field: String,
+ value: u64,
+ type_: String,
+ },
+}
+
+#[derive(Debug, Error)]
+#[error("{0}")]
+pub struct TryFromError(&'static str);
+
+pub trait Packet {
+ fn to_bytes(self) -> Bytes;
+ fn to_vec(self) -> Vec<u8>;
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum PacketBoundaryFlag {
+ Complete = 0x0,
+ NotComplete = 0x1,
+}
+impl fmt::Display for PacketBoundaryFlag {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ PacketBoundaryFlag::Complete => write!(f, "{:#04X} (COMPLETE)", self.to_u8().unwrap()),
+ PacketBoundaryFlag::NotComplete => {
+ write!(f, "{:#04X} (NOT_COMPLETE)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum GroupId {
+ Core = 0x0,
+ SessionConfig = 0x1,
+ RangingSessionControl = 0x2,
+ DataControl = 0x3,
+ VendorReservedA = 0xa,
+ VendorReservedB = 0xb,
+ VendorAndroid = 0xc,
+ Test = 0xd,
+ VendorReservedE = 0xe,
+ VendorReservedF = 0xf,
+}
+impl fmt::Display for GroupId {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ GroupId::Core => write!(f, "{:#04X} (CORE)", self.to_u8().unwrap()),
+ GroupId::SessionConfig => write!(f, "{:#04X} (SESSION_CONFIG)", self.to_u8().unwrap()),
+ GroupId::RangingSessionControl => write!(
+ f,
+ "{:#04X} (RANGING_SESSION_CONTROL)",
+ self.to_u8().unwrap()
+ ),
+ GroupId::DataControl => write!(f, "{:#04X} (DATA_CONTROL)", self.to_u8().unwrap()),
+ GroupId::VendorReservedA => {
+ write!(f, "{:#04X} (VENDOR_RESERVED_A)", self.to_u8().unwrap())
+ }
+ GroupId::VendorReservedB => {
+ write!(f, "{:#04X} (VENDOR_RESERVED_B)", self.to_u8().unwrap())
+ }
+ GroupId::VendorAndroid => write!(f, "{:#04X} (VENDOR_ANDROID)", self.to_u8().unwrap()),
+ GroupId::Test => write!(f, "{:#04X} (TEST)", self.to_u8().unwrap()),
+ GroupId::VendorReservedE => {
+ write!(f, "{:#04X} (VENDOR_RESERVED_E)", self.to_u8().unwrap())
+ }
+ GroupId::VendorReservedF => {
+ write!(f, "{:#04X} (VENDOR_RESERVED_F)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum CoreOpCode {
+ CoreDeviceReset = 0x0,
+ CoreDeviceStatusNtf = 0x1,
+ CoreGetDeviceInfo = 0x2,
+ CoreGetCapsInfo = 0x3,
+ CoreSetConfig = 0x4,
+ CoreGetConfig = 0x5,
+ CoreDeviceSuspend = 0x6,
+ CoreGenericErrorNtf = 0x7,
+}
+impl fmt::Display for CoreOpCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ CoreOpCode::CoreDeviceReset => {
+ write!(f, "{:#04X} (CORE_DEVICE_RESET)", self.to_u8().unwrap())
+ }
+ CoreOpCode::CoreDeviceStatusNtf => {
+ write!(f, "{:#04X} (CORE_DEVICE_STATUS_NTF)", self.to_u8().unwrap())
+ }
+ CoreOpCode::CoreGetDeviceInfo => {
+ write!(f, "{:#04X} (CORE_GET_DEVICE_INFO)", self.to_u8().unwrap())
+ }
+ CoreOpCode::CoreGetCapsInfo => {
+ write!(f, "{:#04X} (CORE_GET_CAPS_INFO)", self.to_u8().unwrap())
+ }
+ CoreOpCode::CoreSetConfig => {
+ write!(f, "{:#04X} (CORE_SET_CONFIG)", self.to_u8().unwrap())
+ }
+ CoreOpCode::CoreGetConfig => {
+ write!(f, "{:#04X} (CORE_GET_CONFIG)", self.to_u8().unwrap())
+ }
+ CoreOpCode::CoreDeviceSuspend => {
+ write!(f, "{:#04X} (CORE_DEVICE_SUSPEND)", self.to_u8().unwrap())
+ }
+ CoreOpCode::CoreGenericErrorNtf => {
+ write!(f, "{:#04X} (CORE_GENERIC_ERROR_NTF)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum SessionOpCode {
+ SessionInit = 0x0,
+ SessionDeinit = 0x1,
+ SessionStatusNtf = 0x2,
+ SessionSetAppConfig = 0x3,
+ SessionGetAppConfig = 0x4,
+ SessionGetCount = 0x5,
+ SessionGetState = 0x6,
+ SessionUpdateControllerMulticastList = 0x7,
+}
+impl fmt::Display for SessionOpCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ SessionOpCode::SessionInit => {
+ write!(f, "{:#04X} (SESSION_INIT)", self.to_u8().unwrap())
+ }
+ SessionOpCode::SessionDeinit => {
+ write!(f, "{:#04X} (SESSION_DEINIT)", self.to_u8().unwrap())
+ }
+ SessionOpCode::SessionStatusNtf => {
+ write!(f, "{:#04X} (SESSION_STATUS_NTF)", self.to_u8().unwrap())
+ }
+ SessionOpCode::SessionSetAppConfig => {
+ write!(f, "{:#04X} (SESSION_SET_APP_CONFIG)", self.to_u8().unwrap())
+ }
+ SessionOpCode::SessionGetAppConfig => {
+ write!(f, "{:#04X} (SESSION_GET_APP_CONFIG)", self.to_u8().unwrap())
+ }
+ SessionOpCode::SessionGetCount => {
+ write!(f, "{:#04X} (SESSION_GET_COUNT)", self.to_u8().unwrap())
+ }
+ SessionOpCode::SessionGetState => {
+ write!(f, "{:#04X} (SESSION_GET_STATE)", self.to_u8().unwrap())
+ }
+ SessionOpCode::SessionUpdateControllerMulticastList => write!(
+ f,
+ "{:#04X} (SESSION_UPDATE_CONTROLLER_MULTICAST_LIST)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum RangeOpCode {
+ RangeStart = 0x0,
+ RangeStop = 0x1,
+ RangeIntervalUpdateReq = 0x2,
+ RangeGetRangingCount = 0x3,
+}
+impl fmt::Display for RangeOpCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ RangeOpCode::RangeStart => write!(f, "{:#04X} (RANGE_START)", self.to_u8().unwrap()),
+ RangeOpCode::RangeStop => write!(f, "{:#04X} (RANGE_STOP)", self.to_u8().unwrap()),
+ RangeOpCode::RangeIntervalUpdateReq => write!(
+ f,
+ "{:#04X} (RANGE_INTERVAL_UPDATE_REQ)",
+ self.to_u8().unwrap()
+ ),
+ RangeOpCode::RangeGetRangingCount => write!(
+ f,
+ "{:#04X} (RANGE_GET_RANGING_COUNT)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum AppDataOpCode {
+ AppDataTx = 0x0,
+ AppDataRx = 0x1,
+}
+impl fmt::Display for AppDataOpCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ AppDataOpCode::AppDataTx => write!(f, "{:#04X} (APP_DATA_TX)", self.to_u8().unwrap()),
+ AppDataOpCode::AppDataRx => write!(f, "{:#04X} (APP_DATA_RX)", self.to_u8().unwrap()),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum PicaOpCode {
+ PicaInitDevice = 0x0,
+ PicaSetDevicePosition = 0x1,
+ PicaCreateBeacon = 0x2,
+ PicaSetBeaconPosition = 0x3,
+ PicaDestroyBeacon = 0x4,
+}
+impl fmt::Display for PicaOpCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ PicaOpCode::PicaInitDevice => {
+ write!(f, "{:#04X} (PICA_INIT_DEVICE)", self.to_u8().unwrap())
+ }
+ PicaOpCode::PicaSetDevicePosition => write!(
+ f,
+ "{:#04X} (PICA_SET_DEVICE_POSITION)",
+ self.to_u8().unwrap()
+ ),
+ PicaOpCode::PicaCreateBeacon => {
+ write!(f, "{:#04X} (PICA_CREATE_BEACON)", self.to_u8().unwrap())
+ }
+ PicaOpCode::PicaSetBeaconPosition => write!(
+ f,
+ "{:#04X} (PICA_SET_BEACON_POSITION)",
+ self.to_u8().unwrap()
+ ),
+ PicaOpCode::PicaDestroyBeacon => {
+ write!(f, "{:#04X} (PICA_DESTROY_BEACON)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum AndroidOpCode {
+ AndroidGetPowerStats = 0x0,
+ AndroidSetCountryCode = 0x1,
+}
+impl fmt::Display for AndroidOpCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ AndroidOpCode::AndroidGetPowerStats => write!(
+ f,
+ "{:#04X} (ANDROID_GET_POWER_STATS)",
+ self.to_u8().unwrap()
+ ),
+ AndroidOpCode::AndroidSetCountryCode => write!(
+ f,
+ "{:#04X} (ANDROID_SET_COUNTRY_CODE)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum StatusCode {
+ UciStatusOk = 0x0,
+ UciStatusRejected = 0x1,
+ UciStatusFailed = 0x2,
+ UciStatusSyntaxError = 0x3,
+ UciStatusInvalidParam = 0x4,
+ UciStatusInvalidRange = 0x5,
+ UciStatusInvalidMsgSize = 0x6,
+ UciStatusUnknownGid = 0x7,
+ UciStatusUnknownOid = 0x8,
+ UciStatusReadOnly = 0x9,
+ UciStatusCommandRetry = 0xa,
+ UciStatusSessionNotExist = 0x11,
+ UciStatusSessionDuplicate = 0x12,
+ UciStatusSessionActive = 0x13,
+ UciStatusMaxSessionsExceeded = 0x14,
+ UciStatusSessionNotConfigured = 0x15,
+ UciStatusActiveSessionOngoing = 0x16,
+ UciStatusMulticastListFull = 0x17,
+ UciStatusAddressNotFound = 0x18,
+ UciStatusAddressAlreadyPresent = 0x19,
+ UciStatusRangingTxFailed = 0x20,
+ UciStatusRangingRxTimeout = 0x21,
+ UciStatusRangingRxPhyDecFailed = 0x22,
+ UciStatusRangingRxPhyToaFailed = 0x23,
+ UciStatusRangingRxPhyStsFailed = 0x24,
+ UciStatusRangingRxMacDecFailed = 0x25,
+ UciStatusRangingRxMacIeDecFailed = 0x26,
+ UciStatusRangingRxMacIeMissing = 0x27,
+ UciStatusDataMaxTxPsduSizeExceeded = 0x30,
+ UciStatusDataRxCrcError = 0x31,
+ UciStatusErrorCccSeBusy = 0x50,
+ UciStatusErrorCccLifecycle = 0x51,
+}
+impl fmt::Display for StatusCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ StatusCode::UciStatusOk => write!(f, "{:#04X} (UCI_STATUS_OK)", self.to_u8().unwrap()),
+ StatusCode::UciStatusRejected => {
+ write!(f, "{:#04X} (UCI_STATUS_REJECTED)", self.to_u8().unwrap())
+ }
+ StatusCode::UciStatusFailed => {
+ write!(f, "{:#04X} (UCI_STATUS_FAILED)", self.to_u8().unwrap())
+ }
+ StatusCode::UciStatusSyntaxError => write!(
+ f,
+ "{:#04X} (UCI_STATUS_SYNTAX_ERROR)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusInvalidParam => write!(
+ f,
+ "{:#04X} (UCI_STATUS_INVALID_PARAM)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusInvalidRange => write!(
+ f,
+ "{:#04X} (UCI_STATUS_INVALID_RANGE)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusInvalidMsgSize => write!(
+ f,
+ "{:#04X} (UCI_STATUS_INVALID_MSG_SIZE)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusUnknownGid => {
+ write!(f, "{:#04X} (UCI_STATUS_UNKNOWN_GID)", self.to_u8().unwrap())
+ }
+ StatusCode::UciStatusUnknownOid => {
+ write!(f, "{:#04X} (UCI_STATUS_UNKNOWN_OID)", self.to_u8().unwrap())
+ }
+ StatusCode::UciStatusReadOnly => {
+ write!(f, "{:#04X} (UCI_STATUS_READ_ONLY)", self.to_u8().unwrap())
+ }
+ StatusCode::UciStatusCommandRetry => write!(
+ f,
+ "{:#04X} (UCI_STATUS_COMMAND_RETRY)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusSessionNotExist => write!(
+ f,
+ "{:#04X} (UCI_STATUS_SESSION_NOT_EXIST)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusSessionDuplicate => write!(
+ f,
+ "{:#04X} (UCI_STATUS_SESSION_DUPLICATE)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusSessionActive => write!(
+ f,
+ "{:#04X} (UCI_STATUS_SESSION_ACTIVE)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusMaxSessionsExceeded => write!(
+ f,
+ "{:#04X} (UCI_STATUS_MAX_SESSIONS_EXCEEDED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusSessionNotConfigured => write!(
+ f,
+ "{:#04X} (UCI_STATUS_SESSION_NOT_CONFIGURED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusActiveSessionOngoing => write!(
+ f,
+ "{:#04X} (UCI_STATUS_ACTIVE_SESSION_ONGOING)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusMulticastListFull => write!(
+ f,
+ "{:#04X} (UCI_STATUS_MULTICAST_LIST_FULL)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusAddressNotFound => write!(
+ f,
+ "{:#04X} (UCI_STATUS_ADDRESS_NOT_FOUND)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusAddressAlreadyPresent => write!(
+ f,
+ "{:#04X} (UCI_STATUS_ADDRESS_ALREADY_PRESENT)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingTxFailed => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_TX_FAILED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingRxTimeout => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_RX_TIMEOUT)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingRxPhyDecFailed => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_RX_PHY_DEC_FAILED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingRxPhyToaFailed => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_RX_PHY_TOA_FAILED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingRxPhyStsFailed => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_RX_PHY_STS_FAILED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingRxMacDecFailed => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_RX_MAC_DEC_FAILED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingRxMacIeDecFailed => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusRangingRxMacIeMissing => write!(
+ f,
+ "{:#04X} (UCI_STATUS_RANGING_RX_MAC_IE_MISSING)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusDataMaxTxPsduSizeExceeded => write!(
+ f,
+ "{:#04X} (UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusDataRxCrcError => write!(
+ f,
+ "{:#04X} (UCI_STATUS_DATA_RX_CRC_ERROR)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusErrorCccSeBusy => write!(
+ f,
+ "{:#04X} (UCI_STATUS_ERROR_CCC_SE_BUSY)",
+ self.to_u8().unwrap()
+ ),
+ StatusCode::UciStatusErrorCccLifecycle => write!(
+ f,
+ "{:#04X} (UCI_STATUS_ERROR_CCC_LIFECYCLE)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum ResetConfig {
+ UwbsReset = 0x0,
+}
+impl fmt::Display for ResetConfig {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ResetConfig::UwbsReset => write!(f, "{:#04X} (UWBS_RESET)", self.to_u8().unwrap()),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum DeviceConfigId {
+ DeviceState = 0x0,
+ LowPowerMode = 0x1,
+}
+impl fmt::Display for DeviceConfigId {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ DeviceConfigId::DeviceState => {
+ write!(f, "{:#04X} (DEVICE_STATE)", self.to_u8().unwrap())
+ }
+ DeviceConfigId::LowPowerMode => {
+ write!(f, "{:#04X} (LOW_POWER_MODE)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum AppConfigTlvType {
+ DeviceType = 0x0,
+ RangingRoundUsage = 0x1,
+ StsConfig = 0x2,
+ MultiNodeMode = 0x3,
+ ChannelNumber = 0x4,
+ NoOfControlee = 0x5,
+ DeviceMacAddress = 0x6,
+ DstMacAddress = 0x7,
+ SlotDuration = 0x8,
+ RangingInterval = 0x9,
+ StsIndex = 0xa,
+ MacFcsType = 0xb,
+ RangingRoundControl = 0xc,
+ AoaResultReq = 0xd,
+ RngDataNtf = 0xe,
+ RngDataNtfProximityNear = 0xf,
+ RngDataNtfProximityFar = 0x10,
+ DeviceRole = 0x11,
+ RframeConfig = 0x12,
+ PreambleCodeIndex = 0x14,
+ SfdId = 0x15,
+ PsduDataRate = 0x16,
+ PreambleDuration = 0x17,
+ RangingTimeStruct = 0x1a,
+ SlotsPerRr = 0x1b,
+ TxAdaptivePayloadPower = 0x1c,
+ ResponderSlotIndex = 0x1e,
+ PrfMode = 0x1f,
+ ScheduledMode = 0x22,
+ KeyRotation = 0x23,
+ KeyRotationRate = 0x24,
+ SessionPriority = 0x25,
+ MacAddressMode = 0x26,
+ VendorId = 0x27,
+ StaticStsIv = 0x28,
+ NumberOfStsSegments = 0x29,
+ MaxRrRetry = 0x2a,
+ UwbInitiationTime = 0x2b,
+ HoppingMode = 0x2c,
+ BlockStrideLength = 0x2d,
+ ResultReportConfig = 0x2e,
+ InBandTerminationAttemptCount = 0x2f,
+ SubSessionId = 0x30,
+ BprfPhrDataRate = 0x31,
+ MaxNumberOfMeasurements = 0x32,
+ StsLength = 0x35,
+ CccHopModeKey = 0xa0,
+ CccUwbTime0 = 0xa1,
+ CccRangingProtocolVer = 0xa3,
+ CccUwbConfigId = 0xa4,
+ CccPulseshapeCombo = 0xa5,
+ CccUrskTtl = 0xa6,
+ NbOfRangeMeasurements = 0xe3,
+ NbOfAzimuthMeasurements = 0xe4,
+ NbOfElevationMeasurements = 0xe5,
+}
+impl fmt::Display for AppConfigTlvType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ AppConfigTlvType::DeviceType => {
+ write!(f, "{:#04X} (DEVICE_TYPE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::RangingRoundUsage => {
+ write!(f, "{:#04X} (RANGING_ROUND_USAGE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::StsConfig => write!(f, "{:#04X} (STS_CONFIG)", self.to_u8().unwrap()),
+ AppConfigTlvType::MultiNodeMode => {
+ write!(f, "{:#04X} (MULTI_NODE_MODE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::ChannelNumber => {
+ write!(f, "{:#04X} (CHANNEL_NUMBER)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::NoOfControlee => {
+ write!(f, "{:#04X} (NO_OF_CONTROLEE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::DeviceMacAddress => {
+ write!(f, "{:#04X} (DEVICE_MAC_ADDRESS)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::DstMacAddress => {
+ write!(f, "{:#04X} (DST_MAC_ADDRESS)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::SlotDuration => {
+ write!(f, "{:#04X} (SLOT_DURATION)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::RangingInterval => {
+ write!(f, "{:#04X} (RANGING_INTERVAL)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::StsIndex => write!(f, "{:#04X} (STS_INDEX)", self.to_u8().unwrap()),
+ AppConfigTlvType::MacFcsType => {
+ write!(f, "{:#04X} (MAC_FCS_TYPE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::RangingRoundControl => {
+ write!(f, "{:#04X} (RANGING_ROUND_CONTROL)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::AoaResultReq => {
+ write!(f, "{:#04X} (AOA_RESULT_REQ)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::RngDataNtf => {
+ write!(f, "{:#04X} (RNG_DATA_NTF)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::RngDataNtfProximityNear => write!(
+ f,
+ "{:#04X} (RNG_DATA_NTF_PROXIMITY_NEAR)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::RngDataNtfProximityFar => write!(
+ f,
+ "{:#04X} (RNG_DATA_NTF_PROXIMITY_FAR)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::DeviceRole => {
+ write!(f, "{:#04X} (DEVICE_ROLE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::RframeConfig => {
+ write!(f, "{:#04X} (RFRAME_CONFIG)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::PreambleCodeIndex => {
+ write!(f, "{:#04X} (PREAMBLE_CODE_INDEX)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::SfdId => write!(f, "{:#04X} (SFD_ID)", self.to_u8().unwrap()),
+ AppConfigTlvType::PsduDataRate => {
+ write!(f, "{:#04X} (PSDU_DATA_RATE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::PreambleDuration => {
+ write!(f, "{:#04X} (PREAMBLE_DURATION)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::RangingTimeStruct => {
+ write!(f, "{:#04X} (RANGING_TIME_STRUCT)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::SlotsPerRr => {
+ write!(f, "{:#04X} (SLOTS_PER_RR)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::TxAdaptivePayloadPower => write!(
+ f,
+ "{:#04X} (TX_ADAPTIVE_PAYLOAD_POWER)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::ResponderSlotIndex => {
+ write!(f, "{:#04X} (RESPONDER_SLOT_INDEX)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::PrfMode => write!(f, "{:#04X} (PRF_MODE)", self.to_u8().unwrap()),
+ AppConfigTlvType::ScheduledMode => {
+ write!(f, "{:#04X} (SCHEDULED_MODE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::KeyRotation => {
+ write!(f, "{:#04X} (KEY_ROTATION)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::KeyRotationRate => {
+ write!(f, "{:#04X} (KEY_ROTATION_RATE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::SessionPriority => {
+ write!(f, "{:#04X} (SESSION_PRIORITY)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::MacAddressMode => {
+ write!(f, "{:#04X} (MAC_ADDRESS_MODE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::VendorId => write!(f, "{:#04X} (VENDOR_ID)", self.to_u8().unwrap()),
+ AppConfigTlvType::StaticStsIv => {
+ write!(f, "{:#04X} (STATIC_STS_IV)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::NumberOfStsSegments => {
+ write!(f, "{:#04X} (NUMBER_OF_STS_SEGMENTS)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::MaxRrRetry => {
+ write!(f, "{:#04X} (MAX_RR_RETRY)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::UwbInitiationTime => {
+ write!(f, "{:#04X} (UWB_INITIATION_TIME)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::HoppingMode => {
+ write!(f, "{:#04X} (HOPPING_MODE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::BlockStrideLength => {
+ write!(f, "{:#04X} (BLOCK_STRIDE_LENGTH)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::ResultReportConfig => {
+ write!(f, "{:#04X} (RESULT_REPORT_CONFIG)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::InBandTerminationAttemptCount => write!(
+ f,
+ "{:#04X} (IN_BAND_TERMINATION_ATTEMPT_COUNT)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::SubSessionId => {
+ write!(f, "{:#04X} (SUB_SESSION_ID)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::BprfPhrDataRate => {
+ write!(f, "{:#04X} (BPRF_PHR_DATA_RATE)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::MaxNumberOfMeasurements => write!(
+ f,
+ "{:#04X} (MAX_NUMBER_OF_MEASUREMENTS)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::StsLength => write!(f, "{:#04X} (STS_LENGTH)", self.to_u8().unwrap()),
+ AppConfigTlvType::CccHopModeKey => {
+ write!(f, "{:#04X} (CCC_HOP_MODE_KEY)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::CccUwbTime0 => {
+ write!(f, "{:#04X} (CCC_UWB_TIME0)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::CccRangingProtocolVer => write!(
+ f,
+ "{:#04X} (CCC_RANGING_PROTOCOL_VER)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::CccUwbConfigId => {
+ write!(f, "{:#04X} (CCC_UWB_CONFIG_ID)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::CccPulseshapeCombo => {
+ write!(f, "{:#04X} (CCC_PULSESHAPE_COMBO)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::CccUrskTtl => {
+ write!(f, "{:#04X} (CCC_URSK_TTL)", self.to_u8().unwrap())
+ }
+ AppConfigTlvType::NbOfRangeMeasurements => write!(
+ f,
+ "{:#04X} (NB_OF_RANGE_MEASUREMENTS)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::NbOfAzimuthMeasurements => write!(
+ f,
+ "{:#04X} (NB_OF_AZIMUTH_MEASUREMENTS)",
+ self.to_u8().unwrap()
+ ),
+ AppConfigTlvType::NbOfElevationMeasurements => write!(
+ f,
+ "{:#04X} (NB_OF_ELEVATION_MEASUREMENTS)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum CapTlvType {
+ SupportedFiraPhyVersionRange = 0x0,
+ SupportedFiraMacVersionRange = 0x1,
+ SupportedDeviceRoles = 0x2,
+ SupportedRangingMethod = 0x3,
+ SupportedStsConfig = 0x4,
+ SupportedMultiNodeModes = 0x5,
+ SupportedRangingTimeStruct = 0x6,
+ SupportedScheduledMode = 0x7,
+ SupportedHoppingMode = 0x8,
+ SupportedBlockStriding = 0x9,
+ SupportedUwbInitiationTime = 0xa,
+ SupportedChannels = 0xb,
+ SupportedRframeConfig = 0xc,
+ SupportedCcConstraintLength = 0xd,
+ SupportedBprfParameterSets = 0xe,
+ SupportedHprfParameterSets = 0xf,
+ SupportedAoa = 0x10,
+ SupportedExtendedMacAddress = 0x11,
+ CccSupportedChapsPerSlot = 0xa0,
+ CccSupportedSyncCodes = 0xa1,
+ CccSupportedHoppingConfigModesAndSequences = 0xa2,
+ CccSupportedChannels = 0xa3,
+ CccSupportedVersions = 0xa4,
+ CccSupportedUwbConfigs = 0xa5,
+ CccSupportedPulseShapeCombos = 0xa6,
+ CccSupportedRanMultiplier = 0xa7,
+ SupportedPowerStats = 0xc0,
+ SupportedAoaResultReqAntennaInterleaving = 0xe3,
+}
+impl fmt::Display for CapTlvType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ CapTlvType::SupportedFiraPhyVersionRange => write!(
+ f,
+ "{:#04X} (SUPPORTED_FIRA_PHY_VERSION_RANGE)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedFiraMacVersionRange => write!(
+ f,
+ "{:#04X} (SUPPORTED_FIRA_MAC_VERSION_RANGE)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedDeviceRoles => {
+ write!(f, "{:#04X} (SUPPORTED_DEVICE_ROLES)", self.to_u8().unwrap())
+ }
+ CapTlvType::SupportedRangingMethod => write!(
+ f,
+ "{:#04X} (SUPPORTED_RANGING_METHOD)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedStsConfig => {
+ write!(f, "{:#04X} (SUPPORTED_STS_CONFIG)", self.to_u8().unwrap())
+ }
+ CapTlvType::SupportedMultiNodeModes => write!(
+ f,
+ "{:#04X} (SUPPORTED_MULTI_NODE_MODES)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedRangingTimeStruct => write!(
+ f,
+ "{:#04X} (SUPPORTED_RANGING_TIME_STRUCT)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedScheduledMode => write!(
+ f,
+ "{:#04X} (SUPPORTED_SCHEDULED_MODE)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedHoppingMode => {
+ write!(f, "{:#04X} (SUPPORTED_HOPPING_MODE)", self.to_u8().unwrap())
+ }
+ CapTlvType::SupportedBlockStriding => write!(
+ f,
+ "{:#04X} (SUPPORTED_BLOCK_STRIDING)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedUwbInitiationTime => write!(
+ f,
+ "{:#04X} (SUPPORTED_UWB_INITIATION_TIME)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedChannels => {
+ write!(f, "{:#04X} (SUPPORTED_CHANNELS)", self.to_u8().unwrap())
+ }
+ CapTlvType::SupportedRframeConfig => write!(
+ f,
+ "{:#04X} (SUPPORTED_RFRAME_CONFIG)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedCcConstraintLength => write!(
+ f,
+ "{:#04X} (SUPPORTED_CC_CONSTRAINT_LENGTH)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedBprfParameterSets => write!(
+ f,
+ "{:#04X} (SUPPORTED_BPRF_PARAMETER_SETS)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedHprfParameterSets => write!(
+ f,
+ "{:#04X} (SUPPORTED_HPRF_PARAMETER_SETS)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedAoa => write!(f, "{:#04X} (SUPPORTED_AOA)", self.to_u8().unwrap()),
+ CapTlvType::SupportedExtendedMacAddress => write!(
+ f,
+ "{:#04X} (SUPPORTED_EXTENDED_MAC_ADDRESS)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::CccSupportedChapsPerSlot => write!(
+ f,
+ "{:#04X} (CCC_SUPPORTED_CHAPS_PER_SLOT)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::CccSupportedSyncCodes => write!(
+ f,
+ "{:#04X} (CCC_SUPPORTED_SYNC_CODES)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::CccSupportedHoppingConfigModesAndSequences => write!(
+ f,
+ "{:#04X} (CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::CccSupportedChannels => {
+ write!(f, "{:#04X} (CCC_SUPPORTED_CHANNELS)", self.to_u8().unwrap())
+ }
+ CapTlvType::CccSupportedVersions => {
+ write!(f, "{:#04X} (CCC_SUPPORTED_VERSIONS)", self.to_u8().unwrap())
+ }
+ CapTlvType::CccSupportedUwbConfigs => write!(
+ f,
+ "{:#04X} (CCC_SUPPORTED_UWB_CONFIGS)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::CccSupportedPulseShapeCombos => write!(
+ f,
+ "{:#04X} (CCC_SUPPORTED_PULSE_SHAPE_COMBOS)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::CccSupportedRanMultiplier => write!(
+ f,
+ "{:#04X} (CCC_SUPPORTED_RAN_MULTIPLIER)",
+ self.to_u8().unwrap()
+ ),
+ CapTlvType::SupportedPowerStats => {
+ write!(f, "{:#04X} (SUPPORTED_POWER_STATS)", self.to_u8().unwrap())
+ }
+ CapTlvType::SupportedAoaResultReqAntennaInterleaving => write!(
+ f,
+ "{:#04X} (SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum AoaResultReqType {
+ AoaDisable = 0x0,
+ AoaEnable = 0x1,
+ AoaEnableAzimuth = 0x2,
+ AoaEnableElevation = 0x3,
+ AoaEnableInterleaved = 0xf0,
+}
+impl fmt::Display for AoaResultReqType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ AoaResultReqType::AoaDisable => {
+ write!(f, "{:#04X} (AOA_DISABLE)", self.to_u8().unwrap())
+ }
+ AoaResultReqType::AoaEnable => write!(f, "{:#04X} (AOA_ENABLE)", self.to_u8().unwrap()),
+ AoaResultReqType::AoaEnableAzimuth => {
+ write!(f, "{:#04X} (AOA_ENABLE_AZIMUTH)", self.to_u8().unwrap())
+ }
+ AoaResultReqType::AoaEnableElevation => {
+ write!(f, "{:#04X} (AOA_ENABLE_ELEVATION)", self.to_u8().unwrap())
+ }
+ AoaResultReqType::AoaEnableInterleaved => {
+ write!(f, "{:#04X} (AOA_ENABLE_INTERLEAVED)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum DeviceState {
+ DeviceStateReady = 0x1,
+ DeviceStateActive = 0x2,
+ DeviceStateError = 0xff,
+}
+impl fmt::Display for DeviceState {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ DeviceState::DeviceStateReady => {
+ write!(f, "{:#04X} (DEVICE_STATE_READY)", self.to_u8().unwrap())
+ }
+ DeviceState::DeviceStateActive => {
+ write!(f, "{:#04X} (DEVICE_STATE_ACTIVE)", self.to_u8().unwrap())
+ }
+ DeviceState::DeviceStateError => {
+ write!(f, "{:#04X} (DEVICE_STATE_ERROR)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum SessionState {
+ SessionStateInit = 0x0,
+ SessionStateDeinit = 0x1,
+ SessionStateActive = 0x2,
+ SessionStateIdle = 0x3,
+}
+impl fmt::Display for SessionState {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ SessionState::SessionStateInit => {
+ write!(f, "{:#04X} (SESSION_STATE_INIT)", self.to_u8().unwrap())
+ }
+ SessionState::SessionStateDeinit => {
+ write!(f, "{:#04X} (SESSION_STATE_DEINIT)", self.to_u8().unwrap())
+ }
+ SessionState::SessionStateActive => {
+ write!(f, "{:#04X} (SESSION_STATE_ACTIVE)", self.to_u8().unwrap())
+ }
+ SessionState::SessionStateIdle => {
+ write!(f, "{:#04X} (SESSION_STATE_IDLE)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum ReasonCode {
+ StateChangeWithSessionManagementCommands = 0x0,
+ MaxRangingRoundRetryCountReached = 0x1,
+ MaxNumberOfMeasurementsReached = 0x2,
+ ErrorSlotLengthNotSupported = 0x20,
+ ErrorInsufficientSlotsPerRr = 0x21,
+ ErrorMacAddressModeNotSupported = 0x22,
+ ErrorInvalidRangingInterval = 0x23,
+ ErrorInvalidStsConfig = 0x24,
+ ErrorInvalidRframeConfig = 0x25,
+}
+impl fmt::Display for ReasonCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ReasonCode::StateChangeWithSessionManagementCommands => write!(
+ f,
+ "{:#04X} (STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::MaxRangingRoundRetryCountReached => write!(
+ f,
+ "{:#04X} (MAX_RANGING_ROUND_RETRY_COUNT_REACHED)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::MaxNumberOfMeasurementsReached => write!(
+ f,
+ "{:#04X} (MAX_NUMBER_OF_MEASUREMENTS_REACHED)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::ErrorSlotLengthNotSupported => write!(
+ f,
+ "{:#04X} (ERROR_SLOT_LENGTH_NOT_SUPPORTED)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::ErrorInsufficientSlotsPerRr => write!(
+ f,
+ "{:#04X} (ERROR_INSUFFICIENT_SLOTS_PER_RR)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::ErrorMacAddressModeNotSupported => write!(
+ f,
+ "{:#04X} (ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::ErrorInvalidRangingInterval => write!(
+ f,
+ "{:#04X} (ERROR_INVALID_RANGING_INTERVAL)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::ErrorInvalidStsConfig => write!(
+ f,
+ "{:#04X} (ERROR_INVALID_STS_CONFIG)",
+ self.to_u8().unwrap()
+ ),
+ ReasonCode::ErrorInvalidRframeConfig => write!(
+ f,
+ "{:#04X} (ERROR_INVALID_RFRAME_CONFIG)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum MulticastUpdateStatusCode {
+ StatusOkMulticastListUpdate = 0x0,
+ StatusErrorMulticastListFull = 0x1,
+ StatusErrorKeyFetchFail = 0x2,
+ StatusErrorSubSessionIdNotFound = 0x3,
+}
+impl fmt::Display for MulticastUpdateStatusCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ MulticastUpdateStatusCode::StatusOkMulticastListUpdate => write!(
+ f,
+ "{:#04X} (STATUS_OK_MULTICAST_LIST_UPDATE)",
+ self.to_u8().unwrap()
+ ),
+ MulticastUpdateStatusCode::StatusErrorMulticastListFull => write!(
+ f,
+ "{:#04X} (STATUS_ERROR_MULTICAST_LIST_FULL)",
+ self.to_u8().unwrap()
+ ),
+ MulticastUpdateStatusCode::StatusErrorKeyFetchFail => write!(
+ f,
+ "{:#04X} (STATUS_ERROR_KEY_FETCH_FAIL)",
+ self.to_u8().unwrap()
+ ),
+ MulticastUpdateStatusCode::StatusErrorSubSessionIdNotFound => write!(
+ f,
+ "{:#04X} (STATUS_ERROR_SUB_SESSION_ID_NOT_FOUND)",
+ self.to_u8().unwrap()
+ ),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum MacAddressIndicator {
+ ShortAddress = 0x0,
+ ExtendedAddress = 0x1,
+}
+impl fmt::Display for MacAddressIndicator {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ MacAddressIndicator::ShortAddress => {
+ write!(f, "{:#04X} (SHORT_ADDRESS)", self.to_u8().unwrap())
+ }
+ MacAddressIndicator::ExtendedAddress => {
+ write!(f, "{:#04X} (EXTENDED_ADDRESS)", self.to_u8().unwrap())
+ }
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum SessionType {
+ FiraRangingSession = 0x0,
+ FiraDataTransfer = 0x1,
+ Ccc = 0xa0,
+}
+impl fmt::Display for SessionType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ SessionType::FiraRangingSession => {
+ write!(f, "{:#04X} (FIRA_RANGING_SESSION)", self.to_u8().unwrap())
+ }
+ SessionType::FiraDataTransfer => {
+ write!(f, "{:#04X} (FIRA_DATA_TRANSFER)", self.to_u8().unwrap())
+ }
+ SessionType::Ccc => write!(f, "{:#04X} (CCC)", self.to_u8().unwrap()),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum MessageType {
+ Command = 0x1,
+ Response = 0x2,
+ Notification = 0x3,
+}
+impl fmt::Display for MessageType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ MessageType::Command => write!(f, "{:#04X} (COMMAND)", self.to_u8().unwrap()),
+ MessageType::Response => write!(f, "{:#04X} (RESPONSE)", self.to_u8().unwrap()),
+ MessageType::Notification => write!(f, "{:#04X} (NOTIFICATION)", self.to_u8().unwrap()),
+ }
+ }
+}
+
+#[derive(FromPrimitive, ToPrimitive, Debug, Hash, Eq, PartialEq, Clone, Copy)]
+#[repr(u64)]
+pub enum RangingMeasurementType {
+ OneWay = 0x0,
+ TwoWay = 0x1,
+}
+impl fmt::Display for RangingMeasurementType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ RangingMeasurementType::OneWay => write!(f, "{:#04X} (ONE_WAY)", self.to_u8().unwrap()),
+ RangingMeasurementType::TwoWay => write!(f, "{:#04X} (TWO_WAY)", self.to_u8().unwrap()),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct CapTlv {
+ pub t: CapTlvType,
+ pub v: Vec<u8>,
+}
+impl CapTlv {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 2 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 1 {
+ return Err(Error::InvalidLengthError {
+ obj: "CapTlv".to_string(),
+ field: "t".to_string(),
+ wanted: 1,
+ got: bytes.len(),
+ });
+ }
+ let t = u8::from_le_bytes([bytes[0]]);
+ let t = CapTlvType::from_u8(t).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "CapTlv".to_string(),
+ field: "t".to_string(),
+ value: t as u64,
+ type_: "CapTlvType".to_string(),
+ })?;
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "CapTlv".to_string(),
+ field: "v_count".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let v_count = u8::from_le_bytes([bytes[1]]);
+ let want_ = 2 + ((v_count as usize) * 1);
+ if bytes.len() < want_ {
+ return Err(Error::InvalidLengthError {
+ obj: "CapTlv".to_string(),
+ field: "v".to_string(),
+ wanted: want_,
+ got: bytes.len(),
+ });
+ }
+ let v: Vec<u8> = bytes[2..2 + ((v_count as usize) * 1)]
+ .to_vec()
+ .chunks_exact(1)
+ .into_iter()
+ .map(|i| u8::from_le_bytes([i[0]]))
+ .collect();
+ Ok(Self { t, v })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let t = self.t.to_u8().unwrap();
+ buffer[0..1].copy_from_slice(&t.to_le_bytes()[0..1]);
+ buffer[1..2].copy_from_slice(&(self.v.len() as u8).to_le_bytes());
+ for (i, e) in self.v.iter().enumerate() {
+ buffer[2 + i..2 + i + 1].copy_from_slice(&e.to_le_bytes())
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 2;
+ let ret = ret + (self.v.len() * ((/* Bits: */8 + /* Dynamic: */ 0) / 8));
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct DeviceParameter {
+ pub id: u8,
+ pub value: Vec<u8>,
+}
+impl DeviceParameter {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 2 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 1 {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceParameter".to_string(),
+ field: "id".to_string(),
+ wanted: 1,
+ got: bytes.len(),
+ });
+ }
+ let id = u8::from_le_bytes([bytes[0]]);
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceParameter".to_string(),
+ field: "value_count".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let value_count = u8::from_le_bytes([bytes[1]]);
+ let want_ = 2 + ((value_count as usize) * 1);
+ if bytes.len() < want_ {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceParameter".to_string(),
+ field: "value".to_string(),
+ wanted: want_,
+ got: bytes.len(),
+ });
+ }
+ let value: Vec<u8> = bytes[2..2 + ((value_count as usize) * 1)]
+ .to_vec()
+ .chunks_exact(1)
+ .into_iter()
+ .map(|i| u8::from_le_bytes([i[0]]))
+ .collect();
+ Ok(Self { id, value })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let id = self.id;
+ buffer[0..1].copy_from_slice(&id.to_le_bytes()[0..1]);
+ buffer[1..2].copy_from_slice(&(self.value.len() as u8).to_le_bytes());
+ for (i, e) in self.value.iter().enumerate() {
+ buffer[2 + i..2 + i + 1].copy_from_slice(&e.to_le_bytes())
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 2;
+ let ret = ret + (self.value.len() * ((/* Bits: */8 + /* Dynamic: */ 0) / 8));
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct DeviceConfigStatus {
+ pub parameter_id: u8,
+ pub status: StatusCode,
+}
+impl DeviceConfigStatus {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 2 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 1 {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceConfigStatus".to_string(),
+ field: "parameter_id".to_string(),
+ wanted: 1,
+ got: bytes.len(),
+ });
+ }
+ let parameter_id = u8::from_le_bytes([bytes[0]]);
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceConfigStatus".to_string(),
+ field: "status".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[1]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "DeviceConfigStatus".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ Ok(Self {
+ parameter_id,
+ status,
+ })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let parameter_id = self.parameter_id;
+ buffer[0..1].copy_from_slice(¶meter_id.to_le_bytes()[0..1]);
+ let status = self.status.to_u8().unwrap();
+ buffer[1..2].copy_from_slice(&status.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 2;
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct AppConfigParameter {
+ pub id: u8,
+ pub value: Vec<u8>,
+}
+impl AppConfigParameter {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 2 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 1 {
+ return Err(Error::InvalidLengthError {
+ obj: "AppConfigParameter".to_string(),
+ field: "id".to_string(),
+ wanted: 1,
+ got: bytes.len(),
+ });
+ }
+ let id = u8::from_le_bytes([bytes[0]]);
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "AppConfigParameter".to_string(),
+ field: "value_count".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let value_count = u8::from_le_bytes([bytes[1]]);
+ let want_ = 2 + ((value_count as usize) * 1);
+ if bytes.len() < want_ {
+ return Err(Error::InvalidLengthError {
+ obj: "AppConfigParameter".to_string(),
+ field: "value".to_string(),
+ wanted: want_,
+ got: bytes.len(),
+ });
+ }
+ let value: Vec<u8> = bytes[2..2 + ((value_count as usize) * 1)]
+ .to_vec()
+ .chunks_exact(1)
+ .into_iter()
+ .map(|i| u8::from_le_bytes([i[0]]))
+ .collect();
+ Ok(Self { id, value })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let id = self.id;
+ buffer[0..1].copy_from_slice(&id.to_le_bytes()[0..1]);
+ buffer[1..2].copy_from_slice(&(self.value.len() as u8).to_le_bytes());
+ for (i, e) in self.value.iter().enumerate() {
+ buffer[2 + i..2 + i + 1].copy_from_slice(&e.to_le_bytes())
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 2;
+ let ret = ret + (self.value.len() * ((/* Bits: */8 + /* Dynamic: */ 0) / 8));
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct AppConfigStatus {
+ pub config_id: u8,
+ pub status: StatusCode,
+}
+impl AppConfigStatus {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 2 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 1 {
+ return Err(Error::InvalidLengthError {
+ obj: "AppConfigStatus".to_string(),
+ field: "config_id".to_string(),
+ wanted: 1,
+ got: bytes.len(),
+ });
+ }
+ let config_id = u8::from_le_bytes([bytes[0]]);
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "AppConfigStatus".to_string(),
+ field: "status".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[1]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "AppConfigStatus".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ Ok(Self { config_id, status })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let config_id = self.config_id;
+ buffer[0..1].copy_from_slice(&config_id.to_le_bytes()[0..1]);
+ let status = self.status.to_u8().unwrap();
+ buffer[1..2].copy_from_slice(&status.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 2;
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct Controlee {
+ pub short_address: u16,
+ pub subsession_id: u32,
+}
+impl Controlee {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 6 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "Controlee".to_string(),
+ field: "short_address".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let short_address = u16::from_le_bytes([bytes[0], bytes[1]]);
+ if bytes.len() < 6 {
+ return Err(Error::InvalidLengthError {
+ obj: "Controlee".to_string(),
+ field: "subsession_id".to_string(),
+ wanted: 6,
+ got: bytes.len(),
+ });
+ }
+ let subsession_id = u32::from_le_bytes([bytes[2], bytes[3], bytes[4], bytes[5]]);
+ Ok(Self {
+ short_address,
+ subsession_id,
+ })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let short_address = self.short_address;
+ buffer[0..2].copy_from_slice(&short_address.to_le_bytes()[0..2]);
+ let subsession_id = self.subsession_id;
+ buffer[2..6].copy_from_slice(&subsession_id.to_le_bytes()[0..4]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 6;
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ControleeStatus {
+ pub mac_address: u16,
+ pub subsession_id: u32,
+ pub status: u8,
+}
+impl ControleeStatus {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 7 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "ControleeStatus".to_string(),
+ field: "mac_address".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let mac_address = u16::from_le_bytes([bytes[0], bytes[1]]);
+ if bytes.len() < 6 {
+ return Err(Error::InvalidLengthError {
+ obj: "ControleeStatus".to_string(),
+ field: "subsession_id".to_string(),
+ wanted: 6,
+ got: bytes.len(),
+ });
+ }
+ let subsession_id = u32::from_le_bytes([bytes[2], bytes[3], bytes[4], bytes[5]]);
+ if bytes.len() < 7 {
+ return Err(Error::InvalidLengthError {
+ obj: "ControleeStatus".to_string(),
+ field: "status".to_string(),
+ wanted: 7,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[6]]);
+ Ok(Self {
+ mac_address,
+ subsession_id,
+ status,
+ })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let mac_address = self.mac_address;
+ buffer[0..2].copy_from_slice(&mac_address.to_le_bytes()[0..2]);
+ let subsession_id = self.subsession_id;
+ buffer[2..6].copy_from_slice(&subsession_id.to_le_bytes()[0..4]);
+ let status = self.status;
+ buffer[6..7].copy_from_slice(&status.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 7;
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ShortAddressTwoWayRangingMeasurement {
+ pub mac_address: u16,
+ pub status: StatusCode,
+ pub nlos: u8,
+ pub distance: u16,
+ pub aoa_azimuth: u16,
+ pub aoa_azimuth_fom: u8,
+ pub aoa_elevation: u16,
+ pub aoa_elevation_fom: u8,
+ pub aoa_destination_azimuth: u16,
+ pub aoa_destination_azimuth_fom: u8,
+ pub aoa_destination_elevation: u16,
+ pub aoa_destination_elevation_fom: u8,
+ pub slot_index: u8,
+}
+impl ShortAddressTwoWayRangingMeasurement {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 31 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "mac_address".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let mac_address = u16::from_le_bytes([bytes[0], bytes[1]]);
+ if bytes.len() < 3 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "status".to_string(),
+ wanted: 3,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[2]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ if bytes.len() < 4 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "nlos".to_string(),
+ wanted: 4,
+ got: bytes.len(),
+ });
+ }
+ let nlos = u8::from_le_bytes([bytes[3]]);
+ if bytes.len() < 6 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "distance".to_string(),
+ wanted: 6,
+ got: bytes.len(),
+ });
+ }
+ let distance = u16::from_le_bytes([bytes[4], bytes[5]]);
+ if bytes.len() < 8 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_azimuth".to_string(),
+ wanted: 8,
+ got: bytes.len(),
+ });
+ }
+ let aoa_azimuth = u16::from_le_bytes([bytes[6], bytes[7]]);
+ if bytes.len() < 9 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_azimuth_fom".to_string(),
+ wanted: 9,
+ got: bytes.len(),
+ });
+ }
+ let aoa_azimuth_fom = u8::from_le_bytes([bytes[8]]);
+ if bytes.len() < 11 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_elevation".to_string(),
+ wanted: 11,
+ got: bytes.len(),
+ });
+ }
+ let aoa_elevation = u16::from_le_bytes([bytes[9], bytes[10]]);
+ if bytes.len() < 12 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_elevation_fom".to_string(),
+ wanted: 12,
+ got: bytes.len(),
+ });
+ }
+ let aoa_elevation_fom = u8::from_le_bytes([bytes[11]]);
+ if bytes.len() < 14 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_azimuth".to_string(),
+ wanted: 14,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_azimuth = u16::from_le_bytes([bytes[12], bytes[13]]);
+ if bytes.len() < 15 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_azimuth_fom".to_string(),
+ wanted: 15,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_azimuth_fom = u8::from_le_bytes([bytes[14]]);
+ if bytes.len() < 17 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_elevation".to_string(),
+ wanted: 17,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_elevation = u16::from_le_bytes([bytes[15], bytes[16]]);
+ if bytes.len() < 18 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_elevation_fom".to_string(),
+ wanted: 18,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_elevation_fom = u8::from_le_bytes([bytes[17]]);
+ if bytes.len() < 19 {
+ return Err(Error::InvalidLengthError {
+ obj: "ShortAddressTwoWayRangingMeasurement".to_string(),
+ field: "slot_index".to_string(),
+ wanted: 19,
+ got: bytes.len(),
+ });
+ }
+ let slot_index = u8::from_le_bytes([bytes[18]]);
+ Ok(Self {
+ mac_address,
+ status,
+ nlos,
+ distance,
+ aoa_azimuth,
+ aoa_azimuth_fom,
+ aoa_elevation,
+ aoa_elevation_fom,
+ aoa_destination_azimuth,
+ aoa_destination_azimuth_fom,
+ aoa_destination_elevation,
+ aoa_destination_elevation_fom,
+ slot_index,
+ })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let mac_address = self.mac_address;
+ buffer[0..2].copy_from_slice(&mac_address.to_le_bytes()[0..2]);
+ let status = self.status.to_u8().unwrap();
+ buffer[2..3].copy_from_slice(&status.to_le_bytes()[0..1]);
+ let nlos = self.nlos;
+ buffer[3..4].copy_from_slice(&nlos.to_le_bytes()[0..1]);
+ let distance = self.distance;
+ buffer[4..6].copy_from_slice(&distance.to_le_bytes()[0..2]);
+ let aoa_azimuth = self.aoa_azimuth;
+ buffer[6..8].copy_from_slice(&aoa_azimuth.to_le_bytes()[0..2]);
+ let aoa_azimuth_fom = self.aoa_azimuth_fom;
+ buffer[8..9].copy_from_slice(&aoa_azimuth_fom.to_le_bytes()[0..1]);
+ let aoa_elevation = self.aoa_elevation;
+ buffer[9..11].copy_from_slice(&aoa_elevation.to_le_bytes()[0..2]);
+ let aoa_elevation_fom = self.aoa_elevation_fom;
+ buffer[11..12].copy_from_slice(&aoa_elevation_fom.to_le_bytes()[0..1]);
+ let aoa_destination_azimuth = self.aoa_destination_azimuth;
+ buffer[12..14].copy_from_slice(&aoa_destination_azimuth.to_le_bytes()[0..2]);
+ let aoa_destination_azimuth_fom = self.aoa_destination_azimuth_fom;
+ buffer[14..15].copy_from_slice(&aoa_destination_azimuth_fom.to_le_bytes()[0..1]);
+ let aoa_destination_elevation = self.aoa_destination_elevation;
+ buffer[15..17].copy_from_slice(&aoa_destination_elevation.to_le_bytes()[0..2]);
+ let aoa_destination_elevation_fom = self.aoa_destination_elevation_fom;
+ buffer[17..18].copy_from_slice(&aoa_destination_elevation_fom.to_le_bytes()[0..1]);
+ let slot_index = self.slot_index;
+ buffer[18..19].copy_from_slice(&slot_index.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 31;
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExtendedAddressTwoWayRangingMeasurement {
+ pub mac_address: u64,
+ pub status: StatusCode,
+ pub nlos: u8,
+ pub distance: u16,
+ pub aoa_azimuth: u16,
+ pub aoa_azimuth_fom: u8,
+ pub aoa_elevation: u16,
+ pub aoa_elevation_fom: u8,
+ pub aoa_destination_azimuth: u16,
+ pub aoa_destination_azimuth_fom: u8,
+ pub aoa_destination_elevation: u16,
+ pub aoa_destination_elevation_fom: u8,
+ pub slot_index: u8,
+}
+impl ExtendedAddressTwoWayRangingMeasurement {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 31 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 8 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "mac_address".to_string(),
+ wanted: 8,
+ got: bytes.len(),
+ });
+ }
+ let mac_address = u64::from_le_bytes([
+ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
+ ]);
+ if bytes.len() < 9 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "status".to_string(),
+ wanted: 9,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[8]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ if bytes.len() < 10 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "nlos".to_string(),
+ wanted: 10,
+ got: bytes.len(),
+ });
+ }
+ let nlos = u8::from_le_bytes([bytes[9]]);
+ if bytes.len() < 12 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "distance".to_string(),
+ wanted: 12,
+ got: bytes.len(),
+ });
+ }
+ let distance = u16::from_le_bytes([bytes[10], bytes[11]]);
+ if bytes.len() < 14 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_azimuth".to_string(),
+ wanted: 14,
+ got: bytes.len(),
+ });
+ }
+ let aoa_azimuth = u16::from_le_bytes([bytes[12], bytes[13]]);
+ if bytes.len() < 15 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_azimuth_fom".to_string(),
+ wanted: 15,
+ got: bytes.len(),
+ });
+ }
+ let aoa_azimuth_fom = u8::from_le_bytes([bytes[14]]);
+ if bytes.len() < 17 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_elevation".to_string(),
+ wanted: 17,
+ got: bytes.len(),
+ });
+ }
+ let aoa_elevation = u16::from_le_bytes([bytes[15], bytes[16]]);
+ if bytes.len() < 18 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_elevation_fom".to_string(),
+ wanted: 18,
+ got: bytes.len(),
+ });
+ }
+ let aoa_elevation_fom = u8::from_le_bytes([bytes[17]]);
+ if bytes.len() < 20 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_azimuth".to_string(),
+ wanted: 20,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_azimuth = u16::from_le_bytes([bytes[18], bytes[19]]);
+ if bytes.len() < 21 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_azimuth_fom".to_string(),
+ wanted: 21,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_azimuth_fom = u8::from_le_bytes([bytes[20]]);
+ if bytes.len() < 23 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_elevation".to_string(),
+ wanted: 23,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_elevation = u16::from_le_bytes([bytes[21], bytes[22]]);
+ if bytes.len() < 24 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "aoa_destination_elevation_fom".to_string(),
+ wanted: 24,
+ got: bytes.len(),
+ });
+ }
+ let aoa_destination_elevation_fom = u8::from_le_bytes([bytes[23]]);
+ if bytes.len() < 25 {
+ return Err(Error::InvalidLengthError {
+ obj: "ExtendedAddressTwoWayRangingMeasurement".to_string(),
+ field: "slot_index".to_string(),
+ wanted: 25,
+ got: bytes.len(),
+ });
+ }
+ let slot_index = u8::from_le_bytes([bytes[24]]);
+ Ok(Self {
+ mac_address,
+ status,
+ nlos,
+ distance,
+ aoa_azimuth,
+ aoa_azimuth_fom,
+ aoa_elevation,
+ aoa_elevation_fom,
+ aoa_destination_azimuth,
+ aoa_destination_azimuth_fom,
+ aoa_destination_elevation,
+ aoa_destination_elevation_fom,
+ slot_index,
+ })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let mac_address = self.mac_address;
+ buffer[0..8].copy_from_slice(&mac_address.to_le_bytes()[0..8]);
+ let status = self.status.to_u8().unwrap();
+ buffer[8..9].copy_from_slice(&status.to_le_bytes()[0..1]);
+ let nlos = self.nlos;
+ buffer[9..10].copy_from_slice(&nlos.to_le_bytes()[0..1]);
+ let distance = self.distance;
+ buffer[10..12].copy_from_slice(&distance.to_le_bytes()[0..2]);
+ let aoa_azimuth = self.aoa_azimuth;
+ buffer[12..14].copy_from_slice(&aoa_azimuth.to_le_bytes()[0..2]);
+ let aoa_azimuth_fom = self.aoa_azimuth_fom;
+ buffer[14..15].copy_from_slice(&aoa_azimuth_fom.to_le_bytes()[0..1]);
+ let aoa_elevation = self.aoa_elevation;
+ buffer[15..17].copy_from_slice(&aoa_elevation.to_le_bytes()[0..2]);
+ let aoa_elevation_fom = self.aoa_elevation_fom;
+ buffer[17..18].copy_from_slice(&aoa_elevation_fom.to_le_bytes()[0..1]);
+ let aoa_destination_azimuth = self.aoa_destination_azimuth;
+ buffer[18..20].copy_from_slice(&aoa_destination_azimuth.to_le_bytes()[0..2]);
+ let aoa_destination_azimuth_fom = self.aoa_destination_azimuth_fom;
+ buffer[20..21].copy_from_slice(&aoa_destination_azimuth_fom.to_le_bytes()[0..1]);
+ let aoa_destination_elevation = self.aoa_destination_elevation;
+ buffer[21..23].copy_from_slice(&aoa_destination_elevation.to_le_bytes()[0..2]);
+ let aoa_destination_elevation_fom = self.aoa_destination_elevation_fom;
+ buffer[23..24].copy_from_slice(&aoa_destination_elevation_fom.to_le_bytes()[0..1]);
+ let slot_index = self.slot_index;
+ buffer[24..25].copy_from_slice(&slot_index.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 31;
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct PicaPosition {
+ pub x: u16,
+ pub y: u16,
+ pub z: u16,
+ pub yaw: u16,
+ pub pitch: u8,
+ pub roll: u16,
+}
+impl PicaPosition {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 11 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 2 {
+ return Err(Error::InvalidLengthError {
+ obj: "PicaPosition".to_string(),
+ field: "x".to_string(),
+ wanted: 2,
+ got: bytes.len(),
+ });
+ }
+ let x = u16::from_le_bytes([bytes[0], bytes[1]]);
+ if bytes.len() < 4 {
+ return Err(Error::InvalidLengthError {
+ obj: "PicaPosition".to_string(),
+ field: "y".to_string(),
+ wanted: 4,
+ got: bytes.len(),
+ });
+ }
+ let y = u16::from_le_bytes([bytes[2], bytes[3]]);
+ if bytes.len() < 6 {
+ return Err(Error::InvalidLengthError {
+ obj: "PicaPosition".to_string(),
+ field: "z".to_string(),
+ wanted: 6,
+ got: bytes.len(),
+ });
+ }
+ let z = u16::from_le_bytes([bytes[4], bytes[5]]);
+ if bytes.len() < 8 {
+ return Err(Error::InvalidLengthError {
+ obj: "PicaPosition".to_string(),
+ field: "yaw".to_string(),
+ wanted: 8,
+ got: bytes.len(),
+ });
+ }
+ let yaw = u16::from_le_bytes([bytes[6], bytes[7]]);
+ if bytes.len() < 9 {
+ return Err(Error::InvalidLengthError {
+ obj: "PicaPosition".to_string(),
+ field: "pitch".to_string(),
+ wanted: 9,
+ got: bytes.len(),
+ });
+ }
+ let pitch = u8::from_le_bytes([bytes[8]]);
+ if bytes.len() < 11 {
+ return Err(Error::InvalidLengthError {
+ obj: "PicaPosition".to_string(),
+ field: "roll".to_string(),
+ wanted: 11,
+ got: bytes.len(),
+ });
+ }
+ let roll = u16::from_le_bytes([bytes[9], bytes[10]]);
+ Ok(Self {
+ x,
+ y,
+ z,
+ yaw,
+ pitch,
+ roll,
+ })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let x = self.x;
+ buffer[0..2].copy_from_slice(&x.to_le_bytes()[0..2]);
+ let y = self.y;
+ buffer[2..4].copy_from_slice(&y.to_le_bytes()[0..2]);
+ let z = self.z;
+ buffer[4..6].copy_from_slice(&z.to_le_bytes()[0..2]);
+ let yaw = self.yaw;
+ buffer[6..8].copy_from_slice(&yaw.to_le_bytes()[0..2]);
+ let pitch = self.pitch;
+ buffer[8..9].copy_from_slice(&pitch.to_le_bytes()[0..1]);
+ let roll = self.roll;
+ buffer[9..11].copy_from_slice(&roll.to_le_bytes()[0..2]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 11;
+ ret
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct PowerStats {
+ pub status: StatusCode,
+ pub idle_time_ms: u32,
+ pub tx_time_ms: u32,
+ pub rx_time_ms: u32,
+ pub total_wake_count: u32,
+}
+impl PowerStats {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 17 {
+ return false;
+ }
+ true
+ }
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 1 {
+ return Err(Error::InvalidLengthError {
+ obj: "PowerStats".to_string(),
+ field: "status".to_string(),
+ wanted: 1,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[0]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "PowerStats".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "PowerStats".to_string(),
+ field: "idle_time_ms".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let idle_time_ms = u32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
+ if bytes.len() < 9 {
+ return Err(Error::InvalidLengthError {
+ obj: "PowerStats".to_string(),
+ field: "tx_time_ms".to_string(),
+ wanted: 9,
+ got: bytes.len(),
+ });
+ }
+ let tx_time_ms = u32::from_le_bytes([bytes[5], bytes[6], bytes[7], bytes[8]]);
+ if bytes.len() < 13 {
+ return Err(Error::InvalidLengthError {
+ obj: "PowerStats".to_string(),
+ field: "rx_time_ms".to_string(),
+ wanted: 13,
+ got: bytes.len(),
+ });
+ }
+ let rx_time_ms = u32::from_le_bytes([bytes[9], bytes[10], bytes[11], bytes[12]]);
+ if bytes.len() < 17 {
+ return Err(Error::InvalidLengthError {
+ obj: "PowerStats".to_string(),
+ field: "total_wake_count".to_string(),
+ wanted: 17,
+ got: bytes.len(),
+ });
+ }
+ let total_wake_count = u32::from_le_bytes([bytes[13], bytes[14], bytes[15], bytes[16]]);
+ Ok(Self {
+ status,
+ idle_time_ms,
+ tx_time_ms,
+ rx_time_ms,
+ total_wake_count,
+ })
+ }
+ fn write_to(&self, buffer: &mut [u8]) {
+ let status = self.status.to_u8().unwrap();
+ buffer[0..1].copy_from_slice(&status.to_le_bytes()[0..1]);
+ let idle_time_ms = self.idle_time_ms;
+ buffer[1..5].copy_from_slice(&idle_time_ms.to_le_bytes()[0..4]);
+ let tx_time_ms = self.tx_time_ms;
+ buffer[5..9].copy_from_slice(&tx_time_ms.to_le_bytes()[0..4]);
+ let rx_time_ms = self.rx_time_ms;
+ buffer[9..13].copy_from_slice(&rx_time_ms.to_le_bytes()[0..4]);
+ let total_wake_count = self.total_wake_count;
+ buffer[13..17].copy_from_slice(&total_wake_count.to_le_bytes()[0..4]);
+ }
+ fn get_total_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 17;
+ ret
+ }
+}
+
+#[derive(Debug)]
+enum UciPacketDataChild {
+ UciCommand(Arc<UciCommandData>),
+ UciResponse(Arc<UciResponseData>),
+ UciNotification(Arc<UciNotificationData>),
+ Payload(Bytes),
+ None,
+}
+impl UciPacketDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ UciPacketDataChild::UciCommand(value) => value.get_total_size(),
+ UciPacketDataChild::UciResponse(value) => value.get_total_size(),
+ UciPacketDataChild::UciNotification(value) => value.get_total_size(),
+ UciPacketDataChild::Payload(p) => p.len(),
+ UciPacketDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum UciPacketChild {
+ UciCommand(UciCommandPacket),
+ UciResponse(UciResponsePacket),
+ UciNotification(UciNotificationPacket),
+ Payload(Bytes),
+ None,
+}
+#[derive(Debug)]
+struct UciPacketData {
+ group_id: GroupId,
+ packet_boundary_flag: PacketBoundaryFlag,
+ message_type: MessageType,
+ opcode: u8,
+ child: UciPacketDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct UciPacketPacket {
+ uci_packet: Arc<UciPacketData>,
+}
+#[derive(Debug)]
+pub struct UciPacketBuilder {
+ pub group_id: GroupId,
+ pub packet_boundary_flag: PacketBoundaryFlag,
+ pub message_type: MessageType,
+ pub opcode: u8,
+ pub payload: Option<Bytes>,
+}
+impl UciPacketData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ let group_id = u8::from_le_bytes([bytes[0]]);
+ let group_id = group_id & 0xf;
+ let group_id = GroupId::from_u8(group_id).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "UciPacket".to_string(),
+ field: "group_id".to_string(),
+ value: group_id as u64,
+ type_: "GroupId".to_string(),
+ })?;
+ let packet_boundary_flag = u8::from_le_bytes([bytes[0]]);
+ let packet_boundary_flag = packet_boundary_flag >> 4;
+ let packet_boundary_flag = packet_boundary_flag & 0x1;
+ let packet_boundary_flag =
+ PacketBoundaryFlag::from_u8(packet_boundary_flag).ok_or_else(|| {
+ Error::InvalidEnumValueError {
+ obj: "UciPacket".to_string(),
+ field: "packet_boundary_flag".to_string(),
+ value: packet_boundary_flag as u64,
+ type_: "PacketBoundaryFlag".to_string(),
+ }
+ })?;
+ let message_type = u8::from_le_bytes([bytes[0]]);
+ let message_type = message_type >> 5;
+ let message_type = message_type & 0x7;
+ let message_type =
+ MessageType::from_u8(message_type).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "UciPacket".to_string(),
+ field: "message_type".to_string(),
+ value: message_type as u64,
+ type_: "MessageType".to_string(),
+ })?;
+ let opcode = u8::from_le_bytes([bytes[1]]);
+ let opcode = opcode & 0x3f;
+ if bytes.len() < 4 {
+ return Err(Error::InvalidLengthError {
+ obj: "UciPacket".to_string(),
+ field: "payload_size".to_string(),
+ wanted: 4,
+ got: bytes.len(),
+ });
+ }
+ let payload_size = u8::from_le_bytes([bytes[3]]);
+ let want_ = 4 + (payload_size as usize);
+ if bytes.len() < want_ {
+ return Err(Error::InvalidLengthError {
+ obj: "UciPacket".to_string(),
+ field: "payload".to_string(),
+ wanted: want_,
+ got: bytes.len(),
+ });
+ }
+ let payload: Vec<u8> = bytes[4..(4 + payload_size as usize)].into();
+ let child = match (packet_boundary_flag, message_type) {
+ (PacketBoundaryFlag::Complete, MessageType::Command)
+ if UciCommandData::conforms(&bytes[..]) =>
+ {
+ UciPacketDataChild::UciCommand(Arc::new(UciCommandData::parse(
+ &bytes[..],
+ group_id,
+ opcode,
+ )?))
+ }
+ (PacketBoundaryFlag::Complete, MessageType::Response)
+ if UciResponseData::conforms(&bytes[..]) =>
+ {
+ UciPacketDataChild::UciResponse(Arc::new(UciResponseData::parse(
+ &bytes[..],
+ group_id,
+ opcode,
+ )?))
+ }
+ (PacketBoundaryFlag::Complete, MessageType::Notification)
+ if UciNotificationData::conforms(&bytes[..]) =>
+ {
+ UciPacketDataChild::UciNotification(Arc::new(UciNotificationData::parse(
+ &bytes[..],
+ group_id,
+ opcode,
+ )?))
+ }
+ (_, _) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self {
+ group_id,
+ packet_boundary_flag,
+ message_type,
+ opcode,
+ child,
+ })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let group_id = self.group_id.to_u8().unwrap();
+ let group_id = group_id & 0xf;
+ buffer[0..1].copy_from_slice(&group_id.to_le_bytes()[0..1]);
+ let packet_boundary_flag = self.packet_boundary_flag.to_u8().unwrap();
+ let packet_boundary_flag = packet_boundary_flag & 0x1;
+ let packet_boundary_flag = (packet_boundary_flag << 4) | ((buffer[0] as u8) & 0xf);
+ buffer[0..1].copy_from_slice(&packet_boundary_flag.to_le_bytes()[0..1]);
+ let message_type = self.message_type.to_u8().unwrap();
+ let message_type = message_type & 0x7;
+ let message_type = (message_type << 5) | ((buffer[0] as u8) & 0x1f);
+ buffer[0..1].copy_from_slice(&message_type.to_le_bytes()[0..1]);
+ let opcode = self.opcode;
+ let opcode = opcode & 0x3f;
+ buffer[1..2].copy_from_slice(&opcode.to_le_bytes()[0..1]);
+ let payload_size =
+ u8::try_from(self.child.get_total_size()).expect("payload size did not fit");
+ buffer[3..4].copy_from_slice(&payload_size.to_le_bytes()[0..1]);
+ match &self.child {
+ UciPacketDataChild::UciCommand(value) => value.write_to(buffer),
+ UciPacketDataChild::UciResponse(value) => value.write_to(buffer),
+ UciPacketDataChild::UciNotification(value) => value.write_to(buffer),
+ UciPacketDataChild::Payload(p) => buffer[4..].copy_from_slice(&p[..]),
+ UciPacketDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 4;
+ ret
+ }
+}
+impl Packet for UciPacketPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<UciPacketPacket> for Bytes {
+ fn from(packet: UciPacketPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<UciPacketPacket> for Vec<u8> {
+ fn from(packet: UciPacketPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl UciPacketPacket {
+ pub fn parse(bytes: &[u8]) -> Result<Self> {
+ Ok(Self::new(Arc::new(UciPacketData::parse(bytes)?)).unwrap())
+ }
+ pub fn specialize(&self) -> UciPacketChild {
+ match &self.uci_packet.child {
+ UciPacketDataChild::UciCommand(_) => {
+ UciPacketChild::UciCommand(UciCommandPacket::new(self.uci_packet.clone()).unwrap())
+ }
+ UciPacketDataChild::UciResponse(_) => UciPacketChild::UciResponse(
+ UciResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciPacketDataChild::UciNotification(_) => UciPacketChild::UciNotification(
+ UciNotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciPacketDataChild::Payload(p) => UciPacketChild::Payload(p.clone()),
+ UciPacketDataChild::None => UciPacketChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ Ok(Self { uci_packet })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl UciPacketBuilder {
+ pub fn build(self) -> UciPacketPacket {
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: self.group_id,
+ packet_boundary_flag: self.packet_boundary_flag,
+ message_type: self.message_type,
+ opcode: self.opcode,
+ child: match self.payload {
+ None => UciPacketDataChild::None,
+ Some(bytes) => UciPacketDataChild::Payload(bytes),
+ },
+ });
+ UciPacketPacket::new(uci_packet).unwrap()
+ }
+}
+
+#[derive(Debug)]
+enum UciCommandDataChild {
+ CoreCommand(Arc<CoreCommandData>),
+ SessionCommand(Arc<SessionCommandData>),
+ RangingCommand(Arc<RangingCommandData>),
+ AndroidCommand(Arc<AndroidCommandData>),
+ UciVendor_A_Command(Arc<UciVendor_A_CommandData>),
+ UciVendor_B_Command(Arc<UciVendor_B_CommandData>),
+ UciVendor_E_Command(Arc<UciVendor_E_CommandData>),
+ UciVendor_F_Command(Arc<UciVendor_F_CommandData>),
+ Payload(Bytes),
+ None,
+}
+impl UciCommandDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ UciCommandDataChild::CoreCommand(value) => value.get_total_size(),
+ UciCommandDataChild::SessionCommand(value) => value.get_total_size(),
+ UciCommandDataChild::RangingCommand(value) => value.get_total_size(),
+ UciCommandDataChild::AndroidCommand(value) => value.get_total_size(),
+ UciCommandDataChild::UciVendor_A_Command(value) => value.get_total_size(),
+ UciCommandDataChild::UciVendor_B_Command(value) => value.get_total_size(),
+ UciCommandDataChild::UciVendor_E_Command(value) => value.get_total_size(),
+ UciCommandDataChild::UciVendor_F_Command(value) => value.get_total_size(),
+ UciCommandDataChild::Payload(p) => p.len(),
+ UciCommandDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum UciCommandChild {
+ CoreCommand(CoreCommandPacket),
+ SessionCommand(SessionCommandPacket),
+ RangingCommand(RangingCommandPacket),
+ AndroidCommand(AndroidCommandPacket),
+ UciVendor_A_Command(UciVendor_A_CommandPacket),
+ UciVendor_B_Command(UciVendor_B_CommandPacket),
+ UciVendor_E_Command(UciVendor_E_CommandPacket),
+ UciVendor_F_Command(UciVendor_F_CommandPacket),
+ Payload(Bytes),
+ None,
+}
+#[derive(Debug)]
+struct UciCommandData {
+ child: UciCommandDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct UciCommandPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+}
+#[derive(Debug)]
+pub struct UciCommandBuilder {
+ pub group_id: GroupId,
+ pub opcode: u8,
+ pub payload: Option<Bytes>,
+}
+impl UciCommandData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], group_id: GroupId, opcode: u8) -> Result<Self> {
+ let payload: Vec<u8> = bytes[4..].into();
+ let child = match (group_id) {
+ (GroupId::Core) if CoreCommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::CoreCommand(Arc::new(CoreCommandData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::SessionConfig) if SessionCommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::SessionCommand(Arc::new(SessionCommandData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::RangingSessionControl) if RangingCommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::RangingCommand(Arc::new(RangingCommandData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::VendorAndroid) if AndroidCommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::AndroidCommand(Arc::new(AndroidCommandData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::VendorReservedA) if UciVendor_A_CommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::UciVendor_A_Command(Arc::new(UciVendor_A_CommandData::parse(
+ &bytes[..],
+ )?))
+ }
+ (GroupId::VendorReservedB) if UciVendor_B_CommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::UciVendor_B_Command(Arc::new(UciVendor_B_CommandData::parse(
+ &bytes[..],
+ )?))
+ }
+ (GroupId::VendorReservedE) if UciVendor_E_CommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::UciVendor_E_Command(Arc::new(UciVendor_E_CommandData::parse(
+ &bytes[..],
+ )?))
+ }
+ (GroupId::VendorReservedF) if UciVendor_F_CommandData::conforms(&bytes[..]) => {
+ UciCommandDataChild::UciVendor_F_Command(Arc::new(UciVendor_F_CommandData::parse(
+ &bytes[..],
+ )?))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ UciCommandDataChild::CoreCommand(value) => value.write_to(buffer),
+ UciCommandDataChild::SessionCommand(value) => value.write_to(buffer),
+ UciCommandDataChild::RangingCommand(value) => value.write_to(buffer),
+ UciCommandDataChild::AndroidCommand(value) => value.write_to(buffer),
+ UciCommandDataChild::UciVendor_A_Command(value) => value.write_to(buffer),
+ UciCommandDataChild::UciVendor_B_Command(value) => value.write_to(buffer),
+ UciCommandDataChild::UciVendor_E_Command(value) => value.write_to(buffer),
+ UciCommandDataChild::UciVendor_F_Command(value) => value.write_to(buffer),
+ UciCommandDataChild::Payload(p) => buffer[4..].copy_from_slice(&p[..]),
+ UciCommandDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for UciCommandPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<UciCommandPacket> for Bytes {
+ fn from(packet: UciCommandPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<UciCommandPacket> for Vec<u8> {
+ fn from(packet: UciCommandPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for UciCommandPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl UciCommandPacket {
+ pub fn specialize(&self) -> UciCommandChild {
+ match &self.uci_command.child {
+ UciCommandDataChild::CoreCommand(_) => UciCommandChild::CoreCommand(
+ CoreCommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::SessionCommand(_) => UciCommandChild::SessionCommand(
+ SessionCommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::RangingCommand(_) => UciCommandChild::RangingCommand(
+ RangingCommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::AndroidCommand(_) => UciCommandChild::AndroidCommand(
+ AndroidCommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::UciVendor_A_Command(_) => UciCommandChild::UciVendor_A_Command(
+ UciVendor_A_CommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::UciVendor_B_Command(_) => UciCommandChild::UciVendor_B_Command(
+ UciVendor_B_CommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::UciVendor_E_Command(_) => UciCommandChild::UciVendor_E_Command(
+ UciVendor_E_CommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::UciVendor_F_Command(_) => UciCommandChild::UciVendor_F_Command(
+ UciVendor_F_CommandPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciCommandDataChild::Payload(p) => UciCommandChild::Payload(p.clone()),
+ UciCommandDataChild::None => UciCommandChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for UciCommandPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl UciCommandBuilder {
+ pub fn build(self) -> UciCommandPacket {
+ let uci_command = Arc::new(UciCommandData {
+ child: match self.payload {
+ None => UciCommandDataChild::None,
+ Some(bytes) => UciCommandDataChild::Payload(bytes),
+ },
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: self.group_id,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ UciCommandPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for UciCommandBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum UciResponseDataChild {
+ CoreResponse(Arc<CoreResponseData>),
+ SessionResponse(Arc<SessionResponseData>),
+ RangingResponse(Arc<RangingResponseData>),
+ AndroidResponse(Arc<AndroidResponseData>),
+ UciVendor_A_Response(Arc<UciVendor_A_ResponseData>),
+ UciVendor_B_Response(Arc<UciVendor_B_ResponseData>),
+ UciVendor_E_Response(Arc<UciVendor_E_ResponseData>),
+ UciVendor_F_Response(Arc<UciVendor_F_ResponseData>),
+ Payload(Bytes),
+ None,
+}
+impl UciResponseDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ UciResponseDataChild::CoreResponse(value) => value.get_total_size(),
+ UciResponseDataChild::SessionResponse(value) => value.get_total_size(),
+ UciResponseDataChild::RangingResponse(value) => value.get_total_size(),
+ UciResponseDataChild::AndroidResponse(value) => value.get_total_size(),
+ UciResponseDataChild::UciVendor_A_Response(value) => value.get_total_size(),
+ UciResponseDataChild::UciVendor_B_Response(value) => value.get_total_size(),
+ UciResponseDataChild::UciVendor_E_Response(value) => value.get_total_size(),
+ UciResponseDataChild::UciVendor_F_Response(value) => value.get_total_size(),
+ UciResponseDataChild::Payload(p) => p.len(),
+ UciResponseDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum UciResponseChild {
+ CoreResponse(CoreResponsePacket),
+ SessionResponse(SessionResponsePacket),
+ RangingResponse(RangingResponsePacket),
+ AndroidResponse(AndroidResponsePacket),
+ UciVendor_A_Response(UciVendor_A_ResponsePacket),
+ UciVendor_B_Response(UciVendor_B_ResponsePacket),
+ UciVendor_E_Response(UciVendor_E_ResponsePacket),
+ UciVendor_F_Response(UciVendor_F_ResponsePacket),
+ Payload(Bytes),
+ None,
+}
+#[derive(Debug)]
+struct UciResponseData {
+ child: UciResponseDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct UciResponsePacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+}
+#[derive(Debug)]
+pub struct UciResponseBuilder {
+ pub group_id: GroupId,
+ pub opcode: u8,
+ pub payload: Option<Bytes>,
+}
+impl UciResponseData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], group_id: GroupId, opcode: u8) -> Result<Self> {
+ let payload: Vec<u8> = bytes[4..].into();
+ let child = match (group_id) {
+ (GroupId::Core) if CoreResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::CoreResponse(Arc::new(CoreResponseData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::SessionConfig) if SessionResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::SessionResponse(Arc::new(SessionResponseData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::RangingSessionControl) if RangingResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::RangingResponse(Arc::new(RangingResponseData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::VendorAndroid) if AndroidResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::AndroidResponse(Arc::new(AndroidResponseData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::VendorReservedA) if UciVendor_A_ResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::UciVendor_A_Response(Arc::new(
+ UciVendor_A_ResponseData::parse(&bytes[..])?,
+ ))
+ }
+ (GroupId::VendorReservedB) if UciVendor_B_ResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::UciVendor_B_Response(Arc::new(
+ UciVendor_B_ResponseData::parse(&bytes[..])?,
+ ))
+ }
+ (GroupId::VendorReservedE) if UciVendor_E_ResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::UciVendor_E_Response(Arc::new(
+ UciVendor_E_ResponseData::parse(&bytes[..])?,
+ ))
+ }
+ (GroupId::VendorReservedF) if UciVendor_F_ResponseData::conforms(&bytes[..]) => {
+ UciResponseDataChild::UciVendor_F_Response(Arc::new(
+ UciVendor_F_ResponseData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ UciResponseDataChild::CoreResponse(value) => value.write_to(buffer),
+ UciResponseDataChild::SessionResponse(value) => value.write_to(buffer),
+ UciResponseDataChild::RangingResponse(value) => value.write_to(buffer),
+ UciResponseDataChild::AndroidResponse(value) => value.write_to(buffer),
+ UciResponseDataChild::UciVendor_A_Response(value) => value.write_to(buffer),
+ UciResponseDataChild::UciVendor_B_Response(value) => value.write_to(buffer),
+ UciResponseDataChild::UciVendor_E_Response(value) => value.write_to(buffer),
+ UciResponseDataChild::UciVendor_F_Response(value) => value.write_to(buffer),
+ UciResponseDataChild::Payload(p) => buffer[4..].copy_from_slice(&p[..]),
+ UciResponseDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for UciResponsePacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<UciResponsePacket> for Bytes {
+ fn from(packet: UciResponsePacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<UciResponsePacket> for Vec<u8> {
+ fn from(packet: UciResponsePacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for UciResponsePacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl UciResponsePacket {
+ pub fn specialize(&self) -> UciResponseChild {
+ match &self.uci_response.child {
+ UciResponseDataChild::CoreResponse(_) => UciResponseChild::CoreResponse(
+ CoreResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciResponseDataChild::SessionResponse(_) => UciResponseChild::SessionResponse(
+ SessionResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciResponseDataChild::RangingResponse(_) => UciResponseChild::RangingResponse(
+ RangingResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciResponseDataChild::AndroidResponse(_) => UciResponseChild::AndroidResponse(
+ AndroidResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ UciResponseDataChild::UciVendor_A_Response(_) => {
+ UciResponseChild::UciVendor_A_Response(
+ UciVendor_A_ResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciResponseDataChild::UciVendor_B_Response(_) => {
+ UciResponseChild::UciVendor_B_Response(
+ UciVendor_B_ResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciResponseDataChild::UciVendor_E_Response(_) => {
+ UciResponseChild::UciVendor_E_Response(
+ UciVendor_E_ResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciResponseDataChild::UciVendor_F_Response(_) => {
+ UciResponseChild::UciVendor_F_Response(
+ UciVendor_F_ResponsePacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciResponseDataChild::Payload(p) => UciResponseChild::Payload(p.clone()),
+ UciResponseDataChild::None => UciResponseChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for UciResponsePacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl UciResponseBuilder {
+ pub fn build(self) -> UciResponsePacket {
+ let uci_response = Arc::new(UciResponseData {
+ child: match self.payload {
+ None => UciResponseDataChild::None,
+ Some(bytes) => UciResponseDataChild::Payload(bytes),
+ },
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: self.group_id,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ UciResponsePacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for UciResponseBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum UciNotificationDataChild {
+ CoreNotification(Arc<CoreNotificationData>),
+ SessionNotification(Arc<SessionNotificationData>),
+ RangingNotification(Arc<RangingNotificationData>),
+ AndroidNotification(Arc<AndroidNotificationData>),
+ UciVendor_A_Notification(Arc<UciVendor_A_NotificationData>),
+ UciVendor_B_Notification(Arc<UciVendor_B_NotificationData>),
+ UciVendor_E_Notification(Arc<UciVendor_E_NotificationData>),
+ UciVendor_F_Notification(Arc<UciVendor_F_NotificationData>),
+ Payload(Bytes),
+ None,
+}
+impl UciNotificationDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ UciNotificationDataChild::CoreNotification(value) => value.get_total_size(),
+ UciNotificationDataChild::SessionNotification(value) => value.get_total_size(),
+ UciNotificationDataChild::RangingNotification(value) => value.get_total_size(),
+ UciNotificationDataChild::AndroidNotification(value) => value.get_total_size(),
+ UciNotificationDataChild::UciVendor_A_Notification(value) => value.get_total_size(),
+ UciNotificationDataChild::UciVendor_B_Notification(value) => value.get_total_size(),
+ UciNotificationDataChild::UciVendor_E_Notification(value) => value.get_total_size(),
+ UciNotificationDataChild::UciVendor_F_Notification(value) => value.get_total_size(),
+ UciNotificationDataChild::Payload(p) => p.len(),
+ UciNotificationDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum UciNotificationChild {
+ CoreNotification(CoreNotificationPacket),
+ SessionNotification(SessionNotificationPacket),
+ RangingNotification(RangingNotificationPacket),
+ AndroidNotification(AndroidNotificationPacket),
+ UciVendor_A_Notification(UciVendor_A_NotificationPacket),
+ UciVendor_B_Notification(UciVendor_B_NotificationPacket),
+ UciVendor_E_Notification(UciVendor_E_NotificationPacket),
+ UciVendor_F_Notification(UciVendor_F_NotificationPacket),
+ Payload(Bytes),
+ None,
+}
+#[derive(Debug)]
+struct UciNotificationData {
+ child: UciNotificationDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct UciNotificationPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_notification: Arc<UciNotificationData>,
+}
+#[derive(Debug)]
+pub struct UciNotificationBuilder {
+ pub group_id: GroupId,
+ pub opcode: u8,
+ pub payload: Option<Bytes>,
+}
+impl UciNotificationData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], group_id: GroupId, opcode: u8) -> Result<Self> {
+ let payload: Vec<u8> = bytes[4..].into();
+ let child = match (group_id) {
+ (GroupId::Core) if CoreNotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::CoreNotification(Arc::new(CoreNotificationData::parse(
+ &bytes[..],
+ opcode,
+ )?))
+ }
+ (GroupId::SessionConfig) if SessionNotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::SessionNotification(Arc::new(
+ SessionNotificationData::parse(&bytes[..], opcode)?,
+ ))
+ }
+ (GroupId::RangingSessionControl) if RangingNotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::RangingNotification(Arc::new(
+ RangingNotificationData::parse(&bytes[..], opcode)?,
+ ))
+ }
+ (GroupId::VendorAndroid) if AndroidNotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::AndroidNotification(Arc::new(
+ AndroidNotificationData::parse(&bytes[..])?,
+ ))
+ }
+ (GroupId::VendorReservedA) if UciVendor_A_NotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::UciVendor_A_Notification(Arc::new(
+ UciVendor_A_NotificationData::parse(&bytes[..])?,
+ ))
+ }
+ (GroupId::VendorReservedB) if UciVendor_B_NotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::UciVendor_B_Notification(Arc::new(
+ UciVendor_B_NotificationData::parse(&bytes[..])?,
+ ))
+ }
+ (GroupId::VendorReservedE) if UciVendor_E_NotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::UciVendor_E_Notification(Arc::new(
+ UciVendor_E_NotificationData::parse(&bytes[..])?,
+ ))
+ }
+ (GroupId::VendorReservedF) if UciVendor_F_NotificationData::conforms(&bytes[..]) => {
+ UciNotificationDataChild::UciVendor_F_Notification(Arc::new(
+ UciVendor_F_NotificationData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ UciNotificationDataChild::CoreNotification(value) => value.write_to(buffer),
+ UciNotificationDataChild::SessionNotification(value) => value.write_to(buffer),
+ UciNotificationDataChild::RangingNotification(value) => value.write_to(buffer),
+ UciNotificationDataChild::AndroidNotification(value) => value.write_to(buffer),
+ UciNotificationDataChild::UciVendor_A_Notification(value) => value.write_to(buffer),
+ UciNotificationDataChild::UciVendor_B_Notification(value) => value.write_to(buffer),
+ UciNotificationDataChild::UciVendor_E_Notification(value) => value.write_to(buffer),
+ UciNotificationDataChild::UciVendor_F_Notification(value) => value.write_to(buffer),
+ UciNotificationDataChild::Payload(p) => buffer[4..].copy_from_slice(&p[..]),
+ UciNotificationDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for UciNotificationPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<UciNotificationPacket> for Bytes {
+ fn from(packet: UciNotificationPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<UciNotificationPacket> for Vec<u8> {
+ fn from(packet: UciNotificationPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for UciNotificationPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl UciNotificationPacket {
+ pub fn specialize(&self) -> UciNotificationChild {
+ match &self.uci_notification.child {
+ UciNotificationDataChild::CoreNotification(_) => {
+ UciNotificationChild::CoreNotification(
+ CoreNotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::SessionNotification(_) => {
+ UciNotificationChild::SessionNotification(
+ SessionNotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::RangingNotification(_) => {
+ UciNotificationChild::RangingNotification(
+ RangingNotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::AndroidNotification(_) => {
+ UciNotificationChild::AndroidNotification(
+ AndroidNotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::UciVendor_A_Notification(_) => {
+ UciNotificationChild::UciVendor_A_Notification(
+ UciVendor_A_NotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::UciVendor_B_Notification(_) => {
+ UciNotificationChild::UciVendor_B_Notification(
+ UciVendor_B_NotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::UciVendor_E_Notification(_) => {
+ UciNotificationChild::UciVendor_E_Notification(
+ UciVendor_E_NotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::UciVendor_F_Notification(_) => {
+ UciNotificationChild::UciVendor_F_Notification(
+ UciVendor_F_NotificationPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ UciNotificationDataChild::Payload(p) => UciNotificationChild::Payload(p.clone()),
+ UciNotificationDataChild::None => UciNotificationChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_notification = match &uci_packet.child {
+ UciPacketDataChild::UciNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciNotification"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_notification,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for UciNotificationPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl UciNotificationBuilder {
+ pub fn build(self) -> UciNotificationPacket {
+ let uci_notification = Arc::new(UciNotificationData {
+ child: match self.payload {
+ None => UciNotificationDataChild::None,
+ Some(bytes) => UciNotificationDataChild::Payload(bytes),
+ },
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: self.group_id,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Notification,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciNotification(uci_notification),
+ });
+ UciNotificationPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for UciNotificationBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum CoreCommandDataChild {
+ DeviceResetCmd(Arc<DeviceResetCmdData>),
+ GetDeviceInfoCmd(Arc<GetDeviceInfoCmdData>),
+ GetCapsInfoCmd(Arc<GetCapsInfoCmdData>),
+ SetConfigCmd(Arc<SetConfigCmdData>),
+ GetConfigCmd(Arc<GetConfigCmdData>),
+ None,
+}
+impl CoreCommandDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ CoreCommandDataChild::DeviceResetCmd(value) => value.get_total_size(),
+ CoreCommandDataChild::GetDeviceInfoCmd(value) => value.get_total_size(),
+ CoreCommandDataChild::GetCapsInfoCmd(value) => value.get_total_size(),
+ CoreCommandDataChild::SetConfigCmd(value) => value.get_total_size(),
+ CoreCommandDataChild::GetConfigCmd(value) => value.get_total_size(),
+ CoreCommandDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum CoreCommandChild {
+ DeviceResetCmd(DeviceResetCmdPacket),
+ GetDeviceInfoCmd(GetDeviceInfoCmdPacket),
+ GetCapsInfoCmd(GetCapsInfoCmdPacket),
+ SetConfigCmd(SetConfigCmdPacket),
+ GetConfigCmd(GetConfigCmdPacket),
+ None,
+}
+#[derive(Debug)]
+struct CoreCommandData {
+ child: CoreCommandDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct CoreCommandPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ core_command: Arc<CoreCommandData>,
+}
+#[derive(Debug)]
+pub struct CoreCommandBuilder {
+ pub opcode: u8,
+}
+impl CoreCommandData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (0) if DeviceResetCmdData::conforms(&bytes[..]) => {
+ CoreCommandDataChild::DeviceResetCmd(Arc::new(DeviceResetCmdData::parse(
+ &bytes[..],
+ )?))
+ }
+ (2) if GetDeviceInfoCmdData::conforms(&bytes[..]) => {
+ CoreCommandDataChild::GetDeviceInfoCmd(Arc::new(GetDeviceInfoCmdData::parse(
+ &bytes[..],
+ )?))
+ }
+ (3) if GetCapsInfoCmdData::conforms(&bytes[..]) => {
+ CoreCommandDataChild::GetCapsInfoCmd(Arc::new(GetCapsInfoCmdData::parse(
+ &bytes[..],
+ )?))
+ }
+ (4) if SetConfigCmdData::conforms(&bytes[..]) => {
+ CoreCommandDataChild::SetConfigCmd(Arc::new(SetConfigCmdData::parse(&bytes[..])?))
+ }
+ (5) if GetConfigCmdData::conforms(&bytes[..]) => {
+ CoreCommandDataChild::GetConfigCmd(Arc::new(GetConfigCmdData::parse(&bytes[..])?))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ CoreCommandDataChild::DeviceResetCmd(value) => value.write_to(buffer),
+ CoreCommandDataChild::GetDeviceInfoCmd(value) => value.write_to(buffer),
+ CoreCommandDataChild::GetCapsInfoCmd(value) => value.write_to(buffer),
+ CoreCommandDataChild::SetConfigCmd(value) => value.write_to(buffer),
+ CoreCommandDataChild::GetConfigCmd(value) => value.write_to(buffer),
+ CoreCommandDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for CoreCommandPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<CoreCommandPacket> for Bytes {
+ fn from(packet: CoreCommandPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<CoreCommandPacket> for Vec<u8> {
+ fn from(packet: CoreCommandPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for CoreCommandPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl CoreCommandPacket {
+ pub fn specialize(&self) -> CoreCommandChild {
+ match &self.core_command.child {
+ CoreCommandDataChild::DeviceResetCmd(_) => CoreCommandChild::DeviceResetCmd(
+ DeviceResetCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreCommandDataChild::GetDeviceInfoCmd(_) => CoreCommandChild::GetDeviceInfoCmd(
+ GetDeviceInfoCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreCommandDataChild::GetCapsInfoCmd(_) => CoreCommandChild::GetCapsInfoCmd(
+ GetCapsInfoCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreCommandDataChild::SetConfigCmd(_) => CoreCommandChild::SetConfigCmd(
+ SetConfigCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreCommandDataChild::GetConfigCmd(_) => CoreCommandChild::GetConfigCmd(
+ GetConfigCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreCommandDataChild::None => CoreCommandChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let core_command = match &uci_command.child {
+ UciCommandDataChild::CoreCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreCommand"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ core_command,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for CoreCommandPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for CoreCommandPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl CoreCommandBuilder {
+ pub fn build(self) -> CoreCommandPacket {
+ let core_command = Arc::new(CoreCommandData {
+ child: CoreCommandDataChild::None,
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::CoreCommand(core_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ CoreCommandPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for CoreCommandBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for CoreCommandBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum CoreResponseDataChild {
+ DeviceResetRsp(Arc<DeviceResetRspData>),
+ GetDeviceInfoRsp(Arc<GetDeviceInfoRspData>),
+ GetCapsInfoRsp(Arc<GetCapsInfoRspData>),
+ SetConfigRsp(Arc<SetConfigRspData>),
+ GetConfigRsp(Arc<GetConfigRspData>),
+ None,
+}
+impl CoreResponseDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ CoreResponseDataChild::DeviceResetRsp(value) => value.get_total_size(),
+ CoreResponseDataChild::GetDeviceInfoRsp(value) => value.get_total_size(),
+ CoreResponseDataChild::GetCapsInfoRsp(value) => value.get_total_size(),
+ CoreResponseDataChild::SetConfigRsp(value) => value.get_total_size(),
+ CoreResponseDataChild::GetConfigRsp(value) => value.get_total_size(),
+ CoreResponseDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum CoreResponseChild {
+ DeviceResetRsp(DeviceResetRspPacket),
+ GetDeviceInfoRsp(GetDeviceInfoRspPacket),
+ GetCapsInfoRsp(GetCapsInfoRspPacket),
+ SetConfigRsp(SetConfigRspPacket),
+ GetConfigRsp(GetConfigRspPacket),
+ None,
+}
+#[derive(Debug)]
+struct CoreResponseData {
+ child: CoreResponseDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct CoreResponsePacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ core_response: Arc<CoreResponseData>,
+}
+#[derive(Debug)]
+pub struct CoreResponseBuilder {
+ pub opcode: u8,
+}
+impl CoreResponseData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (0) if DeviceResetRspData::conforms(&bytes[..]) => {
+ CoreResponseDataChild::DeviceResetRsp(Arc::new(DeviceResetRspData::parse(
+ &bytes[..],
+ )?))
+ }
+ (2) if GetDeviceInfoRspData::conforms(&bytes[..]) => {
+ CoreResponseDataChild::GetDeviceInfoRsp(Arc::new(GetDeviceInfoRspData::parse(
+ &bytes[..],
+ )?))
+ }
+ (3) if GetCapsInfoRspData::conforms(&bytes[..]) => {
+ CoreResponseDataChild::GetCapsInfoRsp(Arc::new(GetCapsInfoRspData::parse(
+ &bytes[..],
+ )?))
+ }
+ (4) if SetConfigRspData::conforms(&bytes[..]) => {
+ CoreResponseDataChild::SetConfigRsp(Arc::new(SetConfigRspData::parse(&bytes[..])?))
+ }
+ (5) if GetConfigRspData::conforms(&bytes[..]) => {
+ CoreResponseDataChild::GetConfigRsp(Arc::new(GetConfigRspData::parse(&bytes[..])?))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ CoreResponseDataChild::DeviceResetRsp(value) => value.write_to(buffer),
+ CoreResponseDataChild::GetDeviceInfoRsp(value) => value.write_to(buffer),
+ CoreResponseDataChild::GetCapsInfoRsp(value) => value.write_to(buffer),
+ CoreResponseDataChild::SetConfigRsp(value) => value.write_to(buffer),
+ CoreResponseDataChild::GetConfigRsp(value) => value.write_to(buffer),
+ CoreResponseDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for CoreResponsePacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<CoreResponsePacket> for Bytes {
+ fn from(packet: CoreResponsePacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<CoreResponsePacket> for Vec<u8> {
+ fn from(packet: CoreResponsePacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for CoreResponsePacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl CoreResponsePacket {
+ pub fn specialize(&self) -> CoreResponseChild {
+ match &self.core_response.child {
+ CoreResponseDataChild::DeviceResetRsp(_) => CoreResponseChild::DeviceResetRsp(
+ DeviceResetRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreResponseDataChild::GetDeviceInfoRsp(_) => CoreResponseChild::GetDeviceInfoRsp(
+ GetDeviceInfoRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreResponseDataChild::GetCapsInfoRsp(_) => CoreResponseChild::GetCapsInfoRsp(
+ GetCapsInfoRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreResponseDataChild::SetConfigRsp(_) => CoreResponseChild::SetConfigRsp(
+ SetConfigRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreResponseDataChild::GetConfigRsp(_) => CoreResponseChild::GetConfigRsp(
+ GetConfigRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreResponseDataChild::None => CoreResponseChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let core_response = match &uci_response.child {
+ UciResponseDataChild::CoreResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreResponse"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ core_response,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for CoreResponsePacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for CoreResponsePacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl CoreResponseBuilder {
+ pub fn build(self) -> CoreResponsePacket {
+ let core_response = Arc::new(CoreResponseData {
+ child: CoreResponseDataChild::None,
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::CoreResponse(core_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ CoreResponsePacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for CoreResponseBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for CoreResponseBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum CoreNotificationDataChild {
+ DeviceStatusNtf(Arc<DeviceStatusNtfData>),
+ GenericError(Arc<GenericErrorData>),
+ None,
+}
+impl CoreNotificationDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ CoreNotificationDataChild::DeviceStatusNtf(value) => value.get_total_size(),
+ CoreNotificationDataChild::GenericError(value) => value.get_total_size(),
+ CoreNotificationDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum CoreNotificationChild {
+ DeviceStatusNtf(DeviceStatusNtfPacket),
+ GenericError(GenericErrorPacket),
+ None,
+}
+#[derive(Debug)]
+struct CoreNotificationData {
+ child: CoreNotificationDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct CoreNotificationPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_notification: Arc<UciNotificationData>,
+ core_notification: Arc<CoreNotificationData>,
+}
+#[derive(Debug)]
+pub struct CoreNotificationBuilder {
+ pub opcode: u8,
+}
+impl CoreNotificationData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (1) if DeviceStatusNtfData::conforms(&bytes[..]) => {
+ CoreNotificationDataChild::DeviceStatusNtf(Arc::new(DeviceStatusNtfData::parse(
+ &bytes[..],
+ )?))
+ }
+ (7) if GenericErrorData::conforms(&bytes[..]) => {
+ CoreNotificationDataChild::GenericError(Arc::new(GenericErrorData::parse(
+ &bytes[..],
+ )?))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ CoreNotificationDataChild::DeviceStatusNtf(value) => value.write_to(buffer),
+ CoreNotificationDataChild::GenericError(value) => value.write_to(buffer),
+ CoreNotificationDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for CoreNotificationPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<CoreNotificationPacket> for Bytes {
+ fn from(packet: CoreNotificationPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<CoreNotificationPacket> for Vec<u8> {
+ fn from(packet: CoreNotificationPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for CoreNotificationPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl CoreNotificationPacket {
+ pub fn specialize(&self) -> CoreNotificationChild {
+ match &self.core_notification.child {
+ CoreNotificationDataChild::DeviceStatusNtf(_) => {
+ CoreNotificationChild::DeviceStatusNtf(
+ DeviceStatusNtfPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ CoreNotificationDataChild::GenericError(_) => CoreNotificationChild::GenericError(
+ GenericErrorPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ CoreNotificationDataChild::None => CoreNotificationChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_notification = match &uci_packet.child {
+ UciPacketDataChild::UciNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciNotification"),
+ };
+ let core_notification = match &uci_notification.child {
+ UciNotificationDataChild::CoreNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreNotification"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_notification,
+ core_notification,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for CoreNotificationPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciNotificationPacket> for CoreNotificationPacket {
+ fn into(self) -> UciNotificationPacket {
+ UciNotificationPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl CoreNotificationBuilder {
+ pub fn build(self) -> CoreNotificationPacket {
+ let core_notification = Arc::new(CoreNotificationData {
+ child: CoreNotificationDataChild::None,
+ });
+ let uci_notification = Arc::new(UciNotificationData {
+ child: UciNotificationDataChild::CoreNotification(core_notification),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Notification,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciNotification(uci_notification),
+ });
+ CoreNotificationPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for CoreNotificationBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciNotificationPacket> for CoreNotificationBuilder {
+ fn into(self) -> UciNotificationPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum SessionCommandDataChild {
+ SessionInitCmd(Arc<SessionInitCmdData>),
+ SessionDeinitCmd(Arc<SessionDeinitCmdData>),
+ SessionSetAppConfigCmd(Arc<SessionSetAppConfigCmdData>),
+ SessionGetAppConfigCmd(Arc<SessionGetAppConfigCmdData>),
+ SessionGetCountCmd(Arc<SessionGetCountCmdData>),
+ SessionGetStateCmd(Arc<SessionGetStateCmdData>),
+ SessionUpdateControllerMulticastListCmd(Arc<SessionUpdateControllerMulticastListCmdData>),
+ None,
+}
+impl SessionCommandDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ SessionCommandDataChild::SessionInitCmd(value) => value.get_total_size(),
+ SessionCommandDataChild::SessionDeinitCmd(value) => value.get_total_size(),
+ SessionCommandDataChild::SessionSetAppConfigCmd(value) => value.get_total_size(),
+ SessionCommandDataChild::SessionGetAppConfigCmd(value) => value.get_total_size(),
+ SessionCommandDataChild::SessionGetCountCmd(value) => value.get_total_size(),
+ SessionCommandDataChild::SessionGetStateCmd(value) => value.get_total_size(),
+ SessionCommandDataChild::SessionUpdateControllerMulticastListCmd(value) => {
+ value.get_total_size()
+ }
+ SessionCommandDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum SessionCommandChild {
+ SessionInitCmd(SessionInitCmdPacket),
+ SessionDeinitCmd(SessionDeinitCmdPacket),
+ SessionSetAppConfigCmd(SessionSetAppConfigCmdPacket),
+ SessionGetAppConfigCmd(SessionGetAppConfigCmdPacket),
+ SessionGetCountCmd(SessionGetCountCmdPacket),
+ SessionGetStateCmd(SessionGetStateCmdPacket),
+ SessionUpdateControllerMulticastListCmd(SessionUpdateControllerMulticastListCmdPacket),
+ None,
+}
+#[derive(Debug)]
+struct SessionCommandData {
+ child: SessionCommandDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct SessionCommandPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ session_command: Arc<SessionCommandData>,
+}
+#[derive(Debug)]
+pub struct SessionCommandBuilder {
+ pub opcode: u8,
+}
+impl SessionCommandData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (0) if SessionInitCmdData::conforms(&bytes[..]) => {
+ SessionCommandDataChild::SessionInitCmd(Arc::new(SessionInitCmdData::parse(
+ &bytes[..],
+ )?))
+ }
+ (1) if SessionDeinitCmdData::conforms(&bytes[..]) => {
+ SessionCommandDataChild::SessionDeinitCmd(Arc::new(SessionDeinitCmdData::parse(
+ &bytes[..],
+ )?))
+ }
+ (3) if SessionSetAppConfigCmdData::conforms(&bytes[..]) => {
+ SessionCommandDataChild::SessionSetAppConfigCmd(Arc::new(
+ SessionSetAppConfigCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (4) if SessionGetAppConfigCmdData::conforms(&bytes[..]) => {
+ SessionCommandDataChild::SessionGetAppConfigCmd(Arc::new(
+ SessionGetAppConfigCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (5) if SessionGetCountCmdData::conforms(&bytes[..]) => {
+ SessionCommandDataChild::SessionGetCountCmd(Arc::new(
+ SessionGetCountCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (6) if SessionGetStateCmdData::conforms(&bytes[..]) => {
+ SessionCommandDataChild::SessionGetStateCmd(Arc::new(
+ SessionGetStateCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (7) if SessionUpdateControllerMulticastListCmdData::conforms(&bytes[..]) => {
+ SessionCommandDataChild::SessionUpdateControllerMulticastListCmd(Arc::new(
+ SessionUpdateControllerMulticastListCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ SessionCommandDataChild::SessionInitCmd(value) => value.write_to(buffer),
+ SessionCommandDataChild::SessionDeinitCmd(value) => value.write_to(buffer),
+ SessionCommandDataChild::SessionSetAppConfigCmd(value) => value.write_to(buffer),
+ SessionCommandDataChild::SessionGetAppConfigCmd(value) => value.write_to(buffer),
+ SessionCommandDataChild::SessionGetCountCmd(value) => value.write_to(buffer),
+ SessionCommandDataChild::SessionGetStateCmd(value) => value.write_to(buffer),
+ SessionCommandDataChild::SessionUpdateControllerMulticastListCmd(value) => {
+ value.write_to(buffer)
+ }
+ SessionCommandDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for SessionCommandPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<SessionCommandPacket> for Bytes {
+ fn from(packet: SessionCommandPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<SessionCommandPacket> for Vec<u8> {
+ fn from(packet: SessionCommandPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for SessionCommandPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl SessionCommandPacket {
+ pub fn specialize(&self) -> SessionCommandChild {
+ match &self.session_command.child {
+ SessionCommandDataChild::SessionInitCmd(_) => SessionCommandChild::SessionInitCmd(
+ SessionInitCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ SessionCommandDataChild::SessionDeinitCmd(_) => SessionCommandChild::SessionDeinitCmd(
+ SessionDeinitCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ SessionCommandDataChild::SessionSetAppConfigCmd(_) => {
+ SessionCommandChild::SessionSetAppConfigCmd(
+ SessionSetAppConfigCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionCommandDataChild::SessionGetAppConfigCmd(_) => {
+ SessionCommandChild::SessionGetAppConfigCmd(
+ SessionGetAppConfigCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionCommandDataChild::SessionGetCountCmd(_) => {
+ SessionCommandChild::SessionGetCountCmd(
+ SessionGetCountCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionCommandDataChild::SessionGetStateCmd(_) => {
+ SessionCommandChild::SessionGetStateCmd(
+ SessionGetStateCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionCommandDataChild::SessionUpdateControllerMulticastListCmd(_) => {
+ SessionCommandChild::SessionUpdateControllerMulticastListCmd(
+ SessionUpdateControllerMulticastListCmdPacket::new(self.uci_packet.clone())
+ .unwrap(),
+ )
+ }
+ SessionCommandDataChild::None => SessionCommandChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let session_command = match &uci_command.child {
+ UciCommandDataChild::SessionCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not SessionCommand"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ session_command,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for SessionCommandPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for SessionCommandPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl SessionCommandBuilder {
+ pub fn build(self) -> SessionCommandPacket {
+ let session_command = Arc::new(SessionCommandData {
+ child: SessionCommandDataChild::None,
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::SessionCommand(session_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::SessionConfig,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ SessionCommandPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for SessionCommandBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for SessionCommandBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum SessionResponseDataChild {
+ SessionInitRsp(Arc<SessionInitRspData>),
+ SessionDeinitRsp(Arc<SessionDeinitRspData>),
+ SessionSetAppConfigRsp(Arc<SessionSetAppConfigRspData>),
+ SessionGetAppConfigRsp(Arc<SessionGetAppConfigRspData>),
+ SessionGetCountRsp(Arc<SessionGetCountRspData>),
+ SessionGetStateRsp(Arc<SessionGetStateRspData>),
+ SessionUpdateControllerMulticastListRsp(Arc<SessionUpdateControllerMulticastListRspData>),
+ None,
+}
+impl SessionResponseDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ SessionResponseDataChild::SessionInitRsp(value) => value.get_total_size(),
+ SessionResponseDataChild::SessionDeinitRsp(value) => value.get_total_size(),
+ SessionResponseDataChild::SessionSetAppConfigRsp(value) => value.get_total_size(),
+ SessionResponseDataChild::SessionGetAppConfigRsp(value) => value.get_total_size(),
+ SessionResponseDataChild::SessionGetCountRsp(value) => value.get_total_size(),
+ SessionResponseDataChild::SessionGetStateRsp(value) => value.get_total_size(),
+ SessionResponseDataChild::SessionUpdateControllerMulticastListRsp(value) => {
+ value.get_total_size()
+ }
+ SessionResponseDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum SessionResponseChild {
+ SessionInitRsp(SessionInitRspPacket),
+ SessionDeinitRsp(SessionDeinitRspPacket),
+ SessionSetAppConfigRsp(SessionSetAppConfigRspPacket),
+ SessionGetAppConfigRsp(SessionGetAppConfigRspPacket),
+ SessionGetCountRsp(SessionGetCountRspPacket),
+ SessionGetStateRsp(SessionGetStateRspPacket),
+ SessionUpdateControllerMulticastListRsp(SessionUpdateControllerMulticastListRspPacket),
+ None,
+}
+#[derive(Debug)]
+struct SessionResponseData {
+ child: SessionResponseDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct SessionResponsePacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ session_response: Arc<SessionResponseData>,
+}
+#[derive(Debug)]
+pub struct SessionResponseBuilder {
+ pub opcode: u8,
+}
+impl SessionResponseData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (0) if SessionInitRspData::conforms(&bytes[..]) => {
+ SessionResponseDataChild::SessionInitRsp(Arc::new(SessionInitRspData::parse(
+ &bytes[..],
+ )?))
+ }
+ (1) if SessionDeinitRspData::conforms(&bytes[..]) => {
+ SessionResponseDataChild::SessionDeinitRsp(Arc::new(SessionDeinitRspData::parse(
+ &bytes[..],
+ )?))
+ }
+ (3) if SessionSetAppConfigRspData::conforms(&bytes[..]) => {
+ SessionResponseDataChild::SessionSetAppConfigRsp(Arc::new(
+ SessionSetAppConfigRspData::parse(&bytes[..])?,
+ ))
+ }
+ (4) if SessionGetAppConfigRspData::conforms(&bytes[..]) => {
+ SessionResponseDataChild::SessionGetAppConfigRsp(Arc::new(
+ SessionGetAppConfigRspData::parse(&bytes[..])?,
+ ))
+ }
+ (5) if SessionGetCountRspData::conforms(&bytes[..]) => {
+ SessionResponseDataChild::SessionGetCountRsp(Arc::new(
+ SessionGetCountRspData::parse(&bytes[..])?,
+ ))
+ }
+ (6) if SessionGetStateRspData::conforms(&bytes[..]) => {
+ SessionResponseDataChild::SessionGetStateRsp(Arc::new(
+ SessionGetStateRspData::parse(&bytes[..])?,
+ ))
+ }
+ (7) if SessionUpdateControllerMulticastListRspData::conforms(&bytes[..]) => {
+ SessionResponseDataChild::SessionUpdateControllerMulticastListRsp(Arc::new(
+ SessionUpdateControllerMulticastListRspData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ SessionResponseDataChild::SessionInitRsp(value) => value.write_to(buffer),
+ SessionResponseDataChild::SessionDeinitRsp(value) => value.write_to(buffer),
+ SessionResponseDataChild::SessionSetAppConfigRsp(value) => value.write_to(buffer),
+ SessionResponseDataChild::SessionGetAppConfigRsp(value) => value.write_to(buffer),
+ SessionResponseDataChild::SessionGetCountRsp(value) => value.write_to(buffer),
+ SessionResponseDataChild::SessionGetStateRsp(value) => value.write_to(buffer),
+ SessionResponseDataChild::SessionUpdateControllerMulticastListRsp(value) => {
+ value.write_to(buffer)
+ }
+ SessionResponseDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for SessionResponsePacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<SessionResponsePacket> for Bytes {
+ fn from(packet: SessionResponsePacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<SessionResponsePacket> for Vec<u8> {
+ fn from(packet: SessionResponsePacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for SessionResponsePacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl SessionResponsePacket {
+ pub fn specialize(&self) -> SessionResponseChild {
+ match &self.session_response.child {
+ SessionResponseDataChild::SessionInitRsp(_) => SessionResponseChild::SessionInitRsp(
+ SessionInitRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ SessionResponseDataChild::SessionDeinitRsp(_) => {
+ SessionResponseChild::SessionDeinitRsp(
+ SessionDeinitRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionResponseDataChild::SessionSetAppConfigRsp(_) => {
+ SessionResponseChild::SessionSetAppConfigRsp(
+ SessionSetAppConfigRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionResponseDataChild::SessionGetAppConfigRsp(_) => {
+ SessionResponseChild::SessionGetAppConfigRsp(
+ SessionGetAppConfigRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionResponseDataChild::SessionGetCountRsp(_) => {
+ SessionResponseChild::SessionGetCountRsp(
+ SessionGetCountRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionResponseDataChild::SessionGetStateRsp(_) => {
+ SessionResponseChild::SessionGetStateRsp(
+ SessionGetStateRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionResponseDataChild::SessionUpdateControllerMulticastListRsp(_) => {
+ SessionResponseChild::SessionUpdateControllerMulticastListRsp(
+ SessionUpdateControllerMulticastListRspPacket::new(self.uci_packet.clone())
+ .unwrap(),
+ )
+ }
+ SessionResponseDataChild::None => SessionResponseChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let session_response = match &uci_response.child {
+ UciResponseDataChild::SessionResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not SessionResponse"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ session_response,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for SessionResponsePacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for SessionResponsePacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl SessionResponseBuilder {
+ pub fn build(self) -> SessionResponsePacket {
+ let session_response = Arc::new(SessionResponseData {
+ child: SessionResponseDataChild::None,
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::SessionResponse(session_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::SessionConfig,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ SessionResponsePacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for SessionResponseBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for SessionResponseBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum SessionNotificationDataChild {
+ SessionStatusNtf(Arc<SessionStatusNtfData>),
+ SessionUpdateControllerMulticastListNtf(Arc<SessionUpdateControllerMulticastListNtfData>),
+ None,
+}
+impl SessionNotificationDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ SessionNotificationDataChild::SessionStatusNtf(value) => value.get_total_size(),
+ SessionNotificationDataChild::SessionUpdateControllerMulticastListNtf(value) => {
+ value.get_total_size()
+ }
+ SessionNotificationDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum SessionNotificationChild {
+ SessionStatusNtf(SessionStatusNtfPacket),
+ SessionUpdateControllerMulticastListNtf(SessionUpdateControllerMulticastListNtfPacket),
+ None,
+}
+#[derive(Debug)]
+struct SessionNotificationData {
+ child: SessionNotificationDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct SessionNotificationPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_notification: Arc<UciNotificationData>,
+ session_notification: Arc<SessionNotificationData>,
+}
+#[derive(Debug)]
+pub struct SessionNotificationBuilder {
+ pub opcode: u8,
+}
+impl SessionNotificationData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (2) if SessionStatusNtfData::conforms(&bytes[..]) => {
+ SessionNotificationDataChild::SessionStatusNtf(Arc::new(
+ SessionStatusNtfData::parse(&bytes[..])?,
+ ))
+ }
+ (7) if SessionUpdateControllerMulticastListNtfData::conforms(&bytes[..]) => {
+ SessionNotificationDataChild::SessionUpdateControllerMulticastListNtf(Arc::new(
+ SessionUpdateControllerMulticastListNtfData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ SessionNotificationDataChild::SessionStatusNtf(value) => value.write_to(buffer),
+ SessionNotificationDataChild::SessionUpdateControllerMulticastListNtf(value) => {
+ value.write_to(buffer)
+ }
+ SessionNotificationDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for SessionNotificationPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<SessionNotificationPacket> for Bytes {
+ fn from(packet: SessionNotificationPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<SessionNotificationPacket> for Vec<u8> {
+ fn from(packet: SessionNotificationPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for SessionNotificationPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl SessionNotificationPacket {
+ pub fn specialize(&self) -> SessionNotificationChild {
+ match &self.session_notification.child {
+ SessionNotificationDataChild::SessionStatusNtf(_) => {
+ SessionNotificationChild::SessionStatusNtf(
+ SessionStatusNtfPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ SessionNotificationDataChild::SessionUpdateControllerMulticastListNtf(_) => {
+ SessionNotificationChild::SessionUpdateControllerMulticastListNtf(
+ SessionUpdateControllerMulticastListNtfPacket::new(self.uci_packet.clone())
+ .unwrap(),
+ )
+ }
+ SessionNotificationDataChild::None => SessionNotificationChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_notification = match &uci_packet.child {
+ UciPacketDataChild::UciNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciNotification"),
+ };
+ let session_notification = match &uci_notification.child {
+ UciNotificationDataChild::SessionNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not SessionNotification"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_notification,
+ session_notification,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for SessionNotificationPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciNotificationPacket> for SessionNotificationPacket {
+ fn into(self) -> UciNotificationPacket {
+ UciNotificationPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl SessionNotificationBuilder {
+ pub fn build(self) -> SessionNotificationPacket {
+ let session_notification = Arc::new(SessionNotificationData {
+ child: SessionNotificationDataChild::None,
+ });
+ let uci_notification = Arc::new(UciNotificationData {
+ child: UciNotificationDataChild::SessionNotification(session_notification),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::SessionConfig,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Notification,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciNotification(uci_notification),
+ });
+ SessionNotificationPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for SessionNotificationBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciNotificationPacket> for SessionNotificationBuilder {
+ fn into(self) -> UciNotificationPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum RangingCommandDataChild {
+ RangeStartCmd(Arc<RangeStartCmdData>),
+ RangeStopCmd(Arc<RangeStopCmdData>),
+ RangeGetRangingCountCmd(Arc<RangeGetRangingCountCmdData>),
+ None,
+}
+impl RangingCommandDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ RangingCommandDataChild::RangeStartCmd(value) => value.get_total_size(),
+ RangingCommandDataChild::RangeStopCmd(value) => value.get_total_size(),
+ RangingCommandDataChild::RangeGetRangingCountCmd(value) => value.get_total_size(),
+ RangingCommandDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum RangingCommandChild {
+ RangeStartCmd(RangeStartCmdPacket),
+ RangeStopCmd(RangeStopCmdPacket),
+ RangeGetRangingCountCmd(RangeGetRangingCountCmdPacket),
+ None,
+}
+#[derive(Debug)]
+struct RangingCommandData {
+ session_id: u32,
+ child: RangingCommandDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct RangingCommandPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ ranging_command: Arc<RangingCommandData>,
+}
+#[derive(Debug)]
+pub struct RangingCommandBuilder {
+ pub opcode: u8,
+ pub session_id: u32,
+}
+impl RangingCommandData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 8 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ if bytes.len() < 8 {
+ return Err(Error::InvalidLengthError {
+ obj: "RangingCommand".to_string(),
+ field: "session_id".to_string(),
+ wanted: 8,
+ got: bytes.len(),
+ });
+ }
+ let session_id = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
+ let child = match (opcode) {
+ (0) if RangeStartCmdData::conforms(&bytes[..]) => {
+ RangingCommandDataChild::RangeStartCmd(Arc::new(RangeStartCmdData::parse(
+ &bytes[..],
+ )?))
+ }
+ (1) if RangeStopCmdData::conforms(&bytes[..]) => RangingCommandDataChild::RangeStopCmd(
+ Arc::new(RangeStopCmdData::parse(&bytes[..])?),
+ ),
+ (3) if RangeGetRangingCountCmdData::conforms(&bytes[..]) => {
+ RangingCommandDataChild::RangeGetRangingCountCmd(Arc::new(
+ RangeGetRangingCountCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { session_id, child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let session_id = self.session_id;
+ buffer[4..8].copy_from_slice(&session_id.to_le_bytes()[0..4]);
+ match &self.child {
+ RangingCommandDataChild::RangeStartCmd(value) => value.write_to(buffer),
+ RangingCommandDataChild::RangeStopCmd(value) => value.write_to(buffer),
+ RangingCommandDataChild::RangeGetRangingCountCmd(value) => value.write_to(buffer),
+ RangingCommandDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 4;
+ ret
+ }
+}
+impl Packet for RangingCommandPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<RangingCommandPacket> for Bytes {
+ fn from(packet: RangingCommandPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<RangingCommandPacket> for Vec<u8> {
+ fn from(packet: RangingCommandPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for RangingCommandPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl RangingCommandPacket {
+ pub fn specialize(&self) -> RangingCommandChild {
+ match &self.ranging_command.child {
+ RangingCommandDataChild::RangeStartCmd(_) => RangingCommandChild::RangeStartCmd(
+ RangeStartCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ RangingCommandDataChild::RangeStopCmd(_) => RangingCommandChild::RangeStopCmd(
+ RangeStopCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ RangingCommandDataChild::RangeGetRangingCountCmd(_) => {
+ RangingCommandChild::RangeGetRangingCountCmd(
+ RangeGetRangingCountCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ RangingCommandDataChild::None => RangingCommandChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let ranging_command = match &uci_command.child {
+ UciCommandDataChild::RangingCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not RangingCommand"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ ranging_command,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_session_id(&self) -> u32 {
+ self.ranging_command.as_ref().session_id
+ }
+}
+impl Into<UciPacketPacket> for RangingCommandPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for RangingCommandPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl RangingCommandBuilder {
+ pub fn build(self) -> RangingCommandPacket {
+ let ranging_command = Arc::new(RangingCommandData {
+ session_id: self.session_id,
+ child: RangingCommandDataChild::None,
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::RangingCommand(ranging_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::RangingSessionControl,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ RangingCommandPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for RangingCommandBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for RangingCommandBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum RangingResponseDataChild {
+ RangeStartRsp(Arc<RangeStartRspData>),
+ RangeStopRsp(Arc<RangeStopRspData>),
+ RangeGetRangingCountRsp(Arc<RangeGetRangingCountRspData>),
+ None,
+}
+impl RangingResponseDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ RangingResponseDataChild::RangeStartRsp(value) => value.get_total_size(),
+ RangingResponseDataChild::RangeStopRsp(value) => value.get_total_size(),
+ RangingResponseDataChild::RangeGetRangingCountRsp(value) => value.get_total_size(),
+ RangingResponseDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum RangingResponseChild {
+ RangeStartRsp(RangeStartRspPacket),
+ RangeStopRsp(RangeStopRspPacket),
+ RangeGetRangingCountRsp(RangeGetRangingCountRspPacket),
+ None,
+}
+#[derive(Debug)]
+struct RangingResponseData {
+ child: RangingResponseDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct RangingResponsePacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ ranging_response: Arc<RangingResponseData>,
+}
+#[derive(Debug)]
+pub struct RangingResponseBuilder {
+ pub opcode: u8,
+}
+impl RangingResponseData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (0) if RangeStartRspData::conforms(&bytes[..]) => {
+ RangingResponseDataChild::RangeStartRsp(Arc::new(RangeStartRspData::parse(
+ &bytes[..],
+ )?))
+ }
+ (1) if RangeStopRspData::conforms(&bytes[..]) => {
+ RangingResponseDataChild::RangeStopRsp(Arc::new(RangeStopRspData::parse(
+ &bytes[..],
+ )?))
+ }
+ (3) if RangeGetRangingCountRspData::conforms(&bytes[..]) => {
+ RangingResponseDataChild::RangeGetRangingCountRsp(Arc::new(
+ RangeGetRangingCountRspData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ RangingResponseDataChild::RangeStartRsp(value) => value.write_to(buffer),
+ RangingResponseDataChild::RangeStopRsp(value) => value.write_to(buffer),
+ RangingResponseDataChild::RangeGetRangingCountRsp(value) => value.write_to(buffer),
+ RangingResponseDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for RangingResponsePacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<RangingResponsePacket> for Bytes {
+ fn from(packet: RangingResponsePacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<RangingResponsePacket> for Vec<u8> {
+ fn from(packet: RangingResponsePacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for RangingResponsePacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl RangingResponsePacket {
+ pub fn specialize(&self) -> RangingResponseChild {
+ match &self.ranging_response.child {
+ RangingResponseDataChild::RangeStartRsp(_) => RangingResponseChild::RangeStartRsp(
+ RangeStartRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ RangingResponseDataChild::RangeStopRsp(_) => RangingResponseChild::RangeStopRsp(
+ RangeStopRspPacket::new(self.uci_packet.clone()).unwrap(),
+ ),
+ RangingResponseDataChild::RangeGetRangingCountRsp(_) => {
+ RangingResponseChild::RangeGetRangingCountRsp(
+ RangeGetRangingCountRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ RangingResponseDataChild::None => RangingResponseChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let ranging_response = match &uci_response.child {
+ UciResponseDataChild::RangingResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not RangingResponse"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ ranging_response,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for RangingResponsePacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for RangingResponsePacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl RangingResponseBuilder {
+ pub fn build(self) -> RangingResponsePacket {
+ let ranging_response = Arc::new(RangingResponseData {
+ child: RangingResponseDataChild::None,
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::RangingResponse(ranging_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::RangingSessionControl,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ RangingResponsePacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for RangingResponseBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for RangingResponseBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum RangingNotificationDataChild {
+ RangeDataNtf(Arc<RangeDataNtfData>),
+ None,
+}
+impl RangingNotificationDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ RangingNotificationDataChild::RangeDataNtf(value) => value.get_total_size(),
+ RangingNotificationDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum RangingNotificationChild {
+ RangeDataNtf(RangeDataNtfPacket),
+ None,
+}
+#[derive(Debug)]
+struct RangingNotificationData {
+ child: RangingNotificationDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct RangingNotificationPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_notification: Arc<UciNotificationData>,
+ ranging_notification: Arc<RangingNotificationData>,
+}
+#[derive(Debug)]
+pub struct RangingNotificationBuilder {
+ pub opcode: u8,
+}
+impl RangingNotificationData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match RangeDataNtfData::parse(&bytes[..]) {
+ Ok(c) if RangeDataNtfData::conforms(&bytes[..]) => {
+ RangingNotificationDataChild::RangeDataNtf(Arc::new(c))
+ }
+ Err(Error::InvalidLengthError { .. }) => RangingNotificationDataChild::None,
+ _ => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ RangingNotificationDataChild::RangeDataNtf(value) => value.write_to(buffer),
+ RangingNotificationDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for RangingNotificationPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<RangingNotificationPacket> for Bytes {
+ fn from(packet: RangingNotificationPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<RangingNotificationPacket> for Vec<u8> {
+ fn from(packet: RangingNotificationPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for RangingNotificationPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl RangingNotificationPacket {
+ pub fn specialize(&self) -> RangingNotificationChild {
+ match &self.ranging_notification.child {
+ RangingNotificationDataChild::RangeDataNtf(_) => {
+ RangingNotificationChild::RangeDataNtf(
+ RangeDataNtfPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ RangingNotificationDataChild::None => RangingNotificationChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_notification = match &uci_packet.child {
+ UciPacketDataChild::UciNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciNotification"),
+ };
+ let ranging_notification = match &uci_notification.child {
+ UciNotificationDataChild::RangingNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not RangingNotification"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_notification,
+ ranging_notification,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for RangingNotificationPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciNotificationPacket> for RangingNotificationPacket {
+ fn into(self) -> UciNotificationPacket {
+ UciNotificationPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl RangingNotificationBuilder {
+ pub fn build(self) -> RangingNotificationPacket {
+ let ranging_notification = Arc::new(RangingNotificationData {
+ child: RangingNotificationDataChild::None,
+ });
+ let uci_notification = Arc::new(UciNotificationData {
+ child: UciNotificationDataChild::RangingNotification(ranging_notification),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::RangingSessionControl,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Notification,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciNotification(uci_notification),
+ });
+ RangingNotificationPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for RangingNotificationBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciNotificationPacket> for RangingNotificationBuilder {
+ fn into(self) -> UciNotificationPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum AndroidCommandDataChild {
+ AndroidGetPowerStatsCmd(Arc<AndroidGetPowerStatsCmdData>),
+ AndroidSetCountryCodeCmd(Arc<AndroidSetCountryCodeCmdData>),
+ None,
+}
+impl AndroidCommandDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ AndroidCommandDataChild::AndroidGetPowerStatsCmd(value) => value.get_total_size(),
+ AndroidCommandDataChild::AndroidSetCountryCodeCmd(value) => value.get_total_size(),
+ AndroidCommandDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum AndroidCommandChild {
+ AndroidGetPowerStatsCmd(AndroidGetPowerStatsCmdPacket),
+ AndroidSetCountryCodeCmd(AndroidSetCountryCodeCmdPacket),
+ None,
+}
+#[derive(Debug)]
+struct AndroidCommandData {
+ child: AndroidCommandDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct AndroidCommandPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ android_command: Arc<AndroidCommandData>,
+}
+#[derive(Debug)]
+pub struct AndroidCommandBuilder {
+ pub opcode: u8,
+}
+impl AndroidCommandData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (0) if AndroidGetPowerStatsCmdData::conforms(&bytes[..]) => {
+ AndroidCommandDataChild::AndroidGetPowerStatsCmd(Arc::new(
+ AndroidGetPowerStatsCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (1) if AndroidSetCountryCodeCmdData::conforms(&bytes[..]) => {
+ AndroidCommandDataChild::AndroidSetCountryCodeCmd(Arc::new(
+ AndroidSetCountryCodeCmdData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ AndroidCommandDataChild::AndroidGetPowerStatsCmd(value) => value.write_to(buffer),
+ AndroidCommandDataChild::AndroidSetCountryCodeCmd(value) => value.write_to(buffer),
+ AndroidCommandDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for AndroidCommandPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<AndroidCommandPacket> for Bytes {
+ fn from(packet: AndroidCommandPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<AndroidCommandPacket> for Vec<u8> {
+ fn from(packet: AndroidCommandPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for AndroidCommandPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl AndroidCommandPacket {
+ pub fn specialize(&self) -> AndroidCommandChild {
+ match &self.android_command.child {
+ AndroidCommandDataChild::AndroidGetPowerStatsCmd(_) => {
+ AndroidCommandChild::AndroidGetPowerStatsCmd(
+ AndroidGetPowerStatsCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ AndroidCommandDataChild::AndroidSetCountryCodeCmd(_) => {
+ AndroidCommandChild::AndroidSetCountryCodeCmd(
+ AndroidSetCountryCodeCmdPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ AndroidCommandDataChild::None => AndroidCommandChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let android_command = match &uci_command.child {
+ UciCommandDataChild::AndroidCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not AndroidCommand"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ android_command,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for AndroidCommandPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for AndroidCommandPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl AndroidCommandBuilder {
+ pub fn build(self) -> AndroidCommandPacket {
+ let android_command = Arc::new(AndroidCommandData {
+ child: AndroidCommandDataChild::None,
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::AndroidCommand(android_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::VendorAndroid,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ AndroidCommandPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for AndroidCommandBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for AndroidCommandBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+enum AndroidResponseDataChild {
+ AndroidGetPowerStatsRsp(Arc<AndroidGetPowerStatsRspData>),
+ AndroidSetCountryCodeRsp(Arc<AndroidSetCountryCodeRspData>),
+ None,
+}
+impl AndroidResponseDataChild {
+ fn get_total_size(&self) -> usize {
+ match self {
+ AndroidResponseDataChild::AndroidGetPowerStatsRsp(value) => value.get_total_size(),
+ AndroidResponseDataChild::AndroidSetCountryCodeRsp(value) => value.get_total_size(),
+ AndroidResponseDataChild::None => 0,
+ }
+ }
+}
+#[derive(Debug)]
+pub enum AndroidResponseChild {
+ AndroidGetPowerStatsRsp(AndroidGetPowerStatsRspPacket),
+ AndroidSetCountryCodeRsp(AndroidSetCountryCodeRspPacket),
+ None,
+}
+#[derive(Debug)]
+struct AndroidResponseData {
+ child: AndroidResponseDataChild,
+}
+#[derive(Debug, Clone)]
+pub struct AndroidResponsePacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ android_response: Arc<AndroidResponseData>,
+}
+#[derive(Debug)]
+pub struct AndroidResponseBuilder {
+ pub opcode: u8,
+}
+impl AndroidResponseData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8], opcode: u8) -> Result<Self> {
+ let child = match (opcode) {
+ (0) if AndroidGetPowerStatsRspData::conforms(&bytes[..]) => {
+ AndroidResponseDataChild::AndroidGetPowerStatsRsp(Arc::new(
+ AndroidGetPowerStatsRspData::parse(&bytes[..])?,
+ ))
+ }
+ (1) if AndroidSetCountryCodeRspData::conforms(&bytes[..]) => {
+ AndroidResponseDataChild::AndroidSetCountryCodeRsp(Arc::new(
+ AndroidSetCountryCodeRspData::parse(&bytes[..])?,
+ ))
+ }
+ (_) => return Err(Error::InvalidPacketError),
+ };
+ Ok(Self { child })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ match &self.child {
+ AndroidResponseDataChild::AndroidGetPowerStatsRsp(value) => value.write_to(buffer),
+ AndroidResponseDataChild::AndroidSetCountryCodeRsp(value) => value.write_to(buffer),
+ AndroidResponseDataChild::None => {}
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size() + self.child.get_total_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for AndroidResponsePacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<AndroidResponsePacket> for Bytes {
+ fn from(packet: AndroidResponsePacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<AndroidResponsePacket> for Vec<u8> {
+ fn from(packet: AndroidResponsePacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for AndroidResponsePacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl AndroidResponsePacket {
+ pub fn specialize(&self) -> AndroidResponseChild {
+ match &self.android_response.child {
+ AndroidResponseDataChild::AndroidGetPowerStatsRsp(_) => {
+ AndroidResponseChild::AndroidGetPowerStatsRsp(
+ AndroidGetPowerStatsRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ AndroidResponseDataChild::AndroidSetCountryCodeRsp(_) => {
+ AndroidResponseChild::AndroidSetCountryCodeRsp(
+ AndroidSetCountryCodeRspPacket::new(self.uci_packet.clone()).unwrap(),
+ )
+ }
+ AndroidResponseDataChild::None => AndroidResponseChild::None,
+ }
+ }
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let android_response = match &uci_response.child {
+ UciResponseDataChild::AndroidResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not AndroidResponse"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ android_response,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for AndroidResponsePacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for AndroidResponsePacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl AndroidResponseBuilder {
+ pub fn build(self) -> AndroidResponsePacket {
+ let android_response = Arc::new(AndroidResponseData {
+ child: AndroidResponseDataChild::None,
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::AndroidResponse(android_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::VendorAndroid,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ AndroidResponsePacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for AndroidResponseBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for AndroidResponseBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+struct AndroidNotificationData {}
+#[derive(Debug, Clone)]
+pub struct AndroidNotificationPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_notification: Arc<UciNotificationData>,
+ android_notification: Arc<AndroidNotificationData>,
+}
+#[derive(Debug)]
+pub struct AndroidNotificationBuilder {
+ pub opcode: u8,
+}
+impl AndroidNotificationData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ Ok(Self {})
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {}
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for AndroidNotificationPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<AndroidNotificationPacket> for Bytes {
+ fn from(packet: AndroidNotificationPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<AndroidNotificationPacket> for Vec<u8> {
+ fn from(packet: AndroidNotificationPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for AndroidNotificationPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl AndroidNotificationPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_notification = match &uci_packet.child {
+ UciPacketDataChild::UciNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciNotification"),
+ };
+ let android_notification = match &uci_notification.child {
+ UciNotificationDataChild::AndroidNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not AndroidNotification"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_notification,
+ android_notification,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for AndroidNotificationPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciNotificationPacket> for AndroidNotificationPacket {
+ fn into(self) -> UciNotificationPacket {
+ UciNotificationPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl AndroidNotificationBuilder {
+ pub fn build(self) -> AndroidNotificationPacket {
+ let android_notification = Arc::new(AndroidNotificationData {});
+ let uci_notification = Arc::new(UciNotificationData {
+ child: UciNotificationDataChild::AndroidNotification(android_notification),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::VendorAndroid,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Notification,
+ opcode: self.opcode,
+ child: UciPacketDataChild::UciNotification(uci_notification),
+ });
+ AndroidNotificationPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for AndroidNotificationBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciNotificationPacket> for AndroidNotificationBuilder {
+ fn into(self) -> UciNotificationPacket {
+ self.build().into()
+ }
+}
+
+#[derive(Debug)]
+struct DeviceResetCmdData {
+ reset_config: ResetConfig,
+}
+#[derive(Debug, Clone)]
+pub struct DeviceResetCmdPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ core_command: Arc<CoreCommandData>,
+ device_reset_cmd: Arc<DeviceResetCmdData>,
+}
+#[derive(Debug)]
+pub struct DeviceResetCmdBuilder {
+ pub reset_config: ResetConfig,
+}
+impl DeviceResetCmdData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 5 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceResetCmd".to_string(),
+ field: "reset_config".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let reset_config = u8::from_le_bytes([bytes[4]]);
+ let reset_config =
+ ResetConfig::from_u8(reset_config).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "DeviceResetCmd".to_string(),
+ field: "reset_config".to_string(),
+ value: reset_config as u64,
+ type_: "ResetConfig".to_string(),
+ })?;
+ Ok(Self { reset_config })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let reset_config = self.reset_config.to_u8().unwrap();
+ buffer[4..5].copy_from_slice(&reset_config.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 1;
+ ret
+ }
+}
+impl Packet for DeviceResetCmdPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<DeviceResetCmdPacket> for Bytes {
+ fn from(packet: DeviceResetCmdPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<DeviceResetCmdPacket> for Vec<u8> {
+ fn from(packet: DeviceResetCmdPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for DeviceResetCmdPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl DeviceResetCmdPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let core_command = match &uci_command.child {
+ UciCommandDataChild::CoreCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreCommand"),
+ };
+ let device_reset_cmd = match &core_command.child {
+ CoreCommandDataChild::DeviceResetCmd(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not DeviceResetCmd"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ core_command,
+ device_reset_cmd,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_reset_config(&self) -> ResetConfig {
+ self.device_reset_cmd.as_ref().reset_config
+ }
+}
+impl Into<UciPacketPacket> for DeviceResetCmdPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for DeviceResetCmdPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreCommandPacket> for DeviceResetCmdPacket {
+ fn into(self) -> CoreCommandPacket {
+ CoreCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl DeviceResetCmdBuilder {
+ pub fn build(self) -> DeviceResetCmdPacket {
+ let device_reset_cmd = Arc::new(DeviceResetCmdData {
+ reset_config: self.reset_config,
+ });
+ let core_command = Arc::new(CoreCommandData {
+ child: CoreCommandDataChild::DeviceResetCmd(device_reset_cmd),
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::CoreCommand(core_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: 0,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ DeviceResetCmdPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for DeviceResetCmdBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for DeviceResetCmdBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+impl Into<CoreCommandPacket> for DeviceResetCmdBuilder {
+ fn into(self) -> CoreCommandPacket {
+ self.build().into()
+ }
+}
+macro_rules! device_reset_cmd_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciCommand(uci_command_packet) => {match uci_command_packet.specialize() {/* (2) */
+UciCommandChild::CoreCommand(core_command_packet) => {match core_command_packet.specialize() {/* (3) */
+CoreCommandChild::DeviceResetCmd(packet) => {let rebuilder = DeviceResetCmdBuilder {reset_config : packet.get_reset_config(),};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse device_reset_cmd
+ {:#02x?}", core_command_packet); }}}_ => {panic!("Couldn't parse core_command
+ {:#02x?}", uci_command_packet); }}}_ => {panic!("Couldn't parse uci_command
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+device_reset_cmd_builder_tests! { device_reset_cmd_builder_test_00: b"\x20\x00\x00\x01\x00",}
+
+#[derive(Debug)]
+struct DeviceResetRspData {
+ status: StatusCode,
+}
+#[derive(Debug, Clone)]
+pub struct DeviceResetRspPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ core_response: Arc<CoreResponseData>,
+ device_reset_rsp: Arc<DeviceResetRspData>,
+}
+#[derive(Debug)]
+pub struct DeviceResetRspBuilder {
+ pub status: StatusCode,
+}
+impl DeviceResetRspData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 5 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceResetRsp".to_string(),
+ field: "status".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[4]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "DeviceResetRsp".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ Ok(Self { status })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let status = self.status.to_u8().unwrap();
+ buffer[4..5].copy_from_slice(&status.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 1;
+ ret
+ }
+}
+impl Packet for DeviceResetRspPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<DeviceResetRspPacket> for Bytes {
+ fn from(packet: DeviceResetRspPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<DeviceResetRspPacket> for Vec<u8> {
+ fn from(packet: DeviceResetRspPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for DeviceResetRspPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl DeviceResetRspPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let core_response = match &uci_response.child {
+ UciResponseDataChild::CoreResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreResponse"),
+ };
+ let device_reset_rsp = match &core_response.child {
+ CoreResponseDataChild::DeviceResetRsp(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not DeviceResetRsp"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ core_response,
+ device_reset_rsp,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_status(&self) -> StatusCode {
+ self.device_reset_rsp.as_ref().status
+ }
+}
+impl Into<UciPacketPacket> for DeviceResetRspPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for DeviceResetRspPacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreResponsePacket> for DeviceResetRspPacket {
+ fn into(self) -> CoreResponsePacket {
+ CoreResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl DeviceResetRspBuilder {
+ pub fn build(self) -> DeviceResetRspPacket {
+ let device_reset_rsp = Arc::new(DeviceResetRspData {
+ status: self.status,
+ });
+ let core_response = Arc::new(CoreResponseData {
+ child: CoreResponseDataChild::DeviceResetRsp(device_reset_rsp),
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::CoreResponse(core_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: 0,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ DeviceResetRspPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for DeviceResetRspBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for DeviceResetRspBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+impl Into<CoreResponsePacket> for DeviceResetRspBuilder {
+ fn into(self) -> CoreResponsePacket {
+ self.build().into()
+ }
+}
+macro_rules! device_reset_rsp_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciResponse(uci_response_packet) => {match uci_response_packet.specialize() {/* (2) */
+UciResponseChild::CoreResponse(core_response_packet) => {match core_response_packet.specialize() {/* (3) */
+CoreResponseChild::DeviceResetRsp(packet) => {let rebuilder = DeviceResetRspBuilder {status : packet.get_status(),};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse device_reset_rsp
+ {:#02x?}", core_response_packet); }}}_ => {panic!("Couldn't parse core_response
+ {:#02x?}", uci_response_packet); }}}_ => {panic!("Couldn't parse uci_response
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+device_reset_rsp_builder_tests! { device_reset_rsp_builder_test_00: b"\x40\x00\x00\x01\x00",}
+
+#[derive(Debug)]
+struct DeviceStatusNtfData {
+ device_state: DeviceState,
+}
+#[derive(Debug, Clone)]
+pub struct DeviceStatusNtfPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_notification: Arc<UciNotificationData>,
+ core_notification: Arc<CoreNotificationData>,
+ device_status_ntf: Arc<DeviceStatusNtfData>,
+}
+#[derive(Debug)]
+pub struct DeviceStatusNtfBuilder {
+ pub device_state: DeviceState,
+}
+impl DeviceStatusNtfData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 5 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "DeviceStatusNtf".to_string(),
+ field: "device_state".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let device_state = u8::from_le_bytes([bytes[4]]);
+ let device_state =
+ DeviceState::from_u8(device_state).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "DeviceStatusNtf".to_string(),
+ field: "device_state".to_string(),
+ value: device_state as u64,
+ type_: "DeviceState".to_string(),
+ })?;
+ Ok(Self { device_state })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let device_state = self.device_state.to_u8().unwrap();
+ buffer[4..5].copy_from_slice(&device_state.to_le_bytes()[0..1]);
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 1;
+ ret
+ }
+}
+impl Packet for DeviceStatusNtfPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<DeviceStatusNtfPacket> for Bytes {
+ fn from(packet: DeviceStatusNtfPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<DeviceStatusNtfPacket> for Vec<u8> {
+ fn from(packet: DeviceStatusNtfPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for DeviceStatusNtfPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl DeviceStatusNtfPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_notification = match &uci_packet.child {
+ UciPacketDataChild::UciNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciNotification"),
+ };
+ let core_notification = match &uci_notification.child {
+ UciNotificationDataChild::CoreNotification(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreNotification"),
+ };
+ let device_status_ntf = match &core_notification.child {
+ CoreNotificationDataChild::DeviceStatusNtf(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not DeviceStatusNtf"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_notification,
+ core_notification,
+ device_status_ntf,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_device_state(&self) -> DeviceState {
+ self.device_status_ntf.as_ref().device_state
+ }
+}
+impl Into<UciPacketPacket> for DeviceStatusNtfPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciNotificationPacket> for DeviceStatusNtfPacket {
+ fn into(self) -> UciNotificationPacket {
+ UciNotificationPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreNotificationPacket> for DeviceStatusNtfPacket {
+ fn into(self) -> CoreNotificationPacket {
+ CoreNotificationPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl DeviceStatusNtfBuilder {
+ pub fn build(self) -> DeviceStatusNtfPacket {
+ let device_status_ntf = Arc::new(DeviceStatusNtfData {
+ device_state: self.device_state,
+ });
+ let core_notification = Arc::new(CoreNotificationData {
+ child: CoreNotificationDataChild::DeviceStatusNtf(device_status_ntf),
+ });
+ let uci_notification = Arc::new(UciNotificationData {
+ child: UciNotificationDataChild::CoreNotification(core_notification),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Notification,
+ opcode: 1,
+ child: UciPacketDataChild::UciNotification(uci_notification),
+ });
+ DeviceStatusNtfPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for DeviceStatusNtfBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciNotificationPacket> for DeviceStatusNtfBuilder {
+ fn into(self) -> UciNotificationPacket {
+ self.build().into()
+ }
+}
+impl Into<CoreNotificationPacket> for DeviceStatusNtfBuilder {
+ fn into(self) -> CoreNotificationPacket {
+ self.build().into()
+ }
+}
+macro_rules! device_status_ntf_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciNotification(uci_notification_packet) => {match uci_notification_packet.specialize() {/* (2) */
+UciNotificationChild::CoreNotification(core_notification_packet) => {match core_notification_packet.specialize() {/* (3) */
+CoreNotificationChild::DeviceStatusNtf(packet) => {let rebuilder = DeviceStatusNtfBuilder {device_state : packet.get_device_state(),};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse device_status_ntf
+ {:#02x?}", core_notification_packet); }}}_ => {panic!("Couldn't parse core_notification
+ {:#02x?}", uci_notification_packet); }}}_ => {panic!("Couldn't parse uci_notification
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+device_status_ntf_builder_tests! { device_status_ntf_builder_test_00: b"\x60\x01\x00\x01\x01",}
+
+#[derive(Debug)]
+struct GetDeviceInfoCmdData {}
+#[derive(Debug, Clone)]
+pub struct GetDeviceInfoCmdPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ core_command: Arc<CoreCommandData>,
+ get_device_info_cmd: Arc<GetDeviceInfoCmdData>,
+}
+#[derive(Debug)]
+pub struct GetDeviceInfoCmdBuilder {}
+impl GetDeviceInfoCmdData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ Ok(Self {})
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {}
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for GetDeviceInfoCmdPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<GetDeviceInfoCmdPacket> for Bytes {
+ fn from(packet: GetDeviceInfoCmdPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<GetDeviceInfoCmdPacket> for Vec<u8> {
+ fn from(packet: GetDeviceInfoCmdPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for GetDeviceInfoCmdPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl GetDeviceInfoCmdPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let core_command = match &uci_command.child {
+ UciCommandDataChild::CoreCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreCommand"),
+ };
+ let get_device_info_cmd = match &core_command.child {
+ CoreCommandDataChild::GetDeviceInfoCmd(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not GetDeviceInfoCmd"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ core_command,
+ get_device_info_cmd,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for GetDeviceInfoCmdPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for GetDeviceInfoCmdPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreCommandPacket> for GetDeviceInfoCmdPacket {
+ fn into(self) -> CoreCommandPacket {
+ CoreCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl GetDeviceInfoCmdBuilder {
+ pub fn build(self) -> GetDeviceInfoCmdPacket {
+ let get_device_info_cmd = Arc::new(GetDeviceInfoCmdData {});
+ let core_command = Arc::new(CoreCommandData {
+ child: CoreCommandDataChild::GetDeviceInfoCmd(get_device_info_cmd),
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::CoreCommand(core_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: 2,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ GetDeviceInfoCmdPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for GetDeviceInfoCmdBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for GetDeviceInfoCmdBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+impl Into<CoreCommandPacket> for GetDeviceInfoCmdBuilder {
+ fn into(self) -> CoreCommandPacket {
+ self.build().into()
+ }
+}
+macro_rules! get_device_info_cmd_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciCommand(uci_command_packet) => {match uci_command_packet.specialize() {/* (2) */
+UciCommandChild::CoreCommand(core_command_packet) => {match core_command_packet.specialize() {/* (3) */
+CoreCommandChild::GetDeviceInfoCmd(packet) => {let rebuilder = GetDeviceInfoCmdBuilder {};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse get_device_info_cmd
+ {:#02x?}", core_command_packet); }}}_ => {panic!("Couldn't parse core_command
+ {:#02x?}", uci_command_packet); }}}_ => {panic!("Couldn't parse uci_command
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+get_device_info_cmd_builder_tests! { get_device_info_cmd_builder_test_00: b"\x20\x02\x00\x00",}
+
+#[derive(Debug)]
+struct GetDeviceInfoRspData {
+ status: StatusCode,
+ uci_version: u16,
+ mac_version: u16,
+ phy_version: u16,
+ uci_test_version: u16,
+ vendor_spec_info: Vec<u8>,
+}
+#[derive(Debug, Clone)]
+pub struct GetDeviceInfoRspPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ core_response: Arc<CoreResponseData>,
+ get_device_info_rsp: Arc<GetDeviceInfoRspData>,
+}
+#[derive(Debug)]
+pub struct GetDeviceInfoRspBuilder {
+ pub status: StatusCode,
+ pub uci_version: u16,
+ pub mac_version: u16,
+ pub phy_version: u16,
+ pub uci_test_version: u16,
+ pub vendor_spec_info: Vec<u8>,
+}
+impl GetDeviceInfoRspData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 14 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "status".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[4]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ if bytes.len() < 7 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "uci_version".to_string(),
+ wanted: 7,
+ got: bytes.len(),
+ });
+ }
+ let uci_version = u16::from_le_bytes([bytes[5], bytes[6]]);
+ if bytes.len() < 9 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "mac_version".to_string(),
+ wanted: 9,
+ got: bytes.len(),
+ });
+ }
+ let mac_version = u16::from_le_bytes([bytes[7], bytes[8]]);
+ if bytes.len() < 11 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "phy_version".to_string(),
+ wanted: 11,
+ got: bytes.len(),
+ });
+ }
+ let phy_version = u16::from_le_bytes([bytes[9], bytes[10]]);
+ if bytes.len() < 13 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "uci_test_version".to_string(),
+ wanted: 13,
+ got: bytes.len(),
+ });
+ }
+ let uci_test_version = u16::from_le_bytes([bytes[11], bytes[12]]);
+ if bytes.len() < 14 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "vendor_spec_info_count".to_string(),
+ wanted: 14,
+ got: bytes.len(),
+ });
+ }
+ let vendor_spec_info_count = u8::from_le_bytes([bytes[13]]);
+ let want_ = 14 + ((vendor_spec_info_count as usize) * 1);
+ if bytes.len() < want_ {
+ return Err(Error::InvalidLengthError {
+ obj: "GetDeviceInfoRsp".to_string(),
+ field: "vendor_spec_info".to_string(),
+ wanted: want_,
+ got: bytes.len(),
+ });
+ }
+ let vendor_spec_info: Vec<u8> = bytes[14..14 + ((vendor_spec_info_count as usize) * 1)]
+ .to_vec()
+ .chunks_exact(1)
+ .into_iter()
+ .map(|i| u8::from_le_bytes([i[0]]))
+ .collect();
+ Ok(Self {
+ status,
+ uci_version,
+ mac_version,
+ phy_version,
+ uci_test_version,
+ vendor_spec_info,
+ })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let status = self.status.to_u8().unwrap();
+ buffer[4..5].copy_from_slice(&status.to_le_bytes()[0..1]);
+ let uci_version = self.uci_version;
+ buffer[5..7].copy_from_slice(&uci_version.to_le_bytes()[0..2]);
+ let mac_version = self.mac_version;
+ buffer[7..9].copy_from_slice(&mac_version.to_le_bytes()[0..2]);
+ let phy_version = self.phy_version;
+ buffer[9..11].copy_from_slice(&phy_version.to_le_bytes()[0..2]);
+ let uci_test_version = self.uci_test_version;
+ buffer[11..13].copy_from_slice(&uci_test_version.to_le_bytes()[0..2]);
+ buffer[13..14].copy_from_slice(&(self.vendor_spec_info.len() as u8).to_le_bytes());
+ for (i, e) in self.vendor_spec_info.iter().enumerate() {
+ buffer[14 + i..14 + i + 1].copy_from_slice(&e.to_le_bytes())
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 10;
+ let ret = ret + (self.vendor_spec_info.len() * ((/* Bits: */8 + /* Dynamic: */ 0) / 8));
+ ret
+ }
+}
+impl Packet for GetDeviceInfoRspPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<GetDeviceInfoRspPacket> for Bytes {
+ fn from(packet: GetDeviceInfoRspPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<GetDeviceInfoRspPacket> for Vec<u8> {
+ fn from(packet: GetDeviceInfoRspPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for GetDeviceInfoRspPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl GetDeviceInfoRspPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let core_response = match &uci_response.child {
+ UciResponseDataChild::CoreResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreResponse"),
+ };
+ let get_device_info_rsp = match &core_response.child {
+ CoreResponseDataChild::GetDeviceInfoRsp(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not GetDeviceInfoRsp"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ core_response,
+ get_device_info_rsp,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_status(&self) -> StatusCode {
+ self.get_device_info_rsp.as_ref().status
+ }
+ pub fn get_uci_version(&self) -> u16 {
+ self.get_device_info_rsp.as_ref().uci_version
+ }
+ pub fn get_mac_version(&self) -> u16 {
+ self.get_device_info_rsp.as_ref().mac_version
+ }
+ pub fn get_phy_version(&self) -> u16 {
+ self.get_device_info_rsp.as_ref().phy_version
+ }
+ pub fn get_uci_test_version(&self) -> u16 {
+ self.get_device_info_rsp.as_ref().uci_test_version
+ }
+ pub fn get_vendor_spec_info(&self) -> &Vec<u8> {
+ &self.get_device_info_rsp.as_ref().vendor_spec_info
+ }
+}
+impl Into<UciPacketPacket> for GetDeviceInfoRspPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for GetDeviceInfoRspPacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreResponsePacket> for GetDeviceInfoRspPacket {
+ fn into(self) -> CoreResponsePacket {
+ CoreResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl GetDeviceInfoRspBuilder {
+ pub fn build(self) -> GetDeviceInfoRspPacket {
+ let get_device_info_rsp = Arc::new(GetDeviceInfoRspData {
+ status: self.status,
+ uci_version: self.uci_version,
+ mac_version: self.mac_version,
+ phy_version: self.phy_version,
+ uci_test_version: self.uci_test_version,
+ vendor_spec_info: self.vendor_spec_info,
+ });
+ let core_response = Arc::new(CoreResponseData {
+ child: CoreResponseDataChild::GetDeviceInfoRsp(get_device_info_rsp),
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::CoreResponse(core_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: 2,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ GetDeviceInfoRspPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for GetDeviceInfoRspBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for GetDeviceInfoRspBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+impl Into<CoreResponsePacket> for GetDeviceInfoRspBuilder {
+ fn into(self) -> CoreResponsePacket {
+ self.build().into()
+ }
+}
+macro_rules! get_device_info_rsp_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciResponse(uci_response_packet) => {match uci_response_packet.specialize() {/* (2) */
+UciResponseChild::CoreResponse(core_response_packet) => {match core_response_packet.specialize() {/* (3) */
+CoreResponseChild::GetDeviceInfoRsp(packet) => {let rebuilder = GetDeviceInfoRspBuilder {status : packet.get_status(),uci_version : packet.get_uci_version(),mac_version : packet.get_mac_version(),phy_version : packet.get_phy_version(),uci_test_version : packet.get_uci_test_version(),vendor_spec_info : packet.get_vendor_spec_info().to_vec(),};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse get_device_info_rsp
+ {:#02x?}", core_response_packet); }}}_ => {panic!("Couldn't parse core_response
+ {:#02x?}", uci_response_packet); }}}_ => {panic!("Couldn't parse uci_response
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+get_device_info_rsp_builder_tests! { get_device_info_rsp_builder_test_00: b"\x40\x02\x00\x0b\x01\x01\x00\x02\x00\x03\x00\x04\x00\x01\x0a",}
+
+#[derive(Debug)]
+struct GetCapsInfoCmdData {}
+#[derive(Debug, Clone)]
+pub struct GetCapsInfoCmdPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ core_command: Arc<CoreCommandData>,
+ get_caps_info_cmd: Arc<GetCapsInfoCmdData>,
+}
+#[derive(Debug)]
+pub struct GetCapsInfoCmdBuilder {}
+impl GetCapsInfoCmdData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 4 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ Ok(Self {})
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {}
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ ret
+ }
+}
+impl Packet for GetCapsInfoCmdPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<GetCapsInfoCmdPacket> for Bytes {
+ fn from(packet: GetCapsInfoCmdPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<GetCapsInfoCmdPacket> for Vec<u8> {
+ fn from(packet: GetCapsInfoCmdPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for GetCapsInfoCmdPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl GetCapsInfoCmdPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let core_command = match &uci_command.child {
+ UciCommandDataChild::CoreCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreCommand"),
+ };
+ let get_caps_info_cmd = match &core_command.child {
+ CoreCommandDataChild::GetCapsInfoCmd(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not GetCapsInfoCmd"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ core_command,
+ get_caps_info_cmd,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+}
+impl Into<UciPacketPacket> for GetCapsInfoCmdPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for GetCapsInfoCmdPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreCommandPacket> for GetCapsInfoCmdPacket {
+ fn into(self) -> CoreCommandPacket {
+ CoreCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl GetCapsInfoCmdBuilder {
+ pub fn build(self) -> GetCapsInfoCmdPacket {
+ let get_caps_info_cmd = Arc::new(GetCapsInfoCmdData {});
+ let core_command = Arc::new(CoreCommandData {
+ child: CoreCommandDataChild::GetCapsInfoCmd(get_caps_info_cmd),
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::CoreCommand(core_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: 3,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ GetCapsInfoCmdPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for GetCapsInfoCmdBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for GetCapsInfoCmdBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+impl Into<CoreCommandPacket> for GetCapsInfoCmdBuilder {
+ fn into(self) -> CoreCommandPacket {
+ self.build().into()
+ }
+}
+macro_rules! get_caps_info_cmd_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciCommand(uci_command_packet) => {match uci_command_packet.specialize() {/* (2) */
+UciCommandChild::CoreCommand(core_command_packet) => {match core_command_packet.specialize() {/* (3) */
+CoreCommandChild::GetCapsInfoCmd(packet) => {let rebuilder = GetCapsInfoCmdBuilder {};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse get_caps_info_cmd
+ {:#02x?}", core_command_packet); }}}_ => {panic!("Couldn't parse core_command
+ {:#02x?}", uci_command_packet); }}}_ => {panic!("Couldn't parse uci_command
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+get_caps_info_cmd_builder_tests! { get_caps_info_cmd_builder_test_00: b"\x20\x03\x00\x00",}
+
+#[derive(Debug)]
+struct GetCapsInfoRspData {
+ status: StatusCode,
+ tlvs: Vec<CapTlv>,
+}
+#[derive(Debug, Clone)]
+pub struct GetCapsInfoRspPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ core_response: Arc<CoreResponseData>,
+ get_caps_info_rsp: Arc<GetCapsInfoRspData>,
+}
+#[derive(Debug)]
+pub struct GetCapsInfoRspBuilder {
+ pub status: StatusCode,
+ pub tlvs: Vec<CapTlv>,
+}
+impl GetCapsInfoRspData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 6 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetCapsInfoRsp".to_string(),
+ field: "status".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[4]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "GetCapsInfoRsp".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ if bytes.len() < 6 {
+ return Err(Error::InvalidLengthError {
+ obj: "GetCapsInfoRsp".to_string(),
+ field: "tlvs_count".to_string(),
+ wanted: 6,
+ got: bytes.len(),
+ });
+ }
+ let tlvs_count = u8::from_le_bytes([bytes[5]]);
+ let mut tlvs: Vec<CapTlv> = Vec::new();
+ let mut parsable_ = &bytes[6..];
+ let count_ = tlvs_count as usize;
+ for _ in 0..count_ {
+ match CapTlv::parse(&parsable_) {
+ Ok(parsed) => {
+ parsable_ = &parsable_[parsed.get_total_size()..];
+ tlvs.push(parsed);
+ }
+ Err(Error::ImpossibleStructError) => break,
+ Err(e) => return Err(e),
+ }
+ }
+ Ok(Self { status, tlvs })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let status = self.status.to_u8().unwrap();
+ buffer[4..5].copy_from_slice(&status.to_le_bytes()[0..1]);
+ buffer[5..6].copy_from_slice(&(self.tlvs.len() as u8).to_le_bytes());
+ let mut vec_buffer_ = &mut buffer[6..];
+ for e_ in &self.tlvs {
+ e_.write_to(&mut vec_buffer_[0..e_.get_total_size()]);
+ vec_buffer_ = &mut vec_buffer_[e_.get_total_size()..];
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 2;
+ let ret = ret + self.tlvs.iter().fold(0, |acc, x| acc + x.get_total_size());
+ ret
+ }
+}
+impl Packet for GetCapsInfoRspPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<GetCapsInfoRspPacket> for Bytes {
+ fn from(packet: GetCapsInfoRspPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<GetCapsInfoRspPacket> for Vec<u8> {
+ fn from(packet: GetCapsInfoRspPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for GetCapsInfoRspPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl GetCapsInfoRspPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let core_response = match &uci_response.child {
+ UciResponseDataChild::CoreResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreResponse"),
+ };
+ let get_caps_info_rsp = match &core_response.child {
+ CoreResponseDataChild::GetCapsInfoRsp(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not GetCapsInfoRsp"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ core_response,
+ get_caps_info_rsp,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_status(&self) -> StatusCode {
+ self.get_caps_info_rsp.as_ref().status
+ }
+ pub fn get_tlvs(&self) -> &Vec<CapTlv> {
+ &self.get_caps_info_rsp.as_ref().tlvs
+ }
+}
+impl Into<UciPacketPacket> for GetCapsInfoRspPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for GetCapsInfoRspPacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreResponsePacket> for GetCapsInfoRspPacket {
+ fn into(self) -> CoreResponsePacket {
+ CoreResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl GetCapsInfoRspBuilder {
+ pub fn build(self) -> GetCapsInfoRspPacket {
+ let get_caps_info_rsp = Arc::new(GetCapsInfoRspData {
+ status: self.status,
+ tlvs: self.tlvs,
+ });
+ let core_response = Arc::new(CoreResponseData {
+ child: CoreResponseDataChild::GetCapsInfoRsp(get_caps_info_rsp),
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::CoreResponse(core_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: 3,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ GetCapsInfoRspPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for GetCapsInfoRspBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for GetCapsInfoRspBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+impl Into<CoreResponsePacket> for GetCapsInfoRspBuilder {
+ fn into(self) -> CoreResponsePacket {
+ self.build().into()
+ }
+}
+macro_rules! get_caps_info_rsp_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciResponse(uci_response_packet) => {match uci_response_packet.specialize() {/* (2) */
+UciResponseChild::CoreResponse(core_response_packet) => {match core_response_packet.specialize() {/* (3) */
+CoreResponseChild::GetCapsInfoRsp(packet) => {let rebuilder = GetCapsInfoRspBuilder {status : packet.get_status(),tlvs : packet.get_tlvs().to_vec(),};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse get_caps_info_rsp
+ {:#02x?}", core_response_packet); }}}_ => {panic!("Couldn't parse core_response
+ {:#02x?}", uci_response_packet); }}}_ => {panic!("Couldn't parse uci_response
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+get_caps_info_rsp_builder_tests! { get_caps_info_rsp_builder_test_00: b"\x40\x03\x00\x05\x00\x01\x00\x01\x01",}
+
+#[derive(Debug)]
+struct SetConfigCmdData {
+ parameters: Vec<DeviceParameter>,
+}
+#[derive(Debug, Clone)]
+pub struct SetConfigCmdPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ core_command: Arc<CoreCommandData>,
+ set_config_cmd: Arc<SetConfigCmdData>,
+}
+#[derive(Debug)]
+pub struct SetConfigCmdBuilder {
+ pub parameters: Vec<DeviceParameter>,
+}
+impl SetConfigCmdData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 5 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "SetConfigCmd".to_string(),
+ field: "parameters_count".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let parameters_count = u8::from_le_bytes([bytes[4]]);
+ let mut parameters: Vec<DeviceParameter> = Vec::new();
+ let mut parsable_ = &bytes[5..];
+ let count_ = parameters_count as usize;
+ for _ in 0..count_ {
+ match DeviceParameter::parse(&parsable_) {
+ Ok(parsed) => {
+ parsable_ = &parsable_[parsed.get_total_size()..];
+ parameters.push(parsed);
+ }
+ Err(Error::ImpossibleStructError) => break,
+ Err(e) => return Err(e),
+ }
+ }
+ Ok(Self { parameters })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ buffer[4..5].copy_from_slice(&(self.parameters.len() as u8).to_le_bytes());
+ let mut vec_buffer_ = &mut buffer[5..];
+ for e_ in &self.parameters {
+ e_.write_to(&mut vec_buffer_[0..e_.get_total_size()]);
+ vec_buffer_ = &mut vec_buffer_[e_.get_total_size()..];
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 1;
+ let ret = ret
+ + self
+ .parameters
+ .iter()
+ .fold(0, |acc, x| acc + x.get_total_size());
+ ret
+ }
+}
+impl Packet for SetConfigCmdPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<SetConfigCmdPacket> for Bytes {
+ fn from(packet: SetConfigCmdPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<SetConfigCmdPacket> for Vec<u8> {
+ fn from(packet: SetConfigCmdPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for SetConfigCmdPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl SetConfigCmdPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_command = match &uci_packet.child {
+ UciPacketDataChild::UciCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciCommand"),
+ };
+ let core_command = match &uci_command.child {
+ UciCommandDataChild::CoreCommand(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreCommand"),
+ };
+ let set_config_cmd = match &core_command.child {
+ CoreCommandDataChild::SetConfigCmd(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not SetConfigCmd"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_command,
+ core_command,
+ set_config_cmd,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_parameters(&self) -> &Vec<DeviceParameter> {
+ &self.set_config_cmd.as_ref().parameters
+ }
+}
+impl Into<UciPacketPacket> for SetConfigCmdPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciCommandPacket> for SetConfigCmdPacket {
+ fn into(self) -> UciCommandPacket {
+ UciCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreCommandPacket> for SetConfigCmdPacket {
+ fn into(self) -> CoreCommandPacket {
+ CoreCommandPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl SetConfigCmdBuilder {
+ pub fn build(self) -> SetConfigCmdPacket {
+ let set_config_cmd = Arc::new(SetConfigCmdData {
+ parameters: self.parameters,
+ });
+ let core_command = Arc::new(CoreCommandData {
+ child: CoreCommandDataChild::SetConfigCmd(set_config_cmd),
+ });
+ let uci_command = Arc::new(UciCommandData {
+ child: UciCommandDataChild::CoreCommand(core_command),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Command,
+ opcode: 4,
+ child: UciPacketDataChild::UciCommand(uci_command),
+ });
+ SetConfigCmdPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for SetConfigCmdBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciCommandPacket> for SetConfigCmdBuilder {
+ fn into(self) -> UciCommandPacket {
+ self.build().into()
+ }
+}
+impl Into<CoreCommandPacket> for SetConfigCmdBuilder {
+ fn into(self) -> CoreCommandPacket {
+ self.build().into()
+ }
+}
+macro_rules! set_config_cmd_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciCommand(uci_command_packet) => {match uci_command_packet.specialize() {/* (2) */
+UciCommandChild::CoreCommand(core_command_packet) => {match core_command_packet.specialize() {/* (3) */
+CoreCommandChild::SetConfigCmd(packet) => {let rebuilder = SetConfigCmdBuilder {parameters : packet.get_parameters().to_vec(),};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse set_config_cmd
+ {:#02x?}", core_command_packet); }}}_ => {panic!("Couldn't parse core_command
+ {:#02x?}", uci_command_packet); }}}_ => {panic!("Couldn't parse uci_command
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+set_config_cmd_builder_tests! { set_config_cmd_builder_test_00: b"\x20\x04\x00\x03\x01\x01\x00",}
+
+#[derive(Debug)]
+struct SetConfigRspData {
+ status: StatusCode,
+ parameters: Vec<DeviceConfigStatus>,
+}
+#[derive(Debug, Clone)]
+pub struct SetConfigRspPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_response: Arc<UciResponseData>,
+ core_response: Arc<CoreResponseData>,
+ set_config_rsp: Arc<SetConfigRspData>,
+}
+#[derive(Debug)]
+pub struct SetConfigRspBuilder {
+ pub status: StatusCode,
+ pub parameters: Vec<DeviceConfigStatus>,
+}
+impl SetConfigRspData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 6 {
+ return false;
+ }
+ true
+ }
+ fn parse(bytes: &[u8]) -> Result<Self> {
+ if bytes.len() < 5 {
+ return Err(Error::InvalidLengthError {
+ obj: "SetConfigRsp".to_string(),
+ field: "status".to_string(),
+ wanted: 5,
+ got: bytes.len(),
+ });
+ }
+ let status = u8::from_le_bytes([bytes[4]]);
+ let status = StatusCode::from_u8(status).ok_or_else(|| Error::InvalidEnumValueError {
+ obj: "SetConfigRsp".to_string(),
+ field: "status".to_string(),
+ value: status as u64,
+ type_: "StatusCode".to_string(),
+ })?;
+ if bytes.len() < 6 {
+ return Err(Error::InvalidLengthError {
+ obj: "SetConfigRsp".to_string(),
+ field: "parameters_count".to_string(),
+ wanted: 6,
+ got: bytes.len(),
+ });
+ }
+ let parameters_count = u8::from_le_bytes([bytes[5]]);
+ let want_ = 6 + ((parameters_count as usize) * 2);
+ if bytes.len() < want_ {
+ return Err(Error::InvalidLengthError {
+ obj: "SetConfigRsp".to_string(),
+ field: "parameters".to_string(),
+ wanted: want_,
+ got: bytes.len(),
+ });
+ }
+ let mut parameters: Vec<DeviceConfigStatus> = Vec::new();
+ let mut parsable_ = &bytes[6..];
+ let count_ = parameters_count as usize;
+ for _ in 0..count_ {
+ match DeviceConfigStatus::parse(&parsable_) {
+ Ok(parsed) => {
+ parsable_ = &parsable_[parsed.get_total_size()..];
+ parameters.push(parsed);
+ }
+ Err(Error::ImpossibleStructError) => break,
+ Err(e) => return Err(e),
+ }
+ }
+ Ok(Self { status, parameters })
+ }
+ fn write_to(&self, buffer: &mut BytesMut) {
+ let status = self.status.to_u8().unwrap();
+ buffer[4..5].copy_from_slice(&status.to_le_bytes()[0..1]);
+ buffer[5..6].copy_from_slice(&(self.parameters.len() as u8).to_le_bytes());
+ let mut vec_buffer_ = &mut buffer[6..];
+ for e_ in &self.parameters {
+ e_.write_to(&mut vec_buffer_[0..e_.get_total_size()]);
+ vec_buffer_ = &mut vec_buffer_[e_.get_total_size()..];
+ }
+ }
+ fn get_total_size(&self) -> usize {
+ self.get_size()
+ }
+ fn get_size(&self) -> usize {
+ let ret = 0;
+ let ret = ret + 2;
+ let ret = ret + (self.parameters.len() * ((/* Bits: */16 + /* Dynamic: */ 0) / 8));
+ ret
+ }
+}
+impl Packet for SetConfigRspPacket {
+ fn to_bytes(self) -> Bytes {
+ let mut buffer = BytesMut::new();
+ buffer.resize(self.uci_packet.get_total_size(), 0);
+ self.uci_packet.write_to(&mut buffer);
+ buffer.freeze()
+ }
+ fn to_vec(self) -> Vec<u8> {
+ self.to_bytes().to_vec()
+ }
+}
+impl From<SetConfigRspPacket> for Bytes {
+ fn from(packet: SetConfigRspPacket) -> Self {
+ packet.to_bytes()
+ }
+}
+impl From<SetConfigRspPacket> for Vec<u8> {
+ fn from(packet: SetConfigRspPacket) -> Self {
+ packet.to_vec()
+ }
+}
+impl TryFrom<UciPacketPacket> for SetConfigRspPacket {
+ type Error = TryFromError;
+ fn try_from(value: UciPacketPacket) -> std::result::Result<Self, Self::Error> {
+ Self::new(value.uci_packet).map_err(TryFromError)
+ }
+}
+impl SetConfigRspPacket {
+ fn new(root: Arc<UciPacketData>) -> std::result::Result<Self, &'static str> {
+ let uci_packet = root;
+ let uci_response = match &uci_packet.child {
+ UciPacketDataChild::UciResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not UciResponse"),
+ };
+ let core_response = match &uci_response.child {
+ UciResponseDataChild::CoreResponse(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not CoreResponse"),
+ };
+ let set_config_rsp = match &core_response.child {
+ CoreResponseDataChild::SetConfigRsp(value) => (*value).clone(),
+ _ => return Err("inconsistent state - child was not SetConfigRsp"),
+ };
+ Ok(Self {
+ uci_packet,
+ uci_response,
+ core_response,
+ set_config_rsp,
+ })
+ }
+ pub fn get_group_id(&self) -> GroupId {
+ self.uci_packet.as_ref().group_id
+ }
+ pub fn get_packet_boundary_flag(&self) -> PacketBoundaryFlag {
+ self.uci_packet.as_ref().packet_boundary_flag
+ }
+ pub fn get_message_type(&self) -> MessageType {
+ self.uci_packet.as_ref().message_type
+ }
+ pub fn get_opcode(&self) -> u8 {
+ self.uci_packet.as_ref().opcode
+ }
+ pub fn get_status(&self) -> StatusCode {
+ self.set_config_rsp.as_ref().status
+ }
+ pub fn get_parameters(&self) -> &Vec<DeviceConfigStatus> {
+ &self.set_config_rsp.as_ref().parameters
+ }
+}
+impl Into<UciPacketPacket> for SetConfigRspPacket {
+ fn into(self) -> UciPacketPacket {
+ UciPacketPacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<UciResponsePacket> for SetConfigRspPacket {
+ fn into(self) -> UciResponsePacket {
+ UciResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl Into<CoreResponsePacket> for SetConfigRspPacket {
+ fn into(self) -> CoreResponsePacket {
+ CoreResponsePacket::new(self.uci_packet).unwrap()
+ }
+}
+impl SetConfigRspBuilder {
+ pub fn build(self) -> SetConfigRspPacket {
+ let set_config_rsp = Arc::new(SetConfigRspData {
+ status: self.status,
+ parameters: self.parameters,
+ });
+ let core_response = Arc::new(CoreResponseData {
+ child: CoreResponseDataChild::SetConfigRsp(set_config_rsp),
+ });
+ let uci_response = Arc::new(UciResponseData {
+ child: UciResponseDataChild::CoreResponse(core_response),
+ });
+ let uci_packet = Arc::new(UciPacketData {
+ group_id: GroupId::Core,
+ packet_boundary_flag: PacketBoundaryFlag::Complete,
+ message_type: MessageType::Response,
+ opcode: 4,
+ child: UciPacketDataChild::UciResponse(uci_response),
+ });
+ SetConfigRspPacket::new(uci_packet).unwrap()
+ }
+}
+impl Into<UciPacketPacket> for SetConfigRspBuilder {
+ fn into(self) -> UciPacketPacket {
+ self.build().into()
+ }
+}
+impl Into<UciResponsePacket> for SetConfigRspBuilder {
+ fn into(self) -> UciResponsePacket {
+ self.build().into()
+ }
+}
+impl Into<CoreResponsePacket> for SetConfigRspBuilder {
+ fn into(self) -> CoreResponsePacket {
+ self.build().into()
+ }
+}
+macro_rules! set_config_rsp_builder_tests { ($($name:ident: $byte_string:expr,)*) => {$(
+#[test]
+pub fn $name() { let raw_bytes = $byte_string;/* (0) */
+match UciPacketPacket::parse(raw_bytes) {Ok(uci_packet_packet) => {match uci_packet_packet.specialize() {/* (1) */
+UciPacketChild::UciResponse(uci_response_packet) => {match uci_response_packet.specialize() {/* (2) */
+UciResponseChild::CoreResponse(core_response_packet) => {match core_response_packet.specialize() {/* (3) */
+CoreResponseChild::SetConfigRsp(packet) => {let rebuilder = SetConfigRspBuilder {status : packet.get_status(),parameters : packet.get_parameters().to_vec(),};let rebuilder_base : UciPacketPacket = rebuilder.into();let rebuilder_bytes : &[u8] = &rebuilder_base.to_bytes();assert_eq!(rebuilder_bytes, raw_bytes);}_ => {panic!("Couldn't parse set_config_rsp
+ {:#02x?}", core_response_packet); }}}_ => {panic!("Couldn't parse core_response
+ {:#02x?}", uci_response_packet); }}}_ => {panic!("Couldn't parse uci_response
+ {:#02x?}", uci_packet_packet); }}},Err(e) => panic!("could not parse UciPacket: {:?} {:02x?}", e, raw_bytes),}})*}}
+set_config_rsp_builder_tests! { set_config_rsp_builder_test_00: b"\x40\x04\x00\x04\x01\x01\x01\x01",}
+
+#[derive(Debug)]
+struct GetConfigCmdData {
+ parameter_ids: Vec<u8>,
+}
+#[derive(Debug, Clone)]
+pub struct GetConfigCmdPacket {
+ uci_packet: Arc<UciPacketData>,
+ uci_command: Arc<UciCommandData>,
+ core_command: Arc<CoreCommandData>,
+ get_config_cmd: Arc<GetConfigCmdData>,
+}
+#[derive(Debug)]
+pub struct GetConfigCmdBuilder {
+ pub parameter_ids: Vec<u8>,
+}
+impl GetConfigCmdData {
+ fn conforms(bytes: &[u8]) -> bool {
+ if bytes.len() < 5 {
+ return false;
+ }
+ true
+ }