Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 12 additions & 1 deletion packages/examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,18 @@
"build": "tsc -b",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write \"src/**/*.ts\" \"**/*.json\""
"format": "prettier --write \"src/**/*.ts\" \"**/*.json\"",
"bindings:coap:server": "node dist/bindings/coap/example-server.js",
"bindings:coap:client": "node dist/bindings/coap/example-client.js",
"bindings:http:server": "node dist/bindings/http/example-server.js",
"bindings:http:server-secure": "node dist/bindings/http/example-server-secure.js",
"bindings:http:client": "node dist/bindings/http/example-client.js",
"bindings:opcua:1": "node dist/bindings/opcua/opcua-demo1.js",
"bindings:opcua:2": "node dist/bindings/opcua/opcua-demo2.js",
"bindings:opcua:coffee-machine": "node dist/bindings/opcua/opcua-coffee-machine-demo.js",
"quickstart:smart-clock": "node dist/quickstart/smart-clock.js",
"quickstart:simple-coffee-machine": "node dist/quickstart/simple-coffee-machine.js",
"quickstart:presence-sensor": "node dist/quickstart/presence-sensor.js"
},
"bugs": {
"url": "https://github.com/eclipse-thingweb/node-wot/issues"
Expand Down
15 changes: 15 additions & 0 deletions packages/examples/src/bindings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Binding Examples

This folder contains examples for different binding protocols.

It demonstrates how to create Things that take their properties, actions, and events from different protocol bindings.

For each use case a Thing Description is provided that describes the Thing in a protocol-agnostic way.
Then a Servient is created that uses the respective binding protocol to expose the Thing.
A console client is also provided to interact with the Thing.

Examples are located in

- `bindings\coap`
- `bindings\http`
- [`bindings\opcua`](./opcua/README.md)
26 changes: 26 additions & 0 deletions packages/examples/src/bindings/opcua/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## OPCUA

For inializing an OPCUA client Servient, we need to import the `OPCUAClientFactory` from the `@node-wot/binding-opcua` package.

```typescript
const servient = new Servient();
servient.addClientFactory(new OPCUAClientFactory());
const wot = await servient.start();
const thing = await wot.consume(thingDescription);
```

Then we can interact with the Thing as usual:

```typescript
// now interact with the things
await thing.invokeAction(...);
await thing.readProperty(...);
await thing.subscribeEvent(...);

```

Finally, we can shutdown the servient:

```typescript
await servient.shutdown();
```
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const thingDescription: WoT.ThingDescription = {
security: "nosec_sc",
title: "servient",
description: "node-wot CLI Servient",
base: endpointUrl,
properties: {
pumpSpeed: {
description: "the pump speed",
Expand All @@ -34,7 +35,7 @@ export const thingDescription: WoT.ThingDescription = {
type: "number",
forms: [
{
href: endpointUrl + "?id=ns=1;s=PumpSpeed",
href: "?id=ns=1;s=PumpSpeed",
op: ["readproperty", "observeproperty"],
},
],
Expand All @@ -47,7 +48,7 @@ export const thingDescription: WoT.ThingDescription = {
type: "number",
forms: [
{
href: endpointUrl + "?id=ns=1;s=Temperature",
href: "?id=ns=1;s=Temperature",
op: ["readproperty", "observeproperty"],
},
],
Expand Down
183 changes: 169 additions & 14 deletions packages/examples/src/bindings/opcua/opcua-coffee-machine-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
********************************************************************************/

/* eslint no-console: "off" */

import util from "util";
import { Servient } from "@node-wot/core";
import { OPCUAClientFactory } from "@node-wot/binding-opcua";
import { thingDescription } from "./opcua-coffee-machine-thing-description";
Expand All @@ -25,37 +25,192 @@ const pause = async (ms: number) => new Promise((resolve) => setTimeout(resolve,
servient.addClientFactory(new OPCUAClientFactory());

const wot = await servient.start();

const thing = await wot.consume(thingDescription);

let lastTemperature = NaN;
let lastWaterTankLevel = NaN;
let lastCoffeeBeanLevel = NaN;
let lastCurrentState = NaN;
let lastGrindingDuration = NaN;
let lastGrinderStatus = NaN;
let lastHeaterStatus = NaN;
let lastPumpStatus = NaN;
let lastValveStatus = NaN;

const recordedActions: string[] = [];
const recordAction = (actionName: string) => {
recordedActions.push(`${new Date().toISOString()} - ${actionName}`);
};
process.stdout.write("\x1Bc"); // clear console
process.stdout.write("\x1B[?25l"); // hide cursor
const currentStateEnum = ["Off", "Standby", "Error", "Cleaning", "Serving Coffee", "Under Maintenance"];
const grinderStates = ["Off", "On", "Jammed", "Malfunctioning"];
const heaterStates = ["Off", "Heating", "Ready", "Malfunctioning"];
const pumpStates = ["Off", "On", "Malfunctioning"];
const valveStates = ["Open", "Opening", "Close", "Closing", "Malfunctioning"];

const waitingMachineCoffeeStandByState = async () => {
await pause(1000);
let state = lastCurrentState;
while (state !== 1) {
// Standby
await pause(1000);
state = lastCurrentState;
}
};
const writeLine = (...args: unknown[]) => {
process.stdout.write(util.format(...args) + " \n");
};
const displayOnlineStatus = () => {
process.stdout.write("\x1B[1;1H"); // move cursor to top left
writeLine(`======== Coffee Machine Status ======== ${new Date().toISOString()}`);
writeLine(
` 🔄 Current State : ${
isNaN(lastCurrentState) ? "n/a" : (currentStateEnum[lastCurrentState] ?? lastCurrentState)
}`
);
writeLine(
` 🔥 Heater Status : ${
isNaN(lastHeaterStatus) ? "n/a" : (heaterStates[lastHeaterStatus] ?? lastHeaterStatus)
}`
);
writeLine(
` 🌡️ Boiler Temperature : ${isNaN(lastTemperature) ? "n/a" : lastTemperature.toFixed(2) + " °C"}`
);
writeLine(
` 🚰 Pump Status : ${
isNaN(lastPumpStatus) ? "n/a" : (pumpStates[lastPumpStatus] ?? lastPumpStatus)
}`
);
writeLine(
` 🚪 Valve Status : ${
isNaN(lastValveStatus) ? "n/a" : (valveStates[lastValveStatus] ?? lastValveStatus)
}`
);
writeLine(
` 💧 Water Tank Level : ${isNaN(lastWaterTankLevel) ? "n/a" : lastWaterTankLevel.toFixed(2) + " ml"}`
);
writeLine(
` ⚙️ Grinder Status : ${
isNaN(lastGrinderStatus) ? "n/a" : (grinderStates[lastGrinderStatus] ?? lastGrinderStatus)
}`
);
writeLine(
` ⏱️ Grinding Duration : ${
isNaN(lastGrindingDuration) ? "n/a" : lastGrindingDuration.toFixed(2) + " s"
}`
);
writeLine(
` ☕ Coffee Bean Level : ${isNaN(lastCoffeeBeanLevel) ? "n/a" : lastCoffeeBeanLevel.toFixed(2) + " g"}`
);
writeLine("========================================");
writeLine("---- Recorded Actions (last 5) ----");
recordedActions
.slice(-5)
.forEach((action) => writeLine(action + " "));
writeLine("-----------------------------------");
};
try {
thing
.observeProperty("waterTankLevel", async (data) => {
const waterTankLevel = await data.value();
console.log("------------------------------");
console.log("tankLevel : ", waterTankLevel, "ml");
console.log("------------------------------");
lastWaterTankLevel = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing waterTankLevel property:", err);
});
thing
.observeProperty("coffeeBeanLevel", async (data) => {
const coffeBeanLevel = await data.value();
console.log("------------------------------");
console.log("bean level : ", coffeBeanLevel, "g");
console.log("------------------------------");
lastCoffeeBeanLevel = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing coffeeBeanLevel property:", err);
});
thing
.observeProperty("temperature", async (data) => {
lastTemperature = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing temperature property:", err);
});
thing
.observeProperty("currentState", async (data) => {
lastCurrentState = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing currentState property:", err);
});
thing
.observeProperty("grinderStatus", async (data) => {
lastGrinderStatus = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing grinderStatus property:", err);
});
thing
.observeProperty("grindingDuration", async (data) => {
lastGrindingDuration = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing grindingDuration property:", err);
});
thing
.observeProperty("heaterStatus", async (data) => {
lastHeaterStatus = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing heaterStatus property:", err);
});
thing
.observeProperty("pumpStatus", async (data) => {
lastPumpStatus = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing pumpStatus property:", err);
});
thing
.observeProperty("valveStatus", async (data) => {
lastValveStatus = (await data.value()) as number;
displayOnlineStatus();
})
.catch((err) => {
console.error("Error observing valveStatus property:", err);
});

// give some time to gather initial values
await pause(2000);
await waitingMachineCoffeeStandByState();
recordAction("Machine is ready !");

await pause(10000);

recordAction("Invoking brewCoffee(Mocha) action...");
await thing.invokeAction("brewCoffee", { RecipeName: "Mocha" });
await waitingMachineCoffeeStandByState();
recordAction("Coffee is ready !");

await pause(10000);

recordAction("Invoking brewCoffee(Americano) action...");
await thing.invokeAction("brewCoffee", { RecipeName: "Americano" });
await waitingMachineCoffeeStandByState();
recordAction("Coffee is ready !");

await thing.invokeAction("brewCoffee", { CoffeeType: 1 });
await pause(5000);
await thing.invokeAction("brewCoffee", { CoffeeType: 0 });
await pause(5000);
await pause(10000);

recordAction("Invoking fillTank action...");
await thing.invokeAction("fillTank");
await pause(5000);
await waitingMachineCoffeeStandByState();
recordAction("Tank is refilled !");
recordAction("Done !");
} finally {
await servient.shutdown();
}
Expand Down
Loading