diff --git a/.github/workflows/1_run_interoperability_tests.yml b/.github/workflows/1_run_interoperability_tests.yml index 173c82b..08d2716 100644 --- a/.github/workflows/1_run_interoperability_tests.yml +++ b/.github/workflows/1_run_interoperability_tests.yml @@ -6,11 +6,11 @@ on: publishers: description: Publishers to use type: string - default: '["connext_dds","dust_dds","eprosima_fastdds","intercom_dds","opendds","toc_coredx_dds", "hdds", "eclipse_cyclone", "zzdds"]' + default: '["connext_dds","dust_dds","eprosima_fastdds","intercom_dds","opendds","toc_coredx_dds", "hdds", "eclipse_cyclone", "zzdds", "int2dds"]' subscribers: description: Subscribers to use type: string - default: '["connext_dds","dust_dds","eprosima_fastdds","intercom_dds","opendds","toc_coredx_dds", "hdds", "eclipse_cyclone", "zzdds"]' + default: '["connext_dds","dust_dds","eprosima_fastdds","intercom_dds","opendds","toc_coredx_dds", "hdds", "eclipse_cyclone", "zzdds", "int2dds"]' jobs: generate_timestamp: runs-on: ubuntu-latest diff --git a/.github/workflows/ci_int2dds.yml b/.github/workflows/ci_int2dds.yml new file mode 100644 index 0000000..61bcf40 --- /dev/null +++ b/.github/workflows/ci_int2dds.yml @@ -0,0 +1,35 @@ +name: CI int2DDS + +on: + workflow_dispatch: + pull_request: + paths: + - 'srcRs/int2DDS/**' + +jobs: + create_bin_release: + name: Create binary release + runs-on: ubuntu-latest + # Debian 11 ships GLIBC 2.31; the README requires building with 2.31 or older + container: rust:1.89-bullseye + defaults: + run: + working-directory: srcRs/int2DDS + steps: + - name: Checkout sources + uses: actions/checkout@v7 + - name: Install zip + run: apt-get update && apt-get install -y zip + - name: Build executable + run: cargo build --package int2dds_shape_main_linux --release --locked + - name: Rename executable + run: | + version=$( cargo tree --package int2dds --depth 0 --prefix none | awk '{print $2}' | tr -d 'v' ) + cp ./target/release/int2dds_shape_main_linux ./int2dds-${version}_shape_main_linux + mkdir artifacts + zip --junk-paths artifacts/int2dds-${version}_shape_main_linux.zip ./int2dds-${version}_shape_main_linux + - name: Upload executable artifact + uses: actions/upload-artifact@v7 + with: + name: interoperability_executable + path: srcRs/int2DDS/artifacts/ diff --git a/CLA/CLA_Intellectus.md b/CLA/CLA_Intellectus.md new file mode 100644 index 0000000..e91042d --- /dev/null +++ b/CLA/CLA_Intellectus.md @@ -0,0 +1,61 @@ +# OMG DDS INTEROPERABILITY REPOSITORY - CONTRIBUTOR LICENSE AGREEMENT + +**This Contributor License Agreement ("Agreement") specifies the terms under which the individual or corporate entity specified in the signature block below (“You”) agree to make intellectual property contributions to the OMG DDS Interoperability Repository. BY SIGNING BELOW YOU ARE AGREEING TO BE BOUND BY THE TERMS OF THIS AGREEMENT. If You are signing this Agreement in Your capacity as an employee, THEN YOUR EMPLOYER AND YOU ARE BOTH BOUND BY THIS AGREEMENT.** + +1. Definitions + + 1. "OMG DDS Interoperability Repository" (or “Repository”) means the Git repository [https://github.com/omg-dds/dds-rtps](https://github.com/omg-dds/dds-rtps). + + 2. "Moderator" means an entity or individual responsible for authorizing changes to the Repository. + + 3. "Submit" (or “Submitted”) means any submission, including source code, binaries, code, pull requests, issue reports, comments, etc., made to the Moderators for inclusion in the Repository either through the Git repository interface or through electronic file transfer. + + 4. A "Contribution" is any original work of authorship, including any modifications or additions to an existing work, that You Submit to the DDS Interoperability Repository. + + 5. A "User" is anyone who accesses the Repository. + +2. Allowable Contribution Representations + + 1. You represent that You have the necessary rights to the Contribution(s) to meet the obligations of this Agreement. If You are employed, Your employer has authorized Contribution(s) under this Agreement. + + 2. You represent that you have no knowledge of third-party intellectual property rights that are likely to be infringed by the Contribution(s). You represent that you have no knowledge that such infringement or any allegation of misappropriation of intellectual property rights is likely to be claimed or has already been claimed. + +3. License + + You grant Moderators a perpetual, worldwide, non-exclusive, assignable, paid-up license to publish, display, and redistribute the Contribution as part of the Repository. You also license to Moderators under the same terms any other intellectual property rights required to publish, display, and redistribute the Contributions as part of the Repository. You further grant all Users of the Repository a license to the Contribution under the terms of the [OMG DDS Interoperability Testing License](../LICENSE.md) included in the Repository. Moderators are under no obligation to publish Contributions. + +4. No Warranty, Consequential Damages. Limited Liability + + Other than explicitly stated herein, You provide the Contribution(s) "as is" with no warranty nor claims of fitness to any purpose. Neither party shall be liable for consequential or special damages of any kind. Other than for breach of warranty or representations herein, the liability of either party to the other shall be limited to $1000. + +5. General + + 1. If You are an agency of the United States Government, then this Agreement will be governed by the United States federal common law. Otherwise, this Agreement will be governed by the laws of the State of California except with regard to its choice of law rules. + + 2. A party may assign this Agreement to an entity acquiring essentially all of the party’s relevant business. + +6. Electronic Signatures + + "Electronic Signature" means any electronic sound, symbol, or process attached to or logically associated with a record and executed and adopted by a party with the intent to sign such record. + + Each party agrees that the Electronic Signatures, whether digital or encrypted, of the parties included in this Agreement are intended to authenticate this writing and to have the same force and effect as manual signatures. + + +IN WITNESS WHEREOF, You, intending to be legally bound, have executed this Agreement or caused Your employer’s proper and duly authorized officer to execute and deliver this Agreement, for good and valuable consideration, the sufficiency of which is hereby acknowledged, as of the day and year first written below. + +**For:** + +Entity Name: Intellectus Corp. + +Address: 8F, 13, Yeongdong-daero 85-gil, Gangnam-gu, Seoul 06183, Republic of Korea + + ("**You**") + +**By:** + +Name: Lee Sang Woong + +Title: CTO + +Date: October 28, 2026 + diff --git a/generate_xlsx_report.py b/generate_xlsx_report.py index cff1b24..b725c0c 100644 --- a/generate_xlsx_report.py +++ b/generate_xlsx_report.py @@ -85,6 +85,8 @@ def get_company_name(product:str) -> str: return 'Eclipse Foundation' elif 'zzdds' in product.lower() or 'zenzen' in product.lower(): return 'Zenzen IoT' + elif 'int2dds' in product.lower(): + return 'Intellectus Corp.' else: raise RuntimeError('Impossible to get company name: ' + product) @@ -112,6 +114,8 @@ def get_product_name(product:str) -> str: return 'Cyclone DDS ' + re.search(r'([\d.]+)', product).group(1) elif 'zzdds' in product.lower(): return 'Zenzen DDS ' + re.search(r'([\d.]+)', product).group(1) + elif 'int2dds' in product.lower(): + return 'int2DDS ' + re.search(r'int2dds-([\d.]+)', product.lower()).group(1) else: raise RuntimeError('Impossible to get product name: ' + product) diff --git a/srcRs/int2DDS/.gitignore b/srcRs/int2DDS/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/srcRs/int2DDS/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/srcRs/int2DDS/Cargo.lock b/srcRs/int2DDS/Cargo.lock new file mode 100644 index 0000000..805e604 --- /dev/null +++ b/srcRs/int2DDS/Cargo.lock @@ -0,0 +1,2276 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "arc-swap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "clap_lex" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" +dependencies = [ + "const-default-derive", +] + +[[package]] +name = "const-default-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f814dd8cbb812233751ff6857b7fa86d9f52e88ac64e8f54e7a1ca0168f03da" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctrlc" +version = "3.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" +dependencies = [ + "dispatch2", + "nix 0.31.3", + "windows-sys 0.61.2", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "defmt" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a0795ac740cb54d7b3775c9c17345f6aaecf884c01733b39d6017738e0e3e1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "840bb056bd99c495c9188198a258ab787c79f306d864db3e6e8451442d6d8366" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "defmt-parser" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71958f69fb2de856c5dd51ac9377e986ca1aa5919f931bf5395a807277f889a0" +dependencies = [ + "thiserror 2.0.21", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.2", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + +[[package]] +name = "env_filter" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "getset" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpio-cdev" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09831ec59b80be69e75d29cf36e16afbbe5fd1af9c1bf4689ad91c77db5aa6a6" +dependencies = [ + "bitflags 2.13.2", + "libc", + "nix 0.27.1", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + +[[package]] +name = "hostname" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +dependencies = [ + "cfg-if", + "libc", + "windows-link", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" + +[[package]] +name = "icu_properties" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" + +[[package]] +name = "icu_provider" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if-addrs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a05c691e1fae256cf7013d99dad472dc52d5543322761f83ec8d47eab40d2b" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "int2dds" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b25f0f20cb6f8126ee5489e2e17f6f0c31eb270b1b554a5d441275c2af30d8cc" +dependencies = [ + "arc-swap", + "bitflags 2.13.2", + "bytes", + "chrono", + "clap", + "const-default", + "crossbeam-channel", + "dashmap", + "env_logger", + "fern", + "flume", + "futures-util", + "gpio-cdev", + "hostname", + "if-addrs", + "int2dds-derive", + "libc", + "libloading", + "local-ip-address", + "log", + "md5", + "mio", + "network-interface", + "num_cpus", + "paste", + "rand 0.9.5", + "regex", + "roxmltree", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "smallvec", + "socket2 0.5.10", + "speedy 0.8.7", + "strum", + "strum_macros", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "tokio-util", + "uuid", + "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "int2dds-derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a246911b5084973a7f2eb2fada400d575bc09ead627681c23e5b26712cb7a73" +dependencies = [ + "md5", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "int2dds_shape_main_linux" +version = "0.1.0" +dependencies = [ + "clap", + "ctrlc", + "fastrand", + "int2dds", + "log", + "md5", + "speedy 0.6.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b005715dcbeb0089a3c0dab99f2ff1cc3b2525323552703d648585d342a383" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc9817253cf7c7ee4684451bd327e88d6f3658014e54a29198625590650695c" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.191" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "local-ip-address" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa08fb2b1ec3ea84575e94b489d06d4ce0cbf052d12acd515838f50e3c3d63e3" +dependencies = [ + "libc", + "neli", + "windows-sys 0.61.2", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "md5" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebb8d8732c6a6df3d8f032a82911cfc747e00efb95cc46e8d0acd5b5b88570c" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "neli" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b152f4f18819e638ea350a740b16de75099302b5ec3a58f4f8766ef2fcf695d" +dependencies = [ + "bitflags 2.13.2", + "byteorder", + "derive_builder", + "getset", + "libc", + "linux-raw-sys", + "log", + "neli-proc-macros", + "parking_lot", +] + +[[package]] +name = "neli-proc-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d8d08c6e98f20a62417478ebf7be8e1425ec9acecc6f63e22da633f6b71609" +dependencies = [ + "either", + "proc-macro2", + "quote", + "serde", + "syn 2.0.119", +] + +[[package]] +name = "network-interface" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddcb8865ad3d9950f22f42ffa0ef0aecbfbf191867b3122413602b0a360b2a6" +dependencies = [ + "cc", + "libc", + "thiserror 2.0.21", + "winapi", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.13.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.2", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustls" +version = "0.23.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "speedy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98c153e20b455c34900383abc7654734babcea8d683377c1a99c54178ab1577" +dependencies = [ + "speedy-derive 0.6.0", +] + +[[package]] +name = "speedy" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1992073f0e55aab599f4483c460598219b4f9ff0affa124b33580ab511e25a" +dependencies = [ + "memoffset", + "speedy-derive 0.8.7", +] + +[[package]] +name = "speedy-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060ee719e5fcf469c8cc3b236ac07d9e42a137edcb78c7fc7ebfd8b8787388b9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "speedy-derive" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f2ca5276b92c3dfd65fa88316b4e032ace68f88d7570b43967784c0bac5ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tinystr" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.5", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.8", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "yoke" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/srcRs/int2DDS/Cargo.toml b/srcRs/int2DDS/Cargo.toml new file mode 100644 index 0000000..09aea0a --- /dev/null +++ b/srcRs/int2DDS/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] + +[package] +name = "int2dds_shape_main_linux" +version = "0.1.0" +edition = "2021" +description = "OMG DDS-RTPS Interoperability" +repository = "https://github.com/IntellectusCorp/int2DDS" +publish = false + +[dependencies] +int2dds = { version = "1.0.0" } + +clap = { version = "4.2.7", features = ["derive"] } +log = "0.4.11" +speedy = "0.6" +md5 = "0.8.0" +ctrlc = "3.0" +fastrand = "2.0" diff --git a/srcRs/int2DDS/src/main.rs b/srcRs/int2DDS/src/main.rs new file mode 100644 index 0000000..2cc1e36 --- /dev/null +++ b/srcRs/int2DDS/src/main.rs @@ -0,0 +1,1703 @@ +#![allow(dead_code)] + +use std::io::Write; +use std::process; +use std::sync::{Arc, OnceLock}; +use std::time::Duration as StdDuration; + +// Print a line and flush stdout immediately. +macro_rules! println_flush { + ($($arg:tt)*) => {{ + println!($($arg)*); + let _ = std::io::stdout().flush(); + }}; +} + +use clap::{Parser, ValueEnum}; + +use int2dds::core::types::LENGTH_UNLIMITED; +use int2dds::dcps::infrastructure::qos_policy::OwnershipStrengthQosPolicy; +use int2dds::{ + common::instance_handle::InstanceHandle, + core::{error::DdsError, time::Duration}, + domain::{domain_participant_factory::DomainParticipantFactory, qos::DomainParticipantQos}, + infrastructure::{ + qos_policy::{ + DataFragQosPolicy, DataRepresentationId, DataRepresentationQosPolicy, DeadlineQosPolicy, + DurabilityQosPolicy, DurabilityQosPolicyKind, HistoryQosPolicy, HistoryQosPolicyKind, + LifespanQosPolicy, OwnershipQosPolicy, OwnershipQosPolicyKind, PartitionQosPolicy, + PresentationQosAccessScopeKind, ReliabilityQosPolicy, + ReliabilityQosPolicyKind, + TimeBasedFilterQosPolicy, + }, + status::StatusMask, + }, + publication::{ + data_writer_listener::DataWriterListener, + qos::{DataWriterQos, PublisherQos}, + }, + subscription::{ + data_reader_listener::DataReaderListener, + qos::{DataReaderQos, SubscriberQos}, + sample_info::{InstanceStateKind, SampleStateKind, ViewStateKind}, + }, + topic::{qos::TopicQos, topic_listener::TopicListener, type_support::DdsType}, +}; + +#[derive(DdsType)] +#[dds_type(crate_path = "int2dds", extensibility = "Appendable")] +struct ShapeType { + #[dds(key, bound = 128)] + color: String, + x: i32, + y: i32, + shapesize: i32, + #[dds(uint8)] + additional_payload_size: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, ValueEnum)] +enum Verbosity { + #[value(name = "e")] + Error = 1, + #[value(name = "d")] + Debug = 2, +} + +struct Logger { + verbosity: Verbosity, +} + +impl Logger { + fn new(verbosity: Verbosity) -> Self { + Self { verbosity } + } + + fn log_message(&self, message: &str, level: Verbosity) { + if level as i32 <= self.verbosity as i32 { + println_flush!("{}", message); + } + } + + fn set_verbosity(&mut self, verbosity: Verbosity) { + self.verbosity = verbosity; + } +} + +static LOGGER: OnceLock = OnceLock::new(); + +fn init_logger(verbosity: Verbosity) { + let _ = LOGGER.set(Logger::new(verbosity)); +} + +fn get_logger() -> &'static Logger { + LOGGER.get().expect("Logger not initialized") +} + +struct QosUtils; + +impl QosUtils { + fn get_reliability_policy(options: &ShapeOptions) -> ReliabilityQosPolicy { + ReliabilityQosPolicy { + kind: if options.best_effort { + ReliabilityQosPolicyKind::BestEffort + } else { + ReliabilityQosPolicyKind::Reliable + }, + max_blocking_time: Duration { sec: 0, nanosec: 100_000_000 }, + } + } + + fn get_durability_policy(options: &ShapeOptions) -> DurabilityQosPolicy { + DurabilityQosPolicy { + kind: match options.durability.chars().next().unwrap_or('v') { + 'v' => DurabilityQosPolicyKind::Volatile, + 'l' => DurabilityQosPolicyKind::TransientLocal, + 't' => DurabilityQosPolicyKind::Transient, + 'p' => DurabilityQosPolicyKind::Persistent, + _ => DurabilityQosPolicyKind::Volatile, + }, + } + } + + fn get_ownership_policy(options: &ShapeOptions) -> OwnershipQosPolicy { + OwnershipQosPolicy { + kind: if options.ownership_strength != -1 { + OwnershipQosPolicyKind::Exclusive + } else { + OwnershipQosPolicyKind::Shared + }, + } + } + + fn get_ownership_strength_policy(options: &ShapeOptions) -> OwnershipStrengthQosPolicy { + OwnershipStrengthQosPolicy { + value: if options.ownership_strength != -1 { + options.ownership_strength + } else { + 0 // Default value for shared ownership + }, + } + } + + fn get_history_policy(options: &ShapeOptions) -> HistoryQosPolicy { + HistoryQosPolicy { + kind: match options.history_depth { + depth if depth > 0 => HistoryQosPolicyKind::KeepLast(depth), + 0 => HistoryQosPolicyKind::KeepAll, + _ => HistoryQosPolicyKind::default(), + }, + ..Default::default() + } + } + + fn get_access_scope_kind(options: &ShapeOptions) -> PresentationQosAccessScopeKind { + match options.access_scope.as_deref() { + Some("i") | Some("instance") => PresentationQosAccessScopeKind::Instance, + Some("t") | Some("topic") => PresentationQosAccessScopeKind::Topic, + Some("g") | Some("group") => PresentationQosAccessScopeKind::Group, + _ => PresentationQosAccessScopeKind::Instance, + } + } + + fn get_deadline_policy(options: &ShapeOptions) -> DeadlineQosPolicy { + if options.deadline_interval == 0 { + DeadlineQosPolicy::default() + } else { + DeadlineQosPolicy { period: Duration::from_millis(options.deadline_interval as i64) } + } + } + + fn get_lifespan_policy(options: &ShapeOptions) -> LifespanQosPolicy { + if options.lifespan > 0 { + LifespanQosPolicy { + duration: Duration { + sec: (options.get_lifespan_us() / 1_000_000) as i32, + nanosec: ((options.get_lifespan_us() % 1_000_000) * 1_000) as u32, + }, + } + } else { + LifespanQosPolicy::default() // infinite + } + } + + fn get_time_based_filter_policy(options: &ShapeOptions) -> TimeBasedFilterQosPolicy { + let time_filter_us = options.get_time_filter_us(); + if time_filter_us > 0 { + TimeBasedFilterQosPolicy { + minimum_separation: Duration { + sec: (time_filter_us / 1_000_000) as i32, + nanosec: ((time_filter_us % 1_000_000) * 1_000) as u32, + }, + } + } else { + TimeBasedFilterQosPolicy::default() // no filter + } + } + + fn get_data_representation_policy(options: &ShapeOptions) -> DataRepresentationQosPolicy { + match options.data_representation { + 1 => DataRepresentationQosPolicy { + value: vec![DataRepresentationId::XcdrDataRepresentation], + }, + 2 => DataRepresentationQosPolicy { + value: vec![DataRepresentationId::Xcdr2DataRepresentation], + }, + _ => DataRepresentationQosPolicy { + value: vec![DataRepresentationId::XcdrDataRepresentation], + }, // default to XCDR + } + } + fn reliability_to_string(kind: ReliabilityQosPolicyKind) -> String { + match kind { + ReliabilityQosPolicyKind::BestEffort => "BEST_EFFORT".to_string(), + ReliabilityQosPolicyKind::Reliable => "RELIABLE".to_string(), + } + } + + fn durability_to_string(kind: DurabilityQosPolicyKind) -> String { + match kind { + DurabilityQosPolicyKind::Volatile => "VOLATILE".to_string(), + DurabilityQosPolicyKind::TransientLocal => "TRANSIENT_LOCAL".to_string(), + DurabilityQosPolicyKind::Transient => "TRANSIENT".to_string(), + DurabilityQosPolicyKind::Persistent => "PERSISTENT".to_string(), + } + } + + fn ownership_to_string(kind: OwnershipQosPolicyKind) -> String { + match kind { + OwnershipQosPolicyKind::Shared => "SHARED".to_string(), + OwnershipQosPolicyKind::Exclusive => "EXCLUSIVE".to_string(), + } + } + + fn history_to_string(kind: HistoryQosPolicyKind) -> String { + match kind { + HistoryQosPolicyKind::KeepAll => "KEEP_ALL".to_string(), + HistoryQosPolicyKind::KeepLast(_) => "KEEP_LAST".to_string(), + } + } + + fn access_scope_to_string(scope: PresentationQosAccessScopeKind) -> String { + match scope { + PresentationQosAccessScopeKind::Instance => "INSTANCE_PRESENTATION_QOS".to_string(), + PresentationQosAccessScopeKind::Topic => "TOPIC_PRESENTATION_QOS".to_string(), + PresentationQosAccessScopeKind::Group => "GROUP_PRESENTATION_QOS".to_string(), + } + } + + fn verbosity_to_string(verbosity: Verbosity) -> String { + match verbosity { + Verbosity::Error => "ERROR".to_string(), + Verbosity::Debug => "DEBUG".to_string(), + } + } + + fn data_representation_to_string(policy: &DataRepresentationQosPolicy) -> String { + if policy.value.is_empty() { + return "XCDR".to_string(); // default + } + + let mut representations = Vec::new(); + for repr in &policy.value { + match repr { + DataRepresentationId::XcdrDataRepresentation => representations.push("XCDR"), + DataRepresentationId::Xcdr2DataRepresentation => representations.push("XCDR2"), + DataRepresentationId::XmlDataRepresentation => representations.push("XML"), + } + } + representations.join(", ") + } + + fn data_representation_id_to_string(data_representation_value: DataRepresentationId) -> String { + match data_representation_value { + DataRepresentationId::XcdrDataRepresentation => "XCDR".to_string(), + DataRepresentationId::Xcdr2DataRepresentation => "XCDR2".to_string(), + DataRepresentationId::XmlDataRepresentation => "XML".to_string(), + } + } +} + +/// DDS Shapes Demo Application +#[derive(Parser, Clone)] +#[command(author, version, about, long_about = None)] +#[command(allow_negative_numbers = true)] +struct ShapeOptions { + /// Set log message verbosity + #[arg(short = 'v', value_enum, default_value_t = Verbosity::Error)] + verbosity: Verbosity, + + /// Publish samples + #[arg(short = 'P', default_value_t = false)] + publish: bool, + + /// Subscribe samples + #[arg(short = 'S', default_value_t = false)] + subscribe: bool, + + /// Domain id + #[arg(short = 'd', default_value_t = 0)] + domain_id: u32, + + /// BEST_EFFORT reliability + #[arg(short = 'b', default_value_t = false)] + best_effort: bool, + + /// RELIABLE reliability (default) + #[arg(short = 'r', default_value_t = false)] + reliable: bool, + + /// Keep history depth [0: KEEP_ALL] + #[arg(short = 'k', default_value_t = -1)] + history_depth: i32, + + /// Set a 'deadline' with interval (ms) [0: OFF] + #[arg(short = 'f', default_value_t = 0)] + deadline_interval: i32, + + /// Set ownership strength [-1: SHARED] + #[arg(short = 's', default_value_t = -1)] + ownership_strength: i32, + + /// Set the topic name + #[arg(short = 't')] + topic_name: Option, + + /// Set color to publish (filter if subscriber) + #[arg(short = 'c')] + color: Option, + + /// Set a 'partition' string + #[arg(short = 'p')] + partition: Option, + + /// Set durability (v=volatile, l=transient-local, t=transient, p=persistent) + #[arg(short = 'D', default_value = "v")] + durability: String, + + /// Set data representation [1: XCDR, 2: XCDR2] + #[arg(short = 'x', default_value_t = 1)] + data_representation: u8, + + /// Print Publisher's samples + #[arg(short = 'w', default_value_t = false)] + print_writer_samples: bool, + + /// Set shapesize (0: increase the size for every sample) + #[arg(short = 'z', default_value_t = 20)] + shapesize: i32, + + /// Use 'read()' instead of 'take()' + #[arg(short = 'R', default_value_t = false)] + use_read: bool, + + /// Waiting period between 'write()' operations in ms + #[arg(long = "write-period", default_value_t = 33)] + write_period: u64, + + /// Waiting period between 'read()' or 'take()' operations in ms + #[arg(long = "read-period", default_value_t = 100)] + read_period: u64, + + /// Apply 'time based filter' with interval in ms [0: OFF] + #[arg(long = "time-filter", default_value_t = 0)] + time_filter: u64, + + /// Indicates the lifespan of a sample in ms + #[arg(long = "lifespan", default_value_t = 0)] + lifespan: u64, + + /// Number of iterations of the main loop. After that, the application will exit + #[arg(long = "num-iterations", default_value_t = 0)] + num_iterations: u32, + + /// Number of instances a DataWriter writes + #[arg(long = "num-instances", default_value_t = 1)] + num_instances: u32, + + /// Number of topics created (using the same type) + #[arg(long = "num-topics", default_value_t = 1)] + num_topics: u32, + + /// Action performed after the DataWriter finishes its execution (u=unregister, d=dispose) + #[arg(long = "final-instance-state")] + final_instance_state: Option, + + /// Sets Presentation.access_scope to INSTANCE, TOPIC or GROUP (i=instance, t=topic, g=group) + #[arg(long = "access-scope")] + access_scope: Option, + + /// Sets Presentation.coherent_access = true + #[arg(long = "coherent", default_value_t = false)] + coherent: bool, + + /// Sets Presentation.ordered_access = true + #[arg(long = "ordered", default_value_t = false)] + ordered: bool, + + /// Amount of samples sent for each DataWriter and instance that are grouped in a coherent set + #[arg(long = "coherent-sample-count", default_value_t = 0)] + coherent_sample_count: u32, + + /// Amount of bytes added to the samples written + #[arg(long = "additional-payload-size", default_value_t = 0)] + additional_payload_size: u32, + + /// Uses take()/read() instead of take_next_instance() read_next_instance() + #[arg(long = "take-read", default_value_t = false)] + take_read: bool, + + /// Periodic participant announcement period in ms. Default 0 (off) + #[arg(long = "periodic-announcement", default_value_t = 0)] + periodic_announcement: u64, + + /// Content filter expression (subscriber). Overrides default color filter. + #[arg(long = "cft")] + cft_expression: Option, + + /// Publisher size modulo. shapesize cycles 1..=N (requires -z 0) + #[arg(long = "size-modulo", default_value_t = 0)] + size_modulo: u32, + + /// Set the data fragment size in bytes (default: 0, means not set) + #[arg(long = "datafrag-size", default_value_t = 0)] + datafrag_size: u32, + + /// Flag to track if access scope was explicitly set + #[arg(skip)] + coherent_set_access_scope_set: bool, +} + +impl ShapeOptions { + fn validate_and_postprocess(&mut self) -> Result<(), i32> { + let logger = get_logger(); + + // Validation + if self.topic_name.is_none() { + logger.log_message("please specify topic name [-t]", Verbosity::Error); + return Err(1); + } + if !self.publish && !self.subscribe { + logger.log_message("please specify publish [-P] or subscribe [-S]", Verbosity::Error); + return Err(1); + } + if self.publish && self.subscribe { + logger.log_message( + "please specify only one of: publish [-P] or subscribe [-S]", + Verbosity::Error, + ); + return Err(1); + } + + // Set default color for publisher if not specified + if self.publish && self.color.is_none() { + logger.log_message( + "warning: color was not specified, defaulting to \"BLUE\"", + Verbosity::Error, + ); + self.color = Some("BLUE".to_string()); + } + + // --cft and -c cannot be specified together for the subscriber + if self.subscribe && self.cft_expression.is_some() && self.color.is_some() { + logger.log_message( + "error: cannot specify both --cft and -c for subscriber applications", + Verbosity::Error, + ); + return Err(1); + } + + // Validation for final instance state + if let Some(state) = &self.final_instance_state { + match state.as_str() { + "u" | "unregister" => {} + "d" | "dispose" => {} + _ => { + logger.log_message( + &format!("Invalid final instance state: {}", state), + Verbosity::Error, + ); + return Err(1); + } + } + } + + // Validation for data representation + match self.data_representation { + 1 | 2 => {} // Valid values: 1 = XCDR, 2 = XCDR2 + _ => { + logger.log_message( + &format!( + "Invalid data representation: {}. Valid options are: 1 (XCDR), 2 (XCDR2)", + self.data_representation + ), + Verbosity::Error, + ); + return Err(1); + } + } + + // Validation for durability: reject unrecognized values (empty keeps default) + if let Some(c) = self.durability.chars().next() { + if !matches!(c, 'v' | 'l' | 't' | 'p') { + logger.log_message( + &format!("unrecognized value for durability {}", c), + Verbosity::Error, + ); + return Err(1); + } + } + + // Reject fragment sizes above 65535 bytes + if self.datafrag_size > 65535 { + logger.log_message( + &format!( + "incorrect value for datafrag-size, it must be <= 65535 bytes: {}", + self.datafrag_size + ), + Verbosity::Error, + ); + return Err(1); + } + + if self.coherent_sample_count == 1 { + logger.log_message( + &format!( + "incorrect value for coherent-sample-count, it must be >=2 {}", + self.coherent_sample_count + ), + Verbosity::Error, + ); + return Err(1); + } + + self.coherent_set_access_scope_set = self.access_scope.is_some(); + + self.print_warnings(&logger); + + self.print_configuration(&logger); + + Ok(()) + } + + fn print_warnings(&self, logger: &Logger) { + if self.publish && self.time_filter > 0 { + logger.log_message( + "warning: time base filter [--time-filter] ignored on publisher applications", + Verbosity::Error, + ); + } + if self.publish && self.use_read { + logger.log_message( + "warning: use read [-R] ignored on publisher applications", + Verbosity::Error, + ); + } + if self.publish && self.take_read { + logger.log_message( + "warning: --take-read ignored on publisher applications", + Verbosity::Error, + ); + } + if self.publish && self.cft_expression.is_some() { + logger.log_message( + "warning: --cft ignored on publisher applications", + Verbosity::Error, + ); + } + if self.subscribe && self.shapesize != 20 { + logger.log_message( + "warning: shapesize [-z] ignored on subscriber applications", + Verbosity::Error, + ); + } + if self.subscribe && self.lifespan > 0 { + logger.log_message( + "warning: --lifespan ignored on subscriber applications", + Verbosity::Error, + ); + } + if self.subscribe && self.num_instances > 1 { + logger.log_message( + "warning: --num-instances ignored on subscriber applications", + Verbosity::Error, + ); + } + if self.subscribe && self.final_instance_state.is_some() { + logger.log_message( + "warning: --final-instance-state ignored on subscriber applications", + Verbosity::Error, + ); + } + if self.subscribe && self.coherent_sample_count > 0 { + logger.log_message( + "warning: --coherent-sample-count ignored on subscriber applications", + Verbosity::Error, + ); + } + if !self.coherent && !self.ordered && self.coherent_sample_count > 0 { + logger.log_message("warning: --coherent-sample-count ignored because not coherent, or ordered access enabled", Verbosity::Error); + } + if self.subscribe && self.additional_payload_size > 0 { + logger.log_message( + "warning: --additional-payload-size ignored on subscriber applications", + Verbosity::Error, + ); + } + if !self.coherent && !self.ordered && self.access_scope.is_some() { + logger.log_message( + "warning: --access-scope set but not coherent, or ordered access enabled", + Verbosity::Error, + ); + } + if self.size_modulo > 0 && self.shapesize != 0 { + logger.log_message( + "warning: --size-modulo has no effect unless shapesize (-z) is set to 0", + Verbosity::Error, + ); + } + } + + fn print_configuration(&self, logger: &Logger) { + let app_kind = if self.publish { "publisher" } else { "subscriber" }; + let data_representation_policy = QosUtils::get_data_representation_policy(self); + logger.log_message(&format!( + "Shape Options: \n Verbosity = {}\n This application is a {}\n DomainId = {}\n ReliabilityKind = {}\n DurabilityKind = {}\n DataRepresentation = {}\n HistoryDepth = {}\n OwnershipStrength = {}\n TimeBasedFilterInterval = {}ms\n DeadlineInterval = {}ms\n Shapesize = {}\n Reading method = {}\n Write period = {}ms\n Read period = {}ms\n Lifespan = {}ms\n Number of iterations = {}\n Number of instances = {}\n Number of entities = {}\n Coherent sets = {}\n Ordered access = {}\n Access Scope = {}\n Coherent Sample Count = {}\n Additional Payload Size = {}\n Final Instance State = {}\n Periodic Announcement Period = {}ms\n Data Fragmentation Size = {} bytes", + QosUtils::verbosity_to_string(logger.verbosity), + app_kind, + self.domain_id, + QosUtils::reliability_to_string(QosUtils::get_reliability_policy(self).kind), + QosUtils::durability_to_string(QosUtils::get_durability_policy(self).kind), + QosUtils::data_representation_to_string(&data_representation_policy), + self.history_depth, + self.ownership_strength, + self.time_filter, + self.deadline_interval, + self.shapesize, + match (self.use_read, !self.take_read) { + (false, true) => "take_next_instance", + (false, false) => "take", + (true, true) => "read_next_instance", + (true, false) => "read", + }, + self.write_period, + self.read_period, + self.lifespan, + self.num_iterations, + self.num_instances, + self.num_topics, + self.coherent, + self.ordered, + QosUtils::access_scope_to_string(QosUtils::get_access_scope_kind(self)), + self.coherent_sample_count, + self.additional_payload_size, + self.get_final_instance_state_string(), + self.periodic_announcement, + self.datafrag_size + ), Verbosity::Debug); + + if let Some(ref topic_name) = self.topic_name { + logger.log_message(&format!(" Topic = {}", topic_name), Verbosity::Debug); + } + if let Some(ref color) = self.color { + logger.log_message(&format!(" Color = {}", color), Verbosity::Debug); + } + if let Some(ref partition) = self.partition { + logger.log_message(&format!(" Partition = {}", partition), Verbosity::Debug); + } + } + + fn get_final_instance_state_string(&self) -> String { + match self.final_instance_state.as_deref() { + Some("u") | Some("unregister") => "Unregister".to_string(), + Some("d") | Some("dispose") => "Dispose".to_string(), + _ => "not specified".to_string(), + } + } + + // Period options are in milliseconds. These return microseconds. + fn get_write_period_us(&self) -> u64 { + self.write_period * 1000 + } + + fn get_read_period_us(&self) -> u64 { + self.read_period * 1000 + } + + fn get_time_filter_us(&self) -> u64 { + self.time_filter * 1000 + } + + fn get_lifespan_us(&self) -> u64 { + self.lifespan * 1000 + } + + fn get_periodic_announcement_us(&self) -> u64 { + self.periodic_announcement * 1000 + } + + fn should_unregister(&self) -> bool { + matches!(self.final_instance_state.as_deref(), Some("u") | Some("unregister")) + } + + fn should_dispose(&self) -> bool { + matches!(self.final_instance_state.as_deref(), Some("d") | Some("dispose")) + } + + // Drawing area size and default velocity + fn get_da_width(&self) -> i32 { + 240 + } + fn get_da_height(&self) -> i32 { + 270 + } + fn get_xvel(&self) -> i32 { + 3 + } + fn get_yvel(&self) -> i32 { + 3 + } +} + +struct PubListener; + +impl DataWriterListener for PubListener { + type Foo = ShapeType; + + fn on_offered_incompatible_qos( + &self, + writer: &int2dds::publication::data_writer::DataWriter, + status: &int2dds::infrastructure::status::OfferedIncompatibleQosStatus, + ) { + let topic = writer.get_topic().unwrap(); + let topic_name = topic.get_name(); + let type_name = topic.get_type_name(); + println_flush!( + "on_offered_incompatible_qos() topic: '{}' type: '{}' : {:?} ({})", + topic_name, + type_name, + status.last_policy_id(), + status.last_policy_id().as_str() + ); + } + + fn on_publication_matched( + &self, + writer: &int2dds::publication::data_writer::DataWriter, + status: &int2dds::infrastructure::status::PublicationMatchedStatus, + ) { + let topic = writer.get_topic().unwrap(); + let topic_name = topic.get_name(); + let type_name = topic.get_type_name(); + println_flush!( + "on_publication_matched() topic: '{}' type: '{}' : matched readers {} (change = {})", + topic_name, + type_name, + status.current_count(), + status.current_count_change() + ); + } + + fn on_offered_deadline_missed( + &self, + writer: &int2dds::publication::data_writer::DataWriter, + status: &int2dds::infrastructure::status::OfferedDeadlineMissedStatus, + ) { + let topic = writer.get_topic().unwrap(); + let topic_name = topic.get_name(); + let type_name = topic.get_type_name(); + println_flush!( + "on_offered_deadline_missed() topic: '{}' type: '{}' : (total = {}, change = {})", + topic_name, + type_name, + status.total_count(), + status.total_count_change() + ); + } + + fn on_liveliness_lost( + &self, + writer: &int2dds::publication::data_writer::DataWriter, + status: &int2dds::infrastructure::status::LivelinessLostStatus, + ) { + let topic = writer.get_topic().unwrap(); + let topic_name = topic.get_name(); + let type_name = topic.get_type_name(); + println_flush!( + "on_liveliness_lost() topic: '{}' type: '{}' : (total = {}, change = {})", + topic_name, + type_name, + status.total_count(), + status.total_count_change() + ); + } +} + +struct SubListener; + +impl DataReaderListener for SubListener { + type Foo = ShapeType; + + fn on_requested_incompatible_qos( + &self, + reader: &int2dds::subscription::data_reader::DataReader, + status: &int2dds::infrastructure::status::RequestedIncompatibleQosStatus, + ) { + let topic_description = reader.get_topicdescription().unwrap(); + let topic_name = topic_description.get_name(); + let type_name = topic_description.get_type_name(); + println_flush!( + "on_requested_incompatible_qos() topic: '{}' type: '{}' : {:?} ({})", + topic_name, + type_name, + status.last_policy_id(), + status.last_policy_id().as_str() + ); + } + + fn on_subscription_matched( + &self, + reader: &int2dds::subscription::data_reader::DataReader, + status: &int2dds::infrastructure::status::SubscriptionMatchedStatus, + ) { + let topic_description = reader.get_topicdescription().unwrap(); + let topic_name = topic_description.get_name(); + let type_name = topic_description.get_type_name(); + println_flush!( + "on_subscription_matched() topic: '{}' type: '{}' : matched writers {} (change = {})", + topic_name, + type_name, + status.current_count(), + status.current_count_change() + ); + } + + fn on_requested_deadline_missed( + &self, + reader: &int2dds::subscription::data_reader::DataReader, + status: &int2dds::infrastructure::status::RequestedDeadlineMissedStatus, + ) { + let topic_description = reader.get_topicdescription().unwrap(); + let topic_name = topic_description.get_name(); + let type_name = topic_description.get_type_name(); + println_flush!( + "on_requested_deadline_missed() topic: '{}' type: '{}' : (total = {}, change = {})", + topic_name, + type_name, + status.total_count(), + status.total_count_change() + ); + } + + fn on_liveliness_changed( + &self, + reader: &int2dds::subscription::data_reader::DataReader, + status: &int2dds::infrastructure::status::LivelinessChangedStatus, + ) { + let topic_description = reader.get_topicdescription().unwrap(); + let topic_name = topic_description.get_name(); + let type_name = topic_description.get_type_name(); + println_flush!( + "on_liveliness_changed() topic: '{}' type: '{}' : (alive = {}, not_alive = {})", + topic_name, + type_name, + status.alive_count(), + status.not_alive_count() + ); + } +} + +struct ShapeTopicListener; + +impl TopicListener for ShapeTopicListener { + fn on_inconsistent_topic( + &self, + topic: &int2dds::topic::topic::Topic, + _status: &int2dds::infrastructure::status::InconsistentTopicStatus, + ) { + let topic_name = topic.get_name(); + let type_name = topic.get_type_name(); + println_flush!("on_inconsistent_topic() topic: '{}' type: '{}'", topic_name, type_name); + } +} + +use std::sync::atomic::{AtomicBool, Ordering}; + +static SHOULD_EXIT: AtomicBool = AtomicBool::new(false); + +fn install_signal_handlers() -> Result<(), Box> { + ctrlc::set_handler(move || { + SHOULD_EXIT.store(true, Ordering::SeqCst); + })?; + Ok(()) +} + +struct ShapeApplication { + participant: Option>, + topics: Vec>, + publisher: Option>, + subscriber: Option>, + data_writers: Vec>>, + data_readers: Vec>>, +} + +impl ShapeApplication { + fn new() -> Self { + Self { + participant: None, + topics: Vec::new(), + publisher: None, + subscriber: None, + data_writers: Vec::new(), + data_readers: Vec::new(), + } + } + + fn initialize(&mut self, options: &ShapeOptions) -> Result<(), DdsError> { + let logger = get_logger(); + logger.log_message("Running initialize() function", Verbosity::Debug); + + let factory = DomainParticipantFactory::get_instance(); + let participant_qos = DomainParticipantQos::default(); + let participant = factory.create_participant( + options.domain_id.try_into().unwrap(), + participant_qos, + None, + StatusMask::default(), + )?; + let participant_arc = Arc::new(participant); + self.participant = Some(participant_arc.clone()); + + // Create topics + let base_topic_name = options.topic_name.as_ref().unwrap(); + for i in 0..options.num_topics { + let topic_name = + if i == 0 { base_topic_name.clone() } else { format!("{}{}", base_topic_name, i) }; + println_flush!("Create topic: {}", topic_name); + let topic_listener = ShapeTopicListener; + let topic = participant_arc.create_topic::( + &topic_name, + "ShapeType", + TopicQos::default(), + Some(Arc::new(topic_listener)), + StatusMask::default(), + )?; + self.topics.push(Arc::new(topic)); + } + + if options.publish { + self.init_publisher(options)?; + } else { + self.init_subscriber(options)?; + } + + Ok(()) + } + + fn run(&self, options: &ShapeOptions) -> Result<(), DdsError> { + let logger = get_logger(); + logger.log_message("Running run() function", Verbosity::Debug); + + if options.publish { + self.run_publisher(options) + } else { + self.run_subscriber(options) + } + } + + fn cleanup(&mut self) -> Result<(), DdsError> { + if let Some(participant_arc) = self.participant.take() { + participant_arc.delete_contained_entities()?; + + self.data_writers.clear(); + self.data_readers.clear(); + self.publisher = None; + self.subscriber = None; + self.topics.clear(); + + let participant = (*participant_arc).clone(); + drop(participant_arc); + DomainParticipantFactory::get_instance().delete_participant(participant)?; + } + Ok(()) + } + + fn init_publisher(&mut self, options: &ShapeOptions) -> Result<(), DdsError> { + let logger = get_logger(); + logger.log_message("Running init_publisher() function", Verbosity::Debug); + + let participant = self.participant.as_ref().unwrap(); + + // Create publisher + let mut publisher_qos = PublisherQos::default(); + if let Some(ref partition) = options.partition { + publisher_qos.partition = PartitionQosPolicy { name: vec![partition.clone()] }; + } + + if options.coherent { + publisher_qos.presentation.coherent_access = true; + } + if options.ordered { + publisher_qos.presentation.ordered_access = true; + } + if options.coherent || options.ordered { + publisher_qos.presentation.access_scope = QosUtils::get_access_scope_kind(options); + } + + logger.log_message("Publisher QoS:", Verbosity::Debug); + logger.log_message( + &format!( + " Presentation Coherent Access = {}", + publisher_qos.presentation.coherent_access + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Presentation Ordered Access = {}", + publisher_qos.presentation.ordered_access + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Presentation Access Scope = {}", + QosUtils::access_scope_to_string(publisher_qos.presentation.access_scope) + ), + Verbosity::Debug, + ); + + let publisher = participant.create_publisher(publisher_qos, None, StatusMask::default())?; + let publisher_arc = Arc::new(publisher); + self.publisher = Some(publisher_arc.clone()); + + // Create data writers + let mut writer_qos = DataWriterQos { + reliability: QosUtils::get_reliability_policy(options), + durability: QosUtils::get_durability_policy(options), + ownership: QosUtils::get_ownership_policy(options), + ownership_strength: QosUtils::get_ownership_strength_policy(options), + history: QosUtils::get_history_policy(options), + deadline: QosUtils::get_deadline_policy(options), + lifespan: QosUtils::get_lifespan_policy(options), + data_representation: QosUtils::get_data_representation_policy(options), + ..DataWriterQos::default() + }; + if options.should_unregister() { + writer_qos.writer_data_lifecycle.autodispose_unregistered_instances = false; + } + // Per-writer RTPS DATA_FRAG fragment size. + if options.datafrag_size > 0 { + writer_qos.data_frag = DataFragQosPolicy { max_size: options.datafrag_size as i32 }; + } + // Reliable writers send heartbeats standalone (periodic), not piggybacked. + if writer_qos.reliability.kind == ReliabilityQosPolicyKind::Reliable { + writer_qos.writer_reliability_extension.disable_piggyback_heartbeat = true; + writer_qos.writer_reliability_extension.heartbeat_period = Duration::from_millis(100); + } + + logger.log_message("Data Writer QoS:", Verbosity::Debug); + logger.log_message( + &format!( + " Reliability = {}", + QosUtils::reliability_to_string(writer_qos.reliability.kind) + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Durability = {}", + QosUtils::durability_to_string(writer_qos.durability.kind) + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Ownership = {}", + QosUtils::ownership_to_string(writer_qos.ownership.kind) + ), + Verbosity::Debug, + ); + logger.log_message( + &format!(" History = {}", QosUtils::history_to_string(writer_qos.history.kind)), + Verbosity::Debug, + ); + + for (_i, topic) in self.topics.iter().enumerate() { + println_flush!( + "Create writer for topic: {} color: {}", + topic.get_name(), + options.color.as_ref().unwrap() + ); + let listener = PubListener; + let writer = publisher_arc.create_datawriter::( + topic, + writer_qos.clone(), + Some(Arc::new(listener)), + StatusMask::default(), + )?; + self.data_writers.push(Arc::new(writer)); + } + + Ok(()) + } + + fn init_subscriber(&mut self, options: &ShapeOptions) -> Result<(), DdsError> { + let logger = get_logger(); + logger.log_message("Running init_subscriber() function", Verbosity::Debug); + + let participant = self.participant.as_ref().unwrap(); + + // Create subscriber + let mut subscriber_qos = SubscriberQos::default(); + if let Some(ref partition) = options.partition { + subscriber_qos.partition = PartitionQosPolicy { name: vec![partition.to_owned()] }; + } + + if options.coherent { + subscriber_qos.presentation.coherent_access = true; + } + if options.ordered { + subscriber_qos.presentation.ordered_access = true; + } + if options.coherent || options.ordered { + subscriber_qos.presentation.access_scope = QosUtils::get_access_scope_kind(options); + } + + logger.log_message("Subscriber QoS:", Verbosity::Debug); + logger.log_message( + &format!( + " Presentation Coherent Access = {}", + subscriber_qos.presentation.coherent_access + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Presentation Ordered Access = {}", + subscriber_qos.presentation.ordered_access + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Presentation Access Scope = {}", + QosUtils::access_scope_to_string(subscriber_qos.presentation.access_scope) + ), + Verbosity::Debug, + ); + + let subscriber = + participant.create_subscriber(subscriber_qos, None, StatusMask::default())?; + let subscriber_arc = Arc::new(subscriber); + self.subscriber = Some(subscriber_arc.clone()); + + // Create data readers + let data_reader_qos = DataReaderQos { + reliability: QosUtils::get_reliability_policy(options), + durability: QosUtils::get_durability_policy(options), + ownership: QosUtils::get_ownership_policy(options), + history: QosUtils::get_history_policy(options), + deadline: QosUtils::get_deadline_policy(options), + data_representation: QosUtils::get_data_representation_policy(options), + time_based_filter: QosUtils::get_time_based_filter_policy(options), + ..DataReaderQos::default() + }; + + logger.log_message("Data Reader QoS:", Verbosity::Debug); + logger.log_message( + &format!( + " Reliability = {}", + QosUtils::reliability_to_string(data_reader_qos.reliability.kind) + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Durability = {}", + QosUtils::durability_to_string(data_reader_qos.durability.kind) + ), + Verbosity::Debug, + ); + logger.log_message( + &format!( + " Ownership = {}", + QosUtils::ownership_to_string(data_reader_qos.ownership.kind) + ), + Verbosity::Debug, + ); + logger.log_message( + &format!(" History = {}", QosUtils::history_to_string(data_reader_qos.history.kind)), + Verbosity::Debug, + ); + + if let Some(ref expr) = options.cft_expression { + // Custom content filter expression with its parameters embedded in the expression + for (i, topic) in self.topics.iter().enumerate() { + let filtered_topic_name = format!( + "{}{}_filtered", + &topic.get_name(), + if i > 0 { i.to_string() } else { String::new() } + ); + + logger.log_message( + &format!(" ContentFilterTopic = \"{}\"", expr), + Verbosity::Debug, + ); + + let cft = participant + .create_contentfilteredtopic::( + &filtered_topic_name, + &topic, + expr, + Vec::new(), + ) + .map_err(|e| { + DdsError::Error(format!("Failed to create content filtered topic: {:?}", e)) + })?; + + println_flush!("Create reader for topic: {} cft: \"{}\"", topic.get_name(), expr); + + let listener = SubListener; + let reader = subscriber_arc + .create_datareader::( + &cft, + data_reader_qos.clone(), + Some(Arc::new(listener)), + StatusMask::default(), + ) + .map_err(|e| { + DdsError::Error(format!("Failed to create data reader: {:?}", e)) + })?; + + self.data_readers.push(Arc::new(reader)); + } + } else if let Some(ref color) = options.color { + // Filter on the specified color with a content filtered topic + for (i, topic) in self.topics.iter().enumerate() { + let filtered_topic_name = format!( + "{}{}_filtered", + &topic.get_name(), + if i > 0 { i.to_string() } else { String::new() } + ); + + let parameter = format!("'{}'", color); + + logger.log_message( + &format!(" ContentFilterTopic = \"color = {}\"", parameter), + Verbosity::Debug, + ); + + let cft = participant + .create_contentfilteredtopic::( + &filtered_topic_name, + &topic, + "color = %0", + vec![color.to_owned()], + ) + .map_err(|e| { + DdsError::Error(format!("Failed to create content filtered topic: {:?}", e)) + })?; + + println_flush!("Create reader for topic: {} color: {}", topic.get_name(), color); + + let listener = SubListener; + let reader = subscriber_arc + .create_datareader::( + &cft, + data_reader_qos.clone(), + Some(Arc::new(listener)), + StatusMask::default(), + ) + .map_err(|e| { + DdsError::Error(format!("Failed to create data reader: {:?}", e)) + })?; + + self.data_readers.push(Arc::new(reader)); + } + } else { + for topic in &self.topics { + println_flush!("Create reader for topic: {}", topic.get_name()); + + let listener = SubListener; + let reader = subscriber_arc.create_datareader::( + topic.as_ref(), + data_reader_qos.clone(), + Some(Arc::new(listener)), + StatusMask::default(), + )?; + self.data_readers.push(Arc::new(reader)); + } + } + + logger.log_message("Data Reader created", Verbosity::Debug); + + Ok(()) + } + + fn run_publisher(&self, options: &ShapeOptions) -> Result<(), DdsError> { + let logger = get_logger(); + logger.log_message("Running run_publisher() function", Verbosity::Debug); + + let publisher = self.publisher.as_ref().unwrap(); + let data_writers = &self.data_writers; + + logger.log_message("Data Writer created", Verbosity::Debug); + logger.log_message(&format!("Color {}", options.color.as_ref().unwrap()), Verbosity::Debug); + logger.log_message(&format!("xvel {}", options.get_xvel()), Verbosity::Debug); + logger.log_message(&format!("yvel {}", options.get_yvel()), Verbosity::Debug); + logger.log_message(&format!("da_width {}", options.get_da_width()), Verbosity::Debug); + logger.log_message(&format!("da_height {}", options.get_da_height()), Verbosity::Debug); + + // Initialize shape + let mut shape = ShapeType { + color: options.color.clone().unwrap(), + x: fastrand::i32(0..options.get_da_width()), + y: fastrand::i32(0..options.get_da_height()), + shapesize: options.shapesize, + additional_payload_size: if options.additional_payload_size > 0 { + vec![255u8; options.additional_payload_size as usize] + } else { + vec![] + }, + }; + + let mut xvel = ((fastrand::u32(0..5) + 1) as i32) * if fastrand::bool() { -1 } else { 1 }; + let mut yvel = ((fastrand::u32(0..5) + 1) as i32) * if fastrand::bool() { -1 } else { 1 }; + + let mut n = 0; + + loop { + // Move shape + shape.x += xvel; + shape.y += yvel; + + if shape.x < 0 { + shape.x = 0; + xvel = -xvel; + } + if shape.x > options.get_da_width() { + shape.x = options.get_da_width(); + xvel = -xvel; + } + if shape.y < 0 { + shape.y = 0; + yvel = -yvel; + } + if shape.y > options.get_da_height() { + shape.y = options.get_da_height(); + yvel = -yvel; + } + + if options.shapesize == 0 { + if options.size_modulo > 0 { + // Cycle 1..=size_modulo (size cannot be 0) + shape.shapesize = (shape.shapesize % options.size_modulo as i32) + 1; + } else { + shape.shapesize += 1; + } + } + + if (options.coherent || options.ordered) + && options.coherent_sample_count != 0 + && n % options.coherent_sample_count == 0 + { + println_flush!("Started Coherent Set"); + let _ = publisher.begin_coherent_changes(); + } + + // Write one sample per instance on every writer + for writer in data_writers { + for j in 0..options.num_instances { + // Instances after the first append their index to the color + let instance_shape = ShapeType { + color: if options.num_instances > 1 && j > 0 { + format!("{}{}", options.color.as_ref().unwrap(), j) + } else { + options.color.clone().unwrap_or_else(|| "RED".to_string()) + }, + ..shape.clone() + }; + + writer.write(&instance_shape, InstanceHandle::NIL)?; + + if options.print_writer_samples { + let additional_payload_info = if options.additional_payload_size > 0 { + format!( + " {{{}}}", + instance_shape.additional_payload_size.last().unwrap_or(&0) + ) + } else { + String::new() + }; + + let topic = writer.get_topic()?; + let topic_name = topic.get_name(); + let sanitized_topic = sanitize_xml_text(&topic_name); + let sanitized_color = sanitize_xml_text(&instance_shape.color); + println_flush!( + "{:<10} {:<10} {:03} {:03} [{}]{}", + sanitized_topic, + sanitized_color, + instance_shape.x, + instance_shape.y, + instance_shape.shapesize, + additional_payload_info + ); + } + } + } + + if (options.coherent || options.ordered) + && options.coherent_sample_count != 0 + && n % options.coherent_sample_count == options.coherent_sample_count - 1 + { + println_flush!("Finished Coherent Set"); + let _ = publisher.end_coherent_changes(); + } + + std::thread::sleep(StdDuration::from_micros(options.get_write_period_us())); + + n += 1; + logger.log_message(&format!("Publisher iteration: <{}>", n), Verbosity::Debug); + logger.log_message( + &format!("Max number of iterations <{}>", options.num_iterations), + Verbosity::Debug, + ); + + if options.num_iterations != 0 && options.num_iterations <= n { + break; + } + + if SHOULD_EXIT.load(Ordering::SeqCst) { + break; + } + } + + // Unregister or dispose each instance so that the subscriber observes the final state. + if options.should_dispose() || options.should_unregister() { + for writer in data_writers { + for j in 0..options.num_instances { + let instance_shape = ShapeType { + color: if options.num_instances > 1 && j > 0 { + format!("{}{}", options.color.as_ref().unwrap(), j) + } else { + options.color.clone().unwrap() + }, + ..shape.clone() + }; + if options.should_dispose() { + writer.dispose(&instance_shape, InstanceHandle::NIL)?; + } else { + writer.unregister_instance(&instance_shape, InstanceHandle::NIL)?; + } + } + } + } + + // Wait for acknowledgments so the subscriber has a chance to observe the final state. + for writer in data_writers { + let _ = writer.wait_for_acknowledgments(Duration { sec: 1, nanosec: 0 }); + } + + Ok(()) + } + + fn run_subscriber(&self, options: &ShapeOptions) -> Result<(), DdsError> { + let logger = get_logger(); + logger.log_message("Running run_subscriber() function", Verbosity::Debug); + + let subscriber = self.subscriber.as_ref().ok_or(DdsError::PreconditionNotMet)?; + let data_readers = &self.data_readers; + + let mut n = 0; + + loop { + if options.coherent { + println_flush!("Reading coherent sets, iteration {}", n); + } + if options.ordered { + println_flush!("Reading with ordered access, iteration {}", n); + } + if options.coherent || options.ordered { + let _ = subscriber.begin_access(); + } + + for reader in data_readers { + let mut previous_handle = InstanceHandle::NIL; + loop { + if SHOULD_EXIT.load(Ordering::SeqCst) { + break; + } + // --take-read selects take()/read() instead of the *_next_instance() variants + let use_next_instance = !options.take_read; + let log_msg = match (options.use_read, use_next_instance) { + (false, true) => "Calling take_next_instance() function", + (false, false) => "Calling take() function", + (true, true) => "Calling read_next_instance() function", + (true, false) => "Calling read() function", + }; + + logger.log_message(log_msg, Verbosity::Debug); + + let result = match (options.use_read, use_next_instance) { + (false, true) => reader.take_next_instance( + LENGTH_UNLIMITED, + previous_handle, + &[SampleStateKind::ANY_SAMPLE_STATE], + &[ViewStateKind::ANY_VIEW_STATE], + &[InstanceStateKind::ANY_INSTANCE_STATE], + ), + (false, false) => reader.take( + LENGTH_UNLIMITED, + &[SampleStateKind::ANY_SAMPLE_STATE], + &[ViewStateKind::ANY_VIEW_STATE], + &[InstanceStateKind::ANY_INSTANCE_STATE], + ), + (true, true) => reader.read_next_instance( + LENGTH_UNLIMITED, + previous_handle, + &[SampleStateKind::ANY_SAMPLE_STATE], + &[ViewStateKind::ANY_VIEW_STATE], + &[InstanceStateKind::ANY_INSTANCE_STATE], + ), + (true, false) => reader.read( + LENGTH_UNLIMITED, + &[SampleStateKind::ANY_SAMPLE_STATE], + &[ViewStateKind::ANY_VIEW_STATE], + &[InstanceStateKind::ANY_INSTANCE_STATE], + ), + }; + + match result { + Ok(samples) => { + if samples.is_empty() { + break; + } + + logger.log_message( + &format!("Read {} sample(s), printing them...", samples.len()), + Verbosity::Debug, + ); + + for (n_sample, sample) in samples.iter().enumerate() { + logger.log_message( + &format!("Processing sample {}", n_sample), + Verbosity::Debug, + ); + + let info = sample.sample_info(); + let topic_description = reader.get_topicdescription()?; + let topic_name = topic_description.get_name(); + let sanitized_topic = sanitize_xml_text(&topic_name); + + if info.valid_data { + let data = match sample.data() { + Ok(d) => d, + Err(e) => { + logger.log_message( + &format!( + "warning: deserialize failed: {}", + e + ), + Verbosity::Error, + ); + continue; + } + }; + let sanitized_color = sanitize_xml_text(&data.color); + println_flush!( + "{:<10} {:<10} {:03} {:03} [{}]{}", + sanitized_topic, + sanitized_color, + data.x, + data.y, + data.shapesize, + if !data.additional_payload_size.is_empty() { + format!( + " {{{}}}", + data.additional_payload_size.last().unwrap_or(&0) + ) + } else { + String::new() + } + ); + } + + if info.instance_state + != InstanceStateKind::ALIVE_INSTANCE_STATE + { + let color = reader + .get_key_value(info.instance_handle) + .map(|k| k.color) + .unwrap_or_default(); + let sanitized_color = sanitize_xml_text(&color); + match info.instance_state { + InstanceStateKind::NOT_ALIVE_NO_WRITERS_INSTANCE_STATE => { + println_flush!( + "{:<10} {:<10} NOT_ALIVE_NO_WRITERS_INSTANCE_STATE", + sanitized_topic, sanitized_color + ); + } + InstanceStateKind::NOT_ALIVE_DISPOSED_INSTANCE_STATE => { + println_flush!( + "{:<10} {:<10} NOT_ALIVE_DISPOSED_INSTANCE_STATE", + sanitized_topic, sanitized_color + ); + } + _ => {} + } + } + } + + if let Some(first) = samples.first() { + previous_handle = first.sample_info().instance_handle; + } + } + Err(_) => break, + } + } + } + + if options.coherent || options.ordered { + let _ = subscriber.end_access(); + } + + n += 1; + logger.log_message(&format!("Subscriber iteration: <{}>", n), Verbosity::Debug); + logger.log_message( + &format!("Max number of iterations <{}>", options.num_iterations), + Verbosity::Debug, + ); + + if options.num_iterations != 0 && options.num_iterations <= n { + break; + } + + if SHOULD_EXIT.load(Ordering::SeqCst) { + break; + } + + std::thread::sleep(StdDuration::from_micros(options.get_read_period_us())); + } + + Ok(()) + } +} + +fn main() -> Result<(), Box> { + let _ = install_signal_handlers(); + + let mut options = ShapeOptions::parse(); + + init_logger(options.verbosity); + + // Validate options and fill in defaults + if let Err(code) = options.validate_and_postprocess() { + process::exit(code); + } + + let logger = get_logger(); + logger.log_message("Initializing ShapeApp...", Verbosity::Debug); + + let mut shape_app = ShapeApplication::new(); + + if let Err(e) = shape_app.initialize(&options) { + eprintln!("Error initializing application: {}", e); + process::exit(2); + } + + logger.log_message("Running ShapeApp...", Verbosity::Debug); + + shape_app.run(&options).unwrap_or_else(|e| { + eprintln!("Error running application: {}", e); + process::exit(3); + }); + + if let Err(e) = shape_app.cleanup() { + eprintln!("Error during cleanup: {}", e); + } + + println_flush!("Done."); + Ok(()) +} + +use std::borrow::Cow; + +fn sanitize_xml_text(input: &str) -> Cow<'_, str> { + if input.chars().all(is_valid_xml_char) { + Cow::Borrowed(input) + } else { + let mut sanitized = String::with_capacity(input.len()); + for ch in input.chars() { + if is_valid_xml_char(ch) { + sanitized.push(ch); + } + } + Cow::Owned(sanitized) + } +} + +fn is_valid_xml_char(ch: char) -> bool { + matches!(ch, '\u{9}' | '\u{A}' | '\u{D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}') +} diff --git a/test_suite.py b/test_suite.py index 5d58ac3..e146162 100644 --- a/test_suite.py +++ b/test_suite.py @@ -251,6 +251,27 @@ 'instance, without losses or duplicates, in the same order as sent\n' }, + # Unlike Test_History_0/1 (depth 5, no losses expected), depth 1 is the + # most common real-world setting and intentionally drops superseded + # samples; the writer must GAP them for the RELIABLE protocol to proceed. + 'Test_History_2' : { + 'apps' : ['-P -t Square -r -k 1 -z 0 --write-period 10', + '-S -t Square -r -k 1'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'check_function' : tsf.test_order_w_instances, + 'title' : 'Behavior of KEEP_LAST 1 history', + 'description' : 'Verifies a RELIABLE, KEEP_LAST 1 publisher communicates with a RELIABLE, KEEP_LAST 1 ' + 'subscriber. The publisher writes much faster than the subscriber reads, so older ' + 'unread samples are expected to be superseded and never delivered.\n\n' + ' * Configures the publisher and subscriber with a RELIABLE reliability\n' + ' * Configures the publisher and subscriber with history KEEP_LAST 1\n' + ' * Configures the publisher with a writing period of 10ms\n' + ' * The publisher application sends samples with increasing value of the "size" member\n' + ' * Verifies the publisher and subscriber discover and match each other\n\n' + 'The test passes if the subscriber receives samples with a strictly increasing ' + f'"size" value for {tsf.MAX_SAMPLES_READ} samples, even though some are skipped\n' + }, + # OWNERSHIP 'Test_Ownership_0' : { 'apps' : ['-P -t Square -s -1', '-S -t Square -s -1'], @@ -487,6 +508,19 @@ 'The test passes if the publisher and subscriber do not discover each other\n' }, + 'Test_Topic_2' : { + 'apps' : ['-P -t Square --num-topics 3', '-S -t Square --num-topics 3'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'check_function' : tsf.test_receiving_on_3_topics, + 'title' : 'Communication using several topics on the same participant', + 'description' : 'Verifies a publisher and a subscriber, each publishing/subscribing to several topics ' + 'from a single participant, discover and match on every topic\n\n' + ' * Configures the publisher and subscriber with 3 topics: "Square", "Square1", "Square2"\n' + ' * Each topic has its own DataWriter / DataReader on the same participant\n' + ' * Verifies the publisher and subscriber discover and match each other on every topic\n\n' + 'The test passes if the subscriber receives samples on every topic\n' + }, + # Content Filtered Topic 'Test_Cft_0' : { 'apps' : ['-P -t Square -r -k 0 -c BLUE', '-P -t Square -r -k 0 -c RED', '-S -t Square -r -k 0 -c RED'], @@ -523,6 +557,26 @@ f'The test passes if the subscriber receives {tsf.MAX_SAMPLES_READ/2} samples with size < 20\n' }, + # Combines a key and a non-key field in one compound expression, unlike + # Test_Cft_0 (key only) and Test_Cft_1 (non-key only). + 'Test_Cft_2': { + 'apps': ['-P -t Square -r -k 0 -c RED -z 15', + '-P -t Square -r -k 0 -c RED -z 25', + '-S -t Square -r -k 0 --cft "(color = \'RED\') AND (shapesize <= 20)"'], + 'expected_codes': [ReturnCode.OK, ReturnCode.OK, ReturnCode.RECEIVING_FROM_ONE], + 'check_function': tsf.test_size_receivers, + 'title' : 'Use of a compound content filter combining a key and a non-key field', + 'description': 'Verifies a subscription using a ContentFilteredTopic compound (AND) expression only ' + 'receives data that passes both clauses\n\n' + ' * Use RELIABLE Qos in all publishers and subscriber to avoid samples losses\n' + ' * Configures the publisher / subscriber with history KEEP_ALL\n' + ' * Configures a first publisher with "color" equal to "RED" and constant "shapesize" 15\n' + ' * Configures a second publisher with "color" equal to "RED" and constant "shapesize" 25\n' + ' * Subscriber uses --cft "(color = \'RED\') AND (shapesize <= 20)"\n\n' + f'The test passes if the subscriber receives {tsf.MAX_SAMPLES_READ} samples from the ' + 'first publisher only\n' + }, + # PARTITION 'Test_Partition_0' : { 'apps' : ['-P -t Square -p "p1"', '-S -t Square -p "p1"'], @@ -562,6 +616,40 @@ '(first publisher)\n' }, + # Exercises the single-character "?" wildcard, a distinct partition + # matching rule from the "*" (any number of characters) case above. + 'Test_Partition_3' : { + 'apps' : ['-P -t Square -p "p1" -c BLUE', + '-P -t Square -p "pxx" -c RED', + '-S -t Square -p "p?"'], + 'check_function' : tsf.test_color_receivers, + 'expected_codes' : [ReturnCode.OK, ReturnCode.READER_NOT_MATCHED, ReturnCode.RECEIVING_FROM_ONE], + 'title' : 'Usage of a partition "?" wildcard to match a single character', + 'description' : 'Verifies a subscription using a partition "?" wildcard only receives data from ' + 'publishers whose partition differs by exactly one character\n\n' + ' * Configures a subscriber with a PARTITION expression "p?" that only matches ' + 'partitions consisting of "p" followed by exactly one character\n' + ' * Configures a first publisher to use PARTITION "p1" and "color" equal to "BLUE"\n' + ' * Configures a second publisher to use PARTITION "pxx" and "color" equal to "RED"\n' + ' * Verifies that only the first publisher (PARTITION "p1") discovers and matches subscriber\n' + ' * Verifies that the second publisher (PARTITION "pxx") does not match the subscriber\n\n' + f'The test passes if the subscriber receives {tsf.MAX_SAMPLES_READ} samples of one color ' + '(first publisher)\n' + }, + + # The DDS spec treats the default (empty) partition as equivalent to a + # single empty-string partition, which "*" must also match. + 'Test_Partition_4' : { + 'apps' : ['-P -t Square', '-S -t Square -p "*"'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'title' : 'Communication between a publisher using the default partition and a subscriber using "*"', + 'description' : 'Verifies a subscriber using the wildcard partition "*" matches a publisher that does ' + 'not set a PARTITION (the default, empty partition)\n\n' + ' * Configures the publisher with no PARTITION (default)\n' + ' * Configures the subscriber with PARTITION expression "*"\n\n' + 'The test passes if the subscriber receives samples from the publisher\n' + }, + # DURABILITY 'Test_Durability_0' : { 'apps' : ['-P -t Square -D v', '-S -t Square -D v'], @@ -882,6 +970,24 @@ 'the last byte sent in additional_payload_size is correctly set.\n' }, + # BEST_EFFORT variant of Test_LargeData_0: large samples must be fragmented + # (RTPS DATA_FRAG) and reassembled without the retransmission safety net + # that RELIABLE provides. + 'Test_LargeData_1' : { + 'apps' : ['-P -t Square -b --additional-payload-size 100000', + '-S -t Square -b'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'check_function' : tsf.test_large_data, + 'title' : 'Test large data with BEST_EFFORT reliability', + 'description' : 'This test covers the interoperability scenario with large, fragmented data ' + 'sent over BEST_EFFORT reliability:\n\n' + ' * Configures the publisher / subscriber with a BEST_EFFORT reliability\n' + ' * Configures the publisher to use 100000 additional payload size (to represent large data samples)\n' + ' * Verifies the publisher and subscriber discover and match each other\n\n' + 'The tests passes if the subscriber receives samples from the publisher and ' + 'the last byte sent in additional_payload_size is correctly set.\n' + }, + 'Test_Lifespan_0' : { 'apps' : ['-P -t Square -r -k 0 -z 0 --write-period 100 --lifespan 250', '-S -t Square -r -k 0 --read-period 500'], diff --git a/test_suite_functions.py b/test_suite_functions.py index 54a8cad..de16759 100644 --- a/test_suite_functions.py +++ b/test_suite_functions.py @@ -137,6 +137,66 @@ def test_color_receivers(child_sub, samples_sent, last_sample_saved, timeout): print(f'Samples read: {samples_read}') return ReturnCode.RECEIVING_FROM_ONE +def _test_receiving_on_all_topics(child_sub, samples_sent, last_sample_saved, + timeout, num_topics): + """ + This function is used by test cases in which the subscriber application + creates one DataReader per topic. This tests that samples are received on + every topic, and therefore by every DataReader. + + child_sub: child program generated with pexpect + samples_sent: not used + last_sample_saved: not used + timeout: time pexpect waits until it matches a pattern. + num_topics: number of topics that must receive samples. + """ + basic_check_retcode = basic_check(child_sub, samples_sent, last_sample_saved, timeout) + + if basic_check_retcode != ReturnCode.OK: + return basic_check_retcode + + sub_string = re.search(r'(\w+)\s+\w+\s+[0-9]+ [0-9]+ \[[0-9]+\]', + child_sub.before + child_sub.after) + + topics_received = set() + max_samples_received = MAX_SAMPLES_READ + samples_read = 0 + + while sub_string is not None and samples_read < max_samples_received: + topics_received.add(sub_string.group(1)) + if len(topics_received) == num_topics: + break + + index = child_sub.expect( + [ + r'\[[0-9]+\]', # index = 0 + pexpect.TIMEOUT, # index = 1 + pexpect.EOF # index = 2 + ], + timeout + ) + + if index == 1: + break + elif index == 2: + return ReturnCode.DATA_NOT_RECEIVED + + samples_read += 1 + + sub_string = re.search(r'(\w+)\s+\w+\s+[0-9]+ [0-9]+ \[[0-9]+\]', + child_sub.before + child_sub.after) + + print(f'Samples read: {samples_read}, ' + f'topics received: {", ".join(sorted(topics_received))}') + if len(topics_received) != num_topics: + return ReturnCode.DATA_NOT_RECEIVED + return ReturnCode.OK + +def test_receiving_on_3_topics(child_sub, samples_sent, last_sample_saved, timeout): + """_test_receiving_on_all_topics for a subscriber with 3 topics.""" + return _test_receiving_on_all_topics(child_sub, samples_sent, last_sample_saved, + timeout, num_topics=3) + def test_size_less_than_20(child_sub, samples_sent, last_sample_saved, timeout): """ Checks that all received samples have size between 1 and 20 (inclusive).