Skip to content

Commit

Permalink
config to in-memory dt adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
wilyle committed Oct 2, 2023
1 parent 4dac9b1 commit 4c47e33
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ 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>,
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,7 +81,8 @@ 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 {
let config = Config {
values: vec![EntityConfig {
entity: Entity {
id: String::from(ENTITY_ID),
name: None,
Expand All @@ -103,9 +91,9 @@ mod in_memory_mock_digital_twin_adapter_tests {
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

0 comments on commit 4c47e33

Please sign in to comment.