Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) Enabling of Knowledge Layer #50

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
db8e01a
Add basis directory structure with readme files
chrizmc May 23, 2024
64e06ec
add basic setup of informationlayer containing basic dbrouter and rea…
chrizmc May 29, 2024
5ee12fd
add files
claireqiu May 31, 2024
75c0eb3
Fixed readme format of config.js
sschleemilch Jun 6, 2024
a77d8a3
Renamed knowledge-connector -> connector
sschleemilch Jun 6, 2024
327db86
Removed dead README link
sschleemilch Jun 6, 2024
69da82d
Fixed README formatting and fixed typos
sschleemilch Jun 6, 2024
9bb7dc4
Updated docs, added config.js
sschleemilch Jun 6, 2024
e0d7aa6
Added handlers layer
sschleemilch Jun 6, 2024
9722e2b
Hint for hard coded realmdb and js formatting
sschleemilch Jun 6, 2024
dbbed43
Fixed require path for new structure
sschleemilch Jun 6, 2024
f98ef6f
Added missing dot in readme
chrizmc Jun 18, 2024
0da163a
Fixed wrong readme link
chrizmc Jun 18, 2024
0c83de4
add basic router parts
chrizmc Jul 8, 2024
3e36432
Add environment variable functionallity and update README files.
chrizmc Jul 9, 2024
72ab98b
Update link URL and create a IoTDB connection using the IoTDB Handler
chrizmc Jul 24, 2024
c075e51
Update the authentification configuration for the IoTDB handler
chrizmc Jul 31, 2024
684f3f9
Add validator for message format and IoTDB message processing
chrizmc Aug 7, 2024
62e8402
Extend supported data points in RealmDB Handler
sschleemilch Aug 9, 2024
cd597d1
Support for UseCase endpoits in IoTDB Handler
sschleemilch Aug 9, 2024
5f142ab
Add write support to realmdb
sschleemilch Aug 13, 2024
56a1109
Extracted common methods
sschleemilch Aug 21, 2024
6d28e89
Support dots separation for endpoint naming in realm db
aw-muc Aug 22, 2024
deff29c
• Import supported endpoints for IoTDB and RealmDB from YAML/JSON fil…
aw-muc Aug 27, 2024
c8ef06d
Dockerize informations-layer
aw-muc Sep 6, 2024
7f1ae37
Fixed doc typos
sschleemilch Sep 6, 2024
838af3f
Remove unnecessary config files and add it as gitignore.
aw-muc Sep 13, 2024
dfb873f
Data transmission via websocket client in knowledge layer:
aw-muc Sep 16, 2024
28ec28b
Documentation for new data source within information layer
aw-muc Sep 16, 2024
b84a37e
Handle and send error message within information layer for websocket …
aw-muc Sep 16, 2024
8614c90
Vehicle data point knowledge layer configuration
sschleemilch Sep 26, 2024
e68d6bb
CMake windows support
sschleemilch Sep 27, 2024
bbe803f
Add --help CLI feature to display configured and default parameters
chrizmc Oct 9, 2024
9559d7f
Use configuration file to organize the use case model directory
chrizmc Oct 9, 2024
ffce704
Configure RDFox environment
chrizmc Oct 9, 2024
24b871e
Develop RDFox adapter for rest api integration
Oct 17, 2024
1bcadd7
Refactored information-layer Docs (#36)
sschleemilch Oct 21, 2024
7ae2fb4
- Migrate JS to TypeScript
aw-muc Oct 21, 2024
c936c4b
add hello world kl usecase readme
chrizmc Oct 21, 2024
cd64ada
IMplementation of Thrift to access IoTDB without TypeScript; JavaScri…
aw-muc Oct 23, 2024
7465d61
Switched README graph to mermaid
sschleemilch Oct 30, 2024
c078a05
Subscription functionality for IoTDB data layer
aw-muc Nov 4, 2024
21236aa
Locate another transformation function in transformations.ts, too
chrizmc Nov 13, 2024
9693e64
Add instruction how to build and run tests
chrizmc Nov 13, 2024
d9da312
change _ to . notation in readme
chrizmc Nov 14, 2024
c76f57a
Transform Json data into RDF triples using SHACL shapes and SPARQ…
Nov 26, 2024
21e519b
Add subscription status message after subscribe/unsubscribe when usin…
chrizmc Dec 2, 2024
43a1758
Add session management to Information Layer
chrizmc Dec 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions cdsp/information-layer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
This directory contains files related to the [Information Layer](https://en.wikipedia.org/wiki/DIKW_pyramid) of the Central Data Service Playground.

# Information Layer Playground components
- [Database-Handlers](./handlers)
- [Database-Router](./router)

# Setting up a CDSP Information Layer
Setting up an information layer in the CDSP involves running a [Database-Router](./router), which is technically a configurable Websocket server that enables northbound connections for WebSocket clients, such as a [Knowledge Layer Connector](../knowledge-layer/README.md), to read, subscribe, and write data. The WebSocket server is connected to a database, which can be selected before starting the web server. Southbound feeders write data to the database in a predefined semantic format, such as [VSS](https://github.com/COVESA/vehicle_signal_specification).

## Installation of Database-Handler

Please follow installation instructions of the chosen [handler](./handlers/).

## [Installation of Database-Router](./router/README.md#Install)

# Running "Hello World" example

The Hello World example in our case is quite simple. We feed an updated value for the HVAC ambient air temperature into the database and we check afterwards in the logs if the DB-router creates a Websocket update message for it.

## Choose and prepare your Database

### Realm
- Ensure that in your [ATLAS cloud](https://cloud.mongodb.com/) app there is a vehicle *document* with an `_id: 1234567` in a collection named *Vehicles*.
- Ensure that this document as well contains VSS data. At the moment only 1 data point is supported, namely `Vehicle.Cabin.HVAC.AmbientAirTemperature`. Here you can see how the vehicle document within the *Vehicles* should look like in ATLAS:

```
_id: 1234567 (Int64)
Vehicle_Cabin_HVAC_AmbientAirTemperature: 6 (Double)
```

### IoTDB
- Not yet supported

## [Start](./router/README.md#Run) the Database Router

## Look out for the Websocket Server message in the console
Now you can changed the value of `Vehicle_Cabin_HVAC_AmbientAirTemperature` in ATLAS cloud to let's say `23`. After changing you should immediately see this line in console:

```
the value of "Vehicle_Cabin_HVAC_AmbientAirTemperature" changed to 23
```

## Connect your own Websocket Client
Connect your own websocket client by connecting to `ws://localhost:8080`
4 changes: 4 additions & 0 deletions cdsp/information-layer/handlers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Handlers

- [IotDB](./iotdb)
- [RealmDB](./realmdb)
35 changes: 35 additions & 0 deletions cdsp/information-layer/handlers/handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class Handler {
authenticateAndConnect(sendMessageToClients) {
throw new Error("Method 'authenticateAndConnect' must be implemented.");
}

handleMessage(message, ws) {
switch (message.type) {
case 'read':
this.read(message, ws);
break;
case 'write':
this.write(message, ws);
break;
case 'subscribe':
this.subscribe(message, ws);
break;
default:
ws.send(JSON.stringify({ error: 'Unknown message type'}))
}
}

read(message, ws) {
throw new Error('Read method not implemented, yet!')
}

write(message, ws) {
throw new Error('Write method not implemented, yet!')
}

subscribe(message, ws) {
throw new Error('Subscribe method not implemented, yet!')
}
}

module.exports = Handler;
1 change: 1 addition & 0 deletions cdsp/information-layer/handlers/iotdb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tbd
1 change: 1 addition & 0 deletions cdsp/information-layer/handlers/iotdb/src/iotdb-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO: STUB implementation of subscribe, write and read interface methods.g
12 changes: 12 additions & 0 deletions cdsp/information-layer/handlers/realmdb/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# MongoDB Realm
mongodb-realm/

# Node.js
node_modules/
package-lock.json

# Credentials Config File
config.js

# MAC
**/.DS_Store
41 changes: 41 additions & 0 deletions cdsp/information-layer/handlers/realmdb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
This directory contains the RealmDB Handler as Node.js application. As [RealmDB](https://www.mongodb.com/docs/atlas/device-sdks/sdk/node/) is an embedded database, the RealmDB Handler directly embedds the RealmSDK which creates the [RealmDB database](https://github.com/realm/realm-js) file(s) automatically in the working directory during runtime of RealmDB Handler.

# Installation

Execute within `realmdb` directory

```bash
npm install
```

# Configure RealmDB

Before the Database-Router can start the RealmDB Handler without any errors you need to configure the RealmSDK before.

## Create a ATLAS Cloud instance

To get APIKey and AppID you need to setup a [ATLAS cloud](https://cloud.mongodb.com/) instance and App Services. There is a free Tier solution (Status as of May 29, 2024) and you will find a lot of documentation in the internet how to set up everything.

## Configure of a RealmDB Handler

Create `config/config.js` with the following format, replacing the app id and the api key with yours.

```js
module.exports = {
realmAppId: "your-realm-AppId",
realmApiKey: "your-realm-ApiKey",
};
```

> **_IMPORTANT:_** Do not commit this file to github!

## Configure an example vehicle

Change the VIN (Vehicle Identification Number) of the example vehicle in [vehicle-config](./config/vehicle-config.js).
The default VIN is `1234567`.
If you do not want to change it ensure, that in your ATLAS cloud instance there is a vehicle _document_ with an `_id: 1234567` in a collection named `Vehicles`.
More infos how to run an example together with ATLAS cloud you can find [here](../readme.md#case-1-you-choosed-realm-as-database).

## Starting the RealmDB handler

You do not need to start RealmDB Handler manually. It is started by the DB-Router like described [here](../../router/README.md#Install).
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
Vin: 1234567,
};

10 changes: 10 additions & 0 deletions cdsp/information-layer/handlers/realmdb/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "realmdb_handler",
"version": "1.0.0",
"description": "Realm database handler",
"main": "realmdb_handler.js",
"dependencies": {
"realm": "^12.9.0",
"uuid": "^9.0.1"
}
}
132 changes: 132 additions & 0 deletions cdsp/information-layer/handlers/realmdb/src/realmdb_handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
const Realm = require("realm");
const Handler = require('../../handler')
const config = require("../config/config");
//const vehicleConfig = require("../config/vehicle-config"); // Import vehicle config file
const { v4: uuidv4 } = require("uuid"); // Importing UUID generator

const MediaElementSchema = {
primaryKey: "_id",
name: "Vehicles",
properties: {
_id: "int",
Vehicle_Cabin_HVAC_AmbientAirTemperature: "double",
},
};

const app = new Realm.App({ id: config.realmAppId });
const credentials = Realm.Credentials.apiKey(config.realmApiKey);

const realmConfig = {
schema: [MediaElementSchema],
path: "myrealm12.realm",
sync: {
user: null, // will be assigned after authentication
flexible: true,
error: (error) => {
console.error("Realm sync error:", error);
},
},
};

class RealmDBHandler extends Handler {
constructor() {
super();
this.realm = null;
this.sendMessageToClients = null;
}

async authenticateAndConnect(sendMessageToClients) {
try {
this.sendMessageToClients = sendMessageToClients;
const user = await app.logIn(credentials);
console.log("Successfully authenticated with Realm");

realmConfig.sync.user = user;
this.realm = await Realm.open(realmConfig);
console.log("Realm connection established successfully");

const MediaElements = this.realm.objects("Vehicles").subscribe();
console.log(MediaElements); // TODO: Is it necessary to log this?
} catch (error) {
console.error("Failed to authenticate with Realm:", error);
}
}

async read(message, ws) {
try{
const objectId = message.data.Vin; // Retrieve objectId from config file
const mediaElement = this.realm.objectForPrimaryKey("Vehicles", objectId);
console.log(mediaElement);
if (mediaElement) {
const response = {
type: "read_response",
data: mediaElement,
};
ws.send(JSON.stringify(response));
} else {
ws.send(JSON.stringify({ error: 'Object not found' }));
}
} catch (error) {
console.error("Error reading object from Realm:", error);
ws.send(JSON.stringify({ error: 'Error reading object' }));
}
}

async write(message, ws) {
// Implement write logic for RealmDB
}

async subscribe(message, ws) {
const sendMessageToClient = (message) => {
ws.send(JSON.stringify(message));
};

try {
const objectId = message.data.Vin;
console.log(`Subscribing element: ${objectId}`)
const mediaElement = await this.realm.objectForPrimaryKey("Vehicles", objectId);
console.log(mediaElement);

if (mediaElement) {
const websocketId = String(objectId);
mediaElement.addListener((mediaElement, changes) =>
this.onMediaElementChange(mediaElement, changes, websocketId)
);
sendMessageToClient({ success: `Subscribed to changes for object ID ${objectId}` })
} else {
sendMessageToClient({ error: "Vehicles collection not found for subscription"});
}
} catch (error) {
console.error("Error subscribing to object changes in Realm:", error);
sendMessageToClient({ error: 'Error subscribing to object changes' });
}
}

onMediaElementChange(mediaElement, changes, websocketId) {
if (changes.deleted) {
console.log(`MediaElement is deleted: ${changes.deleted}`);
} else {
changes.changedProperties.forEach((prop) => {
console.log(`* the value of "${prop}" changed to ${mediaElement[prop]}`);

// Generate a meaningful UUID for WebSocket response
const uuid = uuidv4();

const message = {
type: "update",
tree: "VSS",
id: websocketId, // Use the WebSocket server ID
uuid: uuid, // Use generated UUID
dateTime: new Date().toISOString(),
node: {
name: prop, // Sending the property name as node name
value: mediaElement[prop], // Sending the property value as node value
},
};
this.sendMessageToClients(message);
});
}
}
}

module.exports = RealmDBHandler;
10 changes: 10 additions & 0 deletions cdsp/information-layer/router/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# MongoDB Realm
mongodb-realm/

# Node.js
node_modules/
package-lock.json
.env

# MAC
**/.DS_Store
20 changes: 20 additions & 0 deletions cdsp/information-layer/router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
With this component one can configure which database it shall connect to.

> [!WARNING]
> As the configuration capability is not yet implemented, RealmDB is hard coded as a database!

# Install

Execute in this directory:

```bash
npm install
```

# Run

Start router by executing in [src](./src/) directory the command:

```bash
node websocket-server.js
```
14 changes: 14 additions & 0 deletions cdsp/information-layer/router/config/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const dotenv = require('dotenv');
dotenv.config();

const getHandlerType = () => {
const handlerType = process.env.HANDLER_TYPE;
if (!handlerType) {
throw new Error('Handler type must be specified as an ENV variable.');
}
return handlerType.toLowerCase();
};

module.exports = {
getHandlerType,
};
12 changes: 12 additions & 0 deletions cdsp/information-layer/router/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "websocket-server",
"version": "1.0.0",
"description": "WebSocket server",
"main": "websocket-server.js",
"scripts": {
"start": "node websocket-server.js"
},
"dependencies": {
"ws": "^7.4.6"
}
}
Loading
Loading