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.
- package/LICENSE +20 -0
- package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +12 -0
- package/dist/convert_data_type_definition_to_structuretype_schema.js +284 -0
- package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -0
- package/dist/extra_data_type_manager.d.ts +16 -0
- package/dist/extra_data_type_manager.js +76 -0
- package/dist/extra_data_type_manager.js.map +1 -0
- package/dist/get_extension_object_constructor.d.ts +7 -0
- package/dist/get_extension_object_constructor.js +38 -0
- package/dist/get_extension_object_constructor.js.map +1 -0
- package/dist/get_extra_data_type_manager.d.ts +3 -0
- package/dist/get_extra_data_type_manager.js +52 -0
- package/dist/get_extra_data_type_manager.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/populate_data_type_manager.d.ts +3 -0
- package/dist/populate_data_type_manager.js +26 -0
- package/dist/populate_data_type_manager.js.map +1 -0
- package/dist/private/convert_data_type_definition_to_structuretype_schema.d.ts +12 -0
- package/dist/private/convert_data_type_definition_to_structuretype_schema.js +284 -0
- package/dist/private/convert_data_type_definition_to_structuretype_schema.js.map +1 -0
- package/dist/private/find_encodings.d.ts +4 -0
- package/dist/private/find_encodings.js +56 -0
- package/dist/private/find_encodings.js.map +1 -0
- package/dist/private/populate_data_type_manager_103.d.ts +9 -0
- package/dist/private/populate_data_type_manager_103.js +599 -0
- package/dist/private/populate_data_type_manager_103.js.map +1 -0
- package/dist/private/populate_data_type_manager_104.d.ts +9 -0
- package/dist/private/populate_data_type_manager_104.js +146 -0
- package/dist/private/populate_data_type_manager_104.js.map +1 -0
- package/dist/promote_opaque_structure.d.ts +6 -0
- package/dist/promote_opaque_structure.js +42 -0
- package/dist/promote_opaque_structure.js.map +1 -0
- package/dist/resolve_dynamic_extension_object.d.ts +4 -0
- package/dist/resolve_dynamic_extension_object.js +106 -0
- package/dist/resolve_dynamic_extension_object.js.map +1 -0
- package/package.json +47 -0
- package/source/convert_data_type_definition_to_structuretype_schema.ts +326 -0
- package/source/extra_data_type_manager.ts +89 -0
- package/source/get_extension_object_constructor.ts +28 -0
- package/source/get_extra_data_type_manager.ts +43 -0
- package/source/index.ts +11 -0
- package/source/populate_data_type_manager.ts +14 -0
- package/source/private/find_encodings.ts +44 -0
- package/source/private/populate_data_type_manager_103.ts +715 -0
- package/source/private/populate_data_type_manager_104.ts +153 -0
- package/source/promote_opaque_structure.ts +42 -0
- 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
|
+
}
|