Skip to content

Commit

Permalink
feat: add Fujitsu bridge binding to remote proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
mkovatsc committed Sep 24, 2018
1 parent dc3d342 commit e5bb785
Show file tree
Hide file tree
Showing 7 changed files with 358 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -56,6 +56,7 @@ packages/*
!packages/binding-websockets
!packages/binding-mqtt
!packages/binding-oracle
!packages/binding-fujitsu
!packages/cli
!packages/demo-servients
!packages/integration-tests
37 changes: 37 additions & 0 deletions packages/binding-fujitsu/package.json
@@ -0,0 +1,37 @@
{
"name": "@node-wot/binding-fujitsu",
"version": "0.5.0-SNAPSHOT.9",
"description": "Fujitsu Remote Proxy bridge for node-wot",
"author": "Eclipse Thingweb <thingweb-dev@eclipse.org> (https://thingweb.io/)",
"license": "EPL-2.0 OR W3C-20150513",
"repository": "https://github.com/thingweb/node-wot/tree/master/packages/binding-http",
"publishConfig": {
"access": "public"
},
"files": [
"dist/"
],
"main": "dist/fujitsu.js",
"types": "dist/fujitsu.d.ts",
"devDependencies": {
"@types/chai": "4.0.4",
"@types/node": "10.9.4",
"@types/ws": "5.1.2",
"@node-wot/td-tools": "0.5.0-SNAPSHOT.9",
"chai": "4.1.2",
"mocha": "3.5.3",
"mocha-typescript": "1.1.8",
"ts-node": "3.3.0",
"typescript": "2.5.2",
"typescript-standard": "0.3.30"
},
"dependencies": {
"@node-wot/core": "0.5.0-SNAPSHOT.9",
"ws": "5.1.1"
},
"scripts": {
"build": "tsc",
"test": "mocha --compilers ts:ts-node/register",
"codestyle": "standard --pretty"
}
}
189 changes: 189 additions & 0 deletions packages/binding-fujitsu/src/fujitsu-server.ts
@@ -0,0 +1,189 @@
/********************************************************************************
* Copyright (c) 2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and
* Document License (2015-05-13) which is available at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document.
*
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
********************************************************************************/

/**
* Fujitsu Bridge that connects to remote proxy by acting like a WoT Server
*/

import * as url from "url";
import * as WebSocket from "ws";

import * as TD from "@node-wot/td-tools";
import { ProtocolServer, ExposedThing, ContentSerdes } from "@node-wot/core";


export default class FujitsuServer implements ProtocolServer {

public readonly scheme: string = "ws";
private readonly remote: string;
private websocket: WebSocket;

private readonly things: Map<string, ExposedThing> = new Map<string, ExposedThing>();

constructor(remoteURI: string) {

let parsed = url.parse(remoteURI);

if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
throw new Error(`FujitsuServer requires WebSocket URI ('ws' or 'wss')`);
}
if (parsed.host === "") {
throw new Error(`FujitsuServer requires WebSocket URI (no host given)`);
}

this.remote = remoteURI;
}

public start(): Promise<void> {
console.info(`FujitsuServer starting for ${this.remote}`);
return new Promise<void>((resolve, reject) => {

this.websocket = new WebSocket(this.remote);

this.websocket.once("error", (err: Error) => { reject(err); });
this.websocket.once("open", () => {
console.log(`FujitsuServer for ${this.remote} connected`);
this.websocket.ping();
// once started, console "handles" errors
this.websocket.on("error", (err: Error) => {
console.error(`FujitsuServer for ${this.remote} failed: ${err.message}`);
});
resolve();
});
this.websocket.on("message", (data) => {
this.handle(data);
});
});
}

public stop(): Promise<void> {
console.info(`WebSocketServer stopping for ${this.remote}`);
return new Promise<void>((resolve, reject) => {

// stop promise handles all errors from now on
this.websocket.once("error", (err: Error) => { reject(err); });
this.websocket.once("close", () => { resolve(); });

for (let id in this.things) {
this.websocket.send(JSON.stringify({
type: "UNREGISTER",
id: id
}));
this.things.delete(id);
}

this.websocket.close();
});
}

public getPort(): number {
// do not show in TD
return -1;
}

public expose(thing: ExposedThing): Promise<void> {

console.log(`FujitsuServer for ${this.remote} exposes '${thing.name}'`);
this.things.set(thing.id, thing);

// create a copy for Fujitsu-specific href handles
let thingCopy = JSON.parse(JSON.stringify(thing));

for (let propertyName in thingCopy.properties) {
thingCopy.properties[propertyName].forms = [new TD.Form(propertyName, ContentSerdes.DEFAULT)];
console.log(`FujitsuServer for ${this.remote} assigns '${propertyName}' to Property '${propertyName}'`);
}
for (let actionName in thingCopy.actions) {
thingCopy.actions[actionName].forms = [new TD.Form(actionName, ContentSerdes.DEFAULT)];
console.log(`FujitsuServer for ${this.remote} assigns '${actionName}' to Action '${actionName}'`);
}
for (let eventName in thingCopy.events) {
thingCopy.events[eventName].forms = [new TD.Form(eventName, ContentSerdes.DEFAULT)];
console.log(`FujitsuServer for ${this.remote} assigns '${eventName}' to Action '${eventName}'`);
}

return new Promise<void>((resolve, reject) => {

this.websocket.send(JSON.stringify({
type: "REGISTER",
id: thing.id,
thingDescription: thingCopy
}), (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}

private handle(data: any) {
console.info("handled " + typeof data);

let message = JSON.parse(data);

if (message.type === "REQUEST") {
console.dir(message);

// select Thing based on "id" field
let thing = this.things.get(message.id);
if (thing) {

switch (message.method) {
case "GET":
thing.properties[message.href].read()
.then((value) => { this.reply(message.requestID, message.id, value); })
.catch((err: Error) => { console.error(`FujitsuServer for ${this.remote} cannot read '${message.href}' of Thing '${message.id}'`); });
break;
case "PUT":
thing.properties[message.href].write(message.entity)
.then(() => { this.reply(message.requestID, message.id); })
.catch((err: Error) => { console.error(`FujitsuServer for ${this.remote} cannot write '${message.href}' of Thing '${message.id}'`); });
break;
case "POST":
thing.actions[message.href].invoke(message.entity)
.then((output) => { this.reply(message.requestID, message.id, output); })
.catch((err: Error) => { console.error(`FujitsuServer for ${this.remote} cannot invoke '${message.href}' of Thing '${message.id}'`); });
break;
default:
console.warn(`FujitsuServer for ${this.remote} received invalid method '${message.method}'`);
}

} else {
console.warn(`FujitsuServer for ${this.remote} received invalid Thing ID '${message.id}'`);
} // thing exists?
} else {
console.warn(`FujitsuServer for ${this.remote} received invalid message type '${message.type}'`);
} // request?

// FIXME: no error message format defined in Fujitsu binding
}

private reply(requestID: string, thingID: string, value?: any) {
let response: any = {
type: "RESPONSE",
requestID: requestID,
id: thingID
};

if (value) {
response.mediaType = ContentSerdes.DEFAULT;
response.buffer = value;
}

this.websocket.send(JSON.stringify(response));
}
}
17 changes: 17 additions & 0 deletions packages/binding-fujitsu/src/fujitsu.ts
@@ -0,0 +1,17 @@
/********************************************************************************
* Copyright (c) 2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and
* Document License (2015-05-13) which is available at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document.
*
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
********************************************************************************/

export { default as FujitsuServer } from "./fujitsu-server";
export * from "./fujitsu-server";
19 changes: 19 additions & 0 deletions packages/binding-fujitsu/tsconfig.json
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"lib" : ["es2015","dom"],
"module": "commonjs",
"outDir": "dist",
"alwaysStrict": true,
"sourceMap": true,
"noImplicitAny": true,
"removeComments": true,
"noLib": false,
"preserveConstEnums": true,
"experimentalDecorators": true,
"declaration": true
},
"include": [
"src/**/*"
]
}
1 change: 1 addition & 0 deletions packages/demo-servients/package.json
Expand Up @@ -25,6 +25,7 @@
"@node-wot/binding-file": "0.5.0-SNAPSHOT.9",
"@node-wot/binding-http": "0.5.0-SNAPSHOT.9",
"@node-wot/binding-oracle": "0.5.0-SNAPSHOT.9",
"@node-wot/binding-fujitsu": "0.5.0-SNAPSHOT.9",
"@node-wot/core": "0.5.0-SNAPSHOT.9",
"@node-wot/td-tools": "0.5.0-SNAPSHOT.9",
"rxjs": "5.4.3"
Expand Down
94 changes: 94 additions & 0 deletions packages/demo-servients/src/fujitsu-local-proxy.ts
@@ -0,0 +1,94 @@
/********************************************************************************
* Copyright (c) 2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and
* Document License (2015-05-13) which is available at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document.
*
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
********************************************************************************/

// node-wot implementation of W3C WoT Servient
import { Servient } from "@node-wot/core";

// exposed protocols
import { FujitsuServer } from "@node-wot/binding-fujitsu";
import { HttpServer } from "@node-wot/binding-http";

// consuming protocols
import { CoapClientFactory } from "@node-wot/binding-coap";
import { FileClientFactory } from "@node-wot/binding-file";

console.debug = () => {};
console.log = () => {};

let servient = new Servient();

servient.addServer(new HttpServer());
servient.addServer(new FujitsuServer("wss://echo.websocket.org/"));
servient.addClientFactory(new CoapClientFactory());
servient.addClientFactory(new FileClientFactory());

// get WoT object for privileged script
servient.start().then(async (WoT) => {

console.info("FujitsuLocalProxy started");

let thing = WoT.produce({
id: "urn:dev:wot:siemens:festolive",
name: "FestoLive",
"iotcs:deviceModel": "urn:com:siemens:wot:festolive"
}
);

console.info(thing.name + " produced");

thing
.addProperty("PumpStatus", { type: "boolean", writable: false }, false)
.addProperty("ValveStatus", { type: "boolean", writable: false }, false)

// upper tank (102)
.addProperty("Tank102LevelValue", { type: "number", writable: false }, 0.0)
.addProperty("Tank102OverflowStatus", { type: "boolean", writable: false }, false)

// lower tank (101)
.addProperty("Tank101MaximumLevelStatus", { type: "boolean", writable: false }, false)
.addProperty("Tank101MinimumLevelStatus", { type: "boolean", writable: false }, false)
.addProperty("Tank101OverflowStatus", { type: "boolean", writable: false }, false)

// actuators
.addAction("StartPump", {}, () => {
return new Promise((resolve, reject) => {
console.warn(">>> Startung pump!");
resolve();
});
})
.addAction("StopPump", {}, () => {
return new Promise((resolve, reject) => {
console.warn(">>> Stopping pump!");
resolve();
});
})
.addAction("OpenValve", {}, () => {
return new Promise((resolve, reject) => {
console.warn(">>> Opening valve!");
resolve();
});
})
.addAction("CloseValve", {}, () => {
return new Promise((resolve, reject) => {
console.warn(">>> Closing valve!");
resolve();
});
});

thing.expose()
.then(() => { console.info(thing.name + " ready"); })
.catch((err) => { console.error("Expose error: " + err); });

}).catch( err => { console.error("Servient start error: " + err); });

0 comments on commit e5bb785

Please sign in to comment.