From 48d8c10a056aa18dac9a3a39331d5613a71b3ebb Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 24 Mar 2024 11:05:48 -0700 Subject: [PATCH 001/109] Digital Twin Graph --- .github/workflows/rust-ci.yml | 6 +- Cargo.toml | 4 + core/invehicle-digital-twin/Cargo.toml | 4 + core/invehicle-digital-twin/src/main.rs | 38 +- core/module/digital_twin_graph/Cargo.toml | 25 ++ .../src/digital_twin_graph_impl.rs | 383 ++++++++++++++++++ .../src/digital_twin_graph_module.rs | 55 +++ core/module/digital_twin_graph/src/lib.rs | 48 +++ .../digital_twin_graph/src/respond_impl.rs | 51 +++ core/module/digital_twin_registry/Cargo.toml | 26 ++ .../src/digital_twin_registry_impl.rs | 259 ++++++++++++ .../src/digital_twin_registry_module.rs | 38 ++ core/module/digital_twin_registry/src/lib.rs | 6 + core/protobuf_data_access/build.rs | 16 + core/protobuf_data_access/src/lib.rs | 21 + digital-twin-model/dtdl/dtmi/sdv/cabin-1.json | 12 +- .../dtdl/dtmi/sdv/infotainment-1.json | 4 +- digital-twin-model/dtdl/dtmi/sdv/seat-1.json | 11 +- .../dtdl/dtmi/sdv/seat_with_massager-1.json | 16 - .../dtdl/dtmi/sdv/vehicle-1.json | 4 +- digital-twin-model/src/sdv_v1.rs | 160 +++++++- .../async_rpc/v1/request.proto | 0 .../async_rpc/v1/respond.proto | 0 .../v1/digital_twin_graph.proto | 49 +++ .../v1/digital_twin_registry.proto | 50 +++ samples/graph/Cargo.toml | 40 ++ samples/graph/consumer/src/main.rs | 131 ++++++ .../graph/vehicle_core_provider/src/main.rs | 252 ++++++++++++ .../vehicle_core_provider/src/request_impl.rs | 131 ++++++ samples/protobuf_data_access/build.rs | 18 +- samples/protobuf_data_access/src/lib.rs | 16 + samples/seat_massager/provider/src/main.rs | 2 +- .../provider/src/request_impl.rs | 1 + 33 files changed, 1843 insertions(+), 34 deletions(-) create mode 100644 core/module/digital_twin_graph/Cargo.toml create mode 100644 core/module/digital_twin_graph/src/digital_twin_graph_impl.rs create mode 100644 core/module/digital_twin_graph/src/digital_twin_graph_module.rs create mode 100644 core/module/digital_twin_graph/src/lib.rs create mode 100644 core/module/digital_twin_graph/src/respond_impl.rs create mode 100644 core/module/digital_twin_registry/Cargo.toml create mode 100644 core/module/digital_twin_registry/src/digital_twin_registry_impl.rs create mode 100644 core/module/digital_twin_registry/src/digital_twin_registry_module.rs create mode 100644 core/module/digital_twin_registry/src/lib.rs delete mode 100644 digital-twin-model/dtdl/dtmi/sdv/seat_with_massager-1.json rename {samples/interfaces => interfaces}/async_rpc/v1/request.proto (100%) rename {samples/interfaces => interfaces}/async_rpc/v1/respond.proto (100%) create mode 100644 interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto create mode 100644 interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto create mode 100644 samples/graph/Cargo.toml create mode 100644 samples/graph/consumer/src/main.rs create mode 100644 samples/graph/vehicle_core_provider/src/main.rs create mode 100644 samples/graph/vehicle_core_provider/src/request_impl.rs diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 88c55cfa..6887f507 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -64,6 +64,8 @@ jobs: - name: Cache Dependencies uses: Swatinem/rust-cache@v2 - name: Build - run: cargo build + # Build the project with the `managed_subscribe` and `digital_twin_graph` features enabled. + run: cargo build --features "managed_subscribe,digital_twin_graph,digital_twin_registry" - name: Test - run: cargo test + # Test the project with the `managed_subscribe` and `digital_twin_graph` features enabled. + run: cargo test --features "managed_subscribe,digital_twin_graph,digital_twin_registry" diff --git a/Cargo.toml b/Cargo.toml index facbd89f..2e3bc3bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ members = [ # extension "core/module/managed_subscribe", + "core/module/digital_twin_graph", + "core/module/digital_twin_registry", # DTDL tools "dtdl-tools", @@ -27,6 +29,7 @@ members = [ "samples/common", "samples/protobuf_data_access", "samples/command", + "samples/graph", "samples/managed_subscribe", "samples/mixed", "samples/property", @@ -67,6 +70,7 @@ strum = "0.26.1" strum_macros = "0.26.1" tokio = "1.29.1" tokio-console-subscriber = { version = "0.2.0", package = "console-subscriber" } +tokio-retry = "0.3" tokio-stream = "0.1.14" tonic = "0.11.0" tonic-build = "0.11.0" diff --git a/core/invehicle-digital-twin/Cargo.toml b/core/invehicle-digital-twin/Cargo.toml index 213a8bd3..abceae65 100644 --- a/core/invehicle-digital-twin/Cargo.toml +++ b/core/invehicle-digital-twin/Cargo.toml @@ -19,6 +19,8 @@ http = { workspace = true } iref = { workspace = true } log = { workspace = true } common = { path = "../common" } +digital_twin_graph = { path = "../module/digital_twin_graph", optional = true } +digital_twin_registry = { path = "../module/digital_twin_registry", optional = true } managed_subscribe = { path = "../module/managed_subscribe", optional = true } parking_lot = { workspace = true } prost = { workspace = true } @@ -38,5 +40,7 @@ yaml-rust = { workspace = true } tonic-build = { workspace = true } [features] +digital_twin_graph = ["dep:digital_twin_graph"] +digital_twin_registry = ["dep:digital_twin_registry"] managed_subscribe = ["dep:managed_subscribe"] tokio_console = ["dep:tokio-console-subscriber", "tokio/tracing"] diff --git a/core/invehicle-digital-twin/src/main.rs b/core/invehicle-digital-twin/src/main.rs index 017af43c..f59c33f0 100644 --- a/core/invehicle-digital-twin/src/main.rs +++ b/core/invehicle-digital-twin/src/main.rs @@ -8,6 +8,12 @@ #[cfg(feature = "managed_subscribe")] use managed_subscribe::managed_subscribe_module::ManagedSubscribeModule; +#[cfg(feature = "digital_twin_graph")] +use digital_twin_graph::digital_twin_graph_module::DigitalTwinGraphModule; + +#[cfg(feature = "digital_twin_registry")] +use digital_twin_registry::digital_twin_registry_module::DigitalTwinRegistryModule; + // End: Module references. #[allow(unused_imports)] @@ -108,7 +114,7 @@ where #[cfg(feature = "managed_subscribe")] // (1) Adds the Managed Subscribe module to the service. - let server = { + let mut server = { // (2) Initialize the Managed Subscribe module, which implements GrpcModule. let managed_subscribe_module = ManagedSubscribeModule::new().await.map_err(|error| { error!("Unable to create Managed Subscribe module."); @@ -129,6 +135,36 @@ where server.add_module(new_middleware, Box::new(managed_subscribe_module)) }; + #[cfg(feature = "digital_twin_graph")] + // (1) Adds the Managed Subscribe module to the service. + let mut server = { + // (2) Initialize the Digital Twin Graph module, which implements GrpcModule. + let digital_twin_graph_module = DigitalTwinGraphModule::new().await.map_err(|error| { + error!("Unable to create Digital Twin Graph module."); + error + })?; + + info!("Initialized Digital Twin Graph module."); + + // (3) Add the module with the updated middleware stack to the server. + server.add_module(server.middleware.clone(), Box::new(digital_twin_graph_module)) + }; + + #[cfg(feature = "digital_twin_registry")] + // (1) Adds the Managed Subscribe module to the service. + let mut server = { + // (2) Initialize the Digital Twin Registry module, which implements GrpcModule. + let digital_twin_registry_module = DigitalTwinRegistryModule::new().await.map_err(|error| { + error!("Unable to create Digital Twin Registry module."); + error + })?; + + info!("Initialized Digital Twin Registry module."); + + // (3) Add the module with the updated middleware stack to the server. + server.add_module(server.middleware.clone(), Box::new(digital_twin_registry_module)) + }; + // Construct the server. let builder = server.construct_server().add_service(base_service); diff --git a/core/module/digital_twin_graph/Cargo.toml b/core/module/digital_twin_graph/Cargo.toml new file mode 100644 index 00000000..efa00c52 --- /dev/null +++ b/core/module/digital_twin_graph/Cargo.toml @@ -0,0 +1,25 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +[package] +name = "digital_twin_graph" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[dependencies] +common = { path = "../../common" } +core-protobuf-data-access = { path = "../../protobuf_data_access" } +log = { workspace = true } +serde = { workspace = true } +serde_derive = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true , features = ["full"] } +tonic = { workspace = true } +tower = { workspace = true } +yaml-rust = { workspace = true } +uuid = { workspace = true } + +[build-dependencies] +tonic-build = { workspace = true } diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs new file mode 100644 index 00000000..50dc0c9d --- /dev/null +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -0,0 +1,383 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use log::{debug, info, warn}; +use core_protobuf_data_access::async_rpc::v1::request::{request_client::RequestClient, AskRequest}; +use core_protobuf_data_access::async_rpc::v1::respond::AnswerRequest; +use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_client::DigitalTwinRegistryClient; +use core_protobuf_data_access::module::digital_twin_registry::v1::{EndpointInfo, FindByModelIdRequest, FindByInstanceIdRequest}; +use core_protobuf_data_access::module::digital_twin_graph::v1::{ + digital_twin_graph_server::DigitalTwinGraph, FindRequest, FindResponse, GetRequest, GetResponse, SetRequest, SetResponse, InvokeRequest, InvokeResponse, +}; +use std::sync::Arc; +use tokio::sync::broadcast; +use tokio::time::{sleep, timeout, Duration}; +use uuid::Uuid; + +use crate::{digital_twin_operation, digital_twin_protocol, TargetedPayload}; + +#[derive(Debug)] +pub struct DigitalTwinGraphImpl { + invehicle_digital_twin_uri: String, + respond_uri: String, + tx: Arc>, +} + +impl DigitalTwinGraphImpl { + /// Create a new instance of a DigitalTwinGraphImpl. + /// + /// # Arguments + /// * `invehicle_digital_twin_uri` - The uri for the invehicle digital twin service. + /// * `respond_uri` - The uri for the respond service. + /// * `tx` - The sender for the asynchronous channel for AnswerRequest's. + pub fn new( + invehicle_digital_twin_uri: &str, + respond_uri: &str, + tx: Arc>, + ) -> DigitalTwinGraphImpl { + DigitalTwinGraphImpl { + invehicle_digital_twin_uri: invehicle_digital_twin_uri.to_string(), + respond_uri: respond_uri.to_string(), + tx: tx, + } + } +} + +/// Is the provided subset a subset of the provided superset? +/// +/// # Arguments +/// * `subset` - The provided subset. +/// * `superset` - The provided superset. +fn is_subset(subset: &[String], superset: &[String]) -> bool { + subset.iter().all(|subset_member| { + superset.iter().any(|supserset_member| subset_member == supserset_member) + }) +} + +/// Use Ibeji to discover the endpoints for digital twin providers that satisfy the requirements. +/// +/// # Arguments +/// * `digitial_twin_registry_service_uri` - Digital Twin Registry Service URI. +/// * `model_id` - The matching model id. +/// * `protocol` - The required protocol. +/// * `operations` - The required operations. +pub async fn discover_digital_twin_providers_with_model_id( + digitial_twin_registry_service_uri: &str, + model_id: &str, + protocol: &str, + operations: &[String], +) -> Result, String> { + info!("Sending a find_by_model_id request for model id {model_id} to the Digital Twin Registry Service at {digitial_twin_registry_service_uri}"); + + let mut client = + DigitalTwinRegistryClient::connect(digitial_twin_registry_service_uri.to_string()) + .await + .map_err(|error| format!("{error}"))?; + let request = tonic::Request::new(FindByModelIdRequest { model_id: model_id.to_string() }); + let response = client.find_by_model_id(request).await.map_err(|error| error.to_string())?; + let response_inner = response.into_inner(); + debug!("Received the response for the find_by_model_id request"); + info!("response_payload: {:?}", response_inner.entity_access_info_list); + + + // Ok(response_inner.entity_access_info_list.iter().map(|entity_access_info| entity_access_info.endpoint_info_list.clone()).flatten().collect()) + + Ok(response_inner.entity_access_info_list.iter() + .map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) + .flatten() + .filter(|endpoint_info| { + endpoint_info.protocol == protocol + && is_subset(operations, &endpoint_info.operations) + }) + .collect()) +} + + +/// Use Ibeji to discover the endpoints for digital twin providers that satisfy the requirements. +/// +/// # Arguments +/// * `digitial_twin_registry_service_uri` - Digital Twin Registry Service URI. +/// * `instance_id` - The matching instance id. +/// * `protocol` - The required protocol. +/// * `operations` - The required operations. +pub async fn discover_digital_twin_providers_with_instance_id( + digitial_twin_registry_service_uri: &str, + instance_id: &str, + protocol: &str, + operations: &[String], +) -> Result, String> { + info!("Sending a find_by_instance_id request for instance id {instance_id} to the Digital Twin Registry Service at {digitial_twin_registry_service_uri}"); + + let mut client = + DigitalTwinRegistryClient::connect(digitial_twin_registry_service_uri.to_string()) + .await + .map_err(|error| format!("{error}"))?; + let request = tonic::Request::new(FindByInstanceIdRequest { instance_id: instance_id.to_string() }); + let response = client.find_by_instance_id(request).await.map_err(|error| error.to_string())?; + let response_inner = response.into_inner(); + debug!("Received the response for the find_by_instance_id request"); + info!("response_payload: {:?}", response_inner.entity_access_info_list); + + Ok(response_inner.entity_access_info_list.iter() + .map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) + .flatten() + .filter(|endpoint_info| { + endpoint_info.protocol == protocol + && is_subset(operations, &endpoint_info.operations) + }) + .collect()) +} + +#[tonic::async_trait] +impl DigitalTwinGraph for DigitalTwinGraphImpl { + /// Find implementation. + /// + /// # Arguments + /// * `request` - Find request. + async fn find( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let request_inner = request.into_inner(); + let model_id = request_inner.model_id; + + info!("Received a find request for model id {model_id}"); + + // Retrieve the provider details. + let provider_endpoint_info_list = discover_digital_twin_providers_with_model_id( + &self.invehicle_digital_twin_uri, + model_id.as_str(), + digital_twin_protocol::GRPC, + &[digital_twin_operation::GET.to_string()], + ) + .await.map_err(|error| tonic::Status::internal(error))?; + + info!(">> Found the provider endpoint info list: {provider_endpoint_info_list:?}"); + + let mut values = vec![]; + + for provider_endpoint_info in &provider_endpoint_info_list { + let provider_uri = provider_endpoint_info.uri.clone(); + let instance_id = provider_endpoint_info.context.clone(); + + let tx = Arc::clone(&self.tx); + let mut rx = tx.subscribe(); + + let client_result = RequestClient::connect(provider_uri.clone()).await; + if client_result.is_err() { + warn!("Unable to connect. We will skip this one."); + continue; + } + let mut client = client_result.unwrap(); + + // Note: The ask id must be a universally unique value. + let ask_id = Uuid::new_v4().to_string(); + + let targeted_payload = TargetedPayload { + instance_id: instance_id.clone(), + member_path: "".to_string(), + operation: digital_twin_operation::GET.to_string(), + payload: "".to_string(), + }; + + // Serialize the targeted payload. + let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); + + let request = tonic::Request::new(AskRequest { + respond_uri: self.respond_uri.clone(), + ask_id: ask_id.clone(), + payload: targeted_payload_json.clone(), + }); + + // Send the ask. + let response = client.ask(request).await; + if let Err(status) = response { + warn!("Unable to call ask, due to {status:?}\nWe will skip this one.."); + continue; + } + + // Wait for the answer request. + let mut answer_request: AnswerRequest = Default::default(); + let mut attempts_after_failure = 0; + const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; + while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { + match timeout(Duration::from_secs(5), rx.recv()).await { + Ok(Ok(request)) => { + if ask_id == request.ask_id { + // We have received the answer request that we are expecting. + answer_request = request; + break; + } else { + // Ignore this answer request, as it is not the one that we are expecting. + warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); + // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. + continue; + } + } + Ok(Err(error_message)) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + Err(error_message) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + } + } + + info!( + "Received an answer request. The ask_id is '{}'. The payload is '{}", + answer_request.ask_id, answer_request.payload + ); + + values.push(answer_request.payload); + } + + Ok(tonic::Response::new(FindResponse { values })) + } + + /// Get implementation. + /// + /// # Arguments + /// * `request` - Get request. + async fn get( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let request_inner = request.into_inner(); + let instance_id = request_inner.instance_id; + + info!("Received a get request for instance id {instance_id}"); + + // Retrieve the provider details. + let provider_endpoint_info_list = discover_digital_twin_providers_with_instance_id( + &self.invehicle_digital_twin_uri, + instance_id.as_str(), + digital_twin_protocol::GRPC, + &[digital_twin_operation::GET.to_string()], + ) + .await.map_err(|error| tonic::Status::internal(error))?; + + info!(">> Found the provider endpoint info list: {provider_endpoint_info_list:?}"); + + let mut values = vec![]; + + for provider_endpoint_info in &provider_endpoint_info_list { + let provider_uri = provider_endpoint_info.uri.clone(); + let instance_id = provider_endpoint_info.context.clone(); + + let tx = Arc::clone(&self.tx); + let mut rx = tx.subscribe(); + + let client_result = RequestClient::connect(provider_uri.clone()).await; + if client_result.is_err() { + warn!("Unable to connect. We will skip this one."); + continue; + } + let mut client = client_result.unwrap(); + + // Note: The ask id must be a universally unique value. + let ask_id = Uuid::new_v4().to_string(); + + let targeted_payload = TargetedPayload { + instance_id: instance_id.clone(), + member_path: "".to_string(), + operation: digital_twin_operation::GET.to_string(), + payload: "".to_string(), + }; + + // Serialize the targeted payload. + let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); + + let request = tonic::Request::new(AskRequest { + respond_uri: self.respond_uri.clone(), + ask_id: ask_id.clone(), + payload: targeted_payload_json.clone(), + }); + + // Send the ask. + let response = client.ask(request).await; + if let Err(status) = response { + warn!("Unable to call ask, due to {status:?}\nWe will skip this one.."); + continue; + } + + // Wait for the answer request. + let mut answer_request: AnswerRequest = Default::default(); + let mut attempts_after_failure = 0; + const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; + while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { + match timeout(Duration::from_secs(5), rx.recv()).await { + Ok(Ok(request)) => { + if ask_id == request.ask_id { + // We have received the answer request that we are expecting. + answer_request = request; + break; + } else { + // Ignore this answer request, as it is not the one that we are expecting. + warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); + // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. + continue; + } + } + Ok(Err(error_message)) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + Err(error_message) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + } + } + + info!( + "Received an answer request. The ask_id is '{}'. The payload is '{}", + answer_request.ask_id, answer_request.payload + ); + + values.push(answer_request.payload); + } + + if values.is_empty() { + return Err(tonic::Status::not_found("No values found")); + } + + Ok(tonic::Response::new(GetResponse { value: values[0].clone()})) + } + + /// Set implementation. + /// + /// # Arguments + /// * `request` - Set request. + async fn set( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + warn!("Got a set request: {request:?}"); + + Err(tonic::Status::unimplemented("set has not been implemented")) + } + + /// Invoke implementation. + /// + /// # Arguments + /// * `request` - Invoke request. + async fn invoke( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + warn!("Got a invoke request: {request:?}"); + + Err(tonic::Status::unimplemented("invoke has not been implemented")) + } +} diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs new file mode 100644 index 00000000..543e9f58 --- /dev/null +++ b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use common::grpc_module::GrpcModule; +use core_protobuf_data_access::module::digital_twin_graph::v1::digital_twin_graph_server::DigitalTwinGraphServer; +use core_protobuf_data_access::async_rpc::v1::respond::respond_server::RespondServer; +// use log::{debug, error, info}; +use std::sync::Arc; +use tonic::transport::server::RoutesBuilder; +use tokio::sync::broadcast; + +use crate::digital_twin_graph_impl::DigitalTwinGraphImpl; +use crate::respond_impl::RespondImpl; + +/// Digital Twin Graph Module. +#[derive(Clone, Debug)] +pub struct DigitalTwinGraphModule { + +} + +impl DigitalTwinGraphModule { + /// Creates a new instance of the DigitalTwinGraphModule. + pub async fn new() -> Result { + Ok(Self {}) + } +} + +impl GrpcModule for DigitalTwinGraphModule { + /// Adds the gRPC services for this module to the server builder. + /// + /// # Arguments + /// * `builder` - A tonic::RoutesBuilder that contains the grpc services to build. + fn add_grpc_services(&self, builder: &mut RoutesBuilder) { + // TODO: Setup config + let invehicle_digital_twin_authority = "0.0.0.0:5010"; + let invehicle_digital_twin_uri = format!("http://{invehicle_digital_twin_authority}"); // Devskim: ignore DS137138 + let respond_authority = "0.0.0.0:5010"; + let respond_uri = format!("http://{respond_authority}"); // Devskim: ignore DS137138 + + let (tx, _rx) = broadcast::channel(100); + let tx = Arc::new(tx); + + // Setup the respond service. + let respond_impl = RespondImpl::new(tx.clone()); + let respond_service = RespondServer::new(respond_impl); + + // Setup the digital twin graph service. + let digital_twin_graph_impl = DigitalTwinGraphImpl::new(&invehicle_digital_twin_uri, &respond_uri, tx); + let digital_twin_graph_service = DigitalTwinGraphServer::new(digital_twin_graph_impl); + + builder.add_service(digital_twin_graph_service); + builder.add_service(respond_service); + } +} \ No newline at end of file diff --git a/core/module/digital_twin_graph/src/lib.rs b/core/module/digital_twin_graph/src/lib.rs new file mode 100644 index 00000000..9401b448 --- /dev/null +++ b/core/module/digital_twin_graph/src/lib.rs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +pub mod digital_twin_graph_impl; +pub mod respond_impl; +pub mod digital_twin_graph_module; + +use serde_derive::{Deserialize, Serialize}; + +/// A targeted payload. +/// The targeting details helps on the receiver's side to dispatch the request. +#[derive(Serialize, Deserialize, Debug)] +pub struct TargetedPayload { + /// The instance id for the target entity. + pub instance_id: String, + /// The path within the target entity to member that we are targeting. + pub member_path: String, + /// The operation to be performed on the target entity's member. + pub operation: String, + /// The operation's payload. + pub payload: String, +} + +/// Status codes and messages. +pub mod status { + pub mod ok { + pub const CODE: i32 = 200; + pub const MESSAGE: &str = "Ok"; + } +} + +/// Supported digital twin operations. +pub mod digital_twin_operation { + pub const GET: &str = "Get"; + pub const SET: &str = "Set"; + pub const SUBSCRIBE: &str = "Subscribe"; + pub const UNSUBSCRIBE: &str = "Unsubscribe"; + pub const INVOKE: &str = "Invoke"; + pub const STREAM: &str = "Stream"; + pub const MANAGEDSUBSCRIBE: &str = "ManagedSubscribe"; +} + +// Supported digital twin protocols. +pub mod digital_twin_protocol { + pub const GRPC: &str = "grpc"; + pub const MQTT: &str = "mqtt"; +} \ No newline at end of file diff --git a/core/module/digital_twin_graph/src/respond_impl.rs b/core/module/digital_twin_graph/src/respond_impl.rs new file mode 100644 index 00000000..f8c1703c --- /dev/null +++ b/core/module/digital_twin_graph/src/respond_impl.rs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use log::{debug, info}; +use core_protobuf_data_access::async_rpc::v1::respond::respond_server::Respond; +use core_protobuf_data_access::async_rpc::v1::respond::{AnswerRequest, AnswerResponse}; +use std::sync::Arc; +use tokio::sync::broadcast; + +#[derive(Debug)] +pub struct RespondImpl { + pub tx: Arc>, +} + +impl RespondImpl { + /// Create a new instance of a RespondImpl. + /// + /// # Arguments + /// * `tx` - The sender for the asynchronous channel for AnswerRequest's. + pub fn new(tx: Arc>) -> RespondImpl { + RespondImpl { tx } + } +} + +#[tonic::async_trait] +impl Respond for RespondImpl { + /// Answer implementation. + /// + /// # Arguments + /// * `request` - The answer's request. + async fn answer( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + info!("Received an answer request"); + + let tx = Arc::clone(&self.tx); + + // Send the request to the channel. + if let Err(err_msg) = tx.send(request.into_inner()) { + return Err(tonic::Status::internal(format!( + "Failed to send the answer request due to {err_msg}" + ))); + } + + debug!("Completed the answer request."); + + Ok(tonic::Response::new(AnswerResponse {})) + } +} diff --git a/core/module/digital_twin_registry/Cargo.toml b/core/module/digital_twin_registry/Cargo.toml new file mode 100644 index 00000000..0c3ea93c --- /dev/null +++ b/core/module/digital_twin_registry/Cargo.toml @@ -0,0 +1,26 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +[package] +name = "digital_twin_registry" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[dependencies] +common = { path = "../../common" } +core-protobuf-data-access = { path = "../../protobuf_data_access" } +iref = { workspace = true } +log = { workspace = true } +parking_lot = { workspace = true } +serde = { workspace = true } +serde_derive = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true , features = ["macros", "rt-multi-thread"] } +tonic = { workspace = true } +tower = { workspace = true } +yaml-rust = { workspace = true } + +[build-dependencies] +tonic-build = { workspace = true } diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs new file mode 100644 index 00000000..891249d5 --- /dev/null +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -0,0 +1,259 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +extern crate iref; + +use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_server::DigitalTwinRegistry; +use core_protobuf_data_access::module::digital_twin_registry::v1::{ + EntityAccessInfo, EndpointInfo, FindByModelIdRequest, FindByModelIdResponse, FindByInstanceIdRequest, FindByInstanceIdResponse, RegisterRequest, RegisterResponse, +}; +use log::{debug, info}; +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use std::collections::HashMap; +use std::sync::Arc; +use std::vec::Vec; +use tonic::{Request, Response, Status}; + +#[derive(Debug, Default)] +pub struct DigitalTwinRegistryImpl { + pub entity_access_info_map: Arc>>>, +} + +#[tonic::async_trait] +impl DigitalTwinRegistry for DigitalTwinRegistryImpl { + /// Find by model id implementation. + /// + /// # Arguments + /// * `request` - Find by model id request. + async fn find_by_model_id( + &self, + request: Request, + ) -> Result, Status> { + let model_id = request.into_inner().model_id; + + info!("Received a find_by_model_id request for entity id {model_id}"); + + let entity_access_info_list; + + // This block controls the lifetime of the lock. + { + let lock: RwLockReadGuard>> = + self.entity_access_info_map.read(); + info!("entity_access_info_map size: {}", lock.len()); + entity_access_info_list = lock.get(&model_id).cloned(); + } + + info!("{entity_access_info_list:?}"); + + if entity_access_info_list.is_none() { + return Err(Status::not_found("Unable to find any entities with model id {model_id}")); + } + + let response = FindByModelIdResponse { entity_access_info_list: entity_access_info_list.unwrap() }; + + debug!("Responded to the find_by_model_id request."); + + Ok(Response::new(response)) + } + + /// Find by instance id implementation. + /// + /// # Arguments + /// * `request` - Find by instamce id request. + async fn find_by_instance_id( + &self, + request: Request, + ) -> Result, Status> { + let instance_id = request.into_inner().instance_id; + + info!("Received a find_by_instance_id request for instance id {instance_id}"); + + let mut new_entity_access_info_list = Vec::::new(); + + // This block controls the lifetime of the lock. + { + let lock: RwLockReadGuard>> = + self.entity_access_info_map.read(); + info!("entity_access_info_map size: {}", lock.len()); + for entity_access_info_list in lock.values() { + for entity_access_info in entity_access_info_list { + let mut instance_found: bool = false; + let mut new_endpoint_info_list: Vec:: = Vec::new(); + for endpoint_info_ in entity_access_info.endpoint_info_list.iter() { + if endpoint_info_.context == instance_id { + instance_found = true; + } + new_endpoint_info_list.push(endpoint_info_.clone()); + } + if instance_found { + let new_entity_access_info = EntityAccessInfo { + name: entity_access_info.name.clone(), + id: entity_access_info.id.clone(), + description: entity_access_info.description.clone(), + endpoint_info_list: new_endpoint_info_list, + }; + new_entity_access_info_list.push(new_entity_access_info); + } + } + } + } + + if new_entity_access_info_list.is_empty() { + return Err(Status::not_found("Unable to find any entities with instance id {instance_id}")); + } + + let response = FindByInstanceIdResponse { entity_access_info_list: new_entity_access_info_list }; + + debug!("Responded to the find_by_instance_id request."); + + Ok(Response::new(response)) + } + + /// Register implementation. + /// + /// # Arguments + /// * `request` - Publish request. + async fn register( + &self, + request: Request, + ) -> Result, Status> { + let request_inner = request.into_inner(); + + for entity_access_info in &request_inner.entity_access_info_list { + info!("Received a register request for the the entity:\n{}", entity_access_info.id); + + self.register_entity(entity_access_info.clone())?; + } + + let response = RegisterResponse {}; + + debug!("Completed the register request."); + + Ok(Response::new(response)) + } +} + +impl DigitalTwinRegistryImpl { + /// Register the entity. + /// + /// # Arguments + /// * `entity` - The entity. + fn register_entity(&self, entity_access_info: EntityAccessInfo) -> Result<(), Status> { + // This block controls the lifetime of the lock. + { + let mut lock: RwLockWriteGuard>> = + self.entity_access_info_map.write(); + let get_result = lock.get(&entity_access_info.id); + match get_result { + Some(_) => { + info!("Registered another entity access info for entity {}", &entity_access_info.id); + lock.get_mut(&entity_access_info.id).unwrap().push(entity_access_info.clone()); + } + None => { + info!("Registered entity {}", &entity_access_info.id); + lock.insert(entity_access_info.id.clone(), vec!(entity_access_info.clone())); + } + }; + } + + debug!("Completed register entity for {}", &entity_access_info.id); + + Ok(()) + } +} + +#[cfg(test)] +mod digital_twin_registry_impl_tests { + use super::*; + use core_protobuf_data_access::invehicle_digital_twin::v1::EndpointInfo; + + #[tokio::test] + async fn find_by_id_test() { + let operations = vec![String::from("Subscribe"), String::from("Unsubscribe")]; + + let endpoint_info = EndpointInfo { + protocol: String::from("grpc"), + uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 + context: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + operations, + }; + + let entity_access_info = EntityAccessInfo { + name: String::from("AmbientAirTemperature"), + id: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + description: String::from("Ambient air temperature"), + endpoint_info_list: vec![endpoint_info], + }; + + let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); + + let invehicle_digital_twin_impl = + InvehicleDigitalTwinImpl { entity_access_info_map: entity_access_info_map.clone() }; + + // This block controls the lifetime of the lock. + { + let mut lock: RwLockWriteGuard> = + entity_access_info_map.write(); + lock.insert(entity_access_info.id.clone(), entity_access_info.clone()); + } + + let request = tonic::Request::new(FindByIdRequest { + id: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + }); + let result = invehicle_digital_twin_impl.find_by_id(request).await; + assert!(result.is_ok()); + let response = result.unwrap(); + let response_inner = response.into_inner(); + + assert!(response_inner.entity_access_info.is_some()); + + let response_entity_access_info = response_inner.entity_access_info.unwrap(); + + assert_eq!( + response_entity_access_info.id, + "dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1" + ); + assert_eq!(response_entity_access_info.endpoint_info_list.len(), 1); + assert_eq!( + response_entity_access_info.endpoint_info_list[0].uri, + "http://[::1]:40010" // Devskim: ignore DS137138 + ); + } + + #[tokio::test] + async fn register_test() { + let endpoint_info = EndpointInfo { + protocol: String::from("grpc"), + uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 + context: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + operations: vec![String::from("Subscribe"), String::from("Unsubscribe")], + }; + + let entity_access_info = EntityAccessInfo { + name: String::from("AmbientAirTemperature"), + id: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + description: String::from("Ambient air temperature"), + endpoint_info_list: vec![endpoint_info], + }; + + let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); + + let invehicle_digital_twin_impl = + InvehicleDigitalTwinImpl { entity_access_info_map: entity_access_info_map.clone() }; + + let request = tonic::Request::new(RegisterRequest { + entity_access_info_list: vec![entity_access_info], + }); + let result = invehicle_digital_twin_impl.register(request).await; + assert!(result.is_ok(), "register result is not okay: {result:?}"); + + // This block controls the lifetime of the lock. + { + let lock: RwLockReadGuard> = + entity_access_info_map.read(); + // Make sure that we populated the entity map from the contents of the DTDL. + assert_eq!(lock.len(), 1, "expected length was 1, actual length is {}", lock.len()); + } + } +} diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_module.rs b/core/module/digital_twin_registry/src/digital_twin_registry_module.rs new file mode 100644 index 00000000..4b14dcb7 --- /dev/null +++ b/core/module/digital_twin_registry/src/digital_twin_registry_module.rs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use common::grpc_module::GrpcModule; +use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_server::DigitalTwinRegistryServer; + +// use log::{debug, error, info}; +use tonic::transport::server::RoutesBuilder; +// use tonic::{Request, Response, Status}; + +use crate::digital_twin_registry_impl::DigitalTwinRegistryImpl; + +/// Digital Twin Registry Module. +#[derive(Clone, Debug)] +pub struct DigitalTwinRegistryModule { + +} + +impl DigitalTwinRegistryModule { + /// Creates a new instance of the DigitalTwinRegistryModule. + pub async fn new() -> Result { + Ok(Self {}) + } +} + +impl GrpcModule for DigitalTwinRegistryModule { + /// Adds the gRPC services for this module to the server builder. + /// + /// # Arguments + /// * `builder` - A tonic::RoutesBuilder that contains the grpc services to build. + fn add_grpc_services(&self, builder: &mut RoutesBuilder) { + // Create the gRPC services. + let digital_twin_registry_service = DigitalTwinRegistryServer::new(DigitalTwinRegistryImpl::default()); + + builder.add_service(digital_twin_registry_service); + } +} \ No newline at end of file diff --git a/core/module/digital_twin_registry/src/lib.rs b/core/module/digital_twin_registry/src/lib.rs new file mode 100644 index 00000000..e8b763f5 --- /dev/null +++ b/core/module/digital_twin_registry/src/lib.rs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +pub mod digital_twin_registry_impl; +pub mod digital_twin_registry_module; diff --git a/core/protobuf_data_access/build.rs b/core/protobuf_data_access/build.rs index fa1cbc65..2e00b42f 100644 --- a/core/protobuf_data_access/build.rs +++ b/core/protobuf_data_access/build.rs @@ -18,6 +18,14 @@ fn main() -> Result<(), Box> { &["../../interfaces/module/managed_subscribe/v1/managed_subscribe.proto"], &["../../interfaces/module/managed_subscribe/v1/"], )?; + tonic_build::configure().compile( + &["../../interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto"], + &["../../interfaces/module/digital_twin_graph/v1/"], + )?; + tonic_build::configure().compile( + &["../../interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto"], + &["../../interfaces/module/digital_twin_registry/v1/"], + )?; tonic_build::configure().compile( &["../../external/chariott/service_discovery/proto/core/v1/service_registry.proto"], &["../../external/chariott/service_discovery/proto/core/v1/"], @@ -30,6 +38,14 @@ fn main() -> Result<(), Box> { &["../../external/agemo/proto/publisher/v1/publisher.proto"], &["../../external/agemo/proto/publisher/v1/"], )?; + tonic_build::configure().compile( + &["../../interfaces/async_rpc/v1/request.proto"], + &["../../interfaces/async_rpc/v1/"], + )?; + tonic_build::configure().compile( + &["../../interfaces/async_rpc/v1/respond.proto"], + &["../../interfaces/async_rpc/v1/"], + )?; Ok(()) } diff --git a/core/protobuf_data_access/src/lib.rs b/core/protobuf_data_access/src/lib.rs index f421f4d8..968d5d0a 100644 --- a/core/protobuf_data_access/src/lib.rs +++ b/core/protobuf_data_access/src/lib.rs @@ -8,12 +8,33 @@ pub mod invehicle_digital_twin { } } +pub mod async_rpc { + pub mod v1 { + pub mod respond { + tonic::include_proto!("async_rpc.v1.respond"); + } + pub mod request { + tonic::include_proto!("async_rpc.v1.request"); + } + } +} + pub mod module { pub mod managed_subscribe { pub mod v1 { tonic::include_proto!("managed_subscribe"); } } + pub mod digital_twin_graph { + pub mod v1 { + tonic::include_proto!("digital_twin_graph.v1.digital_twin_graph"); + } + } + pub mod digital_twin_registry { + pub mod v1 { + tonic::include_proto!("digital_twin_registry.v1.digital_twin_registry"); + } + } } pub mod chariott { diff --git a/digital-twin-model/dtdl/dtmi/sdv/cabin-1.json b/digital-twin-model/dtdl/dtmi/sdv/cabin-1.json index b0e223ce..d63475a7 100644 --- a/digital-twin-model/dtdl/dtmi/sdv/cabin-1.json +++ b/digital-twin-model/dtdl/dtmi/sdv/cabin-1.json @@ -6,22 +6,22 @@ "contents": [ { "@type": "Relationship", - "@id": "dtmi:sdv:cabin:has_infotainment;1", + "@id": "dtmi:sdv:cabin:infotainment;1", "target": "dtmi:sdv:infotainment;1", - "name": "has_infotainment", + "name": "infotainment", "maxMultiplicity": 1 }, { "@type": "Relationship", - "@id": "dtmi:sdv:cabin:has_hvac;1", + "@id": "dtmi:sdv:cabin:hvac;1", "target": "dtmi:sdv:hvac;1", - "name": "has_hvac", + "name": "hvac", "maxMultiplicity": 1 }, { "@type": "Relationship", - "@id": "dtmi:sdv:cabin:has_seat;1", - "name": "has_seat", + "@id": "dtmi:sdv:cabin:seat;1", + "name": "seat", "target": "dtmi:sdv:seat;1", "properties": [ { diff --git a/digital-twin-model/dtdl/dtmi/sdv/infotainment-1.json b/digital-twin-model/dtdl/dtmi/sdv/infotainment-1.json index e55ada8e..5153fc11 100644 --- a/digital-twin-model/dtdl/dtmi/sdv/infotainment-1.json +++ b/digital-twin-model/dtdl/dtmi/sdv/infotainment-1.json @@ -6,9 +6,9 @@ "contents": [ { "@type": "Relationship", - "@id": "dtmi:sdv:infotainment:has_hmi;1", + "@id": "dtmi:sdv:infotainment:hmi;1", "target": "dtmi:sdv:hmi;1", - "name": "has_hmi", + "name": "hmi", "maxMultiplicity": 1 } ] diff --git a/digital-twin-model/dtdl/dtmi/sdv/seat-1.json b/digital-twin-model/dtdl/dtmi/sdv/seat-1.json index ab973ecb..ac92f4c4 100644 --- a/digital-twin-model/dtdl/dtmi/sdv/seat-1.json +++ b/digital-twin-model/dtdl/dtmi/sdv/seat-1.json @@ -2,5 +2,14 @@ "@context": ["dtmi:dtdl:context;3"], "@type": "Interface", "@id": "dtmi:sdv:seat;1", - "description": "Seat Interface." + "description": "Seat Interface.", + "contents": [ + { + "@type": "Relationship", + "@id": "dtmi:sdv:seat:seat_massager;1", + "target": "dtmi:sdv:seatmassager;1", + "name": "seat_massager", + "maxMultiplicity": 1 + } + ] } diff --git a/digital-twin-model/dtdl/dtmi/sdv/seat_with_massager-1.json b/digital-twin-model/dtdl/dtmi/sdv/seat_with_massager-1.json deleted file mode 100644 index 37b9976e..00000000 --- a/digital-twin-model/dtdl/dtmi/sdv/seat_with_massager-1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "@context": ["dtmi:dtdl:context;3"], - "@type": "Interface", - "@id": "dtmi:sdv:seat_with_massager;1", - "description": "Seat with Massager Interface", - "extends": "dtmi:sdv:seat;1", - "contents": [ - { - "@type": "Relationship", - "@id": "dtmi:sdv:seat_with_massager:has_seat_massager;1", - "target": "dtmi:sdv:seatmassager;1", - "name": "has_seat_massager", - "maxMultiplicity": 1 - } - ] -} diff --git a/digital-twin-model/dtdl/dtmi/sdv/vehicle-1.json b/digital-twin-model/dtdl/dtmi/sdv/vehicle-1.json index 63237871..b97346c0 100644 --- a/digital-twin-model/dtdl/dtmi/sdv/vehicle-1.json +++ b/digital-twin-model/dtdl/dtmi/sdv/vehicle-1.json @@ -23,9 +23,9 @@ }, { "@type": "Relationship", - "@id": "dtmi:sdv:vehicle:has_cabin;1", + "@id": "dtmi:sdv:vehicle:cabin;1", "target": "dtmi:sdv:cabin;1", - "name": "has_cabin", + "name": "cabin", "maxMultiplicity": 1 } ] diff --git a/digital-twin-model/src/sdv_v1.rs b/digital-twin-model/src/sdv_v1.rs index 319d110b..554136dd 100644 --- a/digital-twin-model/src/sdv_v1.rs +++ b/digital-twin-model/src/sdv_v1.rs @@ -5,6 +5,8 @@ // This file contains the generated code for the Software Defined Vehicle (SDV) model. // This code is manually generated today, but in the future it should be automatically generated from the DTDL. +#![allow(non_camel_case_types)] + /// The context value for all JSON-LD generated by the code in this file. fn context() -> Vec { vec!["dtmi:dtdl:context;3".to_string(), "dtmi:sdv:context;1".to_string()] @@ -150,11 +152,90 @@ pub mod airbag_seat_massager { pub mod basic_airbag_seat_massager { pub const ID: &str = "dtmi:sdv:basic_airbag_seat_massager;1"; pub const DESCRIPTION: &str = "Basic Airbag Seat Massager Interface."; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@type")] + #[derivative(Default( + value = "crate::sdv_v1::basic_airbag_seat_massager::ID.to_string()" + ))] + pub model_id: String, + } } pub mod cabin { pub const ID: &str = "dtmi:sdv:cabin;1"; pub const DESCRIPTION: &str = "Cabin Interface."; + + pub mod infotainment { + pub const ID: &str = "dtmi:sdv:vehicle:infotainment;1"; + pub const NAME: &str = "infotainment"; + pub const DESCRIPTION: &str = "The infotainment unit."; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct RELATIONSHIP_TYPE { + #[serde(rename = "@id")] + pub instance_id: String, + } + + pub type TYPE = Vec; + } + + pub mod hvac { + pub const ID: &str = "dtmi:sdv:cabin:hvac;1"; + pub const NAME: &str = "hvac"; + pub const DESCRIPTION: &str = "The HVAC unit."; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct RELATIONSHIP_TYPE { + #[serde(rename = "@id")] + pub instance_id: String, + } + + pub type TYPE = Vec; + } + + pub mod seat { + pub const ID: &str = "dtmi:sdv:cabin:seat;1"; + pub const NAME: &str = "seat"; + pub const DESCRIPTION: &str = "The seats."; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct RELATIONSHIP_TYPE { + #[serde(rename = "@id")] + pub instance_id: String, + } + + pub type TYPE = Vec; + } + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@type")] + #[derivative(Default( + value = "crate::sdv_v1::cabin::ID.to_string()" + ))] + pub model_id: String, + pub infotainment: crate::sdv_v1::cabin::infotainment::TYPE, + pub hvac: crate::sdv_v1::cabin::hvac::TYPE, + pub seat: crate::sdv_v1::cabin::seat::TYPE, + } } #[allow(dead_code)] @@ -285,11 +366,56 @@ pub mod obd { pub mod premium_airbag_seat_massager { pub const ID: &str = "dtmi:sdv:premium_airbag_seat_massager;1"; pub const DESCRIPTION: &str = "Premium Airbag Seat Massager Interface."; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@type")] + #[derivative(Default( + value = "crate::sdv_v1::premium_airbag_seat_massager::ID.to_string()" + ))] + pub model_id: String, + } } pub mod seat { pub const ID: &str = "dtmi:sdv:seat;1"; pub const DESCRIPTION: &str = "Seat Interface."; + + pub mod seat_massager { + pub const ID: &str = "dtmi:sdv:seat:seat_massager;1"; + pub const NAME: &str = "seat_massager"; + pub const DESCRIPTION: &str = "The seat massager."; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct RELATIONSHIP_TYPE { + #[serde(rename = "@id")] + pub instance_id: String, + } + + pub type TYPE = Vec; + } + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@type")] + #[derivative(Default( + value = "crate::sdv_v1::seat::ID.to_string()" + ))] + pub model_id: String, + pub seat_massager: crate::sdv_v1::seat::seat_massager::TYPE, + } } pub mod seat_massager { @@ -520,7 +646,7 @@ pub mod seat_massager { } pub mod vehicle { - pub const ID: &str = "dtmi:sdv:vehcile;1"; + pub const ID: &str = "dtmi:sdv:vehicle;1"; pub const DESCRIPTION: &str = "Vehicle Interface."; pub mod vehicle_identification { @@ -551,4 +677,36 @@ pub mod vehicle { pub vin: crate::sdv_v1::vehicle::vehicle_identification::vin::TYPE, } } + + pub mod cabin { + pub const ID: &str = "dtmi:sdv:vehicle:cabin;1"; + pub const NAME: &str = "cabin"; + pub const DESCRIPTION: &str = "The cabin."; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct RELATIONSHIP_TYPE { + #[serde(rename = "@id")] + pub instance_id: String, + } + + pub type TYPE = Vec; + } + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@type")] + #[derivative(Default( + value = "crate::sdv_v1::vehicle::ID.to_string()" + ))] + pub model_id: String, + pub vehicle_identification: crate::sdv_v1::vehicle::vehicle_identification::TYPE, + pub cabin: crate::sdv_v1::vehicle::cabin::TYPE, + } } diff --git a/samples/interfaces/async_rpc/v1/request.proto b/interfaces/async_rpc/v1/request.proto similarity index 100% rename from samples/interfaces/async_rpc/v1/request.proto rename to interfaces/async_rpc/v1/request.proto diff --git a/samples/interfaces/async_rpc/v1/respond.proto b/interfaces/async_rpc/v1/respond.proto similarity index 100% rename from samples/interfaces/async_rpc/v1/respond.proto rename to interfaces/async_rpc/v1/respond.proto diff --git a/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto new file mode 100644 index 00000000..55e8aad4 --- /dev/null +++ b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +syntax = "proto3"; + +package digital_twin_graph.v1.digital_twin_graph; + +service DigitalTwinGraph { + rpc Find (FindRequest) returns (FindResponse); + rpc Get (GetRequest) returns (GetResponse); + rpc Set (SetRequest) returns (SetResponse); + rpc Invoke (InvokeRequest) returns (InvokeResponse); +} + +message FindRequest { + string model_id = 1; +} + +message FindResponse { + repeated string values = 1; +} + +message GetRequest { + string instance_id = 1; + string member_path = 2; +} + +message GetResponse { + string value = 1; +} + +message SetRequest { + string instance_id = 1; + string member_path = 2; + string value = 3; +} + +message SetResponse { +} + +message InvokeRequest { + string instance_id = 1; + string request_payload = 2; +} + +message InvokeResponse { + string response_payload = 1; +} \ No newline at end of file diff --git a/interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto b/interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto new file mode 100644 index 00000000..4953a39b --- /dev/null +++ b/interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +syntax = "proto3"; + +package digital_twin_registry.v1.digital_twin_registry; + +service DigitalTwinRegistry { + rpc FindByModelId (FindByModelIdRequest) returns (FindByModelIdResponse); + rpc FindByInstanceId (FindByInstanceIdRequest) returns (FindByInstanceIdResponse); + rpc Register (RegisterRequest) returns (RegisterResponse); +} + +message EndpointInfo { + string protocol = 1; + repeated string operations = 2; + string uri = 3; + string context = 4; +} + +message EntityAccessInfo { + string name = 1; + string id = 2; + string description = 3; + repeated EndpointInfo endpointInfoList = 4; +} + +message FindByModelIdRequest { + string model_id = 1; +} + +message FindByModelIdResponse { + repeated EntityAccessInfo entityAccessInfoList = 1; +} + +message FindByInstanceIdRequest { + string instance_id = 1; +} + +message FindByInstanceIdResponse { + repeated EntityAccessInfo entityAccessInfoList = 1; +} + +message RegisterRequest { + repeated EntityAccessInfo entityAccessInfoList = 1; +} + +message RegisterResponse { +} diff --git a/samples/graph/Cargo.toml b/samples/graph/Cargo.toml new file mode 100644 index 00000000..056ab8a9 --- /dev/null +++ b/samples/graph/Cargo.toml @@ -0,0 +1,40 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. +# SPDX-License-Identifier: MIT + +[package] +name = "samples-graph" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[dependencies] +async-std = { workspace = true, features = ["attributes"] } +digital_twin_graph = { path = "../../core/module/digital_twin_graph" } +digital_twin_registry = { path = "../../core/module/digital_twin_registry" } +digital-twin-model = { path = "../../digital-twin-model" } +env_logger= { workspace = true } +log = { workspace = true } +parking_lot = { workspace = true } +rand = { workspace = true } +samples-common = { path = "../common" } +samples-protobuf-data-access = { path = "../protobuf_data_access" } +serde = { workspace = true } +serde_derive = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +tokio-retry = { workspace = true } +tokio-stream = { workspace = true } +tonic = { workspace = true } +uuid = { workspace = true, features = ["v4", "fast-rng", "macro-diagnostics"] } + +[build-dependencies] +tonic-build = { workspace = true } + +[[bin]] +name = "graph-vehicle-core-provider" +path = "vehicle_core_provider/src/main.rs" + +[[bin]] +name = "graph-consumer" +path = "consumer/src/main.rs" diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs new file mode 100644 index 00000000..7a316fae --- /dev/null +++ b/samples/graph/consumer/src/main.rs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use digital_twin_model::sdv_v1 as sdv; +use env_logger::{Builder, Target}; +use log::{debug, info, LevelFilter}; +use samples_common::consumer_config; +use samples_common::utils::retrieve_invehicle_digital_twin_uri; +use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::digital_twin_graph_client::DigitalTwinGraphClient; +use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::{FindRequest, GetRequest}; +use tokio_retry::Retry; +use tokio_retry::strategy::{ExponentialBackoff, jitter}; + +/// Start the seat massage steps. +/// +/// # Arguments +/// `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. +async fn interact_with_digital_twin( + invehicle_digital_twin_uri: String +) -> Result<(), String> { + let retry_strategy = ExponentialBackoff::from_millis(100) + .map(jitter) // add jitter to delays + .take(10); // limit to 10 retries + + let client_result = Retry::spawn(retry_strategy.clone(), || async { + let client_result = DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await; + if client_result.is_err() { + return Err("Unable to connect.".to_string()); + } + Ok(client_result.unwrap()) + }).await; + + if client_result.is_err() { + return Err("Unable to connect".to_string()); + } + + let client = client_result.unwrap(); + + let request: FindRequest = FindRequest { + model_id: sdv::vehicle::ID.to_string(), + }; + + let response_result = Retry::spawn(retry_strategy.clone(), || async { + // Make a local mutable copy for use only within this cloure body. + let mut client = client.clone(); + + // Invoke find. + let response_result = client.find(request.clone()).await; + if response_result.is_err() { + return Err("Unable to call find".to_string()); + } + Ok(response_result.unwrap()) + }).await; + + if response_result.is_err() { + return Err("Unable to call find".to_string()); + } + + let response = response_result.unwrap(); + + let mut cabin_instance_id_opt = None; + + let response_inner = response.into_inner(); + for value in response_inner.values.iter() { + let vehicle: sdv::vehicle::TYPE = serde_json::from_str(value).unwrap(); + info!("The value is: {:?}", vehicle); + info!("The cabin's instance id is: {:?}", vehicle.cabin[0].instance_id); + cabin_instance_id_opt = Some(vehicle.cabin[0].instance_id.clone()); + } + + if cabin_instance_id_opt.is_none() { + return Err("Unable to find cabin instance id".to_string()); + } + + let cabin_instance_id = cabin_instance_id_opt.unwrap(); + + let get_cabin_request: GetRequest = GetRequest { + instance_id: cabin_instance_id, + member_path: "".to_string() + }; + + let get_cabin_response_result = Retry::spawn(retry_strategy, || async { + // Make a local mutable copy for use only within this cloure body. + let mut client = client.clone(); + + // Invoke find. + let get_cabin_response_result = client.get(get_cabin_request.clone()).await; + if get_cabin_response_result.is_err() { + return Err("Unable to call get".to_string()); + } + Ok(get_cabin_response_result.unwrap()) + }).await; + + if get_cabin_response_result.is_err() { + return Err("Unable to call get".to_string()); + } + + let get_cabin_response = get_cabin_response_result.unwrap(); + + let get_cabin_response_inner = get_cabin_response.into_inner(); + let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response_inner.value).unwrap(); + info!("The value is: {:?}", cabin); + for seat in cabin.seat.iter() { + info!("The seat's instance id is: {:?}", seat.instance_id); + } + + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Setup logging. + Builder::new().filter(None, LevelFilter::Info).target(Target::Stdout).init(); + + info!("The Consumer has started."); + + let settings = consumer_config::load_settings(); + + let invehicle_digital_twin_uri = retrieve_invehicle_digital_twin_uri( + settings.invehicle_digital_twin_uri, + settings.chariott_uri, + ) + .await?; + + interact_with_digital_twin(invehicle_digital_twin_uri).await?; + + debug!("The Consumer has completed."); + + Ok(()) +} diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/graph/vehicle_core_provider/src/main.rs new file mode 100644 index 00000000..2119df13 --- /dev/null +++ b/samples/graph/vehicle_core_provider/src/main.rs @@ -0,0 +1,252 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +mod request_impl; + +use digital_twin_model::sdv_v1 as sdv; +use env_logger::{Builder, Target}; +use log::{debug, info, LevelFilter}; +use parking_lot::Mutex; +use samples_common::constants::{digital_twin_operation, digital_twin_protocol}; +use samples_common::provider_config; +use samples_common::utils::{retrieve_invehicle_digital_twin_uri, retry_async_based_on_status}; +use samples_protobuf_data_access::async_rpc::v1::request::request_server::RequestServer; +use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::digital_twin_registry_client::DigitalTwinRegistryClient; +use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::{ + EndpointInfo, EntityAccessInfo, RegisterRequest, +}; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::time::Duration; +use tonic::{transport::Server, Status}; + +use crate::request_impl::{InstanceData, RequestImpl, RequestState}; + +fn create_request_state() -> RequestState { + let mut result: RequestState = RequestState { + instance_map: HashMap::new() + }; + + // Seat massager ids. + + let front_left_airbag_seat_massager_instance_id: &str = "front_left_airbag_seat_massager"; + + let front_right_airbag_seat_massager_instance_id: &str = "front_right_airbag_seat_massager"; + + let back_left_airbag_seat_massager_instance_id: &str = "back_left_airbag_seat_massager"; + + let back_center_airbag_seat_massager_instance_id: &str = "back_center_airbag_seat_massager"; + + let back_right_airbag_seat_massager_instance_id: &str = "back_right_airbag_seat_massager"; + + // Create the seats. + + let front_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); + let front_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { + seat_massager: vec! [ + sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: front_left_airbag_seat_massager_instance_id.to_string(), + }, + ], + ..Default::default() + }; + result.instance_map.insert(front_left_seat_instance_id.to_string(), InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&front_left_seat).unwrap(), + }); + + let front_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); + let front_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { + seat_massager: vec! [ + sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: front_right_airbag_seat_massager_instance_id.to_string(), + }, + ], + ..Default::default() + }; + result.instance_map.insert(front_right_seat_instance_id.to_string(), InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&front_right_seat).unwrap(), + }); + + let back_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); + let back_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { + seat_massager: vec! [ + sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: back_left_airbag_seat_massager_instance_id.to_string(), + }, + ], + ..Default::default() + }; + result.instance_map.insert(back_left_seat_instance_id.to_string(), InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_left_seat).unwrap(), + }); + + let back_center_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); + let back_center_seat: sdv::seat::TYPE = sdv::seat::TYPE { + seat_massager: vec! [ + sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: back_center_airbag_seat_massager_instance_id.to_string(), + }, + ], + ..Default::default() + }; + result.instance_map.insert(back_center_seat_instance_id.to_string(), InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_center_seat).unwrap(), + }); + + let back_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); + let back_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { + seat_massager: vec! [ + sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: back_right_airbag_seat_massager_instance_id.to_string(), + }, + ], + ..Default::default() + }; + result.instance_map.insert(back_right_seat_instance_id.to_string(), InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_right_seat).unwrap(), + }); + + // Create the cabin. + let cabin_instance_id = format!("{}", uuid::Uuid::new_v4()); + let cabin_value: sdv::cabin::TYPE = sdv::cabin::TYPE { + seat: vec! [ + sdv::cabin::seat::RELATIONSHIP_TYPE { + instance_id: front_left_seat_instance_id.to_string(), + }, + sdv::cabin::seat::RELATIONSHIP_TYPE { + instance_id: front_right_seat_instance_id.to_string(), + }, + sdv::cabin::seat::RELATIONSHIP_TYPE { + instance_id: back_left_seat_instance_id.to_string(), + }, + sdv::cabin::seat::RELATIONSHIP_TYPE { + instance_id: back_center_seat_instance_id.to_string() + }, + sdv::cabin::seat::RELATIONSHIP_TYPE { + instance_id: back_right_seat_instance_id.to_string(), + }, + ], + ..Default::default() + }; + result.instance_map.insert(cabin_instance_id.clone(), InstanceData { + model_id: sdv::cabin::ID.to_string(), + description: sdv::cabin::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&cabin_value).unwrap(), + }); + + // Create the vehicle. + let vehicle_instance_id = format!("{}", uuid::Uuid::new_v4()); + let _vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { + cabin: vec! [ + sdv::vehicle::cabin::RELATIONSHIP_TYPE { + instance_id: cabin_instance_id, + }, + ], + ..Default::default() + }; + result.instance_map.insert(vehicle_instance_id, InstanceData { + model_id: sdv::vehicle::ID.to_string(), + description: sdv::vehicle::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&_vehicle_value).unwrap(), + }); + + result +} + +/// Register the airbag seat massager's massage airbags property. +/// +/// # Arguments +/// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. +/// * `provider_uri` - The provider's URI. +/// * `instance_id` - The instance id. +async fn register_vehicle_parts( + invehicle_digital_twin_uri: &str, + provider_uri: &str, + state: Arc>, +) -> Result<(), Status> { + + let mut entity_access_info_list: Vec = Vec::new(); + + state.lock().instance_map.iter().for_each(|(instance_id, instance_data)| { + info!("Registering the instance with the instance id '{}' and the model id '{}'", instance_id, instance_data.model_id); + + let endpoint_info = EndpointInfo { + protocol: digital_twin_protocol::GRPC.to_string(), + operations: vec![ + digital_twin_operation::GET.to_string(), + ], + uri: provider_uri.to_string(), + context: instance_id.to_string(), + }; + + let entity_access_info = EntityAccessInfo { + name: String::new(), // no name, so we will use an empty name + id: instance_data.model_id.to_string(), + description: instance_data.description.to_string(), + endpoint_info_list: vec!(endpoint_info), + }; + + entity_access_info_list.push(entity_access_info); + }); + + let mut client = DigitalTwinRegistryClient::connect(invehicle_digital_twin_uri.to_string()) + .await + .map_err(|e| Status::internal(e.to_string()))?; + let request = + tonic::Request::new(RegisterRequest { entity_access_info_list }); + let _response = client.register(request).await?; + + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Setup logging. + Builder::new().filter(None, LevelFilter::Info).target(Target::Stdout).init(); + + info!("The Vehicle Provider has started."); + + let settings = provider_config::load_settings(); + + let provider_authority = settings.provider_authority; + + let invehicle_digital_twin_uri = retrieve_invehicle_digital_twin_uri( + settings.invehicle_digital_twin_uri, + settings.chariott_uri, + ) + .await?; + + // Construct the provider URI from the provider authority. + let provider_uri = format!("http://{provider_authority}"); // Devskim: ignore DS137138 + + // Setup the HTTP server. + let addr: SocketAddr = provider_authority.parse()?; + let state = Arc::new(Mutex::new(create_request_state())); + let request_impl = RequestImpl { state: state.clone() }; + let server_future = Server::builder().add_service(RequestServer::new(request_impl)).serve(addr); + info!("The HTTP server is listening on address '{provider_authority}'"); + + info!("Sending a register request to the In-Vehicle Digital Twin Service URI {invehicle_digital_twin_uri}"); + retry_async_based_on_status(30, Duration::from_secs(1), || { + register_vehicle_parts(&invehicle_digital_twin_uri, &provider_uri, state.clone()) + }) + .await?; + + server_future.await?; + + debug!("The Vehicle Provider has completed."); + + Ok(()) +} diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs new file mode 100644 index 00000000..8690d9d7 --- /dev/null +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use digital_twin_graph::TargetedPayload; +// use digital_twin_model::sdv_v1 as sdv; +use log::{debug, error, info, warn}; +use parking_lot::{Mutex, MutexGuard}; +use samples_common::constants::digital_twin_operation; +use samples_protobuf_data_access::async_rpc::v1::request::{ + request_server::Request, AskRequest, AskResponse, NotifyRequest, NotifyResponse, +}; +use samples_protobuf_data_access::async_rpc::v1::respond::{ + respond_client::RespondClient, AnswerRequest, +}; +// use seat_massager_common::{status, TargetedPayload}; +use std::collections::HashMap; +use std::sync::Arc; + +#[derive(Clone, Debug, Default)] +pub struct InstanceData { + pub model_id: String, + pub description: String, + pub serialized_value: String, +} + +#[derive(Debug, Default)] +pub struct RequestState { + pub instance_map: HashMap, +} + +#[derive(Debug, Default)] +pub struct RequestImpl { + pub state: Arc>, +} + +#[tonic::async_trait] +impl Request for RequestImpl { + /// Ask implementation. + /// + /// # Arguments + /// * `request` - Ask request. + async fn ask( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let request_inner = request.into_inner(); + let respond_uri: String = request_inner.respond_uri.clone(); + let ask_id: String = request_inner.ask_id.clone(); + let payload: String = request_inner.payload.clone(); + + info!("Received an ask request"); + info!(" respond_uri: {respond_uri}"); + info!(" ask_id: {ask_id}"); + + // Deserialize the targeted payload. + let targeted_payload_json: TargetedPayload = serde_json::from_str(&payload).unwrap(); + + info!(" instance_id: {}", targeted_payload_json.instance_id); + info!(" member_path: {}", targeted_payload_json.member_path); + info!(" operation: {}", targeted_payload_json.operation); + + // Extract the type_id from the request payload. + // let type_id_json: serde_json::Value = request_payload_json.get("@type").unwrap().clone(); + // let type_id: String = serde_json::from_value(type_id_json.clone()).unwrap(); + + // Check to make sure that the type_id is for a perform_request request. + if targeted_payload_json.operation != digital_twin_operation::GET { + return Err(tonic::Status::invalid_argument(format!("Unexpected operation '{}'", targeted_payload_json.operation))); + } + + if !targeted_payload_json.payload.is_empty() { + return Err(tonic::Status::invalid_argument(format!("Unexpected payload, it should be empty, not '{}'", targeted_payload_json.payload))); + } + + let state: Arc> = self.state.clone(); + + // Asynchronously perform the step. + tokio::spawn(async move { + let instance_data: InstanceData = { + let lock: MutexGuard = state.lock(); + match lock.instance_map.get(&targeted_payload_json.instance_id) { + Some(instance_data) => instance_data.clone(), + None => { + error!("Instance not found for instance id '{}'", targeted_payload_json.instance_id); + return; + } + } + }; + + let response_payload_json = instance_data.serialized_value.clone(); + + let client_result = RespondClient::connect(respond_uri).await; + if let Err(error_message) = client_result { + error!("Unable to connect due to {error_message}"); + return; + } + let mut client = client_result.unwrap(); + + // Serilaize the response payload. + // let response_payload_json: String = + // serde_json::to_string_pretty(&response_payload).unwrap(); + + let answer_request = + tonic::Request::new(AnswerRequest { ask_id, payload: response_payload_json }); + + // Send the answer. + let response = client.answer(answer_request).await; + if let Err(status) = response { + error!("Answer failed: {status:?}"); + } + }); + + debug!("Completed the ask request."); + + Ok(tonic::Response::new(AskResponse {})) + } + + /// Notify implementation. + /// + /// # Arguments + /// * `request` - Notify request. + async fn notify( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + warn!("Got a notify request: {request:?}"); + + Err(tonic::Status::unimplemented("notify has not been implemented")) + } +} diff --git a/samples/protobuf_data_access/build.rs b/samples/protobuf_data_access/build.rs index 6d5cafbe..ec5a93e7 100644 --- a/samples/protobuf_data_access/build.rs +++ b/samples/protobuf_data_access/build.rs @@ -5,8 +5,14 @@ fn main() -> Result<(), Box> { tonic_build::compile_protos("../interfaces/sample_grpc/v1/digital_twin_consumer.proto")?; tonic_build::compile_protos("../interfaces/sample_grpc/v1/digital_twin_provider.proto")?; - tonic_build::compile_protos("../interfaces/async_rpc/v1/respond.proto")?; - tonic_build::compile_protos("../interfaces/async_rpc/v1/request.proto")?; + tonic_build::configure().compile( + &["../../interfaces/async_rpc/v1/respond.proto"], + &["../../interfaces/async_rpc/v1/"], + )?; + tonic_build::configure().compile( + &["../../interfaces/async_rpc/v1/request.proto"], + &["../../interfaces/async_rpc/v1/"], + )?; tonic_build::configure() .message_attribute("EndpointInfo", "#[derive(serde::Deserialize, serde::Serialize)]") .message_attribute("EntityAccessInfo", "#[derive(serde::Deserialize, serde::Serialize)]") @@ -22,6 +28,14 @@ fn main() -> Result<(), Box> { &["../../interfaces/module/managed_subscribe/v1/managed_subscribe.proto"], &["../../interfaces/module/managed_subscribe/v1/"], )?; + tonic_build::configure().compile( + &["../../interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto"], + &["../../interfaces/module/digital_twin_graph/v1/"], + )?; + tonic_build::configure().compile( + &["../../interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto"], + &["../../interfaces/module/digital_twin_registry/v1/"], + )?; tonic_build::configure().compile( &["../../external/chariott/service_discovery/proto/core/v1/service_registry.proto"], &["../../external/chariott/service_discovery/proto/core/v1/"], diff --git a/samples/protobuf_data_access/src/lib.rs b/samples/protobuf_data_access/src/lib.rs index c61eb076..70487034 100644 --- a/samples/protobuf_data_access/src/lib.rs +++ b/samples/protobuf_data_access/src/lib.rs @@ -8,6 +8,22 @@ pub mod invehicle_digital_twin { } } +pub mod digital_twin_graph { + pub mod v1 { + pub mod digital_twin_graph { + tonic::include_proto!("digital_twin_graph.v1.digital_twin_graph"); + } + } +} + +pub mod digital_twin_registry { + pub mod v1 { + pub mod digital_twin_registry { + tonic::include_proto!("digital_twin_registry.v1.digital_twin_registry"); + } + } +} + pub mod module { pub mod managed_subscribe { pub mod v1 { diff --git a/samples/seat_massager/provider/src/main.rs b/samples/seat_massager/provider/src/main.rs index 2caf99f1..66937e0f 100644 --- a/samples/seat_massager/provider/src/main.rs +++ b/samples/seat_massager/provider/src/main.rs @@ -81,7 +81,7 @@ async fn main() -> Result<(), Box> { // Construct the provider URI from the provider authority. let provider_uri = format!("http://{provider_authority}"); // Devskim: ignore DS137138 - let instance_id = format!("pub_{}", uuid::Uuid::new_v4()); + let instance_id = format!("{}", uuid::Uuid::new_v4()); // Setup the HTTP server. let addr: SocketAddr = provider_authority.parse()?; diff --git a/samples/seat_massager/provider/src/request_impl.rs b/samples/seat_massager/provider/src/request_impl.rs index f918376e..b3c0b43c 100644 --- a/samples/seat_massager/provider/src/request_impl.rs +++ b/samples/seat_massager/provider/src/request_impl.rs @@ -47,6 +47,7 @@ impl Request for RequestImpl { info!(" instance_id: {}", targeted_payload_json.instance_id); info!(" member_path: {}", targeted_payload_json.member_path); info!(" operation: {}", targeted_payload_json.operation); + info!(" payload: {}", targeted_payload_json.payload); // Deserialize the request payload. let request_payload_json: serde_json::Value = From 2f2a7488ca8c7887976a53cd4681e43ab9556d2c Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:48:36 -0700 Subject: [PATCH 002/109] Improved seat massager sample --- Cargo.toml | 2 +- core/invehicle-digital-twin/src/main.rs | 9 +- .../src/digital_twin_graph_impl.rs | 63 ++++--- .../src/digital_twin_graph_module.rs | 15 +- core/module/digital_twin_graph/src/lib.rs | 4 +- .../digital_twin_graph/src/respond_impl.rs | 2 +- .../src/digital_twin_registry_impl.rs | 30 ++-- .../src/digital_twin_registry_module.rs | 9 +- core/protobuf_data_access/build.rs | 16 +- core/protobuf_data_access/src/lib.rs | 2 +- digital-twin-model/src/sdv_v1.rs | 62 +++---- .../v1/digital_twin_graph.proto | 2 +- samples/graph/consumer/src/main.rs | 28 ++- .../graph/vehicle_core_provider/src/main.rs | 168 +++++++++--------- .../vehicle_core_provider/src/request_impl.rs | 15 +- samples/protobuf_data_access/build.rs | 4 +- 16 files changed, 224 insertions(+), 207 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2e3bc3bf..12cfeb2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ # extension "core/module/managed_subscribe", "core/module/digital_twin_graph", - "core/module/digital_twin_registry", + "core/module/digital_twin_registry", # DTDL tools "dtdl-tools", diff --git a/core/invehicle-digital-twin/src/main.rs b/core/invehicle-digital-twin/src/main.rs index f59c33f0..2c7f9a58 100644 --- a/core/invehicle-digital-twin/src/main.rs +++ b/core/invehicle-digital-twin/src/main.rs @@ -154,10 +154,11 @@ where // (1) Adds the Managed Subscribe module to the service. let mut server = { // (2) Initialize the Digital Twin Registry module, which implements GrpcModule. - let digital_twin_registry_module = DigitalTwinRegistryModule::new().await.map_err(|error| { - error!("Unable to create Digital Twin Registry module."); - error - })?; + let digital_twin_registry_module = + DigitalTwinRegistryModule::new().await.map_err(|error| { + error!("Unable to create Digital Twin Registry module."); + error + })?; info!("Initialized Digital Twin Registry module."); diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 50dc0c9d..f067b1ca 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -2,14 +2,19 @@ // Licensed under the MIT license. // SPDX-License-Identifier: MIT -use log::{debug, info, warn}; -use core_protobuf_data_access::async_rpc::v1::request::{request_client::RequestClient, AskRequest}; +use core_protobuf_data_access::async_rpc::v1::request::{ + request_client::RequestClient, AskRequest, +}; use core_protobuf_data_access::async_rpc::v1::respond::AnswerRequest; -use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_client::DigitalTwinRegistryClient; -use core_protobuf_data_access::module::digital_twin_registry::v1::{EndpointInfo, FindByModelIdRequest, FindByInstanceIdRequest}; use core_protobuf_data_access::module::digital_twin_graph::v1::{ - digital_twin_graph_server::DigitalTwinGraph, FindRequest, FindResponse, GetRequest, GetResponse, SetRequest, SetResponse, InvokeRequest, InvokeResponse, + digital_twin_graph_server::DigitalTwinGraph, FindRequest, FindResponse, GetRequest, + GetResponse, InvokeRequest, InvokeResponse, SetRequest, SetResponse, }; +use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_client::DigitalTwinRegistryClient; +use core_protobuf_data_access::module::digital_twin_registry::v1::{ + EndpointInfo, FindByInstanceIdRequest, FindByModelIdRequest, +}; +use log::{debug, info, warn}; use std::sync::Arc; use tokio::sync::broadcast; use tokio::time::{sleep, timeout, Duration}; @@ -19,7 +24,7 @@ use crate::{digital_twin_operation, digital_twin_protocol, TargetedPayload}; #[derive(Debug)] pub struct DigitalTwinGraphImpl { - invehicle_digital_twin_uri: String, + invehicle_digital_twin_uri: String, respond_uri: String, tx: Arc>, } @@ -32,14 +37,14 @@ impl DigitalTwinGraphImpl { /// * `respond_uri` - The uri for the respond service. /// * `tx` - The sender for the asynchronous channel for AnswerRequest's. pub fn new( - invehicle_digital_twin_uri: &str, + invehicle_digital_twin_uri: &str, respond_uri: &str, tx: Arc>, ) -> DigitalTwinGraphImpl { DigitalTwinGraphImpl { - invehicle_digital_twin_uri: invehicle_digital_twin_uri.to_string(), + invehicle_digital_twin_uri: invehicle_digital_twin_uri.to_string(), respond_uri: respond_uri.to_string(), - tx: tx, + tx, } } } @@ -67,7 +72,7 @@ pub async fn discover_digital_twin_providers_with_model_id( model_id: &str, protocol: &str, operations: &[String], -) -> Result, String> { +) -> Result, String> { info!("Sending a find_by_model_id request for model id {model_id} to the Digital Twin Registry Service at {digitial_twin_registry_service_uri}"); let mut client = @@ -80,20 +85,17 @@ pub async fn discover_digital_twin_providers_with_model_id( debug!("Received the response for the find_by_model_id request"); info!("response_payload: {:?}", response_inner.entity_access_info_list); - - // Ok(response_inner.entity_access_info_list.iter().map(|entity_access_info| entity_access_info.endpoint_info_list.clone()).flatten().collect()) - - Ok(response_inner.entity_access_info_list.iter() + Ok(response_inner + .entity_access_info_list + .iter() .map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) .flatten() .filter(|endpoint_info| { - endpoint_info.protocol == protocol - && is_subset(operations, &endpoint_info.operations) + endpoint_info.protocol == protocol && is_subset(operations, &endpoint_info.operations) }) .collect()) } - /// Use Ibeji to discover the endpoints for digital twin providers that satisfy the requirements. /// /// # Arguments @@ -106,25 +108,26 @@ pub async fn discover_digital_twin_providers_with_instance_id( instance_id: &str, protocol: &str, operations: &[String], -) -> Result, String> { +) -> Result, String> { info!("Sending a find_by_instance_id request for instance id {instance_id} to the Digital Twin Registry Service at {digitial_twin_registry_service_uri}"); let mut client = DigitalTwinRegistryClient::connect(digitial_twin_registry_service_uri.to_string()) .await .map_err(|error| format!("{error}"))?; - let request = tonic::Request::new(FindByInstanceIdRequest { instance_id: instance_id.to_string() }); + let request = + tonic::Request::new(FindByInstanceIdRequest { instance_id: instance_id.to_string() }); let response = client.find_by_instance_id(request).await.map_err(|error| error.to_string())?; let response_inner = response.into_inner(); debug!("Received the response for the find_by_instance_id request"); info!("response_payload: {:?}", response_inner.entity_access_info_list); - Ok(response_inner.entity_access_info_list.iter() - .map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) - .flatten() + Ok(response_inner + .entity_access_info_list + .iter() + .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) .filter(|endpoint_info| { - endpoint_info.protocol == protocol - && is_subset(operations, &endpoint_info.operations) + endpoint_info.protocol == protocol && is_subset(operations, &endpoint_info.operations) }) .collect()) } @@ -151,7 +154,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { digital_twin_protocol::GRPC, &[digital_twin_operation::GET.to_string()], ) - .await.map_err(|error| tonic::Status::internal(error))?; + .await + .map_err(tonic::Status::internal)?; info!(">> Found the provider endpoint info list: {provider_endpoint_info_list:?}"); @@ -261,7 +265,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { digital_twin_protocol::GRPC, &[digital_twin_operation::GET.to_string()], ) - .await.map_err(|error| tonic::Status::internal(error))?; + .await + .map_err(tonic::Status::internal)?; info!(">> Found the provider endpoint info list: {provider_endpoint_info_list:?}"); @@ -293,7 +298,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Serialize the targeted payload. let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); - + let request = tonic::Request::new(AskRequest { respond_uri: self.respond_uri.clone(), ask_id: ask_id.clone(), @@ -352,11 +357,11 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { return Err(tonic::Status::not_found("No values found")); } - Ok(tonic::Response::new(GetResponse { value: values[0].clone()})) + Ok(tonic::Response::new(GetResponse { value: values[0].clone() })) } /// Set implementation. - /// + /// /// # Arguments /// * `request` - Set request. async fn set( diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs index 543e9f58..015679a5 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs @@ -3,21 +3,19 @@ // SPDX-License-Identifier: MIT use common::grpc_module::GrpcModule; -use core_protobuf_data_access::module::digital_twin_graph::v1::digital_twin_graph_server::DigitalTwinGraphServer; use core_protobuf_data_access::async_rpc::v1::respond::respond_server::RespondServer; +use core_protobuf_data_access::module::digital_twin_graph::v1::digital_twin_graph_server::DigitalTwinGraphServer; // use log::{debug, error, info}; use std::sync::Arc; -use tonic::transport::server::RoutesBuilder; use tokio::sync::broadcast; +use tonic::transport::server::RoutesBuilder; use crate::digital_twin_graph_impl::DigitalTwinGraphImpl; use crate::respond_impl::RespondImpl; /// Digital Twin Graph Module. #[derive(Clone, Debug)] -pub struct DigitalTwinGraphModule { - -} +pub struct DigitalTwinGraphModule {} impl DigitalTwinGraphModule { /// Creates a new instance of the DigitalTwinGraphModule. @@ -34,7 +32,7 @@ impl GrpcModule for DigitalTwinGraphModule { fn add_grpc_services(&self, builder: &mut RoutesBuilder) { // TODO: Setup config let invehicle_digital_twin_authority = "0.0.0.0:5010"; - let invehicle_digital_twin_uri = format!("http://{invehicle_digital_twin_authority}"); // Devskim: ignore DS137138 + let invehicle_digital_twin_uri = format!("http://{invehicle_digital_twin_authority}"); // Devskim: ignore DS137138 let respond_authority = "0.0.0.0:5010"; let respond_uri = format!("http://{respond_authority}"); // Devskim: ignore DS137138 @@ -46,10 +44,11 @@ impl GrpcModule for DigitalTwinGraphModule { let respond_service = RespondServer::new(respond_impl); // Setup the digital twin graph service. - let digital_twin_graph_impl = DigitalTwinGraphImpl::new(&invehicle_digital_twin_uri, &respond_uri, tx); + let digital_twin_graph_impl = + DigitalTwinGraphImpl::new(&invehicle_digital_twin_uri, &respond_uri, tx); let digital_twin_graph_service = DigitalTwinGraphServer::new(digital_twin_graph_impl); builder.add_service(digital_twin_graph_service); builder.add_service(respond_service); } -} \ No newline at end of file +} diff --git a/core/module/digital_twin_graph/src/lib.rs b/core/module/digital_twin_graph/src/lib.rs index 9401b448..030da7df 100644 --- a/core/module/digital_twin_graph/src/lib.rs +++ b/core/module/digital_twin_graph/src/lib.rs @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MIT pub mod digital_twin_graph_impl; -pub mod respond_impl; pub mod digital_twin_graph_module; +pub mod respond_impl; use serde_derive::{Deserialize, Serialize}; @@ -45,4 +45,4 @@ pub mod digital_twin_operation { pub mod digital_twin_protocol { pub const GRPC: &str = "grpc"; pub const MQTT: &str = "mqtt"; -} \ No newline at end of file +} diff --git a/core/module/digital_twin_graph/src/respond_impl.rs b/core/module/digital_twin_graph/src/respond_impl.rs index f8c1703c..169bf9a5 100644 --- a/core/module/digital_twin_graph/src/respond_impl.rs +++ b/core/module/digital_twin_graph/src/respond_impl.rs @@ -2,9 +2,9 @@ // Licensed under the MIT license. // SPDX-License-Identifier: MIT -use log::{debug, info}; use core_protobuf_data_access::async_rpc::v1::respond::respond_server::Respond; use core_protobuf_data_access::async_rpc::v1::respond::{AnswerRequest, AnswerResponse}; +use log::{debug, info}; use std::sync::Arc; use tokio::sync::broadcast; diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs index 891249d5..83e4d869 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -6,7 +6,8 @@ extern crate iref; use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_server::DigitalTwinRegistry; use core_protobuf_data_access::module::digital_twin_registry::v1::{ - EntityAccessInfo, EndpointInfo, FindByModelIdRequest, FindByModelIdResponse, FindByInstanceIdRequest, FindByInstanceIdResponse, RegisterRequest, RegisterResponse, + EndpointInfo, EntityAccessInfo, FindByInstanceIdRequest, FindByInstanceIdResponse, + FindByModelIdRequest, FindByModelIdResponse, RegisterRequest, RegisterResponse, }; use log::{debug, info}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -17,7 +18,7 @@ use tonic::{Request, Response, Status}; #[derive(Debug, Default)] pub struct DigitalTwinRegistryImpl { - pub entity_access_info_map: Arc>>>, + pub entity_access_info_map: Arc>>>, } #[tonic::async_trait] @@ -38,7 +39,7 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { // This block controls the lifetime of the lock. { - let lock: RwLockReadGuard>> = + let lock: RwLockReadGuard>> = self.entity_access_info_map.read(); info!("entity_access_info_map size: {}", lock.len()); entity_access_info_list = lock.get(&model_id).cloned(); @@ -50,7 +51,8 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { return Err(Status::not_found("Unable to find any entities with model id {model_id}")); } - let response = FindByModelIdResponse { entity_access_info_list: entity_access_info_list.unwrap() }; + let response = + FindByModelIdResponse { entity_access_info_list: entity_access_info_list.unwrap() }; debug!("Responded to the find_by_model_id request."); @@ -73,13 +75,13 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { // This block controls the lifetime of the lock. { - let lock: RwLockReadGuard>> = + let lock: RwLockReadGuard>> = self.entity_access_info_map.read(); info!("entity_access_info_map size: {}", lock.len()); for entity_access_info_list in lock.values() { for entity_access_info in entity_access_info_list { let mut instance_found: bool = false; - let mut new_endpoint_info_list: Vec:: = Vec::new(); + let mut new_endpoint_info_list: Vec = Vec::new(); for endpoint_info_ in entity_access_info.endpoint_info_list.iter() { if endpoint_info_.context == instance_id { instance_found = true; @@ -100,10 +102,13 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { } if new_entity_access_info_list.is_empty() { - return Err(Status::not_found("Unable to find any entities with instance id {instance_id}")); + return Err(Status::not_found( + "Unable to find any entities with instance id {instance_id}", + )); } - let response = FindByInstanceIdResponse { entity_access_info_list: new_entity_access_info_list }; + let response = + FindByInstanceIdResponse { entity_access_info_list: new_entity_access_info_list }; debug!("Responded to the find_by_instance_id request."); @@ -142,17 +147,20 @@ impl DigitalTwinRegistryImpl { fn register_entity(&self, entity_access_info: EntityAccessInfo) -> Result<(), Status> { // This block controls the lifetime of the lock. { - let mut lock: RwLockWriteGuard>> = + let mut lock: RwLockWriteGuard>> = self.entity_access_info_map.write(); let get_result = lock.get(&entity_access_info.id); match get_result { Some(_) => { - info!("Registered another entity access info for entity {}", &entity_access_info.id); + info!( + "Registered another entity access info for entity {}", + &entity_access_info.id + ); lock.get_mut(&entity_access_info.id).unwrap().push(entity_access_info.clone()); } None => { info!("Registered entity {}", &entity_access_info.id); - lock.insert(entity_access_info.id.clone(), vec!(entity_access_info.clone())); + lock.insert(entity_access_info.id.clone(), vec![entity_access_info.clone()]); } }; } diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_module.rs b/core/module/digital_twin_registry/src/digital_twin_registry_module.rs index 4b14dcb7..a3229c13 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_module.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_module.rs @@ -13,9 +13,7 @@ use crate::digital_twin_registry_impl::DigitalTwinRegistryImpl; /// Digital Twin Registry Module. #[derive(Clone, Debug)] -pub struct DigitalTwinRegistryModule { - -} +pub struct DigitalTwinRegistryModule {} impl DigitalTwinRegistryModule { /// Creates a new instance of the DigitalTwinRegistryModule. @@ -31,8 +29,9 @@ impl GrpcModule for DigitalTwinRegistryModule { /// * `builder` - A tonic::RoutesBuilder that contains the grpc services to build. fn add_grpc_services(&self, builder: &mut RoutesBuilder) { // Create the gRPC services. - let digital_twin_registry_service = DigitalTwinRegistryServer::new(DigitalTwinRegistryImpl::default()); + let digital_twin_registry_service = + DigitalTwinRegistryServer::new(DigitalTwinRegistryImpl::default()); builder.add_service(digital_twin_registry_service); } -} \ No newline at end of file +} diff --git a/core/protobuf_data_access/build.rs b/core/protobuf_data_access/build.rs index 2e00b42f..ed0acb4a 100644 --- a/core/protobuf_data_access/build.rs +++ b/core/protobuf_data_access/build.rs @@ -19,13 +19,13 @@ fn main() -> Result<(), Box> { &["../../interfaces/module/managed_subscribe/v1/"], )?; tonic_build::configure().compile( - &["../../interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto"], - &["../../interfaces/module/digital_twin_graph/v1/"], - )?; + &["../../interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto"], + &["../../interfaces/module/digital_twin_graph/v1/"], + )?; tonic_build::configure().compile( - &["../../interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto"], - &["../../interfaces/module/digital_twin_registry/v1/"], - )?; + &["../../interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto"], + &["../../interfaces/module/digital_twin_registry/v1/"], + )?; tonic_build::configure().compile( &["../../external/chariott/service_discovery/proto/core/v1/service_registry.proto"], &["../../external/chariott/service_discovery/proto/core/v1/"], @@ -41,11 +41,11 @@ fn main() -> Result<(), Box> { tonic_build::configure().compile( &["../../interfaces/async_rpc/v1/request.proto"], &["../../interfaces/async_rpc/v1/"], - )?; + )?; tonic_build::configure().compile( &["../../interfaces/async_rpc/v1/respond.proto"], &["../../interfaces/async_rpc/v1/"], - )?; + )?; Ok(()) } diff --git a/core/protobuf_data_access/src/lib.rs b/core/protobuf_data_access/src/lib.rs index 968d5d0a..0b9e4def 100644 --- a/core/protobuf_data_access/src/lib.rs +++ b/core/protobuf_data_access/src/lib.rs @@ -34,7 +34,7 @@ pub mod module { pub mod v1 { tonic::include_proto!("digital_twin_registry.v1.digital_twin_registry"); } - } + } } pub mod chariott { diff --git a/digital-twin-model/src/sdv_v1.rs b/digital-twin-model/src/sdv_v1.rs index 554136dd..15d37772 100644 --- a/digital-twin-model/src/sdv_v1.rs +++ b/digital-twin-model/src/sdv_v1.rs @@ -155,7 +155,7 @@ pub mod basic_airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] @@ -165,7 +165,7 @@ pub mod basic_airbag_seat_massager { value = "crate::sdv_v1::basic_airbag_seat_massager::ID.to_string()" ))] pub model_id: String, - } + } } pub mod cabin { @@ -179,13 +179,13 @@ pub mod cabin { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct RELATIONSHIP_TYPE { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; + + pub type TYPE = Vec; } pub mod hvac { @@ -195,13 +195,13 @@ pub mod cabin { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct RELATIONSHIP_TYPE { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; + + pub type TYPE = Vec; } pub mod seat { @@ -211,30 +211,28 @@ pub mod cabin { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct RELATIONSHIP_TYPE { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; + + pub type TYPE = Vec; } #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, #[serde(rename = "@type")] - #[derivative(Default( - value = "crate::sdv_v1::cabin::ID.to_string()" - ))] + #[derivative(Default(value = "crate::sdv_v1::cabin::ID.to_string()"))] pub model_id: String, pub infotainment: crate::sdv_v1::cabin::infotainment::TYPE, pub hvac: crate::sdv_v1::cabin::hvac::TYPE, - pub seat: crate::sdv_v1::cabin::seat::TYPE, + pub seat: crate::sdv_v1::cabin::seat::TYPE, } } @@ -369,7 +367,7 @@ pub mod premium_airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] @@ -379,7 +377,7 @@ pub mod premium_airbag_seat_massager { value = "crate::sdv_v1::premium_airbag_seat_massager::ID.to_string()" ))] pub model_id: String, - } + } } pub mod seat { @@ -393,29 +391,27 @@ pub mod seat { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct RELATIONSHIP_TYPE { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; + + pub type TYPE = Vec; } #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, #[serde(rename = "@type")] - #[derivative(Default( - value = "crate::sdv_v1::seat::ID.to_string()" - ))] + #[derivative(Default(value = "crate::sdv_v1::seat::ID.to_string()"))] pub model_id: String, pub seat_massager: crate::sdv_v1::seat::seat_massager::TYPE, - } + } } pub mod seat_massager { @@ -685,28 +681,26 @@ pub mod vehicle { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct RELATIONSHIP_TYPE { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; + + pub type TYPE = Vec; } #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, #[serde(rename = "@type")] - #[derivative(Default( - value = "crate::sdv_v1::vehicle::ID.to_string()" - ))] + #[derivative(Default(value = "crate::sdv_v1::vehicle::ID.to_string()"))] pub model_id: String, - pub vehicle_identification: crate::sdv_v1::vehicle::vehicle_identification::TYPE, + pub vehicle_identification: crate::sdv_v1::vehicle::vehicle_identification::TYPE, pub cabin: crate::sdv_v1::vehicle::cabin::TYPE, } } diff --git a/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto index 55e8aad4..65784083 100644 --- a/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto +++ b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto @@ -10,7 +10,7 @@ service DigitalTwinGraph { rpc Find (FindRequest) returns (FindResponse); rpc Get (GetRequest) returns (GetResponse); rpc Set (SetRequest) returns (SetResponse); - rpc Invoke (InvokeRequest) returns (InvokeResponse); + rpc Invoke (InvokeRequest) returns (InvokeResponse); } message FindRequest { diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 7a316fae..9ecd0943 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -16,20 +16,20 @@ use tokio_retry::strategy::{ExponentialBackoff, jitter}; /// /// # Arguments /// `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. -async fn interact_with_digital_twin( - invehicle_digital_twin_uri: String -) -> Result<(), String> { +async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Result<(), String> { let retry_strategy = ExponentialBackoff::from_millis(100) .map(jitter) // add jitter to delays - .take(10); // limit to 10 retries + .take(10); // limit to 10 retries let client_result = Retry::spawn(retry_strategy.clone(), || async { - let client_result = DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await; + let client_result = + DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await; if client_result.is_err() { return Err("Unable to connect.".to_string()); } Ok(client_result.unwrap()) - }).await; + }) + .await; if client_result.is_err() { return Err("Unable to connect".to_string()); @@ -37,9 +37,7 @@ async fn interact_with_digital_twin( let client = client_result.unwrap(); - let request: FindRequest = FindRequest { - model_id: sdv::vehicle::ID.to_string(), - }; + let request: FindRequest = FindRequest { model_id: sdv::vehicle::ID.to_string() }; let response_result = Retry::spawn(retry_strategy.clone(), || async { // Make a local mutable copy for use only within this cloure body. @@ -51,7 +49,8 @@ async fn interact_with_digital_twin( return Err("Unable to call find".to_string()); } Ok(response_result.unwrap()) - }).await; + }) + .await; if response_result.is_err() { return Err("Unable to call find".to_string()); @@ -75,10 +74,8 @@ async fn interact_with_digital_twin( let cabin_instance_id = cabin_instance_id_opt.unwrap(); - let get_cabin_request: GetRequest = GetRequest { - instance_id: cabin_instance_id, - member_path: "".to_string() - }; + let get_cabin_request: GetRequest = + GetRequest { instance_id: cabin_instance_id, member_path: "".to_string() }; let get_cabin_response_result = Retry::spawn(retry_strategy, || async { // Make a local mutable copy for use only within this cloure body. @@ -90,7 +87,8 @@ async fn interact_with_digital_twin( return Err("Unable to call get".to_string()); } Ok(get_cabin_response_result.unwrap()) - }).await; + }) + .await; if get_cabin_response_result.is_err() { return Err("Unable to call get".to_string()); diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/graph/vehicle_core_provider/src/main.rs index 2119df13..a3a3840f 100644 --- a/samples/graph/vehicle_core_provider/src/main.rs +++ b/samples/graph/vehicle_core_provider/src/main.rs @@ -25,15 +25,13 @@ use tonic::{transport::Server, Status}; use crate::request_impl::{InstanceData, RequestImpl, RequestState}; fn create_request_state() -> RequestState { - let mut result: RequestState = RequestState { - instance_map: HashMap::new() - }; + let mut result: RequestState = RequestState { instance_map: HashMap::new() }; // Seat massager ids. let front_left_airbag_seat_massager_instance_id: &str = "front_left_airbag_seat_massager"; - let front_right_airbag_seat_massager_instance_id: &str = "front_right_airbag_seat_massager"; + let front_right_airbag_seat_massager_instance_id: &str = "front_right_airbag_seat_massager"; let back_left_airbag_seat_massager_instance_id: &str = "back_left_airbag_seat_massager"; @@ -45,83 +43,88 @@ fn create_request_state() -> RequestState { let front_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let front_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { - seat_massager: vec! [ - sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: front_left_airbag_seat_massager_instance_id.to_string(), - }, - ], + seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: front_left_airbag_seat_massager_instance_id.to_string(), + }], ..Default::default() }; - result.instance_map.insert(front_left_seat_instance_id.to_string(), InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&front_left_seat).unwrap(), - }); + result.instance_map.insert( + front_left_seat_instance_id.to_string(), + InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&front_left_seat).unwrap(), + }, + ); let front_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let front_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { - seat_massager: vec! [ - sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: front_right_airbag_seat_massager_instance_id.to_string(), - }, - ], + seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: front_right_airbag_seat_massager_instance_id.to_string(), + }], ..Default::default() }; - result.instance_map.insert(front_right_seat_instance_id.to_string(), InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&front_right_seat).unwrap(), - }); + result.instance_map.insert( + front_right_seat_instance_id.to_string(), + InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&front_right_seat).unwrap(), + }, + ); let back_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { - seat_massager: vec! [ - sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: back_left_airbag_seat_massager_instance_id.to_string(), - }, - ], + seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: back_left_airbag_seat_massager_instance_id.to_string(), + }], ..Default::default() }; - result.instance_map.insert(back_left_seat_instance_id.to_string(), InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_left_seat).unwrap(), - }); + result.instance_map.insert( + back_left_seat_instance_id.to_string(), + InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_left_seat).unwrap(), + }, + ); let back_center_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_center_seat: sdv::seat::TYPE = sdv::seat::TYPE { - seat_massager: vec! [ - sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: back_center_airbag_seat_massager_instance_id.to_string(), - }, - ], + seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: back_center_airbag_seat_massager_instance_id.to_string(), + }], ..Default::default() }; - result.instance_map.insert(back_center_seat_instance_id.to_string(), InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_center_seat).unwrap(), - }); + result.instance_map.insert( + back_center_seat_instance_id.to_string(), + InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_center_seat).unwrap(), + }, + ); let back_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { - seat_massager: vec! [ - sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: back_right_airbag_seat_massager_instance_id.to_string(), - }, - ], + seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { + instance_id: back_right_airbag_seat_massager_instance_id.to_string(), + }], ..Default::default() - }; - result.instance_map.insert(back_right_seat_instance_id.to_string(), InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_right_seat).unwrap(), - }); + }; + result.instance_map.insert( + back_right_seat_instance_id.to_string(), + InstanceData { + model_id: sdv::seat::ID.to_string(), + description: sdv::seat::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_right_seat).unwrap(), + }, + ); // Create the cabin. let cabin_instance_id = format!("{}", uuid::Uuid::new_v4()); let cabin_value: sdv::cabin::TYPE = sdv::cabin::TYPE { - seat: vec! [ + seat: vec![ sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: front_left_seat_instance_id.to_string(), }, @@ -132,7 +135,7 @@ fn create_request_state() -> RequestState { instance_id: back_left_seat_instance_id.to_string(), }, sdv::cabin::seat::RELATIONSHIP_TYPE { - instance_id: back_center_seat_instance_id.to_string() + instance_id: back_center_seat_instance_id.to_string(), }, sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: back_right_seat_instance_id.to_string(), @@ -140,27 +143,29 @@ fn create_request_state() -> RequestState { ], ..Default::default() }; - result.instance_map.insert(cabin_instance_id.clone(), InstanceData { - model_id: sdv::cabin::ID.to_string(), - description: sdv::cabin::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&cabin_value).unwrap(), - }); + result.instance_map.insert( + cabin_instance_id.clone(), + InstanceData { + model_id: sdv::cabin::ID.to_string(), + description: sdv::cabin::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&cabin_value).unwrap(), + }, + ); // Create the vehicle. let vehicle_instance_id = format!("{}", uuid::Uuid::new_v4()); - let _vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { - cabin: vec! [ - sdv::vehicle::cabin::RELATIONSHIP_TYPE { - instance_id: cabin_instance_id, - }, - ], - ..Default::default() + let _vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { + cabin: vec![sdv::vehicle::cabin::RELATIONSHIP_TYPE { instance_id: cabin_instance_id }], + ..Default::default() }; - result.instance_map.insert(vehicle_instance_id, InstanceData { - model_id: sdv::vehicle::ID.to_string(), - description: sdv::vehicle::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&_vehicle_value).unwrap(), - }); + result.instance_map.insert( + vehicle_instance_id, + InstanceData { + model_id: sdv::vehicle::ID.to_string(), + description: sdv::vehicle::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&_vehicle_value).unwrap(), + }, + ); result } @@ -176,17 +181,17 @@ async fn register_vehicle_parts( provider_uri: &str, state: Arc>, ) -> Result<(), Status> { - let mut entity_access_info_list: Vec = Vec::new(); state.lock().instance_map.iter().for_each(|(instance_id, instance_data)| { - info!("Registering the instance with the instance id '{}' and the model id '{}'", instance_id, instance_data.model_id); + info!( + "Registering the instance with the instance id '{}' and the model id '{}'", + instance_id, instance_data.model_id + ); let endpoint_info = EndpointInfo { protocol: digital_twin_protocol::GRPC.to_string(), - operations: vec![ - digital_twin_operation::GET.to_string(), - ], + operations: vec![digital_twin_operation::GET.to_string()], uri: provider_uri.to_string(), context: instance_id.to_string(), }; @@ -195,7 +200,7 @@ async fn register_vehicle_parts( name: String::new(), // no name, so we will use an empty name id: instance_data.model_id.to_string(), description: instance_data.description.to_string(), - endpoint_info_list: vec!(endpoint_info), + endpoint_info_list: vec![endpoint_info], }; entity_access_info_list.push(entity_access_info); @@ -204,8 +209,7 @@ async fn register_vehicle_parts( let mut client = DigitalTwinRegistryClient::connect(invehicle_digital_twin_uri.to_string()) .await .map_err(|e| Status::internal(e.to_string()))?; - let request = - tonic::Request::new(RegisterRequest { entity_access_info_list }); + let request = tonic::Request::new(RegisterRequest { entity_access_info_list }); let _response = client.register(request).await?; Ok(()) diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs index 8690d9d7..23639b71 100644 --- a/samples/graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -66,11 +66,17 @@ impl Request for RequestImpl { // Check to make sure that the type_id is for a perform_request request. if targeted_payload_json.operation != digital_twin_operation::GET { - return Err(tonic::Status::invalid_argument(format!("Unexpected operation '{}'", targeted_payload_json.operation))); + return Err(tonic::Status::invalid_argument(format!( + "Unexpected operation '{}'", + targeted_payload_json.operation + ))); } if !targeted_payload_json.payload.is_empty() { - return Err(tonic::Status::invalid_argument(format!("Unexpected payload, it should be empty, not '{}'", targeted_payload_json.payload))); + return Err(tonic::Status::invalid_argument(format!( + "Unexpected payload, it should be empty, not '{}'", + targeted_payload_json.payload + ))); } let state: Arc> = self.state.clone(); @@ -82,7 +88,10 @@ impl Request for RequestImpl { match lock.instance_map.get(&targeted_payload_json.instance_id) { Some(instance_data) => instance_data.clone(), None => { - error!("Instance not found for instance id '{}'", targeted_payload_json.instance_id); + error!( + "Instance not found for instance id '{}'", + targeted_payload_json.instance_id + ); return; } } diff --git a/samples/protobuf_data_access/build.rs b/samples/protobuf_data_access/build.rs index ec5a93e7..d91a0ba9 100644 --- a/samples/protobuf_data_access/build.rs +++ b/samples/protobuf_data_access/build.rs @@ -31,11 +31,11 @@ fn main() -> Result<(), Box> { tonic_build::configure().compile( &["../../interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto"], &["../../interfaces/module/digital_twin_graph/v1/"], - )?; + )?; tonic_build::configure().compile( &["../../interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto"], &["../../interfaces/module/digital_twin_registry/v1/"], - )?; + )?; tonic_build::configure().compile( &["../../external/chariott/service_discovery/proto/core/v1/service_registry.proto"], &["../../external/chariott/service_discovery/proto/core/v1/"], From eebe5bdbc1e6f35627013efabfecbad456484e39 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:09:23 -0700 Subject: [PATCH 003/109] Digital Twin Graph --- .github/workflows/rust-ci.yml | 2 +- .github/workflows/security-audit.yml | 6 +++++- .../digital_twin_graph/src/digital_twin_graph_impl.rs | 3 +-- .../digital_twin_graph/src/digital_twin_graph_module.rs | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 6887f507..d52833ac 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -67,5 +67,5 @@ jobs: # Build the project with the `managed_subscribe` and `digital_twin_graph` features enabled. run: cargo build --features "managed_subscribe,digital_twin_graph,digital_twin_registry" - name: Test - # Test the project with the `managed_subscribe` and `digital_twin_graph` features enabled. + # Test the project with the `managed_subscribe` and `digital_twin_graph` features enabled. run: cargo test --features "managed_subscribe,digital_twin_graph,digital_twin_registry" diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index 225632ac..02210103 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -15,5 +15,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 + # Ignored advisories: + # - https://rustsec.org/advisories/RUSTSEC-2024-0320 : yaml-rust is unmaintained + # - This is a dependency of the config crate, which does not have a version without yaml-rust. + # See https://github.com/mehcode/config-rs/issues/473 - run: | - cargo audit --deny warnings + cargo audit --deny warnings --ignore RUSTSEC-2024-0320 diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index f067b1ca..477cc608 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -88,8 +88,7 @@ pub async fn discover_digital_twin_providers_with_model_id( Ok(response_inner .entity_access_info_list .iter() - .map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) - .flatten() + .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) .filter(|endpoint_info| { endpoint_info.protocol == protocol && is_subset(operations, &endpoint_info.operations) }) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs index 015679a5..6959aaac 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs @@ -30,7 +30,7 @@ impl GrpcModule for DigitalTwinGraphModule { /// # Arguments /// * `builder` - A tonic::RoutesBuilder that contains the grpc services to build. fn add_grpc_services(&self, builder: &mut RoutesBuilder) { - // TODO: Setup config + // Note: The authority is hardcoded for now, but it should be configurable in the future. let invehicle_digital_twin_authority = "0.0.0.0:5010"; let invehicle_digital_twin_uri = format!("http://{invehicle_digital_twin_authority}"); // Devskim: ignore DS137138 let respond_authority = "0.0.0.0:5010"; From 42a3230a06a430f8ce611c2ad28525f64ec6a3e4 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:18:59 -0700 Subject: [PATCH 004/109] Digital Twin Graph --- .github/workflows/security-audit.yml | 2 +- .../digital_twin_registry/src/digital_twin_registry_impl.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index 02210103..64f58c2d 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -18,6 +18,6 @@ jobs: # Ignored advisories: # - https://rustsec.org/advisories/RUSTSEC-2024-0320 : yaml-rust is unmaintained # - This is a dependency of the config crate, which does not have a version without yaml-rust. - # See https://github.com/mehcode/config-rs/issues/473 + # See https://github.com/mehcode/config-rs/issues/473 - run: | cargo audit --deny warnings --ignore RUSTSEC-2024-0320 diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs index 83e4d869..7d8dbd3f 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -171,6 +171,7 @@ impl DigitalTwinRegistryImpl { } } +/* #[cfg(test)] mod digital_twin_registry_impl_tests { use super::*; @@ -265,3 +266,4 @@ mod digital_twin_registry_impl_tests { } } } +*/ From 9fa461df21915329d091d74661bd2df219c8bad3 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:55:16 -0700 Subject: [PATCH 005/109] Digital Twin Graph --- .../src/digital_twin_registry_module.rs | 2 -- .../graph/vehicle_core_provider/src/request_impl.rs | 12 +----------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_module.rs b/core/module/digital_twin_registry/src/digital_twin_registry_module.rs index a3229c13..d0ecacef 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_module.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_module.rs @@ -5,9 +5,7 @@ use common::grpc_module::GrpcModule; use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_server::DigitalTwinRegistryServer; -// use log::{debug, error, info}; use tonic::transport::server::RoutesBuilder; -// use tonic::{Request, Response, Status}; use crate::digital_twin_registry_impl::DigitalTwinRegistryImpl; diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs index 23639b71..9085e421 100644 --- a/samples/graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: MIT use digital_twin_graph::TargetedPayload; -// use digital_twin_model::sdv_v1 as sdv; use log::{debug, error, info, warn}; use parking_lot::{Mutex, MutexGuard}; use samples_common::constants::digital_twin_operation; @@ -13,7 +12,6 @@ use samples_protobuf_data_access::async_rpc::v1::request::{ use samples_protobuf_data_access::async_rpc::v1::respond::{ respond_client::RespondClient, AnswerRequest, }; -// use seat_massager_common::{status, TargetedPayload}; use std::collections::HashMap; use std::sync::Arc; @@ -60,11 +58,7 @@ impl Request for RequestImpl { info!(" member_path: {}", targeted_payload_json.member_path); info!(" operation: {}", targeted_payload_json.operation); - // Extract the type_id from the request payload. - // let type_id_json: serde_json::Value = request_payload_json.get("@type").unwrap().clone(); - // let type_id: String = serde_json::from_value(type_id_json.clone()).unwrap(); - - // Check to make sure that the type_id is for a perform_request request. + // Check to make sure that the targeted operation is a GET. if targeted_payload_json.operation != digital_twin_operation::GET { return Err(tonic::Status::invalid_argument(format!( "Unexpected operation '{}'", @@ -106,10 +100,6 @@ impl Request for RequestImpl { } let mut client = client_result.unwrap(); - // Serilaize the response payload. - // let response_payload_json: String = - // serde_json::to_string_pretty(&response_payload).unwrap(); - let answer_request = tonic::Request::new(AnswerRequest { ask_id, payload: response_payload_json }); From 41f3e7fa6c8433365fb2275add889dfe587dca39 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:49:57 -0700 Subject: [PATCH 006/109] Digital Twin Graph --- .../src/digital_twin_registry_impl.rs | 91 ++++++++++++++----- 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs index 7d8dbd3f..d2d3488a 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -171,58 +171,104 @@ impl DigitalTwinRegistryImpl { } } -/* #[cfg(test)] mod digital_twin_registry_impl_tests { use super::*; - use core_protobuf_data_access::invehicle_digital_twin::v1::EndpointInfo; + // use core_protobuf_data_access::module::digital_twin_registry::v1::EndpointInfo; #[tokio::test] - async fn find_by_id_test() { + async fn find_by_model_id_test() { let operations = vec![String::from("Subscribe"), String::from("Unsubscribe")]; let endpoint_info = EndpointInfo { protocol: String::from("grpc"), uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 - context: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + context: String::from("1234567890"), operations, }; let entity_access_info = EntityAccessInfo { name: String::from("AmbientAirTemperature"), - id: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), description: String::from("Ambient air temperature"), endpoint_info_list: vec![endpoint_info], }; let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); - let invehicle_digital_twin_impl = - InvehicleDigitalTwinImpl { entity_access_info_map: entity_access_info_map.clone() }; + let digital_twin_registry_impl = + DigitalTwinRegistryImpl { entity_access_info_map: entity_access_info_map.clone() }; // This block controls the lifetime of the lock. { - let mut lock: RwLockWriteGuard> = + let mut lock: RwLockWriteGuard>> = entity_access_info_map.write(); - lock.insert(entity_access_info.id.clone(), entity_access_info.clone()); + lock.insert(entity_access_info.id.clone(), vec![entity_access_info.clone()]); } - let request = tonic::Request::new(FindByIdRequest { - id: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + let request = tonic::Request::new(FindByModelIdRequest { + model_id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), }); - let result = invehicle_digital_twin_impl.find_by_id(request).await; + let result = digital_twin_registry_impl.find_by_model_id(request).await; assert!(result.is_ok()); let response = result.unwrap(); let response_inner = response.into_inner(); - assert!(response_inner.entity_access_info.is_some()); + assert!(response_inner.entity_access_info_list.len() == 1); - let response_entity_access_info = response_inner.entity_access_info.unwrap(); + let response_entity_access_info = response_inner.entity_access_info_list[0].clone(); + assert_eq!(response_entity_access_info.id, "dtmi:sdv:hvac:ambient_air_temperature;1"); + assert_eq!(response_entity_access_info.endpoint_info_list.len(), 1); assert_eq!( - response_entity_access_info.id, - "dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1" + response_entity_access_info.endpoint_info_list[0].uri, + "http://[::1]:40010" // Devskim: ignore DS137138 ); + } + + #[tokio::test] + async fn find_by_instance_id_test() { + let operations = vec![String::from("Subscribe"), String::from("Unsubscribe")]; + + let endpoint_info = EndpointInfo { + protocol: String::from("grpc"), + uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 + context: String::from("1234567890"), + operations, + }; + + let entity_access_info = EntityAccessInfo { + name: String::from("AmbientAirTemperature"), + id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), + description: String::from("Ambient air temperature"), + endpoint_info_list: vec![endpoint_info], + }; + + let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); + + let digital_twin_registry_impl = + DigitalTwinRegistryImpl { entity_access_info_map: entity_access_info_map.clone() }; + + // This block controls the lifetime of the lock. + { + let mut lock: RwLockWriteGuard>> = + entity_access_info_map.write(); + lock.insert(entity_access_info.id.clone(), vec![entity_access_info.clone()]); + } + + let request = tonic::Request::new(FindByInstanceIdRequest { + instance_id: String::from("1234567890"), + }); + let result = digital_twin_registry_impl.find_by_instance_id(request).await; + assert!(result.is_ok()); + let response = result.unwrap(); + let response_inner = response.into_inner(); + + assert!(response_inner.entity_access_info_list.len() == 1); + + let response_entity_access_info = response_inner.entity_access_info_list[0].clone(); + + assert_eq!(response_entity_access_info.endpoint_info_list[0].context, "1234567890"); assert_eq!(response_entity_access_info.endpoint_info_list.len(), 1); assert_eq!( response_entity_access_info.endpoint_info_list[0].uri, @@ -235,35 +281,34 @@ mod digital_twin_registry_impl_tests { let endpoint_info = EndpointInfo { protocol: String::from("grpc"), uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 - context: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + context: String::from("1234567890"), operations: vec![String::from("Subscribe"), String::from("Unsubscribe")], }; let entity_access_info = EntityAccessInfo { name: String::from("AmbientAirTemperature"), - id: String::from("dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"), + id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), description: String::from("Ambient air temperature"), endpoint_info_list: vec![endpoint_info], }; let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); - let invehicle_digital_twin_impl = - InvehicleDigitalTwinImpl { entity_access_info_map: entity_access_info_map.clone() }; + let digital_twin_registry_impl = + DigitalTwinRegistryImpl { entity_access_info_map: entity_access_info_map.clone() }; let request = tonic::Request::new(RegisterRequest { entity_access_info_list: vec![entity_access_info], }); - let result = invehicle_digital_twin_impl.register(request).await; + let result = digital_twin_registry_impl.register(request).await; assert!(result.is_ok(), "register result is not okay: {result:?}"); // This block controls the lifetime of the lock. { - let lock: RwLockReadGuard> = + let lock: RwLockReadGuard>> = entity_access_info_map.read(); // Make sure that we populated the entity map from the contents of the DTDL. assert_eq!(lock.len(), 1, "expected length was 1, actual length is {}", lock.len()); } } } -*/ From 6a448bbaf9a4dfcfa61a82ed11f9881353c04737 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:16:14 -0700 Subject: [PATCH 007/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 113 ++++++++++- digital-twin-model/src/sdv_v1.rs | 27 +++ .../v1/digital_twin_graph.proto | 3 +- samples/common/src/provider_config.rs | 11 +- samples/graph/Cargo.toml | 4 + samples/graph/consumer/src/main.rs | 136 +++++++------ .../graph/seat_massager_provider/src/main.rs | 188 ++++++++++++++++++ .../src/request_impl.rs | 130 ++++++++++++ .../graph/vehicle_core_provider/src/main.rs | 31 ++- 9 files changed, 570 insertions(+), 73 deletions(-) create mode 100644 samples/graph/seat_massager_provider/src/main.rs create mode 100644 samples/graph/seat_massager_provider/src/request_impl.rs diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 477cc608..28fba474 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -254,6 +254,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { ) -> Result, tonic::Status> { let request_inner = request.into_inner(); let instance_id = request_inner.instance_id; + let member_path = request_inner.member_path; info!("Received a get request for instance id {instance_id}"); @@ -267,7 +268,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .await .map_err(tonic::Status::internal)?; - info!(">> Found the provider endpoint info list: {provider_endpoint_info_list:?}"); + info!("Found the provider endpoint info list: {provider_endpoint_info_list:?}"); let mut values = vec![]; @@ -290,7 +291,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let targeted_payload = TargetedPayload { instance_id: instance_id.clone(), - member_path: "".to_string(), + member_path: member_path.clone(), operation: digital_twin_operation::GET.to_string(), payload: "".to_string(), }; @@ -380,8 +381,112 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { &self, request: tonic::Request, ) -> Result, tonic::Status> { - warn!("Got a invoke request: {request:?}"); + let request_inner = request.into_inner(); + let instance_id = request_inner.instance_id; + let member_path = request_inner.member_path; + let request_payload = request_inner.request_payload; + + info!("Received an invoke request for instance id {instance_id}"); + + // Retrieve the provider details. + let provider_endpoint_info_list = discover_digital_twin_providers_with_instance_id( + &self.invehicle_digital_twin_uri, + instance_id.as_str(), + digital_twin_protocol::GRPC, + &[digital_twin_operation::INVOKE.to_string()], + ) + .await + .map_err(tonic::Status::internal)?; + + info!("Found the provider endpoint info list: {provider_endpoint_info_list:?}"); + + let mut values = vec![]; + + for provider_endpoint_info in &provider_endpoint_info_list { + let provider_uri = provider_endpoint_info.uri.clone(); + let instance_id = provider_endpoint_info.context.clone(); + + let tx = Arc::clone(&self.tx); + let mut rx = tx.subscribe(); + + let client_result = RequestClient::connect(provider_uri.clone()).await; + if client_result.is_err() { + warn!("Unable to connect. We will skip this one."); + continue; + } + let mut client = client_result.unwrap(); + + // Note: The ask id must be a universally unique value. + let ask_id = Uuid::new_v4().to_string(); + + let targeted_payload = TargetedPayload { + instance_id: instance_id.clone(), + member_path: member_path.clone(), + operation: digital_twin_operation::INVOKE.to_string(), + payload: request_payload.to_string(), + }; + + // Serialize the targeted payload. + let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); + + let request = tonic::Request::new(AskRequest { + respond_uri: self.respond_uri.clone(), + ask_id: ask_id.clone(), + payload: targeted_payload_json.clone(), + }); + + // Send the ask. + let response = client.ask(request).await; + if let Err(status) = response { + warn!("Unable to call ask, due to {status:?}\nWe will skip this one.."); + continue; + } + + // Wait for the answer request. + let mut answer_request: AnswerRequest = Default::default(); + let mut attempts_after_failure = 0; + const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; + while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { + match timeout(Duration::from_secs(5), rx.recv()).await { + Ok(Ok(request)) => { + if ask_id == request.ask_id { + // We have received the answer request that we are expecting. + answer_request = request; + break; + } else { + // Ignore this answer request, as it is not the one that we are expecting. + warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); + // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. + continue; + } + } + Ok(Err(error_message)) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + Err(error_message) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + } + } + + info!( + "Received an answer request. The ask_id is '{}'. The payload is '{}", + answer_request.ask_id, answer_request.payload + ); + + values.push(answer_request.payload); + } + + if values.is_empty() { + return Err(tonic::Status::not_found("No values found")); + } - Err(tonic::Status::unimplemented("invoke has not been implemented")) + Ok(tonic::Response::new(InvokeResponse { response_payload: values[0].clone() })) } } diff --git a/digital-twin-model/src/sdv_v1.rs b/digital-twin-model/src/sdv_v1.rs index 15d37772..019dad7e 100644 --- a/digital-twin-model/src/sdv_v1.rs +++ b/digital-twin-model/src/sdv_v1.rs @@ -160,6 +160,8 @@ pub mod basic_airbag_seat_massager { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, #[serde(rename = "@type")] #[derivative(Default( value = "crate::sdv_v1::basic_airbag_seat_massager::ID.to_string()" @@ -209,12 +211,29 @@ pub mod cabin { pub const NAME: &str = "seat"; pub const DESCRIPTION: &str = "The seats."; + pub type SEAT_ROW_TYPE = i32; + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(PartialEq)] + pub enum SEAT_POSITION_TYPE { + #[derivative(Default)] + /// No value + none, + left, + center, + right, + } + #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] pub struct RELATIONSHIP_TYPE { #[serde(rename = "@id")] pub instance_id: String, + pub seat_row: SEAT_ROW_TYPE, + pub seat_position: SEAT_POSITION_TYPE, } pub type TYPE = Vec; @@ -227,6 +246,8 @@ pub mod cabin { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::cabin::ID.to_string()"))] pub model_id: String, @@ -372,6 +393,8 @@ pub mod premium_airbag_seat_massager { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, #[serde(rename = "@type")] #[derivative(Default( value = "crate::sdv_v1::premium_airbag_seat_massager::ID.to_string()" @@ -407,6 +430,8 @@ pub mod seat { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::seat::ID.to_string()"))] pub model_id: String, @@ -697,6 +722,8 @@ pub mod vehicle { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::vehicle::ID.to_string()"))] pub model_id: String, diff --git a/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto index 65784083..9b48eed1 100644 --- a/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto +++ b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto @@ -41,7 +41,8 @@ message SetResponse { message InvokeRequest { string instance_id = 1; - string request_payload = 2; + string member_path = 2; + string request_payload = 3; } message InvokeResponse { diff --git a/samples/common/src/provider_config.rs b/samples/common/src/provider_config.rs index 109a5873..a6d44eae 100644 --- a/samples/common/src/provider_config.rs +++ b/samples/common/src/provider_config.rs @@ -6,7 +6,7 @@ use crate::utils; use serde_derive::Deserialize; -const CONFIG_FILENAME: &str = "provider_settings"; +const DEFAULT_CONFIG_FILENAME: &str = "provider_settings"; #[derive(Debug, Deserialize)] pub struct Settings { @@ -15,7 +15,12 @@ pub struct Settings { pub invehicle_digital_twin_uri: Option, } -/// Load the settings. +/// Load the settings using the default config filename. pub fn load_settings() -> Settings { - utils::load_settings(CONFIG_FILENAME).unwrap() + load_settings_with_config_filename(DEFAULT_CONFIG_FILENAME) +} + +// Load the settings using the specified config filename. +pub fn load_settings_with_config_filename(config_filename: &str) -> Settings { + utils::load_settings(config_filename).unwrap() } diff --git a/samples/graph/Cargo.toml b/samples/graph/Cargo.toml index 056ab8a9..a875b559 100644 --- a/samples/graph/Cargo.toml +++ b/samples/graph/Cargo.toml @@ -35,6 +35,10 @@ tonic-build = { workspace = true } name = "graph-vehicle-core-provider" path = "vehicle_core_provider/src/main.rs" +[[bin]] +name = "graph-seat-massager-provider" +path = "seat_massager_provider/src/main.rs" + [[bin]] name = "graph-consumer" path = "consumer/src/main.rs" diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 9ecd0943..cec096e3 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -17,91 +17,111 @@ use tokio_retry::strategy::{ExponentialBackoff, jitter}; /// # Arguments /// `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Result<(), String> { + // Define a retry strategy. let retry_strategy = ExponentialBackoff::from_millis(100) .map(jitter) // add jitter to delays .take(10); // limit to 10 retries - let client_result = Retry::spawn(retry_strategy.clone(), || async { - let client_result = - DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await; - if client_result.is_err() { - return Err("Unable to connect.".to_string()); - } - Ok(client_result.unwrap()) + // Connect to the digital twin graph service. + let client = Retry::spawn(retry_strategy.clone(), || async { + DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await.map_err(|err_msg| format!("Unable to connect to the digital twin graph service due to: {err_msg}")) }) - .await; - - if client_result.is_err() { - return Err("Unable to connect".to_string()); - } - - let client = client_result.unwrap(); - - let request: FindRequest = FindRequest { model_id: sdv::vehicle::ID.to_string() }; + .await?; - let response_result = Retry::spawn(retry_strategy.clone(), || async { - // Make a local mutable copy for use only within this cloure body. + // Find vehicle instances. + let find_vehicle_response = Retry::spawn(retry_strategy.clone(), || async { + // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - // Invoke find. - let response_result = client.find(request.clone()).await; - if response_result.is_err() { - return Err("Unable to call find".to_string()); - } - Ok(response_result.unwrap()) + let request: FindRequest = FindRequest { model_id: sdv::vehicle::ID.to_string() }; + + client.find(request).await.map_err(|err_msg| format!("Unable to call find due to: {err_msg}")) }) - .await; + .await?; + let find_vehicle_response_inner = find_vehicle_response.into_inner(); - if response_result.is_err() { - return Err("Unable to call find".to_string()); + if find_vehicle_response_inner.values.is_empty() { + return Err("Unable to find vehicle instances".to_string()); } - let response = response_result.unwrap(); + // For now, we will just use the first vehicle instance. + let vehicle: sdv::vehicle::TYPE = serde_json::from_str(&find_vehicle_response_inner.values[0]).unwrap(); + // info!("The vehicle is: {:?}", vehicle); + info!("The vehicle's instance id is: {:?}", vehicle.instance_id); + if vehicle.cabin.is_empty() { + return Err("The vehicle does not have a cabin".to_string()); + } + let cabin_instance_id = vehicle.cabin[0].instance_id.clone(); + info!("The cabin's instance id is: {:?}", cabin_instance_id); + + // Get the cabin instance. + let get_cabin_response = Retry::spawn(retry_strategy.clone(), || async { + // Make a local mutable copy for use only within this closure body. + let mut client = client.clone(); - let mut cabin_instance_id_opt = None; + let request: GetRequest = GetRequest { instance_id: cabin_instance_id.clone(), member_path: "".to_string() }; - let response_inner = response.into_inner(); - for value in response_inner.values.iter() { - let vehicle: sdv::vehicle::TYPE = serde_json::from_str(value).unwrap(); - info!("The value is: {:?}", vehicle); - info!("The cabin's instance id is: {:?}", vehicle.cabin[0].instance_id); - cabin_instance_id_opt = Some(vehicle.cabin[0].instance_id.clone()); - } + client.get(request.clone()).await.map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + }) + .await?; + let get_cabin_response_inner = get_cabin_response.into_inner(); - if cabin_instance_id_opt.is_none() { - return Err("Unable to find cabin instance id".to_string()); + let mut front_left_seat_instance_id_option: Option = None; + let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response_inner.value).unwrap(); + // info!("The cabin is: {:?}", cabin); + for seat_relationship in cabin.seat.iter() { + if (seat_relationship.seat_row == 1) && (seat_relationship.seat_position == sdv::cabin::seat::SEAT_POSITION_TYPE::left) { + info!("The front left seat's instance id is: {:?}", seat_relationship.instance_id); + front_left_seat_instance_id_option = Some(seat_relationship.instance_id.clone()); + } } - let cabin_instance_id = cabin_instance_id_opt.unwrap(); + if front_left_seat_instance_id_option.is_none() { + return Err("The front left seat is not found".to_string()); + } - let get_cabin_request: GetRequest = - GetRequest { instance_id: cabin_instance_id, member_path: "".to_string() }; + let front_left_seat_instance_id = front_left_seat_instance_id_option.unwrap(); - let get_cabin_response_result = Retry::spawn(retry_strategy, || async { - // Make a local mutable copy for use only within this cloure body. + // Get the seat instance. + let get_seat_response = Retry::spawn(retry_strategy.clone(), || async { + // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - // Invoke find. - let get_cabin_response_result = client.get(get_cabin_request.clone()).await; - if get_cabin_response_result.is_err() { - return Err("Unable to call get".to_string()); - } - Ok(get_cabin_response_result.unwrap()) + let request: GetRequest = GetRequest { instance_id: front_left_seat_instance_id.clone(), member_path: "".to_string() }; + + client.get(request.clone()).await.map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) }) - .await; + .await?; + let get_seat_response_inner = get_seat_response.into_inner(); - if get_cabin_response_result.is_err() { - return Err("Unable to call get".to_string()); + let seat: sdv::seat::TYPE = serde_json::from_str(&get_seat_response_inner.value).unwrap(); + // info!("The seat is: {:?}", seat); + if seat.seat_massager.is_empty() { + return Err("The seat does not have a seat massage".to_string()); } + let seat_massager_instance_id = seat.seat_massager[0].instance_id.clone(); + info!("The seat massager's instance id is: {:?}", seat_massager_instance_id); - let get_cabin_response = get_cabin_response_result.unwrap(); + // Get the seat massager instance. + let get_seat_massager_response = Retry::spawn(retry_strategy.clone(), || async { + // Make a local mutable copy for use only within this closure body. + let mut client = client.clone(); - let get_cabin_response_inner = get_cabin_response.into_inner(); - let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response_inner.value).unwrap(); - info!("The value is: {:?}", cabin); - for seat in cabin.seat.iter() { - info!("The seat's instance id is: {:?}", seat.instance_id); + let request: GetRequest = GetRequest { instance_id: seat_massager_instance_id.clone(), member_path: "".to_string() }; + + client.get(request.clone()).await.map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + }) + .await?; + let get_seat_massager_response_inner = get_seat_massager_response.into_inner(); + + let seat_massager_json: serde_json::Value = serde_json::from_str(&get_seat_massager_response_inner.value).unwrap(); + + if seat_massager_json["@type"] != sdv::premium_airbag_seat_massager::ID { + return Err(format!("The seat massager instance is not of the expected model, instead it is a {0}", seat_massager_json["@type"])); } + let seat_massager: sdv::premium_airbag_seat_massager::TYPE = serde_json::from_value(seat_massager_json.clone()).unwrap(); + + info!("The seat massager is: {:?}", seat_massager); Ok(()) } diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs new file mode 100644 index 00000000..15ca8e51 --- /dev/null +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -0,0 +1,188 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +mod request_impl; + +use digital_twin_model::sdv_v1 as sdv; +use env_logger::{Builder, Target}; +use log::{debug, info, LevelFilter}; +use parking_lot::Mutex; +use samples_common::constants::{digital_twin_operation, digital_twin_protocol}; +use samples_common::provider_config; +use samples_common::utils::{retrieve_invehicle_digital_twin_uri, retry_async_based_on_status}; +use samples_protobuf_data_access::async_rpc::v1::request::request_server::RequestServer; +use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::digital_twin_registry_client::DigitalTwinRegistryClient; +use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::{ + EndpointInfo, EntityAccessInfo, RegisterRequest, +}; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::time::Duration; +use tonic::{transport::Server, Status}; + +use crate::request_impl::{InstanceData, RequestImpl, RequestState}; + +fn create_request_state() -> RequestState { + let mut result: RequestState = RequestState { instance_map: HashMap::new() }; + + // Create the seat massagers. + + let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); + let front_left_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { + instance_id: front_left_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; + result.instance_map.insert( + front_left_airbag_seat_massager_instance_id.to_string(), + InstanceData { + model_id: sdv::premium_airbag_seat_massager::ID.to_string(), + description: sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&front_left_airbag_seat_massager).unwrap(), + }, + ); + + let front_right_airbag_seat_massager_instance_id = "front_right_airbag_seat_massager".to_string(); + let front_right_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { + instance_id: front_right_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; + result.instance_map.insert( + front_right_airbag_seat_massager_instance_id.to_string(), + InstanceData { + model_id: sdv::premium_airbag_seat_massager::ID.to_string(), + description: sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&front_right_airbag_seat_massager).unwrap(), + }, + ); + + let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); + let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { + instance_id: back_left_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; + result.instance_map.insert( + back_left_airbag_seat_massager_instance_id.to_string(), + InstanceData { + model_id: sdv::basic_airbag_seat_massager::ID.to_string(), + description: sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_left_airbag_seat_massager).unwrap(), + }, + ); + + let back_center_airbag_seat_massager_instance_id = "back_center_airbag_seat_massager".to_string(); + let back_center_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { + instance_id: back_center_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; + result.instance_map.insert( + back_center_airbag_seat_massager_instance_id.to_string(), + InstanceData { + model_id: sdv::basic_airbag_seat_massager::ID.to_string(), + description: sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_center_airbag_seat_massager).unwrap(), + }, + ); + + let back_right_airbag_seat_massager_instance_id = "back_right_airbag_seat_massager".to_string(); + let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { + instance_id: back_right_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; + result.instance_map.insert( + back_right_airbag_seat_massager_instance_id.to_string(), + InstanceData { + model_id: sdv::basic_airbag_seat_massager::ID.to_string(), + description: sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), + serialized_value: serde_json::to_string(&back_right_airbag_seat_massager).unwrap(), + }, + ); + + result +} + +/// Register the airbag seat massager's massage airbags property. +/// +/// # Arguments +/// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. +/// * `provider_uri` - The provider's URI. +/// * `instance_id` - The instance id. +async fn register_seat_massagers( + invehicle_digital_twin_uri: &str, + provider_uri: &str, + state: Arc>, +) -> Result<(), Status> { + let mut entity_access_info_list: Vec = Vec::new(); + + state.lock().instance_map.iter().for_each(|(instance_id, instance_data)| { + info!( + "Registering the instance with the instance id '{}' and the model id '{}'", + instance_id, instance_data.model_id + ); + + let endpoint_info = EndpointInfo { + protocol: digital_twin_protocol::GRPC.to_string(), + operations: vec![digital_twin_operation::GET.to_string()], + uri: provider_uri.to_string(), + context: instance_id.to_string(), + }; + + let entity_access_info = EntityAccessInfo { + name: String::new(), // no name, so we will use an empty name + id: instance_data.model_id.to_string(), + description: instance_data.description.to_string(), + endpoint_info_list: vec![endpoint_info], + }; + + entity_access_info_list.push(entity_access_info); + }); + + let mut client = DigitalTwinRegistryClient::connect(invehicle_digital_twin_uri.to_string()) + .await + .map_err(|e| Status::internal(e.to_string()))?; + let request = tonic::Request::new(RegisterRequest { entity_access_info_list }); + let _response = client.register(request).await?; + + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Setup logging. + Builder::new().filter(None, LevelFilter::Info).target(Target::Stdout).init(); + + info!("The Seat Massager Provider has started."); + + let settings = provider_config::load_settings_with_config_filename("seat_massager_provider_settings"); + + let provider_authority = settings.provider_authority; + + let invehicle_digital_twin_uri = retrieve_invehicle_digital_twin_uri( + settings.invehicle_digital_twin_uri, + settings.chariott_uri, + ) + .await?; + + // Construct the provider URI from the provider authority. + let provider_uri = format!("http://{provider_authority}"); // Devskim: ignore DS137138 + + // Setup the HTTP server. + let addr: SocketAddr = provider_authority.parse()?; + let state = Arc::new(Mutex::new(create_request_state())); + let request_impl = RequestImpl { state: state.clone() }; + let server_future = Server::builder().add_service(RequestServer::new(request_impl)).serve(addr); + info!("The HTTP server is listening on address '{provider_authority}'"); + + info!("Sending a register request to the In-Vehicle Digital Twin Service URI {invehicle_digital_twin_uri}"); + retry_async_based_on_status(30, Duration::from_secs(1), || { + register_seat_massagers(&invehicle_digital_twin_uri, &provider_uri, state.clone()) + }) + .await?; + + server_future.await?; + + debug!("The Seat Massager Provider has completed."); + + Ok(()) +} diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs new file mode 100644 index 00000000..9085e421 --- /dev/null +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use digital_twin_graph::TargetedPayload; +use log::{debug, error, info, warn}; +use parking_lot::{Mutex, MutexGuard}; +use samples_common::constants::digital_twin_operation; +use samples_protobuf_data_access::async_rpc::v1::request::{ + request_server::Request, AskRequest, AskResponse, NotifyRequest, NotifyResponse, +}; +use samples_protobuf_data_access::async_rpc::v1::respond::{ + respond_client::RespondClient, AnswerRequest, +}; +use std::collections::HashMap; +use std::sync::Arc; + +#[derive(Clone, Debug, Default)] +pub struct InstanceData { + pub model_id: String, + pub description: String, + pub serialized_value: String, +} + +#[derive(Debug, Default)] +pub struct RequestState { + pub instance_map: HashMap, +} + +#[derive(Debug, Default)] +pub struct RequestImpl { + pub state: Arc>, +} + +#[tonic::async_trait] +impl Request for RequestImpl { + /// Ask implementation. + /// + /// # Arguments + /// * `request` - Ask request. + async fn ask( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let request_inner = request.into_inner(); + let respond_uri: String = request_inner.respond_uri.clone(); + let ask_id: String = request_inner.ask_id.clone(); + let payload: String = request_inner.payload.clone(); + + info!("Received an ask request"); + info!(" respond_uri: {respond_uri}"); + info!(" ask_id: {ask_id}"); + + // Deserialize the targeted payload. + let targeted_payload_json: TargetedPayload = serde_json::from_str(&payload).unwrap(); + + info!(" instance_id: {}", targeted_payload_json.instance_id); + info!(" member_path: {}", targeted_payload_json.member_path); + info!(" operation: {}", targeted_payload_json.operation); + + // Check to make sure that the targeted operation is a GET. + if targeted_payload_json.operation != digital_twin_operation::GET { + return Err(tonic::Status::invalid_argument(format!( + "Unexpected operation '{}'", + targeted_payload_json.operation + ))); + } + + if !targeted_payload_json.payload.is_empty() { + return Err(tonic::Status::invalid_argument(format!( + "Unexpected payload, it should be empty, not '{}'", + targeted_payload_json.payload + ))); + } + + let state: Arc> = self.state.clone(); + + // Asynchronously perform the step. + tokio::spawn(async move { + let instance_data: InstanceData = { + let lock: MutexGuard = state.lock(); + match lock.instance_map.get(&targeted_payload_json.instance_id) { + Some(instance_data) => instance_data.clone(), + None => { + error!( + "Instance not found for instance id '{}'", + targeted_payload_json.instance_id + ); + return; + } + } + }; + + let response_payload_json = instance_data.serialized_value.clone(); + + let client_result = RespondClient::connect(respond_uri).await; + if let Err(error_message) = client_result { + error!("Unable to connect due to {error_message}"); + return; + } + let mut client = client_result.unwrap(); + + let answer_request = + tonic::Request::new(AnswerRequest { ask_id, payload: response_payload_json }); + + // Send the answer. + let response = client.answer(answer_request).await; + if let Err(status) = response { + error!("Answer failed: {status:?}"); + } + }); + + debug!("Completed the ask request."); + + Ok(tonic::Response::new(AskResponse {})) + } + + /// Notify implementation. + /// + /// # Arguments + /// * `request` - Notify request. + async fn notify( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + warn!("Got a notify request: {request:?}"); + + Err(tonic::Status::unimplemented("notify has not been implemented")) + } +} diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/graph/vehicle_core_provider/src/main.rs index a3a3840f..c262eb38 100644 --- a/samples/graph/vehicle_core_provider/src/main.rs +++ b/samples/graph/vehicle_core_provider/src/main.rs @@ -29,20 +29,21 @@ fn create_request_state() -> RequestState { // Seat massager ids. - let front_left_airbag_seat_massager_instance_id: &str = "front_left_airbag_seat_massager"; + let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); - let front_right_airbag_seat_massager_instance_id: &str = "front_right_airbag_seat_massager"; + let front_right_airbag_seat_massager_instance_id = "front_right_airbag_seat_massager".to_string(); - let back_left_airbag_seat_massager_instance_id: &str = "back_left_airbag_seat_massager"; + let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); - let back_center_airbag_seat_massager_instance_id: &str = "back_center_airbag_seat_massager"; + let back_center_airbag_seat_massager_instance_id = "back_center_airbag_seat_massager".to_string(); - let back_right_airbag_seat_massager_instance_id: &str = "back_right_airbag_seat_massager"; + let back_right_airbag_seat_massager_instance_id = "back_right_airbag_seat_massager".to_string(); // Create the seats. let front_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let front_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { + instance_id: front_left_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: front_left_airbag_seat_massager_instance_id.to_string(), }], @@ -59,6 +60,7 @@ fn create_request_state() -> RequestState { let front_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let front_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { + instance_id: front_right_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: front_right_airbag_seat_massager_instance_id.to_string(), }], @@ -75,6 +77,7 @@ fn create_request_state() -> RequestState { let back_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { + instance_id: back_left_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: back_left_airbag_seat_massager_instance_id.to_string(), }], @@ -91,6 +94,7 @@ fn create_request_state() -> RequestState { let back_center_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_center_seat: sdv::seat::TYPE = sdv::seat::TYPE { + instance_id: back_center_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: back_center_airbag_seat_massager_instance_id.to_string(), }], @@ -107,6 +111,7 @@ fn create_request_state() -> RequestState { let back_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { + instance_id: back_right_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: back_right_airbag_seat_massager_instance_id.to_string(), }], @@ -124,21 +129,32 @@ fn create_request_state() -> RequestState { // Create the cabin. let cabin_instance_id = format!("{}", uuid::Uuid::new_v4()); let cabin_value: sdv::cabin::TYPE = sdv::cabin::TYPE { + instance_id: cabin_instance_id.clone(), seat: vec![ sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: front_left_seat_instance_id.to_string(), + seat_row: 1, + seat_position: sdv::cabin::seat::SEAT_POSITION_TYPE::left, }, sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: front_right_seat_instance_id.to_string(), + seat_row: 1, + seat_position: sdv::cabin::seat::SEAT_POSITION_TYPE::right, }, sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: back_left_seat_instance_id.to_string(), + seat_row: 2, + seat_position: sdv::cabin::seat::SEAT_POSITION_TYPE::left, }, sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: back_center_seat_instance_id.to_string(), + seat_row: 2, + seat_position: sdv::cabin::seat::SEAT_POSITION_TYPE::center, }, sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: back_right_seat_instance_id.to_string(), + seat_row: 2, + seat_position: sdv::cabin::seat::SEAT_POSITION_TYPE::right, }, ], ..Default::default() @@ -154,7 +170,8 @@ fn create_request_state() -> RequestState { // Create the vehicle. let vehicle_instance_id = format!("{}", uuid::Uuid::new_v4()); - let _vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { + let vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { + instance_id: vehicle_instance_id.clone(), cabin: vec![sdv::vehicle::cabin::RELATIONSHIP_TYPE { instance_id: cabin_instance_id }], ..Default::default() }; @@ -163,7 +180,7 @@ fn create_request_state() -> RequestState { InstanceData { model_id: sdv::vehicle::ID.to_string(), description: sdv::vehicle::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&_vehicle_value).unwrap(), + serialized_value: serde_json::to_string(&vehicle_value).unwrap(), }, ); From 3144fec40a923cdd7ad1bfd9b5bf79ab296f3296 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:33:37 -0700 Subject: [PATCH 008/109] Digital Twin Graph --- digital-twin-model/src/sdv_v1.rs | 7 +-- samples/graph/consumer/src/main.rs | 59 ++++++++++++++----- .../graph/seat_massager_provider/src/main.rs | 58 ++++++++++-------- .../graph/vehicle_core_provider/src/main.rs | 12 ++-- 4 files changed, 88 insertions(+), 48 deletions(-) diff --git a/digital-twin-model/src/sdv_v1.rs b/digital-twin-model/src/sdv_v1.rs index 019dad7e..4cb90e16 100644 --- a/digital-twin-model/src/sdv_v1.rs +++ b/digital-twin-model/src/sdv_v1.rs @@ -161,7 +161,7 @@ pub mod basic_airbag_seat_massager { #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, #[serde(rename = "@id")] - pub instance_id: String, + pub instance_id: String, #[serde(rename = "@type")] #[derivative(Default( value = "crate::sdv_v1::basic_airbag_seat_massager::ID.to_string()" @@ -215,8 +215,7 @@ pub mod cabin { #[derive(derivative::Derivative)] #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - #[derive(PartialEq)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, PartialEq)] pub enum SEAT_POSITION_TYPE { #[derivative(Default)] /// No value @@ -394,7 +393,7 @@ pub mod premium_airbag_seat_massager { #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, #[serde(rename = "@id")] - pub instance_id: String, + pub instance_id: String, #[serde(rename = "@type")] #[derivative(Default( value = "crate::sdv_v1::premium_airbag_seat_massager::ID.to_string()" diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index cec096e3..3bc82aa5 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -24,7 +24,11 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // Connect to the digital twin graph service. let client = Retry::spawn(retry_strategy.clone(), || async { - DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await.map_err(|err_msg| format!("Unable to connect to the digital twin graph service due to: {err_msg}")) + DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await.map_err( + |err_msg| { + format!("Unable to connect to the digital twin graph service due to: {err_msg}") + }, + ) }) .await?; @@ -35,7 +39,10 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul let request: FindRequest = FindRequest { model_id: sdv::vehicle::ID.to_string() }; - client.find(request).await.map_err(|err_msg| format!("Unable to call find due to: {err_msg}")) + client + .find(request) + .await + .map_err(|err_msg| format!("Unable to call find due to: {err_msg}")) }) .await?; let find_vehicle_response_inner = find_vehicle_response.into_inner(); @@ -45,12 +52,13 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul } // For now, we will just use the first vehicle instance. - let vehicle: sdv::vehicle::TYPE = serde_json::from_str(&find_vehicle_response_inner.values[0]).unwrap(); + let vehicle: sdv::vehicle::TYPE = + serde_json::from_str(&find_vehicle_response_inner.values[0]).unwrap(); // info!("The vehicle is: {:?}", vehicle); info!("The vehicle's instance id is: {:?}", vehicle.instance_id); if vehicle.cabin.is_empty() { return Err("The vehicle does not have a cabin".to_string()); - } + } let cabin_instance_id = vehicle.cabin[0].instance_id.clone(); info!("The cabin's instance id is: {:?}", cabin_instance_id); @@ -59,9 +67,13 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - let request: GetRequest = GetRequest { instance_id: cabin_instance_id.clone(), member_path: "".to_string() }; + let request: GetRequest = + GetRequest { instance_id: cabin_instance_id.clone(), member_path: "".to_string() }; - client.get(request.clone()).await.map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + client + .get(request.clone()) + .await + .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) }) .await?; let get_cabin_response_inner = get_cabin_response.into_inner(); @@ -70,7 +82,9 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response_inner.value).unwrap(); // info!("The cabin is: {:?}", cabin); for seat_relationship in cabin.seat.iter() { - if (seat_relationship.seat_row == 1) && (seat_relationship.seat_position == sdv::cabin::seat::SEAT_POSITION_TYPE::left) { + if (seat_relationship.seat_row == 1) + && (seat_relationship.seat_position == sdv::cabin::seat::SEAT_POSITION_TYPE::left) + { info!("The front left seat's instance id is: {:?}", seat_relationship.instance_id); front_left_seat_instance_id_option = Some(seat_relationship.instance_id.clone()); } @@ -87,9 +101,15 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - let request: GetRequest = GetRequest { instance_id: front_left_seat_instance_id.clone(), member_path: "".to_string() }; + let request: GetRequest = GetRequest { + instance_id: front_left_seat_instance_id.clone(), + member_path: "".to_string(), + }; - client.get(request.clone()).await.map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + client + .get(request.clone()) + .await + .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) }) .await?; let get_seat_response_inner = get_seat_response.into_inner(); @@ -107,19 +127,30 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - let request: GetRequest = GetRequest { instance_id: seat_massager_instance_id.clone(), member_path: "".to_string() }; + let request: GetRequest = GetRequest { + instance_id: seat_massager_instance_id.clone(), + member_path: "".to_string(), + }; - client.get(request.clone()).await.map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + client + .get(request.clone()) + .await + .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) }) .await?; let get_seat_massager_response_inner = get_seat_massager_response.into_inner(); - let seat_massager_json: serde_json::Value = serde_json::from_str(&get_seat_massager_response_inner.value).unwrap(); + let seat_massager_json: serde_json::Value = + serde_json::from_str(&get_seat_massager_response_inner.value).unwrap(); if seat_massager_json["@type"] != sdv::premium_airbag_seat_massager::ID { - return Err(format!("The seat massager instance is not of the expected model, instead it is a {0}", seat_massager_json["@type"])); + return Err(format!( + "The seat massager instance is not of the expected model, instead it is a {0}", + seat_massager_json["@type"] + )); } - let seat_massager: sdv::premium_airbag_seat_massager::TYPE = serde_json::from_value(seat_massager_json.clone()).unwrap(); + let seat_massager: sdv::premium_airbag_seat_massager::TYPE = + serde_json::from_value(seat_massager_json.clone()).unwrap(); info!("The seat massager is: {:?}", seat_massager); diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 15ca8e51..4437b4d3 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -30,10 +30,11 @@ fn create_request_state() -> RequestState { // Create the seat massagers. let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); - let front_left_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { - instance_id: front_left_airbag_seat_massager_instance_id.clone(), - ..Default::default() - }; + let front_left_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = + sdv::premium_airbag_seat_massager::TYPE { + instance_id: front_left_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; result.instance_map.insert( front_left_airbag_seat_massager_instance_id.to_string(), InstanceData { @@ -41,13 +42,15 @@ fn create_request_state() -> RequestState { description: sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), serialized_value: serde_json::to_string(&front_left_airbag_seat_massager).unwrap(), }, - ); + ); - let front_right_airbag_seat_massager_instance_id = "front_right_airbag_seat_massager".to_string(); - let front_right_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { - instance_id: front_right_airbag_seat_massager_instance_id.clone(), - ..Default::default() - }; + let front_right_airbag_seat_massager_instance_id = + "front_right_airbag_seat_massager".to_string(); + let front_right_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = + sdv::premium_airbag_seat_massager::TYPE { + instance_id: front_right_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; result.instance_map.insert( front_right_airbag_seat_massager_instance_id.to_string(), InstanceData { @@ -58,10 +61,11 @@ fn create_request_state() -> RequestState { ); let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); - let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { - instance_id: back_left_airbag_seat_massager_instance_id.clone(), - ..Default::default() - }; + let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = + sdv::basic_airbag_seat_massager::TYPE { + instance_id: back_left_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; result.instance_map.insert( back_left_airbag_seat_massager_instance_id.to_string(), InstanceData { @@ -69,13 +73,15 @@ fn create_request_state() -> RequestState { description: sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), serialized_value: serde_json::to_string(&back_left_airbag_seat_massager).unwrap(), }, - ); + ); - let back_center_airbag_seat_massager_instance_id = "back_center_airbag_seat_massager".to_string(); - let back_center_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { - instance_id: back_center_airbag_seat_massager_instance_id.clone(), - ..Default::default() - }; + let back_center_airbag_seat_massager_instance_id = + "back_center_airbag_seat_massager".to_string(); + let back_center_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = + sdv::basic_airbag_seat_massager::TYPE { + instance_id: back_center_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; result.instance_map.insert( back_center_airbag_seat_massager_instance_id.to_string(), InstanceData { @@ -86,10 +92,11 @@ fn create_request_state() -> RequestState { ); let back_right_airbag_seat_massager_instance_id = "back_right_airbag_seat_massager".to_string(); - let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { - instance_id: back_right_airbag_seat_massager_instance_id.clone(), - ..Default::default() - }; + let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = + sdv::basic_airbag_seat_massager::TYPE { + instance_id: back_right_airbag_seat_massager_instance_id.clone(), + ..Default::default() + }; result.instance_map.insert( back_right_airbag_seat_massager_instance_id.to_string(), InstanceData { @@ -154,7 +161,8 @@ async fn main() -> Result<(), Box> { info!("The Seat Massager Provider has started."); - let settings = provider_config::load_settings_with_config_filename("seat_massager_provider_settings"); + let settings = + provider_config::load_settings_with_config_filename("seat_massager_provider_settings"); let provider_authority = settings.provider_authority; diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/graph/vehicle_core_provider/src/main.rs index c262eb38..e577a7fc 100644 --- a/samples/graph/vehicle_core_provider/src/main.rs +++ b/samples/graph/vehicle_core_provider/src/main.rs @@ -29,13 +29,15 @@ fn create_request_state() -> RequestState { // Seat massager ids. - let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); + let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); - let front_right_airbag_seat_massager_instance_id = "front_right_airbag_seat_massager".to_string(); + let front_right_airbag_seat_massager_instance_id = + "front_right_airbag_seat_massager".to_string(); - let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); + let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); - let back_center_airbag_seat_massager_instance_id = "back_center_airbag_seat_massager".to_string(); + let back_center_airbag_seat_massager_instance_id = + "back_center_airbag_seat_massager".to_string(); let back_right_airbag_seat_massager_instance_id = "back_right_airbag_seat_massager".to_string(); @@ -139,7 +141,7 @@ fn create_request_state() -> RequestState { sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: front_right_seat_instance_id.to_string(), seat_row: 1, - seat_position: sdv::cabin::seat::SEAT_POSITION_TYPE::right, + seat_position: sdv::cabin::seat::SEAT_POSITION_TYPE::right, }, sdv::cabin::seat::RELATIONSHIP_TYPE { instance_id: back_left_seat_instance_id.to_string(), From 2cdc650d09dfc47eaf663ec15e3dac9e99e9d6c3 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sat, 20 Apr 2024 17:56:47 -0700 Subject: [PATCH 009/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 2 +- samples/graph/consumer/src/main.rs | 91 +++++--- .../graph/seat_massager_provider/src/main.rs | 5 +- .../src/request_impl.rs | 221 +++++++++++++----- 4 files changed, 227 insertions(+), 92 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 28fba474..fbd23329 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -234,7 +234,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { } info!( - "Received an answer request. The ask_id is '{}'. The payload is '{}", + "Received an answer request. The ask_id is '{}'. The payload is '{}'", answer_request.ask_id, answer_request.payload ); diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 3bc82aa5..119d4e16 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -4,15 +4,18 @@ use digital_twin_model::sdv_v1 as sdv; use env_logger::{Builder, Target}; -use log::{debug, info, LevelFilter}; +use log::{info, LevelFilter}; +use rand::rngs::StdRng; +use rand::Rng; // trait needed for gen_range +use rand::SeedableRng; // trait needed to initialize StdRng use samples_common::consumer_config; use samples_common::utils::retrieve_invehicle_digital_twin_uri; use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::digital_twin_graph_client::DigitalTwinGraphClient; -use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::{FindRequest, GetRequest}; +use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::{FindRequest, GetRequest, InvokeRequest}; use tokio_retry::Retry; use tokio_retry::strategy::{ExponentialBackoff, jitter}; -/// Start the seat massage steps. +/// Series of interactions with the digital twin. /// /// # Arguments /// `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. @@ -32,13 +35,10 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul }) .await?; - // Find vehicle instances. + // Find all vehicle instances. let find_vehicle_response = Retry::spawn(retry_strategy.clone(), || async { - // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - let request: FindRequest = FindRequest { model_id: sdv::vehicle::ID.to_string() }; - client .find(request) .await @@ -46,7 +46,6 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul }) .await?; let find_vehicle_response_inner = find_vehicle_response.into_inner(); - if find_vehicle_response_inner.values.is_empty() { return Err("Unable to find vehicle instances".to_string()); } @@ -54,8 +53,9 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // For now, we will just use the first vehicle instance. let vehicle: sdv::vehicle::TYPE = serde_json::from_str(&find_vehicle_response_inner.values[0]).unwrap(); - // info!("The vehicle is: {:?}", vehicle); info!("The vehicle's instance id is: {:?}", vehicle.instance_id); + + // Get the cabin instance id. if vehicle.cabin.is_empty() { return Err("The vehicle does not have a cabin".to_string()); } @@ -64,23 +64,22 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // Get the cabin instance. let get_cabin_response = Retry::spawn(retry_strategy.clone(), || async { - // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - let request: GetRequest = GetRequest { instance_id: cabin_instance_id.clone(), member_path: "".to_string() }; - client .get(request.clone()) .await - .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + .map_err(|err_msg| format!("Unable to get the cabin instance due to: {err_msg}")) }) .await?; let get_cabin_response_inner = get_cabin_response.into_inner(); - let mut front_left_seat_instance_id_option: Option = None; + // Deserialize the cabin instance. let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response_inner.value).unwrap(); - // info!("The cabin is: {:?}", cabin); + + // Find the front left seat instance id. + let mut front_left_seat_instance_id_option: Option = None; for seat_relationship in cabin.seat.iter() { if (seat_relationship.seat_row == 1) && (seat_relationship.seat_position == sdv::cabin::seat::SEAT_POSITION_TYPE::left) @@ -89,33 +88,30 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul front_left_seat_instance_id_option = Some(seat_relationship.instance_id.clone()); } } - if front_left_seat_instance_id_option.is_none() { return Err("The front left seat is not found".to_string()); } - let front_left_seat_instance_id = front_left_seat_instance_id_option.unwrap(); // Get the seat instance. let get_seat_response = Retry::spawn(retry_strategy.clone(), || async { - // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - let request: GetRequest = GetRequest { instance_id: front_left_seat_instance_id.clone(), member_path: "".to_string(), }; - client .get(request.clone()) .await - .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + .map_err(|err_msg| format!("Unable to get the seat instance due to: {err_msg}")) }) .await?; let get_seat_response_inner = get_seat_response.into_inner(); + // Deserialize the seat instance. let seat: sdv::seat::TYPE = serde_json::from_str(&get_seat_response_inner.value).unwrap(); - // info!("The seat is: {:?}", seat); + + // Get the seat massager instance id. if seat.seat_massager.is_empty() { return Err("The seat does not have a seat massage".to_string()); } @@ -124,35 +120,62 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // Get the seat massager instance. let get_seat_massager_response = Retry::spawn(retry_strategy.clone(), || async { - // Make a local mutable copy for use only within this closure body. let mut client = client.clone(); - let request: GetRequest = GetRequest { instance_id: seat_massager_instance_id.clone(), member_path: "".to_string(), }; - - client - .get(request.clone()) - .await - .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + client.get(request.clone()).await.map_err(|err_msg| { + format!("Unable to get the seat massager instance due to: {err_msg}") + }) }) .await?; let get_seat_massager_response_inner = get_seat_massager_response.into_inner(); - let seat_massager_json: serde_json::Value = serde_json::from_str(&get_seat_massager_response_inner.value).unwrap(); - if seat_massager_json["@type"] != sdv::premium_airbag_seat_massager::ID { return Err(format!( "The seat massager instance is not of the expected model, instead it is a {0}", seat_massager_json["@type"] )); } - let seat_massager: sdv::premium_airbag_seat_massager::TYPE = + let _seat_massager: sdv::premium_airbag_seat_massager::TYPE = serde_json::from_value(seat_massager_json.clone()).unwrap(); - info!("The seat massager is: {:?}", seat_massager); + // Invoke the perform_step call on the seat massager instance. + let _perform_step_response = Retry::spawn(retry_strategy.clone(), || async { + let mut client = client.clone(); + + // Randomly generate the airbag adjustment field values. + let mut rng = StdRng::from_entropy(); + let airbag_identifier = rng.gen_range(1..=15); + let inflation_level = rng.gen_range(1..=10); + let inflation_duration_in_seconds = rng.gen_range(1..=5); + + let request_payload: sdv::airbag_seat_massager::perform_step::request::TYPE = + sdv::airbag_seat_massager::perform_step::request::TYPE { + step: vec![sdv::airbag_seat_massager::airbag_adjustment::TYPE { + airbag_identifier, + inflation_level, + inflation_duration_in_seconds, + }], + ..Default::default() + }; + + let request_payload_json: String = serde_json::to_string_pretty(&request_payload).unwrap(); + + let request: InvokeRequest = InvokeRequest { + instance_id: seat_massager_instance_id.clone(), + member_path: sdv::airbag_seat_massager::perform_step::NAME.to_string(), + request_payload: request_payload_json, + }; + + client + .invoke(request.clone()) + .await + .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) + }) + .await?; Ok(()) } @@ -174,7 +197,7 @@ async fn main() -> Result<(), Box> { interact_with_digital_twin(invehicle_digital_twin_uri).await?; - debug!("The Consumer has completed."); + info!("The Consumer has completed."); Ok(()) } diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 4437b4d3..13e6d91e 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -130,7 +130,10 @@ async fn register_seat_massagers( let endpoint_info = EndpointInfo { protocol: digital_twin_protocol::GRPC.to_string(), - operations: vec![digital_twin_operation::GET.to_string()], + operations: vec![ + digital_twin_operation::GET.to_string(), + digital_twin_operation::INVOKE.to_string(), + ], uri: provider_uri.to_string(), context: instance_id.to_string(), }; diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index 9085e421..74ffc8b4 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -3,7 +3,8 @@ // SPDX-License-Identifier: MIT use digital_twin_graph::TargetedPayload; -use log::{debug, error, info, warn}; +use digital_twin_model::sdv_v1 as sdv; +use log::{debug, info, warn}; use parking_lot::{Mutex, MutexGuard}; use samples_common::constants::digital_twin_operation; use samples_protobuf_data_access::async_rpc::v1::request::{ @@ -14,6 +15,9 @@ use samples_protobuf_data_access::async_rpc::v1::respond::{ }; use std::collections::HashMap; use std::sync::Arc; +use tokio_retry::strategy::{jitter, ExponentialBackoff}; +use tokio_retry::Retry; +use tonic; #[derive(Clone, Debug, Default)] pub struct InstanceData { @@ -32,6 +36,156 @@ pub struct RequestImpl { pub state: Arc>, } +impl RequestImpl { + /// Get implementation. + /// + /// # Arguments + /// * `respond_uri` - Respond URI. + /// * `ask_id` - Ask ID. + /// * `targeted_payload_json` - Targeted payload. + async fn get( + &self, + respond_uri: String, + ask_id: String, + targeted_payload_json: TargetedPayload, + ) -> Result, tonic::Status> { + if !targeted_payload_json.payload.is_empty() { + return Err(tonic::Status::invalid_argument( + "Unexpected payload, it should be empty".to_string(), + )); + } + + let state: Arc> = self.state.clone(); + + // Define a retry strategy. + let retry_strategy = ExponentialBackoff::from_millis(100) + .map(jitter) // add jitter to delays + .take(10); // limit to 10 retries + + // Asynchronously perform the step. + tokio::spawn(async move { + let response_payload_json: String = { + let instance_data: InstanceData = { + let lock: MutexGuard = state.lock(); + match lock.instance_map.get(&targeted_payload_json.instance_id) { + Some(instance_data) => instance_data.clone(), + None => { + return Err(format!( + "Instance not found for instance id '{}'", + targeted_payload_json.instance_id + )); + } + } + }; + + instance_data.serialized_value.clone() + }; + + Retry::spawn(retry_strategy, || async { + // Connect to the consumer. + let mut client = RespondClient::connect(respond_uri.clone()) + .await + .map_err(|err_msg| format!("Unable to connect due to: {err_msg}"))?; + + // Send the answer to the consumer. + let answer_request = tonic::Request::new(AnswerRequest { + ask_id: ask_id.clone(), + payload: response_payload_json.clone(), + }); + client + .answer(answer_request) + .await + .map_err(|status| format!("Answer failed: {status:?}")) + }) + .await + }); + + Ok(tonic::Response::new(AskResponse {})) + } + + /// Invoke implementation. + /// + /// # Arguments + /// * `respond_uri` - Respond URI. + /// * `ask_id` - Ask ID. + /// * `targeted_payload_json` - Targeted payload. + async fn invoke( + &self, + respond_uri: String, + ask_id: String, + targeted_payload_json: TargetedPayload, + ) -> Result, tonic::Status> { + if targeted_payload_json.payload.is_empty() { + return Err(tonic::Status::invalid_argument( + "Unexpected payload, it should NOT be empty".to_string(), + )); + } + + let state: Arc> = self.state.clone(); + + // Define a retry strategy. + let retry_strategy = ExponentialBackoff::from_millis(100) + .map(jitter) // add jitter to delays + .take(10); // limit to 10 retries + + // Asynchronously perform the step. + tokio::spawn(async move { + let instance_value_json_str: String = { + let instance_data: InstanceData = { + let lock: MutexGuard = state.lock(); + match lock.instance_map.get(&targeted_payload_json.instance_id) { + Some(instance_data) => instance_data.clone(), + None => { + return Err(format!( + "Instance not found for instance id '{}'", + targeted_payload_json.instance_id + )); + } + } + }; + + instance_data.serialized_value.clone() + }; + + // Check that the instance can handle the operation. + let instance_json: serde_json::Value = + serde_json::from_str(&instance_value_json_str).unwrap(); + + let mut supported_method: bool = false; + if instance_json["@type"] == sdv::premium_airbag_seat_massager::ID { + supported_method = true; + } + + if !supported_method { + return Err(format!( + "The instance with the instance id '{}' does not support the operation '{}'", + targeted_payload_json.instance_id, targeted_payload_json.operation + )); + } + + Retry::spawn(retry_strategy, || async { + // Connect to the consumer. + let mut client = RespondClient::connect(respond_uri.clone()) + .await + .map_err(|err_msg| format!("Unable to connect due to: {err_msg}"))?; + + // Send the answer to the consumer. + let answer_request = tonic::Request::new(AnswerRequest { + ask_id: ask_id.clone(), + payload: "".to_string(), + }); + client + .answer(answer_request) + .await + .map_err(|status| format!("Answer failed: {status:?}")) + }) + .await + }); + + Ok(tonic::Response::new(AskResponse {})) + } +} + #[tonic::async_trait] impl Request for RequestImpl { /// Ask implementation. @@ -54,65 +208,20 @@ impl Request for RequestImpl { // Deserialize the targeted payload. let targeted_payload_json: TargetedPayload = serde_json::from_str(&payload).unwrap(); - info!(" instance_id: {}", targeted_payload_json.instance_id); - info!(" member_path: {}", targeted_payload_json.member_path); - info!(" operation: {}", targeted_payload_json.operation); + debug!(" instance_id: {}", targeted_payload_json.instance_id); + debug!(" member_path: {}", targeted_payload_json.member_path); + debug!(" operation: {}", targeted_payload_json.operation); - // Check to make sure that the targeted operation is a GET. - if targeted_payload_json.operation != digital_twin_operation::GET { - return Err(tonic::Status::invalid_argument(format!( + if targeted_payload_json.operation == digital_twin_operation::GET { + self.get(respond_uri, ask_id, targeted_payload_json).await + } else if targeted_payload_json.operation == digital_twin_operation::INVOKE { + self.invoke(respond_uri, ask_id, targeted_payload_json).await + } else { + Err(tonic::Status::invalid_argument(format!( "Unexpected operation '{}'", targeted_payload_json.operation - ))); + ))) } - - if !targeted_payload_json.payload.is_empty() { - return Err(tonic::Status::invalid_argument(format!( - "Unexpected payload, it should be empty, not '{}'", - targeted_payload_json.payload - ))); - } - - let state: Arc> = self.state.clone(); - - // Asynchronously perform the step. - tokio::spawn(async move { - let instance_data: InstanceData = { - let lock: MutexGuard = state.lock(); - match lock.instance_map.get(&targeted_payload_json.instance_id) { - Some(instance_data) => instance_data.clone(), - None => { - error!( - "Instance not found for instance id '{}'", - targeted_payload_json.instance_id - ); - return; - } - } - }; - - let response_payload_json = instance_data.serialized_value.clone(); - - let client_result = RespondClient::connect(respond_uri).await; - if let Err(error_message) = client_result { - error!("Unable to connect due to {error_message}"); - return; - } - let mut client = client_result.unwrap(); - - let answer_request = - tonic::Request::new(AnswerRequest { ask_id, payload: response_payload_json }); - - // Send the answer. - let response = client.answer(answer_request).await; - if let Err(status) = response { - error!("Answer failed: {status:?}"); - } - }); - - debug!("Completed the ask request."); - - Ok(tonic::Response::new(AskResponse {})) } /// Notify implementation. From 084f896e09b5382dd12952c612d10fa8a4a50980 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sat, 20 Apr 2024 18:10:30 -0700 Subject: [PATCH 010/109] Digital Twin Graph --- samples/graph/seat_massager_provider/src/request_impl.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index 74ffc8b4..b892e3af 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -17,7 +17,6 @@ use std::collections::HashMap; use std::sync::Arc; use tokio_retry::strategy::{jitter, ExponentialBackoff}; use tokio_retry::Retry; -use tonic; #[derive(Clone, Debug, Default)] pub struct InstanceData { From bbab189a84748336fca914aa8deee23ef4d64a98 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 21 Apr 2024 21:46:39 -0700 Subject: [PATCH 011/109] Digital Twin Graph --- samples/graph/consumer/src/main.rs | 248 ++++++++++++++++++----------- 1 file changed, 152 insertions(+), 96 deletions(-) diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 119d4e16..2718c51e 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -11,36 +11,57 @@ use rand::SeedableRng; // trait needed to initialize StdRng use samples_common::consumer_config; use samples_common::utils::retrieve_invehicle_digital_twin_uri; use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::digital_twin_graph_client::DigitalTwinGraphClient; -use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::{FindRequest, GetRequest, InvokeRequest}; +use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::{FindRequest, FindResponse, GetRequest, GetResponse, InvokeRequest, InvokeResponse}; use tokio_retry::Retry; use tokio_retry::strategy::{ExponentialBackoff, jitter}; -/// Series of interactions with the digital twin. +const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; +const MAX_RETRIES: usize = 10; + +/// Connect to the digital twin graph service. /// /// # Arguments -/// `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. -async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Result<(), String> { +/// - `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. +async fn connect_to_digital_twin_graph_service( + invehicle_digital_twin_uri: String, +) -> Result, String> { // Define a retry strategy. - let retry_strategy = ExponentialBackoff::from_millis(100) + let retry_strategy = ExponentialBackoff::from_millis(BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays - .take(10); // limit to 10 retries + .take(MAX_RETRIES); // Connect to the digital twin graph service. - let client = Retry::spawn(retry_strategy.clone(), || async { - DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await.map_err( - |err_msg| { - format!("Unable to connect to the digital twin graph service due to: {err_msg}") - }, - ) - }) - .await?; + let client: DigitalTwinGraphClient = + Retry::spawn(retry_strategy.clone(), || async { + DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await.map_err( + |err_msg| { + format!("Unable to connect to the digital twin graph service due to: {err_msg}") + }, + ) + }) + .await?; - // Find all vehicle instances. + Ok(client) +} + +/// Find all instances of a model. +/// +/// # Arguments +/// - `client` - The digital twin graph client. +/// - `model_id` - The model id. +async fn find( + client: DigitalTwinGraphClient, + model_id: String, +) -> Result { + let retry_strategy = ExponentialBackoff::from_millis(BACKOFF_BASE_DURATION_IN_MILLIS) + .map(jitter) // add jitter to delays + .take(MAX_RETRIES); + + let request = FindRequest { model_id: model_id.clone() }; let find_vehicle_response = Retry::spawn(retry_strategy.clone(), || async { let mut client = client.clone(); - let request: FindRequest = FindRequest { model_id: sdv::vehicle::ID.to_string() }; client - .find(request) + .find(request.clone()) .await .map_err(|err_msg| format!("Unable to call find due to: {err_msg}")) }) @@ -50,10 +71,85 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul return Err("Unable to find vehicle instances".to_string()); } + Ok(find_vehicle_response_inner) +} + +/// Get an instance. +/// +/// # Arguments +/// - `client` - The digital twin graph client. +/// - `instance_id` - The instance id. +/// - `member_path` - The member path. +async fn get( + client: DigitalTwinGraphClient, + instance_id: String, + member_path: String, +) -> Result { + let retry_strategy = ExponentialBackoff::from_millis(BACKOFF_BASE_DURATION_IN_MILLIS) + .map(jitter) // add jitter to delays + .take(MAX_RETRIES); + + let request = GetRequest { instance_id: instance_id.clone(), member_path: member_path.clone() }; + let get_response = Retry::spawn(retry_strategy.clone(), || async { + let mut client = client.clone(); + client + .get(request.clone()) + .await + .map_err(|err_msg| format!("Unable to get the instance due to: {err_msg}")) + }) + .await?; + + Ok(get_response.into_inner()) +} + +/// Invoke an instance's operation. +/// +/// # Arguments +/// - `client` - The digital twin graph client. +/// - `instance_id` - The instance id. +/// - `member_path` - The member path. +/// - `request_payload` - The request payload. +async fn invoke( + client: DigitalTwinGraphClient, + instance_id: String, + member_path: String, + request_payload: String, +) -> Result { + let request = InvokeRequest { + instance_id: instance_id.clone(), + member_path: member_path.clone(), + request_payload: request_payload.clone(), + }; + + let mut client = client.clone(); + + let invoke_response = client + .invoke(request.clone()) + .await + .map_err(|err_msg| format!("Unable to invoke the instance due to: {err_msg}"))?; + + Ok(invoke_response.into_inner()) +} + +/// Perform a series of interactions with a vehicle digital twin. +/// +/// # Arguments +/// - `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. +async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Result<(), String> { + // Connect to the digital twin graph service. + let client: DigitalTwinGraphClient = + connect_to_digital_twin_graph_service(invehicle_digital_twin_uri.clone()).await?; + + // Find all vehicle instances. + let find_vehicle_response: FindResponse = + find(client.clone(), sdv::vehicle::ID.to_string()).await?; + if find_vehicle_response.values.is_empty() { + return Err("Unable to find vehicle instances".to_string()); + } // For now, we will just use the first vehicle instance. let vehicle: sdv::vehicle::TYPE = - serde_json::from_str(&find_vehicle_response_inner.values[0]).unwrap(); - info!("The vehicle's instance id is: {:?}", vehicle.instance_id); + serde_json::from_str(&find_vehicle_response.values[0]).unwrap(); + info!("The vehicle's instance id is: {}", vehicle.instance_id); // Get the cabin instance id. if vehicle.cabin.is_empty() { @@ -61,24 +157,13 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul } let cabin_instance_id = vehicle.cabin[0].instance_id.clone(); info!("The cabin's instance id is: {:?}", cabin_instance_id); - // Get the cabin instance. - let get_cabin_response = Retry::spawn(retry_strategy.clone(), || async { - let mut client = client.clone(); - let request: GetRequest = - GetRequest { instance_id: cabin_instance_id.clone(), member_path: "".to_string() }; - client - .get(request.clone()) - .await - .map_err(|err_msg| format!("Unable to get the cabin instance due to: {err_msg}")) - }) - .await?; - let get_cabin_response_inner = get_cabin_response.into_inner(); - + let get_cabin_response: GetResponse = + get(client.clone(), cabin_instance_id.clone(), "".to_string()).await?; // Deserialize the cabin instance. - let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response_inner.value).unwrap(); + let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response.value).unwrap(); - // Find the front left seat instance id. + // Find the front left seat's instance id. let mut front_left_seat_instance_id_option: Option = None; for seat_relationship in cabin.seat.iter() { if (seat_relationship.seat_row == 1) @@ -89,27 +174,15 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul } } if front_left_seat_instance_id_option.is_none() { - return Err("The front left seat is not found".to_string()); + return Err("The front left seat was not found".to_string()); } let front_left_seat_instance_id = front_left_seat_instance_id_option.unwrap(); // Get the seat instance. - let get_seat_response = Retry::spawn(retry_strategy.clone(), || async { - let mut client = client.clone(); - let request: GetRequest = GetRequest { - instance_id: front_left_seat_instance_id.clone(), - member_path: "".to_string(), - }; - client - .get(request.clone()) - .await - .map_err(|err_msg| format!("Unable to get the seat instance due to: {err_msg}")) - }) - .await?; - let get_seat_response_inner = get_seat_response.into_inner(); - + let get_seat_response: GetResponse = + get(client.clone(), front_left_seat_instance_id.clone(), "".to_string()).await?; // Deserialize the seat instance. - let seat: sdv::seat::TYPE = serde_json::from_str(&get_seat_response_inner.value).unwrap(); + let seat: sdv::seat::TYPE = serde_json::from_str(&get_seat_response.value).unwrap(); // Get the seat massager instance id. if seat.seat_massager.is_empty() { @@ -117,64 +190,47 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul } let seat_massager_instance_id = seat.seat_massager[0].instance_id.clone(); info!("The seat massager's instance id is: {:?}", seat_massager_instance_id); - // Get the seat massager instance. - let get_seat_massager_response = Retry::spawn(retry_strategy.clone(), || async { - let mut client = client.clone(); - let request: GetRequest = GetRequest { - instance_id: seat_massager_instance_id.clone(), - member_path: "".to_string(), - }; - client.get(request.clone()).await.map_err(|err_msg| { - format!("Unable to get the seat massager instance due to: {err_msg}") - }) - }) - .await?; - let get_seat_massager_response_inner = get_seat_massager_response.into_inner(); + let get_seat_massager_response: GetResponse = + get(client.clone(), seat_massager_instance_id.clone(), "".to_string()).await?; + // Deserialize the seat massager instance to a JSON object. let seat_massager_json: serde_json::Value = - serde_json::from_str(&get_seat_massager_response_inner.value).unwrap(); + serde_json::from_str(&get_seat_massager_response.value).unwrap(); + // Check that that the seat massager's modei_id (marked by @type) is the expected model (premium_airbag_seat_massager). if seat_massager_json["@type"] != sdv::premium_airbag_seat_massager::ID { return Err(format!( "The seat massager instance is not of the expected model, instead it is a {0}", seat_massager_json["@type"] )); } + // Let's make sure that we can fully serserialize the seat massager instance. This in only a check and it is optional. let _seat_massager: sdv::premium_airbag_seat_massager::TYPE = serde_json::from_value(seat_massager_json.clone()).unwrap(); - // Invoke the perform_step call on the seat massager instance. - let _perform_step_response = Retry::spawn(retry_strategy.clone(), || async { - let mut client = client.clone(); - - // Randomly generate the airbag adjustment field values. - let mut rng = StdRng::from_entropy(); - let airbag_identifier = rng.gen_range(1..=15); - let inflation_level = rng.gen_range(1..=10); - let inflation_duration_in_seconds = rng.gen_range(1..=5); - - let request_payload: sdv::airbag_seat_massager::perform_step::request::TYPE = - sdv::airbag_seat_massager::perform_step::request::TYPE { - step: vec![sdv::airbag_seat_massager::airbag_adjustment::TYPE { - airbag_identifier, - inflation_level, - inflation_duration_in_seconds, - }], - ..Default::default() - }; - - let request_payload_json: String = serde_json::to_string_pretty(&request_payload).unwrap(); - - let request: InvokeRequest = InvokeRequest { - instance_id: seat_massager_instance_id.clone(), - member_path: sdv::airbag_seat_massager::perform_step::NAME.to_string(), - request_payload: request_payload_json, + // Randomly generate the airbag adjustment field values. + let mut rng = StdRng::from_entropy(); + let airbag_identifier = rng.gen_range(1..=15); + let inflation_level = rng.gen_range(1..=10); + let inflation_duration_in_seconds = rng.gen_range(1..=5); + // Generate the perform_step operation's request payload. + let request_payload: sdv::airbag_seat_massager::perform_step::request::TYPE = + sdv::airbag_seat_massager::perform_step::request::TYPE { + step: vec![sdv::airbag_seat_massager::airbag_adjustment::TYPE { + airbag_identifier, + inflation_level, + inflation_duration_in_seconds, + }], + ..Default::default() }; - - client - .invoke(request.clone()) - .await - .map_err(|err_msg| format!("Unable to call get due to: {err_msg}")) - }) + // Serialize the request payload to a JSON string. + let request_payload_json: String = serde_json::to_string_pretty(&request_payload).unwrap(); + // Invoke the perform_step operation. + let _perform_step_response: InvokeResponse = invoke( + client.clone(), + seat_massager_instance_id.clone(), + sdv::airbag_seat_massager::perform_step::NAME.to_string(), + request_payload_json.clone(), + ) .await?; Ok(()) From 30bdb5637d5aa555eb702d8e19bcce44f7cf6ba1 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:28:12 -0700 Subject: [PATCH 012/109] Digital Twin Graph --- samples/graph/consumer/src/main.rs | 50 +++++++++---------- .../src/request_impl.rs | 11 ++-- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 2718c51e..6563064c 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -21,16 +21,14 @@ const MAX_RETRIES: usize = 10; /// Connect to the digital twin graph service. /// /// # Arguments -/// - `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. +/// * `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. async fn connect_to_digital_twin_graph_service( invehicle_digital_twin_uri: String, ) -> Result, String> { - // Define a retry strategy. let retry_strategy = ExponentialBackoff::from_millis(BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays .take(MAX_RETRIES); - // Connect to the digital twin graph service. let client: DigitalTwinGraphClient = Retry::spawn(retry_strategy.clone(), || async { DigitalTwinGraphClient::connect(invehicle_digital_twin_uri.clone()).await.map_err( @@ -47,8 +45,8 @@ async fn connect_to_digital_twin_graph_service( /// Find all instances of a model. /// /// # Arguments -/// - `client` - The digital twin graph client. -/// - `model_id` - The model id. +/// * `client` - The digital twin graph client. +/// * `model_id` - The model id. async fn find( client: DigitalTwinGraphClient, model_id: String, @@ -58,6 +56,7 @@ async fn find( .take(MAX_RETRIES); let request = FindRequest { model_id: model_id.clone() }; + let find_vehicle_response = Retry::spawn(retry_strategy.clone(), || async { let mut client = client.clone(); client @@ -65,21 +64,18 @@ async fn find( .await .map_err(|err_msg| format!("Unable to call find due to: {err_msg}")) }) - .await?; - let find_vehicle_response_inner = find_vehicle_response.into_inner(); - if find_vehicle_response_inner.values.is_empty() { - return Err("Unable to find vehicle instances".to_string()); - } + .await? + .into_inner(); - Ok(find_vehicle_response_inner) + Ok(find_vehicle_response) } /// Get an instance. /// /// # Arguments -/// - `client` - The digital twin graph client. -/// - `instance_id` - The instance id. -/// - `member_path` - The member path. +/// * `client` - The digital twin graph client. +/// * `instance_id` - The instance id. +/// * `member_path` - The member path. async fn get( client: DigitalTwinGraphClient, instance_id: String, @@ -90,51 +86,55 @@ async fn get( .take(MAX_RETRIES); let request = GetRequest { instance_id: instance_id.clone(), member_path: member_path.clone() }; + let get_response = Retry::spawn(retry_strategy.clone(), || async { let mut client = client.clone(); + client .get(request.clone()) .await .map_err(|err_msg| format!("Unable to get the instance due to: {err_msg}")) }) - .await?; + .await? + .into_inner(); - Ok(get_response.into_inner()) + Ok(get_response) } /// Invoke an instance's operation. /// /// # Arguments -/// - `client` - The digital twin graph client. -/// - `instance_id` - The instance id. -/// - `member_path` - The member path. -/// - `request_payload` - The request payload. +/// * `client` - The digital twin graph client. +/// * `instance_id` - The instance id. +/// * `member_path` - The member path. +/// * `request_payload` - The request payload. async fn invoke( client: DigitalTwinGraphClient, instance_id: String, member_path: String, request_payload: String, ) -> Result { + let mut client = client.clone(); + let request = InvokeRequest { instance_id: instance_id.clone(), member_path: member_path.clone(), request_payload: request_payload.clone(), }; - let mut client = client.clone(); - let invoke_response = client .invoke(request.clone()) .await - .map_err(|err_msg| format!("Unable to invoke the instance due to: {err_msg}"))?; + .map_err(|err_msg| format!("Unable to invoke the instance due to: {err_msg}"))? + .into_inner(); - Ok(invoke_response.into_inner()) + Ok(invoke_response) } /// Perform a series of interactions with a vehicle digital twin. /// /// # Arguments -/// - `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. +/// * `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Result<(), String> { // Connect to the digital twin graph service. let client: DigitalTwinGraphClient = diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index b892e3af..30867a2c 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -36,6 +36,9 @@ pub struct RequestImpl { } impl RequestImpl { + const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + const MAX_RETRIES: usize = 10; + /// Get implementation. /// /// # Arguments @@ -57,9 +60,9 @@ impl RequestImpl { let state: Arc> = self.state.clone(); // Define a retry strategy. - let retry_strategy = ExponentialBackoff::from_millis(100) + let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays - .take(10); // limit to 10 retries + .take(Self::MAX_RETRIES); // Asynchronously perform the step. tokio::spawn(async move { @@ -123,9 +126,9 @@ impl RequestImpl { let state: Arc> = self.state.clone(); // Define a retry strategy. - let retry_strategy = ExponentialBackoff::from_millis(100) + let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays - .take(10); // limit to 10 retries + .take(Self::MAX_RETRIES); // Asynchronously perform the step. tokio::spawn(async move { From ace163d71aff1a56e05d66ff738851e61dae668c Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:50:38 -0700 Subject: [PATCH 013/109] Digital Twin Graph --- samples/graph/consumer/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 6563064c..8ccc71d4 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -62,7 +62,7 @@ async fn find( client .find(request.clone()) .await - .map_err(|err_msg| format!("Unable to call find due to: {err_msg}")) + .map_err(|err_msg| format!("Unable to call find the instances due to: {err_msg}")) }) .await? .into_inner(); @@ -125,7 +125,7 @@ async fn invoke( let invoke_response = client .invoke(request.clone()) .await - .map_err(|err_msg| format!("Unable to invoke the instance due to: {err_msg}"))? + .map_err(|err_msg| format!("Unable to invoke the instance's operation due to: {err_msg}"))? .into_inner(); Ok(invoke_response) From 28529514a190d6762c678d7b122823179f077339 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:24:48 -0700 Subject: [PATCH 014/109] Digital Twin Graph --- samples/graph/consumer/src/main.rs | 2 +- .../graph/seat_massager_provider/src/main.rs | 176 +++++++++++------- .../src/request_impl.rs | 18 +- 3 files changed, 121 insertions(+), 75 deletions(-) diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 8ccc71d4..081f14f7 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -16,7 +16,7 @@ use tokio_retry::Retry; use tokio_retry::strategy::{ExponentialBackoff, jitter}; const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; -const MAX_RETRIES: usize = 10; +const MAX_RETRIES: usize = 100; /// Connect to the digital twin graph service. /// diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 13e6d91e..834b562d 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -6,26 +6,48 @@ mod request_impl; use digital_twin_model::sdv_v1 as sdv; use env_logger::{Builder, Target}; -use log::{debug, info, LevelFilter}; +use log::{info, LevelFilter}; use parking_lot::Mutex; use samples_common::constants::{digital_twin_operation, digital_twin_protocol}; use samples_common::provider_config; -use samples_common::utils::{retrieve_invehicle_digital_twin_uri, retry_async_based_on_status}; +use samples_common::utils::retrieve_invehicle_digital_twin_uri; use samples_protobuf_data_access::async_rpc::v1::request::request_server::RequestServer; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::digital_twin_registry_client::DigitalTwinRegistryClient; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::{ - EndpointInfo, EntityAccessInfo, RegisterRequest, + EndpointInfo, EntityAccessInfo, RegisterRequest, RegisterResponse, }; use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; -use tokio::time::Duration; use tonic::{transport::Server, Status}; +use tokio_retry::Retry; +use tokio_retry::strategy::{ExponentialBackoff, jitter}; -use crate::request_impl::{InstanceData, RequestImpl, RequestState}; +use crate::request_impl::{InstanceData, RequestImpl, ProviderState}; -fn create_request_state() -> RequestState { - let mut result: RequestState = RequestState { instance_map: HashMap::new() }; +const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; +const MAX_RETRIES: usize = 100; + +/// Add an entry to the instance map. +/// # Arguments +/// * `instance_map` - The instance map. +/// * `instance_id` - The instance id. +/// * `model_id` - The model id. +/// * `description` - The description. +/// * `serialized_value` - The serialized value. +fn add_entry_to_instance_map(instance_map: &mut HashMap, instance_id: String, model_id: String, description: String, serialized_value: String) { + instance_map.insert( + instance_id, + InstanceData { + model_id, + description, + serialized_value: serialized_value, + }); +} + +/// Create the provider's state. +fn create_provider_state() -> ProviderState { + let mut result: ProviderState = ProviderState { instance_map: HashMap::new() }; // Create the seat massagers. @@ -35,14 +57,6 @@ fn create_request_state() -> RequestState { instance_id: front_left_airbag_seat_massager_instance_id.clone(), ..Default::default() }; - result.instance_map.insert( - front_left_airbag_seat_massager_instance_id.to_string(), - InstanceData { - model_id: sdv::premium_airbag_seat_massager::ID.to_string(), - description: sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&front_left_airbag_seat_massager).unwrap(), - }, - ); let front_right_airbag_seat_massager_instance_id = "front_right_airbag_seat_massager".to_string(); @@ -51,14 +65,6 @@ fn create_request_state() -> RequestState { instance_id: front_right_airbag_seat_massager_instance_id.clone(), ..Default::default() }; - result.instance_map.insert( - front_right_airbag_seat_massager_instance_id.to_string(), - InstanceData { - model_id: sdv::premium_airbag_seat_massager::ID.to_string(), - description: sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&front_right_airbag_seat_massager).unwrap(), - }, - ); let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = @@ -66,14 +72,6 @@ fn create_request_state() -> RequestState { instance_id: back_left_airbag_seat_massager_instance_id.clone(), ..Default::default() }; - result.instance_map.insert( - back_left_airbag_seat_massager_instance_id.to_string(), - InstanceData { - model_id: sdv::basic_airbag_seat_massager::ID.to_string(), - description: sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_left_airbag_seat_massager).unwrap(), - }, - ); let back_center_airbag_seat_massager_instance_id = "back_center_airbag_seat_massager".to_string(); @@ -82,47 +80,68 @@ fn create_request_state() -> RequestState { instance_id: back_center_airbag_seat_massager_instance_id.clone(), ..Default::default() }; - result.instance_map.insert( - back_center_airbag_seat_massager_instance_id.to_string(), - InstanceData { - model_id: sdv::basic_airbag_seat_massager::ID.to_string(), - description: sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_center_airbag_seat_massager).unwrap(), - }, - ); let back_right_airbag_seat_massager_instance_id = "back_right_airbag_seat_massager".to_string(); let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { instance_id: back_right_airbag_seat_massager_instance_id.clone(), ..Default::default() - }; - result.instance_map.insert( - back_right_airbag_seat_massager_instance_id.to_string(), - InstanceData { - model_id: sdv::basic_airbag_seat_massager::ID.to_string(), - description: sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_right_airbag_seat_massager).unwrap(), - }, - ); + }; + + // Build the instance map. + + add_entry_to_instance_map( + &mut result.instance_map, + front_left_airbag_seat_massager_instance_id.clone(), + sdv::premium_airbag_seat_massager::ID.to_string(), + sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), + serde_json::to_string(&front_left_airbag_seat_massager).unwrap()); + + add_entry_to_instance_map( + &mut result.instance_map, + front_right_airbag_seat_massager_instance_id.clone(), + sdv::premium_airbag_seat_massager::ID.to_string(), + sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), + serde_json::to_string(&front_right_airbag_seat_massager).unwrap()); + + add_entry_to_instance_map( + &mut result.instance_map, + back_left_airbag_seat_massager_instance_id.clone(), + sdv::basic_airbag_seat_massager::ID.to_string(), + sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), + serde_json::to_string(&back_left_airbag_seat_massager).unwrap()); + + add_entry_to_instance_map( + &mut result.instance_map, + back_center_airbag_seat_massager_instance_id.clone(), + sdv::basic_airbag_seat_massager::ID.to_string(), + sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), + serde_json::to_string(&back_center_airbag_seat_massager).unwrap()); + + add_entry_to_instance_map( + &mut result.instance_map, + back_right_airbag_seat_massager_instance_id.clone(), + sdv::basic_airbag_seat_massager::ID.to_string(), + sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), + serde_json::to_string(&back_right_airbag_seat_massager).unwrap()); result } -/// Register the airbag seat massager's massage airbags property. +/// Register the airbag seat massagers. /// /// # Arguments /// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. /// * `provider_uri` - The provider's URI. -/// * `instance_id` - The instance id. +/// * `provider_state` - The provider's state. async fn register_seat_massagers( invehicle_digital_twin_uri: &str, provider_uri: &str, - state: Arc>, + provider_state: Arc>, ) -> Result<(), Status> { let mut entity_access_info_list: Vec = Vec::new(); - state.lock().instance_map.iter().for_each(|(instance_id, instance_data)| { + provider_state.lock().instance_map.iter().for_each(|(instance_id, instance_data)| { info!( "Registering the instance with the instance id '{}' and the model id '{}'", instance_id, instance_data.model_id @@ -148,13 +167,40 @@ async fn register_seat_massagers( entity_access_info_list.push(entity_access_info); }); - let mut client = DigitalTwinRegistryClient::connect(invehicle_digital_twin_uri.to_string()) - .await - .map_err(|e| Status::internal(e.to_string()))?; - let request = tonic::Request::new(RegisterRequest { entity_access_info_list }); - let _response = client.register(request).await?; - - Ok(()) + let retry_strategy = ExponentialBackoff::from_millis(BACKOFF_BASE_DURATION_IN_MILLIS) + .map(jitter) // add jitter to delays + .take(MAX_RETRIES); + + let result: Result = Retry::spawn(retry_strategy.clone(), || async { + let mut client = DigitalTwinRegistryClient::connect(invehicle_digital_twin_uri.to_string()) + .await + .map_err(|e: tonic::transport::Error| Status::internal(e.to_string()))?; + + let request = tonic::Request::new(RegisterRequest { + entity_access_info_list: entity_access_info_list.clone(), + }); + + info!("Sending a register request to the In-Vehicle Digital Twin Service URI {invehicle_digital_twin_uri}"); + + let response: RegisterResponse = client + .register(request) + .await + .map_err(|e| Status::internal(e.to_string()))? + .into_inner(); + Ok(response) + }) + .await; + + match result { + Ok(_) => { + info!("The registration was successful."); + Ok(()) + } + Err(status) => { + info!("The registration failed with the status '{}'", status); + Err(status) + } + } } #[tokio::main] @@ -180,20 +226,14 @@ async fn main() -> Result<(), Box> { // Setup the HTTP server. let addr: SocketAddr = provider_authority.parse()?; - let state = Arc::new(Mutex::new(create_request_state())); - let request_impl = RequestImpl { state: state.clone() }; + let state = Arc::new(Mutex::new(create_provider_state())); + let request_impl = RequestImpl { provider_state: state.clone() }; let server_future = Server::builder().add_service(RequestServer::new(request_impl)).serve(addr); info!("The HTTP server is listening on address '{provider_authority}'"); - info!("Sending a register request to the In-Vehicle Digital Twin Service URI {invehicle_digital_twin_uri}"); - retry_async_based_on_status(30, Duration::from_secs(1), || { - register_seat_massagers(&invehicle_digital_twin_uri, &provider_uri, state.clone()) - }) - .await?; + register_seat_massagers(&invehicle_digital_twin_uri, &provider_uri, state.clone()).await?; server_future.await?; - debug!("The Seat Massager Provider has completed."); - Ok(()) } diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index 30867a2c..56144658 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -18,21 +18,27 @@ use std::sync::Arc; use tokio_retry::strategy::{jitter, ExponentialBackoff}; use tokio_retry::Retry; +/// Instance data. #[derive(Clone, Debug, Default)] pub struct InstanceData { + /// Model ID. pub model_id: String, + /// Description. pub description: String, + /// Serialized value (using JSON-LD as a string). pub serialized_value: String, } +/// The provider's state. #[derive(Debug, Default)] -pub struct RequestState { +pub struct ProviderState { + /// Maps an instance id to its associated instance data. pub instance_map: HashMap, } #[derive(Debug, Default)] pub struct RequestImpl { - pub state: Arc>, + pub provider_state: Arc>, } impl RequestImpl { @@ -57,7 +63,7 @@ impl RequestImpl { )); } - let state: Arc> = self.state.clone(); + let state: Arc> = self.provider_state.clone(); // Define a retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) @@ -68,7 +74,7 @@ impl RequestImpl { tokio::spawn(async move { let response_payload_json: String = { let instance_data: InstanceData = { - let lock: MutexGuard = state.lock(); + let lock: MutexGuard = state.lock(); match lock.instance_map.get(&targeted_payload_json.instance_id) { Some(instance_data) => instance_data.clone(), None => { @@ -123,7 +129,7 @@ impl RequestImpl { )); } - let state: Arc> = self.state.clone(); + let state: Arc> = self.provider_state.clone(); // Define a retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) @@ -134,7 +140,7 @@ impl RequestImpl { tokio::spawn(async move { let instance_value_json_str: String = { let instance_data: InstanceData = { - let lock: MutexGuard = state.lock(); + let lock: MutexGuard = state.lock(); match lock.instance_map.get(&targeted_payload_json.instance_id) { Some(instance_data) => instance_data.clone(), None => { From 0e267fbb5943ddead37aa9c274aed813bd22a9cb Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:25:33 -0700 Subject: [PATCH 015/109] Digital Twin Graph --- .../graph/seat_massager_provider/src/main.rs | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 834b562d..937820bc 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -23,7 +23,7 @@ use tonic::{transport::Server, Status}; use tokio_retry::Retry; use tokio_retry::strategy::{ExponentialBackoff, jitter}; -use crate::request_impl::{InstanceData, RequestImpl, ProviderState}; +use crate::request_impl::{InstanceData, ProviderState, RequestImpl}; const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; const MAX_RETRIES: usize = 100; @@ -35,14 +35,17 @@ const MAX_RETRIES: usize = 100; /// * `model_id` - The model id. /// * `description` - The description. /// * `serialized_value` - The serialized value. -fn add_entry_to_instance_map(instance_map: &mut HashMap, instance_id: String, model_id: String, description: String, serialized_value: String) { +fn add_entry_to_instance_map( + instance_map: &mut HashMap, + instance_id: String, + model_id: String, + description: String, + serialized_value: String, +) { instance_map.insert( - instance_id, - InstanceData { - model_id, - description, - serialized_value: serialized_value, - }); + instance_id, + InstanceData { model_id, description, serialized_value: serialized_value }, + ); } /// Create the provider's state. @@ -86,8 +89,8 @@ fn create_provider_state() -> ProviderState { sdv::basic_airbag_seat_massager::TYPE { instance_id: back_right_airbag_seat_massager_instance_id.clone(), ..Default::default() - }; - + }; + // Build the instance map. add_entry_to_instance_map( @@ -95,35 +98,40 @@ fn create_provider_state() -> ProviderState { front_left_airbag_seat_massager_instance_id.clone(), sdv::premium_airbag_seat_massager::ID.to_string(), sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), - serde_json::to_string(&front_left_airbag_seat_massager).unwrap()); - + serde_json::to_string(&front_left_airbag_seat_massager).unwrap(), + ); + add_entry_to_instance_map( &mut result.instance_map, front_right_airbag_seat_massager_instance_id.clone(), sdv::premium_airbag_seat_massager::ID.to_string(), sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), - serde_json::to_string(&front_right_airbag_seat_massager).unwrap()); - + serde_json::to_string(&front_right_airbag_seat_massager).unwrap(), + ); + add_entry_to_instance_map( &mut result.instance_map, back_left_airbag_seat_massager_instance_id.clone(), sdv::basic_airbag_seat_massager::ID.to_string(), sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), - serde_json::to_string(&back_left_airbag_seat_massager).unwrap()); + serde_json::to_string(&back_left_airbag_seat_massager).unwrap(), + ); add_entry_to_instance_map( &mut result.instance_map, back_center_airbag_seat_massager_instance_id.clone(), sdv::basic_airbag_seat_massager::ID.to_string(), sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), - serde_json::to_string(&back_center_airbag_seat_massager).unwrap()); + serde_json::to_string(&back_center_airbag_seat_massager).unwrap(), + ); add_entry_to_instance_map( &mut result.instance_map, back_right_airbag_seat_massager_instance_id.clone(), sdv::basic_airbag_seat_massager::ID.to_string(), sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), - serde_json::to_string(&back_right_airbag_seat_massager).unwrap()); + serde_json::to_string(&back_right_airbag_seat_massager).unwrap(), + ); result } @@ -181,7 +189,7 @@ async fn register_seat_massagers( }); info!("Sending a register request to the In-Vehicle Digital Twin Service URI {invehicle_digital_twin_uri}"); - + let response: RegisterResponse = client .register(request) .await From 1bfd6f916865de9a2581e8b07ad34ba43d7c9a95 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:35:32 -0700 Subject: [PATCH 016/109] Digital Twin Graph --- samples/graph/seat_massager_provider/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 937820bc..cfe68e08 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -44,7 +44,7 @@ fn add_entry_to_instance_map( ) { instance_map.insert( instance_id, - InstanceData { model_id, description, serialized_value: serialized_value }, + InstanceData { model_id, description, serialized_value }, ); } From 5baf3ce9888c7de1c8ae361ba9cac9cfca324169 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:41:33 -0700 Subject: [PATCH 017/109] Digital Twin Graph --- samples/graph/seat_massager_provider/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index cfe68e08..18497a5c 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -44,7 +44,7 @@ fn add_entry_to_instance_map( ) { instance_map.insert( instance_id, - InstanceData { model_id, description, serialized_value }, + InstanceData { model_id, description, serialized_value, }, ); } From 3001f7e6dc8313017555b9d19e455925e7a0a06b Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:45:48 -0700 Subject: [PATCH 018/109] Digital Twin Graph --- samples/graph/seat_massager_provider/src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 18497a5c..7327b06b 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -42,10 +42,7 @@ fn add_entry_to_instance_map( description: String, serialized_value: String, ) { - instance_map.insert( - instance_id, - InstanceData { model_id, description, serialized_value, }, - ); + instance_map.insert(instance_id, InstanceData { model_id, description, serialized_value }); } /// Create the provider's state. From ec8b1bbef088bc87c3bd9e475b5a9a0c92d4ab73 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 22:23:49 -0700 Subject: [PATCH 019/109] Digital Twin Graph --- .../graph/seat_massager_provider/src/main.rs | 10 +- .../src/request_impl.rs | 32 +-- .../graph/vehicle_core_provider/src/main.rs | 208 +++++++++++------- .../vehicle_core_provider/src/request_impl.rs | 143 +++++++----- 4 files changed, 229 insertions(+), 164 deletions(-) diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 7327b06b..46e40205 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -197,14 +197,8 @@ async fn register_seat_massagers( .await; match result { - Ok(_) => { - info!("The registration was successful."); - Ok(()) - } - Err(status) => { - info!("The registration failed with the status '{}'", status); - Err(status) - } + Ok(_) => Ok(()), + Err(status) => Err(status), } } diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index 56144658..62be5f5c 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -43,21 +43,21 @@ pub struct RequestImpl { impl RequestImpl { const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; - const MAX_RETRIES: usize = 10; + const MAX_RETRIES: usize = 100; /// Get implementation. /// /// # Arguments /// * `respond_uri` - Respond URI. /// * `ask_id` - Ask ID. - /// * `targeted_payload_json` - Targeted payload. + /// * `targeted_payload` - Targeted payload. async fn get( &self, respond_uri: String, ask_id: String, - targeted_payload_json: TargetedPayload, + targeted_payload: TargetedPayload, ) -> Result, tonic::Status> { - if !targeted_payload_json.payload.is_empty() { + if !targeted_payload.payload.is_empty() { return Err(tonic::Status::invalid_argument( "Unexpected payload, it should be empty".to_string(), )); @@ -70,17 +70,18 @@ impl RequestImpl { .map(jitter) // add jitter to delays .take(Self::MAX_RETRIES); - // Asynchronously perform the step. + // Asynchronously perform the get. tokio::spawn(async move { - let response_payload_json: String = { + // Get the answer's payload. + let answer_payload: String = { let instance_data: InstanceData = { let lock: MutexGuard = state.lock(); - match lock.instance_map.get(&targeted_payload_json.instance_id) { + match lock.instance_map.get(&targeted_payload.instance_id) { Some(instance_data) => instance_data.clone(), None => { return Err(format!( "Instance not found for instance id '{}'", - targeted_payload_json.instance_id + targeted_payload.instance_id )); } } @@ -89,6 +90,7 @@ impl RequestImpl { instance_data.serialized_value.clone() }; + // Send the answer to the consumer. Retry::spawn(retry_strategy, || async { // Connect to the consumer. let mut client = RespondClient::connect(respond_uri.clone()) @@ -98,7 +100,7 @@ impl RequestImpl { // Send the answer to the consumer. let answer_request = tonic::Request::new(AnswerRequest { ask_id: ask_id.clone(), - payload: response_payload_json.clone(), + payload: answer_payload.clone(), }); client .answer(answer_request) @@ -116,14 +118,14 @@ impl RequestImpl { /// # Arguments /// * `respond_uri` - Respond URI. /// * `ask_id` - Ask ID. - /// * `targeted_payload_json` - Targeted payload. + /// * `targeted_payload` - Targeted payload. async fn invoke( &self, respond_uri: String, ask_id: String, - targeted_payload_json: TargetedPayload, + targeted_payload: TargetedPayload, ) -> Result, tonic::Status> { - if targeted_payload_json.payload.is_empty() { + if targeted_payload.payload.is_empty() { return Err(tonic::Status::invalid_argument( "Unexpected payload, it should NOT be empty".to_string(), )); @@ -141,12 +143,12 @@ impl RequestImpl { let instance_value_json_str: String = { let instance_data: InstanceData = { let lock: MutexGuard = state.lock(); - match lock.instance_map.get(&targeted_payload_json.instance_id) { + match lock.instance_map.get(&targeted_payload.instance_id) { Some(instance_data) => instance_data.clone(), None => { return Err(format!( "Instance not found for instance id '{}'", - targeted_payload_json.instance_id + targeted_payload.instance_id )); } } @@ -167,7 +169,7 @@ impl RequestImpl { if !supported_method { return Err(format!( "The instance with the instance id '{}' does not support the operation '{}'", - targeted_payload_json.instance_id, targeted_payload_json.operation + targeted_payload.instance_id, targeted_payload.operation )); } diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/graph/vehicle_core_provider/src/main.rs index e577a7fc..f9b82c87 100644 --- a/samples/graph/vehicle_core_provider/src/main.rs +++ b/samples/graph/vehicle_core_provider/src/main.rs @@ -6,28 +6,50 @@ mod request_impl; use digital_twin_model::sdv_v1 as sdv; use env_logger::{Builder, Target}; -use log::{debug, info, LevelFilter}; +use log::{info, LevelFilter}; use parking_lot::Mutex; use samples_common::constants::{digital_twin_operation, digital_twin_protocol}; use samples_common::provider_config; -use samples_common::utils::{retrieve_invehicle_digital_twin_uri, retry_async_based_on_status}; +use samples_common::utils::retrieve_invehicle_digital_twin_uri; use samples_protobuf_data_access::async_rpc::v1::request::request_server::RequestServer; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::digital_twin_registry_client::DigitalTwinRegistryClient; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::{ - EndpointInfo, EntityAccessInfo, RegisterRequest, + EndpointInfo, EntityAccessInfo, RegisterRequest, RegisterResponse, }; use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; -use tokio::time::Duration; use tonic::{transport::Server, Status}; +use tokio_retry::Retry; +use tokio_retry::strategy::{ExponentialBackoff, jitter}; -use crate::request_impl::{InstanceData, RequestImpl, RequestState}; +use crate::request_impl::{InstanceData, ProviderState, RequestImpl}; -fn create_request_state() -> RequestState { - let mut result: RequestState = RequestState { instance_map: HashMap::new() }; +const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; +const MAX_RETRIES: usize = 100; - // Seat massager ids. +/// Add an entry to the instance map. +/// # Arguments +/// * `instance_map` - The instance map. +/// * `instance_id` - The instance id. +/// * `model_id` - The model id. +/// * `description` - The description. +/// * `serialized_value` - The serialized value. +fn add_entry_to_instance_map( + instance_map: &mut HashMap, + instance_id: String, + model_id: String, + description: String, + serialized_value: String, +) { + instance_map.insert(instance_id, InstanceData { model_id, description, serialized_value }); +} + +/// Create the provider's state. +fn create_provider_state() -> ProviderState { + let mut result: ProviderState = ProviderState { instance_map: HashMap::new() }; + + // Create the seat massager ids. let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); @@ -51,14 +73,6 @@ fn create_request_state() -> RequestState { }], ..Default::default() }; - result.instance_map.insert( - front_left_seat_instance_id.to_string(), - InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&front_left_seat).unwrap(), - }, - ); let front_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let front_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { @@ -68,14 +82,6 @@ fn create_request_state() -> RequestState { }], ..Default::default() }; - result.instance_map.insert( - front_right_seat_instance_id.to_string(), - InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&front_right_seat).unwrap(), - }, - ); let back_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { @@ -85,14 +91,6 @@ fn create_request_state() -> RequestState { }], ..Default::default() }; - result.instance_map.insert( - back_left_seat_instance_id.to_string(), - InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_left_seat).unwrap(), - }, - ); let back_center_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_center_seat: sdv::seat::TYPE = sdv::seat::TYPE { @@ -102,14 +100,6 @@ fn create_request_state() -> RequestState { }], ..Default::default() }; - result.instance_map.insert( - back_center_seat_instance_id.to_string(), - InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_center_seat).unwrap(), - }, - ); let back_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let back_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { @@ -119,14 +109,6 @@ fn create_request_state() -> RequestState { }], ..Default::default() }; - result.instance_map.insert( - back_right_seat_instance_id.to_string(), - InstanceData { - model_id: sdv::seat::ID.to_string(), - description: sdv::seat::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&back_right_seat).unwrap(), - }, - ); // Create the cabin. let cabin_instance_id = format!("{}", uuid::Uuid::new_v4()); @@ -161,48 +143,92 @@ fn create_request_state() -> RequestState { ], ..Default::default() }; - result.instance_map.insert( - cabin_instance_id.clone(), - InstanceData { - model_id: sdv::cabin::ID.to_string(), - description: sdv::cabin::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&cabin_value).unwrap(), - }, - ); // Create the vehicle. let vehicle_instance_id = format!("{}", uuid::Uuid::new_v4()); let vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { instance_id: vehicle_instance_id.clone(), - cabin: vec![sdv::vehicle::cabin::RELATIONSHIP_TYPE { instance_id: cabin_instance_id }], + cabin: vec![sdv::vehicle::cabin::RELATIONSHIP_TYPE { + instance_id: cabin_instance_id.clone(), + }], ..Default::default() }; - result.instance_map.insert( - vehicle_instance_id, - InstanceData { - model_id: sdv::vehicle::ID.to_string(), - description: sdv::vehicle::DESCRIPTION.to_string(), - serialized_value: serde_json::to_string(&vehicle_value).unwrap(), - }, + + // Build the instance map. + + add_entry_to_instance_map( + &mut result.instance_map, + front_left_seat_instance_id.clone(), + sdv::seat::ID.to_string(), + sdv::seat::DESCRIPTION.to_string(), + serde_json::to_string(&front_left_seat).unwrap(), + ); + + add_entry_to_instance_map( + &mut result.instance_map, + front_right_seat_instance_id.clone(), + sdv::seat::ID.to_string(), + sdv::seat::DESCRIPTION.to_string(), + serde_json::to_string(&front_right_seat).unwrap(), + ); + + add_entry_to_instance_map( + &mut result.instance_map, + back_left_seat_instance_id.clone(), + sdv::seat::ID.to_string(), + sdv::seat::DESCRIPTION.to_string(), + serde_json::to_string(&back_left_seat).unwrap(), + ); + + add_entry_to_instance_map( + &mut result.instance_map, + back_center_seat_instance_id.clone(), + sdv::seat::ID.to_string(), + sdv::seat::DESCRIPTION.to_string(), + serde_json::to_string(&back_center_seat).unwrap(), + ); + + add_entry_to_instance_map( + &mut result.instance_map, + back_right_seat_instance_id.clone(), + sdv::seat::ID.to_string(), + sdv::seat::DESCRIPTION.to_string(), + serde_json::to_string(&back_right_seat).unwrap(), + ); + + add_entry_to_instance_map( + &mut result.instance_map, + cabin_instance_id, + sdv::cabin::ID.to_string(), + sdv::cabin::DESCRIPTION.to_string(), + serde_json::to_string(&cabin_value).unwrap(), + ); + + add_entry_to_instance_map( + &mut result.instance_map, + vehicle_instance_id.clone(), + sdv::vehicle::ID.to_string(), + sdv::vehicle::DESCRIPTION.to_string(), + serde_json::to_string(&vehicle_value).unwrap(), ); result } -/// Register the airbag seat massager's massage airbags property. +/// Register the vehicle parts /// /// # Arguments /// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. /// * `provider_uri` - The provider's URI. -/// * `instance_id` - The instance id. +/// * `provider_state` - The provider's state. async fn register_vehicle_parts( invehicle_digital_twin_uri: &str, provider_uri: &str, - state: Arc>, + provider_state: Arc>, ) -> Result<(), Status> { let mut entity_access_info_list: Vec = Vec::new(); - state.lock().instance_map.iter().for_each(|(instance_id, instance_data)| { + provider_state.lock().instance_map.iter().for_each(|(instance_id, instance_data)| { info!( "Registering the instance with the instance id '{}' and the model id '{}'", instance_id, instance_data.model_id @@ -225,13 +251,34 @@ async fn register_vehicle_parts( entity_access_info_list.push(entity_access_info); }); - let mut client = DigitalTwinRegistryClient::connect(invehicle_digital_twin_uri.to_string()) - .await - .map_err(|e| Status::internal(e.to_string()))?; - let request = tonic::Request::new(RegisterRequest { entity_access_info_list }); - let _response = client.register(request).await?; + let retry_strategy = ExponentialBackoff::from_millis(BACKOFF_BASE_DURATION_IN_MILLIS) + .map(jitter) // add jitter to delays + .take(MAX_RETRIES); - Ok(()) + let result: Result = Retry::spawn(retry_strategy.clone(), || async { + let mut client = DigitalTwinRegistryClient::connect(invehicle_digital_twin_uri.to_string()) + .await + .map_err(|e: tonic::transport::Error| Status::internal(e.to_string()))?; + + let request = tonic::Request::new(RegisterRequest { + entity_access_info_list: entity_access_info_list.clone(), + }); + + info!("Sending a register request to the In-Vehicle Digital Twin Service URI {invehicle_digital_twin_uri}"); + + let response: RegisterResponse = client + .register(request) + .await + .map_err(|e| Status::internal(e.to_string()))? + .into_inner(); + Ok(response) + }) + .await; + + match result { + Ok(_) => Ok(()), + Err(status) => Err(status), + } } #[tokio::main] @@ -256,20 +303,15 @@ async fn main() -> Result<(), Box> { // Setup the HTTP server. let addr: SocketAddr = provider_authority.parse()?; - let state = Arc::new(Mutex::new(create_request_state())); - let request_impl = RequestImpl { state: state.clone() }; + let provider_state = Arc::new(Mutex::new(create_provider_state())); + let request_impl = RequestImpl { provider_state: provider_state.clone() }; let server_future = Server::builder().add_service(RequestServer::new(request_impl)).serve(addr); info!("The HTTP server is listening on address '{provider_authority}'"); - info!("Sending a register request to the In-Vehicle Digital Twin Service URI {invehicle_digital_twin_uri}"); - retry_async_based_on_status(30, Duration::from_secs(1), || { - register_vehicle_parts(&invehicle_digital_twin_uri, &provider_uri, state.clone()) - }) - .await?; + register_vehicle_parts(&invehicle_digital_twin_uri, &provider_uri, provider_state.clone()) + .await?; server_future.await?; - debug!("The Vehicle Provider has completed."); - Ok(()) } diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs index 9085e421..b67f0cd6 100644 --- a/samples/graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use digital_twin_graph::TargetedPayload; -use log::{debug, error, info, warn}; +use log::{debug, info, warn}; use parking_lot::{Mutex, MutexGuard}; use samples_common::constants::digital_twin_operation; use samples_protobuf_data_access::async_rpc::v1::request::{ @@ -14,6 +14,8 @@ use samples_protobuf_data_access::async_rpc::v1::respond::{ }; use std::collections::HashMap; use std::sync::Arc; +use tokio_retry::strategy::{jitter, ExponentialBackoff}; +use tokio_retry::Retry; #[derive(Clone, Debug, Default)] pub struct InstanceData { @@ -23,13 +25,85 @@ pub struct InstanceData { } #[derive(Debug, Default)] -pub struct RequestState { +pub struct ProviderState { pub instance_map: HashMap, } #[derive(Debug, Default)] pub struct RequestImpl { - pub state: Arc>, + pub provider_state: Arc>, +} +impl RequestImpl { + const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + const MAX_RETRIES: usize = 100; + + /// Get implementation. + /// + /// # Arguments + /// * `respond_uri` - Respond URI. + /// * `ask_id` - Ask ID. + /// * `targeted_payload` - Targeted payload. + async fn get( + &self, + respond_uri: String, + ask_id: String, + targeted_payload: TargetedPayload, + ) -> Result, tonic::Status> { + if !targeted_payload.payload.is_empty() { + return Err(tonic::Status::invalid_argument( + "Unexpected payload, it should be empty".to_string(), + )); + } + + let state: Arc> = self.provider_state.clone(); + + // Define a retry strategy. + let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) + .map(jitter) // add jitter to delays + .take(Self::MAX_RETRIES); + + // Asynchronously perform the get. + tokio::spawn(async move { + // Get the answer's payload. + let answer_payload: String = { + let instance_data: InstanceData = { + let lock: MutexGuard = state.lock(); + match lock.instance_map.get(&targeted_payload.instance_id) { + Some(instance_data) => instance_data.clone(), + None => { + return Err(format!( + "Instance not found for instance id '{}'", + targeted_payload.instance_id + )); + } + } + }; + + instance_data.serialized_value.clone() + }; + + // Send the answer to the consumer. + Retry::spawn(retry_strategy, || async { + // Connect to the consumer. + let mut client = RespondClient::connect(respond_uri.clone()) + .await + .map_err(|err_msg| format!("Unable to connect due to: {err_msg}"))?; + + // Send the answer to the consumer. + let answer_request = tonic::Request::new(AnswerRequest { + ask_id: ask_id.clone(), + payload: answer_payload.clone(), + }); + client + .answer(answer_request) + .await + .map_err(|status| format!("Answer failed: {status:?}")) + }) + .await + }); + + Ok(tonic::Response::new(AskResponse {})) + } } #[tonic::async_trait] @@ -54,65 +128,18 @@ impl Request for RequestImpl { // Deserialize the targeted payload. let targeted_payload_json: TargetedPayload = serde_json::from_str(&payload).unwrap(); - info!(" instance_id: {}", targeted_payload_json.instance_id); - info!(" member_path: {}", targeted_payload_json.member_path); - info!(" operation: {}", targeted_payload_json.operation); + debug!(" instance_id: {}", targeted_payload_json.instance_id); + debug!(" member_path: {}", targeted_payload_json.member_path); + debug!(" operation: {}", targeted_payload_json.operation); - // Check to make sure that the targeted operation is a GET. - if targeted_payload_json.operation != digital_twin_operation::GET { - return Err(tonic::Status::invalid_argument(format!( + if targeted_payload_json.operation == digital_twin_operation::GET { + self.get(respond_uri, ask_id, targeted_payload_json).await + } else { + Err(tonic::Status::invalid_argument(format!( "Unexpected operation '{}'", targeted_payload_json.operation - ))); + ))) } - - if !targeted_payload_json.payload.is_empty() { - return Err(tonic::Status::invalid_argument(format!( - "Unexpected payload, it should be empty, not '{}'", - targeted_payload_json.payload - ))); - } - - let state: Arc> = self.state.clone(); - - // Asynchronously perform the step. - tokio::spawn(async move { - let instance_data: InstanceData = { - let lock: MutexGuard = state.lock(); - match lock.instance_map.get(&targeted_payload_json.instance_id) { - Some(instance_data) => instance_data.clone(), - None => { - error!( - "Instance not found for instance id '{}'", - targeted_payload_json.instance_id - ); - return; - } - } - }; - - let response_payload_json = instance_data.serialized_value.clone(); - - let client_result = RespondClient::connect(respond_uri).await; - if let Err(error_message) = client_result { - error!("Unable to connect due to {error_message}"); - return; - } - let mut client = client_result.unwrap(); - - let answer_request = - tonic::Request::new(AnswerRequest { ask_id, payload: response_payload_json }); - - // Send the answer. - let response = client.answer(answer_request).await; - if let Err(status) = response { - error!("Answer failed: {status:?}"); - } - }); - - debug!("Completed the ask request."); - - Ok(tonic::Response::new(AskResponse {})) } /// Notify implementation. From 30c43e307282934950b3c553d951b4a3a2ac07f0 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 22:30:21 -0700 Subject: [PATCH 020/109] Digital Twin Graph --- samples/graph/vehicle_core_provider/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/graph/vehicle_core_provider/src/main.rs index f9b82c87..95a2ba74 100644 --- a/samples/graph/vehicle_core_provider/src/main.rs +++ b/samples/graph/vehicle_core_provider/src/main.rs @@ -215,7 +215,7 @@ fn create_provider_state() -> ProviderState { result } -/// Register the vehicle parts +/// Register the vehicle parts. /// /// # Arguments /// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. From 0f104ddca69615d3e4a0d5b0c86f0627e8677187 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:15:36 -0700 Subject: [PATCH 021/109] Digital Twin Graph --- .../src/request_impl.rs | 35 ++++++++++++++++--- .../vehicle_core_provider/src/request_impl.rs | 4 ++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index 62be5f5c..e54c5620 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -97,11 +97,13 @@ impl RequestImpl { .await .map_err(|err_msg| format!("Unable to connect due to: {err_msg}"))?; - // Send the answer to the consumer. + // Prepare the answer request. let answer_request = tonic::Request::new(AnswerRequest { ask_id: ask_id.clone(), payload: answer_payload.clone(), }); + + // Send the answer to the consumer. client .answer(answer_request) .await @@ -157,13 +159,34 @@ impl RequestImpl { instance_data.serialized_value.clone() }; - // Check that the instance can handle the operation. let instance_json: serde_json::Value = serde_json::from_str(&instance_value_json_str).unwrap(); + let mut response_payload: String = "".to_string(); + let mut supported_method: bool = false; - if instance_json["@type"] == sdv::premium_airbag_seat_massager::ID { + + if (instance_json["@type"] == sdv::premium_airbag_seat_massager::ID + || instance_json["@type"] == sdv::basic_airbag_seat_massager::ID) + && targeted_payload.member_path + == sdv::airbag_seat_massager::perform_step::NAME.to_string() + { supported_method = true; + + let response: sdv::airbag_seat_massager::perform_step::response::TYPE = + sdv::airbag_seat_massager::perform_step::response::TYPE { + status: sdv::airbag_seat_massager::status::TYPE { + code: 200, + message: "The step was performed successfully".to_string(), + }, + ..Default::default() + }; + response_payload = serde_json::to_string(&response).unwrap(); + + info!( + "Executed the operation {} on instance {}", + targeted_payload.member_path, targeted_payload.instance_id + ); } if !supported_method { @@ -179,11 +202,13 @@ impl RequestImpl { .await .map_err(|err_msg| format!("Unable to connect due to: {err_msg}"))?; - // Send the answer to the consumer. + // Prepare the answer request. let answer_request = tonic::Request::new(AnswerRequest { ask_id: ask_id.clone(), - payload: "".to_string(), + payload: response_payload.clone(), }); + + // Send the answer to the consumer. client .answer(answer_request) .await diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs index b67f0cd6..93724d1d 100644 --- a/samples/graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -89,11 +89,13 @@ impl RequestImpl { .await .map_err(|err_msg| format!("Unable to connect due to: {err_msg}"))?; - // Send the answer to the consumer. + // Prepare the answer request. let answer_request = tonic::Request::new(AnswerRequest { ask_id: ask_id.clone(), payload: answer_payload.clone(), }); + + // Send the answer to the consumer. client .answer(answer_request) .await From e1bcafb8d9600dea2294e0c948ff0be90e66e7fa Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:21:26 -0700 Subject: [PATCH 022/109] Digital Twin Graph --- samples/graph/seat_massager_provider/src/request_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index e54c5620..8ea2c7a5 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -169,7 +169,7 @@ impl RequestImpl { if (instance_json["@type"] == sdv::premium_airbag_seat_massager::ID || instance_json["@type"] == sdv::basic_airbag_seat_massager::ID) && targeted_payload.member_path - == sdv::airbag_seat_massager::perform_step::NAME.to_string() + == sdv::airbag_seat_massager::perform_step::NAME { supported_method = true; From 0ba7580d461ccbdf4e0bb37dab543b43f6a4184e Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:28:54 -0700 Subject: [PATCH 023/109] Digital Twin Graph --- samples/graph/seat_massager_provider/src/request_impl.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index 8ea2c7a5..8b1cbdef 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -168,8 +168,7 @@ impl RequestImpl { if (instance_json["@type"] == sdv::premium_airbag_seat_massager::ID || instance_json["@type"] == sdv::basic_airbag_seat_massager::ID) - && targeted_payload.member_path - == sdv::airbag_seat_massager::perform_step::NAME + && targeted_payload.member_path == sdv::airbag_seat_massager::perform_step::NAME { supported_method = true; From 788f4d03b7ed728ea742b596e8d2499d95824c1f Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 25 Apr 2024 09:29:28 -0700 Subject: [PATCH 024/109] Digital Twin Graph --- samples/graph/consumer/src/main.rs | 3 +- .../graph/seat_massager_provider/src/main.rs | 4 +- .../src/request_impl.rs | 114 ++++++++++-------- .../vehicle_core_provider/src/request_impl.rs | 50 ++++---- 4 files changed, 94 insertions(+), 77 deletions(-) diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 081f14f7..9a72564f 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -225,13 +225,14 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul // Serialize the request payload to a JSON string. let request_payload_json: String = serde_json::to_string_pretty(&request_payload).unwrap(); // Invoke the perform_step operation. - let _perform_step_response: InvokeResponse = invoke( + let perform_step_response: InvokeResponse = invoke( client.clone(), seat_massager_instance_id.clone(), sdv::airbag_seat_massager::perform_step::NAME.to_string(), request_payload_json.clone(), ) .await?; + info!("The perform_step operation response is:\n{}", perform_step_response.response_payload); Ok(()) } diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 46e40205..40f95640 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -133,7 +133,7 @@ fn create_provider_state() -> ProviderState { result } -/// Register the airbag seat massagers. +/// Register the seat massagers. /// /// # Arguments /// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. @@ -159,7 +159,7 @@ async fn register_seat_massagers( digital_twin_operation::INVOKE.to_string(), ], uri: provider_uri.to_string(), - context: instance_id.to_string(), + context: instance_id.to_string(), // the context holds te }; let entity_access_info = EntityAccessInfo { diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index 8b1cbdef..b9ca1ea0 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -4,7 +4,7 @@ use digital_twin_graph::TargetedPayload; use digital_twin_model::sdv_v1 as sdv; -use log::{debug, info, warn}; +use log::{info, warn}; use parking_lot::{Mutex, MutexGuard}; use samples_common::constants::digital_twin_operation; use samples_protobuf_data_access::async_rpc::v1::request::{ @@ -21,7 +21,7 @@ use tokio_retry::Retry; /// Instance data. #[derive(Clone, Debug, Default)] pub struct InstanceData { - /// Model ID. + /// Model Id. pub model_id: String, /// Description. pub description: String, @@ -38,9 +38,11 @@ pub struct ProviderState { #[derive(Debug, Default)] pub struct RequestImpl { + /// Provider state. pub provider_state: Arc>, } +/// The implementation for the Request interface, which is used to handle requests from the consumer. impl RequestImpl { const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; const MAX_RETRIES: usize = 100; @@ -49,7 +51,7 @@ impl RequestImpl { /// /// # Arguments /// * `respond_uri` - Respond URI. - /// * `ask_id` - Ask ID. + /// * `ask_id` - Ask Id. /// * `targeted_payload` - Targeted payload. async fn get( &self, @@ -63,7 +65,7 @@ impl RequestImpl { )); } - let state: Arc> = self.provider_state.clone(); + let provider_state: Arc> = self.provider_state.clone(); // Define a retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) @@ -72,10 +74,10 @@ impl RequestImpl { // Asynchronously perform the get. tokio::spawn(async move { - // Get the answer's payload. - let answer_payload: String = { + // Retrieve the instance's value (it will be represented as a JSON string). + let instance_value: String = { let instance_data: InstanceData = { - let lock: MutexGuard = state.lock(); + let lock: MutexGuard = provider_state.lock(); match lock.instance_map.get(&targeted_payload.instance_id) { Some(instance_data) => instance_data.clone(), None => { @@ -100,7 +102,7 @@ impl RequestImpl { // Prepare the answer request. let answer_request = tonic::Request::new(AnswerRequest { ask_id: ask_id.clone(), - payload: answer_payload.clone(), + payload: instance_value.clone(), }); // Send the answer to the consumer. @@ -121,6 +123,7 @@ impl RequestImpl { /// * `respond_uri` - Respond URI. /// * `ask_id` - Ask ID. /// * `targeted_payload` - Targeted payload. + #[allow(unused_assignments)] async fn invoke( &self, respond_uri: String, @@ -133,7 +136,7 @@ impl RequestImpl { )); } - let state: Arc> = self.provider_state.clone(); + let provider_state: Arc> = self.provider_state.clone(); // Define a retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) @@ -142,9 +145,10 @@ impl RequestImpl { // Asynchronously perform the step. tokio::spawn(async move { - let instance_value_json_str: String = { + // Retrieve the instance's value (it will be represented as a JSON string). + let instance_value: String = { let instance_data: InstanceData = { - let lock: MutexGuard = state.lock(); + let lock: MutexGuard = provider_state.lock(); match lock.instance_map.get(&targeted_payload.instance_id) { Some(instance_data) => instance_data.clone(), None => { @@ -159,42 +163,25 @@ impl RequestImpl { instance_data.serialized_value.clone() }; - let instance_json: serde_json::Value = - serde_json::from_str(&instance_value_json_str).unwrap(); - - let mut response_payload: String = "".to_string(); + // Deserialize the instance value JSON. + let instance_value_json: serde_json::Value = + serde_json::from_str(&instance_value).unwrap(); - let mut supported_method: bool = false; + let mut response_payload: String = String::new(); - if (instance_json["@type"] == sdv::premium_airbag_seat_massager::ID - || instance_json["@type"] == sdv::basic_airbag_seat_massager::ID) + if (instance_value_json["@type"] == sdv::premium_airbag_seat_massager::ID + || instance_value_json["@type"] == sdv::basic_airbag_seat_massager::ID) && targeted_payload.member_path == sdv::airbag_seat_massager::perform_step::NAME { - supported_method = true; - - let response: sdv::airbag_seat_massager::perform_step::response::TYPE = - sdv::airbag_seat_massager::perform_step::response::TYPE { - status: sdv::airbag_seat_massager::status::TYPE { - code: 200, - message: "The step was performed successfully".to_string(), - }, - ..Default::default() - }; - response_payload = serde_json::to_string(&response).unwrap(); - - info!( - "Executed the operation {} on instance {}", - targeted_payload.member_path, targeted_payload.instance_id - ); - } - - if !supported_method { + response_payload = Self::perform_step(&targeted_payload)?; + } else { return Err(format!( - "The instance with the instance id '{}' does not support the operation '{}'", + "The instance with the instance id '{}' does not support the command '{}'", targeted_payload.instance_id, targeted_payload.operation )); } + // Send the answer to the consumer. Retry::spawn(retry_strategy, || async { // Connect to the consumer. let mut client = RespondClient::connect(respond_uri.clone()) @@ -218,6 +205,27 @@ impl RequestImpl { Ok(tonic::Response::new(AskResponse {})) } + + /// Perform step implementation. + /// # Arguments + /// * `targeted_payload` - Targeted payload. + fn perform_step(targeted_payload: &TargetedPayload) -> Result { + info!( + "Executed the operation {} on instance {}", + targeted_payload.member_path, targeted_payload.instance_id + ); + + let response = sdv::airbag_seat_massager::perform_step::response::TYPE { + status: sdv::airbag_seat_massager::status::TYPE { + code: 200, + message: "The step was performed successfully".to_string(), + }, + ..Default::default() + }; + + serde_json::to_string(&response) + .map_err(|e| format!("Failed to serialize the response: {}", e)) + } } #[tonic::async_trait] @@ -230,30 +238,30 @@ impl Request for RequestImpl { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let request_inner = request.into_inner(); - let respond_uri: String = request_inner.respond_uri.clone(); - let ask_id: String = request_inner.ask_id.clone(); - let payload: String = request_inner.payload.clone(); + let ask_request: AskRequest = request.into_inner(); - info!("Received an ask request"); - info!(" respond_uri: {respond_uri}"); - info!(" ask_id: {ask_id}"); + info!("Received an ask request:"); + info!(" respond_uri: {}", ask_request.respond_uri); + info!(" ask_id: {}", ask_request.ask_id); // Deserialize the targeted payload. - let targeted_payload_json: TargetedPayload = serde_json::from_str(&payload).unwrap(); + let targeted_payload_json: TargetedPayload = + serde_json::from_str(&ask_request.payload).unwrap(); - debug!(" instance_id: {}", targeted_payload_json.instance_id); - debug!(" member_path: {}", targeted_payload_json.member_path); - debug!(" operation: {}", targeted_payload_json.operation); + info!(" instance_id: {}", targeted_payload_json.instance_id); + info!(" member_path: {}", targeted_payload_json.member_path); + info!(" operation: {}", targeted_payload_json.operation); if targeted_payload_json.operation == digital_twin_operation::GET { - self.get(respond_uri, ask_id, targeted_payload_json).await + self.get(ask_request.respond_uri, ask_request.ask_id, targeted_payload_json).await } else if targeted_payload_json.operation == digital_twin_operation::INVOKE { - self.invoke(respond_uri, ask_id, targeted_payload_json).await + self.invoke(ask_request.respond_uri, ask_request.ask_id, targeted_payload_json).await } else { Err(tonic::Status::invalid_argument(format!( - "Unexpected operation '{}'", - targeted_payload_json.operation + "Unexpected operation '{}'. Expected '{}' or '{}'.", + targeted_payload_json.operation, + digital_twin_operation::GET, + digital_twin_operation::INVOKE ))) } } diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs index 93724d1d..f43ef7bb 100644 --- a/samples/graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use digital_twin_graph::TargetedPayload; -use log::{debug, info, warn}; +use log::{info, warn}; use parking_lot::{Mutex, MutexGuard}; use samples_common::constants::digital_twin_operation; use samples_protobuf_data_access::async_rpc::v1::request::{ @@ -17,22 +17,31 @@ use std::sync::Arc; use tokio_retry::strategy::{jitter, ExponentialBackoff}; use tokio_retry::Retry; +/// Instance data. #[derive(Clone, Debug, Default)] pub struct InstanceData { + /// Model Id. pub model_id: String, + /// Description. pub description: String, + /// Serialized value (using JSON-LD as a string). pub serialized_value: String, } +/// Provider state. #[derive(Debug, Default)] pub struct ProviderState { + /// Maps an instance id to its associated instance data. pub instance_map: HashMap, } #[derive(Debug, Default)] pub struct RequestImpl { + /// Provider state. pub provider_state: Arc>, } + +/// The implementation for the Request interface, which is used to handle requests from the consumer. impl RequestImpl { const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; const MAX_RETRIES: usize = 100; @@ -41,7 +50,7 @@ impl RequestImpl { /// /// # Arguments /// * `respond_uri` - Respond URI. - /// * `ask_id` - Ask ID. + /// * `ask_id` - Ask Id. /// * `targeted_payload` - Targeted payload. async fn get( &self, @@ -55,7 +64,7 @@ impl RequestImpl { )); } - let state: Arc> = self.provider_state.clone(); + let provider_state: Arc> = self.provider_state.clone(); // Define a retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) @@ -64,10 +73,10 @@ impl RequestImpl { // Asynchronously perform the get. tokio::spawn(async move { - // Get the answer's payload. - let answer_payload: String = { + // Retrieve the instance's value (it will be represented as a JSON string). + let instance_value: String = { let instance_data: InstanceData = { - let lock: MutexGuard = state.lock(); + let lock: MutexGuard = provider_state.lock(); match lock.instance_map.get(&targeted_payload.instance_id) { Some(instance_data) => instance_data.clone(), None => { @@ -92,7 +101,7 @@ impl RequestImpl { // Prepare the answer request. let answer_request = tonic::Request::new(AnswerRequest { ask_id: ask_id.clone(), - payload: answer_payload.clone(), + payload: instance_value.clone(), }); // Send the answer to the consumer. @@ -118,28 +127,27 @@ impl Request for RequestImpl { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let request_inner = request.into_inner(); - let respond_uri: String = request_inner.respond_uri.clone(); - let ask_id: String = request_inner.ask_id.clone(); - let payload: String = request_inner.payload.clone(); + let ask_request = request.into_inner(); - info!("Received an ask request"); - info!(" respond_uri: {respond_uri}"); - info!(" ask_id: {ask_id}"); + info!("Received an ask request:"); + info!(" respond_uri: {}", ask_request.respond_uri); + info!(" ask_id: {}", ask_request.ask_id); // Deserialize the targeted payload. - let targeted_payload_json: TargetedPayload = serde_json::from_str(&payload).unwrap(); + let targeted_payload_json: TargetedPayload = + serde_json::from_str(&ask_request.payload).unwrap(); - debug!(" instance_id: {}", targeted_payload_json.instance_id); - debug!(" member_path: {}", targeted_payload_json.member_path); - debug!(" operation: {}", targeted_payload_json.operation); + info!(" instance_id: {}", targeted_payload_json.instance_id); + info!(" member_path: {}", targeted_payload_json.member_path); + info!(" operation: {}", targeted_payload_json.operation); if targeted_payload_json.operation == digital_twin_operation::GET { - self.get(respond_uri, ask_id, targeted_payload_json).await + self.get(ask_request.respond_uri, ask_request.ask_id, targeted_payload_json).await } else { Err(tonic::Status::invalid_argument(format!( - "Unexpected operation '{}'", - targeted_payload_json.operation + "Unexpected operation '{}'. Expected '{}'.", + targeted_payload_json.operation, + digital_twin_operation::GET ))) } } From 1950d822ed82d260445c85aaaac990c1a92b51d7 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:10:31 -0700 Subject: [PATCH 025/109] Digital Twin Graph --- .github/workflows/rust-ci.yml | 4 ++-- core/invehicle-digital-twin/src/main.rs | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index d52833ac..e99a911e 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -64,8 +64,8 @@ jobs: - name: Cache Dependencies uses: Swatinem/rust-cache@v2 - name: Build - # Build the project with the `managed_subscribe` and `digital_twin_graph` features enabled. + # Build the project with the `managed_subscribe`, `digital_twin_graph` and `digital_twin_registry` features enabled. run: cargo build --features "managed_subscribe,digital_twin_graph,digital_twin_registry" - name: Test - # Test the project with the `managed_subscribe` and `digital_twin_graph` features enabled. + # Test the project with the `managed_subscribe`, `digital_twin_graph` and `digital_twin_registry` features enabled. run: cargo test --features "managed_subscribe,digital_twin_graph,digital_twin_registry" diff --git a/core/invehicle-digital-twin/src/main.rs b/core/invehicle-digital-twin/src/main.rs index 2c7f9a58..0258c30a 100644 --- a/core/invehicle-digital-twin/src/main.rs +++ b/core/invehicle-digital-twin/src/main.rs @@ -113,32 +113,32 @@ where let mut server: GrpcServer = GrpcServer::new(addr); #[cfg(feature = "managed_subscribe")] - // (1) Adds the Managed Subscribe module to the service. + // Adds the Managed Subscribe module to the service. let mut server = { - // (2) Initialize the Managed Subscribe module, which implements GrpcModule. + // Initialize the Managed Subscribe module, which implements GrpcModule. let managed_subscribe_module = ManagedSubscribeModule::new().await.map_err(|error| { error!("Unable to create Managed Subscribe module."); error })?; - // (3) Create interceptor layer to be added to the server. + // Create interceptor layer to be added to the server. let managed_subscribe_layer = GrpcInterceptorLayer::new(Box::new(managed_subscribe_module.create_interceptor())); - // (4) Add the interceptor(s) to the middleware stack. + // Add the interceptor(s) to the middleware stack. let current_middleware = server.middleware.clone(); let new_middleware = current_middleware.layer(managed_subscribe_layer); info!("Initialized Managed Subscribe module."); - // (5) Add the module with the updated middleware stack to the server. + // Add the module with the updated middleware stack to the server. server.add_module(new_middleware, Box::new(managed_subscribe_module)) }; #[cfg(feature = "digital_twin_graph")] - // (1) Adds the Managed Subscribe module to the service. + // Adds the Digital Twin Graph module to the service. let mut server = { - // (2) Initialize the Digital Twin Graph module, which implements GrpcModule. + // Initialize the Digital Twin Graph module, which implements GrpcModule. let digital_twin_graph_module = DigitalTwinGraphModule::new().await.map_err(|error| { error!("Unable to create Digital Twin Graph module."); error @@ -146,14 +146,14 @@ where info!("Initialized Digital Twin Graph module."); - // (3) Add the module with the updated middleware stack to the server. + // Add the module with the updated middleware stack to the server. server.add_module(server.middleware.clone(), Box::new(digital_twin_graph_module)) }; #[cfg(feature = "digital_twin_registry")] - // (1) Adds the Managed Subscribe module to the service. + // Adds the Digital Twin Registry module to the service. let mut server = { - // (2) Initialize the Digital Twin Registry module, which implements GrpcModule. + // Initialize the Digital Twin Registry module, which implements GrpcModule. let digital_twin_registry_module = DigitalTwinRegistryModule::new().await.map_err(|error| { error!("Unable to create Digital Twin Registry module."); @@ -162,7 +162,7 @@ where info!("Initialized Digital Twin Registry module."); - // (3) Add the module with the updated middleware stack to the server. + // Add the module with the updated middleware stack to the server. server.add_module(server.middleware.clone(), Box::new(digital_twin_registry_module)) }; From f02a537f2d944adc00fbc36e931087521fdb3103 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:37:20 -0700 Subject: [PATCH 026/109] Digital Twin Graph --- core/invehicle-digital-twin/src/main.rs | 20 +- core/module/digital_twin_graph/Cargo.toml | 1 + .../src/digital_twin_graph_config.rs | 23 ++ .../src/digital_twin_graph_impl.rs | 266 ++++++++++-------- .../src/digital_twin_graph_module.rs | 13 +- core/module/digital_twin_graph/src/lib.rs | 1 + .../digital_twin_graph/src/respond_impl.rs | 8 +- .../digital_twin_graph_component.puml | 30 ++ .../diagrams/digital_twin_graph_component.svg | 43 +++ .../diagrams/find_sequence.puml | 30 ++ .../diagrams/find_sequence.svg | 43 +++ .../diagrams/get_sequence.puml | 21 ++ .../diagrams/get_sequence.svg | 34 +++ .../diagrams/invoke_sequence.puml | 20 ++ .../diagrams/invoke_sequence.svg | 33 +++ 15 files changed, 445 insertions(+), 141 deletions(-) create mode 100644 core/module/digital_twin_graph/src/digital_twin_graph_config.rs create mode 100644 docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml create mode 100644 docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg create mode 100644 docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml create mode 100644 docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg create mode 100644 docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml create mode 100644 docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg create mode 100644 docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml create mode 100644 docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg diff --git a/core/invehicle-digital-twin/src/main.rs b/core/invehicle-digital-twin/src/main.rs index 0258c30a..6e88f4ea 100644 --- a/core/invehicle-digital-twin/src/main.rs +++ b/core/invehicle-digital-twin/src/main.rs @@ -84,7 +84,7 @@ async fn register_invehicle_digital_twin_service_with_chariott( Ok(()) } -/// Builds the enabled modules for the grpc server and starts the server. +/// Builds the enabled modules for the app server and starts the app server. /// /// # Arguments /// * `addr` - The address the server will be hosted on. @@ -98,7 +98,7 @@ async fn register_invehicle_digital_twin_service_with_chariott( /// 5. Call and return from the block `.add_module()` on the server with the updated middleware and /// module. #[allow(unused_assignments, unused_mut)] // Necessary when no extra modules are built. -async fn build_server_and_serve( +async fn build_app_server_and_serve( addr: SocketAddr, base_service: S, ) -> Result<(), Box> @@ -113,7 +113,7 @@ where let mut server: GrpcServer = GrpcServer::new(addr); #[cfg(feature = "managed_subscribe")] - // Adds the Managed Subscribe module to the service. + // Adds the Managed Subscribe module to the app server. let mut server = { // Initialize the Managed Subscribe module, which implements GrpcModule. let managed_subscribe_module = ManagedSubscribeModule::new().await.map_err(|error| { @@ -121,7 +121,7 @@ where error })?; - // Create interceptor layer to be added to the server. + // Create interceptor layer to be added to the app. let managed_subscribe_layer = GrpcInterceptorLayer::new(Box::new(managed_subscribe_module.create_interceptor())); @@ -136,7 +136,7 @@ where }; #[cfg(feature = "digital_twin_graph")] - // Adds the Digital Twin Graph module to the service. + // Adds the Digital Twin Graph module to the app server. let mut server = { // Initialize the Digital Twin Graph module, which implements GrpcModule. let digital_twin_graph_module = DigitalTwinGraphModule::new().await.map_err(|error| { @@ -151,7 +151,7 @@ where }; #[cfg(feature = "digital_twin_registry")] - // Adds the Digital Twin Registry module to the service. + // Adds the Digital Twin Registry module to the app server. let mut server = { // Initialize the Digital Twin Registry module, which implements GrpcModule. let digital_twin_registry_module = @@ -166,10 +166,10 @@ where server.add_module(server.middleware.clone(), Box::new(digital_twin_registry_module)) }; - // Construct the server. + // Construct the app server. let builder = server.construct_server().add_service(base_service); - // Start the server. + // Start the app server. builder.serve(addr).await.map_err(|error| error.into()) } @@ -243,8 +243,8 @@ async fn main() -> Result<(), Box> { let base_service = InvehicleDigitalTwinServer::new(invehicle_digital_twin_impl); - // Build and start the grpc server. - build_server_and_serve(addr, base_service).await?; + // Build and start the app server. + build_app_server_and_serve(addr, base_service).await?; debug!("The Digital Twin Service has completed."); diff --git a/core/module/digital_twin_graph/Cargo.toml b/core/module/digital_twin_graph/Cargo.toml index efa00c52..9991eca8 100644 --- a/core/module/digital_twin_graph/Cargo.toml +++ b/core/module/digital_twin_graph/Cargo.toml @@ -16,6 +16,7 @@ serde = { workspace = true } serde_derive = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true , features = ["full"] } +tokio-retry = { workspace = true } tonic = { workspace = true } tower = { workspace = true } yaml-rust = { workspace = true } diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_config.rs b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs new file mode 100644 index 00000000..6abc51b6 --- /dev/null +++ b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +use common::utils; +use serde_derive::Deserialize; + +const DEFAULT_CONFIG_FILENAME: &str = "digital_twin_graph_settings"; + +#[derive(Debug, Deserialize)] +pub struct Settings { + pub base_authority: String, +} + +/// Load the settings. +pub fn load_settings() -> Settings { + utils::load_settings(DEFAULT_CONFIG_FILENAME).unwrap() +} + +/// Load the settings. +pub fn load_settings_with_config_filename(config_filename: &str) -> Settings { + utils::load_settings(config_filename).unwrap() +} diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index fbd23329..d15ffc6d 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -12,123 +12,147 @@ use core_protobuf_data_access::module::digital_twin_graph::v1::{ }; use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_client::DigitalTwinRegistryClient; use core_protobuf_data_access::module::digital_twin_registry::v1::{ - EndpointInfo, FindByInstanceIdRequest, FindByModelIdRequest, + EndpointInfo, FindByInstanceIdRequest, FindByInstanceIdResponse, FindByModelIdRequest, + FindByModelIdResponse, }; use log::{debug, info, warn}; use std::sync::Arc; use tokio::sync::broadcast; use tokio::time::{sleep, timeout, Duration}; +use tokio_retry::strategy::{jitter, ExponentialBackoff}; +use tokio_retry::Retry; use uuid::Uuid; use crate::{digital_twin_operation, digital_twin_protocol, TargetedPayload}; #[derive(Debug)] pub struct DigitalTwinGraphImpl { - invehicle_digital_twin_uri: String, + /// Digital Twin Registry URI. + digital_twin_registry_uri: String, + /// Respond URI. respond_uri: String, + /// The sender for the asynchronous channel for AnswerRequests. tx: Arc>, } impl DigitalTwinGraphImpl { + const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + const MAX_RETRIES: usize = 100; + const TIMEOUT_PERIOD_IN_MILLIS: u64 = 5000; + /// Create a new instance of a DigitalTwinGraphImpl. /// /// # Arguments - /// * `invehicle_digital_twin_uri` - The uri for the invehicle digital twin service. + /// * `digital_twin_registry_uri` - The uri for the digital twin registry service. /// * `respond_uri` - The uri for the respond service. /// * `tx` - The sender for the asynchronous channel for AnswerRequest's. pub fn new( - invehicle_digital_twin_uri: &str, + digital_twin_registry_uri: &str, respond_uri: &str, tx: Arc>, ) -> DigitalTwinGraphImpl { DigitalTwinGraphImpl { - invehicle_digital_twin_uri: invehicle_digital_twin_uri.to_string(), + digital_twin_registry_uri: digital_twin_registry_uri.to_string(), respond_uri: respond_uri.to_string(), tx, } } -} -/// Is the provided subset a subset of the provided superset? -/// -/// # Arguments -/// * `subset` - The provided subset. -/// * `superset` - The provided superset. -fn is_subset(subset: &[String], superset: &[String]) -> bool { - subset.iter().all(|subset_member| { - superset.iter().any(|supserset_member| subset_member == supserset_member) - }) -} + /// Is the provided subset a subset of the provided superset? + /// + /// # Arguments + /// * `subset` - The provided subset. + /// * `superset` - The provided superset. + fn is_subset(subset: &[String], superset: &[String]) -> bool { + subset.iter().all(|subset_member| { + superset.iter().any(|supserset_member| subset_member == supserset_member) + }) + } -/// Use Ibeji to discover the endpoints for digital twin providers that satisfy the requirements. -/// -/// # Arguments -/// * `digitial_twin_registry_service_uri` - Digital Twin Registry Service URI. -/// * `model_id` - The matching model id. -/// * `protocol` - The required protocol. -/// * `operations` - The required operations. -pub async fn discover_digital_twin_providers_with_model_id( - digitial_twin_registry_service_uri: &str, - model_id: &str, - protocol: &str, - operations: &[String], -) -> Result, String> { - info!("Sending a find_by_model_id request for model id {model_id} to the Digital Twin Registry Service at {digitial_twin_registry_service_uri}"); - - let mut client = - DigitalTwinRegistryClient::connect(digitial_twin_registry_service_uri.to_string()) - .await - .map_err(|error| format!("{error}"))?; - let request = tonic::Request::new(FindByModelIdRequest { model_id: model_id.to_string() }); - let response = client.find_by_model_id(request).await.map_err(|error| error.to_string())?; - let response_inner = response.into_inner(); - debug!("Received the response for the find_by_model_id request"); - info!("response_payload: {:?}", response_inner.entity_access_info_list); - - Ok(response_inner - .entity_access_info_list - .iter() - .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) - .filter(|endpoint_info| { - endpoint_info.protocol == protocol && is_subset(operations, &endpoint_info.operations) + /// Use the Digital Twin Registery service to discover the endpoints for digital twin providers that support the specified model id, protocol and operations. + /// Note: This operation will be retried when there is a failure. + /// + /// # Arguments + /// * `model_id` - The matching model id. + /// * `protocol` - The required protocol. + /// * `operations` - The required operations. + pub async fn discover_digital_twin_providers_with_model_id( + &self, + model_id: &str, + protocol: &str, + operations: &[String], + ) -> Result, String> { + let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) + .map(jitter) // add jitter to delays + .take(Self::MAX_RETRIES); + + let response: FindByModelIdResponse = Retry::spawn(retry_strategy.clone(), || async { + let mut client = + DigitalTwinRegistryClient::connect(self.digital_twin_registry_uri.to_string()) + .await + .map_err(|error| format!("{error}"))?; + + let request = + tonic::Request::new(FindByModelIdRequest { model_id: model_id.to_string() }); + + client.find_by_model_id(request).await.map_err(|error| error.to_string()) }) - .collect()) -} + .await? + .into_inner(); + + Ok(response + .entity_access_info_list + .iter() + .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) + .filter(|endpoint_info| { + endpoint_info.protocol == protocol + && Self::is_subset(operations, &endpoint_info.operations) + }) + .collect()) + } -/// Use Ibeji to discover the endpoints for digital twin providers that satisfy the requirements. -/// -/// # Arguments -/// * `digitial_twin_registry_service_uri` - Digital Twin Registry Service URI. -/// * `instance_id` - The matching instance id. -/// * `protocol` - The required protocol. -/// * `operations` - The required operations. -pub async fn discover_digital_twin_providers_with_instance_id( - digitial_twin_registry_service_uri: &str, - instance_id: &str, - protocol: &str, - operations: &[String], -) -> Result, String> { - info!("Sending a find_by_instance_id request for instance id {instance_id} to the Digital Twin Registry Service at {digitial_twin_registry_service_uri}"); - - let mut client = - DigitalTwinRegistryClient::connect(digitial_twin_registry_service_uri.to_string()) - .await - .map_err(|error| format!("{error}"))?; - let request = - tonic::Request::new(FindByInstanceIdRequest { instance_id: instance_id.to_string() }); - let response = client.find_by_instance_id(request).await.map_err(|error| error.to_string())?; - let response_inner = response.into_inner(); - debug!("Received the response for the find_by_instance_id request"); - info!("response_payload: {:?}", response_inner.entity_access_info_list); - - Ok(response_inner - .entity_access_info_list - .iter() - .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) - .filter(|endpoint_info| { - endpoint_info.protocol == protocol && is_subset(operations, &endpoint_info.operations) + /// Use the Digital Twin Registry service to discover the endpoints for digital twin providers that support the specified instance id, protocol and operations. + /// Note: This operation will be retried when there is a failure. + /// + /// # Arguments + /// * `instance_id` - The matching instance id. + /// * `protocol` - The required protocol. + /// * `operations` - The required operations. + pub async fn discover_digital_twin_providers_with_instance_id( + &self, + instance_id: &str, + protocol: &str, + operations: &[String], + ) -> Result, String> { + let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) + .map(jitter) // add jitter to delays + .take(Self::MAX_RETRIES); + + let response: FindByInstanceIdResponse = Retry::spawn(retry_strategy.clone(), || async { + let mut client = + DigitalTwinRegistryClient::connect(self.digital_twin_registry_uri.to_string()) + .await + .map_err(|error| format!("{error}"))?; + + let request = tonic::Request::new(FindByInstanceIdRequest { + instance_id: instance_id.to_string(), + }); + + client.find_by_instance_id(request).await.map_err(|error| error.to_string()) }) - .collect()) + .await? + .into_inner(); + + Ok(response + .entity_access_info_list + .iter() + .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) + .filter(|endpoint_info| { + endpoint_info.protocol == protocol + && Self::is_subset(operations, &endpoint_info.operations) + }) + .collect()) + } } #[tonic::async_trait] @@ -144,19 +168,17 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let request_inner = request.into_inner(); let model_id = request_inner.model_id; - info!("Received a find request for model id {model_id}"); + debug!("Received a find request for model id {model_id}"); // Retrieve the provider details. - let provider_endpoint_info_list = discover_digital_twin_providers_with_model_id( - &self.invehicle_digital_twin_uri, - model_id.as_str(), - digital_twin_protocol::GRPC, - &[digital_twin_operation::GET.to_string()], - ) - .await - .map_err(tonic::Status::internal)?; - - info!(">> Found the provider endpoint info list: {provider_endpoint_info_list:?}"); + let provider_endpoint_info_list = self + .discover_digital_twin_providers_with_model_id( + model_id.as_str(), + digital_twin_protocol::GRPC, + &[digital_twin_operation::GET.to_string()], + ) + .await + .map_err(tonic::Status::internal)?; let mut values = vec![]; @@ -196,15 +218,14 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Send the ask. let response = client.ask(request).await; if let Err(status) = response { - warn!("Unable to call ask, due to {status:?}\nWe will skip this one.."); + warn!("Unable to call ask, due to {status:?}\nWe will skip this one."); continue; } // Wait for the answer request. let mut answer_request: AnswerRequest = Default::default(); let mut attempts_after_failure = 0; - const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; - while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { + while attempts_after_failure < Self::MAX_RETRIES { match timeout(Duration::from_secs(5), rx.recv()).await { Ok(Ok(request)) => { if ask_id == request.ask_id { @@ -220,20 +241,20 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { } Ok(Err(error_message)) => { warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; + sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; } Err(error_message) => { warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; + sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; } } } - info!( + debug!( "Received an answer request. The ask_id is '{}'. The payload is '{}'", answer_request.ask_id, answer_request.payload ); @@ -241,6 +262,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { values.push(answer_request.payload); } + debug!("Completed the find request"); + Ok(tonic::Response::new(FindResponse { values })) } @@ -259,14 +282,14 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { info!("Received a get request for instance id {instance_id}"); // Retrieve the provider details. - let provider_endpoint_info_list = discover_digital_twin_providers_with_instance_id( - &self.invehicle_digital_twin_uri, - instance_id.as_str(), - digital_twin_protocol::GRPC, - &[digital_twin_operation::GET.to_string()], - ) - .await - .map_err(tonic::Status::internal)?; + let provider_endpoint_info_list = self + .discover_digital_twin_providers_with_instance_id( + instance_id.as_str(), + digital_twin_protocol::GRPC, + &[digital_twin_operation::GET.to_string()], + ) + .await + .map_err(tonic::Status::internal)?; info!("Found the provider endpoint info list: {provider_endpoint_info_list:?}"); @@ -308,16 +331,17 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Send the ask. let response = client.ask(request).await; if let Err(status) = response { - warn!("Unable to call ask, due to {status:?}\nWe will skip this one.."); + warn!("Unable to call ask, due to {status:?}\nWe will skip this one."); continue; } // Wait for the answer request. let mut answer_request: AnswerRequest = Default::default(); let mut attempts_after_failure = 0; - const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; - while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { - match timeout(Duration::from_secs(5), rx.recv()).await { + while attempts_after_failure < Self::MAX_RETRIES { + match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()) + .await + { Ok(Ok(request)) => { if ask_id == request.ask_id { // We have received the answer request that we are expecting. @@ -332,13 +356,13 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { } Ok(Err(error_message)) => { warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; + sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; } Err(error_message) => { warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; + sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; } @@ -389,14 +413,14 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { info!("Received an invoke request for instance id {instance_id}"); // Retrieve the provider details. - let provider_endpoint_info_list = discover_digital_twin_providers_with_instance_id( - &self.invehicle_digital_twin_uri, - instance_id.as_str(), - digital_twin_protocol::GRPC, - &[digital_twin_operation::INVOKE.to_string()], - ) - .await - .map_err(tonic::Status::internal)?; + let provider_endpoint_info_list = self + .discover_digital_twin_providers_with_instance_id( + instance_id.as_str(), + digital_twin_protocol::GRPC, + &[digital_twin_operation::INVOKE.to_string()], + ) + .await + .map_err(tonic::Status::internal)?; info!("Found the provider endpoint info list: {provider_endpoint_info_list:?}"); diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs index 6959aaac..d4e7c3d9 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs @@ -5,11 +5,11 @@ use common::grpc_module::GrpcModule; use core_protobuf_data_access::async_rpc::v1::respond::respond_server::RespondServer; use core_protobuf_data_access::module::digital_twin_graph::v1::digital_twin_graph_server::DigitalTwinGraphServer; -// use log::{debug, error, info}; use std::sync::Arc; use tokio::sync::broadcast; use tonic::transport::server::RoutesBuilder; +use crate::digital_twin_graph_config; use crate::digital_twin_graph_impl::DigitalTwinGraphImpl; use crate::respond_impl::RespondImpl; @@ -30,11 +30,12 @@ impl GrpcModule for DigitalTwinGraphModule { /// # Arguments /// * `builder` - A tonic::RoutesBuilder that contains the grpc services to build. fn add_grpc_services(&self, builder: &mut RoutesBuilder) { - // Note: The authority is hardcoded for now, but it should be configurable in the future. - let invehicle_digital_twin_authority = "0.0.0.0:5010"; - let invehicle_digital_twin_uri = format!("http://{invehicle_digital_twin_authority}"); // Devskim: ignore DS137138 - let respond_authority = "0.0.0.0:5010"; - let respond_uri = format!("http://{respond_authority}"); // Devskim: ignore DS137138 + // Load the config. + let settings = digital_twin_graph_config::load_settings(); + let base_authority = settings.base_authority; + + let invehicle_digital_twin_uri = format!("http://{base_authority}"); // Devskim: ignore DS137138 + let respond_uri = format!("http://{base_authority}"); // Devskim: ignore DS137138 let (tx, _rx) = broadcast::channel(100); let tx = Arc::new(tx); diff --git a/core/module/digital_twin_graph/src/lib.rs b/core/module/digital_twin_graph/src/lib.rs index 030da7df..a17f09b7 100644 --- a/core/module/digital_twin_graph/src/lib.rs +++ b/core/module/digital_twin_graph/src/lib.rs @@ -2,6 +2,7 @@ // Licensed under the MIT license. // SPDX-License-Identifier: MIT +pub mod digital_twin_graph_config; pub mod digital_twin_graph_impl; pub mod digital_twin_graph_module; pub mod respond_impl; diff --git a/core/module/digital_twin_graph/src/respond_impl.rs b/core/module/digital_twin_graph/src/respond_impl.rs index 169bf9a5..d4e9494b 100644 --- a/core/module/digital_twin_graph/src/respond_impl.rs +++ b/core/module/digital_twin_graph/src/respond_impl.rs @@ -4,7 +4,7 @@ use core_protobuf_data_access::async_rpc::v1::respond::respond_server::Respond; use core_protobuf_data_access::async_rpc::v1::respond::{AnswerRequest, AnswerResponse}; -use log::{debug, info}; +use log::debug; use std::sync::Arc; use tokio::sync::broadcast; @@ -17,7 +17,7 @@ impl RespondImpl { /// Create a new instance of a RespondImpl. /// /// # Arguments - /// * `tx` - The sender for the asynchronous channel for AnswerRequest's. + /// * `tx` - The sender for the asynchronous channel for AnswerRequests. pub fn new(tx: Arc>) -> RespondImpl { RespondImpl { tx } } @@ -33,14 +33,14 @@ impl Respond for RespondImpl { &self, request: tonic::Request, ) -> Result, tonic::Status> { - info!("Received an answer request"); + debug!("Received an answer request"); let tx = Arc::clone(&self.tx); // Send the request to the channel. if let Err(err_msg) = tx.send(request.into_inner()) { return Err(tonic::Status::internal(format!( - "Failed to send the answer request due to {err_msg}" + "Failed to send the answer request due to: {err_msg}" ))); } diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml new file mode 100644 index 00000000..a8c43dd5 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml @@ -0,0 +1,30 @@ +@startuml + +component "Digital Twin Consumer" { +} + +component "Digital Twin App Server" { + component "Digital Twin Graph Service" { + interface "Digital Twin Graph Interface" + interface "Respond Interface" + } + component "Digital Twin Registry Service" { + interface "Digital Twin Registry Interface" + } +} + +component "Digital Twin Provider" { + interface "Request Interface" +} + +"Digital Twin Provider" -left-> "Digital Twin Registry Interface" : Register + +"Digital Twin Consumer" -right-> "Digital Twin Graph Interface" : Find/Get/Set/Invoke + +"Digital Twin Graph Service" -down-> "Digital Twin Registry Interface": FindByModelId + +"Digital Twin Graph Service" -right-> "Request Interface": Ask + +"Digital Twin Provider" -left-> "Respond Interface": Answer + +@enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg new file mode 100644 index 00000000..f4d4ee8a --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -0,0 +1,43 @@ +Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml new file mode 100644 index 00000000..4a9f01fb --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml @@ -0,0 +1,30 @@ +@startuml + +autonumber + +participant "Digital Twin Consumer" as CONSUMER +participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH +participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY +participant "Digital Twin Provider" as PROVIDER + +CONSUMER -> DIGITAL_TWIN_GRAPH: Find(model_id: "dtmi:sdv:vehicle;1") - request + +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request +note right +end note +DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByModelId - response +note left +end note + +loop Iterate over the results from the FindByModelId call +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Get +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Get +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer +end + +CONSUMER <- DIGITAL_TWIN_GRAPH: Find - response +note left +end note + +@enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg new file mode 100644 index 00000000..fb6ee844 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -0,0 +1,43 @@ +Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:vehicle;1") - request2FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request3FindByModelId - responseloop[Iterate over the results from the FindByModelId call]4Ask - Get5Ask - Get6Answer7Answer8Find - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml new file mode 100644 index 00000000..71181723 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml @@ -0,0 +1,21 @@ +@startuml + +autonumber + +participant "Digital Twin Consumer" as CONSUMER +participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH +participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY +participant "Digital Twin Provider" as PROVIDER + +CONSUMER -> DIGITAL_TWIN_GRAPH: Get(instance_id: "1234567890", member_path: "vehicle_identification") - request + +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "1234567890") - request +DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response + +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Get + +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer + +CONSUMER <- DIGITAL_TWIN_GRAPH: Get - response + +@enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg new file mode 100644 index 00000000..319a00e1 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -0,0 +1,34 @@ +Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "1234567890", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Get5Answer6Get - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml new file mode 100644 index 00000000..6d9148d9 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml @@ -0,0 +1,20 @@ +@startuml + +autonumber + +participant "Digital Twin Consumer" as CONSUMER +participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH +participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY +participant "Digital Twin Provider" as PROVIDER + +CONSUMER -> DIGITAL_TWIN_GRAPH: Invoke(instance_id: "1234567890", payload: "") - request + +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(id: "1234567890") - request +DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response + +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - request +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer + +CONSUMER <- DIGITAL_TWIN_GRAPH: Invoke - response + +@enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg new file mode 100644 index 00000000..29744015 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -0,0 +1,33 @@ +Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "1234567890", payload: "") - request2FindByInstanceId(id: 1234567890") - request3FindByInstanceId - response4Ask - request5Answer6Invoke - response \ No newline at end of file From 2d8f2daf3de515d4e207b3f5277e83318fab151d Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Fri, 26 Apr 2024 20:38:54 +0000 Subject: [PATCH 027/109] Generate PlantUML Diagrams --- .../diagrams/digital_twin_graph_component.svg | 24 +++++++++++++++---- .../diagrams/find_sequence.svg | 10 ++++---- .../diagrams/get_sequence.svg | 10 ++++---- .../diagrams/invoke_sequence.svg | 12 +++++----- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index f4d4ee8a..365cd993 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,5 +1,20 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index fb6ee844..b22dc4df 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:vehicle;1") - request2FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request3FindByModelId - responseloop[Iterate over the results from the FindByModelId call]4Ask - Get5Ask - Get6Answer7Answer8Find - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index 319a00e1..5cb2dffc 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "1234567890", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Get5Answer6Get - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 29744015..62080436 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "1234567890", payload: "") - request2FindByInstanceId(id: 1234567890") - request3FindByInstanceId - response4Ask - request5Answer6Invoke - response \ No newline at end of file From 4390681a9f9488da3c1c7a98388247d385a1f8b1 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:44:36 -0700 Subject: [PATCH 028/109] Digital Twin Graph --- .../diagrams/digital_twin_graph_component.puml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml index a8c43dd5..63a405fe 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml @@ -6,7 +6,7 @@ component "Digital Twin Consumer" { component "Digital Twin App Server" { component "Digital Twin Graph Service" { interface "Digital Twin Graph Interface" - interface "Respond Interface" + interface "Respond Interface" } component "Digital Twin Registry Service" { interface "Digital Twin Registry Interface" From 158137787e880dab9d489b1516cedab0bbc94f6f Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Fri, 26 Apr 2024 20:46:21 +0000 Subject: [PATCH 029/109] Generate PlantUML Diagrams --- .../diagrams/digital_twin_graph_component.svg | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index 365cd993..901d7799 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,18 +1,18 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml index 4a9f01fb..b35e6279 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml @@ -10,21 +10,22 @@ participant "Digital Twin Provider" as PROVIDER CONSUMER -> DIGITAL_TWIN_GRAPH: Find(model_id: "dtmi:sdv:vehicle;1") - request DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request -note right -end note + DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByModelId - response note left + list of value (serialized as a JSON-LD string) end note loop Iterate over the results from the FindByModelId call -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Get -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Get -DIGITAL_TWIN_GRAPH <- PROVIDER: Answer -DIGITAL_TWIN_GRAPH <- PROVIDER: Answer +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri, ask_id: "1", payload) +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri, ask_id: "2", payload) +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload) +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "2", payload) end CONSUMER <- DIGITAL_TWIN_GRAPH: Find - response note left + list of EntityAccessInfo end note @enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index b22dc4df..c583d238 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:vehicle;1") - request2FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request3FindByModelId - responseloop[Iterate over the results from the FindByModelId call]4Ask - Get5Ask - Get6Answer7Answer8Find - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index 5cb2dffc..319a00e1 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "1234567890", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Get5Answer6Get - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 62080436..02723f95 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "1234567890", payload: "") - request2FindByInstanceId(id: "1234567890") - request3FindByInstanceId - response4Ask - request5Answer6Invoke - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml new file mode 100644 index 00000000..402b4060 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml @@ -0,0 +1,21 @@ +@startuml + +autonumber + +participant "Digital Twin Consumer" as CONSUMER +participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH +participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY +participant "Digital Twin Provider" as PROVIDER + +CONSUMER -> DIGITAL_TWIN_GRAPH: Set(instance_id: "1234567890", member_path: "vehicle_identification/vin", value) - request + +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "1234567890") - request +DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response + +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Set + +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer + +CONSUMER <- DIGITAL_TWIN_GRAPH: Set - response + +@enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg new file mode 100644 index 00000000..598c35d8 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -0,0 +1,34 @@ +Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "1234567890", member_path: "vehicle_identification/vin", value) - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Set5Answer6Set - response \ No newline at end of file diff --git a/interfaces/async_rpc/v1/.respond.proto.swp b/interfaces/async_rpc/v1/.respond.proto.swp new file mode 100644 index 0000000000000000000000000000000000000000..cd15286c97c02ca19da441b90b8f225684ea6288 GIT binary patch literal 12288 zcmeI2&2G~`6on_SE+CNDAq`P4gbN}XejVDKxJpE<9BaW)da0J#mt A0ssI2 literal 0 HcmV?d00001 From 0b9bc6a98858268b4051bed58aef6ece66ed3e61 Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Mon, 29 Apr 2024 19:53:29 +0000 Subject: [PATCH 032/109] Generate PlantUML Diagrams --- .../diagrams/digital_twin_graph_component.svg | 24 +++++++++++++++---- .../diagrams/find_sequence.svg | 10 ++++---- .../diagrams/get_sequence.svg | 10 ++++---- .../diagrams/invoke_sequence.svg | 10 ++++---- .../diagrams/set_sequence.svg | 10 ++++---- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index 6d784395..901d7799 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,5 +1,20 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index c583d238..3ffa34fc 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:vehicle;1") - request2FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request3FindByModelId - responselist of value (serialized as a JSON-LD string)loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri, ask_id: "1", payload)5Ask(respond_uri, ask_id: "2", payload)6Answer(ask_id: "1", payload)7Answer(ask_id: "2", payload)8Find - responselist of EntityAccessInfo \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index 319a00e1..5cb2dffc 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "1234567890", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Get5Answer6Get - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 02723f95..62080436 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "1234567890", payload: "") - request2FindByInstanceId(id: "1234567890") - request3FindByInstanceId - response4Ask - request5Answer6Invoke - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg index 598c35d8..8e37959b 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "1234567890", member_path: "vehicle_identification/vin", value) - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Set5Answer6Set - response \ No newline at end of file From 72067f79cae57e6522b26f4619b959b2a5536a25 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:37:16 -0700 Subject: [PATCH 033/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 2b8da99c..855a80cb 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -11,7 +11,7 @@ Ibeji today provides the foundations for constructing and interacting with the a ## Architecture -Ibeji's Application Server, which we will refer to as "Digital Twin App Server", has a modular architecture that allows new services to easily be added and existing services to easily be removed. It also has build-time feature switches for controlling which service should be available at run-time. Ibeji's initial service, the Invehicle Digital Twin service, was developed before the adoption of the modular architecture, but it will eventually be migrated across to it. +Ibeji's Application Server, which we will refer to as "Digital Twin App Server", has a modular architecture that allows new services to readily be added and existing services to readily be removed. It also has build-time feature switches for controlling which service should be available at run-time. Ibeji's initial service, the Invehicle Digital Twin service, was developed before the adoption of the modular architecture, but it will eventually be migrated across to it. We will introduce a new service named "Digital Twin Graph" that will provide a facade for the Invehicle Digital Twin service and the providers. Ideally, the consumer will not need to directly interact with provider endpoints. Instead, they will interact with a graph structure that represnts the ditial twin, From 80c53c5b0452fc21c5145dfcdc5c3e073185ecce Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:39:54 -0700 Subject: [PATCH 034/109] Digital Twin Graph --- interfaces/async_rpc/v1/.respond.proto.swp | Bin 12288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 interfaces/async_rpc/v1/.respond.proto.swp diff --git a/interfaces/async_rpc/v1/.respond.proto.swp b/interfaces/async_rpc/v1/.respond.proto.swp deleted file mode 100644 index cd15286c97c02ca19da441b90b8f225684ea6288..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&2G~`6on_SE+CNDAq`P4gbN}XejVDKxJpE<9BaW)da0J#mt A0ssI2 From efea9e8a09a66ba25b14180f81f16e5e9b8a4dcb Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:45:16 -0700 Subject: [PATCH 035/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/.accepted_words.txt | 5 +++++ docs/design/modules/digital_twin_graph/README.md | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 docs/design/modules/digital_twin_graph/.accepted_words.txt diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt new file mode 100644 index 00000000..c44de012 --- /dev/null +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -0,0 +1,5 @@ +App +Ibeji +Ibeji's +Invehicle +svg diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 855a80cb..3e8f9534 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -6,7 +6,7 @@ ## Introduction -Ibeji today provides the foundations for constructing and interacting with the a digital twin on an edge device. These abilities are primitve and do not necessartily provide a consumer with the best experience. They can be used as building blocks to build facades that provide a consumer with a better experience. This design specifies a graph-based facade, which will be named the Digital Twin Graph service. +Ibeji today provides the foundations for constructing and interacting with the a digital twin on an edge device. These abilities are primitive and do not necessarily provide a consumer with the best experience. They can be used as building blocks to build facades that provide a consumer with a better experience. This design specifies a graph-based facade, which will be named the Digital Twin Graph service. ## Architecture @@ -14,9 +14,9 @@ Ibeji today provides the foundations for constructing and interacting with the a Ibeji's Application Server, which we will refer to as "Digital Twin App Server", has a modular architecture that allows new services to readily be added and existing services to readily be removed. It also has build-time feature switches for controlling which service should be available at run-time. Ibeji's initial service, the Invehicle Digital Twin service, was developed before the adoption of the modular architecture, but it will eventually be migrated across to it. We will introduce a new service named "Digital Twin Graph" that will provide a facade for the Invehicle Digital Twin service and the providers. Ideally, the consumer will not need to directly interact with provider -endpoints. Instead, they will interact with a graph structure that represnts the ditial twin, +endpoints. Instead, they will interact with a graph structure that represents the digital twin, -Ibeji's existing Invehicle Digital Twin service needs some adjustments to support the Digital Twin Graph service. There is a future plan plan to rename it as the Digital Twin Registery service. +Ibeji's existing Invehicle Digital Twin service needs some adjustments to support the Digital Twin Graph service. There is a future plan plan to rename it as the Digital Twin Registry service. We will introduce a modified form of the service under the name "Digital Twin Registry" and for now keep the existing functionality intact under the original name "Invehicle Digital Twin". ![Component Diagram](diagrams/digital_twin_graph_component.svg) From 57942c56e9cff7f17de4c99f0d8cfca6651c254b Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:46:46 -0700 Subject: [PATCH 036/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 3e8f9534..fa7a406b 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -2,7 +2,7 @@ - [Introduction](#introduction) - [Architecture](#architecture) -- [Sequences](#Sequences) +- [Sequences](#sequences) ## Introduction From 3881c10f416a0e188824facb25d142ad120c0f99 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:50:01 -0700 Subject: [PATCH 037/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index fa7a406b..99a866f2 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -6,7 +6,7 @@ ## Introduction -Ibeji today provides the foundations for constructing and interacting with the a digital twin on an edge device. These abilities are primitive and do not necessarily provide a consumer with the best experience. They can be used as building blocks to build facades that provide a consumer with a better experience. This design specifies a graph-based facade, which will be named the Digital Twin Graph service. +Ibeji today provides the foundations for constructing and interacting with the a digital twin on an edge device. These are low-level abilities and do not necessarily provide a consumer with the best experience. They can be used as building blocks to build facades that provide a consumer with a better experience. This design specifies a graph-based facade, which will be named the Digital Twin Graph service. ## Architecture From 9d5404d1117c085f83d450297c106b9cf0b3a86d Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:53:49 -0700 Subject: [PATCH 038/109] Digital Twin Graph --- .../modules/digital_twin_graph/README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 99a866f2..7b0fb60a 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -6,18 +6,22 @@ ## Introduction -Ibeji today provides the foundations for constructing and interacting with the a digital twin on an edge device. These are low-level abilities and do not necessarily provide a consumer with the best experience. They can be used as building blocks to build facades that provide a consumer with a better experience. This design specifies a graph-based facade, which will be named the Digital Twin Graph service. - +Ibeji today provides the foundations for constructing and interacting with the a digital twin on an edge device. These are low-level abilities and do not necessarily +provide a consumer with the best experience. They can be used as building blocks to build facades that provide a consumer with a better experience. This design +specifies a graph-based facade, which will be named the Digital Twin Graph service. ## Architecture -Ibeji's Application Server, which we will refer to as "Digital Twin App Server", has a modular architecture that allows new services to readily be added and existing services to readily be removed. It also has build-time feature switches for controlling which service should be available at run-time. Ibeji's initial service, the Invehicle Digital Twin service, was developed before the adoption of the modular architecture, but it will eventually be migrated across to it. +Ibeji's Application Server, which we will refer to as "Digital Twin App Server", has a modular architecture that allows new services to readily be added and existing +services to readily be removed. It also has build-time feature switches for controlling which service should be available at run-time. Ibeji's initial service, the +Invehicle Digital Twin service, was developed before the adoption of the modular architecture, but it will eventually be migrated across to it. -We will introduce a new service named "Digital Twin Graph" that will provide a facade for the Invehicle Digital Twin service and the providers. Ideally, the consumer will not need to directly interact with provider -endpoints. Instead, they will interact with a graph structure that represents the digital twin, +We will introduce a new service named "Digital Twin Graph" that will provide a facade for the Invehicle Digital Twin service and the providers. Ideally, the consumer +will not need to directly interact with provider endpoints. Instead, they will interact with a graph structure that represents the digital twin, -Ibeji's existing Invehicle Digital Twin service needs some adjustments to support the Digital Twin Graph service. There is a future plan plan to rename it as the Digital Twin Registry service. -We will introduce a modified form of the service under the name "Digital Twin Registry" and for now keep the existing functionality intact under the original name "Invehicle Digital Twin". +Ibeji's existing Invehicle Digital Twin service needs some adjustments to support the Digital Twin Graph service. There is a future plan plan to rename it as the +Digital Twin Registry service. We will introduce a modified form of the service under the name "Digital Twin Registry" and for now keep the existing functionality +intact under the original name "Invehicle Digital Twin". ![Component Diagram](diagrams/digital_twin_graph_component.svg) From 2aed3581d70b19f745f40f583309488f17f26c19 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:18:54 -0700 Subject: [PATCH 039/109] Digital Twin Graph --- .../digital_twin_graph_component.puml | 16 +++-- .../diagrams/digital_twin_graph_component.svg | 40 +++++------- .../diagrams/find_sequence.puml | 54 +++++++++++++--- .../diagrams/find_sequence.svg | 64 +++++++++++++++---- .../diagrams/get_sequence.puml | 38 +++++++++-- .../diagrams/get_sequence.svg | 46 ++++++++++--- .../diagrams/invoke_sequence.puml | 25 ++++++-- .../diagrams/invoke_sequence.svg | 35 +++++++--- .../diagrams/set_sequence.puml | 24 +++++-- .../diagrams/set_sequence.svg | 34 ++++++---- 10 files changed, 280 insertions(+), 96 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml index 63a405fe..1e3f16a5 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.puml @@ -11,20 +11,26 @@ component "Digital Twin App Server" { component "Digital Twin Registry Service" { interface "Digital Twin Registry Interface" } + component "Invehicle Digital Twin Service" { + interface "Invehicle Digital Twin Interface" + } + component "Managed Subscribe Service" { + interface "Managed Subscribe Interface" + } } component "Digital Twin Provider" { interface "Request Interface" } -"Digital Twin Provider" -left-> "Digital Twin Registry Interface" : Register +"Digital Twin Provider" -up-> "Digital Twin Registry Interface" : Register -"Digital Twin Consumer" -right-> "Digital Twin Graph Interface" : Find/Get/Set/Invoke +"Digital Twin Consumer" -down-> "Digital Twin Graph Interface" : Find/Get/Set/Invoke -"Digital Twin Graph Service" -down-> "Digital Twin Registry Interface": FindByModelId +"Digital Twin Graph Service" -left-> "Digital Twin Registry Interface": FindByModelId -"Digital Twin Graph Service" -right-> "Request Interface": Ask +"Digital Twin Graph Service" -down-> "Request Interface": Ask -"Digital Twin Provider" -left-> "Respond Interface": Answer +"Digital Twin Provider" -up-> "Respond Interface": Answer @enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index 901d7799..10eb9b8b 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,20 +1,5 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml index b35e6279..08040588 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml @@ -7,25 +7,63 @@ participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY participant "Digital Twin Provider" as PROVIDER -CONSUMER -> DIGITAL_TWIN_GRAPH: Find(model_id: "dtmi:sdv:vehicle;1") - request +CONSUMER -> DIGITAL_TWIN_GRAPH: Find(model_id: "dtmi:sdv:seat;1") - request -DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByModeld(model_id: "dtmi:sdv:seat;1") - request DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByModelId - response note left - list of value (serialized as a JSON-LD string) + list of EndpointInfo + + [ + { + model_id : "dtmi:sdv:seat;1" + instance_id: "front left seat" + protocol: "grpc" + operations: ["get", "invoke"] + uri: Digital Twin Provider's uri + }, + { + model_id : "dtmi:sdv:seat;1" + instance_id: "front right seat" + protocol: "grpc" + operations: ["get", "invoke"] + uri: Digital Twin Provider's uri + } + ] end note loop Iterate over the results from the FindByModelId call -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri, ask_id: "1", payload) -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri, ask_id: "2", payload) -DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload) -DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "2", payload) + DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" }) + DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload: instance value as JSON-LD string) end CONSUMER <- DIGITAL_TWIN_GRAPH: Find - response note left - list of EntityAccessInfo + list of instance values as JSON-LD string + + [ + { + "@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"] + "@id": "front left seat", + "@type": "dtmi:sdv:seat;1", + "seat_massager": [ + { + "@id": "front left seat massager" + } + ] + }, + { + "@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"] + "@id": "front right seat", + "@type": "dtmi:sdv:seat;1", + "seat_massager": [ + { + "@id": "front right seat massager" + } + ] + } + ] end note @enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index 3ffa34fc..dcbf59d4 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:vehicle;1") - request2FindByModeld(model_id: "dtmi:sdv:vehcile;1") - request3FindByModelId - responselist of value (serialized as a JSON-LD string)loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri, ask_id: "1", payload)5Ask(respond_uri, ask_id: "2", payload)6Answer(ask_id: "1", payload)7Answer(ask_id: "2", payload)8Find - responselist of EntityAccessInfo \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml index 71181723..fa275fb5 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml @@ -7,15 +7,41 @@ participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY participant "Digital Twin Provider" as PROVIDER -CONSUMER -> DIGITAL_TWIN_GRAPH: Get(instance_id: "1234567890", member_path: "vehicle_identification") - request +CONSUMER -> DIGITAL_TWIN_GRAPH: Get(instance_id: "the vehicle", member_path: "vehicle_identification") - request -DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "1234567890") - request +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "the vehicle") - request DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response - -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Get - -DIGITAL_TWIN_GRAPH <- PROVIDER: Answer +note left + list of EndpointInfo + + [ + { + model_id : "dtmi:sdv:vehicle;1" + instance_id: "the vehicle" + protocol: "grpc" + operations: ["Get"] + uri: Digital Twin Provider's uri + } + ] +end note + +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"}) +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "3", payload: instance value as JSON-LD string) CONSUMER <- DIGITAL_TWIN_GRAPH: Get - response +note left + instance value as JSON-LD string + + { + "@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"] + "@type": "dtmi:sdv:vehicle:vehicle_identification;1", + "vehicle_identification": [ + { + "vin": "1HGCM82633A123456" + } + ] + } + +end note @enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index 5cb2dffc..bdca5fe0 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "1234567890", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Get5Answer6Get - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml index 6d9148d9..88159fa0 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml @@ -7,14 +7,31 @@ participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY participant "Digital Twin Provider" as PROVIDER -CONSUMER -> DIGITAL_TWIN_GRAPH: Invoke(instance_id: "1234567890", payload: "") - request +CONSUMER -> DIGITAL_TWIN_GRAPH: Invoke(instance_id: "front left seat massager", member_path: "perform_step", request_payload: request payload as JSON-LD string) - request -DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(id: "1234567890") - request +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(id: "front left seat massager") - request DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response +note left + list of EndpointInfo -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - request -DIGITAL_TWIN_GRAPH <- PROVIDER: Answer + [ + { + model_id : "dtmi:sdv:premium_airbag_seat_massager;1" + instance_id: "front left seat massager" + protocol: "grpc" + operations: ["get", "invoke"] + uri: Digital Twin Provider's uri + } + ] + +end note + +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string}) +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "5", payload: response payload as JSON-LD string) CONSUMER <- DIGITAL_TWIN_GRAPH: Invoke - response +note left + response payload as JSON-LD string +end note @enduml diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 62080436..69c02004 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "1234567890", payload: "") - request2FindByInstanceId(id: "1234567890") - request3FindByInstanceId - response4Ask - request5Answer6Invoke - response \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml index 402b4060..39353361 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml @@ -7,14 +7,26 @@ participant "Digital Twin Graph" as DIGITAL_TWIN_GRAPH participant "Digital Twin Registry" as DIGITAL_TWIN_REGISTRY participant "Digital Twin Provider" as PROVIDER -CONSUMER -> DIGITAL_TWIN_GRAPH: Set(instance_id: "1234567890", member_path: "vehicle_identification/vin", value) - request +CONSUMER -> DIGITAL_TWIN_GRAPH: Set(instance_id: "the vehicle", member_path: "vehicle_identification/vin", value: value as JSON-LD string) - request -DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "1234567890") - request +DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "the vehicle") - request DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response - -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask - Set - -DIGITAL_TWIN_GRAPH <- PROVIDER: Answer +note left + list of EndpointInfo + + [ + { + model_id : "dtmi:sdv:vehicle;1" + instance_id: "the vehicle" + protocol: "grpc" + operations: ["Get"] + uri: Digital Twin Provider's uri + } + ] +end note + +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string}) +DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "4", payload: "") CONSUMER <- DIGITAL_TWIN_GRAPH: Set - response diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg index 8e37959b..4a4b2262 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "1234567890", member_path: "vehicle_identification/vin", value) - request2FindByInstanceId(instance_id: "1234567890") - request3FindByInstanceId - response4Ask - Set5Answer6Set - response \ No newline at end of file From 0a881d4eb3d5150740c6444afcb6bf29c4f6689a Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Tue, 30 Apr 2024 06:20:06 +0000 Subject: [PATCH 040/109] Generate PlantUML Diagrams --- .../diagrams/digital_twin_graph_component.svg | 28 +++++++++++++++---- .../diagrams/find_sequence.svg | 10 +++---- .../diagrams/get_sequence.svg | 10 +++---- .../diagrams/invoke_sequence.svg | 10 +++---- .../diagrams/set_sequence.svg | 10 +++---- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index 10eb9b8b..80585b63 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,5 +1,24 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index dcbf59d4..8b4f2678 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:seat;1") - request2FindByModeld(model_id: "dtmi:sdv:seat;1") - request3FindByModelId - responselist of EndpointInfo[{model_id : "dtmi:sdv:seat;1"instance_id: "front left seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri},{model_id : "dtmi:sdv:seat;1"instance_id: "front right seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" })5Answer(ask_id: "1", payload: instance value as JSON-LD string)6Find - responselist of instance values as JSON-LD string[{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front left seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front left seat massager"}]},{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front right seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front right seat massager"}]}] \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index bdca5fe0..e77425e0 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "the vehicle", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"})5Answer(ask_id: "3", payload: instance value as JSON-LD string)6Get - responseinstance value as JSON-LD string{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@type": "dtmi:sdv:vehicle:vehicle_identification;1","vehicle_identification": [{"vin": "1HGCM82633A123456"}]} \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 69c02004..37aa956f 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "front left seat massager", member_path: "perform_step", request_payload: request payload as JSON-LD string) - request2FindByInstanceId(id: "front left seat massager") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front left seat massager"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string})5Answer(ask_id: "5", payload: response payload as JSON-LD string)6Invoke - responseresponse payload as JSON-LD string \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg index 4a4b2262..68dd9b8e 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "the vehicle", member_path: "vehicle_identification/vin", value: value as JSON-LD string) - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string})5Answer(ask_id: "4", payload: "")6Set - response \ No newline at end of file From 8c60c7c637b92f5f503848cd9f542d54f64bb70e Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:27:22 -0700 Subject: [PATCH 041/109] Digital Twin Graph --- .../modules/digital_twin_graph/diagrams/find_sequence.puml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml index 08040588..535fd27c 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml @@ -35,7 +35,7 @@ end note loop Iterate over the results from the FindByModelId call DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" }) - DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload: instance value as JSON-LD string) + DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload: instance values as JSON-LD string) end CONSUMER <- DIGITAL_TWIN_GRAPH: Find - response From 23cdfc352dd6efc1ea1085a6f93adc321b0bcb72 Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Tue, 30 Apr 2024 06:28:50 +0000 Subject: [PATCH 042/109] Generate PlantUML Diagrams --- .../modules/digital_twin_graph/diagrams/find_sequence.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index 8b4f2678..86314870 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,4 +1,4 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:seat;1") - request2FindByModeld(model_id: "dtmi:sdv:seat;1") - request3FindByModelId - responselist of EndpointInfo[{model_id : "dtmi:sdv:seat;1"instance_id: "front left seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri},{model_id : "dtmi:sdv:seat;1"instance_id: "front right seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" })5Answer(ask_id: "1", payload: instance value as JSON-LD string)6Find - responselist of instance values as JSON-LD string[{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front left seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front left seat massager"}]},{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front right seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front right seat massager"}]}]Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml index 535fd27c..08040588 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml @@ -35,7 +35,7 @@ end note loop Iterate over the results from the FindByModelId call DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" }) - DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload: instance values as JSON-LD string) + DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload: instance value as JSON-LD string) end CONSUMER <- DIGITAL_TWIN_GRAPH: Find - response diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index 86314870..dcbf59d4 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:seat;1") - request2FindByModeld(model_id: "dtmi:sdv:seat;1") - request3FindByModelId - responselist of EndpointInfo[{model_id : "dtmi:sdv:seat;1"instance_id: "front left seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri},{model_id : "dtmi:sdv:seat;1"instance_id: "front right seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" })5Answer(ask_id: "1", payload: instance values as JSON-LD string)6Find - responselist of instance values as JSON-LD string[{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front left seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front left seat massager"}]},{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front right seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front right seat massager"}]}] \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index e77425e0..bdca5fe0 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "the vehicle", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"})5Answer(ask_id: "3", payload: instance value as JSON-LD string)6Get - responseinstance value as JSON-LD string{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@type": "dtmi:sdv:vehicle:vehicle_identification;1","vehicle_identification": [{"vin": "1HGCM82633A123456"}]} \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 37aa956f..69c02004 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "front left seat massager", member_path: "perform_step", request_payload: request payload as JSON-LD string) - request2FindByInstanceId(id: "front left seat massager") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front left seat massager"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string})5Answer(ask_id: "5", payload: response payload as JSON-LD string)6Invoke - responseresponse payload as JSON-LD string \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg index 68dd9b8e..4a4b2262 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "the vehicle", member_path: "vehicle_identification/vin", value: value as JSON-LD string) - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string})5Answer(ask_id: "4", payload: "")6Set - response \ No newline at end of file From a2d05f8b00d939180e12fbcc379135f45613038d Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Tue, 30 Apr 2024 16:59:57 +0000 Subject: [PATCH 044/109] Generate PlantUML Diagrams --- .../diagrams/digital_twin_graph_component.svg | 28 +++++++++++++++---- .../diagrams/find_sequence.svg | 10 +++---- .../diagrams/get_sequence.svg | 10 +++---- .../diagrams/invoke_sequence.svg | 10 +++---- .../diagrams/set_sequence.svg | 10 +++---- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index 10eb9b8b..80585b63 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,5 +1,24 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index dcbf59d4..8b4f2678 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:seat;1") - request2FindByModeld(model_id: "dtmi:sdv:seat;1") - request3FindByModelId - responselist of EndpointInfo[{model_id : "dtmi:sdv:seat;1"instance_id: "front left seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri},{model_id : "dtmi:sdv:seat;1"instance_id: "front right seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" })5Answer(ask_id: "1", payload: instance value as JSON-LD string)6Find - responselist of instance values as JSON-LD string[{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front left seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front left seat massager"}]},{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front right seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front right seat massager"}]}] \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index bdca5fe0..e77425e0 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "the vehicle", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"})5Answer(ask_id: "3", payload: instance value as JSON-LD string)6Get - responseinstance value as JSON-LD string{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@type": "dtmi:sdv:vehicle:vehicle_identification;1","vehicle_identification": [{"vin": "1HGCM82633A123456"}]} \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 69c02004..37aa956f 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "front left seat massager", member_path: "perform_step", request_payload: request payload as JSON-LD string) - request2FindByInstanceId(id: "front left seat massager") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front left seat massager"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string})5Answer(ask_id: "5", payload: response payload as JSON-LD string)6Invoke - responseresponse payload as JSON-LD string \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg index 4a4b2262..68dd9b8e 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "the vehicle", member_path: "vehicle_identification/vin", value: value as JSON-LD string) - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string})5Answer(ask_id: "4", payload: "")6Set - response \ No newline at end of file From 26088cb88c4bb29c623abd4b5ec309169a4927e7 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 30 Apr 2024 10:58:51 -0700 Subject: [PATCH 045/109] Digital Twin Graph --- .../modules/digital_twin_graph/.accepted_words.txt | 1 + docs/design/modules/digital_twin_graph/README.md | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index c44de012..b01fc87c 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -1,3 +1,4 @@ +Agemo App Ibeji Ibeji's diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index ca52ca0b..8685820b 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -6,11 +6,15 @@ ## Introduction +Ibeji today provides the foundations for constructing and interacting with a digital twin on an edge device. These are low-level abilities and do not necessarily +provide a consumer with the best interaction experience. They can be used as building blocks to build facades that provide a consumer with an abstraction that +delivers a better interaction experience. In the future, Ibeji may support multiple facades and the user can select the one that they prefer to use. +This design specifies a graph-based facade, which will be named the Digital Twin Graph service. The digital twin will be represented as a graph of digital twin +entities whose arcs represent the relationships between those entities. -Ibeji today provides the foundations for constructing and interacting with a digital twin on an edge device. These are low-level abilities and do not necessarily -provide a consumer with the best experience. They can be used as building blocks to build facades that provide a consumer with a better experience. This design -specifies a graph-based facade, which will be named the Digital Twin Graph service. +Please note that Ibeji is intended for use on an edge device to satisfy IoT needs. It is not intended for use in the cloud. The data that it manages can be +transferred to the cloud, through components like Freyja. ## Architecture @@ -25,7 +29,7 @@ Ibeji's existing Invehicle Digital Twin service needs some adjustments to suppor Digital Twin Registry service. We will introduce a modified form of the service under the name "Digital Twin Registry" and for now keep the existing functionality intact under the original name "Invehicle Digital Twin". -The Managed Subscriber service is an optional service that provides integration with Agemo. It has been included in the component diagram for completness sake. +The Managed Subscriber service is an optional service that provides integration with Agemo. It has been included in the component diagram for completeness sake. ![Component Diagram](diagrams/digital_twin_graph_component.svg) @@ -50,5 +54,5 @@ The Digital Twin's set operation allows you to modify an instance value. You can ### Invoke -The Digital Twin's invoke operation allows you to call an instance's command. You can use the member path to specify which of the instance's command is to be perfromed. +The Digital Twin's invoke operation allows you to call an instance's command. You can use the member path to specify which of the instance's command is to be performed. ![Invoke Sequence Diagram](diagrams/invoke_sequence.svg) From 66693e2d17307d246c3ad3ed2e2e71ee6c83c783 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:01:37 -0700 Subject: [PATCH 046/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/.accepted_words.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index b01fc87c..d53f6a2d 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -1,6 +1,8 @@ Agemo App +Freyja Ibeji Ibeji's +IoT Invehicle svg From a51723ddf28cc73632de32f1e4bbed9aeddbb4a9 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 30 Apr 2024 13:43:30 -0700 Subject: [PATCH 047/109] Digital Twin Graph --- core/invehicle-digital-twin/src/main.rs | 2 +- samples/common/src/provider_config.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/invehicle-digital-twin/src/main.rs b/core/invehicle-digital-twin/src/main.rs index 6e88f4ea..df681c7e 100644 --- a/core/invehicle-digital-twin/src/main.rs +++ b/core/invehicle-digital-twin/src/main.rs @@ -121,7 +121,7 @@ where error })?; - // Create interceptor layer to be added to the app. + // Create interceptor layer to be added to the app server. let managed_subscribe_layer = GrpcInterceptorLayer::new(Box::new(managed_subscribe_module.create_interceptor())); diff --git a/samples/common/src/provider_config.rs b/samples/common/src/provider_config.rs index a6d44eae..7de7b27c 100644 --- a/samples/common/src/provider_config.rs +++ b/samples/common/src/provider_config.rs @@ -21,6 +21,8 @@ pub fn load_settings() -> Settings { } // Load the settings using the specified config filename. +/// # Arguments +/// * `config_filename` - The name of the configuration file to load. pub fn load_settings_with_config_filename(config_filename: &str) -> Settings { utils::load_settings(config_filename).unwrap() } From 3b3f331ac203e65529dbbeba5a433f61622a0c2a Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:47:03 -0700 Subject: [PATCH 048/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 169 +++++++++--------- 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index d15ffc6d..37077710 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -82,6 +82,7 @@ impl DigitalTwinGraphImpl { protocol: &str, operations: &[String], ) -> Result, String> { + // Define the retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays .take(Self::MAX_RETRIES); @@ -124,6 +125,7 @@ impl DigitalTwinGraphImpl { protocol: &str, operations: &[String], ) -> Result, String> { + // Define the retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays .take(Self::MAX_RETRIES); @@ -165,8 +167,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let request_inner = request.into_inner(); - let model_id = request_inner.model_id; + let find_request = request.into_inner(); + let model_id = find_request.model_id; debug!("Received a find request for model id {model_id}"); @@ -275,9 +277,9 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let request_inner = request.into_inner(); - let instance_id = request_inner.instance_id; - let member_path = request_inner.member_path; + let get_request = request.into_inner(); + let instance_id = get_request.instance_id; + let member_path = get_request.member_path; info!("Received a get request for instance id {instance_id}"); @@ -405,10 +407,10 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { &self, request: tonic::Request, ) -> Result, tonic::Status> { - let request_inner = request.into_inner(); - let instance_id = request_inner.instance_id; - let member_path = request_inner.member_path; - let request_payload = request_inner.request_payload; + let invoke_request = request.into_inner(); + let instance_id = invoke_request.instance_id; + let member_path = invoke_request.member_path; + let request_payload = invoke_request.request_payload; info!("Received an invoke request for instance id {instance_id}"); @@ -424,93 +426,90 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { info!("Found the provider endpoint info list: {provider_endpoint_info_list:?}"); - let mut values = vec![]; + if provider_endpoint_info_list.is_empty() { + return Err(tonic::Status::internal("No providers found")); + } - for provider_endpoint_info in &provider_endpoint_info_list { - let provider_uri = provider_endpoint_info.uri.clone(); - let instance_id = provider_endpoint_info.context.clone(); + // We will only use the first provider. + let provider_endpoint_info = &provider_endpoint_info_list[0]; - let tx = Arc::clone(&self.tx); - let mut rx = tx.subscribe(); + let provider_uri = provider_endpoint_info.uri.clone(); + let instance_id = provider_endpoint_info.context.clone(); - let client_result = RequestClient::connect(provider_uri.clone()).await; - if client_result.is_err() { - warn!("Unable to connect. We will skip this one."); - continue; - } - let mut client = client_result.unwrap(); + let tx = Arc::clone(&self.tx); + let mut rx = tx.subscribe(); - // Note: The ask id must be a universally unique value. - let ask_id = Uuid::new_v4().to_string(); - - let targeted_payload = TargetedPayload { - instance_id: instance_id.clone(), - member_path: member_path.clone(), - operation: digital_twin_operation::INVOKE.to_string(), - payload: request_payload.to_string(), - }; - - // Serialize the targeted payload. - let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); - - let request = tonic::Request::new(AskRequest { - respond_uri: self.respond_uri.clone(), - ask_id: ask_id.clone(), - payload: targeted_payload_json.clone(), - }); - - // Send the ask. - let response = client.ask(request).await; - if let Err(status) = response { - warn!("Unable to call ask, due to {status:?}\nWe will skip this one.."); - continue; - } + let client_result = RequestClient::connect(provider_uri.clone()).await; + if client_result.is_err() { + return Err(tonic::Status::internal("Unable to connect to the provider.")); + } + let mut client = client_result.unwrap(); + + // Note: The ask id must be a universally unique value. + let ask_id = Uuid::new_v4().to_string(); + + let targeted_payload = TargetedPayload { + instance_id: instance_id.clone(), + member_path: member_path.clone(), + operation: digital_twin_operation::INVOKE.to_string(), + payload: request_payload.to_string(), + }; + + // Serialize the targeted payload. + let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); + + let request = tonic::Request::new(AskRequest { + respond_uri: self.respond_uri.clone(), + ask_id: ask_id.clone(), + payload: targeted_payload_json.clone(), + }); + + // Send the ask. + let response = client.ask(request).await; + if let Err(status) = response { + return Err(tonic::Status::internal(format!("Unable to call ask, due to {status:?}"))); + } - // Wait for the answer request. - let mut answer_request: AnswerRequest = Default::default(); - let mut attempts_after_failure = 0; - const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; - while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { - match timeout(Duration::from_secs(5), rx.recv()).await { - Ok(Ok(request)) => { - if ask_id == request.ask_id { - // We have received the answer request that we are expecting. - answer_request = request; - break; - } else { - // Ignore this answer request, as it is not the one that we are expecting. - warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); - // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. - continue; - } - } - Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; - attempts_after_failure += 1; - continue; - } - Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; - attempts_after_failure += 1; + // Wait for the answer request. + let mut answer_request: AnswerRequest = Default::default(); + let mut attempts_after_failure = 0; + const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; + while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { + match timeout(Duration::from_secs(5), rx.recv()).await { + Ok(Ok(request)) => { + if ask_id == request.ask_id { + // We have received the answer request that we are expecting. + answer_request = request; + break; + } else { + // Ignore this answer request, as it is not the one that we are expecting. + warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); + // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. continue; } } + Ok(Err(error_message)) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + Err(error_message) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } } - - info!( - "Received an answer request. The ask_id is '{}'. The payload is '{}", - answer_request.ask_id, answer_request.payload - ); - - values.push(answer_request.payload); } - if values.is_empty() { - return Err(tonic::Status::not_found("No values found")); - } + info!( + "Received an answer request. The ask_id is '{}'. The payload is '{}", + answer_request.ask_id, answer_request.payload + ); - Ok(tonic::Response::new(InvokeResponse { response_payload: values[0].clone() })) + Ok(tonic::Response::new(InvokeResponse { + response_payload: answer_request.payload.clone(), + })) } } From 0aa2baf8c28a3437d83cfbd19348c6ee1e68e5b3 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 30 Apr 2024 21:00:18 -0700 Subject: [PATCH 049/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 141 ++++++++---------- 1 file changed, 65 insertions(+), 76 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 37077710..61c1aa9d 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -293,97 +293,88 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .await .map_err(tonic::Status::internal)?; - info!("Found the provider endpoint info list: {provider_endpoint_info_list:?}"); + if provider_endpoint_info_list.is_empty() { + return Err(tonic::Status::internal("No providers found")); + } - let mut values = vec![]; + // We will only use the first provider. + let provider_endpoint_info = &provider_endpoint_info_list[0]; - for provider_endpoint_info in &provider_endpoint_info_list { - let provider_uri = provider_endpoint_info.uri.clone(); - let instance_id = provider_endpoint_info.context.clone(); + let provider_uri = provider_endpoint_info.uri.clone(); + let instance_id = provider_endpoint_info.context.clone(); - let tx = Arc::clone(&self.tx); - let mut rx = tx.subscribe(); + let tx = Arc::clone(&self.tx); + let mut rx = tx.subscribe(); - let client_result = RequestClient::connect(provider_uri.clone()).await; - if client_result.is_err() { - warn!("Unable to connect. We will skip this one."); - continue; - } - let mut client = client_result.unwrap(); + let client_result = RequestClient::connect(provider_uri.clone()).await; + if client_result.is_err() { + return Err(tonic::Status::internal("Unable to connect to the provider.")); + } + let mut client = client_result.unwrap(); - // Note: The ask id must be a universally unique value. - let ask_id = Uuid::new_v4().to_string(); + // Note: The ask id must be a universally unique value. + let ask_id = Uuid::new_v4().to_string(); - let targeted_payload = TargetedPayload { - instance_id: instance_id.clone(), - member_path: member_path.clone(), - operation: digital_twin_operation::GET.to_string(), - payload: "".to_string(), - }; + let targeted_payload = TargetedPayload { + instance_id: instance_id.clone(), + member_path: member_path.clone(), + operation: digital_twin_operation::GET.to_string(), + payload: "".to_string(), + }; - // Serialize the targeted payload. - let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); + // Serialize the targeted payload. + let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); - let request = tonic::Request::new(AskRequest { - respond_uri: self.respond_uri.clone(), - ask_id: ask_id.clone(), - payload: targeted_payload_json.clone(), - }); + let request = tonic::Request::new(AskRequest { + respond_uri: self.respond_uri.clone(), + ask_id: ask_id.clone(), + payload: targeted_payload_json.clone(), + }); - // Send the ask. - let response = client.ask(request).await; - if let Err(status) = response { - warn!("Unable to call ask, due to {status:?}\nWe will skip this one."); - continue; - } + // Send the ask. + let response = client.ask(request).await; + if let Err(status) = response { + return Err(tonic::Status::internal(format!("Unable to call ask, due to {status:?}"))); + } - // Wait for the answer request. - let mut answer_request: AnswerRequest = Default::default(); - let mut attempts_after_failure = 0; - while attempts_after_failure < Self::MAX_RETRIES { - match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()) - .await - { - Ok(Ok(request)) => { - if ask_id == request.ask_id { - // We have received the answer request that we are expecting. - answer_request = request; - break; - } else { - // Ignore this answer request, as it is not the one that we are expecting. - warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); - // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. - continue; - } - } - Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; - attempts_after_failure += 1; - continue; - } - Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); - sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; - attempts_after_failure += 1; + // Wait for the answer request. + let mut answer_request: AnswerRequest = Default::default(); + let mut attempts_after_failure = 0; + while attempts_after_failure < Self::MAX_RETRIES { + match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()).await { + Ok(Ok(request)) => { + if ask_id == request.ask_id { + // We have received the answer request that we are expecting. + answer_request = request; + break; + } else { + // Ignore this answer request, as it is not the one that we are expecting. + warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); + // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. continue; } } + Ok(Err(error_message)) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; + attempts_after_failure += 1; + continue; + } + Err(error_message) => { + warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; + attempts_after_failure += 1; + continue; + } } - - info!( - "Received an answer request. The ask_id is '{}'. The payload is '{}", - answer_request.ask_id, answer_request.payload - ); - - values.push(answer_request.payload); } - if values.is_empty() { - return Err(tonic::Status::not_found("No values found")); - } + info!( + "Received an answer request. The ask_id is '{}'. The payload is '{}", + answer_request.ask_id, answer_request.payload + ); - Ok(tonic::Response::new(GetResponse { value: values[0].clone() })) + Ok(tonic::Response::new(GetResponse { value: answer_request.payload.clone() })) } /// Set implementation. @@ -424,8 +415,6 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .await .map_err(tonic::Status::internal)?; - info!("Found the provider endpoint info list: {provider_endpoint_info_list:?}"); - if provider_endpoint_info_list.is_empty() { return Err(tonic::Status::internal("No providers found")); } From c663f87eb0c04a4dd60bde94921700da1e8ff228 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 1 May 2024 10:27:03 -0700 Subject: [PATCH 050/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 57 +++++++++++-------- samples/graph/consumer/src/main.rs | 3 + .../graph/seat_massager_provider/src/main.rs | 3 + .../src/request_impl.rs | 3 + .../graph/vehicle_core_provider/src/main.rs | 3 + .../vehicle_core_provider/src/request_impl.rs | 3 + 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 61c1aa9d..e535ae49 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -15,7 +15,7 @@ use core_protobuf_data_access::module::digital_twin_registry::v1::{ EndpointInfo, FindByInstanceIdRequest, FindByInstanceIdResponse, FindByModelIdRequest, FindByModelIdResponse, }; -use log::{debug, info, warn}; +use log::{debug, warn}; use std::sync::Arc; use tokio::sync::broadcast; use tokio::time::{sleep, timeout, Duration}; @@ -36,8 +36,13 @@ pub struct DigitalTwinGraphImpl { } impl DigitalTwinGraphImpl { + /// The base duration in milliseconds for the backoff strategy. const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + + /// The maximum number of retries for the backoff strategy. const MAX_RETRIES: usize = 100; + + /// The timeout period in milliseconds for the backoff strategy. const TIMEOUT_PERIOD_IN_MILLIS: u64 = 5000; /// Create a new instance of a DigitalTwinGraphImpl. @@ -69,14 +74,14 @@ impl DigitalTwinGraphImpl { }) } - /// Use the Digital Twin Registery service to discover the endpoints for digital twin providers that support the specified model id, protocol and operations. - /// Note: This operation will be retried when there is a failure. + /// Use the Digital Twin Registery service to find the endpoints for digital twin providers that support + /// the specified model id, protocol and operations. /// /// # Arguments /// * `model_id` - The matching model id. /// * `protocol` - The required protocol. /// * `operations` - The required operations. - pub async fn discover_digital_twin_providers_with_model_id( + pub async fn find_digital_twin_providers_with_model_id( &self, model_id: &str, protocol: &str, @@ -112,14 +117,13 @@ impl DigitalTwinGraphImpl { .collect()) } - /// Use the Digital Twin Registry service to discover the endpoints for digital twin providers that support the specified instance id, protocol and operations. - /// Note: This operation will be retried when there is a failure. + /// Use the Digital Twin Registry service to find the endpoints for digital twin providers that support the specified instance id, protocol and operations. /// /// # Arguments /// * `instance_id` - The matching instance id. /// * `protocol` - The required protocol. /// * `operations` - The required operations. - pub async fn discover_digital_twin_providers_with_instance_id( + pub async fn find_digital_twin_providers_with_instance_id( &self, instance_id: &str, protocol: &str, @@ -174,7 +178,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Retrieve the provider details. let provider_endpoint_info_list = self - .discover_digital_twin_providers_with_model_id( + .find_digital_twin_providers_with_model_id( model_id.as_str(), digital_twin_protocol::GRPC, &[digital_twin_operation::GET.to_string()], @@ -182,6 +186,10 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .await .map_err(tonic::Status::internal)?; + // TODO: Organize result into a map of instance id to associated endpoint infos. + // This will allow us to ensure that each instance's values only appears + // once in the response. We can also try other providers when one fails to respond. + let mut values = vec![]; for provider_endpoint_info in &provider_endpoint_info_list { @@ -228,7 +236,9 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let mut answer_request: AnswerRequest = Default::default(); let mut attempts_after_failure = 0; while attempts_after_failure < Self::MAX_RETRIES { - match timeout(Duration::from_secs(5), rx.recv()).await { + match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()) + .await + { Ok(Ok(request)) => { if ask_id == request.ask_id { // We have received the answer request that we are expecting. @@ -236,19 +246,18 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { break; } else { // Ignore this answer request, as it is not the one that we are expecting. - warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. continue; } } Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; } Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; @@ -281,11 +290,11 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let instance_id = get_request.instance_id; let member_path = get_request.member_path; - info!("Received a get request for instance id {instance_id}"); + debug!("Received a get request for instance id {instance_id}"); // Retrieve the provider details. let provider_endpoint_info_list = self - .discover_digital_twin_providers_with_instance_id( + .find_digital_twin_providers_with_instance_id( instance_id.as_str(), digital_twin_protocol::GRPC, &[digital_twin_operation::GET.to_string()], @@ -349,19 +358,18 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { break; } else { // Ignore this answer request, as it is not the one that we are expecting. - warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. continue; } } Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; } Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; attempts_after_failure += 1; continue; @@ -369,7 +377,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { } } - info!( + debug!( "Received an answer request. The ask_id is '{}'. The payload is '{}", answer_request.ask_id, answer_request.payload ); @@ -403,11 +411,11 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let member_path = invoke_request.member_path; let request_payload = invoke_request.request_payload; - info!("Received an invoke request for instance id {instance_id}"); + debug!("Received an invoke request for instance id {instance_id}"); // Retrieve the provider details. let provider_endpoint_info_list = self - .discover_digital_twin_providers_with_instance_id( + .find_digital_twin_providers_with_instance_id( instance_id.as_str(), digital_twin_protocol::GRPC, &[digital_twin_operation::INVOKE.to_string()], @@ -464,7 +472,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let mut attempts_after_failure = 0; const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { - match timeout(Duration::from_secs(5), rx.recv()).await { + match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()).await { Ok(Ok(request)) => { if ask_id == request.ask_id { // We have received the answer request that we are expecting. @@ -472,19 +480,18 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { break; } else { // Ignore this answer request, as it is not the one that we are expecting. - warn!("Received an unexpected answer request with ask_id '{}'. We will retry in a moment.", request.ask_id); // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. continue; } } Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); sleep(Duration::from_secs(1)).await; attempts_after_failure += 1; continue; } Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We will retry in a moment.", error_message); + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); sleep(Duration::from_secs(1)).await; attempts_after_failure += 1; continue; @@ -492,7 +499,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { } } - info!( + debug!( "Received an answer request. The ask_id is '{}'. The payload is '{}", answer_request.ask_id, answer_request.payload ); diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 9a72564f..157ef3d5 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -15,7 +15,10 @@ use samples_protobuf_data_access::digital_twin_graph::v1::digital_twin_graph::{F use tokio_retry::Retry; use tokio_retry::strategy::{ExponentialBackoff, jitter}; +// The base duration in milliseconds for the exponential backoff strategy. const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + +// The maximum number of retries for the exponential backoff strategy. const MAX_RETRIES: usize = 100; /// Connect to the digital twin graph service. diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/graph/seat_massager_provider/src/main.rs index 40f95640..ac82cc50 100644 --- a/samples/graph/seat_massager_provider/src/main.rs +++ b/samples/graph/seat_massager_provider/src/main.rs @@ -25,7 +25,10 @@ use tokio_retry::strategy::{ExponentialBackoff, jitter}; use crate::request_impl::{InstanceData, ProviderState, RequestImpl}; +/// The base duration in milliseconds for the exponential backoff strategy. const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + +/// The maximum number of retries. const MAX_RETRIES: usize = 100; /// Add an entry to the instance map. diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/graph/seat_massager_provider/src/request_impl.rs index b9ca1ea0..55929a59 100644 --- a/samples/graph/seat_massager_provider/src/request_impl.rs +++ b/samples/graph/seat_massager_provider/src/request_impl.rs @@ -44,7 +44,10 @@ pub struct RequestImpl { /// The implementation for the Request interface, which is used to handle requests from the consumer. impl RequestImpl { + /// The base duration for the exponential backoff strategy in milliseconds. const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + + /// The maximum number of retries. const MAX_RETRIES: usize = 100; /// Get implementation. diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/graph/vehicle_core_provider/src/main.rs index 95a2ba74..8e1b14c4 100644 --- a/samples/graph/vehicle_core_provider/src/main.rs +++ b/samples/graph/vehicle_core_provider/src/main.rs @@ -25,7 +25,10 @@ use tokio_retry::strategy::{ExponentialBackoff, jitter}; use crate::request_impl::{InstanceData, ProviderState, RequestImpl}; +/// The base duration in milliseconds for the exponential backoff strategy. const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + +/// The maximum number of retries. const MAX_RETRIES: usize = 100; /// Add an entry to the instance map. diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs index f43ef7bb..8e19c7df 100644 --- a/samples/graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -43,7 +43,10 @@ pub struct RequestImpl { /// The implementation for the Request interface, which is used to handle requests from the consumer. impl RequestImpl { + /// const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; + + /// const MAX_RETRIES: usize = 100; /// Get implementation. From de66a57cc602880d336ff63a141951f1a68b9df8 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 1 May 2024 15:32:41 -0700 Subject: [PATCH 051/109] Digital Twin Graph --- .../modules/digital_twin_graph/README.md | 3 ++ docs/samples/graph/README.md | 39 +++++++++++++++++++ .../template/digital_twin_graph_settings.yaml | 7 ++++ 3 files changed, 49 insertions(+) create mode 100644 docs/samples/graph/README.md create mode 100644 samples/common/template/digital_twin_graph_settings.yaml diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 8685820b..56380778 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -50,6 +50,9 @@ The Digital Twin's get operation allows you to retrieve an instance value. You c ### Set The Digital Twin's set operation allows you to modify an instance value. You can reduce the scope of the change by specifying a specific member path within the instance. + +Note: This operation will not be implemented during the first phase of the Digital Twin Graph. However, it will implemented soon after. + ![Get Sequence Diagram](diagrams/set_sequence.svg) ### Invoke diff --git a/docs/samples/graph/README.md b/docs/samples/graph/README.md new file mode 100644 index 00000000..e6db1b4a --- /dev/null +++ b/docs/samples/graph/README.md @@ -0,0 +1,39 @@ +# Sample: Graph + +The graph sample demonstrates the use of the Digital Twin Graph service. + +Follow these instructions to run the demo. + +Steps: + +1. The best way to run the demo is by using four windows: one running the In-Vehicle Digital Twin, two running the Digital Twin Providers and one running the Digital Twin Consumer. +Orientate the four windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. +The middle two window can be used for the Digital Twin Providers. The bottom window can be used for the Digital Twin Consumer.
+1. In each window, change directory to the directory containing the build artifacts. +Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

+`cd {repo-root-dir}/target/debug`
+1. Create the four config files with the following contents, if they are not already there:

+---- invehicle_digital_twin_settings.yaml ----
+`invehicle_digital_twin_authority: "0.0.0.0:5010"`

+---- digital_twin_graph_settings.yaml ----
+`base_authority: "0.0.0.0:5010"`
+---- consumer_settings.yaml ----
+`consumer_authority: "0.0.0.0:6010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+---- seat_massager_provider_settings.yaml ----
+`provider_authority: "0.0.0.0:4020"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+1. In the top window, run:

+`./invehicle-digital-twin`
+1. In the second window, run:

+`./graph-vehicle-core-provider`
+1. In the third window, run:

+`./graph-seat-massager-provider`
+1. In the bottom window, run:

+`./graph-consumer`
+1. Use control-c in each of the windows when you wish to stop the demo. + +A templated version of each config file can be found in: + +- {repo-root-dir}/core/invehicle-digital-twin/template +- {repo-root-dir}/samples/common/template diff --git a/samples/common/template/digital_twin_graph_settings.yaml b/samples/common/template/digital_twin_graph_settings.yaml new file mode 100644 index 00000000..3904d059 --- /dev/null +++ b/samples/common/template/digital_twin_graph_settings.yaml @@ -0,0 +1,7 @@ +# +# Digital Twin Graph Settings +# + +# The IP address and port number that the Digital Twin Provider listens on for requests. +# Example: "0.0.0.0:80" +base_authority: <> From 7b2bb1652834fcbb65f331e829c98fba8782be5e Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 1 May 2024 15:55:42 -0700 Subject: [PATCH 052/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index e535ae49..663f0eb4 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -186,13 +186,27 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .await .map_err(tonic::Status::internal)?; - // TODO: Organize result into a map of instance id to associated endpoint infos. - // This will allow us to ensure that each instance's values only appears - // once in the response. We can also try other providers when one fails to respond. + // Build a map of instance id to its associated endpoint infos. + let instance_provide_map: std::collections::HashMap> = + provider_endpoint_info_list + .iter() + .map(|provider_endpoint_info| { + (provider_endpoint_info.context.clone(), provider_endpoint_info.clone()) + }) + .fold( + std::collections::HashMap::new(), + |mut accumulator, (instance_id, endpoint_info)| { + accumulator.entry(instance_id).or_insert_with(Vec::new).push(endpoint_info); + accumulator + }, + ); let mut values = vec![]; - for provider_endpoint_info in &provider_endpoint_info_list { + for instance_id in instance_provide_map.keys().into_iter() { + // We will only use the first provider. For a high availability scenario, we can try multiple providers. + let provider_endpoint_info = &instance_provide_map[instance_id][0]; + let provider_uri = provider_endpoint_info.uri.clone(); let instance_id = provider_endpoint_info.context.clone(); From 8afdd76cd712b5f598526f5bb87568cfd32314de Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 1 May 2024 16:03:41 -0700 Subject: [PATCH 053/109] Digital Twin Graph --- .../digital_twin_graph/src/digital_twin_graph_impl.rs | 2 +- docs/samples/graph/.accepted_words.txt | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 docs/samples/graph/.accepted_words.txt diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 663f0eb4..51e2a1a0 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -203,7 +203,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let mut values = vec![]; - for instance_id in instance_provide_map.keys().into_iter() { + for instance_id in instance_provide_map.keys() { // We will only use the first provider. For a high availability scenario, we can try multiple providers. let provider_endpoint_info = &instance_provide_map[instance_id][0]; diff --git a/docs/samples/graph/.accepted_words.txt b/docs/samples/graph/.accepted_words.txt new file mode 100644 index 00000000..79caae2b --- /dev/null +++ b/docs/samples/graph/.accepted_words.txt @@ -0,0 +1,8 @@ +br +cd +config +dir +invehicle +repo +uri +yaml From 68f1124fe55ef8aabe30aa2f3a7ab683abac3742 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 1 May 2024 16:18:18 -0700 Subject: [PATCH 054/109] Digital Twin Graph --- docs/samples/graph/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/samples/graph/README.md b/docs/samples/graph/README.md index e6db1b4a..ade71859 100644 --- a/docs/samples/graph/README.md +++ b/docs/samples/graph/README.md @@ -16,21 +16,21 @@ Make sure that you replace "{repo-root-dir}" with the repository root directory ---- invehicle_digital_twin_settings.yaml ----
`invehicle_digital_twin_authority: "0.0.0.0:5010"`

---- digital_twin_graph_settings.yaml ----
-`base_authority: "0.0.0.0:5010"`
+`base_authority: "0.0.0.0:5010"`

---- consumer_settings.yaml ----
`consumer_authority: "0.0.0.0:6010"`
`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

---- seat_massager_provider_settings.yaml ----
`provider_authority: "0.0.0.0:4020"`
`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

-1. In the top window, run:

-`./invehicle-digital-twin`
-1. In the second window, run:

-`./graph-vehicle-core-provider`
-1. In the third window, run:

-`./graph-seat-massager-provider`
-1. In the bottom window, run:

-`./graph-consumer`
+1. In the top window, run:
+`./invehicle-digital-twin`

+1. In the second window, run:
+`./graph-vehicle-core-provider`

+1. In the third window, run:
+`./graph-seat-massager-provider`

+1. In the bottom window, run:
+`./graph-consumer`

1. Use control-c in each of the windows when you wish to stop the demo. A templated version of each config file can be found in: From 7b9b4d6d53270462d385ffb7c0040f704e8ce45a Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 1 May 2024 17:30:14 -0700 Subject: [PATCH 055/109] Digital Twin Graph --- .../modules/digital_twin_graph/README.md | 36 ++++++++++--------- docs/samples/graph/README.md | 8 ++--- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 56380778..24ee2b4a 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -8,32 +8,38 @@ Ibeji today provides the foundations for constructing and interacting with a digital twin on an edge device. These are low-level abilities and do not necessarily provide a consumer with the best interaction experience. They can be used as building blocks to build facades that provide a consumer with an abstraction that -delivers a better interaction experience. In the future, Ibeji may support multiple facades and the user can select the one that they prefer to use. +delivers a much better interaction experience. In the future, Ibeji may support multiple facades and the user can select the one that they prefer to use. -This design specifies a graph-based facade, which will be named the Digital Twin Graph service. The digital twin will be represented as a graph of digital twin -entities whose arcs represent the relationships between those entities. +This design specifies a graph-based facade, which will be named the Digital Twin Graph Service. With this facade, the digital twin will be represented as a +graph of digital twin entities whose arcs represent the relationships between those entities. Instance ids will be used to refer to entities. -Please note that Ibeji is intended for use on an edge device to satisfy IoT needs. It is not intended for use in the cloud. The data that it manages can be +Please note that Ibeji is only intended for use on an IoT edge device. It is not intended for use in the cloud. The data that it manages can be transferred to the cloud, through components like Freyja. ## Architecture -Ibeji's Application Server, which we will refer to as "Digital Twin App Server", has a modular architecture that allows new services to readily be added and existing -services to readily be removed. It also has build-time feature switches for controlling which service should be available at run-time. Ibeji's initial service, the -Invehicle Digital Twin service, was developed before the adoption of the modular architecture, but it will eventually be migrated across to it. +Ibeji's Application Server, which we will refer to as "Digital Twin App Server", has a modular architecture that allows services to readily be added and removed. +It also has build-time feature switches for controlling which service should be available at run-time. Ibeji's initial service, the In-vehicle Digital Twin +service, was developed before the adoption of the modular architecture, so it cannot be readily removed. -We will introduce a new service named "Digital Twin Graph" that will provide a facade for the Invehicle Digital Twin service and the providers. Ideally, the consumer -will not need to directly interact with provider endpoints. Instead, they will interact with a graph structure that represents the digital twin, +We will introduce a new service named "Digital Twin Graph" that will provide a facade for interactions with the In-vehicle Digital Twin Service and the +providers. Ideally, the consumer will not need to directly interact with provider endpoints. Instead, the consumer will interact with a graph structure that +represents the digital twin. -Ibeji's existing Invehicle Digital Twin service needs some adjustments to support the Digital Twin Graph service. There is a future plan plan to rename it as the -Digital Twin Registry service. We will introduce a modified form of the service under the name "Digital Twin Registry" and for now keep the existing functionality -intact under the original name "Invehicle Digital Twin". +Ibeji's In-vehicle Digital Twin Service needs some adjustments to support the Digital Twin Graph Service. We will introduce a modified form of the service under the name "Digital Twin Registry" and keep the existing functionality intact under the original In-vehcile Digital Twin Service. -The Managed Subscriber service is an optional service that provides integration with Agemo. It has been included in the component diagram for completeness sake. +The Managed Subscriber Service is an optional service that provides integration with Agemo. It has been included in the component diagram for completeness sake. ![Component Diagram](diagrams/digital_twin_graph_component.svg) -## Sequences +## Operations + +The Digital Twin Graph Service will support four operations: + +- Find +- Get +- Invoke +- Set (this operation will be implemneted in a later phase) ### Find @@ -51,8 +57,6 @@ The Digital Twin's get operation allows you to retrieve an instance value. You c The Digital Twin's set operation allows you to modify an instance value. You can reduce the scope of the change by specifying a specific member path within the instance. -Note: This operation will not be implemented during the first phase of the Digital Twin Graph. However, it will implemented soon after. - ![Get Sequence Diagram](diagrams/set_sequence.svg) ### Invoke diff --git a/docs/samples/graph/README.md b/docs/samples/graph/README.md index ade71859..3ac27202 100644 --- a/docs/samples/graph/README.md +++ b/docs/samples/graph/README.md @@ -8,11 +8,11 @@ Steps: 1. The best way to run the demo is by using four windows: one running the In-Vehicle Digital Twin, two running the Digital Twin Providers and one running the Digital Twin Consumer. Orientate the four windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. -The middle two window can be used for the Digital Twin Providers. The bottom window can be used for the Digital Twin Consumer.
+The middle two windows can be used for the Digital Twin Providers. The bottom window can be used for the Digital Twin Consumer.
1. In each window, change directory to the directory containing the build artifacts. -Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

-`cd {repo-root-dir}/target/debug`
-1. Create the four config files with the following contents, if they are not already there:

+Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.
+`cd {repo-root-dir}/target/debug`

+1. Create the four config files with the following contents, if they are not already there:
---- invehicle_digital_twin_settings.yaml ----
`invehicle_digital_twin_authority: "0.0.0.0:5010"`

---- digital_twin_graph_settings.yaml ----
From 05345abea9166210f307fbee6a0fbb399409ff5e Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 1 May 2024 17:34:38 -0700 Subject: [PATCH 056/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 24ee2b4a..c7d96586 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -2,7 +2,7 @@ - [Introduction](#introduction) - [Architecture](#architecture) -- [Sequences](#sequences) +- [Operations](#operations) ## Introduction @@ -26,7 +26,7 @@ We will introduce a new service named "Digital Twin Graph" that will provide a f providers. Ideally, the consumer will not need to directly interact with provider endpoints. Instead, the consumer will interact with a graph structure that represents the digital twin. -Ibeji's In-vehicle Digital Twin Service needs some adjustments to support the Digital Twin Graph Service. We will introduce a modified form of the service under the name "Digital Twin Registry" and keep the existing functionality intact under the original In-vehcile Digital Twin Service. +Ibeji's In-vehicle Digital Twin Service needs some adjustments to support the Digital Twin Graph Service. We will introduce a modified form of the service under the name "Digital Twin Registry" and keep the existing functionality intact under the original In-vehicle Digital Twin Service. The Managed Subscriber Service is an optional service that provides integration with Agemo. It has been included in the component diagram for completeness sake. @@ -39,7 +39,7 @@ The Digital Twin Graph Service will support four operations: - Find - Get - Invoke -- Set (this operation will be implemneted in a later phase) +- Set (this operation will be implemented in a later phase) ### Find From 1807489faaf9426bf16132e40b0776cf90355059 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 2 May 2024 09:29:03 -0700 Subject: [PATCH 057/109] Digital Twin Graph --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 36a5f1de..261034bb 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,12 @@ Once you have installed the prerequisites, go to your enlistment's root director `cargo build` -This should build all of the libraries and executables. +This will build all of the foundation libraries and executables. + +Ibeji also has add-on modules that rely on feature flags to include them in the build. For example, to build Ibeji with the Digital Twin Graph +and the Digital Twin Registry modules run: + +`cargo build --features "digital_twin_graph,digital_twin_registry"` ### Tokio Console Support From 5ae74c4ea4dbba5ffcdb5bd9c18d3de8bdd811b1 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 2 May 2024 10:05:03 -0700 Subject: [PATCH 058/109] Digital Twin Graph --- README.md | 8 ++++---- .../digital_twin_graph/src/digital_twin_graph_module.rs | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 261034bb..06c5f057 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Once you have installed the prerequisites, go to your enlistment's root director This will build all of the foundation libraries and executables. -Ibeji also has add-on modules that rely on feature flags to include them in the build. For example, to build Ibeji with the Digital Twin Graph +Ibeji also has add-on modules that rely on feature flags to include them in the build. For example, to build Ibeji with the Digital Twin Graph and the Digital Twin Registry modules run: `cargo build --features "digital_twin_graph,digital_twin_registry"` @@ -138,7 +138,7 @@ Currently, we have no integration tests or end-to-end tests. There are currently four samples: one that demonstrates the use of a property, one that demonstrates the use of a command, one that demonstrates the mixed use of properties and commands and one that demonstrates the use of get/set for a seat massager. -The demos use config files and we have provided a templated version of each config file. These templates can be found in: +The demos use config files and we have provided a templated version of each config file. These templates can be found in: - {repo-root-dir}/core/invehicle-digital-twin/template - {repo-root-dir}/samples/common/template @@ -153,11 +153,11 @@ IBEJI_HOME=/etc/ibeji ./invehicle-digital-twin The above example tells `invehicle-digital-twin` to load configuration files from `/etc/ibeji` instead of using the current working directory. -Chariott may be used to discover the in-vehicle digital twin service. We will discuss how to enable this feature. +Chariott may be used to discover the in-vehicle digital twin service. We will discuss how to enable this feature. ### Property Sample -The following instructions are for the demo for the use of a property. This sample uses a MQTT Broker; please make sure that it is running. +The following instructions are for the demo for the use of a property. This sample uses a MQTT Broker; please make sure that it is running. Steps: diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs index d4e7c3d9..90f1e5c8 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_module.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_module.rs @@ -13,6 +13,9 @@ use crate::digital_twin_graph_config; use crate::digital_twin_graph_impl::DigitalTwinGraphImpl; use crate::respond_impl::RespondImpl; +/// The capacity of the broadcast channel. +const BROADCAST_CHANNEL_CAPACITY: usize = 100; + /// Digital Twin Graph Module. #[derive(Clone, Debug)] pub struct DigitalTwinGraphModule {} @@ -37,7 +40,7 @@ impl GrpcModule for DigitalTwinGraphModule { let invehicle_digital_twin_uri = format!("http://{base_authority}"); // Devskim: ignore DS137138 let respond_uri = format!("http://{base_authority}"); // Devskim: ignore DS137138 - let (tx, _rx) = broadcast::channel(100); + let (tx, _rx) = broadcast::channel(BROADCAST_CHANNEL_CAPACITY); let tx = Arc::new(tx); // Setup the respond service. From 22a6f4c3d992046dede5b9f87624600d22592ebc Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 2 May 2024 10:19:06 -0700 Subject: [PATCH 059/109] Digital Twin Graph --- core/module/digital_twin_graph/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/module/digital_twin_graph/src/lib.rs b/core/module/digital_twin_graph/src/lib.rs index a17f09b7..e4209b99 100644 --- a/core/module/digital_twin_graph/src/lib.rs +++ b/core/module/digital_twin_graph/src/lib.rs @@ -19,7 +19,7 @@ pub struct TargetedPayload { pub member_path: String, /// The operation to be performed on the target entity's member. pub operation: String, - /// The operation's payload. + /// The operation's payload. It will be empty when the operation does not require a payload. pub payload: String, } @@ -42,7 +42,7 @@ pub mod digital_twin_operation { pub const MANAGEDSUBSCRIBE: &str = "ManagedSubscribe"; } -// Supported digital twin protocols. +/// Supported digital twin protocols. pub mod digital_twin_protocol { pub const GRPC: &str = "grpc"; pub const MQTT: &str = "mqtt"; From 4a590fb668bd869870bcd26a3e65c1d705bbe293 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 2 May 2024 20:32:06 -0700 Subject: [PATCH 060/109] Digital Twin Graph --- README.md | 20 +++++++++++++------ core/invehicle-digital-twin/src/main.rs | 2 +- .../modules/digital_twin_graph/README.md | 2 +- docs/samples/graph/README.md | 4 ++-- samples/graph/consumer/src/main.rs | 2 +- .../vehicle_core_provider/src/request_impl.rs | 4 ++-- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 06c5f057..7df5c28c 100644 --- a/README.md +++ b/README.md @@ -98,26 +98,32 @@ Instructions for installing Mosquitto can be found [here](https://github.com/ecl The repo has two submodules [opendigitaltwins-dtdl](https://github.com/Azure/opendigitaltwins-dtdl) and [iot-plugandplay-models](https://github.com/Azure/iot-plugandplay-models) that provide DTDL context files and DTDL samples file. To ensure that these are included, please use the following command when cloning Ibeji's github repo: -`git clone --recurse-submodules https://github.com/eclipse-ibeji/ibeji` +````shell +git clone --recurse-submodules https://github.com/eclipse-ibeji/ibeji` +```` ## Building Once you have installed the prerequisites, go to your enlistment's root directory and run: -`cargo build` +````shell +cargo build +```` This will build all of the foundation libraries and executables. Ibeji also has add-on modules that rely on feature flags to include them in the build. For example, to build Ibeji with the Digital Twin Graph and the Digital Twin Registry modules run: -`cargo build --features "digital_twin_graph,digital_twin_registry"` +````shell +cargo build --features "digital_twin_graph,digital_twin_registry" +```` ### Tokio Console Support Ibeji has support for using the [tokio console](https://github.com/tokio-rs/console) for advanced debugging. To enable this support, you need to build with the `tokio_console` feature enabled and with the `tokio_unstable` config flag for the rust compiler: -```bash +```shell RUSTFLAGS="--cfg tokio_unstable" cargo build --features tokio_console ``` @@ -129,7 +135,9 @@ Note that the tokio console will intercept trace-level logs, so these will not b After successfully building Ibeji, you can run all of the unit tests. To do this go to the enlistment's root directory and run: -`cargo test` +````shell +cargo test +```` Currently, we have no integration tests or end-to-end tests. @@ -146,7 +154,7 @@ The demos use config files and we have provided a templated version of each conf Configuration files will be loaded from the current working directory by default but an `IBEJI_HOME` environment variable can be used to change the base configuration directory to a different one: -```bash +```shell IBEJI_HOME=/etc/ibeji ./invehicle-digital-twin ``` diff --git a/core/invehicle-digital-twin/src/main.rs b/core/invehicle-digital-twin/src/main.rs index df681c7e..6e5916e0 100644 --- a/core/invehicle-digital-twin/src/main.rs +++ b/core/invehicle-digital-twin/src/main.rs @@ -121,7 +121,7 @@ where error })?; - // Create interceptor layer to be added to the app server. + // Create the interceptor layer to be added to the app server. let managed_subscribe_layer = GrpcInterceptorLayer::new(Box::new(managed_subscribe_module.create_interceptor())); diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index c7d96586..d6e88b67 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -14,7 +14,7 @@ This design specifies a graph-based facade, which will be named the Digital Twin graph of digital twin entities whose arcs represent the relationships between those entities. Instance ids will be used to refer to entities. Please note that Ibeji is only intended for use on an IoT edge device. It is not intended for use in the cloud. The data that it manages can be -transferred to the cloud, through components like Freyja. +transferred to the cloud, through components like [Eclipse Freyja](https://github.com/eclipse-ibeji/freyja). ## Architecture diff --git a/docs/samples/graph/README.md b/docs/samples/graph/README.md index 3ac27202..968c1092 100644 --- a/docs/samples/graph/README.md +++ b/docs/samples/graph/README.md @@ -1,6 +1,6 @@ -# Sample: Graph +# Sample: Digital Twin Graph -The graph sample demonstrates the use of the Digital Twin Graph service. +The Digital Twin Graph sample demonstrates the use of the Digital Twin Graph Service. Follow these instructions to run the demo. diff --git a/samples/graph/consumer/src/main.rs b/samples/graph/consumer/src/main.rs index 157ef3d5..735dfb64 100644 --- a/samples/graph/consumer/src/main.rs +++ b/samples/graph/consumer/src/main.rs @@ -206,7 +206,7 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul seat_massager_json["@type"] )); } - // Let's make sure that we can fully serserialize the seat massager instance. This in only a check and it is optional. + // Let's make sure that we can fully serialize the seat massager instance. This is only a check and it is optional. let _seat_massager: sdv::premium_airbag_seat_massager::TYPE = serde_json::from_value(seat_massager_json.clone()).unwrap(); diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/graph/vehicle_core_provider/src/request_impl.rs index 8e19c7df..387625b3 100644 --- a/samples/graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/graph/vehicle_core_provider/src/request_impl.rs @@ -43,10 +43,10 @@ pub struct RequestImpl { /// The implementation for the Request interface, which is used to handle requests from the consumer. impl RequestImpl { - /// + /// Backoff base duration in milliseconds. const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; - /// + /// Maximum number of retries. const MAX_RETRIES: usize = 100; /// Get implementation. From 532a84eab29ade3f22dd3ed8e6a1d37d2f242fe5 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 2 May 2024 23:18:27 -0700 Subject: [PATCH 061/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/.accepted_words.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index d53f6a2d..66519b4a 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -1,6 +1,10 @@ Agemo App +com +freyja Freyja +github +ibeji Ibeji Ibeji's IoT From f3b11fdb6343bed7130d74b5963fcdc4028a9bde Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 3 May 2024 16:06:51 -0700 Subject: [PATCH 062/109] Digital Twin Graph --- Cargo.toml | 2 +- README.md | 2 +- .../digital_twin_graph/src/digital_twin_graph_impl.rs | 10 +++++----- samples/{graph => digital_twin_graph}/Cargo.toml | 2 +- .../{graph => digital_twin_graph}/consumer/src/main.rs | 0 .../seat_massager_provider/src/main.rs | 0 .../seat_massager_provider/src/request_impl.rs | 0 .../vehicle_core_provider/src/main.rs | 0 .../vehicle_core_provider/src/request_impl.rs | 0 9 files changed, 8 insertions(+), 8 deletions(-) rename samples/{graph => digital_twin_graph}/Cargo.toml (97%) rename samples/{graph => digital_twin_graph}/consumer/src/main.rs (100%) rename samples/{graph => digital_twin_graph}/seat_massager_provider/src/main.rs (100%) rename samples/{graph => digital_twin_graph}/seat_massager_provider/src/request_impl.rs (100%) rename samples/{graph => digital_twin_graph}/vehicle_core_provider/src/main.rs (100%) rename samples/{graph => digital_twin_graph}/vehicle_core_provider/src/request_impl.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 12cfeb2a..0634fc50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ members = [ "samples/common", "samples/protobuf_data_access", "samples/command", - "samples/graph", + "samples/digital_twin_graph", "samples/managed_subscribe", "samples/mixed", "samples/property", diff --git a/README.md b/README.md index 7df5c28c..e69546f4 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ IBEJI_HOME=/etc/ibeji ./invehicle-digital-twin The above example tells `invehicle-digital-twin` to load configuration files from `/etc/ibeji` instead of using the current working directory. -Chariott may be used to discover the in-vehicle digital twin service. We will discuss how to enable this feature. +Chariott may be used to discover the in-vehicle digital twin service. We will discuss how to enable this feature in the section on [Using Chariott](#using-chariott). ### Property Sample diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 51e2a1a0..b6ddff4e 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -187,7 +187,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .map_err(tonic::Status::internal)?; // Build a map of instance id to its associated endpoint infos. - let instance_provide_map: std::collections::HashMap> = + let instance_provider_map: std::collections::HashMap> = provider_endpoint_info_list .iter() .map(|provider_endpoint_info| { @@ -203,9 +203,9 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let mut values = vec![]; - for instance_id in instance_provide_map.keys() { + for instance_id in instance_provider_map.keys() { // We will only use the first provider. For a high availability scenario, we can try multiple providers. - let provider_endpoint_info = &instance_provide_map[instance_id][0]; + let provider_endpoint_info = &instance_provider_map[instance_id][0]; let provider_uri = provider_endpoint_info.uri.clone(); let instance_id = provider_endpoint_info.context.clone(); @@ -317,7 +317,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .map_err(tonic::Status::internal)?; if provider_endpoint_info_list.is_empty() { - return Err(tonic::Status::internal("No providers found")); + return Err(tonic::Status::not_found("No providers found")); } // We will only use the first provider. @@ -438,7 +438,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .map_err(tonic::Status::internal)?; if provider_endpoint_info_list.is_empty() { - return Err(tonic::Status::internal("No providers found")); + return Err(tonic::Status::not_found("No providers found")); } // We will only use the first provider. diff --git a/samples/graph/Cargo.toml b/samples/digital_twin_graph/Cargo.toml similarity index 97% rename from samples/graph/Cargo.toml rename to samples/digital_twin_graph/Cargo.toml index a875b559..c1b13245 100644 --- a/samples/graph/Cargo.toml +++ b/samples/digital_twin_graph/Cargo.toml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT [package] -name = "samples-graph" +name = "samples-digital-twin-graph" version = "0.1.0" edition = "2021" license = "MIT" diff --git a/samples/graph/consumer/src/main.rs b/samples/digital_twin_graph/consumer/src/main.rs similarity index 100% rename from samples/graph/consumer/src/main.rs rename to samples/digital_twin_graph/consumer/src/main.rs diff --git a/samples/graph/seat_massager_provider/src/main.rs b/samples/digital_twin_graph/seat_massager_provider/src/main.rs similarity index 100% rename from samples/graph/seat_massager_provider/src/main.rs rename to samples/digital_twin_graph/seat_massager_provider/src/main.rs diff --git a/samples/graph/seat_massager_provider/src/request_impl.rs b/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs similarity index 100% rename from samples/graph/seat_massager_provider/src/request_impl.rs rename to samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs diff --git a/samples/graph/vehicle_core_provider/src/main.rs b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs similarity index 100% rename from samples/graph/vehicle_core_provider/src/main.rs rename to samples/digital_twin_graph/vehicle_core_provider/src/main.rs diff --git a/samples/graph/vehicle_core_provider/src/request_impl.rs b/samples/digital_twin_graph/vehicle_core_provider/src/request_impl.rs similarity index 100% rename from samples/graph/vehicle_core_provider/src/request_impl.rs rename to samples/digital_twin_graph/vehicle_core_provider/src/request_impl.rs From 1f132a873981fb39830fc33752b5cf59a6a84a7b Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 3 May 2024 17:10:19 -0700 Subject: [PATCH 063/109] Digital Twin Graph --- .../digital_twin_graph/src/digital_twin_graph_impl.rs | 8 +++++--- docs/design/modules/digital_twin_graph/README.md | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index b6ddff4e..9a501c08 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -210,7 +210,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let provider_uri = provider_endpoint_info.uri.clone(); let instance_id = provider_endpoint_info.context.clone(); - let tx = Arc::clone(&self.tx); + let tx = self.tx.clone(); let mut rx = tx.subscribe(); let client_result = RequestClient::connect(provider_uri.clone()).await; @@ -326,9 +326,10 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let provider_uri = provider_endpoint_info.uri.clone(); let instance_id = provider_endpoint_info.context.clone(); - let tx = Arc::clone(&self.tx); + let tx = self.tx.clone(); let mut rx = tx.subscribe(); + // Connect to the provider where we will send the ask to get the instance's value. let client_result = RequestClient::connect(provider_uri.clone()).await; if client_result.is_err() { return Err(tonic::Status::internal("Unable to connect to the provider.")); @@ -338,6 +339,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Note: The ask id must be a universally unique value. let ask_id = Uuid::new_v4().to_string(); + // Create the targeted payload. Note: The member path is not used when the operation is GET. let targeted_payload = TargetedPayload { instance_id: instance_id.clone(), member_path: member_path.clone(), @@ -447,7 +449,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let provider_uri = provider_endpoint_info.uri.clone(); let instance_id = provider_endpoint_info.context.clone(); - let tx = Arc::clone(&self.tx); + let tx = self.tx.clone(); let mut rx = tx.subscribe(); let client_result = RequestClient::connect(provider_uri.clone()).await; diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index d6e88b67..3a704eea 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -11,7 +11,7 @@ provide a consumer with the best interaction experience. They can be used as bui delivers a much better interaction experience. In the future, Ibeji may support multiple facades and the user can select the one that they prefer to use. This design specifies a graph-based facade, which will be named the Digital Twin Graph Service. With this facade, the digital twin will be represented as a -graph of digital twin entities whose arcs represent the relationships between those entities. Instance ids will be used to refer to entities. +graph of digital twin entities whose arcs represent the relationships between those entities. Instance IDs will be used to refer to entities. Please note that Ibeji is only intended for use on an IoT edge device. It is not intended for use in the cloud. The data that it manages can be transferred to the cloud, through components like [Eclipse Freyja](https://github.com/eclipse-ibeji/freyja). @@ -28,7 +28,7 @@ represents the digital twin. Ibeji's In-vehicle Digital Twin Service needs some adjustments to support the Digital Twin Graph Service. We will introduce a modified form of the service under the name "Digital Twin Registry" and keep the existing functionality intact under the original In-vehicle Digital Twin Service. -The Managed Subscriber Service is an optional service that provides integration with Agemo. It has been included in the component diagram for completeness sake. +The Managed Subscriber Service is an optional service that provides integration with Agemo. The Managed Subscriber Service has been included in the component diagram for completeness' sake. ![Component Diagram](diagrams/digital_twin_graph_component.svg) From cc4a5be641b3398ff665503a83ae05e1691f5274 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 3 May 2024 17:40:13 -0700 Subject: [PATCH 064/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/.accepted_words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index 66519b4a..1b44e566 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -7,6 +7,7 @@ github ibeji Ibeji Ibeji's +IDs IoT Invehicle svg From 21735bfd0b568d4baf4aa49c41d007d086fe6496 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sat, 4 May 2024 17:16:14 -0700 Subject: [PATCH 065/109] Digital Twin Graph --- docs/samples/{graph => digital_twin_graph}/.accepted_words.txt | 0 docs/samples/{graph => digital_twin_graph}/README.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/samples/{graph => digital_twin_graph}/.accepted_words.txt (100%) rename docs/samples/{graph => digital_twin_graph}/README.md (100%) diff --git a/docs/samples/graph/.accepted_words.txt b/docs/samples/digital_twin_graph/.accepted_words.txt similarity index 100% rename from docs/samples/graph/.accepted_words.txt rename to docs/samples/digital_twin_graph/.accepted_words.txt diff --git a/docs/samples/graph/README.md b/docs/samples/digital_twin_graph/README.md similarity index 100% rename from docs/samples/graph/README.md rename to docs/samples/digital_twin_graph/README.md From 84369e9afb2942cf6e25058b3f5ab54183d58c6d Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 14:42:02 -0700 Subject: [PATCH 066/109] Digital Twin Graph --- README.md | 173 ++------------------------- docs/samples/command/README.md | 37 ++++++ docs/samples/mixed/README.md | 35 ++++++ docs/samples/property/README.md | 36 ++++++ docs/samples/seat_massager/README.md | 35 ++++++ docs/samples/streaming/README.md | 36 ++++++ 6 files changed, 191 insertions(+), 161 deletions(-) create mode 100644 docs/samples/command/README.md create mode 100644 docs/samples/mixed/README.md create mode 100644 docs/samples/property/README.md create mode 100644 docs/samples/seat_massager/README.md create mode 100644 docs/samples/streaming/README.md diff --git a/README.md b/README.md index e69546f4..6e741ffe 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,7 @@ - [Tokio Console Support](#tokio-console-support) - [Running the Tests](#running-the-tests) - [Running the Samples](#running-the-samples) - - [Property Sample](#property-sample) - - [Command Sample](#command-sample) - - [Mixed Sample](#mixed-sample) - - [Seat Massager Sample](#seat-massager-sample) - - [Streaming Sample](#streaming-sample) - - [Using Chariott](#using-chariott) +- [Using Chariott](#using-chariott) - [Running in a Container](#running-in-a-container) - [Trademarks](#trademarks) @@ -143,15 +138,16 @@ Currently, we have no integration tests or end-to-end tests. ## Running the Samples -There are currently four samples: one that demonstrates the use of a property, one that demonstrates the use of a command, one that -demonstrates the mixed use of properties and commands and one that demonstrates the use of get/set for a seat massager. +There are currently six samples: -The demos use config files and we have provided a templated version of each config file. These templates can be found in: + - [Property Sample](docs/samples/property/README.md) - demonstrates the use of a property + - [Command Sample](docs/samples/command/README.md) - demonstrates the use of a command + - [Mixed Sample](docs/samples/mixed/README.md) - demonstrates the mixed use of properties and commands + - [Seat Massager Sample](docs/samples/seat_massager/README.md) - demonstrates the use of get/set for a seat massager + - [Streaming Sample](docs/samples/streaming/README.md) - demonstrates the use of streaming + - [Digital Twin Graph Sample](docs/samples/digital_twin_graph/README.md) - demonstrates the use of the Digital Twin Graph Service -- {repo-root-dir}/core/invehicle-digital-twin/template -- {repo-root-dir}/samples/common/template - -Configuration files will be loaded from the current working directory by default +The samples' onfiguration files will be loaded from the current working directory by default, but an `IBEJI_HOME` environment variable can be used to change the base configuration directory to a different one: ```shell @@ -161,154 +157,9 @@ IBEJI_HOME=/etc/ibeji ./invehicle-digital-twin The above example tells `invehicle-digital-twin` to load configuration files from `/etc/ibeji` instead of using the current working directory. -Chariott may be used to discover the in-vehicle digital twin service. We will discuss how to enable this feature in the section on [Using Chariott](#using-chariott). - -### Property Sample - -The following instructions are for the demo for the use of a property. This sample uses a MQTT Broker; please make sure that it is running. - -Steps: - -1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. -Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. -The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
-1. In each window, change directory to the directory containing the build artifacts. -Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

-`cd {repo-root-dir}/target/debug`
-1. Create the three config files with the following contents, if they are not already there:

----- consumer_settings.yaml ----
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

----- invehicle_digital_twin_settings.yaml ----
-`invehicle_digital_twin_authority: "0.0.0.0:5010"`

----- provider_settings.yaml ----
-`provider_authority: "0.0.0.0:1883"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

-1. In the top window, run:

-`./invehicle-digital-twin`
-1. In the middle window, run:

-`./property-provider`
-1. In the bottom window, run:

-`./property-consumer`
-1. Use control-c in each of the windows when you wish to stop the demo. - -### Command Sample - -The following instructions are for the demo for the use of a command. - -Steps: - -1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. -Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. -The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
-1. In each window, change directory to the directory containing the build artifacts. -Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

-`cd {repo-root-dir}/target/debug`
-1. Create the three config files with the following contents, if they are not already there:

----- consumer_settings.yaml ----
-`consumer_authority: "0.0.0.0:6010"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

----- invehicle_digital_twin_settings.yaml ----
-`invehicle_digital_twin_authority: "0.0.0.0:5010"`

----- provider_settings.yaml ----
-`provider_authority: "0.0.0.0:4010"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

-1. In the top window, run:

-`./invehicle-digital-twin`
-1. In the middle window, run:

-`./command-provider`
-1. In the bottom window, run:

-`./command-consumer`
-1. Use control-c in each of the windows when you wish to stop the demo. - -### Mixed Sample - -The following instructions are for the demo for the mixed use of commands and properties. - -Steps: - -1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. -Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. -The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
-1. In each window, change directory to the directory containing the build artifacts. -Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

-`cd {repo-root-dir}/target/debug`
-1. Create the three config files with the following contents, if they are not already there:

----- consumer_settings.yaml ----
-`consumer_authority: "0.0.0.0:6010"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

----- invehicle_digital_twin_settings.yaml ----
-`invehicle_digital_twin_authority: "0.0.0.0:5010"`

----- provider_settings.yaml ----
-`provider_authority: "0.0.0.0:4010"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

-1. In the top window, run:

-`./invehicle-digital-twin`
-1. In the middle window, run:

-`./mixed-provider`
-1. In the bottom window, run:

-`./mixed-consumer`
-1. Use control-c in each of the windows when you wish to stop the demo. - -### Seat Massager Sample - -The following instructions are for the demo for a seat massager. - -Steps: - -1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. -Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. -The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
-1. In each window, change directory to the directory containing the build artifacts. -Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

-`cd {repo-root-dir}/target/debug`
-1. Create the three config files with the following contents, if they are not already there:

----- consumer_settings.yaml ----
-`consumer_authority: "0.0.0.0:6010"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

----- invehicle_digital_twin_settings.yaml ----
-`invehicle_digital_twin_authority: "0.0.0.0:5010"`

----- provider_settings.yaml ----
-`provider_authority: "0.0.0.0:4010"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

-1. In the top window, run:

-`./invehicle-digital-twin`
-1. In the middle window, run:

-`./seat-massager-provider`
-1. In the bottom window, run:

-`./seat-massager-consumer`
-1. Use control-c in each of the windows when you wish to stop the demo. - -### Streaming Sample - -The following instructions are for the demo for streaming. - -Steps: - -1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. -Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. -The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
-1. In each window, change directory to the directory containing the build artifacts. -Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

-`cd {repo-root-dir}/target/debug`
-1. Create the three config files with the following contents, if they are not already there:

----- streaming_consumer_settings.yaml ----
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

-`number_of_images: 20`

----- invehicle_digital_twin_settings.yaml ----
-`invehicle_digital_twin_authority: "0.0.0.0:5010"`

----- streaming_provider_settings.yaml ----
-`provider_authority: "0.0.0.0:4010"`
-`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

-`image_directory: "<>/examples/applications/simulated-camera/images"` -1. In the top window, run:

-`./invehicle-digital-twin`
-1. In the middle window, run:

-`./streaming-provider`
-1. In the bottom window, run:

-`./streaming-consumer`
-1. Use control-c in each of the windows when you wish to stop the demo. - -### Using Chariott +With the samples, Chariott may be used to discover the in-vehicle digital twin service. We will discuss how to enable this feature in the section on [Using Chariott](#using-chariott). + +## Using Chariott If you want the digital twin consumers and digital twin providers for each demo to use Chariott to discover the URI for the In-Vehicle Digital Twin Service, rather than having it statically provided in their respective config file, then do the following before starting each demo: diff --git a/docs/samples/command/README.md b/docs/samples/command/README.md new file mode 100644 index 00000000..2c7ad2fe --- /dev/null +++ b/docs/samples/command/README.md @@ -0,0 +1,37 @@ +# Sample: Command + +The command sample demonstrates the use of a command. + +Follow these instructions to run the demo. + +Steps: + +1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. +Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. +The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
+1. In each window, change directory to the directory containing the build artifacts. +Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

+`cd {repo-root-dir}/target/debug`
+1. Create the three config files with the following contents, if they are not already there:

+---- consumer_settings.yaml ----
+`consumer_authority: "0.0.0.0:6010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+---- invehicle_digital_twin_settings.yaml ----
+`invehicle_digital_twin_authority: "0.0.0.0:5010"`

+---- provider_settings.yaml ----
+`provider_authority: "0.0.0.0:4010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+1. In the top window, run:

+`./invehicle-digital-twin`
+1. In the middle window, run:

+`./command-provider`
+1. In the bottom window, run:

+`./command-consumer`
+1. Use control-c in each of the windows when you wish to stop the demo. + + + +A templated version of each config file can be found in: + +- {repo-root-dir}/core/invehicle-digital-twin/template +- {repo-root-dir}/samples/common/template diff --git a/docs/samples/mixed/README.md b/docs/samples/mixed/README.md new file mode 100644 index 00000000..f0582452 --- /dev/null +++ b/docs/samples/mixed/README.md @@ -0,0 +1,35 @@ +# Sample: Mixed + +The mixed sample demonstrates the use of both properties and commands. + +Follow these instructions to run the demo. + +Steps: + +1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. +Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. +The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
+1. In each window, change directory to the directory containing the build artifacts. +Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

+`cd {repo-root-dir}/target/debug`
+1. Create the three config files with the following contents, if they are not already there:

+---- consumer_settings.yaml ----
+`consumer_authority: "0.0.0.0:6010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+---- invehicle_digital_twin_settings.yaml ----
+`invehicle_digital_twin_authority: "0.0.0.0:5010"`

+---- provider_settings.yaml ----
+`provider_authority: "0.0.0.0:4010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+1. In the top window, run:

+`./invehicle-digital-twin`
+1. In the middle window, run:

+`./mixed-provider`
+1. In the bottom window, run:

+`./mixed-consumer`
+1. Use control-c in each of the windows when you wish to stop the demo. + +A templated version of each config file can be found in: + +- {repo-root-dir}/core/invehicle-digital-twin/template +- {repo-root-dir}/samples/common/template diff --git a/docs/samples/property/README.md b/docs/samples/property/README.md new file mode 100644 index 00000000..66ebeac8 --- /dev/null +++ b/docs/samples/property/README.md @@ -0,0 +1,36 @@ +# Sample: Property + +The property sample demonstrates the use of a property. + +This sample uses a MQTT Broker; please make sure that it is running. + +Follow these instructions to run the demo. + +Steps: + +1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. +Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. +The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
+1. In each window, change directory to the directory containing the build artifacts. +Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

+`cd {repo-root-dir}/target/debug`
+1. Create the three config files with the following contents, if they are not already there:

+---- consumer_settings.yaml ----
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+---- invehicle_digital_twin_settings.yaml ----
+`invehicle_digital_twin_authority: "0.0.0.0:5010"`

+---- provider_settings.yaml ----
+`provider_authority: "0.0.0.0:1883"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+1. In the top window, run:

+`./invehicle-digital-twin`
+1. In the middle window, run:

+`./property-provider`
+1. In the bottom window, run:

+`./property-consumer`
+1. Use control-c in each of the windows when you wish to stop the demo. + +A templated version of each config file can be found in: + +- {repo-root-dir}/core/invehicle-digital-twin/template +- {repo-root-dir}/samples/common/template diff --git a/docs/samples/seat_massager/README.md b/docs/samples/seat_massager/README.md new file mode 100644 index 00000000..5330217d --- /dev/null +++ b/docs/samples/seat_massager/README.md @@ -0,0 +1,35 @@ +# Sample: Seat Massager + +The seat massager sample demonstrates how a seat massager may be implemented. + +Follow these instructions to run the demo. + +Steps: + +1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. +Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. +The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
+1. In each window, change directory to the directory containing the build artifacts. +Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

+`cd {repo-root-dir}/target/debug`
+1. Create the three config files with the following contents, if they are not already there:

+---- consumer_settings.yaml ----
+`consumer_authority: "0.0.0.0:6010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+---- invehicle_digital_twin_settings.yaml ----
+`invehicle_digital_twin_authority: "0.0.0.0:5010"`

+---- provider_settings.yaml ----
+`provider_authority: "0.0.0.0:4010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+1. In the top window, run:

+`./invehicle-digital-twin`
+1. In the middle window, run:

+`./seat-massager-provider`
+1. In the bottom window, run:

+`./seat-massager-consumer`
+1. Use control-c in each of the windows when you wish to stop the demo. + +A templated version of each config file can be found in: + +- {repo-root-dir}/core/invehicle-digital-twin/template +- {repo-root-dir}/samples/common/template diff --git a/docs/samples/streaming/README.md b/docs/samples/streaming/README.md new file mode 100644 index 00000000..d469579f --- /dev/null +++ b/docs/samples/streaming/README.md @@ -0,0 +1,36 @@ +# Sample: Streaming + +The streaming sample demonstrates the streaming of a video stream. + +Follow these instructions to run the demo. + +Steps: + +1. The best way to run the demo is by using three windows: one running the In-Vehicle Digital Twin, one running the Digital Twin Provider and one running the Digital Twin Consumer. +Orientate the three windows so that they are lined up in a column. The top window can be used for the In-Vehicle Digital Twin. +The middle window can be used for the Digital Twin Provider. The bottom window can be used for the Digital Twin Consumer.
+1. In each window, change directory to the directory containing the build artifacts. +Make sure that you replace "{repo-root-dir}" with the repository root directory on the machine where you are running the demo.

+`cd {repo-root-dir}/target/debug`
+1. Create the three config files with the following contents, if they are not already there:

+---- streaming_consumer_settings.yaml ----
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+`number_of_images: 20`

+---- invehicle_digital_twin_settings.yaml ----
+`invehicle_digital_twin_authority: "0.0.0.0:5010"`

+---- streaming_provider_settings.yaml ----
+`provider_authority: "0.0.0.0:4010"`
+`invehicle_digital_twin_uri: "http://0.0.0.0:5010"`

+`image_directory: "<>/examples/applications/simulated-camera/images"` +1. In the top window, run:

+`./invehicle-digital-twin`
+1. In the middle window, run:

+`./streaming-provider`
+1. In the bottom window, run:

+`./streaming-consumer`
+1. Use control-c in each of the windows when you wish to stop the demo. + +A templated version of each config file can be found in: + +- {repo-root-dir}/core/invehicle-digital-twin/template +- {repo-root-dir}/samples/common/template From dba0a31ec19b91911facbb1d73dbfec0e7874b9e Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 15:02:01 -0700 Subject: [PATCH 067/109] Digital Twin Graph --- docs/samples/command/.accepted_words.txt | 8 ++++++++ docs/samples/command/README.md | 2 -- docs/samples/mixed/.accepted_words.txt | 8 ++++++++ docs/samples/property/.accepted_words.txt | 9 +++++++++ docs/samples/seat_massager/.accepted_words.txt | 8 ++++++++ docs/samples/streaming/.accepted_words.txt | 9 +++++++++ 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 docs/samples/command/.accepted_words.txt create mode 100644 docs/samples/mixed/.accepted_words.txt create mode 100644 docs/samples/property/.accepted_words.txt create mode 100644 docs/samples/seat_massager/.accepted_words.txt create mode 100644 docs/samples/streaming/.accepted_words.txt diff --git a/docs/samples/command/.accepted_words.txt b/docs/samples/command/.accepted_words.txt new file mode 100644 index 00000000..79caae2b --- /dev/null +++ b/docs/samples/command/.accepted_words.txt @@ -0,0 +1,8 @@ +br +cd +config +dir +invehicle +repo +uri +yaml diff --git a/docs/samples/command/README.md b/docs/samples/command/README.md index 2c7ad2fe..0ed0b189 100644 --- a/docs/samples/command/README.md +++ b/docs/samples/command/README.md @@ -29,8 +29,6 @@ Make sure that you replace "{repo-root-dir}" with the repository root directory `./command-consumer`
1. Use control-c in each of the windows when you wish to stop the demo. - - A templated version of each config file can be found in: - {repo-root-dir}/core/invehicle-digital-twin/template diff --git a/docs/samples/mixed/.accepted_words.txt b/docs/samples/mixed/.accepted_words.txt new file mode 100644 index 00000000..79caae2b --- /dev/null +++ b/docs/samples/mixed/.accepted_words.txt @@ -0,0 +1,8 @@ +br +cd +config +dir +invehicle +repo +uri +yaml diff --git a/docs/samples/property/.accepted_words.txt b/docs/samples/property/.accepted_words.txt new file mode 100644 index 00000000..e4431803 --- /dev/null +++ b/docs/samples/property/.accepted_words.txt @@ -0,0 +1,9 @@ +br +cd +config +dir +invehicle +MQTT +repo +uri +yaml diff --git a/docs/samples/seat_massager/.accepted_words.txt b/docs/samples/seat_massager/.accepted_words.txt new file mode 100644 index 00000000..79caae2b --- /dev/null +++ b/docs/samples/seat_massager/.accepted_words.txt @@ -0,0 +1,8 @@ +br +cd +config +dir +invehicle +repo +uri +yaml diff --git a/docs/samples/streaming/.accepted_words.txt b/docs/samples/streaming/.accepted_words.txt new file mode 100644 index 00000000..d11b301e --- /dev/null +++ b/docs/samples/streaming/.accepted_words.txt @@ -0,0 +1,9 @@ +br +cd +chariott +config +dir +invehicle +repo +uri +yaml From 4506ec0e883f6d3c7ce7ad9933e6a5a8e53c4740 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 15:05:36 -0700 Subject: [PATCH 068/109] Digital Twin Graph --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6e741ffe..9ba27ad7 100644 --- a/README.md +++ b/README.md @@ -140,12 +140,12 @@ Currently, we have no integration tests or end-to-end tests. There are currently six samples: - - [Property Sample](docs/samples/property/README.md) - demonstrates the use of a property - - [Command Sample](docs/samples/command/README.md) - demonstrates the use of a command - - [Mixed Sample](docs/samples/mixed/README.md) - demonstrates the mixed use of properties and commands - - [Seat Massager Sample](docs/samples/seat_massager/README.md) - demonstrates the use of get/set for a seat massager - - [Streaming Sample](docs/samples/streaming/README.md) - demonstrates the use of streaming - - [Digital Twin Graph Sample](docs/samples/digital_twin_graph/README.md) - demonstrates the use of the Digital Twin Graph Service +- [Property Sample](docs/samples/property/README.md) - demonstrates the use of a property +- [Command Sample](docs/samples/command/README.md) - demonstrates the use of a command +- [Mixed Sample](docs/samples/mixed/README.md) - demonstrates the mixed use of properties and commands +- [Seat Massager Sample](docs/samples/seat_massager/README.md) - demonstrates the use of get/set for a seat massager +- [Streaming Sample](docs/samples/streaming/README.md) - demonstrates the use of streaming +- [Digital Twin Graph Sample](docs/samples/digital_twin_graph/README.md) - demonstrates the use of the Digital Twin Graph Service The samples' onfiguration files will be loaded from the current working directory by default, but an `IBEJI_HOME` environment variable can be used to change the base configuration directory to a different one: From ef9ced4c6f75ff787ef3a2f4809c6d6b783436c0 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 15:09:26 -0700 Subject: [PATCH 069/109] Digital Twin Graph --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ba27ad7..7ff4f918 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ There are currently six samples: - [Streaming Sample](docs/samples/streaming/README.md) - demonstrates the use of streaming - [Digital Twin Graph Sample](docs/samples/digital_twin_graph/README.md) - demonstrates the use of the Digital Twin Graph Service -The samples' onfiguration files will be loaded from the current working directory by default, +The samples' configuration files will be loaded from the current working directory by default, but an `IBEJI_HOME` environment variable can be used to change the base configuration directory to a different one: ```shell From ce9f99271ee076c28521cb23443730224ab06c01 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 17:11:53 -0700 Subject: [PATCH 070/109] Digital Twin Graph --- .../digital_twin_graph/consumer/src/main.rs | 201 +++++++++++++++--- 1 file changed, 166 insertions(+), 35 deletions(-) diff --git a/samples/digital_twin_graph/consumer/src/main.rs b/samples/digital_twin_graph/consumer/src/main.rs index 735dfb64..c3078d79 100644 --- a/samples/digital_twin_graph/consumer/src/main.rs +++ b/samples/digital_twin_graph/consumer/src/main.rs @@ -25,6 +25,8 @@ const MAX_RETRIES: usize = 100; /// /// # Arguments /// * `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. +/// # Returns +/// The digital twin graph client. async fn connect_to_digital_twin_graph_service( invehicle_digital_twin_uri: String, ) -> Result, String> { @@ -50,6 +52,8 @@ async fn connect_to_digital_twin_graph_service( /// # Arguments /// * `client` - The digital twin graph client. /// * `model_id` - The model id. +/// # Returns +/// The find response. async fn find( client: DigitalTwinGraphClient, model_id: String, @@ -79,6 +83,8 @@ async fn find( /// * `client` - The digital twin graph client. /// * `instance_id` - The instance id. /// * `member_path` - The member path. +/// # Returns +/// The get response. async fn get( client: DigitalTwinGraphClient, instance_id: String, @@ -111,6 +117,8 @@ async fn get( /// * `instance_id` - The instance id. /// * `member_path` - The member path. /// * `request_payload` - The request payload. +/// # Returns +/// The invoke response. async fn invoke( client: DigitalTwinGraphClient, instance_id: String, @@ -134,71 +142,127 @@ async fn invoke( Ok(invoke_response) } -/// Perform a series of interactions with a vehicle digital twin. +/// Find a vehicle instance. /// /// # Arguments -/// * `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. -async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Result<(), String> { - // Connect to the digital twin graph service. - let client: DigitalTwinGraphClient = - connect_to_digital_twin_graph_service(invehicle_digital_twin_uri.clone()).await?; - +/// * `client` - The digital twin graph client. +/// # Returns +/// The vehicle instance. +async fn find_vehicle( + client: DigitalTwinGraphClient, +) -> Result { // Find all vehicle instances. - let find_vehicle_response: FindResponse = - find(client.clone(), sdv::vehicle::ID.to_string()).await?; + let find_vehicle_response: FindResponse = find(client, sdv::vehicle::ID.to_string()).await?; if find_vehicle_response.values.is_empty() { return Err("Unable to find vehicle instances".to_string()); } + // For now, we will just use the first vehicle instance. let vehicle: sdv::vehicle::TYPE = serde_json::from_str(&find_vehicle_response.values[0]).unwrap(); + info!("The vehicle's instance id is: {}", vehicle.instance_id); + Ok(vehicle) +} + +/// Find a cabin instance. +/// +/// # Arguments +/// * `client` - The digital twin graph client. +/// * `vehicle` - The vehicle instance. +/// # Returns +/// The cabin instance. +async fn find_cabin( + client: DigitalTwinGraphClient, + vehicle: &sdv::vehicle::TYPE, +) -> Result { // Get the cabin instance id. if vehicle.cabin.is_empty() { return Err("The vehicle does not have a cabin".to_string()); } + + // A vehicle has at most one cabin instance. We will use the first cabin instance. let cabin_instance_id = vehicle.cabin[0].instance_id.clone(); + info!("The cabin's instance id is: {:?}", cabin_instance_id); + // Get the cabin instance. let get_cabin_response: GetResponse = get(client.clone(), cabin_instance_id.clone(), "".to_string()).await?; + // Deserialize the cabin instance. let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response.value).unwrap(); - // Find the front left seat's instance id. - let mut front_left_seat_instance_id_option: Option = None; + Ok(cabin) +} + +/// Find a seat instance. +/// +/// # Arguments +/// * `client` - The digital twin graph client. +/// * `cabin` - The cabin instance. +/// * `seat_row` - The seat row. +/// * `seat_posotion` - The seat position. +/// # Returns +/// The seat instance. +async fn find_seat( + client: DigitalTwinGraphClient, + cabin: &sdv::cabin::TYPE, + seat_row: i32, + seat_posotion: sdv::cabin::seat::SEAT_POSITION_TYPE, +) -> Result { + if cabin.seat.is_empty() { + return Err("The cabin does not have any seats".to_string()); + } + + // Find the specified seat instance. for seat_relationship in cabin.seat.iter() { - if (seat_relationship.seat_row == 1) - && (seat_relationship.seat_position == sdv::cabin::seat::SEAT_POSITION_TYPE::left) + if (seat_relationship.seat_row == seat_row) + && (seat_relationship.seat_position == seat_posotion) { - info!("The front left seat's instance id is: {:?}", seat_relationship.instance_id); - front_left_seat_instance_id_option = Some(seat_relationship.instance_id.clone()); + // Get the seat instance. + let get_seat_response: GetResponse = + get(client.clone(), seat_relationship.instance_id.clone(), "".to_string()).await?; + + // Deserialize the seat instance. + let seat: sdv::seat::TYPE = serde_json::from_str(&get_seat_response.value).unwrap(); + + info!("The seat's instance id is: {}", seat.instance_id); + + return Ok(seat); } } - if front_left_seat_instance_id_option.is_none() { - return Err("The front left seat was not found".to_string()); - } - let front_left_seat_instance_id = front_left_seat_instance_id_option.unwrap(); - // Get the seat instance. - let get_seat_response: GetResponse = - get(client.clone(), front_left_seat_instance_id.clone(), "".to_string()).await?; - // Deserialize the seat instance. - let seat: sdv::seat::TYPE = serde_json::from_str(&get_seat_response.value).unwrap(); + Err("The seat was not found".to_string()) +} - // Get the seat massager instance id. +/// Find a premium airbag seat massager instance. +/// +/// # Arguments +/// * `client` - The digital twin graph client. +/// * `seat` - The seat instance. +/// # Returns +/// The premium airbag seat massager instance. +async fn find_premium_airbag_seat_massager( + client: DigitalTwinGraphClient, + seat: &sdv::seat::TYPE, +) -> Result { if seat.seat_massager.is_empty() { return Err("The seat does not have a seat massage".to_string()); } + + // Get the seat massager instance id. let seat_massager_instance_id = seat.seat_massager[0].instance_id.clone(); - info!("The seat massager's instance id is: {:?}", seat_massager_instance_id); + // Get the seat massager instance. let get_seat_massager_response: GetResponse = get(client.clone(), seat_massager_instance_id.clone(), "".to_string()).await?; + // Deserialize the seat massager instance to a JSON object. let seat_massager_json: serde_json::Value = serde_json::from_str(&get_seat_massager_response.value).unwrap(); + // Check that that the seat massager's modei_id (marked by @type) is the expected model (premium_airbag_seat_massager). if seat_massager_json["@type"] != sdv::premium_airbag_seat_massager::ID { return Err(format!( @@ -206,15 +270,33 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul seat_massager_json["@type"] )); } - // Let's make sure that we can fully serialize the seat massager instance. This is only a check and it is optional. - let _seat_massager: sdv::premium_airbag_seat_massager::TYPE = - serde_json::from_value(seat_massager_json.clone()).unwrap(); - // Randomly generate the airbag adjustment field values. - let mut rng = StdRng::from_entropy(); - let airbag_identifier = rng.gen_range(1..=15); - let inflation_level = rng.gen_range(1..=10); - let inflation_duration_in_seconds = rng.gen_range(1..=5); + // Deserialize the seat massager instance. + let seat_massager: sdv::premium_airbag_seat_massager::TYPE = + serde_json::from_str(&get_seat_massager_response.value).unwrap(); + + info!("The seat massager's instance id is: {}", seat_massager.instance_id); + + Ok(seat_massager) +} + +/// Perform the perform_step operation. +/// +/// # Arguments +/// * `client` - The digital twin graph client. +/// * `seat_massager` - The premium airbag seat massager instance. +/// * `airbag_identifier` - The airbag identifier. +/// * `inflation_level` - The inflation level. +/// * `inflation_duration_in_seconds` - The inflation duration in seconds. +/// # Returns +/// An empty result if the operation is successful. +async fn perform_step( + client: DigitalTwinGraphClient, + seat_massager: sdv::premium_airbag_seat_massager::TYPE, + airbag_identifier: i32, + inflation_level: i32, + inflation_duration_in_seconds: i32, +) -> Result<(), String> { // Generate the perform_step operation's request payload. let request_payload: sdv::airbag_seat_massager::perform_step::request::TYPE = sdv::airbag_seat_massager::perform_step::request::TYPE { @@ -225,21 +307,70 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul }], ..Default::default() }; + // Serialize the request payload to a JSON string. let request_payload_json: String = serde_json::to_string_pretty(&request_payload).unwrap(); + // Invoke the perform_step operation. let perform_step_response: InvokeResponse = invoke( client.clone(), - seat_massager_instance_id.clone(), + seat_massager.instance_id.clone(), sdv::airbag_seat_massager::perform_step::NAME.to_string(), request_payload_json.clone(), ) .await?; + info!("The perform_step operation response is:\n{}", perform_step_response.response_payload); Ok(()) } +/// Perform a series of interactions with a vehicle digital twin. +/// +/// # Arguments +/// * `invehicle_digital_twin_uri` - The in-vehicle digital twin uri. +/// # Returns +/// An empty result if the interactions are successful. +async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Result<(), String> { + // Connect to the digital twin graph service. + let client: DigitalTwinGraphClient = + connect_to_digital_twin_graph_service(invehicle_digital_twin_uri.clone()).await?; + + // Find the vehicle instance. + let vehicle: sdv::vehicle::TYPE = find_vehicle(client.clone()).await.unwrap(); + + // Find the cabin instance. + let cabin: sdv::cabin::TYPE = find_cabin(client.clone(), &vehicle).await.unwrap(); + + // Find the front left seat instance. + let front_left_seat: sdv::seat::TYPE = + find_seat(client.clone(), &cabin, 1, sdv::cabin::seat::SEAT_POSITION_TYPE::left) + .await + .unwrap(); + + // Find the premium airbag seat massager instance. + let seat_massager: sdv::premium_airbag_seat_massager::TYPE = + find_premium_airbag_seat_massager(client.clone(), &front_left_seat).await.unwrap(); + + // Randomly generate the airbag adjustment field values. + let mut rng = StdRng::from_entropy(); + let airbag_identifier = rng.gen_range(1..=15); + let inflation_level = rng.gen_range(1..=10); + let inflation_duration_in_seconds = rng.gen_range(1..=5); + + // Perform the perform_step operation. + perform_step( + client.clone(), + seat_massager, + airbag_identifier, + inflation_level, + inflation_duration_in_seconds, + ) + .await?; + + Ok(()) +} + #[tokio::main] async fn main() -> Result<(), Box> { // Setup logging. From 66f2c0fdc96779bade972411cf0ac49ff018ee31 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 17:25:42 -0700 Subject: [PATCH 071/109] Digital Twin Graph --- .../src/digital_twin_graph_config.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_config.rs b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs index 6abc51b6..3ce9f3f5 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_config.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs @@ -7,17 +7,28 @@ use serde_derive::Deserialize; const DEFAULT_CONFIG_FILENAME: &str = "digital_twin_graph_settings"; +/// The settings for the digital twin graph service. #[derive(Debug, Deserialize)] pub struct Settings { + /// The authority (address + optional port in the format "
[:]") for the Ibeji application server. pub base_authority: String, } /// Load the settings. +/// The settings are loaded from the default config file name. +/// +/// # Returns +/// The settings. pub fn load_settings() -> Settings { utils::load_settings(DEFAULT_CONFIG_FILENAME).unwrap() } -/// Load the settings. +/// Load the settings with the specified config file name. +/// +/// # Arguments +/// * `config_filename` - The name of the config file. +/// # Returns +/// The settings. pub fn load_settings_with_config_filename(config_filename: &str) -> Settings { utils::load_settings(config_filename).unwrap() } From d10a7dd97c0f18e88176c92af095aa6f861487ef Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 17:30:20 -0700 Subject: [PATCH 072/109] Digital Twin Graph --- core/module/digital_twin_graph/src/digital_twin_graph_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 9a501c08..5dee904d 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -193,7 +193,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .map(|provider_endpoint_info| { (provider_endpoint_info.context.clone(), provider_endpoint_info.clone()) }) - .fold( + .fold( // fold is used to group the endpoint infos by instance id. std::collections::HashMap::new(), |mut accumulator, (instance_id, endpoint_info)| { accumulator.entry(instance_id).or_insert_with(Vec::new).push(endpoint_info); From 4608cf2df82e5f23c296068431e85956ad7fb750 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 17:37:26 -0700 Subject: [PATCH 073/109] Digital Twin Graph --- .../module/digital_twin_graph/src/digital_twin_graph_config.rs | 2 +- core/module/digital_twin_graph/src/digital_twin_graph_impl.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_config.rs b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs index 3ce9f3f5..362d4666 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_config.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs @@ -16,7 +16,7 @@ pub struct Settings { /// Load the settings. /// The settings are loaded from the default config file name. -/// +/// /// # Returns /// The settings. pub fn load_settings() -> Settings { diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 5dee904d..21b9370e 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -193,7 +193,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .map(|provider_endpoint_info| { (provider_endpoint_info.context.clone(), provider_endpoint_info.clone()) }) - .fold( // fold is used to group the endpoint infos by instance id. + .fold( + // fold is used to group the endpoint infos by instance id. std::collections::HashMap::new(), |mut accumulator, (instance_id, endpoint_info)| { accumulator.entry(instance_id).or_insert_with(Vec::new).push(endpoint_info); From 837c3831ec50ab4a7b44e461efc92449b6fca056 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 17:52:05 -0700 Subject: [PATCH 074/109] Digital Twin Graph --- docs/samples/digital_twin_graph/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/samples/digital_twin_graph/README.md b/docs/samples/digital_twin_graph/README.md index 968c1092..64e3d8e3 100644 --- a/docs/samples/digital_twin_graph/README.md +++ b/docs/samples/digital_twin_graph/README.md @@ -2,6 +2,9 @@ The Digital Twin Graph sample demonstrates the use of the Digital Twin Graph Service. +This sample has two providers. The vehicle-core provider handles the vehicle, the vehicle's cabin and the cabin's seats. +The seat-massager provider handles all of the seat's seat masagers. + Follow these instructions to run the demo. Steps: From bdb67ac6b639e51412d952aa58ddde64e033c472 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 17:58:06 -0700 Subject: [PATCH 075/109] Digital Twin Graph --- docs/samples/digital_twin_graph/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/samples/digital_twin_graph/README.md b/docs/samples/digital_twin_graph/README.md index 64e3d8e3..f58a3cb6 100644 --- a/docs/samples/digital_twin_graph/README.md +++ b/docs/samples/digital_twin_graph/README.md @@ -3,7 +3,7 @@ The Digital Twin Graph sample demonstrates the use of the Digital Twin Graph Service. This sample has two providers. The vehicle-core provider handles the vehicle, the vehicle's cabin and the cabin's seats. -The seat-massager provider handles all of the seat's seat masagers. +The seat-massager provider handles all of the seat's seat massagers. Follow these instructions to run the demo. From 7fc6ff1dbed04f4d5db864b86ec32eed7d4694b0 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Sun, 5 May 2024 18:04:54 -0700 Subject: [PATCH 076/109] Digital Twin Graph --- docs/samples/digital_twin_graph/.accepted_words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/samples/digital_twin_graph/.accepted_words.txt b/docs/samples/digital_twin_graph/.accepted_words.txt index 79caae2b..30db5c61 100644 --- a/docs/samples/digital_twin_graph/.accepted_words.txt +++ b/docs/samples/digital_twin_graph/.accepted_words.txt @@ -3,6 +3,7 @@ cd config dir invehicle +massagers repo uri yaml From 7a97fc33ba4f1864a7c45974ec0b656d8716d12b Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 09:27:07 -0700 Subject: [PATCH 077/109] Digital Twin Graph --- core/common/src/utils.rs | 43 +++++++++++++++++++ .../src/digital_twin_graph_impl.rs | 16 ++----- .../modules/digital_twin_graph/README.md | 9 ++-- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/core/common/src/utils.rs b/core/common/src/utils.rs index 7acfdc03..2e22d49c 100644 --- a/core/common/src/utils.rs +++ b/core/common/src/utils.rs @@ -42,6 +42,8 @@ pub enum ServiceUriSource { /// /// # Arguments /// * `config_filename` - Name of the config file to load settings from. +/// # Returns +/// * The settings. pub fn load_settings(config_filename: &str) -> Result where T: for<'de> serde::Deserialize<'de>, @@ -65,6 +67,8 @@ where /// * `retry_interval_ms` - The retry interval between retries in milliseconds. /// * `function` - The function to retry. /// * `context` - Context field to provide additional info for logging. +/// # Returns +/// * The result of the function. pub async fn execute_with_retry Fut>( max_retries: u32, retry_interval_ms: Duration, @@ -108,6 +112,8 @@ where /// * `version` - The service's version. /// # `expected_communication_kind` - The service's expected communication kind. /// # `expected_communication_reference` - The service's expected communication reference. +/// # Returns +/// * The service's URI. pub async fn discover_service_using_chariott( chariott_uri: &str, namespace: &str, @@ -147,6 +153,8 @@ pub async fn discover_service_using_chariott( /// * `service_uri_source` - Enum providing information on how to get the service URI. /// # `expected_communication_kind` - The service's expected communication kind. /// # `expected_communication_reference` - The service's expected communication reference. +/// # Returns +/// * The service's URI. pub async fn get_service_uri( service_uri_source: ServiceUriSource, expected_communication_kind: &str, @@ -185,6 +193,19 @@ pub async fn get_service_uri( Ok(result) } +/// Is the provided subset a subset of the provided superset? +/// +/// # Arguments +/// * `subset` - The provided subset. +/// * `superset` - The provided superset. +/// # Returns +/// * `true` if the subset is a subset of the superset, `false` otherwise. +pub fn is_subset(subset: &[String], superset: &[String]) -> bool { + subset.iter().all(|subset_member| { + superset.iter().any(|supserset_member| subset_member == supserset_member) + }) +} + #[cfg(test)] mod tests { use super::*; @@ -229,3 +250,25 @@ mod tests { assert!(result.is_err()); } } + +#[test] +fn is_subset_test() { + assert!(is_subset(&[], &[])); + assert!(is_subset(&[], &["one".to_string()])); + assert!(is_subset(&[], &["one".to_string(), "two".to_string()])); + assert!(is_subset(&["one".to_string()], &["one".to_string()])); + assert!(is_subset(&["one".to_string()], &["one".to_string(), "two".to_string()])); + assert!(is_subset( + &["one".to_string(), "two".to_string()], + &["one".to_string(), "two".to_string()] + )); + assert!(!is_subset( + &["one".to_string(), "two".to_string(), "three".to_string()], + &["one".to_string(), "two".to_string()] + )); + assert!(!is_subset( + &["one".to_string(), "two".to_string(), "three".to_string()], + &["one".to_string()] + )); + assert!(!is_subset(&["one".to_string(), "two".to_string(), "three".to_string()], &[])); +} diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 21b9370e..0d941d76 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -2,6 +2,7 @@ // Licensed under the MIT license. // SPDX-License-Identifier: MIT +use common::utils::is_subset; use core_protobuf_data_access::async_rpc::v1::request::{ request_client::RequestClient, AskRequest, }; @@ -63,17 +64,6 @@ impl DigitalTwinGraphImpl { } } - /// Is the provided subset a subset of the provided superset? - /// - /// # Arguments - /// * `subset` - The provided subset. - /// * `superset` - The provided superset. - fn is_subset(subset: &[String], superset: &[String]) -> bool { - subset.iter().all(|subset_member| { - superset.iter().any(|supserset_member| subset_member == supserset_member) - }) - } - /// Use the Digital Twin Registery service to find the endpoints for digital twin providers that support /// the specified model id, protocol and operations. /// @@ -112,7 +102,7 @@ impl DigitalTwinGraphImpl { .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) .filter(|endpoint_info| { endpoint_info.protocol == protocol - && Self::is_subset(operations, &endpoint_info.operations) + && is_subset(operations, &endpoint_info.operations) }) .collect()) } @@ -155,7 +145,7 @@ impl DigitalTwinGraphImpl { .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) .filter(|endpoint_info| { endpoint_info.protocol == protocol - && Self::is_subset(operations, &endpoint_info.operations) + && is_subset(operations, &endpoint_info.operations) }) .collect()) } diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 3a704eea..237fd9a2 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -6,9 +6,10 @@ ## Introduction -Ibeji today provides the foundations for constructing and interacting with a digital twin on an edge device. These are low-level abilities and do not necessarily -provide a consumer with the best interaction experience. They can be used as building blocks to build facades that provide a consumer with an abstraction that -delivers a much better interaction experience. In the future, Ibeji may support multiple facades and the user can select the one that they prefer to use. +The initial Ibeji provided the foundations for constructing and interacting with a digital twin on an edge device. These foundations are low-level abilities +and they do not necessarily provide a consumer with the best interaction experience. However, they can be used as building blocks to build facades that +provide a consumer with an abstraction that delivers a much better interaction experience. Ibeji may support multiple facades and the user can select the one +that they prefer to use. This design specifies a graph-based facade, which will be named the Digital Twin Graph Service. With this facade, the digital twin will be represented as a graph of digital twin entities whose arcs represent the relationships between those entities. Instance IDs will be used to refer to entities. @@ -26,7 +27,7 @@ We will introduce a new service named "Digital Twin Graph" that will provide a f providers. Ideally, the consumer will not need to directly interact with provider endpoints. Instead, the consumer will interact with a graph structure that represents the digital twin. -Ibeji's In-vehicle Digital Twin Service needs some adjustments to support the Digital Twin Graph Service. We will introduce a modified form of the service under the name "Digital Twin Registry" and keep the existing functionality intact under the original In-vehicle Digital Twin Service. +Ibeji's In-vehicle Digital Twin Service needs some adjustments to support the Digital Twin Graph Service. We will introduce a modified form of the service under the name "Digital Twin Registry" and keep the existing functionality intact, for now, under the original In-vehicle Digital Twin Service. The Managed Subscriber Service is an optional service that provides integration with Agemo. The Managed Subscriber Service has been included in the component diagram for completeness' sake. From f5bfaee020cd734f087fcf41dfa5e8a79fc9655b Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 09:39:33 -0700 Subject: [PATCH 078/109] Digital Twin Graph --- core/common/src/utils.rs | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/core/common/src/utils.rs b/core/common/src/utils.rs index 2e22d49c..4f840a95 100644 --- a/core/common/src/utils.rs +++ b/core/common/src/utils.rs @@ -249,26 +249,26 @@ mod tests { .await; assert!(result.is_err()); } -} -#[test] -fn is_subset_test() { - assert!(is_subset(&[], &[])); - assert!(is_subset(&[], &["one".to_string()])); - assert!(is_subset(&[], &["one".to_string(), "two".to_string()])); - assert!(is_subset(&["one".to_string()], &["one".to_string()])); - assert!(is_subset(&["one".to_string()], &["one".to_string(), "two".to_string()])); - assert!(is_subset( - &["one".to_string(), "two".to_string()], - &["one".to_string(), "two".to_string()] - )); - assert!(!is_subset( - &["one".to_string(), "two".to_string(), "three".to_string()], - &["one".to_string(), "two".to_string()] - )); - assert!(!is_subset( - &["one".to_string(), "two".to_string(), "three".to_string()], - &["one".to_string()] - )); - assert!(!is_subset(&["one".to_string(), "two".to_string(), "three".to_string()], &[])); + #[test] + fn is_subset_test() { + assert!(is_subset(&[], &[])); + assert!(is_subset(&[], &["one".to_string()])); + assert!(is_subset(&[], &["one".to_string(), "two".to_string()])); + assert!(is_subset(&["one".to_string()], &["one".to_string()])); + assert!(is_subset(&["one".to_string()], &["one".to_string(), "two".to_string()])); + assert!(is_subset( + &["one".to_string(), "two".to_string()], + &["one".to_string(), "two".to_string()] + )); + assert!(!is_subset( + &["one".to_string(), "two".to_string(), "three".to_string()], + &["one".to_string(), "two".to_string()] + )); + assert!(!is_subset( + &["one".to_string(), "two".to_string(), "three".to_string()], + &["one".to_string()] + )); + assert!(!is_subset(&["one".to_string(), "two".to_string(), "three".to_string()], &[])); + } } From f502fac97448f6bf89879a028d59377e853458e1 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 10:51:43 -0700 Subject: [PATCH 079/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 0d941d76..8fabf9a6 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -52,6 +52,8 @@ impl DigitalTwinGraphImpl { /// * `digital_twin_registry_uri` - The uri for the digital twin registry service. /// * `respond_uri` - The uri for the respond service. /// * `tx` - The sender for the asynchronous channel for AnswerRequest's. + /// # Returns + /// A new instance of a DigitalTwinGraphImpl. pub fn new( digital_twin_registry_uri: &str, respond_uri: &str, @@ -71,12 +73,14 @@ impl DigitalTwinGraphImpl { /// * `model_id` - The matching model id. /// * `protocol` - The required protocol. /// * `operations` - The required operations. + /// # Returns + /// A list of endpoint infos. pub async fn find_digital_twin_providers_with_model_id( &self, model_id: &str, protocol: &str, operations: &[String], - ) -> Result, String> { + ) -> Result, tonic::Status> { // Define the retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays @@ -93,7 +97,8 @@ impl DigitalTwinGraphImpl { client.find_by_model_id(request).await.map_err(|error| error.to_string()) }) - .await? + .await + .map_err(|error| tonic::Status::internal(error))? .into_inner(); Ok(response @@ -113,12 +118,14 @@ impl DigitalTwinGraphImpl { /// * `instance_id` - The matching instance id. /// * `protocol` - The required protocol. /// * `operations` - The required operations. + /// # Returns + /// A list of endpoint infos. pub async fn find_digital_twin_providers_with_instance_id( &self, instance_id: &str, protocol: &str, operations: &[String], - ) -> Result, String> { + ) -> Result, tonic::Status> { // Define the retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays @@ -136,7 +143,8 @@ impl DigitalTwinGraphImpl { client.find_by_instance_id(request).await.map_err(|error| error.to_string()) }) - .await? + .await + .map_err(|error| tonic::Status::internal(error))? .into_inner(); Ok(response @@ -157,6 +165,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Find request. + /// # Returns + /// Find response. async fn find( &self, request: tonic::Request, @@ -173,8 +183,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { digital_twin_protocol::GRPC, &[digital_twin_operation::GET.to_string()], ) - .await - .map_err(tonic::Status::internal)?; + .await?; // Build a map of instance id to its associated endpoint infos. let instance_provider_map: std::collections::HashMap> = @@ -287,6 +296,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Get request. + /// # Returns + /// Get response. async fn get( &self, request: tonic::Request, @@ -304,8 +315,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { digital_twin_protocol::GRPC, &[digital_twin_operation::GET.to_string()], ) - .await - .map_err(tonic::Status::internal)?; + .await?; if provider_endpoint_info_list.is_empty() { return Err(tonic::Status::not_found("No providers found")); @@ -396,6 +406,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Set request. + /// # Returns + /// Set response. async fn set( &self, request: tonic::Request, @@ -409,6 +421,8 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Invoke request. + /// # Returns + /// Invoke response. async fn invoke( &self, request: tonic::Request, @@ -427,8 +441,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { digital_twin_protocol::GRPC, &[digital_twin_operation::INVOKE.to_string()], ) - .await - .map_err(tonic::Status::internal)?; + .await?; if provider_endpoint_info_list.is_empty() { return Err(tonic::Status::not_found("No providers found")); From 40293c03b15ffcf7f752a2413f7c78dc93259ad0 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 11:08:09 -0700 Subject: [PATCH 080/109] Digital Twin Graph --- core/module/digital_twin_graph/src/digital_twin_graph_impl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 8fabf9a6..0a56d0ae 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -98,7 +98,7 @@ impl DigitalTwinGraphImpl { client.find_by_model_id(request).await.map_err(|error| error.to_string()) }) .await - .map_err(|error| tonic::Status::internal(error))? + .map_err(tonic::Status::internal)? .into_inner(); Ok(response @@ -144,7 +144,7 @@ impl DigitalTwinGraphImpl { client.find_by_instance_id(request).await.map_err(|error| error.to_string()) }) .await - .map_err(|error| tonic::Status::internal(error))? + .map_err(tonic::Status::internal)? .into_inner(); Ok(response From 57b2534c0a0ed6799ecff61c0ab07359f820f606 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 13:44:57 -0700 Subject: [PATCH 081/109] Digital Twin Graph --- docs/samples/digital_twin_graph/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/samples/digital_twin_graph/README.md b/docs/samples/digital_twin_graph/README.md index f58a3cb6..f7304de4 100644 --- a/docs/samples/digital_twin_graph/README.md +++ b/docs/samples/digital_twin_graph/README.md @@ -3,7 +3,7 @@ The Digital Twin Graph sample demonstrates the use of the Digital Twin Graph Service. This sample has two providers. The vehicle-core provider handles the vehicle, the vehicle's cabin and the cabin's seats. -The seat-massager provider handles all of the seat's seat massagers. +The seat-massager provider handles all of the seats' seat massagers. Follow these instructions to run the demo. From edde513d5006b18cc466df53e5c1f646897d2fbe Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 21:15:12 -0700 Subject: [PATCH 082/109] Digital Twin Graph --- docs/samples/digital_twin_graph/README.md | 4 + .../diagrams/vehicle_graph.puml | 78 ++++++++++++++++ .../diagrams/vehicle_graph.svg | 91 +++++++++++++++++++ .../vehicle_core_provider/src/main.rs | 6 ++ 4 files changed, 179 insertions(+) create mode 100644 docs/samples/digital_twin_graph/diagrams/vehicle_graph.puml create mode 100644 docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg diff --git a/docs/samples/digital_twin_graph/README.md b/docs/samples/digital_twin_graph/README.md index f7304de4..335925be 100644 --- a/docs/samples/digital_twin_graph/README.md +++ b/docs/samples/digital_twin_graph/README.md @@ -5,6 +5,10 @@ The Digital Twin Graph sample demonstrates the use of the Digital Twin Graph Ser This sample has two providers. The vehicle-core provider handles the vehicle, the vehicle's cabin and the cabin's seats. The seat-massager provider handles all of the seats' seat massagers. +The graph representation for the vehicle in this sample is shown below. + +![Graph Diagram](diagrams/vehicle_graph.svg) + Follow these instructions to run the demo. Steps: diff --git a/docs/samples/digital_twin_graph/diagrams/vehicle_graph.puml b/docs/samples/digital_twin_graph/diagrams/vehicle_graph.puml new file mode 100644 index 00000000..525f54db --- /dev/null +++ b/docs/samples/digital_twin_graph/diagrams/vehicle_graph.puml @@ -0,0 +1,78 @@ +@startuml + +object Vehicle { + model_id: "dtmi:sdv:vehicle;1" + instance_id: "550e8400-e29b-41d4-a716-446655440000" + vehicle_identification.vin: 1M8GDM9AXKP042788 +} + +object Cabin { + model_id: "dtmi:sdv:cabin;1" + instance_id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8" +} + +object "Seat" as front_left_seat { + model_id: "dtmi:sdv:seat;1" + instance_id: "7a9c5fe2-2c16-2f1f-b3c8-9a1b76b21f00" +} + +object "Seat" as front_right_seat { + model_id: "dtmi:sdv:seat;1" + instance_id: "8b3d6eg3-3c16-3f1f-c3c8-ba1c76c31f00" +} + +object "Seat" as back_left_seat { + model_id: "dtmi:sdv:seat;1" + instance_id: "9c4e7fh4-4c16-4f1f-d3c8-ca1d76d41f00" +} + +object "Seat" as back_center_seat { + model_id: "dtmi:sdv:seat;1" + instance_id: "ad5f8ig5-5c16-5f1f-e3c8-da1e76e51f00" +} + +object "Seat" as back_right_seat { + model_id: "dtmi:sdv:seat;1" + instance_id: "be6g9jh6-6c16-6f1f-f3c8-ea1f76f61f00" +} + +object "Seat Massager" as front_left_seat_massager { + model_id: "dtmi:sdv:premium_airbag_seat_massager;1" + instance_id: "front_left_airbag_seat_massager" +} + +object "Seat Massager" as front_right_seat_massager { + model_id: "dtmi:sdv:premium_airbag_seat_massager;1" + instance_id: "front_right_airbag_seat_massager" +} + +object "Seat Massager" as back_left_seat_massager { + model_id: "dtmi:sdv:basic_airbag_seat_massager;1" + instance_id: "back_left_airbag_seat_massager" +} + +object "Seat Massager" as back_center_seat_massager { + model_id: "dtmi:sdv:basic_airbag_seat_massager;1" + instance_id: "back_center_airbag_seat_massager" +} + +object "Seat Massager" as back_right_seat_massager { + model_id: "dtmi:sdv:basic_airbag_seat_massager;1" + instance_id: "back_right_airbag_seat_massager" +} + +Vehicle --|> Cabin + +Cabin --|> front_left_seat: seat_row = "1"; seat_position = "left" +Cabin --|> front_right_seat: seat_row = "1"; seat_position = "right" +Cabin --|> back_left_seat: seat_row = "2"; seat_position = "left" +Cabin --|> back_center_seat: seat_row = "2"; seat_position = "center" +Cabin --|> back_right_seat: seat_row = "2"; seat_position = "right" + +front_left_seat --|> front_left_seat_massager +front_right_seat --|> front_right_seat_massager +back_left_seat --|> back_left_seat_massager +back_center_seat --|> back_center_seat_massager +back_right_seat --|> back_right_seat_massager + +@enduml diff --git a/docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg b/docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg new file mode 100644 index 00000000..d5052852 --- /dev/null +++ b/docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg @@ -0,0 +1,91 @@ +Vehiclemodel_id: "dtmi:sdv:vehicle;1"instance_id: "550e8400-e29b-41d4-a716-446655440000"vehicle_identification.vin: 1M8GDM9AXKP042788Cabinmodel_id: "dtmi:sdv:cabin;1"instance_id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "7a9c5fe2-2c16-2f1f-b3c8-9a1b76b21f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "8b3d6eg3-3c16-3f1f-c3c8-ba1c76c31f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "9c4e7fh4-4c16-4f1f-d3c8-ca1d76d41f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "ad5f8ig5-5c16-5f1f-e3c8-da1e76e51f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "be6g9jh6-6c16-6f1f-f3c8-ea1f76f61f00"Seat Massagermodel_id: "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front_left_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front_right_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:basic_airbag_seat_massager;1"instance_id: "back_left_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:basic_airbag_seat_massager;1"instance_id: "back_center_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:basic_airbag_seat_massager;1"instance_id: "back_right_airbag_seat_massager"seat_row = "1"; seat_position = "left"seat_row = "1"; seat_position = "right"seat_row = "2"; seat_position = "left"seat_row = "2"; seat_position = "center"seat_row = "2"; seat_position = "right" \ No newline at end of file diff --git a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs index 8e1b14c4..7d388fc7 100644 --- a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs +++ b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs @@ -149,8 +149,14 @@ fn create_provider_state() -> ProviderState { // Create the vehicle. let vehicle_instance_id = format!("{}", uuid::Uuid::new_v4()); + let vehicle_identification: sdv::vehicle::vehicle_identification::TYPE = + sdv::vehicle::vehicle_identification::TYPE { + vin: "1M8GDM9AXKP042788".to_string(), + ..Default::default() + }; let vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { instance_id: vehicle_instance_id.clone(), + vehicle_identification, cabin: vec![sdv::vehicle::cabin::RELATIONSHIP_TYPE { instance_id: cabin_instance_id.clone(), }], From 2ad820e6139924175024e22233532e5ab881738d Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Tue, 7 May 2024 04:17:42 +0000 Subject: [PATCH 083/109] Generate PlantUML Diagrams --- .../diagrams/vehicle_graph.svg | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg b/docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg index d5052852..a1888f3b 100644 --- a/docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg +++ b/docs/samples/digital_twin_graph/diagrams/vehicle_graph.svg @@ -1,5 +1,17 @@ -Vehiclemodel_id: "dtmi:sdv:vehicle;1"instance_id: "550e8400-e29b-41d4-a716-446655440000"vehicle_identification.vin: 1M8GDM9AXKP042788Cabinmodel_id: "dtmi:sdv:cabin;1"instance_id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "7a9c5fe2-2c16-2f1f-b3c8-9a1b76b21f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "8b3d6eg3-3c16-3f1f-c3c8-ba1c76c31f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "9c4e7fh4-4c16-4f1f-d3c8-ca1d76d41f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "ad5f8ig5-5c16-5f1f-e3c8-da1e76e51f00"Seatmodel_id: "dtmi:sdv:seat;1"instance_id: "be6g9jh6-6c16-6f1f-f3c8-ea1f76f61f00"Seat Massagermodel_id: "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front_left_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front_right_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:basic_airbag_seat_massager;1"instance_id: "back_left_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:basic_airbag_seat_massager;1"instance_id: "back_center_airbag_seat_massager"Seat Massagermodel_id: "dtmi:sdv:basic_airbag_seat_massager;1"instance_id: "back_right_airbag_seat_massager"seat_row = "1"; seat_position = "left"seat_row = "1"; seat_position = "right"seat_row = "2"; seat_position = "left"seat_row = "2"; seat_position = "center"seat_row = "2"; seat_position = "right"seat_row = "1"; seat_position = "left"seat_row = "1"; seat_position = "right"seat_row = "2"; seat_position = "left"seat_row = "2"; seat_position = "center"seat_row = "2"; seat_position = "right" \ No newline at end of file From 61de269005ee847560c24cabf421afc42561643c Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 21:22:32 -0700 Subject: [PATCH 084/109] Digital Twin Graph --- docs/samples/digital_twin_graph/.accepted_words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/samples/digital_twin_graph/.accepted_words.txt b/docs/samples/digital_twin_graph/.accepted_words.txt index 30db5c61..e8da4eb2 100644 --- a/docs/samples/digital_twin_graph/.accepted_words.txt +++ b/docs/samples/digital_twin_graph/.accepted_words.txt @@ -5,5 +5,6 @@ dir invehicle massagers repo +svg uri yaml From 11bda50b9546144c9b7ab609105c7dc92b85bf09 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Mon, 6 May 2024 22:04:54 -0700 Subject: [PATCH 085/109] Digital Twin Graph --- README.md | 6 ++---- docs/design/modules/digital_twin_graph/README.md | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7ff4f918..625fcec0 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,7 @@ Instructions for installing Mosquitto can be found [here](https://github.com/ecl ## Cloning the Repo -The repo has two submodules [opendigitaltwins-dtdl](https://github.com/Azure/opendigitaltwins-dtdl) and [iot-plugandplay-models](https://github.com/Azure/iot-plugandplay-models) that provide DTDL context files -and DTDL samples file. To ensure that these are included, please use the following command when cloning Ibeji's github repo: +The repo has two submodules [opendigitaltwins-dtdl](https://github.com/Azure/opendigitaltwins-dtdl) and [iot-plugandplay-models](https://github.com/Azure/iot-plugandplay-models) that provide DTDL context files and DTDL samples file. To ensure that these are included, please use the following command when cloning Ibeji's github repo: ````shell git clone --recurse-submodules https://github.com/eclipse-ibeji/ibeji` @@ -107,8 +106,7 @@ cargo build This will build all of the foundation libraries and executables. -Ibeji also has add-on modules that rely on feature flags to include them in the build. For example, to build Ibeji with the Digital Twin Graph -and the Digital Twin Registry modules run: +Ibeji also has add-on modules that rely on feature flags to include them in the build. For example, to build Ibeji with the Digital Twin Graph and the Digital Twin Registry modules run: ````shell cargo build --features "digital_twin_graph,digital_twin_registry" diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 237fd9a2..6be78d28 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -12,7 +12,7 @@ provide a consumer with an abstraction that delivers a much better interaction e that they prefer to use. This design specifies a graph-based facade, which will be named the Digital Twin Graph Service. With this facade, the digital twin will be represented as a -graph of digital twin entities whose arcs represent the relationships between those entities. Instance IDs will be used to refer to entities. +graph of digital twin entities whose edges represent the relationships between those entities. Instance IDs will be used to refer to entities. Please note that Ibeji is only intended for use on an IoT edge device. It is not intended for use in the cloud. The data that it manages can be transferred to the cloud, through components like [Eclipse Freyja](https://github.com/eclipse-ibeji/freyja). From 2cb4bce3de6b595b130b6a619f9c4006f67c1e1f Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 7 May 2024 09:55:19 -0700 Subject: [PATCH 086/109] Digital Twin Graph --- core/common/src/utils.rs | 10 ---------- .../src/digital_twin_graph_config.rs | 5 ----- .../src/digital_twin_graph_impl.rs | 14 -------------- docs/design/modules/digital_twin_graph/README.md | 2 +- 4 files changed, 1 insertion(+), 30 deletions(-) diff --git a/core/common/src/utils.rs b/core/common/src/utils.rs index 4f840a95..03139218 100644 --- a/core/common/src/utils.rs +++ b/core/common/src/utils.rs @@ -42,8 +42,6 @@ pub enum ServiceUriSource { /// /// # Arguments /// * `config_filename` - Name of the config file to load settings from. -/// # Returns -/// * The settings. pub fn load_settings(config_filename: &str) -> Result where T: for<'de> serde::Deserialize<'de>, @@ -67,8 +65,6 @@ where /// * `retry_interval_ms` - The retry interval between retries in milliseconds. /// * `function` - The function to retry. /// * `context` - Context field to provide additional info for logging. -/// # Returns -/// * The result of the function. pub async fn execute_with_retry Fut>( max_retries: u32, retry_interval_ms: Duration, @@ -112,8 +108,6 @@ where /// * `version` - The service's version. /// # `expected_communication_kind` - The service's expected communication kind. /// # `expected_communication_reference` - The service's expected communication reference. -/// # Returns -/// * The service's URI. pub async fn discover_service_using_chariott( chariott_uri: &str, namespace: &str, @@ -153,8 +147,6 @@ pub async fn discover_service_using_chariott( /// * `service_uri_source` - Enum providing information on how to get the service URI. /// # `expected_communication_kind` - The service's expected communication kind. /// # `expected_communication_reference` - The service's expected communication reference. -/// # Returns -/// * The service's URI. pub async fn get_service_uri( service_uri_source: ServiceUriSource, expected_communication_kind: &str, @@ -198,8 +190,6 @@ pub async fn get_service_uri( /// # Arguments /// * `subset` - The provided subset. /// * `superset` - The provided superset. -/// # Returns -/// * `true` if the subset is a subset of the superset, `false` otherwise. pub fn is_subset(subset: &[String], superset: &[String]) -> bool { subset.iter().all(|subset_member| { superset.iter().any(|supserset_member| subset_member == supserset_member) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_config.rs b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs index 362d4666..ddd1fb42 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_config.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_config.rs @@ -16,9 +16,6 @@ pub struct Settings { /// Load the settings. /// The settings are loaded from the default config file name. -/// -/// # Returns -/// The settings. pub fn load_settings() -> Settings { utils::load_settings(DEFAULT_CONFIG_FILENAME).unwrap() } @@ -27,8 +24,6 @@ pub fn load_settings() -> Settings { /// /// # Arguments /// * `config_filename` - The name of the config file. -/// # Returns -/// The settings. pub fn load_settings_with_config_filename(config_filename: &str) -> Settings { utils::load_settings(config_filename).unwrap() } diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 0a56d0ae..ec3bba39 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -52,8 +52,6 @@ impl DigitalTwinGraphImpl { /// * `digital_twin_registry_uri` - The uri for the digital twin registry service. /// * `respond_uri` - The uri for the respond service. /// * `tx` - The sender for the asynchronous channel for AnswerRequest's. - /// # Returns - /// A new instance of a DigitalTwinGraphImpl. pub fn new( digital_twin_registry_uri: &str, respond_uri: &str, @@ -73,8 +71,6 @@ impl DigitalTwinGraphImpl { /// * `model_id` - The matching model id. /// * `protocol` - The required protocol. /// * `operations` - The required operations. - /// # Returns - /// A list of endpoint infos. pub async fn find_digital_twin_providers_with_model_id( &self, model_id: &str, @@ -118,8 +114,6 @@ impl DigitalTwinGraphImpl { /// * `instance_id` - The matching instance id. /// * `protocol` - The required protocol. /// * `operations` - The required operations. - /// # Returns - /// A list of endpoint infos. pub async fn find_digital_twin_providers_with_instance_id( &self, instance_id: &str, @@ -165,8 +159,6 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Find request. - /// # Returns - /// Find response. async fn find( &self, request: tonic::Request, @@ -296,8 +288,6 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Get request. - /// # Returns - /// Get response. async fn get( &self, request: tonic::Request, @@ -406,8 +396,6 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Set request. - /// # Returns - /// Set response. async fn set( &self, request: tonic::Request, @@ -421,8 +409,6 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { /// /// # Arguments /// * `request` - Invoke request. - /// # Returns - /// Invoke response. async fn invoke( &self, request: tonic::Request, diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 6be78d28..7eb63792 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -6,7 +6,7 @@ ## Introduction -The initial Ibeji provided the foundations for constructing and interacting with a digital twin on an edge device. These foundations are low-level abilities +The initial Ibeji implementation provided the foundations for constructing and interacting with a digital twin on an edge device. These foundations are low-level abilities and they do not necessarily provide a consumer with the best interaction experience. However, they can be used as building blocks to build facades that provide a consumer with an abstraction that delivers a much better interaction experience. Ibeji may support multiple facades and the user can select the one that they prefer to use. From 7e637c323bcc8b3c0a740ab070ae0f882cda352e Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 7 May 2024 12:21:24 -0700 Subject: [PATCH 087/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 290 ++++++++---------- 1 file changed, 130 insertions(+), 160 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index ec3bba39..c9093f3d 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -151,6 +151,95 @@ impl DigitalTwinGraphImpl { }) .collect()) } + + /// Send an ask to the provider. + /// + /// # Arguments + /// * `client` - The client to use to send the ask. + /// * `respond_uri` - The respond uri. + /// * `ask_id` - The ask id. + /// * `instance_id` - The instance id. + /// * `member_path` - The member path. + /// * `operation` - The operation. + /// * `payload` - The payload. + pub async fn send_ask( + &self, + mut client: RequestClient, + respond_uri: &str, + ask_id: &str, + instance_id: &str, + member_path: &str, + operation: &str, + payload: &str, + ) -> Result<(), tonic::Status> { + let targeted_payload = TargetedPayload { + instance_id: instance_id.to_string(), + member_path: member_path.to_string(), + operation: operation.to_string(), + payload: payload.to_string(), + }; + + // Serialize the targeted payload. + let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); + + let request = tonic::Request::new(AskRequest { + respond_uri: respond_uri.to_string(), + ask_id: ask_id.to_string(), + payload: targeted_payload_json.clone(), + }); + + // Send the ask. + let response = client.ask(request).await; + if let Err(status) = response { + return Err(tonic::Status::internal(format!("Unable to call ask, due to {status:?}"))); + } + + Ok(()) + } + + /// Wait for the answer. + /// + /// # Arguments + /// * `ask_id` - The ask id. + /// * `rx` - The receiver for the asynchronous channel for AnswerRequest's. + pub async fn wait_for_answer( + &self, + ask_id: String, + rx: &mut broadcast::Receiver, + ) -> Result { + let mut answer_request: AnswerRequest = Default::default(); + let mut attempts_after_failure = 0; + const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; + while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { + match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()).await { + Ok(Ok(request)) => { + if ask_id == request.ask_id { + // We have received the answer request that we are expecting. + answer_request = request; + break; + } else { + // Ignore this answer request, as it is not the one that we are expecting. + // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. + continue; + } + } + Ok(Err(error_message)) => { + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + Err(error_message) => { + warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); + sleep(Duration::from_secs(1)).await; + attempts_after_failure += 1; + continue; + } + } + } + + Ok(answer_request) + } } #[tonic::async_trait] @@ -210,66 +299,25 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { warn!("Unable to connect. We will skip this one."); continue; } - let mut client = client_result.unwrap(); + let client = client_result.unwrap(); // Note: The ask id must be a universally unique value. let ask_id = Uuid::new_v4().to_string(); - let targeted_payload = TargetedPayload { - instance_id: instance_id.clone(), - member_path: "".to_string(), - operation: digital_twin_operation::GET.to_string(), - payload: "".to_string(), - }; - - // Serialize the targeted payload. - let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); - - let request = tonic::Request::new(AskRequest { - respond_uri: self.respond_uri.clone(), - ask_id: ask_id.clone(), - payload: targeted_payload_json.clone(), - }); - // Send the ask. - let response = client.ask(request).await; - if let Err(status) = response { - warn!("Unable to call ask, due to {status:?}\nWe will skip this one."); - continue; - } + self.send_ask( + client, + &self.respond_uri, + &ask_id, + &instance_id, + "", + digital_twin_operation::GET, + "", + ) + .await?; - // Wait for the answer request. - let mut answer_request: AnswerRequest = Default::default(); - let mut attempts_after_failure = 0; - while attempts_after_failure < Self::MAX_RETRIES { - match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()) - .await - { - Ok(Ok(request)) => { - if ask_id == request.ask_id { - // We have received the answer request that we are expecting. - answer_request = request; - break; - } else { - // Ignore this answer request, as it is not the one that we are expecting. - // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. - continue; - } - } - Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); - sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; - attempts_after_failure += 1; - continue; - } - Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); - sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; - attempts_after_failure += 1; - continue; - } - } - } + // Wait for the answer. + let answer_request = self.wait_for_answer(ask_id, &mut rx).await?; debug!( "Received an answer request. The ask_id is '{}'. The payload is '{}'", @@ -325,64 +373,25 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { if client_result.is_err() { return Err(tonic::Status::internal("Unable to connect to the provider.")); } - let mut client = client_result.unwrap(); + let client = client_result.unwrap(); // Note: The ask id must be a universally unique value. let ask_id = Uuid::new_v4().to_string(); - // Create the targeted payload. Note: The member path is not used when the operation is GET. - let targeted_payload = TargetedPayload { - instance_id: instance_id.clone(), - member_path: member_path.clone(), - operation: digital_twin_operation::GET.to_string(), - payload: "".to_string(), - }; - - // Serialize the targeted payload. - let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); - - let request = tonic::Request::new(AskRequest { - respond_uri: self.respond_uri.clone(), - ask_id: ask_id.clone(), - payload: targeted_payload_json.clone(), - }); - // Send the ask. - let response = client.ask(request).await; - if let Err(status) = response { - return Err(tonic::Status::internal(format!("Unable to call ask, due to {status:?}"))); - } - - // Wait for the answer request. - let mut answer_request: AnswerRequest = Default::default(); - let mut attempts_after_failure = 0; - while attempts_after_failure < Self::MAX_RETRIES { - match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()).await { - Ok(Ok(request)) => { - if ask_id == request.ask_id { - // We have received the answer request that we are expecting. - answer_request = request; - break; - } else { - // Ignore this answer request, as it is not the one that we are expecting. - // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. - continue; - } - } - Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); - sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; - attempts_after_failure += 1; - continue; - } - Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); - sleep(Duration::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS)).await; - attempts_after_failure += 1; - continue; - } - } - } + self.send_ask( + client, + &self.respond_uri, + &ask_id, + &instance_id, + &member_path, + digital_twin_operation::GET, + "", + ) + .await?; + + // Wait for the answer. + let answer_request = self.wait_for_answer(ask_id, &mut rx).await?; debug!( "Received an answer request. The ask_id is '{}'. The payload is '{}", @@ -446,64 +455,25 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { if client_result.is_err() { return Err(tonic::Status::internal("Unable to connect to the provider.")); } - let mut client = client_result.unwrap(); + let client = client_result.unwrap(); // Note: The ask id must be a universally unique value. let ask_id = Uuid::new_v4().to_string(); - let targeted_payload = TargetedPayload { - instance_id: instance_id.clone(), - member_path: member_path.clone(), - operation: digital_twin_operation::INVOKE.to_string(), - payload: request_payload.to_string(), - }; - - // Serialize the targeted payload. - let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); - - let request = tonic::Request::new(AskRequest { - respond_uri: self.respond_uri.clone(), - ask_id: ask_id.clone(), - payload: targeted_payload_json.clone(), - }); - // Send the ask. - let response = client.ask(request).await; - if let Err(status) = response { - return Err(tonic::Status::internal(format!("Unable to call ask, due to {status:?}"))); - } - - // Wait for the answer request. - let mut answer_request: AnswerRequest = Default::default(); - let mut attempts_after_failure = 0; - const MAX_ATTEMPTS_AFTER_FAILURE: u8 = 10; - while attempts_after_failure < MAX_ATTEMPTS_AFTER_FAILURE { - match timeout(Duration::from_millis(Self::TIMEOUT_PERIOD_IN_MILLIS), rx.recv()).await { - Ok(Ok(request)) => { - if ask_id == request.ask_id { - // We have received the answer request that we are expecting. - answer_request = request; - break; - } else { - // Ignore this answer request, as it is not the one that we are expecting. - // Immediately try again. This was not a failure, so we do not increment attempts_after_failure or sleep. - continue; - } - } - Ok(Err(error_message)) => { - warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; - attempts_after_failure += 1; - continue; - } - Err(error_message) => { - warn!("Failed to receive the answer request. The error message is '{}'. We may retry in a moment.", error_message); - sleep(Duration::from_secs(1)).await; - attempts_after_failure += 1; - continue; - } - } - } + self.send_ask( + client, + &self.respond_uri, + &ask_id, + &instance_id, + &member_path, + digital_twin_operation::INVOKE, + &request_payload, + ) + .await?; + + // Wait for the answer. + let answer_request = self.wait_for_answer(ask_id, &mut rx).await?; debug!( "Received an answer request. The ask_id is '{}'. The payload is '{}", From 07d51b2a3acdefa6f8f0e654a9dd9fc8b93d59fe Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 7 May 2024 12:40:54 -0700 Subject: [PATCH 088/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 74 ++++++++----------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index c9093f3d..af06ae93 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -158,27 +158,14 @@ impl DigitalTwinGraphImpl { /// * `client` - The client to use to send the ask. /// * `respond_uri` - The respond uri. /// * `ask_id` - The ask id. - /// * `instance_id` - The instance id. - /// * `member_path` - The member path. - /// * `operation` - The operation. - /// * `payload` - The payload. + /// * `targeted_payload` - The targeted payload. pub async fn send_ask( &self, mut client: RequestClient, respond_uri: &str, ask_id: &str, - instance_id: &str, - member_path: &str, - operation: &str, - payload: &str, + targeted_payload: &TargetedPayload, ) -> Result<(), tonic::Status> { - let targeted_payload = TargetedPayload { - instance_id: instance_id.to_string(), - member_path: member_path.to_string(), - operation: operation.to_string(), - payload: payload.to_string(), - }; - // Serialize the targeted payload. let targeted_payload_json = serde_json::to_string_pretty(&targeted_payload).unwrap(); @@ -304,17 +291,16 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Note: The ask id must be a universally unique value. let ask_id = Uuid::new_v4().to_string(); + // Create the targeted payload. + let targeted_payload = TargetedPayload { + instance_id: instance_id.to_string(), + member_path: "".to_string(), + operation: digital_twin_operation::GET.to_string(), + payload: "".to_string(), + }; + // Send the ask. - self.send_ask( - client, - &self.respond_uri, - &ask_id, - &instance_id, - "", - digital_twin_operation::GET, - "", - ) - .await?; + self.send_ask(client, &self.respond_uri, &ask_id, &targeted_payload).await?; // Wait for the answer. let answer_request = self.wait_for_answer(ask_id, &mut rx).await?; @@ -378,17 +364,16 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Note: The ask id must be a universally unique value. let ask_id = Uuid::new_v4().to_string(); + // Create the targeted payload. + let targeted_payload = TargetedPayload { + instance_id: instance_id.to_string(), + member_path: member_path.to_string(), + operation: digital_twin_operation::GET.to_string(), + payload: "".to_string(), + }; + // Send the ask. - self.send_ask( - client, - &self.respond_uri, - &ask_id, - &instance_id, - &member_path, - digital_twin_operation::GET, - "", - ) - .await?; + self.send_ask(client, &self.respond_uri, &ask_id, &targeted_payload).await?; // Wait for the answer. let answer_request = self.wait_for_answer(ask_id, &mut rx).await?; @@ -460,17 +445,16 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { // Note: The ask id must be a universally unique value. let ask_id = Uuid::new_v4().to_string(); + // Create the targeted payload. + let targeted_payload = TargetedPayload { + instance_id: instance_id.to_string(), + member_path: member_path.to_string(), + operation: digital_twin_operation::INVOKE.to_string(), + payload: request_payload.to_string(), + }; + // Send the ask. - self.send_ask( - client, - &self.respond_uri, - &ask_id, - &instance_id, - &member_path, - digital_twin_operation::INVOKE, - &request_payload, - ) - .await?; + self.send_ask(client, &self.respond_uri, &ask_id, &targeted_payload).await?; // Wait for the answer. let answer_request = self.wait_for_answer(ask_id, &mut rx).await?; From 0b5b67537cf17e9dc489b297ef051d1b2283d003 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Tue, 7 May 2024 22:54:08 -0700 Subject: [PATCH 089/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 14 ++++++------- digital-twin-model/src/sdv_v1.rs | 4 +++- .../seat_massager_provider/src/main.rs | 20 +++++++++++++++++++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index af06ae93..2ee0bb4b 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -86,15 +86,14 @@ impl DigitalTwinGraphImpl { let mut client = DigitalTwinRegistryClient::connect(self.digital_twin_registry_uri.to_string()) .await - .map_err(|error| format!("{error}"))?; + .map_err(|error| tonic::Status::internal(format!("{error}")))?; let request = tonic::Request::new(FindByModelIdRequest { model_id: model_id.to_string() }); - client.find_by_model_id(request).await.map_err(|error| error.to_string()) + client.find_by_model_id(request).await }) - .await - .map_err(tonic::Status::internal)? + .await? .into_inner(); Ok(response @@ -129,16 +128,15 @@ impl DigitalTwinGraphImpl { let mut client = DigitalTwinRegistryClient::connect(self.digital_twin_registry_uri.to_string()) .await - .map_err(|error| format!("{error}"))?; + .map_err(|error| tonic::Status::internal(format!("{error}")))?; let request = tonic::Request::new(FindByInstanceIdRequest { instance_id: instance_id.to_string(), }); - client.find_by_instance_id(request).await.map_err(|error| error.to_string()) + client.find_by_instance_id(request).await }) - .await - .map_err(tonic::Status::internal)? + .await? .into_inner(); Ok(response diff --git a/digital-twin-model/src/sdv_v1.rs b/digital-twin-model/src/sdv_v1.rs index 4cb90e16..ba5587ae 100644 --- a/digital-twin-model/src/sdv_v1.rs +++ b/digital-twin-model/src/sdv_v1.rs @@ -167,6 +167,7 @@ pub mod basic_airbag_seat_massager { value = "crate::sdv_v1::basic_airbag_seat_massager::ID.to_string()" ))] pub model_id: String, + pub sequence_names: crate::sdv_v1::seat_massager::sequence_names::TYPE, } } @@ -399,6 +400,7 @@ pub mod premium_airbag_seat_massager { value = "crate::sdv_v1::premium_airbag_seat_massager::ID.to_string()" ))] pub model_id: String, + pub sequence_names: crate::sdv_v1::seat_massager::sequence_names::TYPE, } } @@ -459,7 +461,7 @@ pub mod seat_massager { value = "crate::sdv_v1::seat_massager::sequence_names::ID.to_string()" ))] pub model_id: String, - pub sequence_names: Vec, + pub value: Vec, } } diff --git a/samples/digital_twin_graph/seat_massager_provider/src/main.rs b/samples/digital_twin_graph/seat_massager_provider/src/main.rs index ac82cc50..f244f994 100644 --- a/samples/digital_twin_graph/seat_massager_provider/src/main.rs +++ b/samples/digital_twin_graph/seat_massager_provider/src/main.rs @@ -58,6 +58,10 @@ fn create_provider_state() -> ProviderState { let front_left_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { instance_id: front_left_airbag_seat_massager_instance_id.clone(), + sequence_names: sdv::seat_massager::sequence_names::TYPE { + value: Vec::::new(), + ..Default::default() + }, ..Default::default() }; @@ -66,6 +70,10 @@ fn create_provider_state() -> ProviderState { let front_right_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { instance_id: front_right_airbag_seat_massager_instance_id.clone(), + sequence_names: sdv::seat_massager::sequence_names::TYPE { + value: Vec::::new(), + ..Default::default() + }, ..Default::default() }; @@ -73,6 +81,10 @@ fn create_provider_state() -> ProviderState { let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { instance_id: back_left_airbag_seat_massager_instance_id.clone(), + sequence_names: sdv::seat_massager::sequence_names::TYPE { + value: Vec::::new(), + ..Default::default() + }, ..Default::default() }; @@ -81,6 +93,10 @@ fn create_provider_state() -> ProviderState { let back_center_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { instance_id: back_center_airbag_seat_massager_instance_id.clone(), + sequence_names: sdv::seat_massager::sequence_names::TYPE { + value: Vec::::new(), + ..Default::default() + }, ..Default::default() }; @@ -88,6 +104,10 @@ fn create_provider_state() -> ProviderState { let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { instance_id: back_right_airbag_seat_massager_instance_id.clone(), + sequence_names: sdv::seat_massager::sequence_names::TYPE { + value: Vec::::new(), + ..Default::default() + }, ..Default::default() }; From 56ab4b95774984a678cf867fb0903716507478da Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 8 May 2024 16:39:10 -0700 Subject: [PATCH 090/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 52 +++++---- .../src/digital_twin_registry_impl.rs | 108 +++++++----------- .../v1/digital_twin_graph.proto | 18 +++ .../v1/digital_twin_registry.proto | 34 ++++-- .../seat_massager_provider/src/main.rs | 33 +++--- .../src/request_impl.rs | 2 - .../vehicle_core_provider/src/main.rs | 41 +++---- .../vehicle_core_provider/src/request_impl.rs | 2 - 8 files changed, 147 insertions(+), 143 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 2ee0bb4b..341ecf7e 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -13,7 +13,7 @@ use core_protobuf_data_access::module::digital_twin_graph::v1::{ }; use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_client::DigitalTwinRegistryClient; use core_protobuf_data_access::module::digital_twin_registry::v1::{ - EndpointInfo, FindByInstanceIdRequest, FindByInstanceIdResponse, FindByModelIdRequest, + EntityAccessInfo, FindByInstanceIdRequest, FindByInstanceIdResponse, FindByModelIdRequest, FindByModelIdResponse, }; use log::{debug, warn}; @@ -76,7 +76,7 @@ impl DigitalTwinGraphImpl { model_id: &str, protocol: &str, operations: &[String], - ) -> Result, tonic::Status> { + ) -> Result, tonic::Status> { // Define the retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays @@ -99,11 +99,11 @@ impl DigitalTwinGraphImpl { Ok(response .entity_access_info_list .iter() - .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) - .filter(|endpoint_info| { - endpoint_info.protocol == protocol - && is_subset(operations, &endpoint_info.operations) + .filter(|entity_access_info| { + entity_access_info.protocol == protocol + && is_subset(operations, &entity_access_info.operations) }) + .cloned() .collect()) } @@ -118,7 +118,7 @@ impl DigitalTwinGraphImpl { instance_id: &str, protocol: &str, operations: &[String], - ) -> Result, tonic::Status> { + ) -> Result, tonic::Status> { // Define the retry strategy. let retry_strategy = ExponentialBackoff::from_millis(Self::BACKOFF_BASE_DURATION_IN_MILLIS) .map(jitter) // add jitter to delays @@ -142,11 +142,11 @@ impl DigitalTwinGraphImpl { Ok(response .entity_access_info_list .iter() - .flat_map(|entity_access_info| entity_access_info.endpoint_info_list.clone()) - .filter(|endpoint_info| { - endpoint_info.protocol == protocol - && is_subset(operations, &endpoint_info.operations) + .filter(|entity_access_info| { + entity_access_info.protocol == protocol + && is_subset(operations, &entity_access_info.operations) }) + .cloned() .collect()) } @@ -243,7 +243,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { debug!("Received a find request for model id {model_id}"); // Retrieve the provider details. - let provider_endpoint_info_list = self + let provider_entity_access_info_list = self .find_digital_twin_providers_with_model_id( model_id.as_str(), digital_twin_protocol::GRPC, @@ -252,17 +252,23 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { .await?; // Build a map of instance id to its associated endpoint infos. - let instance_provider_map: std::collections::HashMap> = - provider_endpoint_info_list + let instance_provider_map: std::collections::HashMap> = + provider_entity_access_info_list .iter() - .map(|provider_endpoint_info| { - (provider_endpoint_info.context.clone(), provider_endpoint_info.clone()) + .map(|provider_entity_access_info| { + ( + provider_entity_access_info.instance_id.clone(), + provider_entity_access_info.clone(), + ) }) .fold( // fold is used to group the endpoint infos by instance id. std::collections::HashMap::new(), - |mut accumulator, (instance_id, endpoint_info)| { - accumulator.entry(instance_id).or_insert_with(Vec::new).push(endpoint_info); + |mut accumulator, (instance_id, entity_access_info)| { + accumulator + .entry(instance_id) + .or_insert_with(Vec::new) + .push(entity_access_info); accumulator }, ); @@ -271,10 +277,10 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { for instance_id in instance_provider_map.keys() { // We will only use the first provider. For a high availability scenario, we can try multiple providers. - let provider_endpoint_info = &instance_provider_map[instance_id][0]; + let provider_entity_access_info = &instance_provider_map[instance_id][0]; - let provider_uri = provider_endpoint_info.uri.clone(); - let instance_id = provider_endpoint_info.context.clone(); + let provider_uri = provider_entity_access_info.uri.clone(); + let instance_id = provider_entity_access_info.instance_id.clone(); let tx = self.tx.clone(); let mut rx = tx.subscribe(); @@ -347,7 +353,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let provider_endpoint_info = &provider_endpoint_info_list[0]; let provider_uri = provider_endpoint_info.uri.clone(); - let instance_id = provider_endpoint_info.context.clone(); + let instance_id = provider_endpoint_info.instance_id.clone(); let tx = self.tx.clone(); let mut rx = tx.subscribe(); @@ -429,7 +435,7 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let provider_endpoint_info = &provider_endpoint_info_list[0]; let provider_uri = provider_endpoint_info.uri.clone(); - let instance_id = provider_endpoint_info.context.clone(); + let instance_id = provider_endpoint_info.instance_id.clone(); let tx = self.tx.clone(); let mut rx = tx.subscribe(); diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs index 955a7642..73f7d31e 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -6,8 +6,8 @@ extern crate iref; use core_protobuf_data_access::module::digital_twin_registry::v1::digital_twin_registry_server::DigitalTwinRegistry; use core_protobuf_data_access::module::digital_twin_registry::v1::{ - EndpointInfo, EntityAccessInfo, FindByInstanceIdRequest, FindByInstanceIdResponse, - FindByModelIdRequest, FindByModelIdResponse, RegisterRequest, RegisterResponse, + EntityAccessInfo, FindByInstanceIdRequest, FindByInstanceIdResponse, FindByModelIdRequest, + FindByModelIdResponse, RegisterRequest, RegisterResponse, }; use log::{debug, info}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -69,7 +69,7 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { debug!("Received a find_by_instance_id request for instance id {instance_id}"); - let mut new_entity_access_info_list = Vec::::new(); + let mut matching_entity_access_info_list = Vec::::new(); // This block controls the lifetime of the lock. { @@ -77,35 +77,21 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { self.entity_access_info_map.read(); for entity_access_info_list in lock.values() { for entity_access_info in entity_access_info_list { - let mut instance_found: bool = false; - let mut new_endpoint_info_list: Vec = Vec::new(); - for endpoint_info_ in entity_access_info.endpoint_info_list.iter() { - if endpoint_info_.context == instance_id { - instance_found = true; - } - new_endpoint_info_list.push(endpoint_info_.clone()); - } - if instance_found { - let new_entity_access_info = EntityAccessInfo { - name: entity_access_info.name.clone(), - id: entity_access_info.id.clone(), - description: entity_access_info.description.clone(), - endpoint_info_list: new_endpoint_info_list, - }; - new_entity_access_info_list.push(new_entity_access_info); + if entity_access_info.instance_id == instance_id { + matching_entity_access_info_list.push(entity_access_info.clone()); } } } } - if new_entity_access_info_list.is_empty() { + if matching_entity_access_info_list.is_empty() { return Err(Status::not_found( "Unable to find any entities with instance id {instance_id}", )); } let response = - FindByInstanceIdResponse { entity_access_info_list: new_entity_access_info_list }; + FindByInstanceIdResponse { entity_access_info_list: matching_entity_access_info_list }; debug!("Completed the find_by_instance_id request."); @@ -125,11 +111,14 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { for entity_access_info in &request_inner.entity_access_info_list { self.register_entity(entity_access_info.clone()).map_err(|e| { Status::internal(format!( - "Failed to register the entity: {}, error: {}", - entity_access_info.id, e + "Failed to register the entity with instance id: {}, error: {}", + entity_access_info.instance_id, e )) })?; - info!("Registered the entity: {}", entity_access_info.id); + info!( + "Registered the entity with model id: {} and instance id: {}", + entity_access_info.model_id, entity_access_info.instance_id + ); } let response = RegisterResponse {}; @@ -150,18 +139,23 @@ impl DigitalTwinRegistryImpl { { let mut lock: RwLockWriteGuard>> = self.entity_access_info_map.write(); - let get_result = lock.get(&entity_access_info.id); + let get_result = lock.get(&entity_access_info.model_id); match get_result { Some(_) => { info!( "Registered another entity access info for entity {}", - &entity_access_info.id + &entity_access_info.model_id ); - lock.get_mut(&entity_access_info.id).unwrap().push(entity_access_info.clone()); + lock.get_mut(&entity_access_info.model_id) + .unwrap() + .push(entity_access_info.clone()); } None => { - info!("Registered entity {}", &entity_access_info.id); - lock.insert(entity_access_info.id.clone(), vec![entity_access_info.clone()]); + info!("Registered entity {}", &entity_access_info.model_id); + lock.insert( + entity_access_info.model_id.clone(), + vec![entity_access_info.clone()], + ); } }; } @@ -178,20 +172,16 @@ mod digital_twin_registry_impl_tests { async fn find_by_model_id_test() { let operations = vec![String::from("Subscribe"), String::from("Unsubscribe")]; - let endpoint_info = EndpointInfo { + let entity_access_info = EntityAccessInfo { + provider_id: String::from("test-provider"), + instance_id: String::from("1234567890"), + model_id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), protocol: String::from("grpc"), uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 - context: String::from("1234567890"), + context: String::from(""), operations, }; - let entity_access_info = EntityAccessInfo { - name: String::from("AmbientAirTemperature"), - id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), - description: String::from("Ambient air temperature"), - endpoint_info_list: vec![endpoint_info], - }; - let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); let digital_twin_registry_impl = @@ -201,7 +191,7 @@ mod digital_twin_registry_impl_tests { { let mut lock: RwLockWriteGuard>> = entity_access_info_map.write(); - lock.insert(entity_access_info.id.clone(), vec![entity_access_info.clone()]); + lock.insert(entity_access_info.model_id.clone(), vec![entity_access_info.clone()]); } let request = tonic::Request::new(FindByModelIdRequest { @@ -216,10 +206,9 @@ mod digital_twin_registry_impl_tests { let response_entity_access_info = response_inner.entity_access_info_list[0].clone(); - assert_eq!(response_entity_access_info.id, "dtmi:sdv:hvac:ambient_air_temperature;1"); - assert_eq!(response_entity_access_info.endpoint_info_list.len(), 1); + assert_eq!(response_entity_access_info.model_id, "dtmi:sdv:hvac:ambient_air_temperature;1"); assert_eq!( - response_entity_access_info.endpoint_info_list[0].uri, + response_entity_access_info.uri, "http://[::1]:40010" // Devskim: ignore DS137138 ); } @@ -228,20 +217,16 @@ mod digital_twin_registry_impl_tests { async fn find_by_instance_id_test() { let operations = vec![String::from("Subscribe"), String::from("Unsubscribe")]; - let endpoint_info = EndpointInfo { + let entity_access_info = EntityAccessInfo { + provider_id: String::from("test-provider"), + instance_id: String::from("1234567890"), + model_id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), protocol: String::from("grpc"), uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 - context: String::from("1234567890"), + context: String::from(""), operations, }; - let entity_access_info = EntityAccessInfo { - name: String::from("AmbientAirTemperature"), - id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), - description: String::from("Ambient air temperature"), - endpoint_info_list: vec![endpoint_info], - }; - let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); let digital_twin_registry_impl = @@ -251,7 +236,7 @@ mod digital_twin_registry_impl_tests { { let mut lock: RwLockWriteGuard>> = entity_access_info_map.write(); - lock.insert(entity_access_info.id.clone(), vec![entity_access_info.clone()]); + lock.insert(entity_access_info.model_id.clone(), vec![entity_access_info.clone()]); } let request = tonic::Request::new(FindByInstanceIdRequest { @@ -266,30 +251,25 @@ mod digital_twin_registry_impl_tests { let response_entity_access_info = response_inner.entity_access_info_list[0].clone(); - assert_eq!(response_entity_access_info.endpoint_info_list[0].context, "1234567890"); - assert_eq!(response_entity_access_info.endpoint_info_list.len(), 1); + assert_eq!(response_entity_access_info.instance_id, "1234567890"); assert_eq!( - response_entity_access_info.endpoint_info_list[0].uri, + response_entity_access_info.uri, "http://[::1]:40010" // Devskim: ignore DS137138 ); } #[tokio::test] async fn register_test() { - let endpoint_info = EndpointInfo { + let entity_access_info = EntityAccessInfo { + provider_id: String::from("test-provider"), + instance_id: String::from("1234567890"), + model_id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), protocol: String::from("grpc"), uri: String::from("http://[::1]:40010"), // Devskim: ignore DS137138 - context: String::from("1234567890"), + context: String::from(""), operations: vec![String::from("Subscribe"), String::from("Unsubscribe")], }; - let entity_access_info = EntityAccessInfo { - name: String::from("AmbientAirTemperature"), - id: String::from("dtmi:sdv:hvac:ambient_air_temperature;1"), - description: String::from("Ambient air temperature"), - endpoint_info_list: vec![endpoint_info], - }; - let entity_access_info_map = Arc::new(RwLock::new(HashMap::new())); let digital_twin_registry_impl = diff --git a/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto index 9b48eed1..642c7daf 100644 --- a/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto +++ b/interfaces/module/digital_twin_graph/v1/digital_twin_graph.proto @@ -7,32 +7,46 @@ syntax = "proto3"; package digital_twin_graph.v1.digital_twin_graph; service DigitalTwinGraph { + // Find the values of all instances that have the provided model id. rpc Find (FindRequest) returns (FindResponse); + // Get an instance or an instance's member's value. rpc Get (GetRequest) returns (GetResponse); + // Set an instance's or an instance memeber's value. rpc Set (SetRequest) returns (SetResponse); + // Invoke an instance's command. rpc Invoke (InvokeRequest) returns (InvokeResponse); } message FindRequest { + // The model id. string model_id = 1; } message FindResponse { + // The JSON-LD string for each matching value. repeated string values = 1; } message GetRequest { + // The instance id. string instance_id = 1; + // Scopes the request to a specific instance member located at the provided path. + // An empty string means the entire instance. string member_path = 2; } message GetResponse { + // The JSON-LD string for the retieved value. string value = 1; } message SetRequest { + // The instance id. string instance_id = 1; + // Scopes the request to a specific instance member located at the provided path. + // An empty string means the entire instance. string member_path = 2; + // The JSON-LD string for the value to be set. string value = 3; } @@ -40,11 +54,15 @@ message SetResponse { } message InvokeRequest { + // The instance id. string instance_id = 1; + // The instance's command that is to be invoked. string member_path = 2; + // The JSON-LD string for the command's request payload. string request_payload = 3; } message InvokeResponse { + // The JSON-LD string for the command's response payload. string response_payload = 1; } \ No newline at end of file diff --git a/interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto b/interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto index 4953a39b..1b79c533 100644 --- a/interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto +++ b/interfaces/module/digital_twin_registry/v1/digital_twin_registry.proto @@ -7,42 +7,54 @@ syntax = "proto3"; package digital_twin_registry.v1.digital_twin_registry; service DigitalTwinRegistry { + // Find the access details that have the provided model id. rpc FindByModelId (FindByModelIdRequest) returns (FindByModelIdResponse); + // Find the access details that have the provided instance id. rpc FindByInstanceId (FindByInstanceIdRequest) returns (FindByInstanceIdResponse); + // Register access details. rpc Register (RegisterRequest) returns (RegisterResponse); } -message EndpointInfo { - string protocol = 1; - repeated string operations = 2; - string uri = 3; - string context = 4; -} - message EntityAccessInfo { - string name = 1; - string id = 2; - string description = 3; - repeated EndpointInfo endpointInfoList = 4; + // The id of the provider that registered these access details. + string provider_id = 1; + // The provider id. + string instance_id = 2; + // The model id. + string model_id = 3; + // The protocol that should be used to access the instance. + string protocol = 4; + // The URI speific to the protocol that should be used to access the instance. + string uri = 5; + // Additional context specific tp the protocol that should be used to acess the instance. + // For example, with MQTT the URI will represent the address of the MQTT provider and the context will represent the topic name. + string context = 6; + // The names of the operations that are available at this endpoint. + repeated string operations = 7; } message FindByModelIdRequest { + // The model id. string model_id = 1; } message FindByModelIdResponse { + // The matching entries. repeated EntityAccessInfo entityAccessInfoList = 1; } message FindByInstanceIdRequest { + // The instance id. string instance_id = 1; } message FindByInstanceIdResponse { + // The matching entries. repeated EntityAccessInfo entityAccessInfoList = 1; } message RegisterRequest { + // The entries to register. repeated EntityAccessInfo entityAccessInfoList = 1; } diff --git a/samples/digital_twin_graph/seat_massager_provider/src/main.rs b/samples/digital_twin_graph/seat_massager_provider/src/main.rs index f244f994..d14c20e0 100644 --- a/samples/digital_twin_graph/seat_massager_provider/src/main.rs +++ b/samples/digital_twin_graph/seat_massager_provider/src/main.rs @@ -14,7 +14,7 @@ use samples_common::utils::retrieve_invehicle_digital_twin_uri; use samples_protobuf_data_access::async_rpc::v1::request::request_server::RequestServer; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::digital_twin_registry_client::DigitalTwinRegistryClient; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::{ - EndpointInfo, EntityAccessInfo, RegisterRequest, RegisterResponse, + EntityAccessInfo, RegisterRequest, RegisterResponse, }; use std::collections::HashMap; use std::net::SocketAddr; @@ -31,21 +31,22 @@ const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; /// The maximum number of retries. const MAX_RETRIES: usize = 100; +/// The provider ID. +const PROVIDER_ID: &str = "seat_massager_provider"; + /// Add an entry to the instance map. /// # Arguments /// * `instance_map` - The instance map. /// * `instance_id` - The instance id. /// * `model_id` - The model id. -/// * `description` - The description. /// * `serialized_value` - The serialized value. fn add_entry_to_instance_map( instance_map: &mut HashMap, instance_id: String, model_id: String, - description: String, serialized_value: String, ) { - instance_map.insert(instance_id, InstanceData { model_id, description, serialized_value }); + instance_map.insert(instance_id, InstanceData { model_id, serialized_value }); } /// Create the provider's state. @@ -117,7 +118,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, front_left_airbag_seat_massager_instance_id.clone(), sdv::premium_airbag_seat_massager::ID.to_string(), - sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), serde_json::to_string(&front_left_airbag_seat_massager).unwrap(), ); @@ -125,7 +125,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, front_right_airbag_seat_massager_instance_id.clone(), sdv::premium_airbag_seat_massager::ID.to_string(), - sdv::premium_airbag_seat_massager::DESCRIPTION.to_string(), serde_json::to_string(&front_right_airbag_seat_massager).unwrap(), ); @@ -133,7 +132,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, back_left_airbag_seat_massager_instance_id.clone(), sdv::basic_airbag_seat_massager::ID.to_string(), - sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), serde_json::to_string(&back_left_airbag_seat_massager).unwrap(), ); @@ -141,7 +139,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, back_center_airbag_seat_massager_instance_id.clone(), sdv::basic_airbag_seat_massager::ID.to_string(), - sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), serde_json::to_string(&back_center_airbag_seat_massager).unwrap(), ); @@ -149,7 +146,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, back_right_airbag_seat_massager_instance_id.clone(), sdv::basic_airbag_seat_massager::ID.to_string(), - sdv::basic_airbag_seat_massager::DESCRIPTION.to_string(), serde_json::to_string(&back_right_airbag_seat_massager).unwrap(), ); @@ -159,10 +155,12 @@ fn create_provider_state() -> ProviderState { /// Register the seat massagers. /// /// # Arguments +/// * `provider_id` - The provider's ID. /// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. /// * `provider_uri` - The provider's URI. /// * `provider_state` - The provider's state. async fn register_seat_massagers( + provider_id: &str, invehicle_digital_twin_uri: &str, provider_uri: &str, provider_state: Arc>, @@ -175,21 +173,17 @@ async fn register_seat_massagers( instance_id, instance_data.model_id ); - let endpoint_info = EndpointInfo { + let entity_access_info = EntityAccessInfo { + provider_id: provider_id.to_string(), + instance_id: instance_id.to_string(), + model_id: instance_data.model_id.to_string(), protocol: digital_twin_protocol::GRPC.to_string(), operations: vec![ digital_twin_operation::GET.to_string(), digital_twin_operation::INVOKE.to_string(), ], uri: provider_uri.to_string(), - context: instance_id.to_string(), // the context holds te - }; - - let entity_access_info = EntityAccessInfo { - name: String::new(), // no name, so we will use an empty name - id: instance_data.model_id.to_string(), - description: instance_data.description.to_string(), - endpoint_info_list: vec![endpoint_info], + context: "".to_string(), }; entity_access_info_list.push(entity_access_info); @@ -253,7 +247,8 @@ async fn main() -> Result<(), Box> { let server_future = Server::builder().add_service(RequestServer::new(request_impl)).serve(addr); info!("The HTTP server is listening on address '{provider_authority}'"); - register_seat_massagers(&invehicle_digital_twin_uri, &provider_uri, state.clone()).await?; + register_seat_massagers(PROVIDER_ID, &invehicle_digital_twin_uri, &provider_uri, state.clone()) + .await?; server_future.await?; diff --git a/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs b/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs index 55929a59..71d345a9 100644 --- a/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs +++ b/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs @@ -23,8 +23,6 @@ use tokio_retry::Retry; pub struct InstanceData { /// Model Id. pub model_id: String, - /// Description. - pub description: String, /// Serialized value (using JSON-LD as a string). pub serialized_value: String, } diff --git a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs index 7d388fc7..ba62682f 100644 --- a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs +++ b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs @@ -14,7 +14,7 @@ use samples_common::utils::retrieve_invehicle_digital_twin_uri; use samples_protobuf_data_access::async_rpc::v1::request::request_server::RequestServer; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::digital_twin_registry_client::DigitalTwinRegistryClient; use samples_protobuf_data_access::digital_twin_registry::v1::digital_twin_registry::{ - EndpointInfo, EntityAccessInfo, RegisterRequest, RegisterResponse, + EntityAccessInfo, RegisterRequest, RegisterResponse, }; use std::collections::HashMap; use std::net::SocketAddr; @@ -31,21 +31,22 @@ const BACKOFF_BASE_DURATION_IN_MILLIS: u64 = 100; /// The maximum number of retries. const MAX_RETRIES: usize = 100; +/// The provider ID. +const PROVIDER_ID: &str = "vehicle_provider"; + /// Add an entry to the instance map. /// # Arguments /// * `instance_map` - The instance map. /// * `instance_id` - The instance id. /// * `model_id` - The model id. -/// * `description` - The description. /// * `serialized_value` - The serialized value. fn add_entry_to_instance_map( instance_map: &mut HashMap, instance_id: String, model_id: String, - description: String, serialized_value: String, ) { - instance_map.insert(instance_id, InstanceData { model_id, description, serialized_value }); + instance_map.insert(instance_id, InstanceData { model_id, serialized_value }); } /// Create the provider's state. @@ -169,7 +170,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, front_left_seat_instance_id.clone(), sdv::seat::ID.to_string(), - sdv::seat::DESCRIPTION.to_string(), serde_json::to_string(&front_left_seat).unwrap(), ); @@ -177,7 +177,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, front_right_seat_instance_id.clone(), sdv::seat::ID.to_string(), - sdv::seat::DESCRIPTION.to_string(), serde_json::to_string(&front_right_seat).unwrap(), ); @@ -185,7 +184,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, back_left_seat_instance_id.clone(), sdv::seat::ID.to_string(), - sdv::seat::DESCRIPTION.to_string(), serde_json::to_string(&back_left_seat).unwrap(), ); @@ -193,7 +191,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, back_center_seat_instance_id.clone(), sdv::seat::ID.to_string(), - sdv::seat::DESCRIPTION.to_string(), serde_json::to_string(&back_center_seat).unwrap(), ); @@ -201,7 +198,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, back_right_seat_instance_id.clone(), sdv::seat::ID.to_string(), - sdv::seat::DESCRIPTION.to_string(), serde_json::to_string(&back_right_seat).unwrap(), ); @@ -209,7 +205,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, cabin_instance_id, sdv::cabin::ID.to_string(), - sdv::cabin::DESCRIPTION.to_string(), serde_json::to_string(&cabin_value).unwrap(), ); @@ -217,7 +212,6 @@ fn create_provider_state() -> ProviderState { &mut result.instance_map, vehicle_instance_id.clone(), sdv::vehicle::ID.to_string(), - sdv::vehicle::DESCRIPTION.to_string(), serde_json::to_string(&vehicle_value).unwrap(), ); @@ -227,10 +221,12 @@ fn create_provider_state() -> ProviderState { /// Register the vehicle parts. /// /// # Arguments +/// * `provider_id` - The provider's ID. /// * `invehicle_digital_twin_uri` - The In-Vehicle Digital Twin URI. /// * `provider_uri` - The provider's URI. /// * `provider_state` - The provider's state. async fn register_vehicle_parts( + provider_id: &str, invehicle_digital_twin_uri: &str, provider_uri: &str, provider_state: Arc>, @@ -243,18 +239,14 @@ async fn register_vehicle_parts( instance_id, instance_data.model_id ); - let endpoint_info = EndpointInfo { + let entity_access_info = EntityAccessInfo { + provider_id: provider_id.to_string(), + instance_id: instance_id.to_string(), + model_id: instance_data.model_id.to_string(), protocol: digital_twin_protocol::GRPC.to_string(), operations: vec![digital_twin_operation::GET.to_string()], uri: provider_uri.to_string(), - context: instance_id.to_string(), - }; - - let entity_access_info = EntityAccessInfo { - name: String::new(), // no name, so we will use an empty name - id: instance_data.model_id.to_string(), - description: instance_data.description.to_string(), - endpoint_info_list: vec![endpoint_info], + context: "".to_string(), }; entity_access_info_list.push(entity_access_info); @@ -317,8 +309,13 @@ async fn main() -> Result<(), Box> { let server_future = Server::builder().add_service(RequestServer::new(request_impl)).serve(addr); info!("The HTTP server is listening on address '{provider_authority}'"); - register_vehicle_parts(&invehicle_digital_twin_uri, &provider_uri, provider_state.clone()) - .await?; + register_vehicle_parts( + PROVIDER_ID, + &invehicle_digital_twin_uri, + &provider_uri, + provider_state.clone(), + ) + .await?; server_future.await?; diff --git a/samples/digital_twin_graph/vehicle_core_provider/src/request_impl.rs b/samples/digital_twin_graph/vehicle_core_provider/src/request_impl.rs index 387625b3..c56ba79f 100644 --- a/samples/digital_twin_graph/vehicle_core_provider/src/request_impl.rs +++ b/samples/digital_twin_graph/vehicle_core_provider/src/request_impl.rs @@ -22,8 +22,6 @@ use tokio_retry::Retry; pub struct InstanceData { /// Model Id. pub model_id: String, - /// Description. - pub description: String, /// Serialized value (using JSON-LD as a string). pub serialized_value: String, } From b116968dcc3ad657b2d6adc485030837f683257d Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 8 May 2024 17:20:41 -0700 Subject: [PATCH 091/109] Digital Twin Graph --- .../seat_massager_provider/src/main.rs | 35 ++++++++++--------- .../vehicle_core_provider/src/main.rs | 32 +++++++---------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/samples/digital_twin_graph/seat_massager_provider/src/main.rs b/samples/digital_twin_graph/seat_massager_provider/src/main.rs index d14c20e0..f30a6cd9 100644 --- a/samples/digital_twin_graph/seat_massager_provider/src/main.rs +++ b/samples/digital_twin_graph/seat_massager_provider/src/main.rs @@ -34,6 +34,14 @@ const MAX_RETRIES: usize = 100; /// The provider ID. const PROVIDER_ID: &str = "seat_massager_provider"; +// The seat massager ids. +// Issue #120 have been created to move these to a config file shared by the providers. +const FRONT_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "front_left_airbag_seat_massager"; +const FRONT_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "front_right_airbag_seat_massager"; +const BACK_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "back_left_airbag_seat_massager"; +const BACK_CENTER_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "back_center_airbag_seat_massager"; +const BACK_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "back_right_airbag_seat_massager"; + /// Add an entry to the instance map. /// # Arguments /// * `instance_map` - The instance map. @@ -55,10 +63,9 @@ fn create_provider_state() -> ProviderState { // Create the seat massagers. - let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); let front_left_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { - instance_id: front_left_airbag_seat_massager_instance_id.clone(), + instance_id: FRONT_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sequence_names: sdv::seat_massager::sequence_names::TYPE { value: Vec::::new(), ..Default::default() @@ -66,11 +73,9 @@ fn create_provider_state() -> ProviderState { ..Default::default() }; - let front_right_airbag_seat_massager_instance_id = - "front_right_airbag_seat_massager".to_string(); let front_right_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = sdv::premium_airbag_seat_massager::TYPE { - instance_id: front_right_airbag_seat_massager_instance_id.clone(), + instance_id: FRONT_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sequence_names: sdv::seat_massager::sequence_names::TYPE { value: Vec::::new(), ..Default::default() @@ -78,10 +83,9 @@ fn create_provider_state() -> ProviderState { ..Default::default() }; - let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { - instance_id: back_left_airbag_seat_massager_instance_id.clone(), + instance_id: BACK_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sequence_names: sdv::seat_massager::sequence_names::TYPE { value: Vec::::new(), ..Default::default() @@ -89,11 +93,9 @@ fn create_provider_state() -> ProviderState { ..Default::default() }; - let back_center_airbag_seat_massager_instance_id = - "back_center_airbag_seat_massager".to_string(); let back_center_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { - instance_id: back_center_airbag_seat_massager_instance_id.clone(), + instance_id: BACK_CENTER_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sequence_names: sdv::seat_massager::sequence_names::TYPE { value: Vec::::new(), ..Default::default() @@ -101,10 +103,9 @@ fn create_provider_state() -> ProviderState { ..Default::default() }; - let back_right_airbag_seat_massager_instance_id = "back_right_airbag_seat_massager".to_string(); let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = sdv::basic_airbag_seat_massager::TYPE { - instance_id: back_right_airbag_seat_massager_instance_id.clone(), + instance_id: BACK_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sequence_names: sdv::seat_massager::sequence_names::TYPE { value: Vec::::new(), ..Default::default() @@ -116,35 +117,35 @@ fn create_provider_state() -> ProviderState { add_entry_to_instance_map( &mut result.instance_map, - front_left_airbag_seat_massager_instance_id.clone(), + FRONT_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sdv::premium_airbag_seat_massager::ID.to_string(), serde_json::to_string(&front_left_airbag_seat_massager).unwrap(), ); add_entry_to_instance_map( &mut result.instance_map, - front_right_airbag_seat_massager_instance_id.clone(), + FRONT_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sdv::premium_airbag_seat_massager::ID.to_string(), serde_json::to_string(&front_right_airbag_seat_massager).unwrap(), ); add_entry_to_instance_map( &mut result.instance_map, - back_left_airbag_seat_massager_instance_id.clone(), + BACK_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sdv::basic_airbag_seat_massager::ID.to_string(), serde_json::to_string(&back_left_airbag_seat_massager).unwrap(), ); add_entry_to_instance_map( &mut result.instance_map, - back_center_airbag_seat_massager_instance_id.clone(), + BACK_CENTER_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sdv::basic_airbag_seat_massager::ID.to_string(), serde_json::to_string(&back_center_airbag_seat_massager).unwrap(), ); add_entry_to_instance_map( &mut result.instance_map, - back_right_airbag_seat_massager_instance_id.clone(), + BACK_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), sdv::basic_airbag_seat_massager::ID.to_string(), serde_json::to_string(&back_right_airbag_seat_massager).unwrap(), ); diff --git a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs index ba62682f..9b675894 100644 --- a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs +++ b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs @@ -34,6 +34,14 @@ const MAX_RETRIES: usize = 100; /// The provider ID. const PROVIDER_ID: &str = "vehicle_provider"; +// The seat massager ids. +// Issue #120 have been created to move these to a config file shared by the providers. +const FRONT_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "front_left_airbag_seat_massager"; +const FRONT_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "front_right_airbag_seat_massager"; +const BACK_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "back_left_airbag_seat_massager"; +const BACK_CENTER_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "back_center_airbag_seat_massager"; +const BACK_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID: &str = "back_right_airbag_seat_massager"; + /// Add an entry to the instance map. /// # Arguments /// * `instance_map` - The instance map. @@ -53,27 +61,13 @@ fn add_entry_to_instance_map( fn create_provider_state() -> ProviderState { let mut result: ProviderState = ProviderState { instance_map: HashMap::new() }; - // Create the seat massager ids. - - let front_left_airbag_seat_massager_instance_id = "front_left_airbag_seat_massager".to_string(); - - let front_right_airbag_seat_massager_instance_id = - "front_right_airbag_seat_massager".to_string(); - - let back_left_airbag_seat_massager_instance_id = "back_left_airbag_seat_massager".to_string(); - - let back_center_airbag_seat_massager_instance_id = - "back_center_airbag_seat_massager".to_string(); - - let back_right_airbag_seat_massager_instance_id = "back_right_airbag_seat_massager".to_string(); - // Create the seats. let front_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); let front_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { instance_id: front_left_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: front_left_airbag_seat_massager_instance_id.to_string(), + instance_id: FRONT_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), }], ..Default::default() }; @@ -82,7 +76,7 @@ fn create_provider_state() -> ProviderState { let front_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { instance_id: front_right_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: front_right_airbag_seat_massager_instance_id.to_string(), + instance_id: FRONT_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), }], ..Default::default() }; @@ -91,7 +85,7 @@ fn create_provider_state() -> ProviderState { let back_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { instance_id: back_left_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: back_left_airbag_seat_massager_instance_id.to_string(), + instance_id: BACK_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), }], ..Default::default() }; @@ -100,7 +94,7 @@ fn create_provider_state() -> ProviderState { let back_center_seat: sdv::seat::TYPE = sdv::seat::TYPE { instance_id: back_center_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: back_center_airbag_seat_massager_instance_id.to_string(), + instance_id: BACK_CENTER_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), }], ..Default::default() }; @@ -109,7 +103,7 @@ fn create_provider_state() -> ProviderState { let back_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { instance_id: back_right_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { - instance_id: back_right_airbag_seat_massager_instance_id.to_string(), + instance_id: BACK_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), }], ..Default::default() }; From ca4757557e4366672d976cb060fd364fa9936820 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Wed, 8 May 2024 17:39:43 -0700 Subject: [PATCH 092/109] Digital Twin Graph --- core/module/digital_twin_graph/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/module/digital_twin_graph/src/lib.rs b/core/module/digital_twin_graph/src/lib.rs index e4209b99..4b67f3ba 100644 --- a/core/module/digital_twin_graph/src/lib.rs +++ b/core/module/digital_twin_graph/src/lib.rs @@ -15,11 +15,13 @@ use serde_derive::{Deserialize, Serialize}; pub struct TargetedPayload { /// The instance id for the target entity. pub instance_id: String, - /// The path within the target entity to member that we are targeting. + /// The path within the target entity to the specific member that we are targeting. + /// It will be empty when we want to target the entire entity. pub member_path: String, /// The operation to be performed on the target entity's member. pub operation: String, - /// The operation's payload. It will be empty when the operation does not require a payload. + /// The operation's payload. + /// It will be empty when the operation does not require a payload. pub payload: String, } From a0ce789f45aa6daf6b284f2f3affdfc13c7f75cf Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 9 May 2024 14:03:51 -0700 Subject: [PATCH 093/109] Digital Twin Graph --- digital-twin-model/src/sdv_v1.rs | 119 +++++++++++------- .../digital_twin_graph/consumer/src/main.rs | 36 +++--- .../seat_massager_provider/src/main.rs | 45 +++---- .../src/request_impl.rs | 2 +- .../vehicle_core_provider/src/main.rs | 15 ++- samples/seat_massager/consumer/src/main.rs | 4 +- .../provider/src/request_impl.rs | 6 +- 7 files changed, 122 insertions(+), 105 deletions(-) diff --git a/digital-twin-model/src/sdv_v1.rs b/digital-twin-model/src/sdv_v1.rs index ba5587ae..f1932662 100644 --- a/digital-twin-model/src/sdv_v1.rs +++ b/digital-twin-model/src/sdv_v1.rs @@ -31,7 +31,7 @@ pub mod airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -53,7 +53,7 @@ pub mod airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -80,7 +80,7 @@ pub mod airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -101,7 +101,7 @@ pub mod airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -156,7 +156,7 @@ pub mod basic_airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct ENTITY_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -242,7 +242,7 @@ pub mod cabin { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct ENTITY_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -281,6 +281,20 @@ pub mod camera { pub media_content: Vec, } } + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct ENTITY_TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, + #[serde(rename = "@type")] + #[derivative(Default(value = "crate::sdv_v1::camera::ID.to_string()"))] + pub model_id: String, + } } pub mod hmi { @@ -300,7 +314,7 @@ pub mod hmi { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -335,6 +349,20 @@ pub mod hmi { } } + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct ENTITY_TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, + #[serde(rename = "@type")] + #[derivative(Default(value = "crate::sdv_v1::hmi::ID.to_string()"))] + pub model_id: String, + } + pub mod status { pub const ID: &str = "dtmi:sdv:hmi:status;1"; pub const NAME: &str = "status"; @@ -367,6 +395,20 @@ pub mod hvac { pub type TYPE = bool; } + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct ENTITY_TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, + #[serde(rename = "@type")] + #[derivative(Default(value = "crate::sdv_v1::hvac::ID.to_string()"))] + pub model_id: String, + } } pub mod obd { @@ -380,6 +422,20 @@ pub mod obd { pub type TYPE = i32; } + + #[derive(derivative::Derivative)] + #[derivative(Default)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + pub struct ENTITY_TYPE { + #[serde(rename = "@context")] + #[derivative(Default(value = "crate::sdv_v1::context()"))] + pub context: Vec, + #[serde(rename = "@id")] + pub instance_id: String, + #[serde(rename = "@type")] + #[derivative(Default(value = "crate::sdv_v1::obd::ID.to_string()"))] + pub model_id: String, + } } pub mod premium_airbag_seat_massager { @@ -389,7 +445,7 @@ pub mod premium_airbag_seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct ENTITY_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -427,7 +483,7 @@ pub mod seat { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct ENTITY_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -449,20 +505,7 @@ pub mod seat_massager { pub const NAME: &str = "sequence_names"; pub const DESCRIPTION: &str = "The name of each of the stored sequences."; - #[derive(derivative::Derivative)] - #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { - #[serde(rename = "@context")] - #[derivative(Default(value = "crate::sdv_v1::context()"))] - pub context: Vec, - #[serde(rename = "@type")] - #[derivative(Default( - value = "crate::sdv_v1::seat_massager::sequence_names::ID.to_string()" - ))] - pub model_id: String, - pub value: Vec, - } + pub type TYPE = Vec; } pub mod load_sequence { @@ -478,7 +521,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -499,7 +542,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -526,7 +569,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -546,7 +589,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -573,7 +616,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -593,7 +636,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -620,7 +663,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -640,7 +683,7 @@ pub mod seat_massager { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -684,18 +727,8 @@ pub mod vehicle { pub type TYPE = String; } - #[derive(derivative::Derivative)] - #[derivative(Default)] - #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] + #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, Default)] pub struct TYPE { - #[serde(rename = "@context")] - #[derivative(Default(value = "crate::sdv_v1::context()"))] - pub context: Vec, - #[serde(rename = "@type")] - #[derivative(Default( - value = "crate::sdv_v1::vehicle::vehicle_identification::ID.to_string()" - ))] - pub model_id: String, pub vin: crate::sdv_v1::vehicle::vehicle_identification::vin::TYPE, } } @@ -719,7 +752,7 @@ pub mod vehicle { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct ENTITY_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, diff --git a/samples/digital_twin_graph/consumer/src/main.rs b/samples/digital_twin_graph/consumer/src/main.rs index c3078d79..4a0f0d5f 100644 --- a/samples/digital_twin_graph/consumer/src/main.rs +++ b/samples/digital_twin_graph/consumer/src/main.rs @@ -150,7 +150,7 @@ async fn invoke( /// The vehicle instance. async fn find_vehicle( client: DigitalTwinGraphClient, -) -> Result { +) -> Result { // Find all vehicle instances. let find_vehicle_response: FindResponse = find(client, sdv::vehicle::ID.to_string()).await?; if find_vehicle_response.values.is_empty() { @@ -158,7 +158,7 @@ async fn find_vehicle( } // For now, we will just use the first vehicle instance. - let vehicle: sdv::vehicle::TYPE = + let vehicle: sdv::vehicle::ENTITY_TYPE = serde_json::from_str(&find_vehicle_response.values[0]).unwrap(); info!("The vehicle's instance id is: {}", vehicle.instance_id); @@ -175,8 +175,8 @@ async fn find_vehicle( /// The cabin instance. async fn find_cabin( client: DigitalTwinGraphClient, - vehicle: &sdv::vehicle::TYPE, -) -> Result { + vehicle: &sdv::vehicle::ENTITY_TYPE, +) -> Result { // Get the cabin instance id. if vehicle.cabin.is_empty() { return Err("The vehicle does not have a cabin".to_string()); @@ -192,7 +192,7 @@ async fn find_cabin( get(client.clone(), cabin_instance_id.clone(), "".to_string()).await?; // Deserialize the cabin instance. - let cabin: sdv::cabin::TYPE = serde_json::from_str(&get_cabin_response.value).unwrap(); + let cabin: sdv::cabin::ENTITY_TYPE = serde_json::from_str(&get_cabin_response.value).unwrap(); Ok(cabin) } @@ -208,10 +208,10 @@ async fn find_cabin( /// The seat instance. async fn find_seat( client: DigitalTwinGraphClient, - cabin: &sdv::cabin::TYPE, + cabin: &sdv::cabin::ENTITY_TYPE, seat_row: i32, seat_posotion: sdv::cabin::seat::SEAT_POSITION_TYPE, -) -> Result { +) -> Result { if cabin.seat.is_empty() { return Err("The cabin does not have any seats".to_string()); } @@ -226,7 +226,7 @@ async fn find_seat( get(client.clone(), seat_relationship.instance_id.clone(), "".to_string()).await?; // Deserialize the seat instance. - let seat: sdv::seat::TYPE = serde_json::from_str(&get_seat_response.value).unwrap(); + let seat: sdv::seat::ENTITY_TYPE = serde_json::from_str(&get_seat_response.value).unwrap(); info!("The seat's instance id is: {}", seat.instance_id); @@ -246,8 +246,8 @@ async fn find_seat( /// The premium airbag seat massager instance. async fn find_premium_airbag_seat_massager( client: DigitalTwinGraphClient, - seat: &sdv::seat::TYPE, -) -> Result { + seat: &sdv::seat::ENTITY_TYPE, +) -> Result { if seat.seat_massager.is_empty() { return Err("The seat does not have a seat massage".to_string()); } @@ -272,7 +272,7 @@ async fn find_premium_airbag_seat_massager( } // Deserialize the seat massager instance. - let seat_massager: sdv::premium_airbag_seat_massager::TYPE = + let seat_massager: sdv::premium_airbag_seat_massager::ENTITY_TYPE = serde_json::from_str(&get_seat_massager_response.value).unwrap(); info!("The seat massager's instance id is: {}", seat_massager.instance_id); @@ -292,14 +292,14 @@ async fn find_premium_airbag_seat_massager( /// An empty result if the operation is successful. async fn perform_step( client: DigitalTwinGraphClient, - seat_massager: sdv::premium_airbag_seat_massager::TYPE, + seat_massager: sdv::premium_airbag_seat_massager::ENTITY_TYPE, airbag_identifier: i32, inflation_level: i32, inflation_duration_in_seconds: i32, ) -> Result<(), String> { // Generate the perform_step operation's request payload. - let request_payload: sdv::airbag_seat_massager::perform_step::request::TYPE = - sdv::airbag_seat_massager::perform_step::request::TYPE { + let request_payload: sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE = + sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE { step: vec![sdv::airbag_seat_massager::airbag_adjustment::TYPE { airbag_identifier, inflation_level, @@ -337,19 +337,19 @@ async fn interact_with_digital_twin(invehicle_digital_twin_uri: String) -> Resul connect_to_digital_twin_graph_service(invehicle_digital_twin_uri.clone()).await?; // Find the vehicle instance. - let vehicle: sdv::vehicle::TYPE = find_vehicle(client.clone()).await.unwrap(); + let vehicle: sdv::vehicle::ENTITY_TYPE = find_vehicle(client.clone()).await.unwrap(); // Find the cabin instance. - let cabin: sdv::cabin::TYPE = find_cabin(client.clone(), &vehicle).await.unwrap(); + let cabin: sdv::cabin::ENTITY_TYPE = find_cabin(client.clone(), &vehicle).await.unwrap(); // Find the front left seat instance. - let front_left_seat: sdv::seat::TYPE = + let front_left_seat: sdv::seat::ENTITY_TYPE = find_seat(client.clone(), &cabin, 1, sdv::cabin::seat::SEAT_POSITION_TYPE::left) .await .unwrap(); // Find the premium airbag seat massager instance. - let seat_massager: sdv::premium_airbag_seat_massager::TYPE = + let seat_massager: sdv::premium_airbag_seat_massager::ENTITY_TYPE = find_premium_airbag_seat_massager(client.clone(), &front_left_seat).await.unwrap(); // Randomly generate the airbag adjustment field values. diff --git a/samples/digital_twin_graph/seat_massager_provider/src/main.rs b/samples/digital_twin_graph/seat_massager_provider/src/main.rs index f30a6cd9..e3ea6347 100644 --- a/samples/digital_twin_graph/seat_massager_provider/src/main.rs +++ b/samples/digital_twin_graph/seat_massager_provider/src/main.rs @@ -63,53 +63,38 @@ fn create_provider_state() -> ProviderState { // Create the seat massagers. - let front_left_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = - sdv::premium_airbag_seat_massager::TYPE { + let front_left_airbag_seat_massager: sdv::premium_airbag_seat_massager::ENTITY_TYPE = + sdv::premium_airbag_seat_massager::ENTITY_TYPE { instance_id: FRONT_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), - sequence_names: sdv::seat_massager::sequence_names::TYPE { - value: Vec::::new(), - ..Default::default() - }, + sequence_names: Vec::::new(), ..Default::default() }; - let front_right_airbag_seat_massager: sdv::premium_airbag_seat_massager::TYPE = - sdv::premium_airbag_seat_massager::TYPE { + let front_right_airbag_seat_massager: sdv::premium_airbag_seat_massager::ENTITY_TYPE = + sdv::premium_airbag_seat_massager::ENTITY_TYPE { instance_id: FRONT_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), - sequence_names: sdv::seat_massager::sequence_names::TYPE { - value: Vec::::new(), - ..Default::default() - }, + sequence_names: Vec::::new(), ..Default::default() }; - let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = - sdv::basic_airbag_seat_massager::TYPE { + let back_left_airbag_seat_massager: sdv::basic_airbag_seat_massager::ENTITY_TYPE = + sdv::basic_airbag_seat_massager::ENTITY_TYPE { instance_id: BACK_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), - sequence_names: sdv::seat_massager::sequence_names::TYPE { - value: Vec::::new(), - ..Default::default() - }, + sequence_names: Vec::::new(), ..Default::default() }; - let back_center_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = - sdv::basic_airbag_seat_massager::TYPE { + let back_center_airbag_seat_massager: sdv::basic_airbag_seat_massager::ENTITY_TYPE = + sdv::basic_airbag_seat_massager::ENTITY_TYPE { instance_id: BACK_CENTER_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), - sequence_names: sdv::seat_massager::sequence_names::TYPE { - value: Vec::::new(), - ..Default::default() - }, + sequence_names: Vec::::new(), ..Default::default() }; - let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::TYPE = - sdv::basic_airbag_seat_massager::TYPE { + let back_right_airbag_seat_massager: sdv::basic_airbag_seat_massager::ENTITY_TYPE = + sdv::basic_airbag_seat_massager::ENTITY_TYPE { instance_id: BACK_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), - sequence_names: sdv::seat_massager::sequence_names::TYPE { - value: Vec::::new(), - ..Default::default() - }, + sequence_names: Vec::::new(), ..Default::default() }; diff --git a/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs b/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs index 71d345a9..024548d0 100644 --- a/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs +++ b/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs @@ -216,7 +216,7 @@ impl RequestImpl { targeted_payload.member_path, targeted_payload.instance_id ); - let response = sdv::airbag_seat_massager::perform_step::response::TYPE { + let response = sdv::airbag_seat_massager::perform_step::response::PAYLOAD_TYPE { status: sdv::airbag_seat_massager::status::TYPE { code: 200, message: "The step was performed successfully".to_string(), diff --git a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs index 9b675894..2157e662 100644 --- a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs +++ b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs @@ -64,7 +64,7 @@ fn create_provider_state() -> ProviderState { // Create the seats. let front_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); - let front_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { + let front_left_seat: sdv::seat::ENTITY_TYPE = sdv::seat::ENTITY_TYPE { instance_id: front_left_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: FRONT_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), @@ -73,7 +73,7 @@ fn create_provider_state() -> ProviderState { }; let front_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); - let front_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { + let front_right_seat: sdv::seat::ENTITY_TYPE = sdv::seat::ENTITY_TYPE { instance_id: front_right_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: FRONT_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), @@ -82,7 +82,7 @@ fn create_provider_state() -> ProviderState { }; let back_left_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); - let back_left_seat: sdv::seat::TYPE = sdv::seat::TYPE { + let back_left_seat: sdv::seat::ENTITY_TYPE = sdv::seat::ENTITY_TYPE { instance_id: back_left_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: BACK_LEFT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), @@ -91,7 +91,7 @@ fn create_provider_state() -> ProviderState { }; let back_center_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); - let back_center_seat: sdv::seat::TYPE = sdv::seat::TYPE { + let back_center_seat: sdv::seat::ENTITY_TYPE = sdv::seat::ENTITY_TYPE { instance_id: back_center_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: BACK_CENTER_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), @@ -100,7 +100,7 @@ fn create_provider_state() -> ProviderState { }; let back_right_seat_instance_id = format!("{}", uuid::Uuid::new_v4()); - let back_right_seat: sdv::seat::TYPE = sdv::seat::TYPE { + let back_right_seat: sdv::seat::ENTITY_TYPE = sdv::seat::ENTITY_TYPE { instance_id: back_right_seat_instance_id.clone(), seat_massager: vec![sdv::seat::seat_massager::RELATIONSHIP_TYPE { instance_id: BACK_RIGHT_AIRBAG_SEAT_MASSAGER_INSTANCE_ID.to_string(), @@ -110,7 +110,7 @@ fn create_provider_state() -> ProviderState { // Create the cabin. let cabin_instance_id = format!("{}", uuid::Uuid::new_v4()); - let cabin_value: sdv::cabin::TYPE = sdv::cabin::TYPE { + let cabin_value: sdv::cabin::ENTITY_TYPE = sdv::cabin::ENTITY_TYPE { instance_id: cabin_instance_id.clone(), seat: vec![ sdv::cabin::seat::RELATIONSHIP_TYPE { @@ -147,9 +147,8 @@ fn create_provider_state() -> ProviderState { let vehicle_identification: sdv::vehicle::vehicle_identification::TYPE = sdv::vehicle::vehicle_identification::TYPE { vin: "1M8GDM9AXKP042788".to_string(), - ..Default::default() }; - let vehicle_value: sdv::vehicle::TYPE = sdv::vehicle::TYPE { + let vehicle_value: sdv::vehicle::ENTITY_TYPE = sdv::vehicle::ENTITY_TYPE { instance_id: vehicle_instance_id.clone(), vehicle_identification, cabin: vec![sdv::vehicle::cabin::RELATIONSHIP_TYPE { diff --git a/samples/seat_massager/consumer/src/main.rs b/samples/seat_massager/consumer/src/main.rs index 132c9d06..385a58c9 100644 --- a/samples/seat_massager/consumer/src/main.rs +++ b/samples/seat_massager/consumer/src/main.rs @@ -61,8 +61,8 @@ fn start_seat_massage_steps( let inflation_level = rng.gen_range(1..=10); let inflation_duration_in_seconds = rng.gen_range(1..=5); - let request_payload: sdv::airbag_seat_massager::perform_step::request::TYPE = - sdv::airbag_seat_massager::perform_step::request::TYPE { + let request_payload: sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE = + sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE { step: vec![sdv::airbag_seat_massager::airbag_adjustment::TYPE { airbag_identifier, inflation_level, diff --git a/samples/seat_massager/provider/src/request_impl.rs b/samples/seat_massager/provider/src/request_impl.rs index b3c0b43c..147dccd8 100644 --- a/samples/seat_massager/provider/src/request_impl.rs +++ b/samples/seat_massager/provider/src/request_impl.rs @@ -74,7 +74,7 @@ impl Request for RequestImpl { // Extract the request from the request payload. let perform_step_request_opt: Option< - sdv::airbag_seat_massager::perform_step::request::TYPE, + sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE, > = serde_json::from_value(request_payload_json) .expect("Failed to deserialize the request."); if perform_step_request_opt.is_none() { @@ -86,8 +86,8 @@ impl Request for RequestImpl { info!("Performing the step: {:?}", perform_step_request.step); // Prepare the perform_step response payload. - let response_payload: sdv::airbag_seat_massager::perform_step::response::TYPE = - sdv::airbag_seat_massager::perform_step::response::TYPE { + let response_payload: sdv::airbag_seat_massager::perform_step::response::PAYLOAD_TYPE = + sdv::airbag_seat_massager::perform_step::response::PAYLOAD_TYPE { status: sdv::airbag_seat_massager::status::TYPE { code: status::ok::CODE, message: status::ok::MESSAGE.to_string(), From e32c6d7c8959046a1cedc08f536ce566ff96c7ac Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 9 May 2024 15:18:14 -0700 Subject: [PATCH 094/109] Digital Twin Graph --- .../src/digital_twin_registry_impl.rs | 26 ++++++ digital-twin-model/src/sdv_v1.rs | 81 +++++++++---------- .../digital_twin_graph/consumer/src/main.rs | 5 +- .../src/request_impl.rs | 2 +- .../vehicle_core_provider/src/main.rs | 6 +- samples/seat_massager/consumer/src/main.rs | 2 +- .../provider/src/request_impl.rs | 2 +- 7 files changed, 70 insertions(+), 54 deletions(-) diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs index 73f7d31e..2fea764f 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -137,6 +137,32 @@ impl DigitalTwinRegistryImpl { fn register_entity(&self, entity_access_info: EntityAccessInfo) -> Result<(), String> { // This block controls the lifetime of the lock. { + if entity_access_info.provider_id.is_empty() { + return Err("Provider id cannot be empty".to_string()); + } + + if entity_access_info.model_id.is_empty() { + return Err("Model id cannot be empty".to_string()); + } + + if entity_access_info.instance_id.is_empty() { + return Err("Instance id cannot be empty".to_string()); + } + + if entity_access_info.protocol.is_empty() { + return Err("Protocol cannot be empty".to_string()); + } + + if entity_access_info.uri.is_empty() { + return Err("Uri cannot be empty".to_string()); + } + + if entity_access_info.operations.is_empty() { + return Err("Operations cannot be empty".to_string()); + } + + // Note: the context is optional. + let mut lock: RwLockWriteGuard>> = self.entity_access_info_map.write(); let get_result = lock.get(&entity_access_info.model_id); diff --git a/digital-twin-model/src/sdv_v1.rs b/digital-twin-model/src/sdv_v1.rs index f1932662..69e0cc72 100644 --- a/digital-twin-model/src/sdv_v1.rs +++ b/digital-twin-model/src/sdv_v1.rs @@ -41,7 +41,7 @@ pub mod airbag_seat_massager { ))] pub model_id: String, pub sequence_name: String, - pub sequence: crate::sdv_v1::airbag_seat_massager::massage_step::TYPE, + pub sequence: crate::sdv_v1::airbag_seat_massager::massage_step::SCHEMA_TYPE, } } @@ -62,7 +62,7 @@ pub mod airbag_seat_massager { value = "crate::sdv_v1::airbag_seat_massager::store_sequence::response::ID.to_string()" ))] pub model_id: String, - pub status: crate::sdv_v1::airbag_seat_massager::status::TYPE, + pub status: crate::sdv_v1::airbag_seat_massager::status::SCHEMA_TYPE, } } } @@ -89,7 +89,7 @@ pub mod airbag_seat_massager { value = "crate::sdv_v1::airbag_seat_massager::perform_step::request::ID.to_string()" ))] pub model_id: String, - pub step: crate::sdv_v1::airbag_seat_massager::massage_step::TYPE, + pub step: crate::sdv_v1::airbag_seat_massager::massage_step::SCHEMA_TYPE, } } @@ -110,7 +110,7 @@ pub mod airbag_seat_massager { value = "crate::sdv_v1::airbag_seat_massager::perform_step::response::ID.to_string()" ))] pub model_id: String, - pub status: crate::sdv_v1::airbag_seat_massager::status::TYPE, + pub status: crate::sdv_v1::airbag_seat_massager::status::SCHEMA_TYPE, } } } @@ -121,7 +121,7 @@ pub mod airbag_seat_massager { pub const DESCRIPTION: &str = "An airbag adjustment."; #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct SCHEMA_TYPE { pub airbag_identifier: i32, pub inflation_level: i32, pub inflation_duration_in_seconds: i32, @@ -133,7 +133,8 @@ pub mod airbag_seat_massager { pub const NAME: &str = "massage_step"; pub const DESCRIPTION: &str = "The massage step."; - pub type TYPE = Vec; + pub type SCHEMA_TYPE = + Vec; } pub mod status { @@ -142,7 +143,7 @@ pub mod airbag_seat_massager { pub const DESCRIPTION: &str = "The status."; #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, Default)] - pub struct TYPE { + pub struct SCHEMA_TYPE { pub code: i32, pub message: String, } @@ -167,7 +168,7 @@ pub mod basic_airbag_seat_massager { value = "crate::sdv_v1::basic_airbag_seat_massager::ID.to_string()" ))] pub model_id: String, - pub sequence_names: crate::sdv_v1::seat_massager::sequence_names::TYPE, + pub sequence_names: crate::sdv_v1::seat_massager::sequence_names::SCHEMA_TYPE, } } @@ -187,8 +188,6 @@ pub mod cabin { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; } pub mod hvac { @@ -203,8 +202,6 @@ pub mod cabin { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; } pub mod seat { @@ -235,8 +232,6 @@ pub mod cabin { pub seat_row: SEAT_ROW_TYPE, pub seat_position: SEAT_POSITION_TYPE, } - - pub type TYPE = Vec; } #[derive(derivative::Derivative)] @@ -251,9 +246,9 @@ pub mod cabin { #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::cabin::ID.to_string()"))] pub model_id: String, - pub infotainment: crate::sdv_v1::cabin::infotainment::TYPE, - pub hvac: crate::sdv_v1::cabin::hvac::TYPE, - pub seat: crate::sdv_v1::cabin::seat::TYPE, + pub infotainment: Vec, + pub hvac: Vec, + pub seat: Vec, } } @@ -270,7 +265,7 @@ pub mod camera { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -294,7 +289,7 @@ pub mod camera { #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::camera::ID.to_string()"))] pub model_id: String, - } + } } pub mod hmi { @@ -335,7 +330,7 @@ pub mod hmi { #[derive(derivative::Derivative)] #[derivative(Default)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug)] - pub struct TYPE { + pub struct PAYLOAD_TYPE { #[serde(rename = "@context")] #[derivative(Default(value = "crate::sdv_v1::context()"))] pub context: Vec, @@ -344,7 +339,7 @@ pub mod hmi { value = "crate::sdv_v1::hmi::show_notification::response::ID.to_string()" ))] pub model_id: String, - pub status: crate::sdv_v1::hmi::status::TYPE, + pub status: crate::sdv_v1::hmi::status::SCHEMA_TYPE, } } } @@ -361,7 +356,7 @@ pub mod hmi { #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::hmi::ID.to_string()"))] pub model_id: String, - } + } pub mod status { pub const ID: &str = "dtmi:sdv:hmi:status;1"; @@ -369,7 +364,7 @@ pub mod hmi { pub const DESCRIPTION: &str = "The status."; #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, Default)] - pub struct TYPE { + pub struct SCHEMA_TYPE { pub code: i32, pub message: String, } @@ -385,7 +380,7 @@ pub mod hvac { pub const NAME: &str = "ambient_air_temperature"; pub const DESCRIPTION: &str = "The immediate surroundings air temperature (in Fahrenheit)."; - pub type TYPE = i32; + pub type SCHEMA_TYPE = i32; } pub mod is_air_conditioning_active { @@ -393,7 +388,7 @@ pub mod hvac { pub const NAME: &str = "is_air_conditioning_active"; pub const DESCRIPTION: &str = "Is air conditioning active?"; - pub type TYPE = bool; + pub type SCHEMA_TYPE = bool; } #[derive(derivative::Derivative)] @@ -408,7 +403,7 @@ pub mod hvac { #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::hvac::ID.to_string()"))] pub model_id: String, - } + } } pub mod obd { @@ -420,7 +415,7 @@ pub mod obd { pub const NAME: &str = "hybrid_battery_remaining"; pub const DESCRIPTION: &str = "The remaining hybrid battery life."; - pub type TYPE = i32; + pub type SCHEMA_TYPE = i32; } #[derive(derivative::Derivative)] @@ -435,7 +430,7 @@ pub mod obd { #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::obd::ID.to_string()"))] pub model_id: String, - } + } } pub mod premium_airbag_seat_massager { @@ -456,7 +451,7 @@ pub mod premium_airbag_seat_massager { value = "crate::sdv_v1::premium_airbag_seat_massager::ID.to_string()" ))] pub model_id: String, - pub sequence_names: crate::sdv_v1::seat_massager::sequence_names::TYPE, + pub sequence_names: crate::sdv_v1::seat_massager::sequence_names::SCHEMA_TYPE, } } @@ -476,8 +471,6 @@ pub mod seat { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; } #[derive(derivative::Derivative)] @@ -492,7 +485,7 @@ pub mod seat { #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::seat::ID.to_string()"))] pub model_id: String, - pub seat_massager: crate::sdv_v1::seat::seat_massager::TYPE, + pub seat_massager: Vec, } } @@ -505,7 +498,7 @@ pub mod seat_massager { pub const NAME: &str = "sequence_names"; pub const DESCRIPTION: &str = "The name of each of the stored sequences."; - pub type TYPE = Vec; + pub type SCHEMA_TYPE = Vec; } pub mod load_sequence { @@ -551,7 +544,7 @@ pub mod seat_massager { value = "crate::sdv_v1::seat_massager::load_sequence::response::ID.to_string()" ))] pub model_id: String, - pub status: crate::sdv_v1::seat_massager::status::TYPE, + pub status: crate::sdv_v1::seat_massager::status::SCHEMA_TYPE, } } } @@ -598,7 +591,7 @@ pub mod seat_massager { value = "crate::sdv_v1::seat_massager::pause::response::ID.to_string()" ))] pub model_id: String, - pub status: crate::sdv_v1::seat_massager::status::TYPE, + pub status: crate::sdv_v1::seat_massager::status::SCHEMA_TYPE, } } } @@ -645,7 +638,7 @@ pub mod seat_massager { value = "crate::sdv_v1::seat_massager::play::response::ID.to_string()" ))] pub model_id: String, - pub status: crate::sdv_v1::seat_massager::status::TYPE, + pub status: crate::sdv_v1::seat_massager::status::SCHEMA_TYPE, } } } @@ -692,7 +685,7 @@ pub mod seat_massager { value = "crate::sdv_v1::seat_massager::reset::response::ID.to_string()" ))] pub model_id: String, - pub status: crate::sdv_v1::seat_massager::status::TYPE, + pub status: crate::sdv_v1::seat_massager::status::SCHEMA_TYPE, } } } @@ -703,7 +696,7 @@ pub mod seat_massager { pub const DESCRIPTION: &str = "The status."; #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, Default)] - pub struct TYPE { + pub struct SCHEMA_TYPE { pub code: i32, pub message: String, } @@ -724,12 +717,12 @@ pub mod vehicle { pub const NAME: &str = "vin"; pub const DESCRIPTION: &str = "Vehicle Identification Number."; - pub type TYPE = String; + pub type SCHEMA_TYPE = String; } #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, Default)] - pub struct TYPE { - pub vin: crate::sdv_v1::vehicle::vehicle_identification::vin::TYPE, + pub struct SCHEMA_TYPE { + pub vin: crate::sdv_v1::vehicle::vehicle_identification::vin::SCHEMA_TYPE, } } @@ -745,8 +738,6 @@ pub mod vehicle { #[serde(rename = "@id")] pub instance_id: String, } - - pub type TYPE = Vec; } #[derive(derivative::Derivative)] @@ -761,7 +752,7 @@ pub mod vehicle { #[serde(rename = "@type")] #[derivative(Default(value = "crate::sdv_v1::vehicle::ID.to_string()"))] pub model_id: String, - pub vehicle_identification: crate::sdv_v1::vehicle::vehicle_identification::TYPE, - pub cabin: crate::sdv_v1::vehicle::cabin::TYPE, + pub vehicle_identification: crate::sdv_v1::vehicle::vehicle_identification::SCHEMA_TYPE, + pub cabin: Vec, } } diff --git a/samples/digital_twin_graph/consumer/src/main.rs b/samples/digital_twin_graph/consumer/src/main.rs index 4a0f0d5f..e5a8e3e3 100644 --- a/samples/digital_twin_graph/consumer/src/main.rs +++ b/samples/digital_twin_graph/consumer/src/main.rs @@ -226,7 +226,8 @@ async fn find_seat( get(client.clone(), seat_relationship.instance_id.clone(), "".to_string()).await?; // Deserialize the seat instance. - let seat: sdv::seat::ENTITY_TYPE = serde_json::from_str(&get_seat_response.value).unwrap(); + let seat: sdv::seat::ENTITY_TYPE = + serde_json::from_str(&get_seat_response.value).unwrap(); info!("The seat's instance id is: {}", seat.instance_id); @@ -300,7 +301,7 @@ async fn perform_step( // Generate the perform_step operation's request payload. let request_payload: sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE = sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE { - step: vec![sdv::airbag_seat_massager::airbag_adjustment::TYPE { + step: vec![sdv::airbag_seat_massager::airbag_adjustment::SCHEMA_TYPE { airbag_identifier, inflation_level, inflation_duration_in_seconds, diff --git a/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs b/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs index 024548d0..d9195c59 100644 --- a/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs +++ b/samples/digital_twin_graph/seat_massager_provider/src/request_impl.rs @@ -217,7 +217,7 @@ impl RequestImpl { ); let response = sdv::airbag_seat_massager::perform_step::response::PAYLOAD_TYPE { - status: sdv::airbag_seat_massager::status::TYPE { + status: sdv::airbag_seat_massager::status::SCHEMA_TYPE { code: 200, message: "The step was performed successfully".to_string(), }, diff --git a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs index 2157e662..1ac65c3c 100644 --- a/samples/digital_twin_graph/vehicle_core_provider/src/main.rs +++ b/samples/digital_twin_graph/vehicle_core_provider/src/main.rs @@ -144,10 +144,8 @@ fn create_provider_state() -> ProviderState { // Create the vehicle. let vehicle_instance_id = format!("{}", uuid::Uuid::new_v4()); - let vehicle_identification: sdv::vehicle::vehicle_identification::TYPE = - sdv::vehicle::vehicle_identification::TYPE { - vin: "1M8GDM9AXKP042788".to_string(), - }; + let vehicle_identification: sdv::vehicle::vehicle_identification::SCHEMA_TYPE = + sdv::vehicle::vehicle_identification::SCHEMA_TYPE { vin: "1M8GDM9AXKP042788".to_string() }; let vehicle_value: sdv::vehicle::ENTITY_TYPE = sdv::vehicle::ENTITY_TYPE { instance_id: vehicle_instance_id.clone(), vehicle_identification, diff --git a/samples/seat_massager/consumer/src/main.rs b/samples/seat_massager/consumer/src/main.rs index 385a58c9..e410cde7 100644 --- a/samples/seat_massager/consumer/src/main.rs +++ b/samples/seat_massager/consumer/src/main.rs @@ -63,7 +63,7 @@ fn start_seat_massage_steps( let request_payload: sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE = sdv::airbag_seat_massager::perform_step::request::PAYLOAD_TYPE { - step: vec![sdv::airbag_seat_massager::airbag_adjustment::TYPE { + step: vec![sdv::airbag_seat_massager::airbag_adjustment::SCHEMA_TYPE { airbag_identifier, inflation_level, inflation_duration_in_seconds, diff --git a/samples/seat_massager/provider/src/request_impl.rs b/samples/seat_massager/provider/src/request_impl.rs index 147dccd8..d5102c7f 100644 --- a/samples/seat_massager/provider/src/request_impl.rs +++ b/samples/seat_massager/provider/src/request_impl.rs @@ -88,7 +88,7 @@ impl Request for RequestImpl { // Prepare the perform_step response payload. let response_payload: sdv::airbag_seat_massager::perform_step::response::PAYLOAD_TYPE = sdv::airbag_seat_massager::perform_step::response::PAYLOAD_TYPE { - status: sdv::airbag_seat_massager::status::TYPE { + status: sdv::airbag_seat_massager::status::SCHEMA_TYPE { code: status::ok::CODE, message: status::ok::MESSAGE.to_string(), }, From 8b12825b505a3796173fcdd22f2341a333703c60 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 9 May 2024 15:52:19 -0700 Subject: [PATCH 095/109] Digital Twin Graph --- .../src/digital_twin_graph_impl.rs | 20 +++++++ .../src/digital_twin_registry_impl.rs | 56 +++++++++---------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index 341ecf7e..7c6b0e4f 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -240,6 +240,10 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let find_request = request.into_inner(); let model_id = find_request.model_id; + if model_id.is_empty() { + return Err(tonic::Status::invalid_argument("Model id is required")); + } + debug!("Received a find request for model id {model_id}"); // Retrieve the provider details. @@ -334,6 +338,12 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let instance_id = get_request.instance_id; let member_path = get_request.member_path; + if instance_id.is_empty() { + return Err(tonic::Status::invalid_argument("Model id is required")); + } + + // Note: The member path is optional. + debug!("Received a get request for instance id {instance_id}"); // Retrieve the provider details. @@ -416,6 +426,16 @@ impl DigitalTwinGraph for DigitalTwinGraphImpl { let member_path = invoke_request.member_path; let request_payload = invoke_request.request_payload; + if instance_id.is_empty() { + return Err(tonic::Status::invalid_argument("Instance id is required")); + } + + if member_path.is_empty() { + return Err(tonic::Status::invalid_argument("Member path is required")); + } + + // Note: The request payload is optional. + debug!("Received an invoke request for instance id {instance_id}"); // Retrieve the provider details. diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs index 2fea764f..10401ca0 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -109,15 +109,13 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { let request_inner = request.into_inner(); for entity_access_info in &request_inner.entity_access_info_list { - self.register_entity(entity_access_info.clone()).map_err(|e| { - Status::internal(format!( - "Failed to register the entity with instance id: {}, error: {}", - entity_access_info.instance_id, e - )) - })?; + self.register_entity(&entity_access_info)?; + info!( - "Registered the entity with model id: {} and instance id: {}", - entity_access_info.model_id, entity_access_info.instance_id + "Registered the entity with provider id: {} instance id: {} model id: {}", + entity_access_info.provider_id, + entity_access_info.instance_id, + entity_access_info.model_id ); } @@ -134,33 +132,33 @@ impl DigitalTwinRegistryImpl { /// /// # Arguments /// * `entity` - The entity. - fn register_entity(&self, entity_access_info: EntityAccessInfo) -> Result<(), String> { - // This block controls the lifetime of the lock. - { - if entity_access_info.provider_id.is_empty() { - return Err("Provider id cannot be empty".to_string()); - } + fn register_entity(&self, entity_access_info: &EntityAccessInfo) -> Result<(), Status> { + if entity_access_info.provider_id.is_empty() { + return Err(Status::invalid_argument("Provider id is required")); + } - if entity_access_info.model_id.is_empty() { - return Err("Model id cannot be empty".to_string()); - } + if entity_access_info.model_id.is_empty() { + return Err(Status::invalid_argument("Model id is required")); + } - if entity_access_info.instance_id.is_empty() { - return Err("Instance id cannot be empty".to_string()); - } + if entity_access_info.instance_id.is_empty() { + return Err(Status::invalid_argument("Instance id is required")); + } - if entity_access_info.protocol.is_empty() { - return Err("Protocol cannot be empty".to_string()); - } + if entity_access_info.protocol.is_empty() { + return Err(Status::invalid_argument("Protocol is required")); + } - if entity_access_info.uri.is_empty() { - return Err("Uri cannot be empty".to_string()); - } + if entity_access_info.uri.is_empty() { + return Err(Status::invalid_argument("Uri is required")); + } - if entity_access_info.operations.is_empty() { - return Err("Operations cannot be empty".to_string()); - } + if entity_access_info.operations.is_empty() { + return Err(Status::invalid_argument("Operations is required")); + } + // This block controls the lifetime of the lock. + { // Note: the context is optional. let mut lock: RwLockWriteGuard>> = From 1a17ea6e906d008999b5051b9bde01fca8549ec5 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Thu, 9 May 2024 15:57:09 -0700 Subject: [PATCH 096/109] Digital Twin Graph --- .../digital_twin_registry/src/digital_twin_registry_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs index 10401ca0..b8dd1960 100644 --- a/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs +++ b/core/module/digital_twin_registry/src/digital_twin_registry_impl.rs @@ -109,7 +109,7 @@ impl DigitalTwinRegistry for DigitalTwinRegistryImpl { let request_inner = request.into_inner(); for entity_access_info in &request_inner.entity_access_info_list { - self.register_entity(&entity_access_info)?; + self.register_entity(entity_access_info)?; info!( "Registered the entity with provider id: {} instance id: {} model id: {}", From 0043b28decef6cc6a2d41614bf589c2fefd4922b Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 10 May 2024 10:52:33 -0700 Subject: [PATCH 097/109] Digital Twin Graph --- .../digital_twin_graph/.accepted_words.txt | 1 + .../modules/digital_twin_graph/README.md | 11 ++++++++ .../diagrams/digital_twin_graph_component.svg | 28 ++++--------------- .../diagrams/find_sequence.puml | 8 ++++-- .../diagrams/find_sequence.svg | 18 ++++++------ .../diagrams/get_sequence.puml | 5 ++-- .../diagrams/get_sequence.svg | 15 +++++----- .../diagrams/invoke_sequence.puml | 3 +- .../diagrams/invoke_sequence.svg | 13 +++++---- .../diagrams/set_sequence.puml | 5 ++-- .../diagrams/set_sequence.svg | 15 +++++----- 11 files changed, 63 insertions(+), 59 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index 1b44e566..25fc9579 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -1,6 +1,7 @@ Agemo App com +DTDL freyja Freyja github diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 7eb63792..27f47c87 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -2,6 +2,7 @@ - [Introduction](#introduction) - [Architecture](#architecture) +- [Identifiers](#identifiers) - [Operations](#operations) ## Introduction @@ -33,6 +34,16 @@ The Managed Subscriber Service is an optional service that provides integration ![Component Diagram](diagrams/digital_twin_graph_component.svg) +## Identifiers + +The Digital Twin Graph will use a variety of identifiers. We will discuss the purpose of each. + +The provider ID is the identifier for a Digital Twin Provider. + +The model ID is the identifier for a DTDL fragment. It is expressed as a [DTMI](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v3/DTDL.v3.md#digital-twin-model-identifier). + +The instance ID is the identifier for a digital twin entity. A digital twin may be decomposed into digital twin entities. Each digital twin entity is defined by a fragment of the digital twin's model (specified in DTDL). + ## Operations The Digital Twin Graph Service will support four operations: diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index 80585b63..a4117dcd 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,24 +1,5 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml index 08040588..5698638f 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml @@ -13,18 +13,20 @@ DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByModeld(model_id: "dtmi:sdv:se DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByModelId - response note left - list of EndpointInfo + list of EntityAcessInfo [ { - model_id : "dtmi:sdv:seat;1" + provider_id: "vehicle-core" + model_id: "dtmi:sdv:seat;1" instance_id: "front left seat" protocol: "grpc" operations: ["get", "invoke"] uri: Digital Twin Provider's uri }, { - model_id : "dtmi:sdv:seat;1" + provider_id: "vehicle-core" + model_id: "dtmi:sdv:seat;1" instance_id: "front right seat" protocol: "grpc" operations: ["get", "invoke"] diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index 8b4f2678..9e5ba178 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:seat;1") - request2FindByModeld(model_id: "dtmi:sdv:seat;1") - request3FindByModelId - responselist of EndpointInfo[{model_id : "dtmi:sdv:seat;1"instance_id: "front left seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri},{model_id : "dtmi:sdv:seat;1"instance_id: "front right seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" })5Answer(ask_id: "1", payload: instance value as JSON-LD string)6Find - responselist of instance values as JSON-LD string[{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front left seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front left seat massager"}]},{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front right seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front right seat massager"}]}] \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml index fa275fb5..319d021f 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml @@ -12,14 +12,15 @@ CONSUMER -> DIGITAL_TWIN_GRAPH: Get(instance_id: "the vehicle", member_path: "ve DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "the vehicle") - request DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response note left - list of EndpointInfo + list of EntityAccessInfo [ { + provider_id: "vehicle-core" model_id : "dtmi:sdv:vehicle;1" instance_id: "the vehicle" protocol: "grpc" - operations: ["Get"] + operations: ["get"] uri: Digital Twin Provider's uri } ] diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index e77425e0..4f7c6bb6 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "the vehicle", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"})5Answer(ask_id: "3", payload: instance value as JSON-LD string)6Get - responseinstance value as JSON-LD string{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@type": "dtmi:sdv:vehicle:vehicle_identification;1","vehicle_identification": [{"vin": "1HGCM82633A123456"}]} \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml index 88159fa0..2ece82ea 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml @@ -12,10 +12,11 @@ CONSUMER -> DIGITAL_TWIN_GRAPH: Invoke(instance_id: "front left seat massager", DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(id: "front left seat massager") - request DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response note left - list of EndpointInfo + list of EntityAccessInfo [ { + provider_id: "vehicle-core" model_id : "dtmi:sdv:premium_airbag_seat_massager;1" instance_id: "front left seat massager" protocol: "grpc" diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 37aa956f..223ad054 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "front left seat massager", member_path: "perform_step", request_payload: request payload as JSON-LD string) - request2FindByInstanceId(id: "front left seat massager") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front left seat massager"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string})5Answer(ask_id: "5", payload: response payload as JSON-LD string)6Invoke - responseresponse payload as JSON-LD string \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml index 39353361..6393c443 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml @@ -12,14 +12,15 @@ CONSUMER -> DIGITAL_TWIN_GRAPH: Set(instance_id: "the vehicle", member_path: "ve DIGITAL_TWIN_GRAPH -> DIGITAL_TWIN_REGISTRY: FindByInstanceId(instance_id: "the vehicle") - request DIGITAL_TWIN_GRAPH <- DIGITAL_TWIN_REGISTRY: FindByInstanceId - response note left - list of EndpointInfo + list of EntityAccessInfo [ { + provider_id: "vehicle-core" model_id : "dtmi:sdv:vehicle;1" instance_id: "the vehicle" protocol: "grpc" - operations: ["Get"] + operations: ["get"] uri: Digital Twin Provider's uri } ] diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg index 68dd9b8e..8a46a65e 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -1,6 +1,5 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "the vehicle", member_path: "vehicle_identification/vin", value: value as JSON-LD string) - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EndpointInfo[{model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["Get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string})5Answer(ask_id: "4", payload: "")6Set - response \ No newline at end of file From e0eb9b86982f3341d7becbefcf9e0195d0c22033 Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Fri, 10 May 2024 17:53:43 +0000 Subject: [PATCH 098/109] Generate PlantUML Diagrams --- .../diagrams/digital_twin_graph_component.svg | 28 +++++++++++++++---- .../diagrams/find_sequence.svg | 10 +++---- .../diagrams/get_sequence.svg | 10 +++---- .../diagrams/invoke_sequence.svg | 10 +++---- .../diagrams/set_sequence.svg | 10 +++---- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg index a4117dcd..80585b63 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/digital_twin_graph_component.svg @@ -1,5 +1,24 @@ -Digital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswerDigital Twin App ServerDigital Twin Graph ServiceDigital Twin Registry ServiceInvehicle Digital Twin ServiceManaged Subscribe ServiceDigital Twin ProviderDigital Twin ConsumerDigital Twin Graph InterfaceRespond InterfaceDigital Twin Registry InterfaceInvehicle Digital Twin InterfaceManaged Subscribe InterfaceRequest InterfaceRegisterFind/Get/Set/InvokeFindByModelIdAskAnswer \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index 9e5ba178..03de1d3c 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:seat;1") - request2FindByModeld(model_id: "dtmi:sdv:seat;1") - request3FindByModelId - responselist of EntityAcessInfo[{provider_id: "vehicle-core"model_id: "dtmi:sdv:seat;1"instance_id: "front left seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri},{provider_id: "vehicle-core"model_id: "dtmi:sdv:seat;1"instance_id: "front right seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" })5Answer(ask_id: "1", payload: instance value as JSON-LD string)6Find - responselist of instance values as JSON-LD string[{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front left seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front left seat massager"}]},{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front right seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front right seat massager"}]}] \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index 4f7c6bb6..ef715c0e 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "the vehicle", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EntityAccessInfo[{provider_id: "vehicle-core"model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"})5Answer(ask_id: "3", payload: instance value as JSON-LD string)6Get - responseinstance value as JSON-LD string{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@type": "dtmi:sdv:vehicle:vehicle_identification;1","vehicle_identification": [{"vin": "1HGCM82633A123456"}]} \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index 223ad054..d285565d 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "front left seat massager", member_path: "perform_step", request_payload: request payload as JSON-LD string) - request2FindByInstanceId(id: "front left seat massager") - request3FindByInstanceId - responselist of EntityAccessInfo[{provider_id: "vehicle-core"model_id : "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front left seat massager"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string})5Answer(ask_id: "5", payload: response payload as JSON-LD string)6Invoke - responseresponse payload as JSON-LD string \ No newline at end of file diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg index 8a46a65e..4b281ece 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.svg @@ -1,5 +1,6 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Set(instance_id: "the vehicle", member_path: "vehicle_identification/vin", value: value as JSON-LD string) - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EntityAccessInfo[{provider_id: "vehicle-core"model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string})5Answer(ask_id: "4", payload: "")6Set - response \ No newline at end of file From 9bd59df0f56acca4132df991a6a08b63f77e94c9 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 10 May 2024 12:59:21 -0700 Subject: [PATCH 099/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/.accepted_words.txt | 1 + docs/design/modules/digital_twin_graph/README.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index 25fc9579..df993be9 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -2,6 +2,7 @@ Agemo App com DTDL +DTMI freyja Freyja github diff --git a/docs/design/modules/digital_twin_graph/README.md b/docs/design/modules/digital_twin_graph/README.md index 27f47c87..6ee33c1f 100644 --- a/docs/design/modules/digital_twin_graph/README.md +++ b/docs/design/modules/digital_twin_graph/README.md @@ -38,11 +38,11 @@ The Managed Subscriber Service is an optional service that provides integration The Digital Twin Graph will use a variety of identifiers. We will discuss the purpose of each. -The provider ID is the identifier for a Digital Twin Provider. +The provider ID is the identifier for a Digital Twin Provider. The provider ID must be universally unique and it is up to the provider to ensure this. -The model ID is the identifier for a DTDL fragment. It is expressed as a [DTMI](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v3/DTDL.v3.md#digital-twin-model-identifier). +The model ID is the identifier for a DTDL fragment. It is expressed as a [DTMI](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v3/DTDL.v3.md#digital-twin-model-identifier). -The instance ID is the identifier for a digital twin entity. A digital twin may be decomposed into digital twin entities. Each digital twin entity is defined by a fragment of the digital twin's model (specified in DTDL). +A digital twin may be decomposed into digital twin entities. Each digital twin entity is defined by a fragment of the digital twin's model (specified in DTDL). The instance ID is the identifier for a digital twin entity. The instance ID must be universally unique. ## Operations From 984c0727741c11a500a86ed7fbec40140dc233f1 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 10 May 2024 13:04:04 -0700 Subject: [PATCH 100/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/.accepted_words.txt | 4 +++- .../modules/digital_twin_graph/diagrams/invoke_sequence.puml | 2 +- .../modules/digital_twin_graph/diagrams/set_sequence.puml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index df993be9..58d42313 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -1,6 +1,7 @@ Agemo App com +dtdl DTDL DTMI freyja @@ -11,5 +12,6 @@ Ibeji Ibeji's IDs IoT -Invehicle +Invehiclemd +opendigitaltwins svg diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml index 2ece82ea..4466bd6e 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml @@ -16,7 +16,7 @@ note left [ { - provider_id: "vehicle-core" + provider_id: "vehicle-core" model_id : "dtmi:sdv:premium_airbag_seat_massager;1" instance_id: "front left seat massager" protocol: "grpc" diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml index 6393c443..19c4d426 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml @@ -16,7 +16,7 @@ note left [ { - provider_id: "vehicle-core" + provider_id: "vehicle-core" model_id : "dtmi:sdv:vehicle;1" instance_id: "the vehicle" protocol: "grpc" From 81386992a31d858f9083d72532be3c412bbc3f66 Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Fri, 10 May 2024 20:05:14 +0000 Subject: [PATCH 101/109] Generate PlantUML Diagrams --- .../modules/digital_twin_graph/diagrams/invoke_sequence.svg | 4 ++-- .../modules/digital_twin_graph/diagrams/set_sequence.svg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg index d285565d..f312a8ec 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.svg @@ -1,4 +1,4 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Invoke(instance_id: "front left seat massager", member_path: "perform_step", request_payload: request payload as JSON-LD string) - request2FindByInstanceId(id: "front left seat massager") - request3FindByInstanceId - responselist of EntityAccessInfo[{provider_id: "vehicle-core"model_id : "dtmi:sdv:premium_airbag_seat_massager;1"instance_id: "front left seat massager"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string})5Answer(ask_id: "5", payload: response payload as JSON-LD string)6Invoke - responseresponse payload as JSON-LD stringseat_row = "1"; seat_position = "left"seat_row = "1"; seat_position = "right"seat_row = "2"; seat_position = "left"seat_row = "2"; seat_position = "center"seat_row = "2"; seat_position = "right"seat_row = "1"; seat_position = "left"seat_row = "1"; seat_position = "right"seat_row = "2"; seat_position = "left"seat_row = "2"; seat_position = "center"seat_row = "2"; seat_position = "right" \ No newline at end of file From a0568fe93af6cfb948f86481e31d7d5de0902e3c Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 10 May 2024 13:43:24 -0700 Subject: [PATCH 103/109] Digital Twin Graph --- .../digital_twin_graph/src/digital_twin_graph_impl.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs index ed840c61..02de43cd 100644 --- a/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs +++ b/core/module/digital_twin_graph/src/digital_twin_graph_impl.rs @@ -174,10 +174,9 @@ impl DigitalTwinGraphImpl { }); // Send the ask. - let _ = client - .ask(request) - .await - .map_err(|error| tonic::Status::internal(format!("Unable to call ask, due to {error}")))?; + let _ = client.ask(request).await.map_err(|error| { + tonic::Status::internal(format!("Unable to call ask, due to {error}")) + })?; Ok(()) } From d0ca2ce088d34a46b330130b70b276343505bf58 Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Fri, 10 May 2024 20:44:07 +0000 Subject: [PATCH 104/109] Generate PlantUML Diagrams --- .../diagrams/get_sequence.svg | 4 ++-- .../diagrams/vehicle_graph.svg | 21 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg index ef715c0e..94dd58e9 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.svg @@ -1,4 +1,4 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Get(instance_id: "the vehicle", member_path: "vehicle_identification") - request2FindByInstanceId(instance_id: "the vehicle") - request3FindByInstanceId - responselist of EntityAccessInfo[{provider_id: "vehicle-core"model_id : "dtmi:sdv:vehicle;1"instance_id: "the vehicle"protocol: "grpc"operations: ["get"]uri: Digital Twin Provider's uri}]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"})5Answer(ask_id: "3", payload: instance value as JSON-LD string)6Get - responseinstance value as JSON-LD string{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@type": "dtmi:sdv:vehicle:vehicle_identification;1","vehicle_identification": [{"vin": "1HGCM82633A123456"}]}seat_row = "1"; seat_position = "left"seat_row = "1"; seat_position = "right"seat_row = "2"; seat_position = "left"seat_row = "2"; seat_position = "center"seat_row = "2"; seat_position = "right"seat_row = "1"; seat_position = "left"seat_row = "1"; seat_position = "right"seat_row = "2"; seat_position = "left"seat_row = "2"; seat_position = "center"seat_row = "2"; seat_position = "right" \ No newline at end of file From 9dca7ffc116745e1d497714820ff4905d9693c26 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 10 May 2024 13:46:05 -0700 Subject: [PATCH 105/109] Digital Twin Graph --- docs/design/modules/digital_twin_graph/.accepted_words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/modules/digital_twin_graph/.accepted_words.txt b/docs/design/modules/digital_twin_graph/.accepted_words.txt index 58d42313..6e9c273c 100644 --- a/docs/design/modules/digital_twin_graph/.accepted_words.txt +++ b/docs/design/modules/digital_twin_graph/.accepted_words.txt @@ -14,4 +14,5 @@ IDs IoT Invehiclemd opendigitaltwins +md svg From 7ed2c9663befade4f3e4f954f8a0789eab0db430 Mon Sep 17 00:00:00 2001 From: Ash Beitz <8304894+ashbeitz@users.noreply.github.com> Date: Fri, 10 May 2024 14:07:49 -0700 Subject: [PATCH 106/109] Digital Twin Graph --- .../modules/digital_twin_graph/diagrams/find_sequence.puml | 6 +++--- .../modules/digital_twin_graph/diagrams/get_sequence.puml | 4 ++-- .../digital_twin_graph/diagrams/invoke_sequence.puml | 4 ++-- .../modules/digital_twin_graph/diagrams/set_sequence.puml | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml index 5698638f..f3ca6b7f 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.puml @@ -21,7 +21,7 @@ note left model_id: "dtmi:sdv:seat;1" instance_id: "front left seat" protocol: "grpc" - operations: ["get", "invoke"] + operations: ["Get", "Invoke"] uri: Digital Twin Provider's uri }, { @@ -29,14 +29,14 @@ note left model_id: "dtmi:sdv:seat;1" instance_id: "front right seat" protocol: "grpc" - operations: ["get", "invoke"] + operations: ["Get", "Invoke"] uri: Digital Twin Provider's uri } ] end note loop Iterate over the results from the FindByModelId call - DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" }) + DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "Get" }) DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "1", payload: instance value as JSON-LD string) end diff --git a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml index 44268b31..23dfd2b4 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/get_sequence.puml @@ -20,13 +20,13 @@ note left model_id : "dtmi:sdv:vehicle;1" instance_id: "the vehicle" protocol: "grpc" - operations: ["get"] + operations: ["Get"] uri: Digital Twin Provider's uri } ] end note -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "get", member_path: "vehicle_identification"}) +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "3", payload: {instance_id: "the vehicle", operation: "Get", member_path: "vehicle_identification"}) DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "3", payload: instance value as JSON-LD string) CONSUMER <- DIGITAL_TWIN_GRAPH: Get - response diff --git a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml index 4466bd6e..05033459 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/invoke_sequence.puml @@ -20,14 +20,14 @@ note left model_id : "dtmi:sdv:premium_airbag_seat_massager;1" instance_id: "front left seat massager" protocol: "grpc" - operations: ["get", "invoke"] + operations: ["Get", "Invoke"] uri: Digital Twin Provider's uri } ] end note -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "invoke", member_path: "perform_step", payload: the request payload as JSON-LD string}) +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "5", payload: {instance_id: "front left seat massager", operation: "Invoke", member_path: "perform_step", payload: the request payload as JSON-LD string}) DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "5", payload: response payload as JSON-LD string) CONSUMER <- DIGITAL_TWIN_GRAPH: Invoke - response diff --git a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml index 19c4d426..1629958d 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml +++ b/docs/design/modules/digital_twin_graph/diagrams/set_sequence.puml @@ -20,13 +20,13 @@ note left model_id : "dtmi:sdv:vehicle;1" instance_id: "the vehicle" protocol: "grpc" - operations: ["get"] + operations: ["Set"] uri: Digital Twin Provider's uri } ] end note -DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string}) +DIGITAL_TWIN_GRAPH -> PROVIDER: Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "4", payload: {instance_id: "the vehicle", operation: "Set", member_path: "vehicle_identification/vin", payload: the value as JSON-LD string}) DIGITAL_TWIN_GRAPH <- PROVIDER: Answer(ask_id: "4", payload: "") CONSUMER <- DIGITAL_TWIN_GRAPH: Set - response From b7a4580bbf93005768fc203f25d153dca5a42ffe Mon Sep 17 00:00:00 2001 From: Automated Notice Generation Pipeline Date: Fri, 10 May 2024 21:08:49 +0000 Subject: [PATCH 107/109] Generate PlantUML Diagrams --- .../modules/digital_twin_graph/diagrams/find_sequence.svg | 8 ++++---- .../modules/digital_twin_graph/diagrams/get_sequence.svg | 6 +++--- .../digital_twin_graph/diagrams/invoke_sequence.svg | 6 +++--- .../modules/digital_twin_graph/diagrams/set_sequence.svg | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg index 03de1d3c..62d3b75b 100644 --- a/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg +++ b/docs/design/modules/digital_twin_graph/diagrams/find_sequence.svg @@ -1,4 +1,4 @@ -Digital Twin ConsumerDigital Twin ConsumerDigital Twin GraphDigital Twin GraphDigital Twin RegistryDigital Twin RegistryDigital Twin ProviderDigital Twin Provider1Find(model_id: "dtmi:sdv:seat;1") - request2FindByModeld(model_id: "dtmi:sdv:seat;1") - request3FindByModelId - responselist of EntityAcessInfo[{provider_id: "vehicle-core"model_id: "dtmi:sdv:seat;1"instance_id: "front left seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri},{provider_id: "vehicle-core"model_id: "dtmi:sdv:seat;1"instance_id: "front right seat"protocol: "grpc"operations: ["get", "invoke"]uri: Digital Twin Provider's uri}]loop[Iterate over the results from the FindByModelId call]4Ask(respond_uri: respond uri for Digital Twin Graph, ask_id: "1", payload: {instance_id: "front left seat", operation: "get" })5Answer(ask_id: "1", payload: instance value as JSON-LD string)6Find - responselist of instance values as JSON-LD string[{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front left seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front left seat massager"}]},{"@context": [ "dtmi:dtdl:context;3", "dtmi:sdv:context;1"]"@id": "front right seat","@type": "dtmi:sdv:seat;1","seat_massager": [{"@id": "front right seat massager"}]}]