node-opcua-client-dynamic-extension-object 2.76.2 → 2.78.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/dist/convert_data_type_definition_to_structuretype_schema.d.ts +3 -3
- package/dist/convert_data_type_definition_to_structuretype_schema.js +16 -12
- package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
- package/dist/convert_structuretype_schema_to_structure_definition.d.ts +3 -0
- package/dist/convert_structuretype_schema_to_structure_definition.js +54 -0
- package/dist/convert_structuretype_schema_to_structure_definition.js.map +1 -0
- package/dist/extra_data_type_manager.js +4 -1
- package/dist/extra_data_type_manager.js.map +1 -1
- package/dist/get_extension_object_constructor.js +3 -3
- package/dist/get_extension_object_constructor.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/private/populate_data_type_manager_103.js +62 -23
- package/dist/private/populate_data_type_manager_103.js.map +1 -1
- package/dist/private/populate_data_type_manager_104.js +24 -9
- package/dist/private/populate_data_type_manager_104.js.map +1 -1
- package/dist/resolve_dynamic_extension_object.d.ts +2 -0
- package/dist/resolve_dynamic_extension_object.js +79 -6
- package/dist/resolve_dynamic_extension_object.js.map +1 -1
- package/package.json +18 -18
- package/source/convert_data_type_definition_to_structuretype_schema.ts +28 -16
- package/source/convert_structuretype_schema_to_structure_definition.ts +51 -0
- package/source/extra_data_type_manager.ts +4 -1
- package/source/get_extension_object_constructor.ts +3 -3
- package/source/index.ts +1 -0
- package/source/private/populate_data_type_manager_103.ts +73 -28
- package/source/private/populate_data_type_manager_104.ts +26 -10
- package/source/resolve_dynamic_extension_object.ts +94 -6
- package/test/test_convertStructureTypeSchemaToStructureDefinition.ts +117 -0
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import * as chalk from "chalk";
|
|
7
7
|
|
|
8
8
|
import { assert } from "node-opcua-assert";
|
|
9
|
-
import { AttributeIds, makeNodeClassMask, makeResultMask, QualifiedName } from "node-opcua-data-model";
|
|
10
|
-
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog} from "node-opcua-debug";
|
|
9
|
+
import { AttributeIds, makeNodeClassMask, makeResultMask, NodeClassMask, QualifiedName } from "node-opcua-data-model";
|
|
10
|
+
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
11
11
|
import { ConstructorFuncWithSchema, DataTypeFactory, getStandardDataTypeFactory } from "node-opcua-factory";
|
|
12
12
|
import { ExpandedNodeId, NodeId, resolveNodeId, sameNodeId } from "node-opcua-nodeid";
|
|
13
13
|
import { browseAll, BrowseDescriptionLike, IBasicSession } from "node-opcua-pseudo-session";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { BrowseDescriptionOptions, BrowseDirection, BrowseResult, ReferenceDescription } from "node-opcua-service-browse";
|
|
21
21
|
import { makeBrowsePath } from "node-opcua-service-translate-browse-path";
|
|
22
22
|
import { StatusCodes } from "node-opcua-status-code";
|
|
23
|
-
import { ReadValueIdOptions, StructureDefinition } from "node-opcua-types";
|
|
23
|
+
import { BrowsePath, ReadValueIdOptions, StructureDefinition } from "node-opcua-types";
|
|
24
24
|
|
|
25
25
|
import { ExtraDataTypeManager } from "../extra_data_type_manager";
|
|
26
26
|
import {
|
|
@@ -122,13 +122,20 @@ async function _enrichWithDescriptionOf(session: IBasicSession, dataTypeDescript
|
|
|
122
122
|
const binaryEncodings = [];
|
|
123
123
|
const nodesToBrowseDataType: BrowseDescriptionOptions[] = [];
|
|
124
124
|
|
|
125
|
-
let i
|
|
126
|
-
|
|
127
|
-
const
|
|
125
|
+
for (let i=0;i< results3.length;i++) {
|
|
126
|
+
|
|
127
|
+
const result3 = results3[i];
|
|
128
|
+
const dataTypeDescription = dataTypeDescriptions[i];
|
|
128
129
|
|
|
129
130
|
result3.references = result3.references || [];
|
|
131
|
+
|
|
132
|
+
if (result3.references.length === 0) {
|
|
133
|
+
// may be the dataType is abstract and as no need for DescriptionOF
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
130
136
|
if (result3.references.length !== 1) {
|
|
131
137
|
warningLog("_enrichWithDescriptionOf : expecting 1 reference for ", dataTypeDescription.browseName.toString());
|
|
138
|
+
warningLog(result3.toString());
|
|
132
139
|
continue;
|
|
133
140
|
}
|
|
134
141
|
for (const ref of result3.references) {
|
|
@@ -144,34 +151,65 @@ async function _enrichWithDescriptionOf(session: IBasicSession, dataTypeDescript
|
|
|
144
151
|
nodesToBrowseDataType.push({
|
|
145
152
|
browseDirection: BrowseDirection.Inverse,
|
|
146
153
|
includeSubtypes: false,
|
|
147
|
-
nodeClassMask:
|
|
148
|
-
nodeId: ref.nodeId
|
|
154
|
+
nodeClassMask: NodeClassMask.DataType,
|
|
155
|
+
nodeId: ref.nodeId,
|
|
149
156
|
referenceTypeId: resolveNodeId("HasEncoding"),
|
|
150
157
|
// resultMask: makeResultMask("NodeId | ReferenceType | BrowseName | NodeClass | TypeDefinition")
|
|
151
158
|
resultMask: makeResultMask("NodeId | BrowseName")
|
|
152
159
|
});
|
|
153
160
|
}
|
|
154
161
|
}
|
|
162
|
+
|
|
155
163
|
const dataTypeNodeIds: NodeId[] = [];
|
|
164
|
+
|
|
156
165
|
if (nodesToBrowseDataType.length > 0) {
|
|
157
166
|
const results4 = await browseAll(session, nodesToBrowseDataType);
|
|
158
|
-
i
|
|
159
|
-
|
|
167
|
+
for (let i=0;i< results4.length; i++) {
|
|
168
|
+
|
|
169
|
+
const result4 =results4[i];
|
|
160
170
|
result4.references = result4.references || [];
|
|
161
171
|
|
|
162
172
|
/* istanbul ignore next */
|
|
163
173
|
if (result4.references.length !== 1) {
|
|
164
|
-
|
|
174
|
+
errorLog("What's going on ?", result4.toString(), "result4.references.length = ", result4.references.length);
|
|
165
175
|
}
|
|
166
176
|
|
|
167
|
-
|
|
168
|
-
|
|
177
|
+
const ref = result4.references![0];
|
|
178
|
+
const dataTypeNodeId = ref.nodeId;
|
|
179
|
+
dataTypeNodeIds[i]= dataTypeNodeId;
|
|
180
|
+
const dataTypeDescription = dataTypeDescriptions[i];
|
|
181
|
+
dataTypeDescription.encodings!.dataTypeNodeId = dataTypeNodeId;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
169
184
|
|
|
170
|
-
|
|
185
|
+
const otherEncodingBrowse = dataTypeNodeIds.map((dataTypeNodeId)=>({
|
|
186
|
+
browseDirection: BrowseDirection.Forward,
|
|
187
|
+
includeSubtypes: false,
|
|
188
|
+
nodeClassMask: NodeClassMask.Object,
|
|
189
|
+
nodeId: dataTypeNodeId,
|
|
190
|
+
referenceTypeId: resolveNodeId("HasEncoding"),
|
|
191
|
+
// resultMask: makeResultMask("NodeId | ReferenceType | BrowseName | NodeClass | TypeDefinition")
|
|
192
|
+
resultMask: makeResultMask("NodeId | BrowseName")
|
|
193
|
+
}));
|
|
171
194
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
195
|
+
const results5 = await browseAll(session, otherEncodingBrowse);
|
|
196
|
+
for (let i=0;i<results5.length;i++) {
|
|
197
|
+
const result5= results5[i];
|
|
198
|
+
const dataTypeDescription = dataTypeDescriptions[i];
|
|
199
|
+
for (const ref of result5.references || []) {
|
|
200
|
+
switch(ref.browseName.name) {
|
|
201
|
+
case "Default XML":
|
|
202
|
+
dataTypeDescription.encodings!.xmlEncodingNodeId = ref.nodeId;
|
|
203
|
+
break;
|
|
204
|
+
case "Default Binary":
|
|
205
|
+
dataTypeDescription.encodings!.binaryEncodingNodeId = ref.nodeId;
|
|
206
|
+
break;
|
|
207
|
+
case "Default JSON":
|
|
208
|
+
dataTypeDescription.encodings!.jsonEncodingNodeId = ref.nodeId;
|
|
209
|
+
break;
|
|
210
|
+
default:
|
|
211
|
+
errorLog("Cannot handle unknown encoding", ref.browseName.name);
|
|
212
|
+
}
|
|
175
213
|
}
|
|
176
214
|
}
|
|
177
215
|
return dataTypeNodeIds;
|
|
@@ -181,6 +219,7 @@ interface IDataTypeDefInfo {
|
|
|
181
219
|
className: string;
|
|
182
220
|
dataTypeNodeId: NodeId;
|
|
183
221
|
dataTypeDefinition: StructureDefinition;
|
|
222
|
+
isAbstract: boolean;
|
|
184
223
|
}
|
|
185
224
|
type DataTypeDefinitions = IDataTypeDefInfo[];
|
|
186
225
|
|
|
@@ -211,7 +250,7 @@ function sortStructure(dataTypeDefinitions: DataTypeDefinitions) {
|
|
|
211
250
|
}
|
|
212
251
|
_visit(ddd);
|
|
213
252
|
}
|
|
214
|
-
|
|
253
|
+
|
|
215
254
|
dataTypeDefinitionsSorted.push(d);
|
|
216
255
|
}
|
|
217
256
|
for (const d of dataTypeDefinitions) {
|
|
@@ -255,7 +294,7 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
255
294
|
|
|
256
295
|
if (dataTypeDefinition && dataTypeDefinition instanceof StructureDefinition) {
|
|
257
296
|
const className = dataTypeDescription.browseName.name!;
|
|
258
|
-
dataTypeDefinitions.push({ className, dataTypeNodeId, dataTypeDefinition });
|
|
297
|
+
dataTypeDefinitions.push({ className, dataTypeNodeId, dataTypeDefinition, isAbstract: false });
|
|
259
298
|
}
|
|
260
299
|
} else {
|
|
261
300
|
debugLog(
|
|
@@ -272,12 +311,12 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
272
311
|
if (doDebug) {
|
|
273
312
|
debugLog("order ", dataTypeDefinitionsSorted.map((a) => a.className + " " + a.dataTypeNodeId).join(" -> "));
|
|
274
313
|
}
|
|
275
|
-
for (const { className, dataTypeNodeId, dataTypeDefinition } of dataTypeDefinitionsSorted) {
|
|
314
|
+
for (const { className, dataTypeNodeId, dataTypeDefinition, isAbstract } of dataTypeDefinitionsSorted) {
|
|
276
315
|
// istanbul ignore next
|
|
277
316
|
if (doDebug) {
|
|
278
317
|
debugLog(chalk.yellow("--------------------------------------- "), className, dataTypeNodeId.toString());
|
|
279
318
|
}
|
|
280
|
-
if (dataTypeFactory.
|
|
319
|
+
if (dataTypeFactory.hasStructureByTypeName(className)) {
|
|
281
320
|
continue; // this structure has already been seen
|
|
282
321
|
}
|
|
283
322
|
// now fill typeDictionary
|
|
@@ -288,6 +327,7 @@ async function _extractDataTypeDictionaryFromDefinition(
|
|
|
288
327
|
className,
|
|
289
328
|
dataTypeDefinition,
|
|
290
329
|
dataTypeFactory,
|
|
330
|
+
isAbstract,
|
|
291
331
|
cache
|
|
292
332
|
);
|
|
293
333
|
|
|
@@ -336,6 +376,11 @@ interface TypeDictionaryInfo {
|
|
|
336
376
|
targetNamespace: string;
|
|
337
377
|
}
|
|
338
378
|
|
|
379
|
+
function _isOldDataTypeDictionary(d: TypeDictionaryInfo) {
|
|
380
|
+
const isDictionaryDeprecated = d.isDictionaryDeprecated; // await _readDeprecatedFlag(session, dataTypeDictionaryNodeId);
|
|
381
|
+
const rawSchema = d.rawSchema; // DataValue = await session.read({ nodeId: dataTypeDictionaryNodeId, attributeId: AttributeIds.Value });
|
|
382
|
+
return !isDictionaryDeprecated && rawSchema.length >=0;
|
|
383
|
+
}
|
|
339
384
|
async function _extractDataTypeDictionary(
|
|
340
385
|
session: IBasicSession,
|
|
341
386
|
d: TypeDictionaryInfo,
|
|
@@ -343,13 +388,10 @@ async function _extractDataTypeDictionary(
|
|
|
343
388
|
): Promise<void> {
|
|
344
389
|
const dataTypeDictionaryNodeId = d.reference.nodeId;
|
|
345
390
|
|
|
346
|
-
const isDictionaryDeprecated = d.isDictionaryDeprecated; // await _readDeprecatedFlag(session, dataTypeDictionaryNodeId);
|
|
347
|
-
const rawSchema = d.rawSchema; // DataValue = await session.read({ nodeId: dataTypeDictionaryNodeId, attributeId: AttributeIds.Value });
|
|
348
|
-
|
|
349
391
|
const name = await session.read({ nodeId: dataTypeDictionaryNodeId, attributeId: AttributeIds.BrowseName });
|
|
350
392
|
const namespace = await _readNamespaceUriProperty(session, dataTypeDictionaryNodeId);
|
|
351
393
|
|
|
352
|
-
if (
|
|
394
|
+
if (!_isOldDataTypeDictionary(d)) {
|
|
353
395
|
debugLog(
|
|
354
396
|
"DataTypeDictionary is deprecated or BSD schema stored in dataValue is null !",
|
|
355
397
|
chalk.cyan(name.value.value.toString()),
|
|
@@ -364,8 +406,8 @@ async function _extractDataTypeDictionary(
|
|
|
364
406
|
throw new Error("cannot find dataTypeFactory for namespace " + dataTypeDictionaryNodeId.namespace);
|
|
365
407
|
}
|
|
366
408
|
await _extractDataTypeDictionaryFromDefinition(session, dataTypeDictionaryNodeId, dataTypeFactory2);
|
|
367
|
-
return;
|
|
368
409
|
} else {
|
|
410
|
+
const rawSchema = d.rawSchema; // DataValue = await session.read({ nodeId: dataTypeDictionaryNodeId, attributeId: AttributeIds.Value });
|
|
369
411
|
debugLog(" ----- Using old method for extracting schema => with BSD files");
|
|
370
412
|
// old method ( until 1.03 )
|
|
371
413
|
// one need to read the schema file store in the dataTypeDictionary node and parse it !
|
|
@@ -440,9 +482,12 @@ async function _exploreDataTypeDefinition(
|
|
|
440
482
|
}
|
|
441
483
|
// let's verify that constructor is operational
|
|
442
484
|
try {
|
|
443
|
-
const
|
|
485
|
+
const Constructor = dataTypeFactory.getStructureInfoByTypeName(name).constructor;
|
|
486
|
+
if (!Constructor) {
|
|
487
|
+
throw new Error(`Cannot instantiate abstract DataType(name=${name})`);
|
|
488
|
+
}
|
|
444
489
|
// xx const constructor = getOrCreateConstructor(name, dataTypeFactory, defaultBinary);
|
|
445
|
-
const testObject = new
|
|
490
|
+
const testObject = new Constructor();
|
|
446
491
|
debugLog(testObject.toString());
|
|
447
492
|
} catch (err) {
|
|
448
493
|
debugLog(" Error cannot construct Extension Object " + name);
|
|
@@ -4,7 +4,7 @@ import { make_debugLog, make_errorLog } from "node-opcua-debug";
|
|
|
4
4
|
import { DataTypeFactory } from "node-opcua-factory";
|
|
5
5
|
import { NodeId, NodeIdLike, resolveNodeId } from "node-opcua-nodeid";
|
|
6
6
|
import { IBasicSession, BrowseDescriptionLike } from "node-opcua-pseudo-session";
|
|
7
|
-
import { createDynamicObjectConstructor } from "node-opcua-schemas";
|
|
7
|
+
import { createDynamicObjectConstructor as createDynamicObjectConstructorAndRegister } from "node-opcua-schemas";
|
|
8
8
|
import { StatusCodes } from "node-opcua-status-code";
|
|
9
9
|
import { ReferenceDescription, BrowseResult, BrowseDescriptionOptions } from "node-opcua-types";
|
|
10
10
|
|
|
@@ -25,10 +25,22 @@ export async function readDataTypeDefinitionAndBuildType(
|
|
|
25
25
|
cache: { [key: string]: CacheForFieldResolution }
|
|
26
26
|
) {
|
|
27
27
|
try {
|
|
28
|
-
const dataTypeDefinitionDataValue = await session.read(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const [isAbstractDataValue, dataTypeDefinitionDataValue] = await session.read([
|
|
29
|
+
{
|
|
30
|
+
attributeId: AttributeIds.IsAbstract,
|
|
31
|
+
nodeId: dataTypeNodeId
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
attributeId: AttributeIds.DataTypeDefinition,
|
|
35
|
+
nodeId: dataTypeNodeId
|
|
36
|
+
}
|
|
37
|
+
]);
|
|
38
|
+
/* istanbul ignore next */
|
|
39
|
+
if (isAbstractDataValue.statusCode !== StatusCodes.Good) {
|
|
40
|
+
throw new Error(" Cannot find dataType isAbstract ! with nodeId =" + dataTypeNodeId.toString());
|
|
41
|
+
}
|
|
42
|
+
const isAbstract = isAbstractDataValue.value.value as boolean;
|
|
43
|
+
|
|
32
44
|
/* istanbul ignore next */
|
|
33
45
|
if (dataTypeDefinitionDataValue.statusCode !== StatusCodes.Good) {
|
|
34
46
|
throw new Error(" Cannot find dataType Definition ! with nodeId =" + dataTypeNodeId.toString());
|
|
@@ -41,11 +53,16 @@ export async function readDataTypeDefinitionAndBuildType(
|
|
|
41
53
|
name,
|
|
42
54
|
dataTypeDefinition,
|
|
43
55
|
dataTypeFactory,
|
|
56
|
+
isAbstract,
|
|
44
57
|
cache
|
|
45
58
|
);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
if (isAbstract) {
|
|
60
|
+
// cannot construct an abstract structure
|
|
61
|
+
dataTypeFactory.registerAbstractStructure(dataTypeNodeId, name, schema);
|
|
62
|
+
|
|
63
|
+
} else {
|
|
64
|
+
const Constructor = createDynamicObjectConstructorAndRegister(schema, dataTypeFactory);
|
|
65
|
+
}
|
|
49
66
|
} catch (err) {
|
|
50
67
|
errorLog("Error", err);
|
|
51
68
|
}
|
|
@@ -165,14 +182,13 @@ export async function populateDataTypeManager104(session: IBasicSession, dataTyp
|
|
|
165
182
|
return;
|
|
166
183
|
}
|
|
167
184
|
// if not found already
|
|
168
|
-
if (dataTypeFactory.
|
|
185
|
+
if (dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId)) {
|
|
169
186
|
// already known !
|
|
170
187
|
return;
|
|
171
188
|
}
|
|
172
189
|
// extract it formally
|
|
173
190
|
debugLog(" DataType => ", r.browseName.toString(), dataTypeNodeId.toString());
|
|
174
191
|
await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, r.browseName.name!, dataTypeFactory, cache);
|
|
175
|
-
assert(dataTypeFactory.getConstructorForDataType(dataTypeNodeId));
|
|
176
192
|
} catch (err) {
|
|
177
193
|
errorLog("err=", err);
|
|
178
194
|
}
|
|
@@ -4,7 +4,7 @@ import { DataType, Variant, VariantArrayType } from "node-opcua-variant";
|
|
|
4
4
|
import { hexDump, make_warningLog } from "node-opcua-debug";
|
|
5
5
|
import { IBasicSession } from "node-opcua-pseudo-session";
|
|
6
6
|
import { NodeId } from "node-opcua-nodeid";
|
|
7
|
-
import { ConstructorFunc } from "node-opcua-factory";
|
|
7
|
+
import { ConstructorFunc, StructuredTypeField } from "node-opcua-factory";
|
|
8
8
|
import { BrowseDirection, NodeClassMask, ResultMask } from "node-opcua-data-model";
|
|
9
9
|
import { StatusCodes } from "node-opcua-status-code";
|
|
10
10
|
//
|
|
@@ -19,6 +19,8 @@ async function getOrExtractConstructor(
|
|
|
19
19
|
dataTypeManager: ExtraDataTypeManager
|
|
20
20
|
): Promise<ConstructorFunc> {
|
|
21
21
|
const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(binaryEncodingNodeId.namespace);
|
|
22
|
+
|
|
23
|
+
|
|
22
24
|
const Constructor = dataTypeFactory.getConstructor(binaryEncodingNodeId);
|
|
23
25
|
if (Constructor) {
|
|
24
26
|
return Constructor;
|
|
@@ -41,14 +43,99 @@ async function getOrExtractConstructor(
|
|
|
41
43
|
const r = browseResult.references![0];
|
|
42
44
|
const dataTypeNodeId = r.nodeId;
|
|
43
45
|
|
|
44
|
-
if (dataTypeFactory.
|
|
46
|
+
if (dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId)) {
|
|
45
47
|
throw new Error("Internal Error: we are not expecting this dataType to be processed already");
|
|
46
48
|
}
|
|
47
49
|
await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, r.browseName.name!, dataTypeFactory, {});
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
const structureInfo = dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId)!;
|
|
52
|
+
if (!structureInfo.constructor) {
|
|
53
|
+
throw new Error("Cannot find constructor for abstract DataType");
|
|
54
|
+
}
|
|
55
|
+
return structureInfo.constructor;
|
|
50
56
|
}
|
|
51
57
|
|
|
58
|
+
export async function resolveOpaqueStructureInExtentionObject(
|
|
59
|
+
session: IBasicSession,
|
|
60
|
+
dataTypeManager: ExtraDataTypeManager,
|
|
61
|
+
object: ExtensionObject
|
|
62
|
+
) {
|
|
63
|
+
const schema = object.schema;
|
|
64
|
+
interface D {
|
|
65
|
+
dataTypeManager: ExtraDataTypeManager;
|
|
66
|
+
promises: Promise<void>[];
|
|
67
|
+
}
|
|
68
|
+
async function fixOpaqueStructureOnElement(
|
|
69
|
+
element: Record<string, unknown>,
|
|
70
|
+
field: StructuredTypeField,
|
|
71
|
+
data: D,
|
|
72
|
+
args?: any
|
|
73
|
+
): Promise<unknown> {
|
|
74
|
+
if (element instanceof Variant) {
|
|
75
|
+
await resolveDynamicExtensionObject(session, element, dataTypeManager);
|
|
76
|
+
return element;
|
|
77
|
+
}
|
|
78
|
+
if (!(element instanceof OpaqueStructure)) {
|
|
79
|
+
return element;
|
|
80
|
+
}
|
|
81
|
+
const variant = new Variant({ dataType: DataType.ExtensionObject, value: element });
|
|
82
|
+
await resolveDynamicExtensionObject(session, variant, dataTypeManager);
|
|
83
|
+
return variant.value as unknown;
|
|
84
|
+
}
|
|
85
|
+
function fixOpaqueStructure(object: any, field: StructuredTypeField, data: D, args?: any) {
|
|
86
|
+
if (field.category === "complex" && !field.allowSubType) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (field.category === "basic" && field.fieldType !== "Variant") {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log("field", field.name, field.category, field.fieldType);
|
|
93
|
+
const a = object[field.name];
|
|
94
|
+
if (!a) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (field.isArray) {
|
|
98
|
+
for (let i = 0; i < a.length; i++) {
|
|
99
|
+
const x = a[i];
|
|
100
|
+
promises.push(
|
|
101
|
+
(async () => {
|
|
102
|
+
a[i] = await fixOpaqueStructureOnElement(x, field, data, args);
|
|
103
|
+
})()
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
promises.push(
|
|
108
|
+
(async () => {
|
|
109
|
+
object[field.name] = await fixOpaqueStructureOnElement(a, field, data, args);
|
|
110
|
+
})()
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const promises: Promise<void>[] = [];
|
|
115
|
+
object.applyOnAllFields<D>(fixOpaqueStructure, { dataTypeManager, promises });
|
|
116
|
+
await Promise.all(promises);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// async function resolveInnerVariantType(session: IBasicSession, object: ExtensionObject, dataTypeManager: ExtraDataTypeManager) {
|
|
120
|
+
// console.log("object = ", object.constructor.name);
|
|
121
|
+
|
|
122
|
+
// const fields = object.schema.fields;
|
|
123
|
+
// const _object = object as unknown as Record<string, unknown>;
|
|
124
|
+
// for (const field of fields) {
|
|
125
|
+
// console.log("field = ", field.name);
|
|
126
|
+
// if (field.category !== "complex") continue;
|
|
127
|
+
// const p = _object[field.name];
|
|
128
|
+
// if (p === undefined || p === null) continue;
|
|
129
|
+
// console.log("=>", field.name, (p as any).toString());
|
|
130
|
+
// if (p instanceof ExtensionObject) {
|
|
131
|
+
// resolveInnerVariantType(session, p, dataTypeManager);
|
|
132
|
+
// }
|
|
133
|
+
// if (p instanceof Variant && p.value instanceof OpaqueStructure) {
|
|
134
|
+
// console.log("§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§");
|
|
135
|
+
// p.value = resolveDynamicExtensionObjectV(session, p.value, dataTypeManager);
|
|
136
|
+
// }
|
|
137
|
+
// }
|
|
138
|
+
// }
|
|
52
139
|
async function resolveDynamicExtensionObjectV(
|
|
53
140
|
session: IBasicSession,
|
|
54
141
|
opaque: OpaqueStructure,
|
|
@@ -60,11 +147,12 @@ async function resolveDynamicExtensionObjectV(
|
|
|
60
147
|
const stream = new BinaryStream(opaque.buffer);
|
|
61
148
|
try {
|
|
62
149
|
object.decode(stream);
|
|
63
|
-
|
|
150
|
+
await resolveOpaqueStructureInExtentionObject(session, dataTypeManager, object);
|
|
151
|
+
return object;
|
|
64
152
|
} catch (err) {
|
|
65
153
|
warningLog("Constructor = ", Constructor.name);
|
|
66
154
|
warningLog("opaqueStructure = ", opaque?.nodeId?.toString());
|
|
67
|
-
warningLog("opaqueStructure = ",
|
|
155
|
+
warningLog("opaqueStructure = ", hexDump(opaque.buffer,132,100));
|
|
68
156
|
warningLog(hexDump(opaque.buffer));
|
|
69
157
|
warningLog("resolveDynamicExtensionObjectV err = ", err);
|
|
70
158
|
// try again for debugging
|
|
@@ -74,7 +162,7 @@ async function resolveDynamicExtensionObjectV(
|
|
|
74
162
|
} catch (err) {
|
|
75
163
|
warningLog("err", err);
|
|
76
164
|
warningLog("opaqueStructure = ", opaque.nodeId.toString());
|
|
77
|
-
warningLog("opaqueStructure = ", "0x" + opaque.buffer
|
|
165
|
+
warningLog("opaqueStructure = ", "0x" + hexDump(opaque.buffer,132,100));
|
|
78
166
|
warningLog(hexDump(opaque.buffer));
|
|
79
167
|
warningLog(dataTypeManager.toString());
|
|
80
168
|
throw err;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import "should";
|
|
2
|
+
import * as should from "should";
|
|
3
|
+
import { DataTypeFactory } from "node-opcua-factory";
|
|
4
|
+
import { resolveNodeId } from "node-opcua-nodeid";
|
|
5
|
+
import { parseBinaryXSDAsync } from "node-opcua-schemas";
|
|
6
|
+
import { MockProvider } from "node-opcua-schemas/test/mock_id_provider";
|
|
7
|
+
import { StructureType } from "node-opcua-types";
|
|
8
|
+
import { DataType } from "node-opcua-variant";
|
|
9
|
+
import { convertStructureTypeSchemaToStructureDefinition } from "..";
|
|
10
|
+
|
|
11
|
+
const idProvider = new MockProvider();
|
|
12
|
+
|
|
13
|
+
describe("test convertStructureTypeSchemaToStructureDefinition", function () {
|
|
14
|
+
it("should 1", async () => {
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
const schema1 = `
|
|
19
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
20
|
+
<opc:TypeDictionary xmlns:opc="http://opcfoundation.org/BinarySchema/" TargetNamespace="urn:eclipse:milo:opcua:server:demo" DefaultByteOrder="LittleEndian">
|
|
21
|
+
<opc:Import Namespace="http://opcfoundation.org/BinarySchema/"/>
|
|
22
|
+
<opc:EnumeratedType LengthInBits="32" Name="CustomEnumType">
|
|
23
|
+
<opc:EnumeratedValue Name="Field0" Value="0"/>
|
|
24
|
+
<opc:EnumeratedValue Name="Field1" Value="1"/>
|
|
25
|
+
<opc:EnumeratedValue Name="Field2" Value="2"/>
|
|
26
|
+
</opc:EnumeratedType>
|
|
27
|
+
<opc:StructuredType Name="CustomUnionType">
|
|
28
|
+
<opc:Field Name="SwitchField" TypeName="opc:UInt32"/>
|
|
29
|
+
<opc:Field Name="foo" TypeName="opc:UInt32" SwitchField="SwitchField" SwitchValue="1"/>
|
|
30
|
+
<opc:Field Name="bar" TypeName="opc:String" SwitchField="SwitchField" SwitchValue="2"/>
|
|
31
|
+
</opc:StructuredType>
|
|
32
|
+
<opc:StructuredType Name="CustomStructType">
|
|
33
|
+
<opc:Field Name="foo" TypeName="opc:String"/>
|
|
34
|
+
<opc:Field Name="bar" TypeName="opc:UInt32"/>
|
|
35
|
+
<opc:Field Name="baz" TypeName="opc:Boolean"/>
|
|
36
|
+
<opc:Field Name="fiz" TypeName="CustomEnumType"/>
|
|
37
|
+
<opc:Field Name="poc" TypeName="CustomUnionType"/>
|
|
38
|
+
</opc:StructuredType>
|
|
39
|
+
<opc:StructuredType BaseType="ua:ExtensionObject" Name="StructWithOnlyOptionals">
|
|
40
|
+
<opc:Field TypeName="opc:Bit" Name="OptionalInt32Specified"/>
|
|
41
|
+
<opc:Field TypeName="opc:Bit" Name="OptionalStringArraySpecified"/>
|
|
42
|
+
<opc:Field Length="30" TypeName="opc:Bit" Name="Reserved1"/>
|
|
43
|
+
<opc:Field SwitchField="OptionalInt32Specified" TypeName="opc:Int32" Name="OptionalInt32"/>
|
|
44
|
+
<opc:Field SwitchField="OptionalStringArraySpecified" TypeName="opc:Int32" Name="NoOfOptionalStringArray"/>
|
|
45
|
+
<opc:Field SwitchField="OptionalStringArraySpecified" LengthField="NoOfOptionalStringArray" TypeName="opc:CharArray" Name="OptionalStringArray"/>
|
|
46
|
+
</opc:StructuredType>
|
|
47
|
+
<opc:StructuredType BaseType="ua:ExtensionObject" Name="StructureWithOptionalFields">
|
|
48
|
+
<opc:Field TypeName="opc:Bit" Name="OptionalInt32Specified"/>
|
|
49
|
+
<opc:Field TypeName="opc:Bit" Name="OptionalStringArraySpecified"/>
|
|
50
|
+
<opc:Field Length="30" TypeName="opc:Bit" Name="Reserved1"/>
|
|
51
|
+
<opc:Field TypeName="opc:Int32" Name="MandatoryInt32"/>
|
|
52
|
+
<opc:Field SwitchField="OptionalInt32Specified" TypeName="opc:Int32" Name="OptionalInt32"/>
|
|
53
|
+
<opc:Field TypeName="opc:Int32" Name="NoOfMandatoryStringArray"/>
|
|
54
|
+
<opc:Field LengthField="NoOfMandatoryStringArray" TypeName="opc:CharArray" Name="MandatoryStringArray"/>
|
|
55
|
+
<opc:Field SwitchField="OptionalStringArraySpecified" TypeName="opc:Int32" Name="NoOfOptionalStringArray"/>
|
|
56
|
+
<opc:Field LengthField="NoOfOptionalStringArray" SwitchField="OptionalStringArraySpecified" TypeName="opc:CharArray" Name="OptionalStringArray"/>
|
|
57
|
+
</opc:StructuredType>
|
|
58
|
+
|
|
59
|
+
</opc:TypeDictionary>
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const dataTypeFactory = new DataTypeFactory([]);
|
|
63
|
+
await parseBinaryXSDAsync(schema1, idProvider, dataTypeFactory);
|
|
64
|
+
|
|
65
|
+
for (const f of dataTypeFactory.getStructureIterator()) {
|
|
66
|
+
const ss = convertStructureTypeSchemaToStructureDefinition(f.schema);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// --------------------------------------------------------------------------
|
|
71
|
+
const a = dataTypeFactory.getStructureInfoByTypeName("CustomUnionType");
|
|
72
|
+
const customUnionType = convertStructureTypeSchemaToStructureDefinition(a.schema);
|
|
73
|
+
console.log(customUnionType.toString());
|
|
74
|
+
|
|
75
|
+
customUnionType.structureType.should.eql(StructureType.Union);
|
|
76
|
+
customUnionType.baseDataType.toString().should.eql("ns=0;i=0");
|
|
77
|
+
|
|
78
|
+
customUnionType.fields!.length.should.eql(2);
|
|
79
|
+
|
|
80
|
+
customUnionType.fields![0].name!.should.eql("foo");
|
|
81
|
+
customUnionType.fields![0].dataType.toString().should.eql(resolveNodeId(DataType.UInt32).toString());
|
|
82
|
+
customUnionType.fields![0].valueRank.should.eql(-1);
|
|
83
|
+
customUnionType.fields![0].arrayDimensions!.should.eql([]);
|
|
84
|
+
customUnionType.fields![1].isOptional.should.eql(false);
|
|
85
|
+
should(customUnionType.fields![0].description.text).eql(null);
|
|
86
|
+
|
|
87
|
+
customUnionType.fields![1].name!.should.eql("bar");
|
|
88
|
+
customUnionType.fields![1].dataType.toString().should.eql(resolveNodeId(DataType.String).toString());
|
|
89
|
+
customUnionType.fields![1].valueRank.should.eql(-1);
|
|
90
|
+
customUnionType.fields![1].arrayDimensions!.should.eql([]);
|
|
91
|
+
customUnionType.fields![1].isOptional.should.eql(false);
|
|
92
|
+
should(customUnionType.fields![1].description.text).eql(null);
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
// ----------------------------------------------------------------------- CustomStructType
|
|
96
|
+
const b = dataTypeFactory.getStructureInfoByTypeName("CustomStructType");
|
|
97
|
+
const customStructType = convertStructureTypeSchemaToStructureDefinition(b.schema);
|
|
98
|
+
console.log(customStructType.toString());
|
|
99
|
+
|
|
100
|
+
customStructType.structureType.should.eql(StructureType.Structure);
|
|
101
|
+
customStructType.baseDataType.toString().should.eql("ns=0;i=0");
|
|
102
|
+
|
|
103
|
+
customStructType.fields!.length.should.eql(5);
|
|
104
|
+
|
|
105
|
+
customStructType.fields![0].name!.should.eql("foo");
|
|
106
|
+
customStructType.fields![0].dataType.toString().should.eql(resolveNodeId("String").toString());
|
|
107
|
+
customStructType.fields![1].name!.should.eql("bar");
|
|
108
|
+
customStructType.fields![1].dataType.toString().should.eql(resolveNodeId("UInt32").toString());
|
|
109
|
+
customStructType.fields![2].name!.should.eql("baz");
|
|
110
|
+
customStructType.fields![2].dataType.toString().should.eql(resolveNodeId("Boolean").toString());
|
|
111
|
+
customStructType.fields![3].name!.should.eql("fiz");
|
|
112
|
+
customStructType.fields![3].dataType.toString().should.eql("ns=0;i=0".toString());
|
|
113
|
+
customStructType.fields![4].name!.should.eql("poc");
|
|
114
|
+
customStructType.fields![4].dataType.toString().should.eql("ns=1;i=1".toString());
|
|
115
|
+
|
|
116
|
+
});
|
|
117
|
+
});
|