Skip to content

Commit

Permalink
opcua: handle nsu in nodeId (fixing eclipse-thingweb#1334)
Browse files Browse the repository at this point in the history
  • Loading branch information
erossignon committed Nov 29, 2024
1 parent 913546a commit 71765c5
Show file tree
Hide file tree
Showing 6 changed files with 1,019 additions and 850 deletions.
1,710 changes: 910 additions & 800 deletions package-lock.json

Large diffs are not rendered by default.

45 changes: 23 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"build:podman": "podman build -t wot-servient .",
"clean:dist": "npm exec --workspaces -- npx rimraf tsconfig.tsbuildinfo dist",
"update:wot-typescript-definitions": "npx npm-check-updates -u -f \"wot-typescript-definitions\" --deep",
"link": "npm link -ws"
"link": "npm link -ws",
"ncu:opcua": "npx -y npm-check-updates -u --deep --dep=dev,prod -f \"node-opcua*\" -t newest"
},
"workspaces": [
"./packages/td-tools",
Expand Down Expand Up @@ -80,27 +81,27 @@
"wot-typescript-definitions": "0.8.0-SNAPSHOT.29"
},
"dependencies": {
"node-opcua": "2.113.0",
"node-opcua-address-space": "2.113.0",
"node-opcua-basic-types": "2.113.0",
"node-opcua-binary-stream": "2.110.0",
"node-opcua-buffer-utils": "2.110.0",
"node-opcua-client": "2.113.0",
"node-opcua-constants": "2.98.1",
"node-opcua-data-model": "2.113.0",
"node-opcua-data-value": "2.113.0",
"node-opcua-date-time": "2.113.0",
"node-opcua-debug": "2.113.0",
"node-opcua-extension-object": "2.113.0",
"node-opcua-factory": "2.113.0",
"node-opcua": "2.138.1",
"node-opcua-address-space": "2.138.1",
"node-opcua-basic-types": "2.134.0",
"node-opcua-binary-stream": "2.133.0",
"node-opcua-buffer-utils": "2.133.0",
"node-opcua-client": "2.138.1",
"node-opcua-constants": "2.125.0",
"node-opcua-data-model": "2.137.0",
"node-opcua-data-value": "2.137.0",
"node-opcua-date-time": "2.134.0",
"node-opcua-debug": "2.133.0",
"node-opcua-extension-object": "2.137.0",
"node-opcua-factory": "2.137.0",
"node-opcua-json": "0.50.0",
"node-opcua-nodeid": "2.113.0",
"node-opcua-numeric-range": "2.113.0",
"node-opcua-pseudo-session": "2.113.0",
"node-opcua-service-browse": "2.113.0",
"node-opcua-service-translate-browse-path": "2.113.0",
"node-opcua-status-code": "2.110.0",
"node-opcua-types": "2.113.0",
"node-opcua-variant": "2.113.0"
"node-opcua-nodeid": "2.133.0",
"node-opcua-numeric-range": "2.137.0",
"node-opcua-pseudo-session": "2.138.0",
"node-opcua-service-browse": "2.137.0",
"node-opcua-service-translate-browse-path": "2.137.0",
"node-opcua-status-code": "2.133.0",
"node-opcua-types": "2.137.0",
"node-opcua-variant": "2.137.0"
}
}
42 changes: 21 additions & 21 deletions packages/binding-opcua/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,28 @@
"@node-wot/core": "0.8.17",
"ajv": "^8.11.0",
"ajv-formats": "^2.1.1",
"node-opcua": "2.113.0",
"node-opcua-address-space": "2.113.0",
"node-opcua-basic-types": "2.113.0",
"node-opcua-binary-stream": "2.110.0",
"node-opcua-buffer-utils": "2.110.0",
"node-opcua-client": "2.113.0",
"node-opcua-constants": "2.98.1",
"node-opcua-data-model": "2.113.0",
"node-opcua-data-value": "2.113.0",
"node-opcua-date-time": "2.113.0",
"node-opcua-debug": "2.113.0",
"node-opcua-extension-object": "2.113.0",
"node-opcua-factory": "2.113.0",
"node-opcua": "2.138.1",
"node-opcua-address-space": "2.138.1",
"node-opcua-basic-types": "2.134.0",
"node-opcua-binary-stream": "2.133.0",
"node-opcua-buffer-utils": "2.133.0",
"node-opcua-client": "2.138.1",
"node-opcua-constants": "2.125.0",
"node-opcua-data-model": "2.137.0",
"node-opcua-data-value": "2.137.0",
"node-opcua-date-time": "2.134.0",
"node-opcua-debug": "2.133.0",
"node-opcua-extension-object": "2.137.0",
"node-opcua-factory": "2.137.0",
"node-opcua-json": "0.50.0",
"node-opcua-nodeid": "2.113.0",
"node-opcua-numeric-range": "2.113.0",
"node-opcua-pseudo-session": "2.113.0",
"node-opcua-service-browse": "2.113.0",
"node-opcua-service-translate-browse-path": "2.113.0",
"node-opcua-status-code": "2.110.0",
"node-opcua-types": "2.113.0",
"node-opcua-variant": "2.113.0",
"node-opcua-nodeid": "2.133.0",
"node-opcua-numeric-range": "2.137.0",
"node-opcua-pseudo-session": "2.138.0",
"node-opcua-service-browse": "2.137.0",
"node-opcua-service-translate-browse-path": "2.137.0",
"node-opcua-status-code": "2.133.0",
"node-opcua-types": "2.137.0",
"node-opcua-variant": "2.137.0",
"rxjs": "5.5.11"
},
"scripts": {
Expand Down
29 changes: 22 additions & 7 deletions packages/binding-opcua/src/opcua-protocol-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
Variant,
VariantOptions,
} from "node-opcua-client";
import { ArgumentDefinition, getBuiltInDataType } from "node-opcua-pseudo-session";
import { ArgumentDefinition, getBuiltInDataType, readNamespaceArray } from "node-opcua-pseudo-session";

import { makeNodeId, NodeId, NodeIdLike, NodeIdType, resolveNodeId } from "node-opcua-nodeid";
import { AttributeIds, BrowseDirection, makeResultMask } from "node-opcua-data-model";
Expand Down Expand Up @@ -81,6 +81,7 @@ interface OPCUAConnection {
session: ClientSession;
client: OPCUAClient;
subscription: ClientSubscription;
namespaceArray?: string[];
}

type Resolver = (...arg: [...unknown[]]) => void;
Expand Down Expand Up @@ -211,6 +212,15 @@ export class OPCUAProtocolClient implements ProtocolClient {
});
}

private async _getNamespaceArray(form: OPCUAForm): Promise<string[]> {
return this._withConnection(form, async (c: OPCUAConnection) => {
if (!c.namespaceArray) {
c.namespaceArray = await readNamespaceArray(c.session);
}
return c.namespaceArray;
});
}

private async _withSubscription<T>(
form: OPCUAForm,
next: (session: ClientSession, subscription: ClientSubscription) => Promise<T>
Expand All @@ -220,13 +230,18 @@ export class OPCUAProtocolClient implements ProtocolClient {
});
}

private async _resolveNodeId3(form: OPCUAForm, nodeId: NodeIdLike): Promise<NodeId> {
const namespaceArray = await this._getNamespaceArray(form);
return resolveNodeId(nodeId, { namespaceArray });
}

private async _resolveNodeId2(form: OPCUAForm, fNodeId: NodeIdLike | NodeByBrowsePath): Promise<NodeId> {
if (fNodeId instanceof NodeId) {
return fNodeId;
} else if ((<NodeByBrowsePath>fNodeId).root != null) {
const f = <NodeByBrowsePath>fNodeId;
const r: NodeIdLike = f.root;
const rootNodeId = resolveNodeId(r);
const rootNodeId = await this._resolveNodeId3(form, r);
const nodeId = this._withSession<NodeId>(form, async (session) => {
const path = makeBrowsePath(rootNodeId, f.path);
const result = await session.translateBrowsePath(path);
Expand All @@ -241,7 +256,7 @@ export class OPCUAProtocolClient implements ProtocolClient {
});
return nodeId;
} else {
return resolveNodeId(fNodeId as NodeIdLike);
return await this._resolveNodeId3(form, fNodeId as NodeIdLike);
}
}

Expand All @@ -262,10 +277,10 @@ export class OPCUAProtocolClient implements ProtocolClient {
throw new Error("form must expose a 'opcua:nodeId'");
}
const nodeId = await this._resolveNodeId2(form, fNodeId);
return await this._withSession<DataType>(form, async (session: IBasicSession) => {
const dataTypeOrNull = await promisify(getBuiltInDataType)(session, nodeId);
if (dataTypeOrNull !== null) {
return dataTypeOrNull as DataType;
return await this._withSession<DataType>(form, async (session) => {
const dataTypeOrNull = await getBuiltInDataType(session, nodeId);
if (dataTypeOrNull != undefined && dataTypeOrNull !== DataType.Null) {
return dataTypeOrNull;
}
throw new Error("cannot predict dataType for nodeId " + nodeId.toString());
});
Expand Down
13 changes: 13 additions & 0 deletions packages/binding-opcua/test/fixture/basic-opcua-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ export async function startServer(): Promise<OPCUAServer> {
throw new Error("cannot find DI namespace");
}

const namespaceSpecial = addressSpace.registerNamespace("http://example.org/SpecialNamespace/");
const uaSpecialObject = namespaceSpecial.addObject({
browseName: "SpecialObject",
organizedBy: addressSpace.rootFolder.objects,
});
const uaSpecialVariable = namespaceSpecial.addVariable({
browseName: "SpecialVariable",
nodeId: "s=SpecialVariable",
dataType: "Double",
componentOf: uaSpecialObject,
});
uaSpecialVariable.setValueFromSource({ dataType: DataType.Double, value: 42.0 });

const deviceType = addressSpace.findObjectType("DeviceType", nsDI);
if (!deviceType) {
throw new Error("cannot find DeviceType");
Expand Down
30 changes: 30 additions & 0 deletions packages/binding-opcua/test/full-opcua-thing-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,23 @@ const thingDescription: WoT.ThingDescription = {
},
],
},

specialVariable: {
description: "a special variable",
observable: true,
readOnly: true,
unit: "°C",
type: "number",
"opcua:nodeId": "nsu=http://example.org/SpecialNamespace/;s=SpecialVariable",
forms: [
{
href: "/", // endpoint,
op: ["readproperty", "observeproperty"],
"opcua:nodeId": "nsu=http://example.org/SpecialNamespace/;s=SpecialVariable",
contentType: "application/json",
},
],
},
},
actions: {
setTemperatureSetPoint: {
Expand Down Expand Up @@ -617,4 +634,17 @@ describe("Full OPCUA Thing Test", () => {
await servient.shutdown();
}
});

it("Z11 - should read a variable with nodeid containing a NSU namespace", async () => {
const { thing, servient } = await makeThing();

try {
const content = await thing.readProperty("specialVariable");
const value = await content.value();
debug(`specialVariable = ${value}`);
expect(value).to.eql(42);
} finally {
await servient.shutdown();
}
});
});

0 comments on commit 71765c5

Please sign in to comment.