node-opcua-client-dynamic-extension-object 2.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +20 -0
  2. package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +12 -0
  3. package/dist/convert_data_type_definition_to_structuretype_schema.js +284 -0
  4. package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -0
  5. package/dist/extra_data_type_manager.d.ts +16 -0
  6. package/dist/extra_data_type_manager.js +76 -0
  7. package/dist/extra_data_type_manager.js.map +1 -0
  8. package/dist/get_extension_object_constructor.d.ts +7 -0
  9. package/dist/get_extension_object_constructor.js +38 -0
  10. package/dist/get_extension_object_constructor.js.map +1 -0
  11. package/dist/get_extra_data_type_manager.d.ts +3 -0
  12. package/dist/get_extra_data_type_manager.js +52 -0
  13. package/dist/get_extra_data_type_manager.js.map +1 -0
  14. package/dist/index.d.ts +11 -0
  15. package/dist/index.js +24 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/populate_data_type_manager.d.ts +3 -0
  18. package/dist/populate_data_type_manager.js +26 -0
  19. package/dist/populate_data_type_manager.js.map +1 -0
  20. package/dist/private/convert_data_type_definition_to_structuretype_schema.d.ts +12 -0
  21. package/dist/private/convert_data_type_definition_to_structuretype_schema.js +284 -0
  22. package/dist/private/convert_data_type_definition_to_structuretype_schema.js.map +1 -0
  23. package/dist/private/find_encodings.d.ts +4 -0
  24. package/dist/private/find_encodings.js +56 -0
  25. package/dist/private/find_encodings.js.map +1 -0
  26. package/dist/private/populate_data_type_manager_103.d.ts +9 -0
  27. package/dist/private/populate_data_type_manager_103.js +599 -0
  28. package/dist/private/populate_data_type_manager_103.js.map +1 -0
  29. package/dist/private/populate_data_type_manager_104.d.ts +9 -0
  30. package/dist/private/populate_data_type_manager_104.js +146 -0
  31. package/dist/private/populate_data_type_manager_104.js.map +1 -0
  32. package/dist/promote_opaque_structure.d.ts +6 -0
  33. package/dist/promote_opaque_structure.js +42 -0
  34. package/dist/promote_opaque_structure.js.map +1 -0
  35. package/dist/resolve_dynamic_extension_object.d.ts +4 -0
  36. package/dist/resolve_dynamic_extension_object.js +106 -0
  37. package/dist/resolve_dynamic_extension_object.js.map +1 -0
  38. package/package.json +47 -0
  39. package/source/convert_data_type_definition_to_structuretype_schema.ts +326 -0
  40. package/source/extra_data_type_manager.ts +89 -0
  41. package/source/get_extension_object_constructor.ts +28 -0
  42. package/source/get_extra_data_type_manager.ts +43 -0
  43. package/source/index.ts +11 -0
  44. package/source/populate_data_type_manager.ts +14 -0
  45. package/source/private/find_encodings.ts +44 -0
  46. package/source/private/populate_data_type_manager_103.ts +715 -0
  47. package/source/private/populate_data_type_manager_104.ts +153 -0
  48. package/source/promote_opaque_structure.ts +42 -0
  49. package/source/resolve_dynamic_extension_object.ts +104 -0
@@ -0,0 +1,153 @@
1
+ import { assert } from "node-opcua-assert";
2
+ import { AttributeIds, BrowseDirection } from "node-opcua-data-model";
3
+ import { DataTypeFactory } from "node-opcua-factory";
4
+ import { NodeId, resolveNodeId } from "node-opcua-nodeid";
5
+ import { IBasicSession, BrowseDescriptionLike } from "node-opcua-pseudo-session";
6
+ import { createDynamicObjectConstructor } from "node-opcua-schemas";
7
+ import { StatusCodes } from "node-opcua-status-code";
8
+ import { ReferenceDescription, BrowseResult } from "node-opcua-types";
9
+
10
+ //
11
+ import { ExtraDataTypeManager } from "../extra_data_type_manager";
12
+ import {
13
+ CacheForFieldResolution,
14
+ convertDataTypeDefinitionToStructureTypeSchema
15
+ } from "../convert_data_type_definition_to_structuretype_schema";
16
+ import { make_debugLog, make_errorLog } from "node-opcua-debug";
17
+ const errorLog = make_errorLog(__filename);
18
+ const debugLog =make_debugLog(__filename);
19
+
20
+ export async function readDataTypeDefinitionAndBuildType(
21
+ session: IBasicSession,
22
+ dataTypeNodeId: NodeId,
23
+ name: string,
24
+ dataTypeFactory: DataTypeFactory,
25
+ cache: { [key: string]: CacheForFieldResolution }
26
+ ) {
27
+ try {
28
+ const dataTypeDefinitionDataValue = await session.read({
29
+ attributeId: AttributeIds.DataTypeDefinition,
30
+ nodeId: dataTypeNodeId
31
+ });
32
+ /* istanbul ignore next */
33
+ if (dataTypeDefinitionDataValue.statusCode !== StatusCodes.Good) {
34
+ throw new Error(" Cannot find dataType Definition ! with nodeId =" + dataTypeNodeId.toString());
35
+ }
36
+ const dataTypeDefinition = dataTypeDefinitionDataValue.value.value;
37
+
38
+ const schema = await convertDataTypeDefinitionToStructureTypeSchema(
39
+ session,
40
+ dataTypeNodeId,
41
+ name,
42
+ dataTypeDefinition,
43
+ dataTypeFactory,
44
+ cache
45
+ );
46
+
47
+ createDynamicObjectConstructor(schema, dataTypeFactory);
48
+ } catch (err) {
49
+ errorLog("Error", err);
50
+ }
51
+ }
52
+
53
+ export async function populateDataTypeManager104(session: IBasicSession, dataTypeManager: ExtraDataTypeManager): Promise<void> {
54
+
55
+ const cache: { [key: string]: CacheForFieldResolution } = {};
56
+
57
+ async function withDataType(dataTypeNodeId: NodeId, r: ReferenceDescription): Promise<void> {
58
+ try {
59
+ const dataTypeFactory = dataTypeManager.getDataTypeFactory(dataTypeNodeId.namespace);
60
+ if (dataTypeNodeId.namespace === 0) {
61
+ // already known I guess
62
+ return;
63
+ }
64
+ // if not found already
65
+ if (dataTypeFactory.getConstructorForDataType(dataTypeNodeId)) {
66
+ // already known !
67
+ return;
68
+ }
69
+ // extract it formally
70
+ debugLog(" DataType => ", r.browseName.toString(), dataTypeNodeId.toString());
71
+ await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, r.browseName.name!, dataTypeFactory, cache);
72
+ assert(dataTypeFactory.getConstructorForDataType(dataTypeNodeId));
73
+ } catch (err) {
74
+ errorLog("err=", err);
75
+ }
76
+ }
77
+
78
+ function performAction(done: () => void) {
79
+ let pendingNodesToBrowse: BrowseDescriptionLike[] = [];
80
+ let pendingContinuationPoints: Buffer[] = [];
81
+ function triggerFutureBrowse() {
82
+ if (pendingNodesToBrowse.length + pendingContinuationPoints.length === 1) {
83
+ fencedAction(async ()=>{
84
+ flushBrowse();
85
+ });
86
+ }
87
+ }
88
+ let busyCount = 0;
89
+ function processBrowseResult(browseResults: BrowseResult[]) {
90
+
91
+ for (const result of browseResults) {
92
+ if (result.statusCode === StatusCodes.Good) {
93
+ if (result.continuationPoint) {
94
+ pendingContinuationPoints.push(result.continuationPoint);
95
+ triggerFutureBrowse();
96
+ }
97
+ for (const r of result.references || []) {
98
+ const dataTypeNodeId = r.nodeId;
99
+ fencedAction(async ()=>{
100
+ await withDataType(dataTypeNodeId, r);
101
+ });
102
+ // also explore sub types
103
+ browseSubDataTypeRecursively(dataTypeNodeId);
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ async function fencedAction(lambda: () => Promise<void>) {
110
+ busyCount += 1;
111
+ await lambda();
112
+ busyCount -= 1;
113
+ flushBrowse();
114
+ }
115
+ function flushBrowse() {
116
+ assert(busyCount >= 0);
117
+ if (pendingContinuationPoints.length) {
118
+ const continuationPoints = pendingContinuationPoints;
119
+ pendingContinuationPoints = [];
120
+ fencedAction(async () => {
121
+ const browseResults = await session.browseNext(continuationPoints, false);
122
+ processBrowseResult(browseResults);
123
+ });
124
+ } else if (pendingNodesToBrowse.length) {
125
+ const nodesToBrowse = pendingNodesToBrowse;
126
+ pendingNodesToBrowse = [];
127
+
128
+ fencedAction(async () => {
129
+ const browseResults = await session.browse(nodesToBrowse);
130
+ processBrowseResult(browseResults);
131
+ });
132
+ } else if (pendingContinuationPoints.length + pendingNodesToBrowse.length === 0 && busyCount === 0) {
133
+ done();
134
+ }
135
+ }
136
+
137
+ function browseSubDataTypeRecursively(nodeId: NodeId): void {
138
+ const nodeToBrowse: BrowseDescriptionLike = {
139
+ nodeId,
140
+ includeSubtypes: true,
141
+ browseDirection: BrowseDirection.Forward,
142
+ nodeClassMask: 0xff,
143
+ referenceTypeId: resolveNodeId("HasSubtype"),
144
+ resultMask: 0xff
145
+ };
146
+ pendingNodesToBrowse.push(nodeToBrowse);
147
+ triggerFutureBrowse();
148
+ }
149
+
150
+ browseSubDataTypeRecursively(resolveNodeId("Structure"));
151
+ }
152
+ await new Promise<void>((resolve) => performAction(resolve));
153
+ }
@@ -0,0 +1,42 @@
1
+ import { OpaqueStructure } from "node-opcua-extension-object";
2
+ import { IBasicSession } from "node-opcua-pseudo-session";
3
+ import { DataType, VariantArrayType, Variant } from "node-opcua-variant";
4
+ //
5
+ import { getExtraDataTypeManager } from "./get_extra_data_type_manager";
6
+ import { resolveDynamicExtensionObject } from "./resolve_dynamic_extension_object";
7
+
8
+
9
+ export interface PseudoDataValue { value: Variant };
10
+
11
+ export async function promoteOpaqueStructure(
12
+ session: IBasicSession,
13
+ dataValues: PseudoDataValue[]
14
+ ) {
15
+
16
+ // count number of Opaque Structures
17
+ const dataValuesToFix = dataValues.filter((dataValue: PseudoDataValue) =>
18
+ dataValue.value && dataValue.value.dataType === DataType.ExtensionObject &&
19
+ (
20
+ (dataValue.value.arrayType === VariantArrayType.Scalar
21
+ && dataValue.value.value instanceof OpaqueStructure)
22
+ ||
23
+ (dataValue.value.arrayType !== VariantArrayType.Scalar
24
+ && dataValue.value.value && dataValue.value.value.length >= 0
25
+ && dataValue.value.value[0] instanceof OpaqueStructure)
26
+ )
27
+ );
28
+
29
+ if (dataValuesToFix.length === 0) {
30
+ return;
31
+ }
32
+
33
+ // construct dataTypeManager if not already present
34
+ const extraDataTypeManager = await getExtraDataTypeManager(session);
35
+
36
+ const promises = dataValuesToFix.map(
37
+ async (dataValue: PseudoDataValue) => {
38
+ return await resolveDynamicExtensionObject(session, dataValue.value, extraDataTypeManager)
39
+ });
40
+ // https://medium.com/swlh/dealing-with-multiple-promises-in-javascript-41d6c21f20ff
41
+ await Promise.all(promises.map(p => p.catch(e => e)));
42
+ }
@@ -0,0 +1,104 @@
1
+ import { BinaryStream } from "node-opcua-binary-stream";
2
+ import { ExtensionObject, OpaqueStructure } from "node-opcua-extension-object";
3
+ import { DataType, Variant, VariantArrayType } from "node-opcua-variant";
4
+ import { hexDump, make_warningLog } from "node-opcua-debug";
5
+ //
6
+ import { ExtraDataTypeManager } from "./extra_data_type_manager";
7
+ import { IBasicSession } from "node-opcua-pseudo-session";
8
+ import { NodeId } from "node-opcua-nodeid";
9
+ import { ConstructorFunc } from "node-opcua-factory";
10
+ import { BrowseDirection, NodeClassMask, ResultMask } from "node-opcua-data-model";
11
+ import { StatusCodes } from "node-opcua-status-code";
12
+ import { readDataTypeDefinitionAndBuildType } from "./private/populate_data_type_manager_104";
13
+
14
+ const warningLog = make_warningLog(__filename);
15
+
16
+ async function getOrExtractConstructor(session: IBasicSession, binaryEncodingNodeId: NodeId, dataTypeManager: ExtraDataTypeManager): Promise<ConstructorFunc>
17
+ {
18
+ const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(binaryEncodingNodeId.namespace);
19
+ const Constructor = dataTypeFactory.getConstructor(binaryEncodingNodeId);
20
+ if (Constructor) {
21
+ return Constructor;
22
+ }
23
+ if (binaryEncodingNodeId.namespace ===0) {
24
+ throw new Error("Internal Error");
25
+ }
26
+ // need to extract it
27
+ const browseResult = await session.browse({
28
+ nodeId: binaryEncodingNodeId,
29
+ referenceTypeId: "HasEncoding",
30
+ browseDirection: BrowseDirection.Inverse,
31
+ includeSubtypes: false,
32
+ nodeClassMask: NodeClassMask.DataType,
33
+ resultMask: ResultMask.BrowseName
34
+ });
35
+ if (browseResult.statusCode !== StatusCodes.Good || browseResult.references!.length !== 1) {
36
+ throw new Error("browse failed");
37
+ }
38
+ const r = browseResult.references![0];
39
+ const dataTypeNodeId =r.nodeId;
40
+
41
+ if (dataTypeFactory.getConstructorForDataType(dataTypeNodeId)) {
42
+ throw new Error("Internal Error: we are not expecting this dataType to be processed already");
43
+ }
44
+ await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, r.browseName.name!, dataTypeFactory, {});
45
+
46
+ return dataTypeFactory.getConstructorForDataType(dataTypeNodeId)!;
47
+ }
48
+
49
+ async function resolveDynamicExtensionObjectV(
50
+ session: IBasicSession,
51
+ opaque: OpaqueStructure,
52
+ dataTypeManager: ExtraDataTypeManager
53
+ ): Promise<ExtensionObject> {
54
+ try {
55
+ const Constructor = await getOrExtractConstructor(session, opaque.nodeId, dataTypeManager);
56
+ const object = new Constructor();
57
+ const stream = new BinaryStream(opaque.buffer);
58
+ try {
59
+ object.decode(stream);
60
+ return object;
61
+ } catch (err) {
62
+ warningLog("Constructor = ", Constructor.name);
63
+ warningLog("opaqueStructure = ", opaque?.nodeId?.toString());
64
+ warningLog("opaqueStructure = ", "0x" + opaque?.buffer?.toString("hex"));
65
+ warningLog(hexDump(opaque.buffer));
66
+ warningLog("resolveDynamicExtensionObjectV err = ", err);
67
+ // try again for debugging
68
+ object.decode(stream);
69
+ return opaque;
70
+ }
71
+ } catch (err) {
72
+ warningLog("err", err);
73
+ warningLog("opaqueStructure = ", opaque.nodeId.toString());
74
+ warningLog("opaqueStructure = ", "0x" + opaque.buffer.toString("hex"));
75
+ warningLog(hexDump(opaque.buffer));
76
+ warningLog(dataTypeManager.toString());
77
+ throw err;
78
+ }
79
+ }
80
+
81
+ export async function resolveDynamicExtensionObject(
82
+ session: IBasicSession,
83
+ variant: Variant,
84
+ dataTypeManager: ExtraDataTypeManager
85
+ ): Promise<void> {
86
+ if (variant.dataType !== DataType.ExtensionObject) {
87
+ return;
88
+ }
89
+ if (variant.arrayType !== VariantArrayType.Scalar) {
90
+ if (variant.value instanceof Array) {
91
+ for (let i = 0; i < variant.value.length; i++) {
92
+ if (variant.value[i] instanceof OpaqueStructure) {
93
+ variant.value[i] = await resolveDynamicExtensionObjectV(session, variant.value[i], dataTypeManager);
94
+ }
95
+ }
96
+ }
97
+ return;
98
+ }
99
+
100
+ if (!(variant.value instanceof OpaqueStructure)) {
101
+ return;
102
+ }
103
+ variant.value = await resolveDynamicExtensionObjectV(session, variant.value as OpaqueStructure, dataTypeManager);
104
+ }