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.
Files changed (30) hide show
  1. package/dist/convert_data_type_definition_to_structuretype_schema.d.ts +3 -3
  2. package/dist/convert_data_type_definition_to_structuretype_schema.js +16 -12
  3. package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
  4. package/dist/convert_structuretype_schema_to_structure_definition.d.ts +3 -0
  5. package/dist/convert_structuretype_schema_to_structure_definition.js +54 -0
  6. package/dist/convert_structuretype_schema_to_structure_definition.js.map +1 -0
  7. package/dist/extra_data_type_manager.js +4 -1
  8. package/dist/extra_data_type_manager.js.map +1 -1
  9. package/dist/get_extension_object_constructor.js +3 -3
  10. package/dist/get_extension_object_constructor.js.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/private/populate_data_type_manager_103.js +62 -23
  15. package/dist/private/populate_data_type_manager_103.js.map +1 -1
  16. package/dist/private/populate_data_type_manager_104.js +24 -9
  17. package/dist/private/populate_data_type_manager_104.js.map +1 -1
  18. package/dist/resolve_dynamic_extension_object.d.ts +2 -0
  19. package/dist/resolve_dynamic_extension_object.js +79 -6
  20. package/dist/resolve_dynamic_extension_object.js.map +1 -1
  21. package/package.json +18 -18
  22. package/source/convert_data_type_definition_to_structuretype_schema.ts +28 -16
  23. package/source/convert_structuretype_schema_to_structure_definition.ts +51 -0
  24. package/source/extra_data_type_manager.ts +4 -1
  25. package/source/get_extension_object_constructor.ts +3 -3
  26. package/source/index.ts +1 -0
  27. package/source/private/populate_data_type_manager_103.ts +73 -28
  28. package/source/private/populate_data_type_manager_104.ts +26 -10
  29. package/source/resolve_dynamic_extension_object.ts +94 -6
  30. 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 = 0;
126
- for (const result3 of results3) {
127
- const dataTypeDescription = dataTypeDescriptions[i++];
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: makeNodeClassMask("DataType"),
148
- nodeId: ref.nodeId.toString(),
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 = 0;
159
- for (const result4 of results4) {
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
- console.log("What's going on ?", result4.toString());
174
+ errorLog("What's going on ?", result4.toString(), "result4.references.length = ", result4.references.length);
165
175
  }
166
176
 
167
- for (const ref of result4.references) {
168
- const dataTypeNodeId = ref.nodeId;
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
- dataTypeNodeIds.push(dataTypeNodeId);
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
- const dataTypeDescription = dataTypeDescriptions[i++];
173
- dataTypeDescription.encodings!.dataTypeNodeId = dataTypeNodeId;
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.hasStructuredType(className)) {
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 (isDictionaryDeprecated || rawSchema.length === 0) {
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 constructor = dataTypeFactory.getStructureTypeConstructor(name);
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 constructor();
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
- attributeId: AttributeIds.DataTypeDefinition,
30
- nodeId: dataTypeNodeId
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
- createDynamicObjectConstructor(schema, dataTypeFactory);
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.getConstructorForDataType(dataTypeNodeId)) {
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.getConstructorForDataType(dataTypeNodeId)) {
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
- return dataTypeFactory.getConstructorForDataType(dataTypeNodeId)!;
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
- return object as ExtensionObject;
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 = ", "0x" + opaque?.buffer?.toString("hex"));
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.toString("hex"));
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
+ });