Skip to content

Commit

Permalink
Improve configuration for digital twin adapters and mock (#56)
Browse files Browse the repository at this point in the history
Improve configuration for digital twin adapters and mock

Closes #17
  • Loading branch information
wilyle authored Oct 2, 2023
1 parent 5d23fee commit 3a4438f
Show file tree
Hide file tree
Showing 24 changed files with 277 additions and 283 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"freyja",
"mapping_clients/in_memory_mock_mapping_client",
"mapping_clients/mock_mapping_service_client",
"mocks/mock_digital_twin",
"mocks/mock_mapping_service",
"proc_macros",
"provider_proxies/grpc/v1",
Expand Down
10 changes: 6 additions & 4 deletions common/src/config_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ where
Err(_) => {
// The path below resolves to $HOME/.freyja/config/{overrides_filename}
home_dir()
.ok_or(io_error_handler(std::io::Error::new(
std::io::ErrorKind::Other,
"Could not retrieve home directory",
)))?
.ok_or_else(|| {
io_error_handler(std::io::Error::new(
std::io::ErrorKind::Other,
"Could not retrieve home directory",
))
})?
.join(DOT_FREYJA_DIR)
.join(CONFIG_DIR)
.join(overrides_filename)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ license = "MIT"

[dependencies]
async-trait = { workspace = true }
freyja-common = { workspace = true }
freyja-contracts = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tokio = { workspace = true }

[build-dependencies]
freyja-build-common = { workspace = true }
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ The In-Memory Mock Digital Twin Adapter mocks the behavior of an in-vehicle digi

## Configuration

The adapter's config is located at `res/config.json` and will be copied to the build output automatically. This file is a list of `EntityConfig` objects with the following properties:

- `entity`: a digital twin entity that will be exposed to the `find_by_id` API. Entities contain the following properties:
- `id`: this is used as the key when calling `find_by_id`.
- `uri`: the uri that is used to invoke a provider. This is a stand-in for whatever the provider contact info is from Ibeji. This is used as the key when calling `subscribe` and `get` in the [In-Memory Provider Proxy](../../provider_proxies/in_memory_mock_provider_proxy/).
- `operation`: the operation that should be used to access this entity. Supported values are `Get` and `Subscribe`.
- `protocol`: the communication protocol that should be used to access this entity. For this particular adapter, the value should always be `in-memory`.
- `name` and `description`: these are currently unused by Freyja. They are included for completeness and parity with Ibeji's Digital Twin Service contract and may potentially be logged.
This adapter supports the following configuration settings:

- `values`: a list of entities to use. Each entry in the list is an object with the following properties:
- `entity`: a digital twin entity that will be exposed to the `find_by_id` API. Entities contain the following properties:
- `id`: this is used as the key when calling `find_by_id`.
- `uri`: the uri that is used to invoke a provider. This is a stand-in for whatever the provider contact info is from Ibeji. This is used as the key when calling `subscribe` and `get` in the [In-Memory Provider Proxy](../../provider_proxies/in_memory_mock_provider_proxy/).
- `operation`: the operation that should be used to access this entity.
- `protocol`: the communication protocol that should be used to access this entity. For this particular adapter, the value should always be `in-memory`.
- `name` and `description`: these are currently unused by Freyja. They are included for completeness and parity with Ibeji's Digital Twin Service contract and may potentially be logged.

This adapter supports [config overrides](../../docs/config-overrides.md). The override filename is `in_memory_digital_twin_config.json`, and the default config is located at `res/in_memory_digital_twin_config.default.json`.
19 changes: 11 additions & 8 deletions digital_twin_adapters/in_memory_mock_digital_twin_adapter/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
// Licensed under the MIT license.
// SPDX-License-Identifier: MIT

use std::{env, fs, path::Path};
use std::env;

fn main() {
// Current directory of the build script is the package's root directory
let config_path = env::current_dir().unwrap().join("res").join("config.json");
use freyja_build_common::copy_to_build_out_dir;

let target_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&target_dir).join("config.json");
const RES_DIR_NAME: &str = "res";
const DEFAULT_CONFIG_FILE: &str = "in_memory_digital_twin_config.default.json";

fs::copy(&config_path, dest_path).unwrap();
fn main() {
// Current directory of the build script is the package's root directory
let config_path = env::current_dir()
.unwrap()
.join(RES_DIR_NAME)
.join(DEFAULT_CONFIG_FILE);

println!("cargo:rerun-if-changed={}", config_path.to_str().unwrap());
copy_to_build_out_dir(config_path, DEFAULT_CONFIG_FILE);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"values": [
{
"entity": {
"id": "dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1",
"name": "AmbientAirTemperature",
"uri": "http://0.0.0.0:1111",
"description": "The immediate surroundings air temperature (in Fahrenheit).",
"operation": "Get",
"protocol": "in-memory"
}
},
{
"entity": {
"id": "dtmi:sdv:Vehicle:Cabin:HVAC:IsAirConditioningActive;1",
"name": "IsAirConditioningActive",
"uri": "http://0.0.0.0:1111",
"description": "Is air conditioning active?",
"operation": "Subscribe",
"protocol": "in-memory"
}
},
{
"entity": {
"id": "dtmi:sdv:Vehicle:OBD:HybridBatteryRemaining;1",
"name": "HybridBatteryRemaining",
"uri": "http://0.0.0.0:1111",
"description": "Percentage of the hybrid battery remaining",
"operation": "Subscribe",
"protocol": "in-memory"
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ use serde::{Deserialize, Serialize};

use freyja_contracts::entity::Entity;

/// The in-memory mock digital twin's config
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
/// The set of config values
pub values: Vec<EntityConfig>,
}

/// Configuration for a entity
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EntityConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,47 @@
// Licensed under the MIT license.
// SPDX-License-Identifier: MIT

use std::{fs, path::Path};

use async_trait::async_trait;

use crate::config::EntityConfig;
use crate::config::Config;
use freyja_common::{config_utils, out_dir};
use freyja_contracts::digital_twin_adapter::{
DigitalTwinAdapter, DigitalTwinAdapterError, DigitalTwinAdapterErrorKind,
GetDigitalTwinProviderRequest, GetDigitalTwinProviderResponse,
};

const CONFIG_FILE: &str = "config.json";
const CONFIG_FILE_STEM: &str = "in_memory_digital_twin_config";

/// In-memory mock that mocks finding endpoint info about entities
/// through find by id
pub struct InMemoryMockDigitalTwinAdapter {
/// Stores configs about entities
data: Vec<EntityConfig>,
/// The adapter config
config: Config,
}

impl InMemoryMockDigitalTwinAdapter {
/// Creates a new InMemoryMockDigitalTwinAdapter with config from the specified file
///
/// # Arguments
/// - `config_path`: the path to the config to use
pub fn from_config_file<P: AsRef<Path>>(
config_path: P,
) -> Result<Self, DigitalTwinAdapterError> {
let config_contents =
fs::read_to_string(config_path).map_err(DigitalTwinAdapterError::io)?;

let config: Vec<EntityConfig> = serde_json::from_str(config_contents.as_str())
.map_err(DigitalTwinAdapterError::deserialize)?;

Self::from_config(config)
}

/// Creates a new InMemoryMockDigitalTwinAdapter with the specified config
///
/// # Arguments
/// - `config`: the config to use
pub fn from_config(config: Vec<EntityConfig>) -> Result<Self, DigitalTwinAdapterError> {
Ok(Self { data: config })
pub fn from_config(config: Config) -> Result<Self, DigitalTwinAdapterError> {
Ok(Self { config })
}
}

#[async_trait]
impl DigitalTwinAdapter for InMemoryMockDigitalTwinAdapter {
/// Creates a new instance of a DigitalTwinAdapter with default settings
fn create_new() -> Result<Self, DigitalTwinAdapterError> {
Self::from_config_file(Path::new(env!("OUT_DIR")).join(CONFIG_FILE))
let config = config_utils::read_from_files(
CONFIG_FILE_STEM,
config_utils::JSON_EXT,
out_dir!(),
DigitalTwinAdapterError::io,
DigitalTwinAdapterError::deserialize,
)?;

Self::from_config(config)
}

/// Gets the entity information based on the request
Expand All @@ -62,7 +53,8 @@ impl DigitalTwinAdapter for InMemoryMockDigitalTwinAdapter {
&self,
request: GetDigitalTwinProviderRequest,
) -> Result<GetDigitalTwinProviderResponse, DigitalTwinAdapterError> {
self.data
self.config
.values
.iter()
.find(|entity_config| entity_config.entity.id == request.entity_id)
.map(|entity_config| GetDigitalTwinProviderResponse {
Expand All @@ -76,16 +68,11 @@ impl DigitalTwinAdapter for InMemoryMockDigitalTwinAdapter {
mod in_memory_mock_digital_twin_adapter_tests {
use super::*;

use crate::config::EntityConfig;
use freyja_contracts::{entity::Entity, provider_proxy::OperationKind};

#[test]
fn from_config_file_returns_err_on_nonexistent_file() {
let result = InMemoryMockDigitalTwinAdapter::from_config_file("fake_file.foo");
assert!(result.is_err());
}

#[test]
fn can_get_default_config() {
fn can_create_new() {
let result = InMemoryMockDigitalTwinAdapter::create_new();
assert!(result.is_ok());
}
Expand All @@ -94,18 +81,20 @@ mod in_memory_mock_digital_twin_adapter_tests {
async fn find_by_id_test() {
const ENTITY_ID: &str = "dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1";

let data = vec![EntityConfig {
entity: Entity {
id: String::from(ENTITY_ID),
name: None,
uri: String::from("http://0.0.0.0:1111"), // Devskim: ignore DS137138
description: None,
operation: OperationKind::Subscribe,
protocol: String::from("in-memory"),
},
}];
let config = Config {
values: vec![EntityConfig {
entity: Entity {
id: String::from(ENTITY_ID),
name: None,
uri: String::from("http://0.0.0.0:1111"), // Devskim: ignore DS137138
description: None,
operation: OperationKind::Subscribe,
protocol: String::from("in-memory"),
},
}],
};

let in_memory_digital_twin_adapter = InMemoryMockDigitalTwinAdapter { data };
let in_memory_digital_twin_adapter = InMemoryMockDigitalTwinAdapter { config };
let request = GetDigitalTwinProviderRequest {
entity_id: String::from(ENTITY_ID),
};
Expand Down
5 changes: 4 additions & 1 deletion digital_twin_adapters/mock_digital_twin_adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ license = "MIT"

[dependencies]
async-trait = { workspace = true }
freyja-common = { workspace = true }
freyja-contracts = { workspace = true }
mock-digital-twin = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }

[build-dependencies]
freyja-build-common = { workspace = true }
6 changes: 4 additions & 2 deletions digital_twin_adapters/mock_digital_twin_adapter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ The Mock Digital Twin Adapter acts as a client for the [Mock Digital Twin](../..

## Config

The adapter's config is located at `res/config.json` and will be copied to the build output automatically. This file contains the following properties:
This adapter supports the following configuration settings:

- `base_uri_for_digital_twin_server`: the base uri for the Mock Digital Twin Service
- `digital_twin_service_uri`: the base uri for the Mock Digital Twin Service

This adapter supports [config overrides](../../docs/config-overrides.md). The override filename is `mock_digital_twin_adapter_config.json`, and the default config is located at `res/mock_digital_twin_adapter_config.default.json`.
20 changes: 8 additions & 12 deletions digital_twin_adapters/mock_digital_twin_adapter/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,19 @@
// Licensed under the MIT license.
// SPDX-License-Identifier: MIT

use std::{env, fs, path::Path};
use std::env;

const OUT_DIR: &str = "OUT_DIR";
const RESOURCE_DIR: &str = "res";
const CONFIG_FILE: &str = "config.json";
use freyja_build_common::copy_to_build_out_dir;

const RES_DIR_NAME: &str = "res";
const DEFAULT_CONFIG_FILE: &str = "mock_digital_twin_adapter_config.default.json";

fn main() {
// Current directory of the build script is the package's root directory
let config_path = env::current_dir()
.unwrap()
.join(RESOURCE_DIR)
.join(CONFIG_FILE);

let target_dir = env::var(OUT_DIR).unwrap();
let dest_path = Path::new(&target_dir).join(CONFIG_FILE);

fs::copy(&config_path, dest_path).unwrap();
.join(RES_DIR_NAME)
.join(DEFAULT_CONFIG_FILE);

println!("cargo:rerun-if-changed={}", config_path.to_str().unwrap());
copy_to_build_out_dir(config_path, DEFAULT_CONFIG_FILE);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"digital_twin_service_uri": "http://127.0.0.1:8800"
}
10 changes: 4 additions & 6 deletions digital_twin_adapters/mock_digital_twin_adapter/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

use serde::{Deserialize, Serialize};

pub const CONFIG_FILE: &str = "config.json";

/// Settings for http provider proxy
/// Config for the mock digital twin adapter
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Settings {
/// the base uri for the digital twin server
pub base_uri_for_digital_twin_server: String,
pub struct Config {
/// the base uri for the digital twin service
pub digital_twin_service_uri: String,
}
Loading

0 comments on commit 3a4438f

Please sign in to comment.