node-opcua-client-dynamic-extension-object 2.162.0 → 2.163.1
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 +4 -3
- package/dist/convert_data_type_definition_to_structuretype_schema.js +96 -66
- package/dist/convert_data_type_definition_to_structuretype_schema.js.map +1 -1
- package/dist/extra_data_type_manager.d.ts +10 -1
- package/dist/extra_data_type_manager.js +96 -2
- package/dist/extra_data_type_manager.js.map +1 -1
- package/dist/get_extension_object_constructor.js +4 -4
- package/dist/get_extension_object_constructor.js.map +1 -1
- package/dist/get_extra_data_type_manager.d.ts +2 -1
- package/dist/get_extra_data_type_manager.js +8 -2
- package/dist/get_extra_data_type_manager.js.map +1 -1
- package/dist/populate_data_type_manager.d.ts +2 -4
- package/dist/populate_data_type_manager.js +88 -81
- package/dist/populate_data_type_manager.js.map +1 -1
- package/dist/private/populate_data_type_manager_103.js +5 -4
- package/dist/private/populate_data_type_manager_103.js.map +1 -1
- package/dist/private/populate_data_type_manager_104.d.ts +1 -2
- package/dist/private/populate_data_type_manager_104.js +41 -18
- package/dist/private/populate_data_type_manager_104.js.map +1 -1
- package/dist/resolve_dynamic_extension_object.d.ts +2 -2
- package/dist/resolve_dynamic_extension_object.js +66 -91
- package/dist/resolve_dynamic_extension_object.js.map +1 -1
- package/package.json +12 -12
- package/source/convert_data_type_definition_to_structuretype_schema.ts +116 -79
- package/source/extra_data_type_manager.ts +113 -4
- package/source/get_extension_object_constructor.ts +5 -4
- package/source/get_extra_data_type_manager.ts +15 -7
- package/source/populate_data_type_manager.ts +99 -89
- package/source/private/populate_data_type_manager_103.ts +6 -4
- package/source/private/populate_data_type_manager_104.ts +67 -28
- package/source/resolve_dynamic_extension_object.ts +79 -110
|
@@ -4,7 +4,6 @@ import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "n
|
|
|
4
4
|
import { INodeId, NodeIdType, sameNodeId } from "node-opcua-nodeid";
|
|
5
5
|
import {
|
|
6
6
|
BitField,
|
|
7
|
-
DataTypeFactory,
|
|
8
7
|
EnumerationDefinitionSchema,
|
|
9
8
|
extractAllPossibleFields,
|
|
10
9
|
FieldCategory,
|
|
@@ -33,11 +32,12 @@ import {
|
|
|
33
32
|
EnumField
|
|
34
33
|
} from "node-opcua-types";
|
|
35
34
|
import { ExtensionObject } from "node-opcua-extension-object";
|
|
36
|
-
import { DataTypeAndEncodingId } from "node-opcua-schemas";
|
|
35
|
+
import { DataTypeAndEncodingId, createDynamicObjectConstructor } from "node-opcua-schemas";
|
|
37
36
|
//
|
|
38
37
|
import { DataType } from "node-opcua-variant";
|
|
39
38
|
import { DataTypeIds } from "node-opcua-constants";
|
|
40
39
|
import { _findEncodings } from "./private/find_encodings";
|
|
40
|
+
import { ExtraDataTypeManager } from "./extra_data_type_manager";
|
|
41
41
|
|
|
42
42
|
const debugLog = make_debugLog(__filename);
|
|
43
43
|
const doDebug = checkDebugFlag(__filename);
|
|
@@ -48,7 +48,7 @@ export interface CacheForFieldResolution {
|
|
|
48
48
|
fieldTypeName: string;
|
|
49
49
|
schema: TypeDefinition;
|
|
50
50
|
category: FieldCategory;
|
|
51
|
-
|
|
51
|
+
allowSubTypes?: boolean;
|
|
52
52
|
dataType?: NodeId;
|
|
53
53
|
}
|
|
54
54
|
export type ResolveReject<T> = [
|
|
@@ -125,7 +125,6 @@ async function findSuperType(session: IBasicSessionAsync2, dataTypeNodeId: NodeI
|
|
|
125
125
|
}
|
|
126
126
|
async function findDataTypeCategory(
|
|
127
127
|
session: IBasicSessionAsync2,
|
|
128
|
-
dataTypeFactory: DataTypeFactory,
|
|
129
128
|
cache: ICache,
|
|
130
129
|
dataTypeNodeId: NodeId,
|
|
131
130
|
): Promise<FieldCategory> {
|
|
@@ -155,7 +154,7 @@ async function findDataTypeCategory(
|
|
|
155
154
|
return category;
|
|
156
155
|
}
|
|
157
156
|
// must drill down ...
|
|
158
|
-
return await findDataTypeCategory(session,
|
|
157
|
+
return await findDataTypeCategory(session, cache, subTypeNodeId);
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
async function findDataTypeBasicType(
|
|
@@ -210,12 +209,13 @@ async function readBrowseNameWithCache(session: IBasicSessionAsync, nodeId: Node
|
|
|
210
209
|
async function resolve2(
|
|
211
210
|
session: IBasicSessionAsync2,
|
|
212
211
|
dataTypeNodeId: NodeId,
|
|
213
|
-
|
|
212
|
+
dataTypeManager: ExtraDataTypeManager,
|
|
214
213
|
fieldTypeName: string,
|
|
215
214
|
cache: ICache
|
|
216
215
|
): Promise<{ schema: TypeDefinition | undefined; category: FieldCategory }> {
|
|
217
216
|
|
|
218
|
-
const
|
|
217
|
+
const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
|
|
218
|
+
const category = await findDataTypeCategory(session, cache, dataTypeNodeId);
|
|
219
219
|
doDebug && debugLog(" type " + fieldTypeName + " has not been seen yet, let resolve it => (category = ", category, " )");
|
|
220
220
|
|
|
221
221
|
let schema: TypeDefinition | undefined = undefined;
|
|
@@ -274,12 +274,15 @@ async function resolve2(
|
|
|
274
274
|
fieldTypeName,
|
|
275
275
|
definition,
|
|
276
276
|
null,
|
|
277
|
-
|
|
277
|
+
dataTypeManager,
|
|
278
278
|
isAbstract,
|
|
279
279
|
cache
|
|
280
280
|
);
|
|
281
|
+
const structuredTypeSchema = schema as IStructuredTypeSchema;
|
|
282
|
+
if (structuredTypeSchema.encodingDefaultBinary && !dataTypeFactory.hasConstructor(structuredTypeSchema.encodingDefaultBinary)) {
|
|
283
|
+
createDynamicObjectConstructor(structuredTypeSchema, dataTypeFactory);
|
|
284
|
+
}
|
|
281
285
|
}
|
|
282
|
-
// xx const schema1 = dataTypeFactory.getStructuredTypeSchema(fieldTypeName);
|
|
283
286
|
}
|
|
284
287
|
break;
|
|
285
288
|
}
|
|
@@ -304,19 +307,19 @@ const isExtensionObject = async (session: IBasicSessionAsync2, dataTypeNodeId: N
|
|
|
304
307
|
return await isExtensionObject(session, baseDataType, cache);
|
|
305
308
|
};
|
|
306
309
|
|
|
307
|
-
// eslint-disable-next-line max-statements
|
|
308
310
|
async function resolveFieldType(
|
|
309
311
|
session: IBasicSessionAsync2,
|
|
310
312
|
dataTypeNodeId: NodeId,
|
|
311
|
-
|
|
313
|
+
dataTypeManager: ExtraDataTypeManager,
|
|
312
314
|
cache: ICache
|
|
313
315
|
): Promise<CacheForFieldResolution | null> {
|
|
314
316
|
|
|
315
317
|
|
|
316
318
|
return await memoize(cache, "fieldResolution", dataTypeNodeId, async () => {
|
|
317
319
|
|
|
320
|
+
const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
|
|
321
|
+
|
|
318
322
|
if (dataTypeNodeId.namespace === 0 && dataTypeNodeId.value === DataTypeIds.Structure) {
|
|
319
|
-
// ERN return null;
|
|
320
323
|
const category: FieldCategory = FieldCategory.complex;
|
|
321
324
|
const fieldTypeName = "Structure";
|
|
322
325
|
const schema = ExtensionObject.schema;
|
|
@@ -324,7 +327,7 @@ async function resolveFieldType(
|
|
|
324
327
|
category,
|
|
325
328
|
fieldTypeName,
|
|
326
329
|
schema,
|
|
327
|
-
|
|
330
|
+
allowSubTypes: true,
|
|
328
331
|
dataType: coerceNodeId(DataType.ExtensionObject)
|
|
329
332
|
};
|
|
330
333
|
}
|
|
@@ -333,7 +336,7 @@ async function resolveFieldType(
|
|
|
333
336
|
const v3: CacheForFieldResolution = {
|
|
334
337
|
category: FieldCategory.basic,
|
|
335
338
|
fieldTypeName: "Variant",
|
|
336
|
-
schema:
|
|
339
|
+
schema: dataTypeManager.getBuiltInType("Variant")
|
|
337
340
|
};
|
|
338
341
|
return v3;
|
|
339
342
|
}
|
|
@@ -342,9 +345,10 @@ async function resolveFieldType(
|
|
|
342
345
|
return (await session.read({ nodeId: dataTypeNodeId, attributeId: AttributeIds.IsAbstract })).value.value
|
|
343
346
|
};
|
|
344
347
|
|
|
345
|
-
const [isAbstract, fieldTypeName] = await Promise.all([
|
|
348
|
+
const [isAbstract, fieldTypeName, _unusedSuperType] = await Promise.all([
|
|
346
349
|
readIsAbstract(dataTypeNodeId),
|
|
347
|
-
readBrowseNameWithCache(session, dataTypeNodeId, cache)
|
|
350
|
+
readBrowseNameWithCache(session, dataTypeNodeId, cache),
|
|
351
|
+
findSuperType(session, dataTypeNodeId, cache)
|
|
348
352
|
]);
|
|
349
353
|
|
|
350
354
|
if (isAbstract) {
|
|
@@ -358,7 +362,7 @@ async function resolveFieldType(
|
|
|
358
362
|
category: FieldCategory.complex,
|
|
359
363
|
fieldTypeName: fieldTypeName,
|
|
360
364
|
schema: ExtensionObject.schema,
|
|
361
|
-
|
|
365
|
+
allowSubTypes: true,
|
|
362
366
|
dataType: dataTypeNodeId
|
|
363
367
|
};
|
|
364
368
|
return v3;
|
|
@@ -367,8 +371,8 @@ async function resolveFieldType(
|
|
|
367
371
|
const v3: CacheForFieldResolution = {
|
|
368
372
|
category: FieldCategory.basic,
|
|
369
373
|
fieldTypeName: fieldTypeName,
|
|
370
|
-
schema:
|
|
371
|
-
|
|
374
|
+
schema: dataTypeManager.getBuiltInType("Variant"),
|
|
375
|
+
allowSubTypes: true,
|
|
372
376
|
dataType: dataTypeNodeId
|
|
373
377
|
};
|
|
374
378
|
return v3;
|
|
@@ -389,7 +393,7 @@ async function resolveFieldType(
|
|
|
389
393
|
schema = dataTypeFactory.getEnumeration(fieldTypeName!)!;
|
|
390
394
|
} else {
|
|
391
395
|
doDebug && debugLog(" type " + fieldTypeName + " has not been seen yet, let resolve it");
|
|
392
|
-
const res = await resolve2(session, dataTypeNodeId,
|
|
396
|
+
const res = await resolve2(session, dataTypeNodeId, dataTypeManager, fieldTypeName, cache);
|
|
393
397
|
schema = res.schema;
|
|
394
398
|
category = res.category;
|
|
395
399
|
}
|
|
@@ -414,13 +418,12 @@ async function _setupEncodings(
|
|
|
414
418
|
session: IBasicSessionAsync & IBasicSessionBrowseNextAsync,
|
|
415
419
|
dataTypeNodeId: NodeId,
|
|
416
420
|
dataTypeDescription: IDataTypeDescriptionMini | null,
|
|
417
|
-
schema: IStructuredTypeSchema
|
|
421
|
+
schema: IStructuredTypeSchema,
|
|
422
|
+
isAbstract: boolean
|
|
418
423
|
): Promise<IStructuredTypeSchema> {
|
|
419
|
-
// read abstract flag
|
|
420
|
-
const isAbstractDV = await session.read({ nodeId: dataTypeNodeId, attributeId: AttributeIds.IsAbstract });
|
|
421
424
|
schema.dataTypeNodeId = dataTypeNodeId;
|
|
422
425
|
|
|
423
|
-
if (
|
|
426
|
+
if (!isAbstract) {
|
|
424
427
|
const encodings = (dataTypeDescription && dataTypeDescription.encodings) || (await _findEncodings(session, dataTypeNodeId));
|
|
425
428
|
schema.encodingDefaultBinary = makeExpandedNodeId(encodings.binaryEncodingNodeId);
|
|
426
429
|
schema.encodingDefaultXml = makeExpandedNodeId(encodings.xmlEncodingNodeId);
|
|
@@ -501,27 +504,27 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
|
501
504
|
name: string,
|
|
502
505
|
definition: DataTypeDefinition,
|
|
503
506
|
dataTypeDescription: IDataTypeDescriptionMini | null,
|
|
504
|
-
|
|
507
|
+
dataTypeManager: ExtraDataTypeManager,
|
|
505
508
|
isAbstract: boolean,
|
|
506
509
|
cache: ICache
|
|
507
510
|
): Promise<IStructuredTypeSchema> {
|
|
508
|
-
|
|
511
|
+
|
|
509
512
|
return await nonReentrant(cache, "convertDataTypeDefinitionToStructureTypeSchema", dataTypeNodeId, async () => {
|
|
510
513
|
|
|
511
|
-
|
|
514
|
+
|
|
512
515
|
if (definition instanceof StructureDefinition) {
|
|
516
|
+
|
|
517
|
+
const dataTypeFactory = dataTypeManager.getDataTypeFactoryForNamespace(dataTypeNodeId.namespace);
|
|
518
|
+
|
|
513
519
|
let fieldCountToIgnore = 0;
|
|
514
|
-
|
|
520
|
+
|
|
521
|
+
const structureInfo = dataTypeManager.getStructureInfoForDataType(definition.baseDataType);
|
|
515
522
|
const baseSchema: IStructuredTypeSchema | undefined | null = structureInfo?.schema;
|
|
516
523
|
|
|
517
524
|
if (baseSchema) {
|
|
518
525
|
const possibleFields = extractAllPossibleFields(baseSchema);
|
|
519
526
|
fieldCountToIgnore += possibleFields.length;
|
|
520
527
|
}
|
|
521
|
-
// while (base && !(base.dataTypeNodeId.value === DataType.ExtensionObject && base.dataTypeNodeId.namespace === 0)) {
|
|
522
|
-
// fieldCountToIgnore += base..length;
|
|
523
|
-
// base = base.getBaseSchema();
|
|
524
|
-
// }
|
|
525
528
|
|
|
526
529
|
const fields: FieldInterfaceOptions[] = [];
|
|
527
530
|
|
|
@@ -536,6 +539,9 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
|
536
539
|
break;
|
|
537
540
|
case StructureType.Structure:
|
|
538
541
|
case StructureType.StructureWithOptionalFields:
|
|
542
|
+
// new in 1.05
|
|
543
|
+
case StructureType.StructureWithSubtypedValues:
|
|
544
|
+
case StructureType.UnionWithSubtypedValues:
|
|
539
545
|
break;
|
|
540
546
|
}
|
|
541
547
|
|
|
@@ -548,51 +554,67 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
|
548
554
|
|
|
549
555
|
if (definition.fields) {
|
|
550
556
|
|
|
551
|
-
|
|
557
|
+
// pre-fetch all dataTypes in parallel
|
|
558
|
+
const dataTypesToResolve: NodeId[] = [];
|
|
559
|
+
const seen = new Set<string>();
|
|
560
|
+
if (!sameNodeId(definition.baseDataType, dataTypeNodeId)) {
|
|
561
|
+
dataTypesToResolve.push(definition.baseDataType);
|
|
562
|
+
seen.add(definition.baseDataType.toString());
|
|
563
|
+
}
|
|
552
564
|
for (let i = fieldCountToIgnore; i < definition.fields.length; i++) {
|
|
553
565
|
const fieldD = definition.fields[i];
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
field.category =
|
|
588
|
-
field.schema = schema;
|
|
589
|
-
field.dataType = dataType || fieldD.dataType;
|
|
590
|
-
field.allowSubType = allowSubType || false;
|
|
591
|
-
field.basicDataType = await findBasicDataTypeEx(session, field.dataType, cache);
|
|
566
|
+
const key = fieldD.dataType.toString();
|
|
567
|
+
if (seen.has(key)) continue;
|
|
568
|
+
seen.add(key);
|
|
569
|
+
if (sameNodeId(fieldD.dataType, dataTypeNodeId)) continue;
|
|
570
|
+
dataTypesToResolve.push(fieldD.dataType);
|
|
571
|
+
}
|
|
572
|
+
await Promise.all([
|
|
573
|
+
...dataTypesToResolve.map((dataType) => resolveFieldType(session, dataType, dataTypeManager, cache)),
|
|
574
|
+
...dataTypesToResolve.map((dataType) => findBasicDataTypeEx(session, dataType, cache)),
|
|
575
|
+
!isAbstract ? _findEncodings(session, dataTypeNodeId) : Promise.resolve(null)
|
|
576
|
+
]);
|
|
577
|
+
|
|
578
|
+
const results = await Promise.all(definition.fields.slice(fieldCountToIgnore).map(async (fieldD) => {
|
|
579
|
+
const isSelf = sameNodeId(fieldD.dataType, dataTypeNodeId);
|
|
580
|
+
// Pre-fetch async data for each field in parallel
|
|
581
|
+
const rt = isSelf ? null : await resolveFieldType(session, fieldD.dataType, dataTypeManager, cache);
|
|
582
|
+
const basicDataType = rt ? await findBasicDataTypeEx(session, rt.dataType || fieldD.dataType, cache) : (isSelf ? DataType.ExtensionObject : undefined);
|
|
583
|
+
const fieldTypeNameForSelfRef = (isSelf)
|
|
584
|
+
? await readBrowseNameWithCache(session, dataTypeNodeId, cache)
|
|
585
|
+
: undefined;
|
|
586
|
+
return { fieldD, rt, basicDataType, fieldTypeNameForSelfRef };
|
|
587
|
+
}));
|
|
588
|
+
|
|
589
|
+
for (const { fieldD, rt, basicDataType, fieldTypeNameForSelfRef } of results) {
|
|
590
|
+
|
|
591
|
+
let field: FieldInterfaceOptions | undefined;
|
|
592
|
+
let currentAllowSubTypes: boolean | undefined;
|
|
593
|
+
|
|
594
|
+
({ field, switchBit, switchValue, allowSubTypes: currentAllowSubTypes } = createField(definition, fieldD, switchBit, bitFields, isUnion, switchValue));
|
|
595
|
+
|
|
596
|
+
if (fieldD.dataType.value === dataTypeNodeId.value && fieldD.dataType.namespace === dataTypeNodeId.namespace) {
|
|
597
|
+
// this is a structure with a field of the same type
|
|
598
|
+
// push an empty placeholder that we will fill later
|
|
599
|
+
(field.fieldType = fieldTypeNameForSelfRef!), (field.category = FieldCategory.complex);
|
|
592
600
|
fields.push(field);
|
|
593
|
-
|
|
594
|
-
|
|
601
|
+
const capturedField = field;
|
|
602
|
+
postActions.push((schema: IStructuredTypeSchema) => {
|
|
603
|
+
capturedField.schema = schema;
|
|
604
|
+
});
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
const resolvedField = rt!;
|
|
595
608
|
|
|
609
|
+
const { schema, category, fieldTypeName, dataType } = resolvedField;
|
|
610
|
+
|
|
611
|
+
field.fieldType = fieldTypeName!;
|
|
612
|
+
field.category = category;
|
|
613
|
+
field.schema = schema;
|
|
614
|
+
field.dataType = dataType || fieldD.dataType;
|
|
615
|
+
field.allowSubTypes = currentAllowSubTypes;
|
|
616
|
+
field.basicDataType = basicDataType;
|
|
617
|
+
fields.push(field);
|
|
596
618
|
}
|
|
597
619
|
|
|
598
620
|
}
|
|
@@ -602,7 +624,8 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
|
602
624
|
definition.baseDataType = resolveNodeId(DataTypeIds.Union); // aka DataTypeIds.Union
|
|
603
625
|
}
|
|
604
626
|
|
|
605
|
-
const a = await resolveFieldType(session, definition.baseDataType,
|
|
627
|
+
const a = await resolveFieldType(session, definition.baseDataType, dataTypeManager, cache);
|
|
628
|
+
|
|
606
629
|
const baseType = a ? a.fieldTypeName : isUnion ? "Union" : "ExtensionObject";
|
|
607
630
|
|
|
608
631
|
const os = new StructuredTypeSchema({
|
|
@@ -612,7 +635,7 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
|
612
635
|
name,
|
|
613
636
|
dataTypeFactory
|
|
614
637
|
});
|
|
615
|
-
const structuredTypeSchema = await _setupEncodings(session, dataTypeNodeId, dataTypeDescription, os);
|
|
638
|
+
const structuredTypeSchema = await _setupEncodings(session, dataTypeNodeId, dataTypeDescription, os, isAbstract);
|
|
616
639
|
|
|
617
640
|
postActions.forEach((action) => action(structuredTypeSchema));
|
|
618
641
|
|
|
@@ -621,28 +644,42 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
|
621
644
|
}
|
|
622
645
|
throw new Error("Not Implemented");
|
|
623
646
|
});
|
|
647
|
+
|
|
624
648
|
function createField(
|
|
649
|
+
definition: StructureDefinition,
|
|
625
650
|
fieldD: StructureField,
|
|
651
|
+
|
|
626
652
|
switchBit: number,
|
|
627
653
|
bitFields: BitField[] | undefined,
|
|
628
654
|
isUnion: boolean,
|
|
629
655
|
switchValue: number
|
|
630
|
-
): { field: FieldInterfaceOptions; switchBit: number; switchValue: number } {
|
|
656
|
+
): { field: FieldInterfaceOptions; switchBit: number; switchValue: number, allowSubTypes?: boolean } {
|
|
657
|
+
|
|
631
658
|
const field: FieldInterfaceOptions = {
|
|
632
659
|
fieldType: "",
|
|
633
660
|
name: fieldD.name!,
|
|
634
661
|
schema: undefined
|
|
635
662
|
};
|
|
636
663
|
|
|
664
|
+
const definitionAllowSubTypes =
|
|
665
|
+
definition.structureType === StructureType.StructureWithSubtypedValues
|
|
666
|
+
|| definition.structureType === StructureType.UnionWithSubtypedValues;
|
|
667
|
+
|
|
637
668
|
if (fieldD.isOptional) {
|
|
638
|
-
|
|
639
|
-
|
|
669
|
+
// Optional has a special handling
|
|
670
|
+
if (!definitionAllowSubTypes) {
|
|
671
|
+
// we are in a true optional field structure
|
|
672
|
+
field.switchBit = switchBit++;
|
|
673
|
+
bitFields?.push({ name: fieldD.name! + "Specified", length: 1 });
|
|
674
|
+
}
|
|
640
675
|
}
|
|
641
676
|
if (isUnion) {
|
|
642
677
|
field.switchValue = switchValue;
|
|
643
678
|
switchValue += 1;
|
|
644
679
|
}
|
|
645
680
|
|
|
681
|
+
const allowSubTypes = definitionAllowSubTypes && fieldD.isOptional;
|
|
682
|
+
|
|
646
683
|
// (fieldD.valueRank === -1 || fieldD.valueRank === 1 || fieldD.valueRank === 0);
|
|
647
684
|
|
|
648
685
|
if (fieldD.valueRank >= 1) {
|
|
@@ -651,6 +688,6 @@ export async function convertDataTypeDefinitionToStructureTypeSchema(
|
|
|
651
688
|
} else {
|
|
652
689
|
field.isArray = false;
|
|
653
690
|
}
|
|
654
|
-
return { field, switchBit, switchValue };
|
|
691
|
+
return { field, switchBit, switchValue, allowSubTypes };
|
|
655
692
|
}
|
|
656
693
|
}
|
|
@@ -4,18 +4,26 @@
|
|
|
4
4
|
import { format } from "util";
|
|
5
5
|
|
|
6
6
|
import { assert } from "node-opcua-assert";
|
|
7
|
-
import {
|
|
7
|
+
import { BrowseDirection, NodeClassMask, ResultMask } from "node-opcua-data-model";
|
|
8
|
+
import { ConstructorFunc, DataTypeFactory, getStandardDataTypeFactory, StructureInfo } from "node-opcua-factory";
|
|
8
9
|
import { NodeId } from "node-opcua-nodeid";
|
|
10
|
+
import { IBasicSessionAsync2 } from "node-opcua-pseudo-session";
|
|
9
11
|
import { AnyConstructorFunc } from "node-opcua-schemas";
|
|
10
12
|
|
|
11
13
|
export class ExtraDataTypeManager {
|
|
12
14
|
public namespaceArray: string[] = [];
|
|
13
15
|
private dataTypeFactoryMapByNamespace: { [key: number]: DataTypeFactory } = {};
|
|
16
|
+
private _session: IBasicSessionAsync2 | null = null;
|
|
17
|
+
private _pendingExtractions: Map<string, Promise<StructureInfo>> = new Map();
|
|
14
18
|
|
|
15
19
|
constructor() {
|
|
16
20
|
/* */
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
public setSession(session: IBasicSessionAsync2): void {
|
|
24
|
+
this._session = session;
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
public setNamespaceArray(namespaceArray: string[]): void {
|
|
20
28
|
this.namespaceArray = namespaceArray;
|
|
21
29
|
}
|
|
@@ -34,7 +42,9 @@ export class ExtraDataTypeManager {
|
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
public getDataTypeFactoryForNamespace(namespaceIndex: number): DataTypeFactory {
|
|
37
|
-
|
|
45
|
+
if (namespaceIndex === 0) {
|
|
46
|
+
return getStandardDataTypeFactory();
|
|
47
|
+
}
|
|
38
48
|
return this.dataTypeFactoryMapByNamespace[namespaceIndex];
|
|
39
49
|
}
|
|
40
50
|
|
|
@@ -45,31 +55,130 @@ export class ExtraDataTypeManager {
|
|
|
45
55
|
return this.dataTypeFactoryMapByNamespace[namespaceIndex];
|
|
46
56
|
}
|
|
47
57
|
|
|
58
|
+
public getBuiltInType(fieldTypeName: string) {
|
|
59
|
+
// fallback to standard factory
|
|
60
|
+
const standardDataTypeFactory = getStandardDataTypeFactory();
|
|
61
|
+
if (standardDataTypeFactory.hasBuiltInType(fieldTypeName)) {
|
|
62
|
+
return standardDataTypeFactory.getBuiltInType(fieldTypeName);
|
|
63
|
+
}
|
|
64
|
+
throw new Error("Cannot find built-in type " + fieldTypeName);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public getStructureInfoForDataType(dataTypeNodeId: NodeId): StructureInfo | null {
|
|
68
|
+
const dataTypeFactory = this.getDataTypeFactory(dataTypeNodeId.namespace);
|
|
69
|
+
if (!dataTypeFactory) {
|
|
70
|
+
throw new Error("cannot find dataFactory for namespace=" + dataTypeNodeId.namespace + " when requested for " + dataTypeNodeId.toString());
|
|
71
|
+
}
|
|
72
|
+
return dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public async getStructureInfoForDataTypeAsync(dataTypeNodeId: NodeId): Promise<StructureInfo> {
|
|
76
|
+
const structureInfo = this.getStructureInfoForDataType(dataTypeNodeId);
|
|
77
|
+
if (structureInfo) {
|
|
78
|
+
return structureInfo;
|
|
79
|
+
}
|
|
80
|
+
if (!this._session) {
|
|
81
|
+
throw new Error("Session is required for lazy loading. Call setSession first.");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const key = dataTypeNodeId.toString();
|
|
85
|
+
if (this._pendingExtractions.has(key)) {
|
|
86
|
+
return await this._pendingExtractions.get(key)!;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const promise = (async () => {
|
|
90
|
+
// We'll need to make sure it's accessible and correctly used.
|
|
91
|
+
// For now, let's assume we can import it or move it.
|
|
92
|
+
// Actually, populate_data_type_manager_104.ts exports readDataTypeDefinitionAndBuildType
|
|
93
|
+
const { readDataTypeDefinitionAndBuildType } = require("./private/populate_data_type_manager_104");
|
|
94
|
+
const cache = {}; // local cache for this extraction
|
|
95
|
+
await readDataTypeDefinitionAndBuildType(this._session!, dataTypeNodeId, undefined, this, cache);
|
|
96
|
+
|
|
97
|
+
const info = this.getStructureInfoForDataType(dataTypeNodeId);
|
|
98
|
+
if (!info) {
|
|
99
|
+
throw new Error("Failed to extract data type structure for " + dataTypeNodeId.toString());
|
|
100
|
+
}
|
|
101
|
+
return info;
|
|
102
|
+
})();
|
|
103
|
+
|
|
104
|
+
this._pendingExtractions.set(key, promise);
|
|
105
|
+
try {
|
|
106
|
+
return await promise;
|
|
107
|
+
} finally {
|
|
108
|
+
this._pendingExtractions.delete(key);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
48
112
|
public getExtensionObjectConstructorFromDataType(dataTypeNodeId: NodeId): AnyConstructorFunc {
|
|
49
113
|
const dataTypeFactory = this.getDataTypeFactory(dataTypeNodeId.namespace);
|
|
50
114
|
if (!dataTypeFactory) {
|
|
51
115
|
throw new Error("cannot find dataFactory for namespace=" + dataTypeNodeId.namespace + " when requested for " + dataTypeNodeId.toString());
|
|
52
116
|
}
|
|
53
117
|
// find schema corresponding to dataTypeNodeId in typeDictionary
|
|
54
|
-
const
|
|
118
|
+
const structureInfo = dataTypeFactory.findStructureInfoForDataType(dataTypeNodeId);
|
|
119
|
+
const Constructor = structureInfo.constructor;
|
|
55
120
|
if (!Constructor) {
|
|
56
121
|
throw new Error("Cannot find Extension Object Constructor for Abstract dataType");
|
|
57
122
|
}
|
|
58
123
|
return Constructor;
|
|
59
124
|
}
|
|
60
125
|
|
|
126
|
+
public async getExtensionObjectConstructorFromDataTypeAsync(dataTypeNodeId: NodeId): Promise<AnyConstructorFunc> {
|
|
127
|
+
const structureInfo = await this.getStructureInfoForDataTypeAsync(dataTypeNodeId);
|
|
128
|
+
const Constructor = structureInfo.constructor;
|
|
129
|
+
if (!Constructor) {
|
|
130
|
+
throw new Error("Cannot find Extension Object Constructor for Abstract dataType " + dataTypeNodeId.toString());
|
|
131
|
+
}
|
|
132
|
+
return Constructor;
|
|
133
|
+
}
|
|
134
|
+
|
|
61
135
|
public getExtensionObjectConstructorFromBinaryEncoding(binaryEncodingNodeId: NodeId): ConstructorFunc {
|
|
62
136
|
const dataTypeFactory = this.getDataTypeFactoryForNamespace(binaryEncodingNodeId.namespace);
|
|
63
137
|
const Constructor = dataTypeFactory.getConstructor(binaryEncodingNodeId);
|
|
64
138
|
if (!Constructor) {
|
|
65
139
|
throw new Error(
|
|
66
140
|
"getExtensionObjectConstructorFromBinaryEncoding cannot find constructor for binaryEncoding " +
|
|
67
|
-
|
|
141
|
+
binaryEncodingNodeId.toString()
|
|
68
142
|
);
|
|
69
143
|
}
|
|
70
144
|
return Constructor;
|
|
71
145
|
}
|
|
72
146
|
|
|
147
|
+
public async getExtensionObjectConstructorFromBinaryEncodingAsync(binaryEncodingNodeId: NodeId): Promise<ConstructorFunc> {
|
|
148
|
+
const dataTypeFactory = this.getDataTypeFactoryForNamespace(binaryEncodingNodeId.namespace);
|
|
149
|
+
let Constructor = dataTypeFactory.getConstructor(binaryEncodingNodeId);
|
|
150
|
+
if (Constructor) {
|
|
151
|
+
return Constructor;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!this._session) {
|
|
155
|
+
throw new Error("Session is required for lazy loading. Call setSession first.");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Need to find the DataType for this binary encoding
|
|
159
|
+
const browseResult = await this._session.browse({
|
|
160
|
+
nodeId: binaryEncodingNodeId,
|
|
161
|
+
referenceTypeId: "HasEncoding",
|
|
162
|
+
browseDirection: BrowseDirection.Inverse,
|
|
163
|
+
includeSubtypes: false,
|
|
164
|
+
nodeClassMask: NodeClassMask.DataType,
|
|
165
|
+
resultMask: ResultMask.BrowseName
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (browseResult.statusCode.isNotGood() || !browseResult.references || browseResult.references.length !== 1) {
|
|
169
|
+
throw new Error("Cannot find DataType for binary encoding " + binaryEncodingNodeId.toString());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const dataTypeNodeId = browseResult.references[0].nodeId;
|
|
173
|
+
await this.getStructureInfoForDataTypeAsync(dataTypeNodeId);
|
|
174
|
+
|
|
175
|
+
Constructor = dataTypeFactory.getConstructor(binaryEncodingNodeId);
|
|
176
|
+
if (!Constructor) {
|
|
177
|
+
throw new Error("Cannot find constructor for binary encoding " + binaryEncodingNodeId.toString() + " after extraction");
|
|
178
|
+
}
|
|
179
|
+
return Constructor;
|
|
180
|
+
}
|
|
181
|
+
|
|
73
182
|
public toString(): string {
|
|
74
183
|
const l: string[] = [];
|
|
75
184
|
function write(...args: [any, ...any[]]) {
|
|
@@ -10,9 +10,10 @@ import { readDataTypeDefinitionAndBuildType } from "./private/populate_data_type
|
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
export async function getExtensionObjectConstructor(session: IBasicSessionAsync2, dataTypeNodeId: NodeId): Promise<AnyConstructorFunc> {
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
const dataTypeManager = await getExtraDataTypeManager(session);
|
|
14
15
|
|
|
15
|
-
const dataTypeFactory =
|
|
16
|
+
const dataTypeFactory = dataTypeManager.getDataTypeFactory(dataTypeNodeId.namespace);
|
|
16
17
|
const structureInfo = dataTypeFactory.getStructureInfoForDataType(dataTypeNodeId);
|
|
17
18
|
if (structureInfo) {
|
|
18
19
|
return structureInfo.constructor as unknown as AnyConstructorFunc;
|
|
@@ -22,7 +23,7 @@ export async function getExtensionObjectConstructor(session: IBasicSessionAsync2
|
|
|
22
23
|
attributeId: AttributeIds.BrowseName
|
|
23
24
|
});
|
|
24
25
|
const browseName = dataValue.value.value as QualifiedName;
|
|
25
|
-
await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, browseName.name!,
|
|
26
|
+
await readDataTypeDefinitionAndBuildType(session, dataTypeNodeId, browseName.name!, dataTypeManager, {});
|
|
26
27
|
|
|
27
|
-
return await
|
|
28
|
+
return await dataTypeManager.getExtensionObjectConstructorFromDataType(dataTypeNodeId);
|
|
28
29
|
}
|