Skip to content

Commit acbb0d0

Browse files
authored
[#4] Implement UTransport for vsomeip
* Initial commit * Support Publish, Request, Response uProtocol message type send and receive * Add integration tests for use cases: PubSub, RPC, point to point (Streamer use case) Implements [#12]
1 parent 2b23b68 commit acbb0d0

39 files changed

Lines changed: 6416 additions & 25 deletions

Cargo.toml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,25 @@ keywords = ["uProtocol", "SDK", "vsomeip", "SOMEIP"]
3333
license = "Apache-2.0"
3434
readme = "README.md"
3535
repository = "https://github.com/eclipse-uprotocol/up-transport-vsomeip-rust"
36-
rust-version = "1.74"
36+
rust-version = "1.76"
3737
version = "0.1.0"
3838

3939
[workspace.dependencies]
4040
async-trait = { version = "0.1" }
41+
bimap = { version = "0.6.3" }
4142
bytes = { version = "1.5" }
42-
rand = { version = "0.8" }
43+
crossbeam-channel = { version = "0.5.13" }
44+
cxx = { version = "1.0" }
45+
lazy_static = { version = "1.4.0" }
46+
log = { version = "0.4.21", features = [] }
47+
once_cell = { version = "1.19.0" }
48+
protobuf = { version = "3.4.0" }
4349
regex = { version = "1.10" }
4450
serde = { version = "1.0", features = ["derive"] }
4551
serde_json = { version = "1.0" }
52+
tokio = { version = "1.35.1", features = ["rt", "rt-multi-thread", "macros", "sync", "time", "tracing"] }
4653
up-rust = { git = "https://github.com/eclipse-uprotocol/up-rust", rev = "3a50104421a801d52e1d9c68979db54c013ce43d" }
47-
url = { version = "2.5" }
48-
uuid = { version = "1.7", features = ["v8"] }
54+
vsomeip-proc-macro = { path = "vsomeip-proc-macro" }
4955
vsomeip-sys = { path = "vsomeip-sys", default-features = false }
5056

5157
[workspace.dev-dependencies]

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,27 @@ This library implements a uTransport client for vsomeip in Rust following the uP
88

99
### Building the Library
1010

11-
To build the library, run `cargo build` in the project root directory. Tests can be run with `cargo test`. This library leverages the [up-rust](https://github.com/eclipse-uprotocol/up-rust/tree/main) library for data types and models specified by uProtocol.
11+
To build the library, run:
12+
```bash
13+
VSOMEIP_INSTALL_PATH=<path/to/where/to/install/vsomeip> GENERIC_CPP_STDLIB_PATH=<path/to/generic/cpp/stdlib> ARCH_SPECIFIC_CPP_STDLIB_PATH=<path/to/arch_specific/cpp/stdlib> cargo build
14+
```
15+
16+
in the project root directory.
17+
18+
See `vsomeip-sys/README.md` for more details on options.
19+
20+
This library leverages the [up-rust](https://github.com/eclipse-uprotocol/up-rust) library for data types and models specified by uProtocol.
21+
22+
### Running the Tests
23+
24+
To run the tests, run
25+
```bash
26+
VSOMEIP_INSTALL_PATH= <path/to/vsomeip/install> LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<path/to/vsomeip/install>/lib GENERIC_CPP_STDLIB_PATH=<path/to/generic/cpp/stdlib> ARCH_SPECIFIC_CPP_STDLIB_PATH=<path/to/arch_specific/cpp/stdlib> cargo test -- --test-threads 1
27+
```
28+
29+
Breaking this down:
30+
* Details about the environment variables can be found in `vsomeip-sys/README.md`. Please reference there for further detail.
31+
* We need to pass in `-- --test-threads 1` because the tests refer to the same configurations and will fall over if they are run simultaneously. So we instruct to use a single thread, i.e. run the tests in serial.
1232

1333
### Using the Library
1434

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright (c) 2023 Contributors to the Eclipse Foundation
2+
#
3+
# See the NOTICE file(s) distributed with this work for additional
4+
# information regarding copyright ownership.
5+
#
6+
# This program and the accompanying materials are made available under the
7+
# terms of the Apache License Version 2.0 which is available at
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# SPDX-License-Identifier: Apache-2.0
11+
12+
[package]
13+
name = "hello-world-protos"
14+
version = "0.1.0"
15+
edition = "2021"
16+
17+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
18+
19+
[dependencies]
20+
bytes = { version = "1.5" }
21+
protobuf = { version = "3.3", features = ["with-bytes"] }
22+
23+
[build-dependencies]
24+
protobuf-codegen = { version = "3.3" }
25+
protoc-bin-vendored = { version = "3.0" }
26+
reqwest = { version = "0.12", features = ["blocking"] }
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/********************************************************************************
2+
* Copyright (c) 2023 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
14+
// use prost_build::Config;
15+
use protobuf_codegen::Customize;
16+
use std::env;
17+
use std::fs;
18+
use std::path::{Path, PathBuf};
19+
20+
fn main() -> std::io::Result<()> {
21+
// use vendored protoc instead of relying on user provided protobuf installation
22+
env::set_var("PROTOC", protoc_bin_vendored::protoc_bin_path().unwrap());
23+
24+
// if let Err(err) = get_and_build_protos(
25+
if let Err(err) = get_and_build_protos(
26+
&[
27+
"https://raw.githubusercontent.com/protocolbuffers/protobuf/main/src/google/protobuf/descriptor.proto",
28+
"https://raw.githubusercontent.com/googleapis/googleapis/master/google/type/timeofday.proto",
29+
"https://raw.githubusercontent.com/eclipse-uprotocol/up-spec/main/up-core-api/uprotocol/uoptions.proto",
30+
"https://raw.githubusercontent.com/COVESA/uservices/main/src/main/proto/example/hello_world/v1/hello_world_topics.proto",
31+
"https://raw.githubusercontent.com/PLeVasseur/uservices/feature/update-uprotocol-options-path/src/main/proto/example/hello_world/v1/hello_world_service.proto",
32+
],
33+
"helloworld",
34+
) {
35+
let error_message = format!("Failed to fetch and build protobuf file: {err:?}");
36+
return Err(std::io::Error::new(std::io::ErrorKind::Other, error_message));
37+
}
38+
39+
Ok(())
40+
}
41+
42+
// Fetch protobuf definitions from `url`, and build them
43+
fn get_and_build_protos(
44+
urls: &[&str],
45+
output_folder: &str,
46+
) -> core::result::Result<(), Box<dyn std::error::Error>> {
47+
let out_dir = env::var_os("OUT_DIR").unwrap();
48+
let proto_folder = Path::new(&out_dir).join("proto");
49+
let mut proto_files = Vec::new();
50+
51+
for url in urls {
52+
let file_name = url.split('/').last().unwrap();
53+
let mut file_path_buf = PathBuf::from(&proto_folder);
54+
55+
// Check if the URL is from googleapis to determine the correct path
56+
if url.contains("googleapis/googleapis") {
57+
file_path_buf.push("google/type");
58+
}
59+
60+
if url.contains("protocolbuffers/protobuf") {
61+
file_path_buf.push("google/protobuf");
62+
}
63+
64+
if url.contains("example/hello_world/v1") {
65+
file_path_buf.push("example/hello_world/v1")
66+
}
67+
68+
if url.contains("uprotocol/uoptions.proto") {
69+
file_path_buf.push("uprotocol");
70+
}
71+
72+
file_path_buf.push(file_name); // Push the file name to the path buffer
73+
74+
// Create the directory path if it doesn't exist
75+
if let Some(parent) = file_path_buf.parent() {
76+
fs::create_dir_all(parent)?;
77+
}
78+
79+
// Download the .proto file
80+
if let Err(err) = download_and_write_file(url, &file_path_buf) {
81+
panic!("Failed to download and write file: {err:?}");
82+
}
83+
84+
proto_files.push(file_path_buf);
85+
}
86+
87+
protobuf_codegen::Codegen::new()
88+
.protoc()
89+
// use vendored protoc instead of relying on user provided protobuf installation
90+
.protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
91+
.customize(Customize::default().tokio_bytes(true))
92+
.include(proto_folder)
93+
.inputs(proto_files)
94+
.cargo_out_dir(output_folder)
95+
.run_from_script();
96+
97+
Ok(())
98+
}
99+
100+
fn download_and_write_file(
101+
url: &str,
102+
dest_path: &PathBuf,
103+
) -> core::result::Result<(), Box<dyn std::error::Error>> {
104+
// Send a GET request to the URL
105+
reqwest::blocking::get(url)
106+
.map_err(Box::from)
107+
.and_then(|mut response| {
108+
if let Some(parent_path) = dest_path.parent() {
109+
std::fs::create_dir_all(parent_path)?;
110+
}
111+
let mut out_file = fs::File::create(dest_path)?;
112+
response
113+
.copy_to(&mut out_file)
114+
.map(|_| ())
115+
.map_err(|e| e.to_string().into())
116+
})
117+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/********************************************************************************
2+
* Copyright (c) 2023 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
14+
include!(concat!(env!("OUT_DIR"), "/helloworld/mod.rs"));

up-transport-vsomeip/Cargo.toml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,23 @@ bundled = ["vsomeip-sys/bundled"]
2929

3030
[dependencies]
3131
async-trait = { workspace = true }
32+
bimap = { workspace = true }
3233
bytes = { workspace = true }
33-
rand = { workspace = true }
34-
regex = { workspace = true }
34+
crossbeam-channel = { workspace = true }
35+
cxx = { workspace = true }
3536
serde = { workspace = true }
3637
serde_json = { workspace = true }
38+
tokio = { workspace = true }
3739
up-rust = { workspace = true }
38-
url = { workspace = true }
39-
uuid = { workspace = true }
40+
vsomeip-proc-macro = { workspace = true }
4041
vsomeip-sys = { workspace = true }
42+
lazy_static = { workspace = true }
43+
protobuf = { workspace = true }
44+
log = { workspace = true }
45+
once_cell = { workspace = true }
46+
futures = "0.3.30"
4147

4248
[dev-dependencies]
49+
env_logger = { version = "0.11.3" }
50+
hello-world-protos = { path = "../example-utils/hello-world-protos" }
4351
test-case = { version = "3.3" }
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
use async_trait::async_trait;
2+
use hello_world_protos::hello_world_service::{HelloRequest, HelloResponse};
3+
use log::trace;
4+
use std::fs::canonicalize;
5+
use std::path::PathBuf;
6+
use std::sync::Arc;
7+
use std::time::Duration;
8+
use up_rust::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY;
9+
use up_rust::{UListener, UMessage, UMessageBuilder, UStatus, UTransport, UUri};
10+
use up_transport_vsomeip::UPTransportVsomeip;
11+
12+
const HELLO_SERVICE_ID: u16 = 0x6000;
13+
const HELLO_INSTANCE_ID: u16 = 0x0001;
14+
const HELLO_METHOD_ID: u16 = 0x7FFF;
15+
16+
/// IMPORTANT: should match vsomeip::DEFAULT_MAJOR in (interface/vsomeip/constants.hpp):
17+
// but Autosar works better if vsomeip::DEFAULT_MAJOR is 1 (thus Autosar needs custom vsomeip build)
18+
const HELLO_SERVICE_MAJOR: u8 = 1;
19+
const _HELLO_SERVICE_MINOR: u32 = 0;
20+
21+
const HELLO_SERVICE_AUTHORITY: &str = "linux";
22+
const HELLO_SERVICE_UE_ID: u32 = (HELLO_INSTANCE_ID as u32) << 16 | HELLO_SERVICE_ID as u32;
23+
const HELLO_SERVICE_UE_VERSION_MAJOR: u8 = HELLO_SERVICE_MAJOR;
24+
const HELLO_SERVICE_RESOURCE_ID: u16 = HELLO_METHOD_ID;
25+
26+
const CLIENT_AUTHORITY: &str = "me_authority";
27+
const CLIENT_UE_ID: u16 = 0x5678;
28+
const CLIENT_UE_VERSION_MAJOR: u8 = 1;
29+
const CLIENT_RESOURCE_ID: u16 = 0;
30+
31+
const REQUEST_TTL: u32 = 1000;
32+
33+
struct ServiceResponseListener;
34+
35+
#[async_trait]
36+
impl UListener for ServiceResponseListener {
37+
async fn on_receive(&self, msg: UMessage) {
38+
println!("ServiceResponseListener: Received a message: {msg:?}");
39+
40+
let mut msg = msg.clone();
41+
42+
if let Some(ref mut attributes) = msg.attributes.as_mut() {
43+
attributes.payload_format =
44+
::protobuf::EnumOrUnknown::new(UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY);
45+
}
46+
47+
let Ok(hello_response) = msg.extract_protobuf_payload::<HelloResponse>() else {
48+
panic!("Unable to parse into HelloResponse");
49+
};
50+
51+
println!("Here we received response: {hello_response:?}");
52+
}
53+
54+
async fn on_error(&self, err: UStatus) {
55+
println!("ServiceResponseListener: Encountered an error: {err:?}");
56+
}
57+
}
58+
59+
#[tokio::main]
60+
async fn main() -> Result<(), UStatus> {
61+
env_logger::init();
62+
63+
println!("mE_client");
64+
65+
let crate_dir = env!("CARGO_MANIFEST_DIR");
66+
// TODO: Make configurable to pass the path to the vsomeip config as a command line argument
67+
let vsomeip_config = PathBuf::from(crate_dir).join("vsomeip_configs/hello_service.json");
68+
let vsomeip_config = canonicalize(vsomeip_config).ok();
69+
trace!("vsomeip_config: {vsomeip_config:?}");
70+
71+
// There will be a single vsomeip_transport, as there is a connection into device and a streamer
72+
// TODO: Add error handling if we fail to create a UPTransportVsomeip
73+
let client: Arc<dyn UTransport> = Arc::new(
74+
UPTransportVsomeip::new_with_config(
75+
&CLIENT_AUTHORITY.to_string(),
76+
&HELLO_SERVICE_AUTHORITY.to_string(),
77+
CLIENT_UE_ID,
78+
&vsomeip_config.unwrap(),
79+
None,
80+
)
81+
.unwrap(),
82+
);
83+
84+
let source = UUri {
85+
authority_name: CLIENT_AUTHORITY.to_string(),
86+
ue_id: CLIENT_UE_ID as u32,
87+
ue_version_major: CLIENT_UE_VERSION_MAJOR as u32,
88+
resource_id: CLIENT_RESOURCE_ID as u32,
89+
..Default::default()
90+
};
91+
let sink = UUri {
92+
authority_name: HELLO_SERVICE_AUTHORITY.to_string(),
93+
ue_id: HELLO_SERVICE_UE_ID,
94+
ue_version_major: HELLO_SERVICE_UE_VERSION_MAJOR as u32,
95+
resource_id: HELLO_SERVICE_RESOURCE_ID as u32,
96+
..Default::default()
97+
};
98+
99+
let service_response_listener: Arc<dyn UListener> = Arc::new(ServiceResponseListener);
100+
client
101+
.register_listener(&sink, Some(&source), service_response_listener)
102+
.await?;
103+
104+
let mut i = 0;
105+
loop {
106+
tokio::time::sleep(Duration::from_millis(1000)).await;
107+
108+
let hello_request = HelloRequest {
109+
name: format!("me_client@i={}", i).to_string(),
110+
..Default::default()
111+
};
112+
i += 1;
113+
114+
let request_msg = UMessageBuilder::request(sink.clone(), source.clone(), REQUEST_TTL)
115+
.build_with_protobuf_payload(&hello_request)
116+
.unwrap();
117+
println!("Sending Request message:\n{request_msg:?}");
118+
119+
client.send(request_msg).await?;
120+
}
121+
}

0 commit comments

Comments
 (0)