Skip to content
Snippets Groups Projects
Commit dc3461b5 authored by Erkan Karabulut's avatar Erkan Karabulut
Browse files

Merge branch 'erkan/merge_tsmatch' into 'master'

Erkan/merge tsmatch

See merge request iiot/demonstrator2!19
parents b3f67823 792b3f64
No related branches found
No related tags found
No related merge requests found
Showing
with 15 additions and 28705 deletions
.idea
.eslintignore
.eslintrc.json
.gitignore
.prettierrc.js
build/
node_modules/
package-lock.json
package.json
tsconfig.json
tslint.json
......@@ -38,8 +38,8 @@ build_docker:
# script is the only required keyword that a job needs. It’s a shell script that is executed by the runner.
# A good practice is to have the scripts in files and call the files here
script:
- docker build -t $CI_REGISTRY_IMAGE/tsmatch-api:latest -f ./TSMatch_API/Dockerfile ./TSMatch_API
- docker build -t $CI_REGISTRY_IMAGE/tsmatch-engine:latest -f ./TSMatch_Engine/Dockerfile ./TSMatch_Engine
- docker build -t $CI_REGISTRY_IMAGE/tsmatch-api:latest -f ./efpf_connector/Dockerfile ./efpf_connector
- docker build -t $CI_REGISTRY_IMAGE/tsmatch-engine:latest -f ./tsmatch_engine/Dockerfile ./tsmatch_engine
- docker build -t $CI_REGISTRY_IMAGE/graphdb:latest -f ./graphdb/Dockerfile ./graphdb
- docker build -t $CI_REGISTRY_IMAGE/broker:latest -f ./broker/Dockerfile ./broker
- docker build -t $CI_REGISTRY_IMAGE/iot_thing:latest -f ./thing/Dockerfile ./thing
......
/**
* Copyright (C) 2021 fortiss GmbH
* @author Jorge de Lima Tostes – "tostes@fortiss.org"
* @version 1.0
* Client Class to connect to, send and receive messages from the MQTT broker
*/
const mqtt = require('mqtt');
const connection = require('../config/mqtt_parameters');
//Defining connection parameters that are not secret
const ConnectionParameters = {
username: connection['username'],
password: connection['password'],
reconnect: false,
cleanSession: true,
mqttVersion: 3,
keepAliveInterval: 60,
timeout: 60
};
//Class that handles MQTT Client communication
class MQTTClient {
static uuidList = [];
static client;
//Define topics that will be used by the Client
constructor() {
this.topic_request = 'INNOVINT/HAMBURG_FACTORY1/REQUEST';
this.topic_delete = 'INNOVINT/HAMBURG_FACTORY1/DELETE_REQUEST';
this.topic_response = 'INNOVINT/RESPONSE/NDATA/TSMATCH_INNOVINT_1';
this.topic_observation = 'INNOVINT/OBSERVATION/NDATA/TSMATCH_INNOVINT_1';
this.QoS = 2;
this.retain = false;
this.connect()
}
//Method to connect clients to the MQTT broker and subscribe to necesseary topics
connect() {
let that = this;
this.client = mqtt.connect(connection['domain'], ConnectionParameters);
this.client.on('connect', function() {
that.client.subscribe(that.topic_response)
that.client.subscribe(that.topic_delete)
that.client.subscribe(that.topic_observation)
})
this.client.on('error', function(err) {
console.log('connection failed: ', err);
that.connectionTimer = setTimeout(() => {
that.connect();
}, 5000)
})
console.log('Connected')
}
//Method to send requests to the MQTT broker
async request(body) {
this.body = body;
this.stringBody = JSON.stringify(this.body);
this.client.publish(this.topic_request, this.stringBody);
}
//Method to send delete requests to the MQTT broker
delete(body) {
this.body = body;
this.stringBody = JSON.stringify(this.body);
this.client.publish(this.topic_delete, this.stringBody);
}
}
module.exports = MQTTClient;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/**
* Copyright (C) 2021 fortiss GmbH
* @author Erkan Karabulut – {@link "karabulut@fortiss.org"}
* @version 1.1
* A Neo4j db adapter for Coaty
*/
import {DbAdapterBase} from "@coaty/core/db";
import {DbConnectionInfo} from "@coaty/core";
import * as neo4j from "neo4j-driver";
import {NodeUtils} from "@coaty/core/runtime-node";
import * as jsyaml from "js-yaml";
import * as fs from "fs";
/**
* Neo4j db adapter for Coaty
*/
export class Neo4jAdapter extends DbAdapterBase {
public static _driver;
public connectionInfo: DbConnectionInfo;
constructor(connectionInfo: DbConnectionInfo) {
super(connectionInfo);
this.connectionInfo = connectionInfo;
// connect to the db
Neo4jAdapter._driver = neo4j.driver(
connectionInfo.connectionString,
neo4j.auth.basic(
connectionInfo.connectionOptions.NEO4J_USERNAME,
connectionInfo.connectionOptions.NEO4J_PASSWORD
)
)
this.registerExtension("initDatabase");
this.registerExtension("runQuery");
}
/**
* Check if the given NEO4J_DATABASE exists
* Create it in case it's not exists and create a db user
* @param dbInfo: database connection information
*/
async initDatabase(dbInfo: DbConnectionInfo) {
// check if the given db exists
let database = await this.runQuery(
'show databases where name=$db_name', {
db_name: dbInfo.connectionOptions.NEO4J_DATABASE
}
)
if (database == false) {
let create_db = this.createDatabase(dbInfo.connectionOptions.NEO4J_DATABASE);
if (!create_db) {
NodeUtils.logError("Neo4J database creation is failed! Exiting...");
process.exit(1);
}
}
let user = await this.runQuery(
'show users where user=$username', {
username: dbInfo.connectionOptions.NEO4J_USERNAME
}
);
if (!user) {
let create_user = this.createUser(dbInfo.connectionOptions.NEO4J_USERNAME, dbInfo.connectionOptions.NEO4J_PASSWORD);
if (!create_user) {
NodeUtils.logError("Couldn't create the db user! Exiting...");
process.exit(1);
}
}
await this.createConstraints();
await this.createIndexes();
const taxonomy = jsyaml.load(fs.readFileSync(__dirname + '/../../taxonomy.yaml', 'utf8'));
await this.createTaxonomy(taxonomy);
NodeUtils.logInfo("Database initialization is done!");
}
/**
* Create predefined taxonomy using taxonomy.yaml file in the root folder
*/
async createTaxonomy(taxonomy: {}, parent: string = null) {
for (let element in taxonomy) {
if (parent == null) {
await this.runQuery(`merge (parent: Taxonomy {name: $name})`, {
name: element
});
await this.createTaxonomy(taxonomy[element], element);
} else {
if (typeof taxonomy[element] == "object") {
await this.runQuery(
`merge (parent:Taxonomy {name: $parentName})
merge (child:Taxonomy {name: $childName})
merge (child)-[:ASSOCIATED_WITH]->(parent)`, {
childName: element,
parentName: parent
});
await this.createTaxonomy(taxonomy[element], element);
} else {
await this.runQuery(
`match (parent:Taxonomy {name: $parentName})
where parent.synonyms is not null
and all(item in parent.synonyms where toLower(item) <> toLower($synonym))
set parent.synonyms = parent.synonyms + $synonym`, {
synonym: taxonomy[element],
parentName: parent
});
await this.runQuery(
`match (parent:Taxonomy {name: $parentName})
where parent.synonyms is null
set parent += {synonyms: [$synonym]}`, {
synonym: taxonomy[element],
parentName: parent
});
}
}
}
}
/**
* Create db constraints
*/
async createConstraints() {
// sensor.objectId must be unique
await this.runQuery(`create constraint constraint_sensor_objectId if not exists
on (s:Sensor) assert s.objectId is unique`, {});
// thing.objectId must be unique
await this.runQuery(`create constraint constraint_thing_objectId if not exists
on (t:Thing) assert t.objectId is unique`, {});
}
async createIndexes() {
await this.runQuery(`CREATE INDEX index_sensor_objectId IF NOT EXISTS FOR (s:Sensor) ON (s.objectId)`, {});
await this.runQuery(`CREATE INDEX index_thing_objectId IF NOT EXISTS FOR (t:Thing) ON (t.objectId)`, {});
}
/**
* Create a database with the given name
* @param db_name
*/
async createDatabase(db_name) {
let result = await this.runQuery(
'create database $dbname', {
db_name: db_name
}
)
/* TODO: We don't know what result returns in case of success.
Because only enterprise version of Neo4j allows creation of a new database
In case we use the enterprise version, check if the db created successfully
*/
if (!result) {
}
return true;
}
/**
* Create a user with the given username and password
* @param db_user
* @param db_user_password
*/
async createUser(db_user, db_user_password) {
let result = await this.runQuery(
'CREATE USER $user SET PASSWORD $password', {
user: db_user,
password: db_user_password
}
)
if (!result) {
return false;
}
return true;
}
/**
* Run the given Cypher query with given parameters
* @param query
* @param parameters
*/
async runQuery(query, parameters) {
if (!query) {
console.warn("Invalid must not be empty!");
return false;
}
const session = Neo4jAdapter._driver.session({
database: this.connectionInfo.connectionOptions.NEO4J_DATABASE,
defaultAccessMode: neo4j.session.WRITE
});
let results = null;
try {
results = await session.run(
query,
parameters
);
} catch (error) {
console.error("An error occurred during query execution: ", error);
} finally {
await session.close();
}
if (!results || !results.records) {
console.warn("Result is empty");
return false;
}
return results.records;
}
}
/**
* Copyright (C) 2020 fortiss GmbH
* @author Nisrine Bnouhanna – {@link "bnouhanna@fortiss.org"}
* @version 1.1
* DB configuration
* Uses coatyio Siemens AG. Licensed under the MIT License
*/
import { DatabaseOptions } from "@coaty/core";
import { DbAdapterFactory, DbContext } from "@coaty/core/db";
import { PostgresAdapter } from "@coaty/core/db/adapter-postgres";
export class Db {
public static readonly COLLECTION_SENSOR = "sensor";
public static readonly COLLECTION_SERIVICE = "service";
public static readonly COLLECTION_THING = "thing";
public static readonly COLLECTION_FEATURE = "feature";
public static readonly COLLECTION_GROUPING = "grouping";
public static readonly COLLECTION_REQUEST = "request";
private static readonly DB_HOST = process.env.DB_HOST;
private static readonly DB_PORT = process.env.DB_PORT;
private static readonly DB_NAME = process.env.DB_NAME;
private static readonly DB_USERNAME = process.env.DB_USERNAME;
private static readonly DB_PASSWORD = process.env.DB_PASSWORD;
private static readonly DB_ADMIN_USERNAME = process.env.DB_ADMIN_USERNAME;
private static readonly DB_ADMIN_PASSWORD = process.env.DB_ADMIN_PASSWORD;
public static getConnectionString() {
return `postgres://${this.DB_USERNAME}:${this.DB_PASSWORD}@${this.DB_HOST}:${this.DB_PORT}/${Db.DB_NAME}`;
}
public static getAdminConnectionString() {
return `postgres://${this.DB_ADMIN_USERNAME}:${this.DB_ADMIN_PASSWORD}@${this.DB_HOST}:${this.DB_PORT}/${Db.DB_NAME}`;
}
public static initDatabaseAdapters() {
DbAdapterFactory.registerAdapter("PostgresAdapter", PostgresAdapter);
}
/**
* Set up the Postgres database by creating a database user and a database
* with two collections for logs and tasks.
*
* @param options Database options
* @param clearData if true, clear all collection data from previous runs
*/
public static initDatabase(options: DatabaseOptions, clearData: boolean = false): Promise<any> {
// tslint:disable-next-line: no-string-literal
const dbcAdmin = new DbContext(options["admindb"]);
return dbcAdmin.callExtension("initDatabase",
// tslint:disable-next-line: no-string-literal
options["db"],
// tslint:disable-next-line: max-line-length
[Db.COLLECTION_SENSOR, Db.COLLECTION_FEATURE, Db.COLLECTION_THING, Db.COLLECTION_SERIVICE, Db.COLLECTION_GROUPING, Db.COLLECTION_REQUEST], clearData);
}
}
/**
* Copyright (C) 2021 fortiss GmbH
* @author Erkan Karabulut – {@link "karabulut@fortiss.org"}
* @version 1.1
* DB operations related with IoT things
*/
import {DbContext} from "@coaty/core/db";
/**
* DB operations related with IoT things
*/
export class ThingRepository {
public static getIoTThingByObjectId(dbContext: DbContext, objectId: string) {
return dbContext.callExtension("runQuery",
`match (t:Thing {objectId: $objectId}) return t`, {
objectId: objectId
});
}
/**
* Get sensor by thing id(parent object id)
* @param dbContext
* @param parentObjectId
*/
public static getSensorByParentObjectId(dbContext: DbContext, parentObjectId: string) {
return dbContext.callExtension("runQuery",
`match (s:Sensor {parentObjectId: $parentObjectId}) return s`, {
parentObjectId: parentObjectId
});
}
public static deleteSensorByObjectId(dbContext: DbContext, objectId: string) {
return dbContext.callExtension("runQuery",
`match (s:Sensor {objectId: $objectId}) detach delete s`, {
objectId: objectId
})
}
public static deleteThingByObjectId(dbContext: DbContext, objectId: string) {
return dbContext.callExtension("runQuery",
`match (t:Thing {objectId: $objectId})-[r]->(:Room),
(t2:Thing {objectId: $objectId}) delete r,t,t2`, {
objectId: objectId
});
}
public static getAllSensors(dbContext: DbContext) {
return dbContext.callExtension("runQuery",
`match (s:Sensor) return s`);
}
}
/**
* Copyright (C) 2021 fortiss GmbH
* @author Erkan Karabulut – {@link "karabulut@fortiss.org"}
* @version 1.1
* Includes common json operations
*/
/**
* Common JSON operations
*/
export class JSONUtil {
/**
* Linearize a given json object
* linearize: convert it in a way that it will only include simple key-value pairs
* excluding other objects or arrays as value
* @param jsonObject
*/
public static linearize(jsonObject: JSON) {
let linearizedJson = {};
for (let attribute in jsonObject) {
// check type of the element
if (typeof jsonObject[attribute] == "object") {
// object means either an object {} or an array []
// check if it is an array
if (Array.isArray(jsonObject[attribute])) {
let arrayCounter = 1;
// iterate over the array elements
for (let element in jsonObject[attribute]) {
// array elements can be object as well
if (typeof jsonObject[attribute][element] == "object") {
// call recursively if it's an object
let linearizedInnerJson = this.linearize(jsonObject[attribute][element]);
// concat array elements with the linearized json
for (let element in linearizedInnerJson) {
linearizedJson[attribute + "_" + element] = linearizedInnerJson[element];
}
} else {
// use array index to concat array elements
linearizedJson[attribute + "_" + arrayCounter.toString()] = element;
arrayCounter++;
}
}
} else {
// if not an array, then call recursively to get the elements inside
let linearizedInnerJson = this.linearize(jsonObject[attribute]);
for (let element in linearizedInnerJson) {
linearizedJson[attribute + "_" + element] = linearizedInnerJson[element];
}
}
} else {
// if not an object then directly put it into linearized json
linearizedJson[attribute] = jsonObject[attribute];
}
}
return linearizedJson;
}
}
Taxonomy:
Domain:
Health:
Hospital:
- Hospital
Nursing Home:
- Nursing Home
Private Home:
- Private Home
Logistics:
Warehouse:
- Warehouse
Transport:
- Transport
Agriculture:
Irrigation:
- Irrigation
Livestock:
- Livestock
Crop:
- Crop
Smart City:
Public Building:
- Public Building
Mobility:
- Mobility
Green Space:
- Green Space
Environment:
- Environment
Energy:
- Energy
Building Management:
Hospitality:
- Hospitality
Private Home:
- Private Home
Offices:
- Offices
Retail:
- Retail
Manufacturing:
Facility:
- Facility
Equipment:
- Equipment
Monitoring Aspect:
Objects/Things:
Human:
- Human
Animal:
- Animal
Plant:
- Plant
Goods:
- Goods
Electrical Devices:
- Electrical Devices
Transport:
- Transport
Place:
Infrastructure:
- Infrastructure
Space:
- Space
Energy:
- Energy
Environment:
- Environment
Measurement Type:
Motion:
Movement:
- Movement
Velocity:
- Velocity
Inertia:
- Inertia
Vibration:
- Vibration
Acceleration:
- Acceleration
Occupancy:
- occupancy
- people
- headcount
- person
Rotation:
- Rotation
Destination:
- Destination
Position:
Orientation:
- Orientation
Inclination:
- Inclination
Proximity:
- Proximity
Presence:
- Presence
Location:
- Location
Layout:
- Layout
Distance:
- Distance
Level:
- Level
Energy:
Energy Consumption:
- Energy Consumption
Energy Production:
- Energy Production
Energy Storage:
- Energy Storage
Environment:
Temperature:
- temperature
- heat
- climate
Humidity:
- humidity
- water
- moisture
- wetness
- dampness
- moistness
Luminance:
- Luminance
Acoustic:
- Acoustic
Radiation:
- Radiation
Gas:
- gas
- air
- CO2
- purity
- pollutant
Magnetic Field:
- Magnetic Field
Chemical:
- particulate
- air quality
- pm
- particulate matter
Electrical:
- Electrical
Color:
- Color
Machine Vision:
- Machine Vision
Electromagnetic Field:
- Electromagnetic Field
Mass:
Volume:
- volume
- sound
Pressure:
- pressure
Density:
- Density
Deformation:
- Deformation
Viscosity:
- Viscosity
Flow:
- Flow
Load:
- Load
Shock:
- Shock
Moisture:
- Moisture
Contact:
- Contact
Strain:
- Strain
Corrosion:
- Corrosion
Electrical Conductivity:
- Electrical Conductivity
Oxygen:
- Oxygen
Biosensor:
Brain Electrical Activity:
- Brain Electrical Activity
Eye Movement:
- Eye Movement
Heart Electrical Activity:
- Heart Electrical Activity
Heart Rate:
- Heart Rate
Respiratory Flow:
- Respiratory Flow
Imaging:
- Imaging
Height:
- Height
Weight:
- Weight
Fat:
- Fat
Muscle Mass:
- Muscle Mass
Bone Density:
- Bone Density
Ground Reaction Force:
- Ground Reaction Force
Gait:
- Gait
Joint Torque:
- Joint Torque
Posture:
- Posture
Skeletal Muscles Activity:
- Skeletal Muscles Activity
Body Temperature:
- Body Temperature
Glucose Level:
- Glucose Level
pH:
- pH
Blood Pressure:
- Blood Pressure
SpO2:
- SpO2
Sodium and Conductivity:
- Sodium and Conductivity
Blood Vessel Stiffness:
- Blood Vessel Stiffness
Microbial Structure:
- Microbial Structure
Cell Structure:
- Cell Structure
This diff is collapsed.
/**
* Copyright (C) 2021 fortiss GmbH
* @author Erkan Karabulut – {@link "karabulut@fortiss.org"}
* @version 1.1
* Includes common graphdb operations
*/
import {DbAdapterFactory, DbContext} from "@coaty/core/db";
import {Neo4jAdapter} from "../Neo4jAdapter";
import {DatabaseOptions, ObjectCacheController} from "@coaty/core";
import {ServiceTaskRequest} from "../models";
/**
* Common db operations
*/
export class BaseRepository extends ObjectCacheController<ServiceTaskRequest> {
public dbContext;
/**
* register the neo4j db adapter to coaty
*/
static registerDBAdapter() {
DbAdapterFactory.registerAdapter("Neo4jAdapter", Neo4jAdapter);
}
/**
* Initialize the neo4j db
* @param options
*/
static initializeDatabase(options: DatabaseOptions) {
const dbContext = new DbContext(options["neo4j"]);
return dbContext.callExtension("initDatabase", options["neo4j"]);
}
}
/**
* Copyright (C) 2021 fortiss GmbH
* @author Erkan Karabulut – {@link "karabulut@fortiss.org"}
* @version 1.1
* DB operations related with sensor-service request grouping
*/
import {DbContext} from "@coaty/core/db";
/**
* DB operations related with the sensor and service request groupings
*/
export class GroupingRepository {
/**
* Get service requests that are not yet processed by the tsmatch engine
* @param dbContext
*/
public static getNonProcessedGroupings(dbContext: DbContext) {
return dbContext.callExtension("runQuery",
`match (r:Request {isProcessed: false})<-[rel:USED_BY]-(s:Sensor)<-[:HAD_MEMBER]-(t:Thing)-[:AT_LOCATION]->
(r2:Room)-[:AT_LOCATION]->(f:Floor)-[:AT_LOCATION]->(b:Building) return r,s,rel,r2.name,f.name,b.name,b.address`);
}
/**
* Get all sensor service request groupings
* @param dbContext
*/
public static getAllGroupings(dbContext: DbContext) {
return dbContext.callExtension("runQuery",
`match (r:Request)<-[rel:USED_BY]-(s:Sensor)<-[:HAD_MEMBER]-(t:Thing)-[:AT_LOCATION]->
(r2:Room)-[:AT_LOCATION]->(f:Floor)-[:AT_LOCATION]->(b:Building) return r,s,rel,r2.name,f.name,b.name,b.address`);
}
/**
* Mark a given request as processed by the tsmatch engine
* @param dbContext
* @param requestObjectId
* @param sensorObjectId
*/
public static markRequestAsProcessed(dbContext: DbContext, requestObjectId: string, sensorObjectId: string) {
return dbContext.callExtension("runQuery",
`match (r:Request {objectId: $requestObjectId, isProcessed: true})<-[rel:USED_BY]-(s:Sensor {objectId: $sensorObjectId}) return r,s,rel`, {
requestObjectId: requestObjectId,
sensorObjectId: sensorObjectId
});
}
/**
* Mark a given grouping as sent to the user
* @param dbContext
* @param requestObjectId
*/
public static markGroupingAsResponseSent(dbContext: DbContext, requestObjectId: string, sensorObjectId: string) {
return dbContext.callExtension("runQuery",
`match (r:Request {objectId: $requestObjectId, responseSent: false})<-[rel:USED_BY]-(s:Sensor {objectId: $sensorObjectId})
set r.responseSent=true`, {
requestObjectId: requestObjectId,
sensorObjectId: sensorObjectId
});
}
}
/**
* Copyright (C) 2021 fortiss GmbH
* @author Erkan Karabulut – {@link "karabulut@fortiss.org"}
* @version 1.1
* DB operations related with location of the sensors/iot things
*/
import {DbContext} from "@coaty/core/db";
/**
* Location related db operations
*/
export class LocationRepository {
/**
* get sensors and group by building
* @param dbContext
*/
static getSensorsByBuilding(dbContext: DbContext) {
return dbContext.callExtension("runQuery",
`match (s:Sensor)-[*4]-(b:Building)
with b.address as buildingAddress, s as sensor, count(s) as sensorCount
return buildingAddress, sensor`);
}
/**
* get sensors and group by floor
* @param dbContext
*/
static getSensorsByFloor(dbContext: DbContext) {
return dbContext.callExtension("runQuery",
`match (s:Sensor)-[*3]-(f:Floor)-[:AT_LOCATION]->(b:Building)
with b.address as buildingAddress, f.name as floorName, s as sensor, count(s) as sensorCount
return buildingAddress, floorName, sensor`);
}
/**
* get sensors and group by room
* @param dbContext
*/
static getSensorsByRoom(dbContext: DbContext) {
return dbContext.callExtension("runQuery",
`match (s:Sensor)-[*2]-(r:Room)-[:AT_LOCATION]->(f:Floor)-[:AT_LOCATION]->(b:Building)
with b.address as buildingAddress, f.name as floorName, r.name as roomName, s as sensor, count(s) as sensorCount
return buildingAddress, floorName, roomName, sensor`);
}
/**
* get sensors and group by iot thing
* @param dbContext
*/
static getSensorsByDevice(dbContext: DbContext) {
return dbContext.callExtension("runQuery",
`match (s:Sensor)<-[:HAD_MEMBER]-(t:Thing)-[:AT_LOCATION]->(r:Room)-[:AT_LOCATION]->(f:Floor)-[:AT_LOCATION]->(b:Building)
with b.address as buildingAddress, f.name as floorName, r.name as roomName, s as sensor, t.objectId as thing, count(s) as sensorCount
return buildingAddress, floorName, roomName, thing, sensor `);
}
/**
* Get featureOfInterest(location) of a given iot thing
* @param dbContext
* @param thingId
*/
static getFeatureOfInterest(dbContext: DbContext, thingId: string) {
return dbContext.callExtension("runQuery",
`match (t:Thing {objectId: $thingId})
-[:AT_LOCATION]->(r:Room)-[:AT_LOCATION]->(f:Floor)-[:AT_LOCATION]->(b:Building)
return t.objectId, r.name, f.name, b.name`, {
thingId: thingId
});
}
}
File moved
......@@ -16,4 +16,4 @@ EXPOSE 3003
EXPOSE 3004
# Execute when node docker image is launched
CMD node index.js
\ No newline at end of file
CMD node index.js
......@@ -6,4 +6,4 @@ api_connector = {
path: '/openapi'
}
module.exports = api_connector;
\ No newline at end of file
module.exports = api_connector;
......@@ -6,4 +6,4 @@ observation_server = {
path: '/observations'
}
module.exports = observation_server;
\ No newline at end of file
module.exports = observation_server;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment