diff --git a/.github/workflows/Dockerfile b/.github/workflows/Dockerfile new file mode 100644 index 0000000..c969b84 --- /dev/null +++ b/.github/workflows/Dockerfile @@ -0,0 +1,42 @@ +# +# Copyright (c) 2022 ZettaScale Technology +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +# which is available at https://www.apache.org/licenses/LICENSE-2.0. +# +# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +# +# Contributors: +# ZettaScale Zenoh Team, +# + +### +### Dockerfile creating the eclipse/zenoh-bridge-ros1 image from cross-compiled binaries. +### It assumes that zenoh-bridge-ros1 is installed in docker/$TARGETPLATFORM/ +### where $TARGETPLATFORM is set by buildx to a Docker supported platform such as linux/amd64 or linux/arm64 +### (see https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images) +### + + +FROM alpine:latest + +ARG TARGETPLATFORM + +RUN apk add --no-cache libgcc libstdc++ + +COPY docker/$TARGETPLATFORM/zenoh-bridge-ros1 / + +RUN echo '#!/bin/ash' > /entrypoint.sh +RUN echo 'echo " * Starting: /zenoh-bridge-ros1 $*"' >> /entrypoint.sh +RUN echo 'exec /zenoh-bridge-ros1 $*' >> /entrypoint.sh +RUN chmod +x /entrypoint.sh + +EXPOSE 7446/udp +EXPOSE 7447/tcp +EXPOSE 8000/tcp + +ENV RUST_LOG info + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4da4a8..cfafd67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ # name: CI -on: +on: push: branches: ["**"] pull_request: @@ -32,6 +32,8 @@ jobs: steps: - uses: actions/checkout@v2 + with: + submodules: recursive - name: Install Rust toolchain uses: actions-rs/toolchain@v1 @@ -47,6 +49,14 @@ jobs: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - name: Clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + + - name: Clippy All targets uses: actions-rs/cargo@v1 with: command: clippy @@ -54,6 +64,7 @@ jobs: env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + test: name: Run tests on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -64,6 +75,8 @@ jobs: steps: - uses: actions/checkout@v2 + with: + submodules: recursive - name: Install latest Rust toolchain uses: actions-rs/toolchain@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34d2cc7..1707b23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: recursive - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: @@ -137,6 +139,7 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 500 # NOTE: get long history for git-version crate to correctly compute a version + submodules: recursive - name: Fetch Git tags # NOTE: workaround for https://github.com/actions/checkout/issues/290 shell: bash run: git fetch --tags --force @@ -258,50 +261,51 @@ jobs: ${{ steps.package.outputs.BIN_PKG_NAME }} ${{ steps.package.outputs.DEBS_PKG_NAME }} - # docker-build: - # name: Docker build and push - # needs: [checks, builds] - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # with: - # fetch-depth: 500 # NOTE: get long history for git-version crate to correctly compute a version - # - name: Fetch Git tags # NOTE: workaround for https://github.com/actions/checkout/issues/290 - # shell: bash - # run: git fetch --tags --force - # - name: Download packages from previous job - # uses: actions/download-artifact@v2 - # with: - # path: PACKAGES - # - name: Unzip PACKAGES - # run: | - # ls PACKAGES - # mkdir -p docker/linux/amd - # unzip PACKAGES/x86_64-unknown-linux-musl/zenoh-bridge-ros1-${{ needs.checks.outputs.PKG_VERSION }}-x86_64-unknown-linux-musl.zip -d docker/linux/amd64/ - # mkdir -p docker/linux/arm64 - # unzip PACKAGES/aarch64-unknown-linux-musl/zenoh-bridge-ros1-${{ needs.checks.outputs.PKG_VERSION }}-aarch64-unknown-linux-musl.zip -d docker/linux/arm64/ - # tree docker - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v1 - # - name: Docker meta - set tags and labels - # id: meta - # uses: docker/metadata-action@v3 - # with: - # images: eclipse/zenoh-bridge-ros1 - # - name: Login to DockerHub - # uses: docker/login-action@v1 - # with: - # username: ${{ secrets.DOCKER_COM_USERNAME }} - # password: ${{ secrets.DOCKER_COM_PASSWORD }} - # - name: Build and push - # uses: docker/build-push-action@v2 - # with: - # context: . - # platforms: linux/amd64,linux/arm64 - # file: .github/workflows/Dockerfile - # push: true - # tags: ${{ steps.meta.outputs.tags }} - # labels: ${{ steps.meta.outputs.labels }} + docker-build: + name: Docker build and push + needs: [checks, builds] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 500 # NOTE: get long history for git-version crate to correctly compute a version + submodules: recursive + - name: Fetch Git tags # NOTE: workaround for https://github.com/actions/checkout/issues/290 + shell: bash + run: git fetch --tags --force + - name: Download packages from previous job + uses: actions/download-artifact@v2 + with: + path: PACKAGES + - name: Unzip PACKAGES + run: | + ls PACKAGES + mkdir -p docker/linux/amd + unzip PACKAGES/x86_64-unknown-linux-musl/zenoh-bridge-ros1-${{ needs.checks.outputs.PKG_VERSION }}-x86_64-unknown-linux-musl.zip -d docker/linux/amd64/ + mkdir -p docker/linux/arm64 + unzip PACKAGES/aarch64-unknown-linux-musl/zenoh-bridge-ros1-${{ needs.checks.outputs.PKG_VERSION }}-aarch64-unknown-linux-musl.zip -d docker/linux/arm64/ + tree docker + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Docker meta - set tags and labels + id: meta + uses: docker/metadata-action@v3 + with: + images: eclipse/zenoh-bridge-ros1 + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_COM_USERNAME }} + password: ${{ secrets.DOCKER_COM_PASSWORD }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + platforms: linux/amd64,linux/arm64 + file: .github/workflows/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} publication: name: Release publication @@ -341,6 +345,8 @@ jobs: echo "---- cleanup identity" ssh-add -D - uses: actions/checkout@v2 + with: + submodules: recursive - name: Install Rust toolchain uses: actions-rs/toolchain@v1 - name: Publish to crates.io diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d992dce --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "rosrust"] + path = rosrust + url = git@github.com:ZettaScaleLabs/rosrust.git + branch = feature/fix_bugs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..aa6cb2e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +# Contributing to Eclipse zenoh + +Thanks for your interest in this project. + +## Project description + +Eclipse zenoh provides is a stack designed to + 1. minimize network overhead, + 2. support extremely constrained devices, + 3. supports devices with low duty-cycle by allowing the negotiation of data exchange modes and schedules, + 4. provide a rich set of abstraction for distributing, querying and storing data along the entire system, and + 5. provide extremely low latency and high throughput. + +* https://projects.eclipse.org/projects/iot.zenoh + +## Developer resources + +Information regarding source code management, builds, coding standards, and +more. + +* https://projects.eclipse.org/projects/iot.zenoh/developer + +The project maintains the following source code repositories + +* https://github.com/eclipse-zenoh + +## Eclipse Contributor Agreement + +Before your contribution can be accepted by the project team contributors must +electronically sign the Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +Commits that are provided by non-committers must have a Signed-off-by field in +the footer indicating that the author is aware of the terms by which the +contribution has been provided to the project. The non-committer must +additionally have an Eclipse Foundation account and must have a signed Eclipse +Contributor Agreement (ECA) on file. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Contact + +Contact the project developers via the project's "dev" list. + +* https://accounts.eclipse.org/mailing-list/zenoh-dev + +Or via the Discord server. + +* https://discord.gg/vSDSpqnbkm diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..75d9c63 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,7 @@ +# Contributors to Eclipse zenoh-plugin-ros1 + +These are the contributors to Eclipse zenoh (the initial contributors and the contributors listed in the Git log). + + +| GitHub username | Name | +| --------------- | -----------------------------| diff --git a/Cargo.lock b/Cargo.lock index 109a7a2..6c719ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2259,7 +2259,6 @@ dependencies = [ [[package]] name = "ros_message" version = "0.1.1" -source = "git+https://github.com/ZettaScaleLabs/rosrust.git?branch=feature/fix_bugs#ff2ed4fe87ae3579ab64453345e54afae7a594a4" dependencies = [ "array-init", "hex", @@ -2275,7 +2274,6 @@ dependencies = [ [[package]] name = "rosrust" version = "0.9.11" -source = "git+https://github.com/ZettaScaleLabs/rosrust.git?branch=feature/fix_bugs#ff2ed4fe87ae3579ab64453345e54afae7a594a4" dependencies = [ "byteorder", "colored", @@ -2298,7 +2296,6 @@ dependencies = [ [[package]] name = "rosrust_codegen" version = "0.9.6" -source = "git+https://github.com/ZettaScaleLabs/rosrust.git?branch=feature/fix_bugs#ff2ed4fe87ae3579ab64453345e54afae7a594a4" dependencies = [ "error-chain 0.12.4", "hex", @@ -2329,7 +2326,7 @@ dependencies = [ "serde_json", "sha1_smol", "threadpool", - "time 0.3.28", + "time 0.3.29", "tiny_http", "url 2.4.1", ] @@ -2545,9 +2542,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" [[package]] name = "serde" @@ -2935,9 +2932,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", "libc", @@ -2948,9 +2945,9 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tiny_http" @@ -3018,9 +3015,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log 0.4.20", @@ -3069,9 +3066,9 @@ checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" [[package]] name = "tungstenite" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", @@ -3557,8 +3554,8 @@ dependencies = [ [[package]] name = "zenoh" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-global-executor", "async-std", @@ -3620,16 +3617,16 @@ dependencies = [ [[package]] name = "zenoh-buffers" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "zenoh-collections", ] [[package]] name = "zenoh-codec" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "log 0.4.20", "serde", @@ -3640,13 +3637,13 @@ dependencies = [ [[package]] name = "zenoh-collections" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" [[package]] name = "zenoh-config" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "flume", "json5", @@ -3663,8 +3660,8 @@ dependencies = [ [[package]] name = "zenoh-core" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "lazy_static", @@ -3673,8 +3670,8 @@ dependencies = [ [[package]] name = "zenoh-crypto" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "aes", "hmac", @@ -3686,8 +3683,8 @@ dependencies = [ [[package]] name = "zenoh-ext" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "bincode", @@ -3706,8 +3703,8 @@ dependencies = [ [[package]] name = "zenoh-keyexpr" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "hashbrown 0.14.0", "keyed-set", @@ -3720,8 +3717,8 @@ dependencies = [ [[package]] name = "zenoh-link" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "async-trait", @@ -3739,8 +3736,8 @@ dependencies = [ [[package]] name = "zenoh-link-commons" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "async-trait", @@ -3755,8 +3752,8 @@ dependencies = [ [[package]] name = "zenoh-link-quic" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-rustls", "async-std", @@ -3779,8 +3776,8 @@ dependencies = [ [[package]] name = "zenoh-link-tcp" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "async-trait", @@ -3795,8 +3792,8 @@ dependencies = [ [[package]] name = "zenoh-link-tls" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-rustls", "async-std", @@ -3818,8 +3815,8 @@ dependencies = [ [[package]] name = "zenoh-link-udp" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "async-trait", @@ -3837,8 +3834,8 @@ dependencies = [ [[package]] name = "zenoh-link-unixsock_stream" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "async-trait", @@ -3855,8 +3852,8 @@ dependencies = [ [[package]] name = "zenoh-link-ws" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "async-trait", @@ -3875,8 +3872,8 @@ dependencies = [ [[package]] name = "zenoh-macros" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "proc-macro2", "quote", @@ -3892,6 +3889,7 @@ version = "0.10.0-dev" dependencies = [ "async-global-executor", "async-std", + "async-trait", "atoi", "ctrlc", "duration-string", @@ -3915,14 +3913,13 @@ dependencies = [ "zenoh", "zenoh-core", "zenoh-ext", - "zenoh-plugin-ros1", "zenoh-plugin-trait", ] [[package]] name = "zenoh-plugin-trait" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "libloading", "log 0.4.20", @@ -3934,8 +3931,8 @@ dependencies = [ [[package]] name = "zenoh-protocol" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "const_format", "hex", @@ -3950,16 +3947,16 @@ dependencies = [ [[package]] name = "zenoh-result" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "anyhow", ] [[package]] name = "zenoh-sync" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "event-listener", @@ -3973,8 +3970,8 @@ dependencies = [ [[package]] name = "zenoh-transport" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-executor", "async-global-executor", @@ -4004,8 +4001,8 @@ dependencies = [ [[package]] name = "zenoh-util" -version = "0.10.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git#46c8fa1f13ac8bab76366332bf30585b53be9d11" +version = "0.10.0-rc" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=release-0.10.0-rc#37f80d9befc757ca091a748e41217b8cd1bdf16c" dependencies = [ "async-std", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 198ab0c..3c42522 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "zenoh-bridge-ros1", "zenoh-plugin-ros1", ] +exclude = [ "rosrust" ] [workspace.package] version = "0.10.0-dev" @@ -33,6 +34,7 @@ categories = ["network-programming"] [workspace.dependencies] atoi = "2.0.0" async-std = "=1.12.0" +async-trait = "0.1" clap = "3.2.23" ctrlc = "3.2.5" env_logger = "0.9.1" @@ -47,11 +49,11 @@ rand = "0.8.5" strum = "0.24" strum_macros = "0.24" duration-string = "0.3.0" -zenoh = { git = "https://github.com/eclipse-zenoh/zenoh.git", features = ["unstable"] } -zenoh-ext = { git = "https://github.com/eclipse-zenoh/zenoh.git", features = ["unstable"] } -zenoh-core = { git = "https://github.com/eclipse-zenoh/zenoh.git" } -zenoh-plugin-trait = { git = "https://github.com/eclipse-zenoh/zenoh.git", default-features = false } -rosrust = { git = "https://github.com/ZettaScaleLabs/rosrust.git", branch = "feature/fix_bugs" } +zenoh = { version = "0.10.0-rc", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "release-0.10.0-rc" } +zenoh-ext = { version = "0.10.0-rc", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "release-0.10.0-rc" } +zenoh-core = { version = "0.10.0-rc", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "release-0.10.0-rc" } +zenoh-plugin-trait = { version = "0.10.0-rc", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "release-0.10.0-rc", default-features = false } +rosrust = { version = "0.9.11", path = "rosrust/rosrust" } flume = "0.11" hex = "0.4.3" xml-rpc = "0.0.12" diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..127ef5e --- /dev/null +++ b/Cross.toml @@ -0,0 +1,18 @@ +[target.x86_64-unknown-linux-musl] +image = "jenoch/rust-cross:x86_64-unknown-linux-musl" + +[target.arm-unknown-linux-gnueabi] +image = "jenoch/rust-cross:arm-unknown-linux-gnueabi" + +[target.arm-unknown-linux-gnueabihf] +image = "jenoch/rust-cross:arm-unknown-linux-gnueabihf" + +[target.armv7-unknown-linux-gnueabihf] +image = "jenoch/rust-cross:armv7-unknown-linux-gnueabihf" + +[target.aarch64-unknown-linux-gnu] +image = "jenoch/rust-cross:aarch64-unknown-linux-gnu" + +[target.aarch64-unknown-linux-musl] +image = "jenoch/rust-cross:aarch64-unknown-linux-musl" + diff --git a/DEFAULT_CONFIG.json5 b/DEFAULT_CONFIG.json5 index e1b6579..0329cf6 100644 --- a/DEFAULT_CONFIG.json5 +++ b/DEFAULT_CONFIG.json5 @@ -25,12 +25,89 @@ // ros_name: "ros1_to_zenoh_bridge", //// - //// ros_bridging_mode: When to bridge topics: - //// "auto"(default) - bridge topics once they are declared locally - //// "lazy" - bridge topics once they are declared both locally and required remotely through discovery - //// Warn: this setting is ignored for local ROS1 clients, as they require a tricky discovery mechanism + //// with_rosmaster: An option wether the bridge should run it's own rosmaster process, the default is "false" + //// + // with_rosmaster: "false", + + //// + //// subscriber_bridging_mode: Global subscriber's topic bridging mode. Accepted values: + //// - "auto"(default) - bridge topics once they are declared locally or discovered remotely + //// - "lazy_auto" - bridge topics once they are both declared locally and discovered remotely + //// - "disabled" - never bridge topics. This setting will also suppress the topic discovery."# + // subscriber_bridging_mode: "auto", + + //// + //// publisher_bridging_mode: Global publisher's topic bridging mode. Accepted values: + //// - "auto"(default) - bridge topics once they are declared locally or discovered remotely + //// - "lazy_auto" - bridge topics once they are both declared locally and discovered remotely + //// - "disabled" - never bridge topics. This setting will also suppress the topic discovery."# + // publisher_bridging_mode: "auto", + //// - // ros_bridging_mode: "auto", + //// service_bridging_mode: Global service's topic bridging mode. Accepted values: + //// - "auto"(default) - bridge topics once they are declared locally or discovered remotely + //// - "lazy_auto" - bridge topics once they are both declared locally and discovered remotely + //// - "disabled" - never bridge topics. This setting will also suppress the topic discovery."# + // service_bridging_mode: "auto", + + //// + //// client_bridging_mode: Mode of client's topic bridging. Accepted values: + //// - "auto" - bridge topics once they are discovered remotely + //// - "disabled"(default) - never bridge topics. This setting will also suppress the topic discovery. + //// NOTE: there are some pecularities on how ROS1 handles clients: + //// - ROS1 doesn't provide any client discovery mechanism + //// - ROS1 doesn't allow multiple services on the same topic + //// Due to this, client's bridging works differently compared to pub\sub bridging: + //// - lazy bridging mode is not available as there is no way to discover local ROS1 clients + //// - client bridging is disabled by default, as it may brake the local ROS1 system if it intends to have client and service interacting on the same topic + //// In order to use client bridging, you have two options: + //// - globally select auto bridging mode (with caution!) with this option + //// - bridge specific topics using 'client_topic_custom_bridging_mode' option (with a little bit less caution!)"# + // client_bridging_mode: "disabled", + + //// + //// subscriber_topic_custom_bridging_mode: A JSON Map describing custom bridging modes for particular topics. + //// Custom bridging mode overrides the global one. + //// Format: {"topic1":"mode", "topic2":"mode"} + //// Example: {"/my/topic1":"lazy_auto","/my/topic2":"auto"} + //// where + //// - topic: ROS1 topic name + //// - mode (auto/lazy_auto/disabled) as described above + //// The default is empty + // subscriber_topic_custom_bridging_mode: "" + + //// + //// publisher_topic_custom_bridging_mode: A JSON Map describing custom bridging modes for particular topics. + //// Custom bridging mode overrides the global one. + //// Format: {"topic1":"mode", "topic2":"mode"} + //// Example: {"/my/topic1":"lazy_auto","/my/topic2":"auto"} + //// where + //// - topic: ROS1 topic name + //// - mode (auto/lazy_auto/disabled) as described above + //// The default is empty + // publisher_topic_custom_bridging_mode: "" + + //// + //// service_topic_custom_bridging_mode: A JSON Map describing custom bridging modes for particular topics. + //// Custom bridging mode overrides the global one. + //// Format: {"topic1":"mode", "topic2":"mode"} + //// Example: {"/my/topic1":"lazy_auto","/my/topic2":"auto"} + //// where + //// - topic: ROS1 topic name + //// - mode (auto/lazy_auto/disabled) as described above + //// The default is empty + // service_topic_custom_bridging_mode: "" + + //// + //// client_topic_custom_bridging_mode: A JSON Map describing custom bridging modes for particular topics. + //// Custom bridging mode overrides the global one. + //// Format: {"topic1":"mode", "topic2":"mode"} + //// Example: {"/my/topic1":"auto","/my/topic2":"auto"} + //// where + //// - topic: ROS1 topic name + //// - mode (auto/disabled) as described above + //// The default is empty + // client_topic_custom_bridging_mode: "" //// //// ros_master_polling_interval: An interval how to poll the ROS1 master for status @@ -41,11 +118,6 @@ //// The string format is [0-9]+(ns|us|ms|[smhdwy]) //// // ros_master_polling_interval: "100ms", - - //// - //// with_rosmaster: An option wether the bridge should run it's own rosmaster process, the default is "false" - //// - // with_rosmaster: "false", }, //// diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f3d2286 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# +# Copyright (c) 2022 ZettaScale Technology +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +# which is available at https://www.apache.org/licenses/LICENSE-2.0. +# +# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +# +# Contributors: +# ZettaScale Zenoh Team, +# + + +### +### Build part +### +FROM rust:slim-buster as builder + +WORKDIR /usr/src/zenoh-plugin-ros1 + +# List of installed tools: +# * for zenoh-plugin-ros1 +# - git +# - clang +RUN apt-get update && apt-get -y install git clang build-essential + +COPY . . +# if exists, copy .git directory to be used by git-version crate to determine the version +COPY .gi? .git/ + +RUN cargo install --locked --path zenoh-bridge-ros1 + + +### +### Run part +### +FROM debian:buster-slim + +COPY --from=builder /usr/local/cargo/bin/zenoh-bridge-ros1 /usr/local/bin/zenoh-bridge-ros1 +RUN ldconfig -v + +RUN echo '#!/bin/bash' > /entrypoint.sh +RUN echo 'echo " * Starting: zenoh-bridge-ros1 $*"' >> /entrypoint.sh +RUN echo 'exec zenoh-bridge-ros1 $*' >> /entrypoint.sh +RUN chmod +x /entrypoint.sh + +EXPOSE 7446/udp +EXPOSE 7447/tcp +EXPOSE 8000/tcp + +ENV RUST_LOG info + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..5b6751c --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,41 @@ +# Notices for Eclipse zenoh-plugin-ros1 + +This content is produced and maintained by the Eclipse zenoh project. + + * Project home: https://projects.eclipse.org/projects/iot.zenoh + +## Trademarks + +Eclipse zenoh is trademark of the Eclipse Foundation. +Eclipse, and the Eclipse Logo are registered trademarks of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. +For more information regarding authorship of content, please consult the +listed source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the +terms of the Eclipse Public License 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +which is available at https://www.apache.org/licenses/LICENSE-2.0. + +SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + +## Source Code + +The project maintains the following source code repositories: + + * https://github.com/eclipse-zenoh/zenoh.git + * https://github.com/eclipse-zenoh/zenoh-c.git + * https://github.com/eclipse-zenoh/zenoh-java.git + * https://github.com/eclipse-zenoh/zenoh-go.git + * https://github.com/eclipse-zenoh/zenoh-python.git + * https://github.com/eclipse-zenoh/zenoh-plugin-dds.git + +## Third-party Content + + *To be completed...* + diff --git a/README.md b/README.md index f4d6eb1..795880b 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,43 @@ Check the website [zenoh.io](http://zenoh.io) and the [roadmap](https://github.c ------------------------------- # ROS1 to Zenoh Bridge plugin +:point_right: **Install latest release:** see [below](#How-to-install-it) + +:point_right: **Docker image:** see [below](#Docker-image) + +:point_right: **Build "master" branch:** see [below](#How-to-build-it) + ## Background ROS1 is a well-known mature platform for building robotic systems. Despite the fact that next generation of ROS - ROS2 is released long time ago, many developers still prefer using ROS1. In order to integrate ROS1 systems to Zenoh infrastructure, [as it was done for DDS/ROS2](https://github.com/eclipse-zenoh/zenoh-plugin-dds), ROS1 to Zenoh Bridge was designed. ## How to install it -Currently, only out-of-source build is supported. + +To install the latest release of either the ROS1 plugin for the Zenoh router, either the `zenoh-bridge-ros1` standalone executable, you can do as follows: + +### Manual installation (all platforms) + +All release packages can be downloaded from: + - https://download.eclipse.org/zenoh/zenoh-plugin-ros1/latest/ + +Each subdirectory has the name of the Rust target. See the platforms each target corresponds to on https://doc.rust-lang.org/stable/rustc/platform-support.html + +Choose your platform and download: + - the `zenoh-plugin-ros1--.zip` file for the plugin. + Then unzip it in the same directory than `zenohd` or to any directory where it can find the plugin library (e.g. /usr/lib) + - the `zenoh-bridge-ros1--.zip` file for the standalone executable. + Then unzip it where you want, and run the extracted `zenoh-bridge-ros1` binary. + +### Linux Debian + +Add Eclipse Zenoh private repository to the sources list: + +```bash +echo "deb [trusted=yes] https://download.eclipse.org/zenoh/debian-repo/ /" | sudo tee -a /etc/apt/sources.list > /dev/null +sudo apt update +``` +Then either: + - install the plugin with: `sudo apt install zenoh-plugin-ros1`. + - install the standalone executable with: `sudo apt install zenoh-bridge-ros1`. ## How to build it @@ -69,6 +101,13 @@ $ cargo build --release -p zenoh-bridge-ros1 ``` The **`zenoh-bridge-ros1`** binary will be generated in the `target/release` sub-directory. +## Docker image +The **`zenoh-bridge-ros1`** standalone executable is also available as a [Docker images](https://hub.docker.com/r/eclipse/zenoh-bridge-ros1/tags?page=1&ordering=last_updated) for both amd64 and arm64. To get it, do: + - `docker pull eclipse/zenoh-bridge-ros1:latest` for the latest release + - `docker pull eclipse/zenoh-bridge-ros1:master` for the master branch version (nightly build) + +Usage: **`docker run --init --net host eclipse/zenoh-bridge-ros1`** +It supports the same command line arguments than the `zenoh-bridge-ros1` (see below or check with `-h` argument). ## A quick test with built-in examples @@ -80,60 +119,35 @@ $ sudo apt install -y ros-base There is a set of example utilities illustarating bridge in operation. Here is a description on how to configure the following schema: ``` -_____________________________ ________________________________ -| | | | -| rosmaster_1 | | rosmaster_2 | -| | | | -| ros1_publisher -> ros_to_zenoh_bridge -> zenoh -> ros_to_zenoh_bridge -> ros1_subscriber | -|___________________________| |______________________________| +_____________________________ ________________________________ +| | | | +| rosmaster_1 | | rosmaster_2 | +| | | | +| ros1_publisher -> zenoh-bridge-ros1 -> zenoh -> zenoh-bridge-ros1 -> ros1_subscriber | +|___________________________| |______________________________| ``` -1. Build everything: ```bash -$ cargo build --release -p zenoh-bridge-ros1 -$ cargo test --release --no-run -``` -There are three executables we'll need: +# build the bridge from source +cargo build -p zenoh-bridge-ros1 +cd target/debug/ +# terminal 1: +./zenoh-bridge-ros1 --with_rosmaster true --ros_master_uri http://localhost:10000 +# terminal 2: +./zenoh-bridge-ros1 --with_rosmaster true --ros_master_uri http://localhost:10001 +# terminal 3: +ROS_MASTER_URI=http://localhost:10000 rostopic pub /topic std_msgs/String -r 1 test_message +# terminal 4: +ROS_MASTER_URI=http://localhost:10001 rostopic echo /topic ``` -target/release/zenoh-bridge-ros1 // bridge executable -target/release/examples/ros1_standalone_sub // ros1 test subscriber -target/release/examples/ros1_standalone_pub // ros1 test publisher -``` - -2. Start first bridge together with rosmaster_1: -```bash -$ ./target/release/zenoh-bridge-ros1 --with_rosmaster true --ros_master_uri http://localhost:10000 -``` -At this step we start ROS1 master together with bridge isolated on port 10000 - -3. Start second bridge together with rosmaster_2: -```bash -$ ./target/release/zenoh-bridge-ros1 --with_rosmaster true --ros_master_uri http://localhost:10001 -``` -At this step we start ROS1 master together with bridge isolated on port 10001 - -4. Start ros1_subscriber: -```bash -$ ROS_MASTER_URI=http://localhost:10000 ./target/release/examples/ros1_standalone_sub -``` -The subscriber will work with ROS1 isolated on port 10000 - -5. Start ros1_publisher: -```bash -$ ROS_MASTER_URI=http://localhost:10001 ./target/release/examples/ros1_standalone_pub -``` -The publisher will work with ROS1 isolated on port 10001 Once completed, you will see the following exchange between ROS1 publisher and subscriber: - - - + ## Implementation -Currently, ROS1 to Zenoh Bridge is based on [rosrust library fork](https://github.com/ZettaScaleLabs/rosrust). Some limitations are applied due to rosrust's implementation details, and we are targeting to re-engineer rosrust to overcome this +Currently, ROS1 to Zenoh Bridge is based on [rosrust library fork](https://github.com/ZettaScaleLabs/rosrust). Some limitations are applied due to rosrust's implementation details, and we are re-engineering rosrust to overcome this ## Limitations - all topic names are bridged as-is -- all topic datatypes and md5 sums are bridged as "*" wildcard and may not work with some ROS1 client implementations - there is a performance impact coming from rosrust diff --git a/pubsub.png b/pubsub.png deleted file mode 100644 index b346760..0000000 Binary files a/pubsub.png and /dev/null differ diff --git a/ros_pubsub.png b/ros_pubsub.png new file mode 100644 index 0000000..0df7006 Binary files /dev/null and b/ros_pubsub.png differ diff --git a/rosrust b/rosrust new file mode 160000 index 0000000..ff2ed4f --- /dev/null +++ b/rosrust @@ -0,0 +1 @@ +Subproject commit ff2ed4fe87ae3579ab64453345e54afae7a594a4 diff --git a/zenoh-bridge-ros1/src/main.rs b/zenoh-bridge-ros1/src/main.rs index 1ec50ee..ee0bddb 100644 --- a/zenoh-bridge-ros1/src/main.rs +++ b/zenoh-bridge-ros1/src/main.rs @@ -138,7 +138,7 @@ r#"--client_bridging_mode=[String] \ .arg(Arg::from_usage( r#"--subscriber_topic_custom_bridging_mode=[JSON] 'A JSON Map describing custom bridging modes for particular topics. Custom bridging mode overrides the global one. -Format: {"topic", "mode"} +Format: {"topic1":"mode", "topic2":"mode"} Example: {\"/my/topic1\":\"lazy_auto\",\"/my/topic2\":\"auto\"} where - topic: ROS1 topic name @@ -148,7 +148,7 @@ The default is empty'"# .arg(Arg::from_usage( r#"--publisher_topic_custom_bridging_mode=[JSON] 'A JSON Map describing custom bridging modes for particular topics. Custom bridging mode overrides the global one. -Format: {"topic", "mode"} +Format: {"topic1":"mode", "topic2":"mode"} Example: {\"/my/topic1\":\"lazy_auto\",\"/my/topic2\":\"auto\"} where - topic: ROS1 topic name @@ -158,7 +158,7 @@ The default is empty'"# .arg(Arg::from_usage( r#"--service_topic_custom_bridging_mode=[JSON] 'A JSON Map describing custom bridging modes for particular topics. Custom bridging mode overrides the global one. -Format: {"topic", "mode"} +Format: {"topic1":"mode", "topic2":"mode"} Example: {\"/my/topic1\":\"lazy_auto\",\"/my/topic2\":\"auto\"} where - topic: ROS1 topic name @@ -168,7 +168,7 @@ The default is empty'"# .arg(Arg::from_usage( r#"--client_topic_custom_bridging_mode=[JSON] 'A JSON Map describing custom bridging modes for particular topics. Custom bridging mode overrides the global one. -Format: {"topic", "mode"} +Format: {"topic1":"mode", "topic2":"mode"} Example: {\"/my/topic1\":\"auto\",\"/my/topic2\":\"auto\"} where - topic: ROS1 topic name diff --git a/zenoh-plugin-ros1/Cargo.toml b/zenoh-plugin-ros1/Cargo.toml index a792380..95fe9d3 100644 --- a/zenoh-plugin-ros1/Cargo.toml +++ b/zenoh-plugin-ros1/Cargo.toml @@ -28,8 +28,8 @@ crate-type = ["cdylib", "rlib"] [features] no_mangle = ["zenoh-plugin-trait/no_mangle"] -default = ["no_mangle"] test = [] +default = ["no_mangle", "test"] # TODO: https://zettascale.atlassian.net/browse/ZEN-291 [dependencies] atoi = { workspace = true } @@ -42,6 +42,7 @@ log = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } async-global-executor = { workspace = true } +async-trait = { workspace = true } rand = { workspace = true } strum = { workspace = true } strum_macros = { workspace = true } @@ -58,7 +59,8 @@ xml-rpc = { workspace = true } serial_test = "0.10.0" multiset = "0.0.5" ctrlc = { workspace = true } -zenoh-plugin-ros1 = { path = ".", features = ["test"]} +# TODO: https://zettascale.atlassian.net/browse/ZEN-291 +# zenoh-plugin-ros1 = { path = ".", features = ["test"]} [dependencies.async-std] version = "=1.12.0" @@ -73,4 +75,4 @@ maintainer = "zenoh-dev@eclipse.org" copyright = "2017, 2022 ZettaScale Technology Inc." section = "net" license-file = ["../LICENSE", "0"] -depends = "zenohd (=0.10.0-dev)" \ No newline at end of file +depends = "zenohd (=0.10.0-dev)" diff --git a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/bridges_storage.rs b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/bridges_storage.rs index 15e2f10..8a2392a 100644 --- a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/bridges_storage.rs +++ b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/bridges_storage.rs @@ -238,42 +238,6 @@ impl<'a> ElementAccessor<'a> { } } -pub struct TypeAccessor<'a> { - storage: &'a mut BridgesStorage, - _v: bool, -} -impl<'a> TypeAccessor<'a> { - fn new(storage: &'a mut BridgesStorage) -> Self { - Self { storage, _v: false } - } - - pub fn complementary_for(&'a mut self, b_type: BridgeType) -> ComplementaryElementAccessor<'a> { - let b_type = match b_type { - BridgeType::Publisher => BridgeType::Subscriber, - BridgeType::Subscriber => BridgeType::Publisher, - BridgeType::Service => BridgeType::Client, - BridgeType::Client => BridgeType::Service, - }; - ComplementaryElementAccessor::new( - b_type, - self.storage.bridges.container_mut(b_type), - self.storage.ros1_client.clone(), - self.storage.zenoh_client.clone(), - self.storage.declaration_interface.clone(), - ) - } - - pub fn for_type(&'a mut self, b_type: BridgeType) -> ElementAccessor<'a> { - ElementAccessor::new( - b_type, - self.storage.bridges.container_mut(b_type), - self.storage.ros1_client.clone(), - self.storage.zenoh_client.clone(), - self.storage.declaration_interface.clone(), - ) - } -} - pub struct BridgesStorage { bridges: Bridges, @@ -296,23 +260,42 @@ impl BridgesStorage { } } - pub fn bridges(&mut self) -> TypeAccessor { - TypeAccessor::new(self) + pub fn complementary_for(&mut self, b_type: BridgeType) -> ComplementaryElementAccessor<'_> { + let b_type = match b_type { + BridgeType::Publisher => BridgeType::Subscriber, + BridgeType::Subscriber => BridgeType::Publisher, + BridgeType::Service => BridgeType::Client, + BridgeType::Client => BridgeType::Service, + }; + ComplementaryElementAccessor::new( + b_type, + self.bridges.container_mut(b_type), + self.ros1_client.clone(), + self.zenoh_client.clone(), + self.declaration_interface.clone(), + ) + } + + pub fn for_type(&mut self, b_type: BridgeType) -> ElementAccessor<'_> { + ElementAccessor::new( + b_type, + self.bridges.container_mut(b_type), + self.ros1_client.clone(), + self.zenoh_client.clone(), + self.declaration_interface.clone(), + ) } pub async fn receive_ros1_state(&mut self, ros1_state: &mut Ros1TopicMapping) -> bool { let mut smth_changed = self - .bridges() .for_type(BridgeType::Publisher) .receive_ros1_state(&mut ros1_state.published) .await; smth_changed |= self - .bridges() .for_type(BridgeType::Service) .receive_ros1_state(&mut ros1_state.serviced) .await; smth_changed |= self - .bridges() .for_type(BridgeType::Subscriber) .receive_ros1_state(&mut ros1_state.subscribed) .await; diff --git a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/discovery.rs b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/discovery.rs index 4062bc9..9919183 100644 --- a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/discovery.rs +++ b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/discovery.rs @@ -46,13 +46,14 @@ const ROS1_DISCOVERY_INFO_SUBSCRIBERS_CLASS: &str = "sub"; const ROS1_DISCOVERY_INFO_SERVICES_CLASS: &str = "srv"; const ROS1_DISCOVERY_INFO_CLIENTS_CLASS: &str = "cl"; -struct RemoteResources { +pub struct RemoteResources { _subscriber: Option, } impl RemoteResources { async fn new( session: Arc, discovery_namespace: String, + bridge_namespace: String, on_discovered: F, on_lost: F, ) -> Self @@ -70,7 +71,7 @@ impl RemoteResources { resource_class = "*", data_type = "*", md5 = "*", - bridge_namespace = "*", + bridge_namespace = bridge_namespace, topic = "*/**" ) .unwrap(); @@ -276,47 +277,12 @@ impl LocalResources { } } -pub struct Discovery { - _remote_resources: RemoteResources, - local_resources: LocalResources, -} -impl Discovery { - pub async fn new( - discovery_namespace: String, - bridge_namespace: String, - session: Arc, - on_discovered: F, - on_lost: F, - ) -> Self - where - F: Fn(BridgeType, TopicDescriptor) -> Box + Unpin + Send> - + Send - + Sync - + 'static, - { - Self { - _remote_resources: RemoteResources::new( - session.clone(), - discovery_namespace.clone(), - on_discovered, - on_lost, - ) - .await, - local_resources: LocalResources::new(discovery_namespace, bridge_namespace, session), - } - } - - pub fn local_resources(&self) -> &LocalResources { - &self.local_resources - } -} - pub type TCallback = dyn Fn(BridgeType, TopicDescriptor) -> Box + Unpin + Send> + Send + Sync + 'static; -pub struct DiscoveryBuilder { +pub struct RemoteResourcesBuilder { discovery_namespace: String, bridge_namespace: String, session: Arc, @@ -325,7 +291,7 @@ pub struct DiscoveryBuilder { on_lost: Option>, } -impl DiscoveryBuilder { +impl RemoteResourcesBuilder { pub fn new( discovery_namespace: String, bridge_namespace: String, @@ -361,11 +327,11 @@ impl DiscoveryBuilder { self } - pub async fn build(self) -> Discovery { - Discovery::new( + pub async fn build(self) -> RemoteResources { + RemoteResources::new( + self.session, self.discovery_namespace, self.bridge_namespace, - self.session, self.on_discovered .unwrap_or(Box::new(|_, _| Box::new(Box::pin(async {})))), self.on_lost diff --git a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_client.rs b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_client.rs index 1b52207..eca538a 100644 --- a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_client.rs +++ b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_client.rs @@ -101,10 +101,6 @@ impl Ros1Client { self.filter(self.ros.state()) } - pub fn topic_types(&self) -> rosrust::api::error::Response> { - self.ros.topics() - } - // PRIVATE /** * Filter out topics, which are published\subscribed\serviced only by the bridge itself diff --git a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_to_zenoh_bridge_impl.rs b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_to_zenoh_bridge_impl.rs index 421e18e..8b10911 100644 --- a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_to_zenoh_bridge_impl.rs +++ b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/ros1_to_zenoh_bridge_impl.rs @@ -33,7 +33,7 @@ use crate::ros_to_zenoh_bridge::{ }; use super::{ - discovery::{Discovery, DiscoveryBuilder}, + discovery::{RemoteResources, RemoteResourcesBuilder}, resource_cache::Ros1ResourceCache, ros1_client::Ros1Client, }; @@ -93,7 +93,8 @@ where local_resources, ))); - let _discovery = make_discovery(session.clone(), bridges.clone()).await; + let _remote_resources_discovery = + make_remote_resources_discovery(session.clone(), bridges.clone()).await; let mut bridge = RosToZenohBridge::new(ros_status_callback, statistics_callback); bridge @@ -102,19 +103,18 @@ where Ok(()) } -async fn make_discovery<'a>( +async fn make_remote_resources_discovery<'a>( session: Arc, bridges: Arc>, -) -> Discovery { +) -> RemoteResources { let bridges2 = bridges.clone(); - let builder = DiscoveryBuilder::new("*".to_string(), "*".to_string(), session); + let builder = RemoteResourcesBuilder::new("*".to_string(), "*".to_string(), session); builder .on_discovered(move |b_type, topic| { let bridges = bridges.clone(); Box::new(Box::pin(async move { zasynclock!(bridges) - .bridges() .complementary_for(b_type) .complementary_entity_discovered(topic) .await; @@ -124,7 +124,6 @@ async fn make_discovery<'a>( let bridges = bridges2.clone(); Box::new(Box::pin(async move { zasynclock!(bridges) - .bridges() .complementary_for(b_type) .complementary_entity_lost(topic) .await; diff --git a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/rosclient_test_helpers.rs b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/rosclient_test_helpers.rs index 7c2bcc3..d750cda 100644 --- a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/rosclient_test_helpers.rs +++ b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/rosclient_test_helpers.rs @@ -16,25 +16,22 @@ use std::time::Duration; use rosrust::{Publisher, RawMessage, Subscriber}; -use super::{ros1_client::Ros1Client, test_helpers::wait}; +use super::{ros1_client::Ros1Client, test_helpers::wait_sync}; pub fn wait_for_rosclient_to_connect(rosclient: &Ros1Client) -> bool { - async_std::task::block_on(wait( - || rosclient.topic_types().is_ok(), - Duration::from_secs(10), - )) + wait_sync(|| rosclient.state().is_ok(), Duration::from_secs(10)) } pub fn wait_for_publishers(subscriber: &Subscriber, count: usize) -> bool { - async_std::task::block_on(wait( + wait_sync( || subscriber.publisher_count() == count, Duration::from_secs(10), - )) + ) } pub fn wait_for_subscribers(publisher: &Publisher, count: usize) -> bool { - async_std::task::block_on(wait( + wait_sync( || publisher.subscriber_count() == count, Duration::from_secs(10), - )) + ) } diff --git a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/test_helpers.rs b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/test_helpers.rs index d1eae1a..e53b7c8 100644 --- a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/test_helpers.rs +++ b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/test_helpers.rs @@ -12,6 +12,9 @@ // ZettaScale Zenoh Team, // +use async_std::prelude::FutureExt; +use async_trait::async_trait; +use futures::Future; use log::error; use rosrust::{Client, RawMessage, RawMessageDescription}; use std::process::Command; @@ -19,11 +22,13 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::*; use std::sync::{Arc, Mutex, RwLock}; +use std::time::Duration; use std::{net::SocketAddr, str::FromStr, sync::atomic::AtomicU16}; use zenoh::config::ModeDependentValue; use zenoh::prelude::OwnedKeyExpr; use zenoh::prelude::SplitBuffer; use zenoh::sample::Sample; +use zenoh::Session; use zenoh_core::{bail, zlock, zresult::ZResult, AsyncResolve, SyncResolve}; use super::discovery::LocalResources; @@ -77,7 +82,7 @@ impl IsolatedROSMaster { } } -pub async fn wait(waiter: Waiter, timeout: core::time::Duration) -> bool +pub fn wait_sync(waiter: Waiter, timeout: Duration) -> bool where Waiter: Fn() -> bool, { @@ -85,17 +90,43 @@ where let millis = timeout.as_millis() / cycles + 1; for _i in 0..cycles { - async_std::task::sleep(core::time::Duration::from_millis( - millis.try_into().unwrap(), - )) - .await; if waiter() { return true; } + std::thread::sleep(Duration::from_millis(millis.try_into().unwrap())); } false } +pub async fn wait_async_fn(waiter: Waiter, timeout: Duration) -> bool +where + Waiter: Fn() -> bool, +{ + let sleep_millis = 10u64; + let cycles = (timeout.as_millis() as u64) / sleep_millis + 1; + + for _i in 0..cycles { + if waiter() { + return true; + } + async_std::task::sleep(Duration::from_millis(sleep_millis)).await; + } + false +} + +pub async fn wait_async(waiter: Waiter, timeout: Duration) -> bool +where + Waiter: Fn() -> Fut + Send + Sync, + Fut: Future, +{ + let w = async { + while !waiter().await { + async_std::task::sleep(Duration::from_millis(10)).await + } + }; + w.timeout(timeout).await.is_ok() +} + pub struct RunningBridge { flag: Arc, @@ -152,14 +183,11 @@ impl RunningBridge { self.assert_status(RosStatus::Ok).await; } pub async fn assert_status(&self, status: RosStatus) { - assert!( - self.wait_ros_status(status, core::time::Duration::from_secs(10)) - .await - ); + assert!(self.wait_ros_status(status, Duration::from_secs(10)).await); } - pub async fn wait_ros_status(&self, status: RosStatus, timeout: core::time::Duration) -> bool { - wait( - move || { + pub async fn wait_ros_status(&self, status: RosStatus, timeout: Duration) -> bool { + wait_async_fn( + || { let val = self.ros_status.lock().unwrap(); *val == status }, @@ -173,16 +201,16 @@ impl RunningBridge { } pub async fn assert_bridge_status BridgeStatus>(&self, status: F) { assert!( - self.wait_bridge_status(status, core::time::Duration::from_secs(120)) + self.wait_bridge_status(status, Duration::from_secs(120)) .await ); } pub async fn wait_bridge_status BridgeStatus>( &self, status: F, - timeout: core::time::Duration, + timeout: Duration, ) -> bool { - wait( + wait_async_fn( move || { let expected = (status)(); let real = self.bridge_status.lock().unwrap(); @@ -252,6 +280,7 @@ impl ROSEnvironment { } pub struct BridgeChecker { + session: Arc, ros_client: ros1_client::Ros1Client, zenoh_client: zenoh_client::ZenohClient, pub local_resources: LocalResources, @@ -263,6 +292,7 @@ impl BridgeChecker { pub fn new(config: zenoh::config::Config, ros_master_uri: &str) -> BridgeChecker { let session = zenoh::open(config).res_sync().unwrap().into_arc(); BridgeChecker { + session: session.clone(), ros_client: ros1_client::Ros1Client::new("test_ros_node", ros_master_uri).unwrap(), zenoh_client: zenoh_client::ZenohClient::new(session.clone()), local_resources: LocalResources::new("*".to_string(), "*".to_string(), session), @@ -270,6 +300,19 @@ impl BridgeChecker { } } + pub async fn assert_zenoh_peers(&self, peer_count: usize) { + assert!( + self.wait_for_zenoh_peers(peer_count, Duration::from_secs(30)) + .await + ); + } + + async fn wait_for_zenoh_peers(&self, peer_count: usize, timeout: Duration) -> bool { + let waiter = + || async { self.session.info().peers_zid().res_async().await.count() == peer_count }; + wait_async(waiter, timeout).await + } + pub async fn make_zenoh_subscriber( &self, name: &str, @@ -607,9 +650,10 @@ impl Drop for ROS1Client { } } -pub trait Publisher { +#[async_trait] +pub trait Publisher: Sync { fn put(&self, data: Vec); - fn ready(&self) -> bool { + async fn ready(&self) -> bool { true } } @@ -619,6 +663,7 @@ impl Publisher for ZenohPublisher { async_std::task::spawn_blocking(move || inner.put(data).res_sync().unwrap()); } } +#[async_trait] impl Publisher for ROS1Publisher { fn put(&self, data: Vec) { let inner = self.inner.clone(); @@ -627,10 +672,11 @@ impl Publisher for ROS1Publisher { }); } - fn ready(&self) -> bool { + async fn ready(&self) -> bool { self.inner.data.subscriber_count() != 0 } } +#[async_trait] impl Publisher for ZenohQuery { fn put(&self, data: Vec) { async_std::task::spawn(Self::query_loop( @@ -642,14 +688,14 @@ impl Publisher for ZenohQuery { )); } - fn ready(&self) -> bool { + async fn ready(&self) -> bool { let data = (0..10).collect(); - async_std::task::block_on( - async move { Self::make_query(&self.inner, &self.key, &data).await }, - ) - .is_ok() + Self::make_query(&self.inner, &self.key, &data) + .await + .is_ok() } } +#[async_trait] impl Publisher for ROS1Client { fn put(&self, data: Vec) { let running = self.running.clone(); @@ -666,14 +712,18 @@ impl Publisher for ROS1Client { }); } - fn ready(&self) -> bool { + async fn ready(&self) -> bool { let description = RawMessageDescription { msg_definition: String::from("*"), md5sum: self.topic.md5.clone(), msg_type: self.topic.datatype.clone(), }; let data = (0..10).collect(); - Self::make_query(description, &data, &self.ros1_client).is_ok() + let ros1_client = self.ros1_client.clone(); + async_std::task::spawn_blocking(move || { + Self::make_query(description, &data, &ros1_client).is_ok() + }) + .await } } @@ -690,7 +740,7 @@ pub struct ROS1Service { pub _inner: RAIICounter, } -pub trait Subscriber { +pub trait Subscriber: Sync { fn ready(&self) -> bool { true } diff --git a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/zenoh_client.rs b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/zenoh_client.rs index 593abde..9184ff3 100644 --- a/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/zenoh_client.rs +++ b/zenoh-plugin-ros1/src/ros_to_zenoh_bridge/zenoh_client.rs @@ -90,6 +90,7 @@ impl ZenohClient { .await } + #[cfg(feature = "test")] pub async fn make_query<'b, Callback, IntoSelector>( &self, selector: IntoSelector, diff --git a/zenoh-plugin-ros1/tests/bridge_to_bridge.rs b/zenoh-plugin-ros1/tests/bridge_to_bridge.rs index 702fbd4..edb75e0 100644 --- a/zenoh-plugin-ros1/tests/bridge_to_bridge.rs +++ b/zenoh-plugin-ros1/tests/bridge_to_bridge.rs @@ -13,14 +13,18 @@ // use std::sync::Arc; +use std::time::Duration; use std::{collections::HashSet, sync::atomic::AtomicU64}; +use async_std::prelude::FutureExt; use log::{debug, trace}; use rosrust::RawMessage; use std::sync::atomic::{AtomicUsize, Ordering::*}; use strum_macros::Display; use zenoh::prelude::{KeyExpr, OwnedKeyExpr}; -use zenoh_plugin_ros1::ros_to_zenoh_bridge::test_helpers::{self, wait, Publisher, Subscriber}; +use zenoh_plugin_ros1::ros_to_zenoh_bridge::test_helpers::{ + self, wait_async, Publisher, Subscriber, +}; use zenoh_plugin_ros1::ros_to_zenoh_bridge::{ bridging_mode::BridgingMode, environment::Environment, @@ -193,27 +197,42 @@ impl SrcDstPair { async fn start(&self) { self.wait_for_ready().await; - self.start_ping_pong().await; + assert!(self.start_ping_pong().await); } async fn wait_for_ready(&self) { assert!( - wait( - move || { self.src.ready() && self.dst.ready() }, + wait_async( + || async { self.src.ready().await && self.dst.ready() }, core::time::Duration::from_secs(30) ) .await ); } - async fn start_ping_pong(&self) { + async fn start_ping_pong(&self) -> bool { debug!("Starting ping-pong!"); let mut data = Vec::new(); data.reserve(TestParams::data_size() as usize); for i in 0..TestParams::data_size() { data.push((i % 255) as u8); } - self.src.put(data.clone()); + + async { + while { + self.src.put(data.clone()); + !test_helpers::wait_async_fn( + || self.counter.load(Relaxed) > 0, + Duration::from_secs(5), + ) + .await + } { + debug!("Restarting ping-pong!"); + } + } + .timeout(Duration::from_secs(30)) + .await + .is_ok() } async fn check_pps(&self, pps_measurements: u32) { diff --git a/zenoh-plugin-ros1/tests/discovery_test.rs b/zenoh-plugin-ros1/tests/discovery_test.rs index 04b1203..7576edb 100644 --- a/zenoh-plugin-ros1/tests/discovery_test.rs +++ b/zenoh-plugin-ros1/tests/discovery_test.rs @@ -22,33 +22,37 @@ use multiset::HashMultiSet; use zenoh::{prelude::keyexpr, OpenBuilder, Session}; use zenoh_core::{AsyncResolve, SyncResolve}; use zenoh_plugin_ros1::ros_to_zenoh_bridge::{ - discovery, + discovery::{self, LocalResources, RemoteResources}, test_helpers::{BridgeChecker, IsolatedConfig}, topic_descriptor::TopicDescriptor, }; -const TIMEOUT: Duration = Duration::from_secs(10); +const TIMEOUT: Duration = Duration::from_secs(60); fn session_builder(cfg: &IsolatedConfig) -> OpenBuilder { zenoh::open(cfg.peer()) } -fn discovery_builder(session: Arc) -> discovery::DiscoveryBuilder { - discovery::DiscoveryBuilder::new("*".to_string(), "*".to_string(), session) +fn remote_resources_builder(session: Arc) -> discovery::RemoteResourcesBuilder { + discovery::RemoteResourcesBuilder::new("*".to_string(), "*".to_string(), session) } fn make_session(cfg: &IsolatedConfig) -> Arc { session_builder(cfg).res_sync().unwrap().into_arc() } -fn make_discovery(session: Arc) -> discovery::Discovery { - async_std::task::block_on(discovery_builder(session).build()) +fn make_local_resources(session: Arc) -> LocalResources { + LocalResources::new("*".to_owned(), "*".to_owned(), session) +} +fn make_remote_resources(session: Arc) -> RemoteResources { + async_std::task::block_on(remote_resources_builder(session).build()) } #[test] fn discovery_instantination_one_instance() { let session = make_session(&IsolatedConfig::default()); - let _discovery = make_discovery(session); + let _remote = make_remote_resources(session.clone()); + let _local = make_local_resources(session); } #[test] @@ -61,7 +65,9 @@ fn discovery_instantination_many_instances() { let mut discoveries = Vec::new(); for session in sessions.iter() { - discoveries.push(make_discovery(session.clone())); + let remote = make_remote_resources(session.clone()); + let local = make_local_resources(session.clone()); + discoveries.push((remote, local)); } } @@ -81,7 +87,10 @@ impl DiscoveryCollector { } } - pub fn use_builder(&self, builder: discovery::DiscoveryBuilder) -> discovery::DiscoveryBuilder { + pub fn use_builder( + &self, + builder: discovery::RemoteResourcesBuilder, + ) -> discovery::RemoteResourcesBuilder { let p = self.publishers.clone(); let s = self.subscribers.clone(); let srv = self.services.clone(); @@ -237,7 +246,7 @@ impl State { } async fn test_state_transition( - src_discovery: &discovery::Discovery, + local_resources: &LocalResources, rcv: &DiscoveryCollector, state: &State, ) { @@ -245,37 +254,22 @@ async fn test_state_transition( let mut _pub_entities = Vec::new(); for publisher in publishers.iter() { - _pub_entities.push( - src_discovery - .local_resources() - .declare_publisher(publisher) - .await, - ); + _pub_entities.push(local_resources.declare_publisher(publisher).await); } let mut _sub_entities = Vec::new(); for subscriber in subscribers.iter() { - _sub_entities.push( - src_discovery - .local_resources() - .declare_subscriber(subscriber) - .await, - ); + _sub_entities.push(local_resources.declare_subscriber(subscriber).await); } let mut _srv_entities = Vec::new(); for service in services.iter() { - _srv_entities.push( - src_discovery - .local_resources() - .declare_service(service) - .await, - ); + _srv_entities.push(local_resources.declare_service(service).await); } let mut _cl_entities = Vec::new(); for client in clients.iter() { - _cl_entities.push(src_discovery.local_resources().declare_client(client).await); + _cl_entities.push(local_resources.declare_client(client).await); } rcv.wait_publishers(publishers).await; @@ -288,17 +282,17 @@ async fn run_discovery(scenario: Vec) { let cfg = IsolatedConfig::default(); let src_session = session_builder(&cfg).res_async().await.unwrap().into_arc(); - let src_discovery = discovery_builder(src_session).build().await; + let local_resources = make_local_resources(src_session.clone()); let rcv = DiscoveryCollector::new(); let rcv_session = session_builder(&cfg).res_async().await.unwrap().into_arc(); let _rcv_discovery = rcv - .use_builder(discovery_builder(rcv_session)) + .use_builder(remote_resources_builder(rcv_session)) .build() .await; for scene in scenario { - test_state_transition(&src_discovery, &rcv, &scene) + test_state_transition(&local_resources, &rcv, &scene) .timeout(TIMEOUT) .await .expect("Timeout waiting state transition!"); diff --git a/zenoh-plugin-ros1/tests/ping_pong_test.rs b/zenoh-plugin-ros1/tests/ping_pong_test.rs index 3ae33c1..44a0a48 100644 --- a/zenoh-plugin-ros1/tests/ping_pong_test.rs +++ b/zenoh-plugin-ros1/tests/ping_pong_test.rs @@ -12,6 +12,7 @@ // ZettaScale Zenoh Team, // +use async_std::prelude::FutureExt; use strum_macros::Display; use zenoh::prelude::SplitBuffer; use zenoh_core::SyncResolve; @@ -22,15 +23,16 @@ use std::{ atomic::{AtomicBool, AtomicU64, Ordering::*}, Arc, }, + time::Duration, }; use zenoh_plugin_ros1::ros_to_zenoh_bridge::{ bridging_mode::BridgingMode, environment::Environment, test_helpers::{ - BridgeChecker, Publisher, ROS1Client, ROS1Publisher, ROS1Service, ROS1Subscriber, - ROSEnvironment, RunningBridge, Subscriber, TestParams, ZenohPublisher, ZenohQuery, - ZenohQueryable, ZenohSubscriber, + self, wait_async_fn, BridgeChecker, Publisher, ROS1Client, ROS1Publisher, ROS1Service, + ROS1Subscriber, ROSEnvironment, RunningBridge, Subscriber, TestParams, ZenohPublisher, + ZenohQuery, ZenohQueryable, ZenohSubscriber, }, }; use zenoh_plugin_ros1::ros_to_zenoh_bridge::{ @@ -234,13 +236,13 @@ impl PingPong { let rpub = ros1_pub.clone(); let zenoh_sub = backend .make_zenoh_subscriber(key, move |msg| { + c.fetch_add(1, Relaxed); let data = msg.value.payload.contiguous().to_vec(); debug!( "PingPong: transferring {} bytes from Zenoh to ROS1!", data.len() ); rpub.data.send(rosrust::RawMessage(data)).unwrap(); - c.fetch_add(1, Relaxed); }) .await; @@ -268,12 +270,12 @@ impl PingPong { let c = cycles.clone(); let zpub = zenoh_pub.clone(); let ros1_sub = backend.make_ros_subscriber(key, move |msg: rosrust::RawMessage| { + c.fetch_add(1, Relaxed); debug!( "PingPong: transferring {} bytes from ROS1 to Zenoh!", msg.0.len() ); zpub.put(msg.0).res_sync().unwrap(); - c.fetch_add(1, Relaxed); }); PingPong { @@ -289,17 +291,28 @@ impl PingPong { async fn start(&self) { self.wait_for_pub_sub_ready().await; - self.start_ping_pong().await; + assert!(self.start_ping_pong().await); } - async fn start_ping_pong(&self) { + async fn start_ping_pong(&self) -> bool { debug!("Starting ping-pong!"); let mut data = Vec::new(); data.reserve(TestParams::data_size() as usize); for i in 0..TestParams::data_size() { data.push((i % 255) as u8); } - self.pub_sub.publisher.put(data.clone()); + + async { + while { + self.pub_sub.publisher.put(data.clone()); + !wait_async_fn(|| self.cycles.load(Relaxed) > 0, Duration::from_secs(5)).await + } { + debug!("Restarting ping-pong!"); + } + } + .timeout(Duration::from_secs(30)) + .await + .is_ok() } async fn check_pps(&self, pps_measurements: u32) { @@ -319,8 +332,8 @@ impl PingPong { let mut duration: u64 = 0; self.cycles.store(0, Relaxed); - while !(result > 0.0 || duration >= 10000) { - async_std::task::sleep(core::time::Duration::from_millis(duration_milliseconds)).await; + while !(result > 0.0 || duration >= 30000) { + async_std::task::sleep(Duration::from_millis(duration_milliseconds)).await; duration += duration_milliseconds; result += self.cycles.load(Relaxed) as f64; } @@ -330,32 +343,14 @@ impl PingPong { async fn wait_for_pub_sub_ready(&self) { assert!( - Self::wait( - move || { self.pub_sub.publisher.ready() && self.pub_sub.subscriber.ready() }, - core::time::Duration::from_secs(30) + test_helpers::wait_async( + || async { + self.pub_sub.publisher.ready().await && self.pub_sub.subscriber.ready() + }, + Duration::from_secs(30) ) .await ); - async_std::task::sleep(time::Duration::from_secs(1)).await; - } - - async fn wait(waiter: Waiter, timeout: core::time::Duration) -> bool - where - Waiter: Fn() -> bool, - { - let cycles = 1000; - let micros = timeout.as_micros() / cycles; - - for _i in 0..cycles { - async_std::task::sleep(core::time::Duration::from_micros( - micros.try_into().unwrap(), - )) - .await; - if waiter() { - return true; - } - } - false } } @@ -383,6 +378,7 @@ impl TestEnvironment { // - performs wait and ensures that everything is properly connected and negotiated within the bridge async_std::task::block_on(bridge.assert_ros_ok()); async_std::task::block_on(bridge.assert_bridge_empy()); + async_std::task::block_on(checker.assert_zenoh_peers(1)); TestEnvironment { bridge,