diff --git a/packages/binding-opcua/src/codec.ts b/packages/binding-opcua/src/codec.ts index b588709c6..57fbc1f7c 100644 --- a/packages/binding-opcua/src/codec.ts +++ b/packages/binding-opcua/src/codec.ts @@ -216,17 +216,18 @@ export class OpcuaJSONCodec implements ContentCodec { const type = parameters?.type ?? "DataValue"; switch (type) { case "DataValue": { + let dataValueJSON: DataValueJSON; if (value instanceof DataValue) { - value = opcuaJsonEncodeDataValue(value, true); + dataValueJSON = opcuaJsonEncodeDataValue(value, true); } else if (value instanceof Variant) { - value = opcuaJsonEncodeDataValue(new DataValue({ value }), true); + dataValueJSON = opcuaJsonEncodeDataValue(new DataValue({ value }), true); } else if (typeof value === "string") { - value = JSON.parse(value) as DataValueJSON; + dataValueJSON = JSON.parse(value) as DataValueJSON; } else { - value = opcuaJsonEncodeDataValue(opcuaJsonDecodeDataValue(value as DataValueJSON), true); + dataValueJSON = opcuaJsonEncodeDataValue(opcuaJsonDecodeDataValue(value as DataValueJSON), true); } - value = formatForNodeWoT(value); - return Buffer.from(JSON.stringify(value), "ascii"); + dataValueJSON = formatForNodeWoT(dataValueJSON); + return Buffer.from(JSON.stringify(dataValueJSON), "ascii"); } case "Variant": { if (value instanceof DataValue) { @@ -256,9 +257,6 @@ export class OpcuaJSONCodec implements ContentCodec { } export const theOpcuaJSONCodec = new OpcuaJSONCodec(); -export function jsonify(a: unknown): unknown { - return JSON.parse(JSON.stringify(a)); -} export class OpcuaBinaryCodec implements ContentCodec { getMediaType(): string { return "application/opcua+octet-stream"; // see Ege @@ -268,7 +266,7 @@ export class OpcuaBinaryCodec implements ContentCodec { const binaryStream = new BinaryStream(bytes); const dataValue = new DataValue(); dataValue.decode(binaryStream); - return jsonify(opcuaJsonEncodeDataValue(dataValue, true)); + return JSON.parse(JSON.stringify(opcuaJsonEncodeDataValue(dataValue, true))); } valueToBytes( @@ -281,7 +279,7 @@ export class OpcuaBinaryCodec implements ContentCodec { // remove unwanted properties dataValue.serverPicoseconds = 0; dataValue.sourcePicoseconds = 0; - dataValue.serverTimestamp = undefined; + dataValue.serverTimestamp = null; const size = dataValue.binaryStoreSize(); const stream = new BinaryStream(size); diff --git a/packages/binding-opcua/src/opcua-protocol-client.ts b/packages/binding-opcua/src/opcua-protocol-client.ts index 6a7b0197c..7ae6351bf 100644 --- a/packages/binding-opcua/src/opcua-protocol-client.ts +++ b/packages/binding-opcua/src/opcua-protocol-client.ts @@ -97,7 +97,7 @@ export function findBasicDataTypeC( ): void { const resultMask = makeResultMask("ReferenceType"); - if (dataTypeId.identifierType === NodeIdType.NUMERIC && dataTypeId.value <= 25) { + if (dataTypeId.identifierType === NodeIdType.NUMERIC && Number(dataTypeId.value) <= 25) { // we have a well-known DataType callback(null, dataTypeId.value as DataType); } else { @@ -127,7 +127,7 @@ export function findBasicDataTypeC( }); } } -const findBasicDataType: (session: IBasicSession, dataTypeId: NodeId) => Promise = +const findBasicDataType: (session: IBasicSession, dataTypeId: NodeId) => Promise = promisify(findBasicDataTypeC); function _variantToJSON(variant: Variant, contentType: string) { @@ -335,7 +335,7 @@ export class OPCUAProtocolClient implements ProtocolClient { session, form, argumentDefinition, - callResult.outputArguments + callResult.outputArguments || [] ); return output; }); @@ -394,7 +394,9 @@ export class OPCUAProtocolClient implements ProtocolClient { m.handlers.forEach((n) => n(content)); } catch (err) { debug(`${nodeId}: ${dataValue}`); - error(err.toString()); + if (error) { + error(new Error(JSON.stringify(err))); + } } if (complete) { complete(); @@ -545,7 +547,7 @@ export class OPCUAProtocolClient implements ProtocolClient { } } - private async _findBasicDataType(session: IBasicSession, dataType: NodeId): Promise { + private async _findBasicDataType(session: IBasicSession, dataType: NodeId): Promise { return await findBasicDataType(session, dataType); } @@ -569,10 +571,13 @@ export class OPCUAProtocolClient implements ProtocolClient { const { name, dataType, /* description, */ arrayDimensions, valueRank } = argument; - if (bodyInput[name] === undefined) { + if (bodyInput[name || "null"] === undefined) { throw new Error("missing value in bodyInput for argument " + name); } const basicDataType = await this._findBasicDataType(session, dataType); + if (basicDataType === undefined) { + throw new Error("basicDataType is undefined for dataType " + dataType); + } const arrayType: VariantArrayType = valueRank === -1 @@ -582,7 +587,7 @@ export class OPCUAProtocolClient implements ProtocolClient { : VariantArrayType.Matrix; const n = (a: unknown) => Buffer.from(JSON.stringify(a)); - const v = await this._contentToVariant(content2.type, n(bodyInput[name]), basicDataType); + const v = await this._contentToVariant(content2.type, n(bodyInput[name || "null"]), basicDataType); variants.push({ dataType: basicDataType, @@ -609,7 +614,7 @@ export class OPCUAProtocolClient implements ProtocolClient { const argument = outputArguments[index]; const { name } = argument; const element = _variantToJSON(outputVariants[index], contentType); - body[name] = element; + body[name || "null"] = element; } return new Content("application/json", Readable.from(JSON.stringify(body))); diff --git a/packages/binding-opcua/test/client-test.ts b/packages/binding-opcua/test/client-test.ts index c1de079eb..415a8dc60 100644 --- a/packages/binding-opcua/test/client-test.ts +++ b/packages/binding-opcua/test/client-test.ts @@ -184,6 +184,9 @@ describe("OPCUA Client", function () { const outputArguments = codecSerDes.contentToValue(contentResult2, schemaDataValue); debug(`Y4: outputArguments: ${outputArguments}`); + if (outputArguments == null) { + expect.fail("outputArguments is null"); + } outputArguments.should.eql({ PreviousSetPoint: 27 }); }); }); diff --git a/packages/binding-opcua/test/full-opcua-thing-test.ts b/packages/binding-opcua/test/full-opcua-thing-test.ts index c8f3ab233..afda30e70 100644 --- a/packages/binding-opcua/test/full-opcua-thing-test.ts +++ b/packages/binding-opcua/test/full-opcua-thing-test.ts @@ -328,8 +328,10 @@ describe("Full OPCUA Thing Test", () => { thing.setPropertyReadHandler("temperature", async () => temperature); const expThing = thing as ExposedThing; - const readHandler = expThing.__propertyHandlers.get("temperature").readHandler; - expect(readHandler, "must have a readHandler"); + const readHandler = expThing.__propertyHandlers.get("temperature")?.readHandler; + if (!readHandler) { + expect.fail("must have a readHandler"); + } const temperatureCheck1 = await readHandler(); expect(temperatureCheck1).to.equal(10); @@ -364,8 +366,8 @@ describe("Full OPCUA Thing Test", () => { debug(json?.toString()); return json; } catch (e) { - debug(e.toString()); - return { err: e.message }; + debug(`${e}`); + return { err: `${e}` }; } } it("Z2 - test $Variant$temperature", async () => { @@ -396,7 +398,7 @@ describe("Full OPCUA Thing Test", () => { const json2 = await doTest(thing, propertyName, { formIndex: 2 }); expect(json2).to.eql({ Type: 11, Body: 25 }); - expect(thingDescription.properties.temperature.forms[3].contentType).eql( + expect(thingDescription.properties?.temperature.forms[3].contentType).eql( "application/opcua+json;type=DataValue" ); const json3 = await doTest(thing, propertyName, { formIndex: 3 }); @@ -423,7 +425,7 @@ describe("Full OPCUA Thing Test", () => { expect(temperatureBefore).to.eql(27); // ---------------------------------------------- application/json - expect(thingDescription.properties.temperatureSetPoint.forms[0].contentType).eql("application/json"); + expect(thingDescription.properties?.temperatureSetPoint.forms[0].contentType).eql("application/json"); await thing.writeProperty("temperatureSetPoint", 110); const temperatureAfter = await readTemperature(thing); expect(temperatureAfter).to.eql(110); @@ -436,7 +438,7 @@ describe("Full OPCUA Thing Test", () => { const { thing, servient } = await makeThing(); try { // ---------------------------------------------- application/opcua+json;type=DataValue - expect(thingDescription.properties.temperatureSetPoint.forms[3].contentType).eql( + expect(thingDescription.properties?.temperatureSetPoint.forms[3].contentType).eql( "application/opcua+json;type=DataValue" ); await thing.writeProperty( @@ -455,7 +457,7 @@ describe("Full OPCUA Thing Test", () => { const { thing, servient } = await makeThing(); try { // ---------------------------------------------- application/opcua+json;type=Variant - expect(thingDescription.properties.temperatureSetPoint.forms[2].contentType).eql( + expect(thingDescription.properties?.temperatureSetPoint.forms[2].contentType).eql( "application/opcua+json;type=Variant" ); await thing.writeProperty("temperatureSetPoint", { Type: 11, Body: 90 }, { formIndex: 2 }); @@ -475,6 +477,9 @@ describe("Full OPCUA Thing Test", () => { try { // read temperature before const contentA = await thing.invokeAction("setTemperatureSetPoint", { TargetTemperature: 26 }); + if (!contentA) { + expect.fail("contentA null"); + } const returnedValue = await contentA.value(); debug(`Temperature setpoint before ${returnedValue}`); @@ -498,6 +503,9 @@ describe("Full OPCUA Thing Test", () => { const contentA = await thing.invokeAction("$OPCUA$setTemperatureSetPoint", { TargetTemperature: { Type: 11, Body: 26 }, }); + if (!contentA) { + expect.fail("contentA null"); + } const returnedValue = await contentA.value(); debug(`Temperature setpoint before ${returnedValue}`); @@ -520,7 +528,7 @@ describe("Full OPCUA Thing Test", () => { SongList: ["Jingle Bell", "Mary has a little lamb"], Volume: 100, }) - ).value(); + )?.value(); const returnedValue = content; debug(`Return value ${JSON.stringify(returnedValue, null, " ")}`); expect(returnedValue).to.eql({ @@ -553,7 +561,7 @@ describe("Full OPCUA Thing Test", () => { SongList: ["Jingle Bell", "Mary has a little lamb"], Volume: 100, }) - ).value(); + )?.value(); const returnedValue = content; debug(`Return value ${JSON.stringify(returnedValue, null, " ")}`); expect(returnedValue).to.eql({ diff --git a/packages/binding-opcua/test/opcua-codec-test.ts b/packages/binding-opcua/test/opcua-codec-test.ts index a74e75375..e63c58350 100644 --- a/packages/binding-opcua/test/opcua-codec-test.ts +++ b/packages/binding-opcua/test/opcua-codec-test.ts @@ -22,10 +22,10 @@ import { ObjectSchema } from "@node-wot/td-tools"; import { DataValue } from "node-opcua-data-value"; import { DataType, VariantArrayType } from "node-opcua-variant"; import { coerceLocalizedText } from "node-opcua-data-model"; -import { opcuaJsonEncodeDataValue } from "node-opcua-json"; +import { opcuaJsonEncodeDataValue, DataValueJSON } from "node-opcua-json"; import { StatusCodes } from "node-opcua-status-code"; -import { jsonify, OpcuaBinaryCodec, OpcuaJSONCodec, theOpcuaBinaryCodec, theOpcuaJSONCodec } from "../src/codec"; +import { OpcuaBinaryCodec, OpcuaJSONCodec, theOpcuaBinaryCodec, theOpcuaJSONCodec } from "../src/codec"; const { debug } = createLoggers("binding-opcua", "opcua-codec-test"); @@ -49,6 +49,10 @@ const dataValue3 = new DataValue({ }, }); +function jsonify(a: unknown): unknown { + return JSON.parse(JSON.stringify(a)); +} + const dataValue1Json = jsonify(opcuaJsonEncodeDataValue(dataValue1, true)); const dataValue2Json = jsonify(opcuaJsonEncodeDataValue(dataValue2, true)); const dataValue3Json = jsonify(opcuaJsonEncodeDataValue(dataValue3, true)); @@ -59,7 +63,7 @@ describe("OPCUA Binary Serdes ", () => { const schema: ObjectSchema = { type: "object", properties: {} }; it("should encode and decode a dataValue with application/opcua+binary codec " + index, () => { - const payload = theOpcuaBinaryCodec.valueToBytes(dataValue, schema); + const payload = theOpcuaBinaryCodec.valueToBytes(dataValue as DataValue | DataValueJSON, schema); const dataValueReloaded = theOpcuaBinaryCodec.bytesToValue(payload, schema); expect(dataValue).to.eql(jsonify(dataValueReloaded)); }); diff --git a/packages/binding-opcua/tsconfig.json b/packages/binding-opcua/tsconfig.json index 65cfae503..401574083 100644 --- a/packages/binding-opcua/tsconfig.json +++ b/packages/binding-opcua/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src" + "rootDir": "src", + "strict": true }, "include": ["./src/**/*"], "references": [{ "path": "../td-tools" }, { "path": "../core" }]