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(&parameter_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
+    }