Skip to content

Commit f12f153

Browse files
committed
chore(binding-opcua): refactor codec into multiple files
1 parent 21ce56a commit f12f153

File tree

6 files changed

+345
-278
lines changed

6 files changed

+345
-278
lines changed

packages/binding-opcua/src/codec.ts

Lines changed: 3 additions & 276 deletions
Original file line numberDiff line numberDiff line change
@@ -12,279 +12,6 @@
1212
*
1313
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
1414
********************************************************************************/
15-
16-
import { ContentCodec, DataSchema, createLoggers } from "@node-wot/core";
17-
import { DataValue } from "node-opcua-data-value";
18-
import { DataType, Variant } from "node-opcua-variant";
19-
import Ajv from "ajv";
20-
import addFormats from "ajv-formats";
21-
22-
// see https://www.w3.org/Protocols/rfc1341/4_Content-Type.html
23-
import {
24-
opcuaJsonEncodeDataValue,
25-
opcuaJsonDecodeDataValue,
26-
opcuaJsonDecodeVariant,
27-
DataValueJSON,
28-
opcuaJsonEncodeVariant,
29-
} from "node-opcua-json";
30-
import { BinaryStream } from "node-opcua-binary-stream";
31-
import { DataSchemaValue } from "wot-typescript-definitions";
32-
33-
const { debug } = createLoggers("binding-opcua", "codec");
34-
35-
// Strict mode has a lot of other checks and it prevents runtime unexpected problems
36-
// TODO: in the future we should use the strict mode
37-
const ajv = new Ajv({ strict: false });
38-
addFormats(ajv);
39-
/**
40-
* this schema, describe the node-opcua JSON format for a DataValue object
41-
*
42-
* const pojo = (new DataValue({})).toString();
43-
*
44-
*/
45-
export const schemaDataValue = {
46-
type: ["object"], // "number", "integer", "string", "boolean", "array", "null"],
47-
properties: {
48-
serverPicoseconds: { type: "integer" },
49-
sourcePicoseconds: { type: "integer" },
50-
serverTimestamp: { type: "string", /* format: "date", */ nullable: true },
51-
sourceTimestamp: { type: "string", /* format: "date", */ nullable: true },
52-
statusCode: {
53-
type: ["object"],
54-
properties: {
55-
value: {
56-
type: "number",
57-
},
58-
},
59-
},
60-
value: {
61-
type: ["object"],
62-
properties: {
63-
dataType: {
64-
type: ["string", "integer"],
65-
},
66-
arrayType: {
67-
type: ["string"],
68-
},
69-
value: {
70-
type: ["number", "integer", "string", "boolean", "array", "null", "object"],
71-
},
72-
dimension: {
73-
type: ["array"],
74-
items: { type: "integer" },
75-
},
76-
additionalProperties: false,
77-
},
78-
},
79-
},
80-
additionalProperties: true,
81-
};
82-
83-
export const schemaVariantJSONNull = {
84-
type: "null",
85-
nullable: true,
86-
};
87-
88-
export const schemaVariantJSON = {
89-
type: "object",
90-
properties: {
91-
Type: {
92-
type: ["number"],
93-
},
94-
Body: {
95-
type: ["number", "integer", "string", "boolean", "array", "null", "object"],
96-
nullable: true,
97-
},
98-
Dimensions: {
99-
type: ["array"],
100-
items: { type: "integer" },
101-
},
102-
},
103-
additionalProperties: false,
104-
required: ["Type", "Body"],
105-
};
106-
107-
export const schemaDataValueJSON1 = {
108-
type: ["object"], // "number", "integer", "string", "boolean", "array", "null"],
109-
properties: {
110-
ServerPicoseconds: { type: "integer" },
111-
SourcePicoseconds: { type: "integer" },
112-
ServerTimestamp: {
113-
type: "string" /*, format: "date" */,
114-
},
115-
SourceTimestamp: {
116-
type: "string" /*, format: "date" */,
117-
},
118-
StatusCode: {
119-
type: "integer",
120-
minimum: 0,
121-
},
122-
123-
Value: schemaVariantJSON,
124-
Value1: { type: "number", nullable: true },
125-
126-
Value2: {
127-
oneOf: [schemaVariantJSON, schemaVariantJSONNull],
128-
},
129-
},
130-
131-
additionalProperties: false,
132-
required: ["Value"],
133-
};
134-
export const schemaDataValueJSON2 = {
135-
properties: {
136-
Value: { type: "null" },
137-
},
138-
};
139-
export const schemaDataValueJSON = {
140-
oneOf: [schemaDataValueJSON2, schemaDataValueJSON1],
141-
};
142-
export const schemaDataValueJSONValidate = ajv.compile(schemaDataValueJSON);
143-
export const schemaDataValueValidate = ajv.compile(schemaDataValue);
144-
145-
export function formatForNodeWoT(dataValue: DataValueJSON): DataValueJSON {
146-
// remove unwanted/unneeded properties
147-
delete dataValue.SourcePicoseconds;
148-
delete dataValue.ServerPicoseconds;
149-
delete dataValue.ServerTimestamp;
150-
return dataValue;
151-
}
152-
153-
// application/json => is equivalent to application/opcua+json;type=Value
154-
155-
export class OpcuaJSONCodec implements ContentCodec {
156-
getMediaType(): string {
157-
return "application/opcua+json";
158-
}
159-
160-
bytesToValue(bytes: Buffer, schema: DataSchema, parameters?: { [key: string]: string }): DataSchemaValue {
161-
const type = parameters?.type ?? "DataValue";
162-
let parsed = JSON.parse(bytes.toString());
163-
164-
const wantDataValue = parameters?.to === "DataValue" || false;
165-
166-
switch (type) {
167-
case "DataValue": {
168-
const isValid = schemaDataValueJSONValidate(parsed);
169-
if (!isValid) {
170-
debug(`bytesToValue: parsed = ${parsed}`);
171-
debug(`bytesToValue: ${schemaDataValueJSONValidate.errors}`);
172-
throw new Error("Invalid JSON dataValue : " + JSON.stringify(parsed, null, " "));
173-
}
174-
if (wantDataValue) {
175-
return opcuaJsonDecodeDataValue(parsed);
176-
}
177-
return formatForNodeWoT(opcuaJsonEncodeDataValue(opcuaJsonDecodeDataValue(parsed), true));
178-
// return parsed;
179-
}
180-
case "Variant": {
181-
if (wantDataValue) {
182-
const dataValue = new DataValue({ value: opcuaJsonDecodeVariant(parsed) });
183-
return dataValue;
184-
}
185-
const v = opcuaJsonEncodeVariant(opcuaJsonDecodeVariant(parsed), true);
186-
debug(`${v}`);
187-
return v;
188-
}
189-
case "Value": {
190-
if (wantDataValue) {
191-
if (!parameters || !parameters.dataType) {
192-
throw new Error("[OpcuaJSONCodec|bytesToValue]: unknown dataType for Value encoding" + type);
193-
}
194-
if (parameters.dataType === DataType[DataType.DateTime]) {
195-
parsed = new Date(parsed);
196-
}
197-
const value = {
198-
dataType: DataType[parameters.dataType as keyof typeof DataType],
199-
value: parsed,
200-
};
201-
return new DataValue({ value });
202-
} else {
203-
if (parameters?.dataType === DataType[DataType.DateTime]) {
204-
parsed = new Date(parsed);
205-
}
206-
return parsed;
207-
}
208-
}
209-
default:
210-
throw new Error("[OpcuaJSONCodec|bytesToValue]: Invalid type " + type);
211-
}
212-
}
213-
214-
valueToBytes(value: unknown, _schema: DataSchema, parameters?: { [key: string]: string }): Buffer {
215-
const type = parameters?.type ?? "DataValue";
216-
switch (type) {
217-
case "DataValue": {
218-
let dataValueJSON: DataValueJSON;
219-
if (value instanceof DataValue) {
220-
dataValueJSON = opcuaJsonEncodeDataValue(value, true);
221-
} else if (value instanceof Variant) {
222-
dataValueJSON = opcuaJsonEncodeDataValue(new DataValue({ value }), true);
223-
} else if (typeof value === "string") {
224-
dataValueJSON = JSON.parse(value) as DataValueJSON;
225-
} else {
226-
dataValueJSON = opcuaJsonEncodeDataValue(opcuaJsonDecodeDataValue(value as DataValueJSON), true);
227-
}
228-
dataValueJSON = formatForNodeWoT(dataValueJSON);
229-
return Buffer.from(JSON.stringify(dataValueJSON), "ascii");
230-
}
231-
case "Variant": {
232-
if (value instanceof DataValue) {
233-
value = opcuaJsonEncodeVariant(value.value, true);
234-
} else if (value instanceof Variant) {
235-
value = opcuaJsonEncodeVariant(value, true);
236-
} else if (typeof value === "string") {
237-
value = JSON.parse(value);
238-
}
239-
return Buffer.from(JSON.stringify(value), "ascii");
240-
}
241-
case "Value": {
242-
if (value === undefined) {
243-
return Buffer.alloc(0);
244-
}
245-
if (value instanceof DataValue) {
246-
value = opcuaJsonEncodeVariant(value.value, false);
247-
} else if (value instanceof Variant) {
248-
value = opcuaJsonEncodeVariant(value, false);
249-
}
250-
return Buffer.from(JSON.stringify(value), "ascii");
251-
}
252-
default:
253-
throw new Error("[OpcuaJSONCodec|valueToBytes]: Invalid type : " + type);
254-
}
255-
}
256-
}
257-
export const theOpcuaJSONCodec = new OpcuaJSONCodec();
258-
259-
export class OpcuaBinaryCodec implements ContentCodec {
260-
getMediaType(): string {
261-
return "application/opcua+octet-stream"; // see Ege
262-
}
263-
264-
bytesToValue(bytes: Buffer, schema: DataSchema, parameters?: { [key: string]: string }): DataValueJSON {
265-
const binaryStream = new BinaryStream(bytes);
266-
const dataValue = new DataValue();
267-
dataValue.decode(binaryStream);
268-
return opcuaJsonEncodeDataValue(dataValue, true);
269-
}
270-
271-
valueToBytes(
272-
dataValue: DataValueJSON | DataValue,
273-
schema: DataSchema,
274-
parameters?: { [key: string]: string }
275-
): Buffer {
276-
dataValue = dataValue instanceof DataValue ? dataValue : opcuaJsonDecodeDataValue(dataValue);
277-
278-
// remove unwanted properties
279-
dataValue.serverPicoseconds = 0;
280-
dataValue.sourcePicoseconds = 0;
281-
dataValue.serverTimestamp = null;
282-
283-
const size = dataValue.binaryStoreSize();
284-
const stream = new BinaryStream(size);
285-
dataValue.encode(stream);
286-
const body = stream.buffer;
287-
return body;
288-
}
289-
}
290-
export const theOpcuaBinaryCodec = new OpcuaBinaryCodec();
15+
export * from "./codecs/opcua-binary-codec";
16+
export * from "./codecs/opcua-json-codec";
17+
export * from "./codecs/opcua-data-schemas";
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/********************************************************************************
2+
* Copyright (c) 2022 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License v. 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and
10+
* Document License (2015-05-13) which is available at
11+
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document.
12+
*
13+
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
14+
********************************************************************************/
15+
16+
import { ContentCodec, DataSchema, createLoggers } from "@node-wot/core";
17+
import { BinaryStream } from "node-opcua-binary-stream";
18+
import { DataValue } from "node-opcua-data-value";
19+
20+
import { opcuaJsonEncodeDataValue, opcuaJsonDecodeDataValue, DataValueJSON } from "node-opcua-json";
21+
22+
export class OpcuaBinaryCodec implements ContentCodec {
23+
getMediaType(): string {
24+
return "application/opcua+octet-stream"; // see Ege
25+
}
26+
27+
bytesToValue(bytes: Buffer, schema: DataSchema, parameters?: { [key: string]: string }): DataValueJSON {
28+
const binaryStream = new BinaryStream(bytes);
29+
const dataValue = new DataValue();
30+
dataValue.decode(binaryStream);
31+
return opcuaJsonEncodeDataValue(dataValue, true);
32+
}
33+
34+
valueToBytes(
35+
dataValue: DataValueJSON | DataValue,
36+
schema: DataSchema,
37+
parameters?: { [key: string]: string }
38+
): Buffer {
39+
dataValue = dataValue instanceof DataValue ? dataValue : opcuaJsonDecodeDataValue(dataValue);
40+
41+
// remove unwanted properties
42+
dataValue.serverPicoseconds = 0;
43+
dataValue.sourcePicoseconds = 0;
44+
dataValue.serverTimestamp = null;
45+
46+
const size = dataValue.binaryStoreSize();
47+
const stream = new BinaryStream(size);
48+
dataValue.encode(stream);
49+
const body = stream.buffer;
50+
return body;
51+
}
52+
}
53+
export const theOpcuaBinaryCodec = new OpcuaBinaryCodec();

0 commit comments

Comments
 (0)