diff --git a/Cargo.lock b/Cargo.lock index 43b9bfdc..f57d7ee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -765,6 +765,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.9.4" @@ -907,6 +913,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.66", +] + [[package]] name = "digest" version = "0.10.7" @@ -1063,6 +1082,26 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "example-streamer-uses" +version = "0.1.5-dev" +dependencies = [ + "async-trait", + "chrono", + "clap", + "env_logger 0.10.2", + "hello-world-protos", + "json5", + "log", + "protobuf", + "serde", + "tokio", + "up-rust", + "up-transport-vsomeip", + "up-transport-zenoh 0.1.0 (git+https://github.com/eclipse-uprotocol/up-transport-zenoh-rust.git?rev=7c839e7a94f526a82027564a609f48a79a3f4eae)", + "zenoh", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -4031,9 +4070,35 @@ dependencies = [ "up-rust", "up-streamer", "up-transport-vsomeip", - "up-transport-zenoh", + "up-transport-zenoh 0.1.0 (git+https://github.com/eclipse-uprotocol/up-transport-zenoh-rust.git?rev=7c839e7a94f526a82027564a609f48a79a3f4eae)", + "usubscription-static-file", + "zenoh", +] + +[[package]] +name = "up-linux-streamer-plugin" +version = "0.1.5-dev" +dependencies = [ + "async-std", + "const_format", + "env_logger 0.10.2", + "futures", + "git-version", + "serde", + "serde_json", + "tokio", + "tracing", + "up-rust", + "up-streamer", + "up-transport-vsomeip", + "up-transport-zenoh 0.1.0 (git+https://github.com/eclipse-uprotocol/up-transport-zenoh-rust.git?rev=b977153265d22c4bb4bbbc20dea403342b9ab438)", "usubscription-static-file", "zenoh", + "zenoh-core", + "zenoh-plugin-trait", + "zenoh-result", + "zenoh-util", + "zenoh_backend_traits", ] [[package]] @@ -4121,6 +4186,28 @@ dependencies = [ "zenoh", ] +[[package]] +name = "up-transport-zenoh" +version = "0.1.0" +source = "git+https://github.com/eclipse-uprotocol/up-transport-zenoh-rust.git?rev=b977153265d22c4bb4bbbc20dea403342b9ab438#b977153265d22c4bb4bbbc20dea403342b9ab438" +dependencies = [ + "anyhow", + "async-trait", + "bitmask-enum", + "chrono", + "crossbeam-channel", + "env_logger 0.10.2", + "lazy_static", + "log", + "prost", + "prost-types", + "protobuf", + "rand", + "tokio", + "up-rust", + "zenoh", +] + [[package]] name = "uriparse" version = "0.6.4" @@ -5113,6 +5200,24 @@ dependencies = [ "zenoh-result", ] +[[package]] +name = "zenoh_backend_traits" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458b5c151d7e31de67d45f1ba9cc409ddeb762adc7ce35c3c81d9dcad314da97" +dependencies = [ + "async-std", + "async-trait", + "const_format", + "derive_more", + "schemars", + "serde_json", + "zenoh", + "zenoh-plugin-trait", + "zenoh-result", + "zenoh-util", +] + [[package]] name = "zerocopy" version = "0.7.34" diff --git a/Cargo.toml b/Cargo.toml index abac373d..e33b907b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,10 @@ [workspace] resolver = "2" members = [ + "example-streamer-uses", "utils/hello-world-protos", "utils/integration-test-utils", - "up-linux-streamer", + "up-linux-streamer", "up-linux-streamer-plugin", "up-streamer", "subscription-cache", "utils/usubscription-static-file"] [workspace.package] diff --git a/example-streamer-uses/Cargo.toml b/example-streamer-uses/Cargo.toml new file mode 100644 index 00000000..fcb1c095 --- /dev/null +++ b/example-streamer-uses/Cargo.toml @@ -0,0 +1,60 @@ +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "example-streamer-uses" +rust-version.workspace = true +version.workspace = true +repository.workspace = true +homepage.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true + +[[bin]] +name = "me_client" + +[[bin]] +name = "me_publisher" + +[[bin]] +name = "me_service" + +[[bin]] +name = "me_subscriber" + +[[bin]] +name = "ue_client" + +[[bin]] +name = "ue_publisher" + +[[bin]] +name = "ue_service" + +[[bin]] +name = "ue_subscriber" + +[dependencies] +async-trait = { workspace = true } +chrono = { version = "0.4" } +clap = { version = "4.5.9", features = ["derive"] } +env_logger = { version = "0.10.2" } +hello-world-protos = { path = "../utils/hello-world-protos" } +log = { workspace = true } +json5 = { workspace = true } +protobuf = { workspace = true } +serde = { workspace = true } +tokio = { workspace = true } +up-rust = { workspace = true } +up-transport-zenoh = { git = "https://github.com/eclipse-uprotocol/up-transport-zenoh-rust.git", rev = "7c839e7a94f526a82027564a609f48a79a3f4eae" } +up-transport-vsomeip = { git = "https://github.com/eclipse-uprotocol/up-transport-vsomeip-rust.git", rev = "acbb0d0c9b8b48dd35c74f461e97151f1e922000", default-features = false } +zenoh = { version = "0.11.0-rc.3", features = ["unstable"]} diff --git a/up-linux-streamer/examples/README.md b/example-streamer-uses/README.md similarity index 100% rename from up-linux-streamer/examples/README.md rename to example-streamer-uses/README.md diff --git a/up-linux-streamer/examples/mE_client.rs b/example-streamer-uses/src/bin/me_client.rs similarity index 86% rename from up-linux-streamer/examples/mE_client.rs rename to example-streamer-uses/src/bin/me_client.rs index 6d1f890f..d6e6c1ce 100644 --- a/up-linux-streamer/examples/mE_client.rs +++ b/example-streamer-uses/src/bin/me_client.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use async_trait::async_trait; use hello_world_protos::hello_world_service::{HelloRequest, HelloResponse}; use log::trace; diff --git a/up-linux-streamer/examples/mE_publisher.rs b/example-streamer-uses/src/bin/me_publisher.rs similarity index 100% rename from up-linux-streamer/examples/mE_publisher.rs rename to example-streamer-uses/src/bin/me_publisher.rs diff --git a/up-linux-streamer/examples/mE_service.rs b/example-streamer-uses/src/bin/me_service.rs similarity index 86% rename from up-linux-streamer/examples/mE_service.rs rename to example-streamer-uses/src/bin/me_service.rs index 9e3453b8..04987800 100644 --- a/up-linux-streamer/examples/mE_service.rs +++ b/example-streamer-uses/src/bin/me_service.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use async_trait::async_trait; use hello_world_protos::hello_world_service::{HelloRequest, HelloResponse}; use log::{error, trace}; diff --git a/up-linux-streamer/examples/mE_subscriber.rs b/example-streamer-uses/src/bin/me_subscriber.rs similarity index 100% rename from up-linux-streamer/examples/mE_subscriber.rs rename to example-streamer-uses/src/bin/me_subscriber.rs diff --git a/up-linux-streamer/examples/uE_client.rs b/example-streamer-uses/src/bin/ue_client.rs similarity index 100% rename from up-linux-streamer/examples/uE_client.rs rename to example-streamer-uses/src/bin/ue_client.rs diff --git a/up-linux-streamer/examples/uE_publisher.rs b/example-streamer-uses/src/bin/ue_publisher.rs similarity index 100% rename from up-linux-streamer/examples/uE_publisher.rs rename to example-streamer-uses/src/bin/ue_publisher.rs diff --git a/up-linux-streamer/examples/uE_service.rs b/example-streamer-uses/src/bin/ue_service.rs similarity index 86% rename from up-linux-streamer/examples/uE_service.rs rename to example-streamer-uses/src/bin/ue_service.rs index ebda9a75..7e4156df 100644 --- a/up-linux-streamer/examples/uE_service.rs +++ b/example-streamer-uses/src/bin/ue_service.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use async_trait::async_trait; use clap::Parser; use hello_world_protos::hello_world_service::{HelloRequest, HelloResponse}; diff --git a/up-linux-streamer/examples/uE_subscriber.rs b/example-streamer-uses/src/bin/ue_subscriber.rs similarity index 100% rename from up-linux-streamer/examples/uE_subscriber.rs rename to example-streamer-uses/src/bin/ue_subscriber.rs diff --git a/up-linux-streamer/vsomeip-configs/mE_client.json b/example-streamer-uses/vsomeip-configs/mE_client.json similarity index 100% rename from up-linux-streamer/vsomeip-configs/mE_client.json rename to example-streamer-uses/vsomeip-configs/mE_client.json diff --git a/up-linux-streamer/vsomeip-configs/mE_publisher.json b/example-streamer-uses/vsomeip-configs/mE_publisher.json similarity index 100% rename from up-linux-streamer/vsomeip-configs/mE_publisher.json rename to example-streamer-uses/vsomeip-configs/mE_publisher.json diff --git a/up-linux-streamer/vsomeip-configs/mE_service.json b/example-streamer-uses/vsomeip-configs/mE_service.json similarity index 100% rename from up-linux-streamer/vsomeip-configs/mE_service.json rename to example-streamer-uses/vsomeip-configs/mE_service.json diff --git a/up-linux-streamer/vsomeip-configs/mE_subscriber.json b/example-streamer-uses/vsomeip-configs/mE_subscriber.json similarity index 100% rename from up-linux-streamer/vsomeip-configs/mE_subscriber.json rename to example-streamer-uses/vsomeip-configs/mE_subscriber.json diff --git a/subscription-cache/Cargo.toml b/subscription-cache/Cargo.toml index 62acd8be..dc8f016d 100644 --- a/subscription-cache/Cargo.toml +++ b/subscription-cache/Cargo.toml @@ -1,3 +1,14 @@ +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + [package] name = "subscription-cache" rust-version.workspace = true @@ -20,4 +31,4 @@ uuid = { workspace = true } serde_json = { workspace = true } serde = { workspace = true } up-rust = { workspace = true, features = ["usubscription"] } -protobuf = { version = "3.3", features = ["with-bytes"] } \ No newline at end of file +protobuf = { version = "3.3", features = ["with-bytes"] } diff --git a/up-linux-streamer-plugin/Cargo.toml b/up-linux-streamer-plugin/Cargo.toml new file mode 100644 index 00000000..8e8ad88c --- /dev/null +++ b/up-linux-streamer-plugin/Cargo.toml @@ -0,0 +1,57 @@ +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "up-linux-streamer-plugin" +rust-version.workspace = true +version.workspace = true +repository.workspace = true +homepage.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true + +[features] +default = ["bundled-vsomeip", "dynamic_plugin", "zenoh/default", "zenoh/unstable", "zenoh/plugins"] +bundled-vsomeip = ["up-transport-vsomeip/bundled"] +dynamic_plugin = [] + +[lib] +# When auto-detecting the "example" plugin, `zenohd` will look for a dynamic library named "zenoh_plugin_example" +# `zenohd` will expect the file to be named according to OS conventions: +# - libzenoh_plugin_example.so on linux +# - libzenoh_plugin_example.dylib on macOS +# - zenoh_plugin_example.dll on Windows +name = "zenoh_plugin_up_linux_streamer" +# This crate type will make `cargo` output a dynamic library instead of a rust static library +crate-type = ["cdylib"] + +[dependencies] +async-std = { version = "=1.12.0", default-features = false } +const_format = "0.2.30" +futures = { version = "0.3.25" } +git-version = { version = "0.3.5" } +tracing = { version = "0.1" } +serde = { version = "1.0.154" } +serde_json = { version = "1.0.94" } +tokio = { version = "1.35.1", default-features = false } +up-rust = { git = "https://github.com/eclipse-uprotocol/up-rust", rev = "3a50104421a801d52e1d9c68979db54c013ce43d" } +up-transport-zenoh = { git = "https://github.com/eclipse-uprotocol/up-transport-zenoh-rust.git", rev = "b977153265d22c4bb4bbbc20dea403342b9ab438" } +up-transport-vsomeip = { git = "https://github.com/eclipse-uprotocol/up-transport-vsomeip-rust.git", rev = "acbb0d0c9b8b48dd35c74f461e97151f1e922000", default-features = false } +up-streamer = { path = "../up-streamer" } +usubscription-static-file = {path = "../utils/usubscription-static-file"} +zenoh = { version = "0.11.0-rc.3", features = ["plugins", "unstable"] } +zenoh-core = { version = "0.11.0-rc.3" } +zenoh-plugin-trait = { version = "0.11.0-rc.3" } +zenoh-result = { version = "0.11.0-rc.3" } +zenoh-util = { version = "0.11.0-rc.3" } +zenoh_backend_traits = { version = "0.11.0-rc.3" } +env_logger = "0.10.2" diff --git a/up-linux-streamer-plugin/DEFAULT_CONFIG.json5 b/up-linux-streamer-plugin/DEFAULT_CONFIG.json5 new file mode 100644 index 00000000..a96a6d7d --- /dev/null +++ b/up-linux-streamer-plugin/DEFAULT_CONFIG.json5 @@ -0,0 +1,51 @@ +//// +//// This file presents the default configuration used by both the `up-linux-streamer-plugin` plugin. +//// The "up_linux_streamer" JSON5 object below can be used as such in the "plugins" part of a config file for the zenoh router (zenohd). +//// +{ + plugins: { + up_linux_streamer: { + // Should be left as true -- enforces that if the plugin .so cannot be found + // zenohd will panic and crash + __required__: true, + up_streamer_config: { + // The message queue size of each route between endpoints within the UStreamer + // Lower numbers mean that some messages will be dropped + message_queue_size: 10000 + }, + usubscription_config: { + // Lists the path to the subscription file when using static file + file_path: "usubscription-static-configs/testdata.json" + }, + // Configurations related to the host device we are running the streamer on + host_config: { + // Determines which transport to initialize for the host device + transport: "Zenoh", + // Determines the authority_name of the host device + // Used when initializing host transport + authority: "linux" + }, + someip_config: { + // Determines the authority_name of the mechatronics network + // Used when initializing SOME/IP transport + authority: "me_authority", + // The vsomeip configuration file to be used when initializing the vsomeip transport + // + // Some guidance: + // * each uEntity (uE) present on the host device should be listed in this file + // * this is needed to ensure that messages from the host are able to have an + // appropriate SOME/IP client_id when interacting with the mechatronics network + // * the `name` field does not matter, but could be used to reflect what a uE is generally + // called + // * the `id` field should be chosen such that it matches the ue_id of the uE on the host + // device + config_file: "vsomeip-configs/point_to_point.json", + // An ID to use for a vsomeip application which will represent all subscriptions to + // publish messages output over vsomeip + default_someip_application_id_for_someip_subscriptions: 10, + // Whether to enable bridging across to the mechatronics network + enabled: true + }, + } + } +} diff --git a/up-linux-streamer-plugin/README.md b/up-linux-streamer-plugin/README.md new file mode 100644 index 00000000..0b13c237 --- /dev/null +++ b/up-linux-streamer-plugin/README.md @@ -0,0 +1,183 @@ +# up-linux-streamer-plugin + +## The Concept + +```mermaid +classDiagram + +namespace EclipseRepos { + class LinuxStreamerZenohPlugin { + libzenoh_plugin_linux_streamer.so + } + class ZenohRouter + class up-transport-zenoh-rust + class up-transport-vsomeip-rust + class up-rust +} + + +namespace ExternalRepos { + class vsomeip { + vsomeip.so + } + class zenoh + class ZenohRouter { + zenohd + } +} + +note for LinuxStreamerZenohPlugin "Deployed to vehicle" +note for vsomeip "Deployed to vehicle" +note for ZenohRouter "Deployed to vehicle" + +LinuxStreamerZenohPlugin o-- up-transport-zenoh-rust: uses +LinuxStreamerZenohPlugin o-- up-transport-vsomeip-rust: uses + +up-transport-zenoh-rust ..> up-rust: builds upon +up-transport-vsomeip-rust ..> up-rust: builds upon + +up-transport-vsomeip-rust ..> vsomeip: builds upon +up-transport-zenoh-rust ..> zenoh: builds upon +LinuxStreamerZenohPlugin ..> ZenohRouter: loaded by +``` + +## Using the plugin + +### Build the plugin + +```bash +LD_LIBRARY_PATH= VSOMEIP_INSTALL_PATH= cargo build +``` + +### Bundled vsomeip or bring your own + +The default is to build a bundled version of [vsomeip](https://github.com/COVESA/vsomeip) for use by the `up-transport-vsomeip` crate. + +The vsomeip library is used to communicate over [SOME/IP](https://some-ip.com/) to mechatronics devices. + +If you wish to bring your own vsomeip install, you can use the flag `--no-default-features` flag when building with `cargo build`. For more details on required environment variables when building `up-transport-vsomeip-rust`, reference the README for [vsomeip-sys](https://github.com/eclipse-uprotocol/up-transport-vsomeip-rust/tree/main/vsomeip-sys). + +### Copy plugin and configs to standalone folder + +```bash +mkdir -p my/new/standalone/zenohd/path +cp target/debug/libzenoh_plugin_up_linux_streamer.so my/new/standalone/zenohd/path/ +cp up-linux-streamer-plugin/DEFAULT_CONFIG.json5 my/new/standalone/zenohd/path/ +cp -r up-linux-streamer-plugin/vsomeip-configs my/new/standalone/zenohd/path/ +``` + +### A note on compatability + +Because up-transport-zenoh-rust uses minimum supported Rust version (**MSRV**) of 1.74.0 and up-rust uses MSRV of 1.72.1, we need to build Zenoh from source in order to get a compatible zenohd (Zenoh Router). The following steps describe how to do so. + +Note: We have an open issue [here](https://github.com/eclipse-uprotocol/up-streamer-rust/issues/43) which is for creating a compatible Zenoh Router (zenohd) which will avoid the next section. + +### Getting Zenoh + +https: + +```bash +git clone https://github.com/eclipse-zenoh/zenoh.git +``` + +ssh: + +```bash +git clone git@github.com:eclipse-zenoh/zenoh.git +``` + +### Check out a compatible release tag + +Currently this is: + +```bash +git checkout release/0.11.0 +``` + +You can `git checkout -b ` here if you wish to save these changes to some fork for later usage. + +### Modify MSRV of Zenoh + +In zenoh/rust-toolchain.toml, modify to version `1.74.0`: + +```toml +[toolchain] +channel = "1.74.0" +``` + +In zenoh/Cargo.toml, modify rust-version: + +```toml +[workspace.package] +rust-version = "1.74.0" +``` + +### Make a build of Zenoh and copy to standalone folder + +Within the zenoh folder: + +```bash +cargo build +``` + +```bash +cp target/debug/zenohd my/new/standalone/zenohd/path +``` + +### Running zenohd with up-linux-streamer-plugin + +From within the `my/new/standalone/zenohd/path`: + +```bash +RUST_LOG=trace LD_LIBRARY_PATH= VSOMEIP_LIB_DIR= ./zenohd --config DEFAULT_CONFIG.json5 +``` + +You can also run without the `RUST_LOG=trace` environment variable prepended and should for production use cases. It can be used for debugging purposes. + +### You're done! + +## Supported Use Cases + +### RPC + +* `uE_client` <-> `mE_service` +* `mE_client` <-> `uE_service` + +### PubSub + +* `uE_publisher` -> `mE_subscriber` +* `mE_publisher` -> `uE_subscriber` + +## Running the examples + +You are able to run example applications of the names listed above like so from within this repo in one terminal: + +```bash +RUST_LOG=trace LD_LIBRARY_PATH= VSOMEIP_LIB_DIR= cargo run --example mE_client +``` + +and then in another terminal, again, within this repo: + +```bash +RUST_LOG=trace cargo run --example uE_service +``` + +You should then see messages coming through in the terminal which you ran mE_client: + +> Here we received response: HelloResponse { message: "The response to the request: me_client@i=3", special_fields: SpecialFields { unknown_fields: UnknownFields { fields: None }, cached_size: CachedSize { size: 0 } } } + +You may also run the other examples similarly. + +## Configuring the plugin + +### up-linux-streamer-plugin configuration + +A configuration file is required to be supplied to `zenohd` as shown in the [Using the plugin](#using-the-plugin) section. + +`DEFAULT_CONFIG.json5` is provided as a starting point, but can have certain parameters modified. Please reference it for guidance. + +### Host device presentation to mechatronics + +A configuration file is required to hold the ue_ids of all uEntities present on the host device which will communicate with the mechatronics network. + +`DEFAULT_CONFIG.json5` has guidance on these configurations as well. diff --git a/up-linux-streamer-plugin/src/config.rs b/up-linux-streamer-plugin/src/config.rs new file mode 100644 index 00000000..e2563bf3 --- /dev/null +++ b/up-linux-streamer-plugin/src/config.rs @@ -0,0 +1,58 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct Config { + pub(crate) __required__: bool, + pub(crate) up_streamer_config: UpStreamerConfig, + pub(crate) usubscription_config: USubscriptionConfig, + pub(crate) host_config: HostConfig, + pub(crate) someip_config: SomeipConfig, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct UpStreamerConfig { + pub(crate) message_queue_size: u16, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct USubscriptionConfig { + pub(crate) file_path: String, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct HostConfig { + pub(crate) transport: HostTransport, + pub(crate) authority: String, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct SomeipConfig { + pub(crate) authority: String, + pub(crate) config_file: PathBuf, + pub(crate) default_someip_application_id_for_someip_subscriptions: u16, + pub(crate) enabled: bool, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum HostTransport { + Zenoh, +} diff --git a/up-linux-streamer-plugin/src/lib.rs b/up-linux-streamer-plugin/src/lib.rs new file mode 100644 index 00000000..b113dd3e --- /dev/null +++ b/up-linux-streamer-plugin/src/lib.rs @@ -0,0 +1,223 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#![recursion_limit = "256"] + +// TODO: Consider if we're ever likely to want to use this _not_ as a Zenoh plugin, in which +// case the config module should be made pub and we should add validation on top of setting +// its members +mod config; + +use crate::config::HostTransport; +use async_std::task; +use config::Config; +use std::env; +use std::sync::{ + atomic::{AtomicBool, Ordering::Relaxed}, + Arc, Mutex, +}; +use std::time::Duration; +use tracing::trace; +use up_rust::UTransport; +use up_streamer::{Endpoint, UStreamer}; +use up_transport_vsomeip::UPTransportVsomeip; +use up_transport_zenoh::UPClientZenoh; +use usubscription_static_file::USubscriptionStaticFile; +use zenoh::plugins::{RunningPluginTrait, ZenohPlugin}; +use zenoh::runtime::Runtime; +use zenoh_core::zlock; +use zenoh_plugin_trait::{plugin_long_version, plugin_version, Plugin, PluginControl}; +use zenoh_result::{zerror, ZResult}; + +// The struct implementing the ZenohPlugin and ZenohPlugin traits +pub struct UpLinuxStreamerPlugin {} + +// declaration of the plugin's VTable for zenohd to find the plugin's functions to be called +#[cfg(feature = "dynamic_plugin")] +zenoh_plugin_trait::declare_plugin!(UpLinuxStreamerPlugin); + +impl ZenohPlugin for UpLinuxStreamerPlugin {} +impl Plugin for UpLinuxStreamerPlugin { + type StartArgs = Runtime; + type Instance = zenoh::plugins::RunningPlugin; + + // A mandatory const to define, in case of the plugin is built as a standalone executable + const DEFAULT_NAME: &'static str = "up_linux_streamer"; + const PLUGIN_VERSION: &'static str = plugin_version!(); + const PLUGIN_LONG_VERSION: &'static str = plugin_long_version!(); + + // The first operation called by zenohd on the plugin + fn start(name: &str, runtime: &Self::StartArgs) -> ZResult { + zenoh_util::try_init_log_from_env(); + trace!("up-linux-streamer-plugin: start"); + + let runtime_conf = runtime.config().lock(); + let plugin_conf = runtime_conf + .plugin(name) + .ok_or_else(|| zerror!("Plugin `{}`: missing config", name))?; + let config: Config = serde_json::from_value(plugin_conf.clone()) + .map_err(|e| zerror!("Plugin `{}` configuration error: {}", name, e))?; + trace!("loaded config: {config:?}"); + trace!("succeeded in reading plugin config"); + + // a flag to end the plugin's loop when the plugin is removed from the config + let flag = Arc::new(AtomicBool::new(true)); + // spawn the task running the plugin's loop + trace!("up-linux-streamer-plugin: before spawning run"); + async_std::task::spawn(run(runtime.clone(), config.clone(), flag.clone())); + trace!("up-linux-streamer-plugin: after spawning run"); + // return a RunningPlugin to zenohd + trace!("up-linux-streamer-plugin: before creating RunningPlugin"); + let ret = Box::new(RunningPlugin(Arc::new(Mutex::new(RunningPluginInner { + flag, + name: name.into(), + runtime: runtime.clone(), + })))); + trace!("up-linux-streamer-plugin: after creating RunningPlugin"); + + Ok(ret) + } +} + +// An inner-state for the RunningPlugin +struct RunningPluginInner { + flag: Arc, + #[allow(dead_code)] // Allowing this to be able to configure streamer at runtime later + name: String, + #[allow(dead_code)] // Allowing this to be able to configure streamer at runtime later + runtime: Runtime, +} +// The RunningPlugin struct implementing the RunningPluginTrait trait +#[derive(Clone)] +struct RunningPlugin(Arc>); + +impl PluginControl for RunningPlugin {} + +impl RunningPluginTrait for RunningPlugin { + fn config_checker( + &self, + _path: &str, + _old: &serde_json::Map, + _new: &serde_json::Map, + ) -> ZResult>> { + // TODO: Learn more about how the config_checker is used + Ok(None) + } +} + +// If the plugin is dropped, set the flag to false to end the loop +impl Drop for RunningPlugin { + fn drop(&mut self) { + zlock!(self.0).flag.store(false, Relaxed); + } +} + +async fn run(runtime: Runtime, config: Config, flag: Arc) { + trace!("up-linux-streamer-plugin: inside of run"); + zenoh_util::try_init_log_from_env(); + trace!("up-linux-streamer-plugin: after try_init_log_from_env()"); + + trace!("attempt to call something on the runtime"); + let timestamp_res = runtime.new_timestamp(); + trace!("called function on runtime: {timestamp_res:?}"); + + env_logger::init(); + + let subscription_path = config.usubscription_config.file_path; + let usubscription = Arc::new(USubscriptionStaticFile::new(subscription_path)); + + let mut streamer = match UStreamer::new( + "up-linux-streamer", + config.up_streamer_config.message_queue_size, + usubscription, + ) { + Ok(streamer) => streamer, + Err(error) => panic!("Failed to create uStreamer: {}", error), + }; + + let host_transport: Arc = Arc::new(match config.host_config.transport { + HostTransport::Zenoh => { + UPClientZenoh::new_with_runtime(runtime.clone(), config.host_config.authority.clone()) + .await + .expect("Unable to initialize Zenoh UTransport") + } // other host transports can be added here as they become available + }); + + let host_endpoint = Endpoint::new( + "host_endpoint", + &config.host_config.authority, + host_transport.clone(), + ); + + if config.someip_config.enabled { + let someip_config_file_abs_path = if config.someip_config.config_file.is_relative() { + env::current_dir() + .unwrap() + .join(&config.someip_config.config_file) + } else { + config.someip_config.config_file + }; + tracing::log::trace!("someip_config_file_abs_path: {someip_config_file_abs_path:?}"); + if !someip_config_file_abs_path.exists() { + panic!( + "The specified someip config_file doesn't exist: {someip_config_file_abs_path:?}" + ); + } + + // There will be at most one vsomeip_transport, as there is a connection into device and a streamer + let someip_transport: Arc = Arc::new( + UPTransportVsomeip::new_with_config( + &config.host_config.authority, + &config.someip_config.authority, + config + .someip_config + .default_someip_application_id_for_someip_subscriptions, + &someip_config_file_abs_path, + None, + ) + .expect("Unable to initialize vsomeip UTransport"), + ); + + let mechatronics_endpoint = Endpoint::new( + "mechatronics_endpoint", + &config.someip_config.authority, + someip_transport.clone(), + ); + let forwarding_res = streamer + .add_forwarding_rule(mechatronics_endpoint.clone(), host_endpoint.clone()) + .await; + + if let Err(err) = forwarding_res { + panic!("Unable to add forwarding result: {err:?}"); + } + + let forwarding_res = streamer + .add_forwarding_rule(host_endpoint.clone(), mechatronics_endpoint.clone()) + .await; + + if let Err(err) = forwarding_res { + panic!("Unable to add forwarding result: {err:?}"); + } + } + + // Plugin's event loop, while the flag is true + let mut counter = 1; + while flag.load(Relaxed) { + // TODO: Need to implement signaling to stop uStreamer + + task::sleep(Duration::from_millis(1000)).await; + trace!("counter: {counter}"); + + counter += 1; + } +} diff --git a/up-linux-streamer-plugin/usubscription-static-configs/testdata.json b/up-linux-streamer-plugin/usubscription-static-configs/testdata.json new file mode 100644 index 00000000..efcb9f41 --- /dev/null +++ b/up-linux-streamer-plugin/usubscription-static-configs/testdata.json @@ -0,0 +1,4 @@ +{ + "//linux/3039/1/8001": ["//me_authority/5678/1/1234"], + "//me_authority/5BA0/1/8001": ["//linux/5678/1/1234"] +} diff --git a/up-linux-streamer-plugin/vsomeip-configs/point_to_point.json b/up-linux-streamer-plugin/vsomeip-configs/point_to_point.json new file mode 100644 index 00000000..aecf2bd9 --- /dev/null +++ b/up-linux-streamer-plugin/vsomeip-configs/point_to_point.json @@ -0,0 +1,9 @@ +{ + "applications" : + [ + { + "name" : "foo", + "id" : "0x1236" + } + ] +} \ No newline at end of file diff --git a/up-linux-streamer/Cargo.toml b/up-linux-streamer/Cargo.toml index 91cd1937..b451093d 100644 --- a/up-linux-streamer/Cargo.toml +++ b/up-linux-streamer/Cargo.toml @@ -1,3 +1,14 @@ +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + [package] name = "up-linux-streamer" rust-version.workspace = true @@ -14,6 +25,7 @@ bundled-vsomeip = ["up-transport-vsomeip/bundled"] [dependencies] async-trait = { workspace = true } +chrono = { version = "0.4" } clap = { version = "4.5.9", features = ["derive"] } env_logger = { version = "0.10.2" } log = { workspace = true } @@ -27,7 +39,6 @@ up-transport-zenoh = { git = "https://github.com/eclipse-uprotocol/up-transport- up-transport-vsomeip = { git = "https://github.com/eclipse-uprotocol/up-transport-vsomeip-rust.git", rev = "acbb0d0c9b8b48dd35c74f461e97151f1e922000", default-features = false } zenoh = { version = "0.11.0-rc.3", features = ["unstable"]} usubscription-static-file = {path = "../utils/usubscription-static-file"} -chrono = "0.4" [dev-dependencies] hello-world-protos = { path = "../utils/hello-world-protos" } diff --git a/up-linux-streamer/DEFAULT_CONFIG.json5 b/up-linux-streamer/DEFAULT_CONFIG.json5 index 3362e4e9..2e55c1f8 100644 --- a/up-linux-streamer/DEFAULT_CONFIG.json5 +++ b/up-linux-streamer/DEFAULT_CONFIG.json5 @@ -8,14 +8,6 @@ // Lower numbers mean that some messages will be dropped message_queue_size: 10000 }, - // Configurations related to the host device we are running the streamer on - host_config: { - // Determines which transport to initialize for the host device - transport: "Zenoh", - // Determines the authority_name of the host device - // Used when initializing host transport - authority: "linux" - }, usubscription_config: { // Lists the path to the subscription file when using static file file_path: "./utils/usubscription-static-file/static-configs/testdata.json" @@ -24,6 +16,14 @@ // Configuration file which is where zenoh config information is stored config_file: "./up-linux-streamer/ZENOH_CONFIG.json5" }, + // Configurations related to the host device we are running the streamer on + host_config: { + // Determines which transport to initialize for the host device + transport: "Zenoh", + // Determines the authority_name of the host device + // Used when initializing host transport + authority: "linux" + }, someip_config: { // Determines the authority_name of the mechatronics network // Used when initializing SOME/IP transport diff --git a/up-linux-streamer/README.md b/up-linux-streamer/README.md index 7b38d00d..50e3b9dc 100644 --- a/up-linux-streamer/README.md +++ b/up-linux-streamer/README.md @@ -10,7 +10,9 @@ As well, the `ZENOH_CONFIG.json5` file is used to set Zenoh configurations. By d ### Bundled vsomeip or bring your own -The default is to build a bundled version of vsomeip for use by the `up-transport-vsomeip` crate. +The default is to build a bundled version of [vsomeip](https://github.com/COVESA/vsomeip) for use by the `up-transport-vsomeip` crate. + +The vsomeip library is used to communicate over [SOME/IP](https://some-ip.com/) to mechatronics devices. If you wish to bring your own vsomeip install, you can use the flag `--no-default-features` flag when building with `cargo build`. For more details on required environment variables when building `up-transport-vsomeip-rust`, reference the README for [vsomeip-sys](https://github.com/eclipse-uprotocol/up-transport-vsomeip-rust/tree/main/vsomeip-sys). diff --git a/up-linux-streamer/src/config.rs b/up-linux-streamer/src/config.rs index b61fbd37..7056088b 100644 --- a/up-linux-streamer/src/config.rs +++ b/up-linux-streamer/src/config.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -5,9 +18,9 @@ use std::path::PathBuf; #[serde(deny_unknown_fields)] pub struct Config { pub(crate) up_streamer_config: UpStreamerConfig, - pub(crate) host_config: HostConfig, pub(crate) usubscription_config: USubscriptionConfig, pub(crate) zenoh_transport_config: ZenohTransportConfig, + pub(crate) host_config: HostConfig, pub(crate) someip_config: SomeipConfig, } @@ -19,15 +32,15 @@ pub struct UpStreamerConfig { #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(deny_unknown_fields)] -pub struct HostConfig { - pub(crate) transport: HostTransport, - pub(crate) authority: String, +pub struct USubscriptionConfig { + pub(crate) file_path: String, } #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(deny_unknown_fields)] -pub struct USubscriptionConfig { - pub(crate) file_path: String, +pub struct HostConfig { + pub(crate) transport: HostTransport, + pub(crate) authority: String, } #[derive(Deserialize, Serialize, Debug, Clone)] diff --git a/up-linux-streamer/src/main.rs b/up-linux-streamer/src/main.rs index acc3de74..b6853c96 100644 --- a/up-linux-streamer/src/main.rs +++ b/up-linux-streamer/src/main.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + mod config; use crate::config::{Config, HostTransport}; diff --git a/utils/hello-world-protos/Cargo.toml b/utils/hello-world-protos/Cargo.toml index f557b996..6004a282 100644 --- a/utils/hello-world-protos/Cargo.toml +++ b/utils/hello-world-protos/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Contributors to the Eclipse Foundation +# Copyright (c) 2024 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. diff --git a/utils/hello-world-protos/build.rs b/utils/hello-world-protos/build.rs index b594e84e..a31951fe 100644 --- a/utils/hello-world-protos/build.rs +++ b/utils/hello-world-protos/build.rs @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -11,7 +11,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -// use prost_build::Config; use protobuf_codegen::Customize; use std::env; use std::fs; @@ -21,13 +20,12 @@ fn main() -> std::io::Result<()> { // use vendored protoc instead of relying on user provided protobuf installation env::set_var("PROTOC", protoc_bin_vendored::protoc_bin_path().unwrap()); - // if let Err(err) = get_and_build_protos( if let Err(err) = get_and_build_protos( &[ "https://raw.githubusercontent.com/googleapis/googleapis/master/google/type/timeofday.proto", "https://raw.githubusercontent.com/eclipse-uprotocol/up-spec/main/up-core-api/uprotocol/uoptions.proto", "https://raw.githubusercontent.com/COVESA/uservices/main/src/main/proto/example/hello_world/v1/hello_world_topics.proto", - "https://raw.githubusercontent.com/PLeVasseur/uservices/feature/update-uprotocol-options-path/src/main/proto/example/hello_world/v1/hello_world_service.proto", + "https://raw.githubusercontent.com/COVESA/uservices/main/src/main/proto/example/hello_world/v1/hello_world_service.proto" ], "helloworld", ) { diff --git a/utils/hello-world-protos/src/lib.rs b/utils/hello-world-protos/src/lib.rs index 5fbfa139..d487b907 100644 --- a/utils/hello-world-protos/src/lib.rs +++ b/utils/hello-world-protos/src/lib.rs @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/utils/integration-test-utils/Cargo.toml b/utils/integration-test-utils/Cargo.toml index dc283c5a..4061b459 100644 --- a/utils/integration-test-utils/Cargo.toml +++ b/utils/integration-test-utils/Cargo.toml @@ -1,3 +1,14 @@ +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + [package] name = "integration-test-utils" rust-version.workspace = true diff --git a/utils/integration-test-utils/src/integration_test_listeners.rs b/utils/integration-test-utils/src/integration_test_listeners.rs index 7ce248e3..4a3435cc 100644 --- a/utils/integration-test-utils/src/integration_test_listeners.rs +++ b/utils/integration-test-utils/src/integration_test_listeners.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use async_std::sync::Mutex; use async_trait::async_trait; use log::debug; diff --git a/utils/integration-test-utils/src/integration_test_messages.rs b/utils/integration-test-utils/src/integration_test_messages.rs index b952207d..96274f20 100644 --- a/utils/integration-test-utils/src/integration_test_messages.rs +++ b/utils/integration-test-utils/src/integration_test_messages.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use crate::local_client_uuri; use up_rust::UMessageType::{ UMESSAGE_TYPE_NOTIFICATION, UMESSAGE_TYPE_PUBLISH, UMESSAGE_TYPE_REQUEST, diff --git a/utils/integration-test-utils/src/integration_test_utils.rs b/utils/integration-test-utils/src/integration_test_utils.rs index db69ed1a..d6bd67d2 100644 --- a/utils/integration-test-utils/src/integration_test_utils.rs +++ b/utils/integration-test-utils/src/integration_test_utils.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use crate::UPClientFoo; use async_broadcast::{Receiver, Sender}; use async_std::sync::{Condvar, Mutex}; diff --git a/utils/integration-test-utils/src/integration_test_uuris.rs b/utils/integration-test-utils/src/integration_test_uuris.rs index 1b79443d..da37972e 100644 --- a/utils/integration-test-utils/src/integration_test_uuris.rs +++ b/utils/integration-test-utils/src/integration_test_uuris.rs @@ -1,3 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + use up_rust::UUri; pub fn local_authority() -> String { diff --git a/utils/usubscription-static-file/Cargo.toml b/utils/usubscription-static-file/Cargo.toml index 8acb23a6..5f3cb988 100644 --- a/utils/usubscription-static-file/Cargo.toml +++ b/utils/usubscription-static-file/Cargo.toml @@ -1,3 +1,14 @@ +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + [package] name = "usubscription-static-file" rust-version.workspace = true @@ -15,4 +26,4 @@ async-std = { workspace = true, features = ["unstable"] } async-trait = "0.1.80" serde_json = { version = "1.0.111" } subscription-cache = {path="../../subscription-cache"} -up-rust = { workspace = true, features = ["usubscription"] } \ No newline at end of file +up-rust = { workspace = true, features = ["usubscription"] }