node-opcua-address-space 2.181.0 → 2.182.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/api/helpers/ensure_secure_access.ts +53 -2
- package/api/index.ts +30 -1
- package/api/loader/generateAddressSpaceRaw.ts +44 -139
- package/api/loader/load_nodeset2.ts +5 -0
- package/api/loader/narrow_instance_datatypes.ts +264 -0
- package/api/loader/nodeset_builtin_formats.ts +143 -0
- package/api/loader/nodeset_format.ts +153 -0
- package/api/loader/nodeset_image.ts +37 -4
- package/api/loader/nodeset_image_codec.ts +153 -34
- package/api/loader/nodeset_record.ts +26 -2
- package/api/loader/nodeset_record_applier.ts +54 -3
- package/api/loader/nodeset_records_to_xml.ts +620 -0
- package/api/loader/nodeset_source.ts +185 -26
- package/api/loader/nodeset_to_image.ts +16 -5
- package/api/loader/nodeset_xml_header.ts +119 -0
- package/api/loader/nodeset_xml_primitives.ts +209 -0
- package/api/loader/nodeset_xml_producer.ts +61 -2
- package/api/loader/parsers/variant_parser.ts +9 -0
- package/bin/nodeset_equivalence.js +750 -0
- package/bin/nodeset_tool_common.js +72 -0
- package/bin/opcua-nodeset-image.js +93 -103
- package/bin/opcua-nodeset-live.js +139 -0
- package/dist/api/helpers/ensure_secure_access.d.ts +20 -0
- package/dist/api/helpers/ensure_secure_access.js +39 -0
- package/dist/api/helpers/ensure_secure_access.js.map +1 -1
- package/dist/api/index.d.ts +7 -2
- package/dist/api/index.js +21 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/loader/generateAddressSpaceRaw.d.ts +1 -7
- package/dist/api/loader/generateAddressSpaceRaw.js +32 -125
- package/dist/api/loader/generateAddressSpaceRaw.js.map +1 -1
- package/dist/api/loader/load_nodeset2.js +4 -0
- package/dist/api/loader/load_nodeset2.js.map +1 -1
- package/dist/api/loader/narrow_instance_datatypes.d.ts +30 -0
- package/dist/api/loader/narrow_instance_datatypes.js +217 -0
- package/dist/api/loader/narrow_instance_datatypes.js.map +1 -0
- package/dist/api/loader/nodeset_builtin_formats.d.ts +27 -0
- package/dist/api/loader/nodeset_builtin_formats.js +133 -0
- package/dist/api/loader/nodeset_builtin_formats.js.map +1 -0
- package/dist/api/loader/nodeset_format.d.ts +123 -0
- package/dist/api/loader/nodeset_format.js +63 -0
- package/dist/api/loader/nodeset_format.js.map +1 -0
- package/dist/api/loader/nodeset_image.d.ts +17 -1
- package/dist/api/loader/nodeset_image.js +32 -4
- package/dist/api/loader/nodeset_image.js.map +1 -1
- package/dist/api/loader/nodeset_image_codec.d.ts +4 -0
- package/dist/api/loader/nodeset_image_codec.js +142 -53
- package/dist/api/loader/nodeset_image_codec.js.map +1 -1
- package/dist/api/loader/nodeset_record.d.ts +26 -2
- package/dist/api/loader/nodeset_record.js +11 -2
- package/dist/api/loader/nodeset_record.js.map +1 -1
- package/dist/api/loader/nodeset_record_applier.js +52 -3
- package/dist/api/loader/nodeset_record_applier.js.map +1 -1
- package/dist/api/loader/nodeset_records_to_xml.d.ts +16 -0
- package/dist/api/loader/nodeset_records_to_xml.js +536 -0
- package/dist/api/loader/nodeset_records_to_xml.js.map +1 -0
- package/dist/api/loader/nodeset_source.d.ts +68 -7
- package/dist/api/loader/nodeset_source.js +157 -20
- package/dist/api/loader/nodeset_source.js.map +1 -1
- package/dist/api/loader/nodeset_to_image.d.ts +4 -4
- package/dist/api/loader/nodeset_to_image.js +14 -4
- package/dist/api/loader/nodeset_to_image.js.map +1 -1
- package/dist/api/loader/nodeset_xml_header.d.ts +22 -0
- package/dist/api/loader/nodeset_xml_header.js +113 -0
- package/dist/api/loader/nodeset_xml_header.js.map +1 -0
- package/dist/api/loader/nodeset_xml_primitives.d.ts +37 -0
- package/dist/api/loader/nodeset_xml_primitives.js +196 -0
- package/dist/api/loader/nodeset_xml_primitives.js.map +1 -0
- package/dist/api/loader/nodeset_xml_producer.js +56 -1
- package/dist/api/loader/nodeset_xml_producer.js.map +1 -1
- package/dist/api/loader/parsers/variant_parser.js +7 -0
- package/dist/api/loader/parsers/variant_parser.js.map +1 -1
- package/dist/impl/_instantiate_helpers.js +1 -0
- package/dist/impl/_instantiate_helpers.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_alarm_condition_impl.js +4 -0
- package/dist/impl/alarms_and_conditions/ua_alarm_condition_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_certificate_expiration_alarm_impl.js +14 -0
- package/dist/impl/alarms_and_conditions/ua_certificate_expiration_alarm_impl.js.map +1 -1
- package/dist/impl/base_node_impl.d.ts +9 -0
- package/dist/impl/base_node_impl.js +11 -0
- package/dist/impl/base_node_impl.js.map +1 -1
- package/dist/impl/base_node_private.js +67 -97
- package/dist/impl/base_node_private.js.map +1 -1
- package/dist/impl/nodeset_tools/construct_namespace_dependency.js +5 -1
- package/dist/impl/nodeset_tools/construct_namespace_dependency.js.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_records.js +61 -15
- package/dist/impl/nodeset_tools/nodeset_to_records.js.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_xml.d.ts +1 -8
- package/dist/impl/nodeset_tools/nodeset_to_xml.js +174 -190
- package/dist/impl/nodeset_tools/nodeset_to_xml.js.map +1 -1
- package/dist/impl/ua_data_type_impl.d.ts +21 -0
- package/dist/impl/ua_data_type_impl.js +27 -2
- package/dist/impl/ua_data_type_impl.js.map +1 -1
- package/dist/impl/ua_object_type_impl.d.ts +9 -0
- package/dist/impl/ua_object_type_impl.js +12 -1
- package/dist/impl/ua_object_type_impl.js.map +1 -1
- package/dist/impl/ua_variable_impl.js +27 -10
- package/dist/impl/ua_variable_impl.js.map +1 -1
- package/dist/impl/ua_variable_impl_ext_obj.js +19 -2
- package/dist/impl/ua_variable_impl_ext_obj.js.map +1 -1
- package/dist/impl/ua_variable_type_impl.d.ts +8 -1
- package/dist/impl/ua_variable_type_impl.js +39 -2
- package/dist/impl/ua_variable_type_impl.js.map +1 -1
- package/dist/tsconfig_base.tsbuildinfo +1 -1
- package/distNodeJS/generate_address_space.js +4 -0
- package/distNodeJS/generate_address_space.js.map +1 -1
- package/impl/_instantiate_helpers.ts +3 -0
- package/impl/alarms_and_conditions/ua_acknowledgeable_condition_impl.ts +4 -4
- package/impl/alarms_and_conditions/ua_alarm_condition_impl.ts +19 -9
- package/impl/alarms_and_conditions/ua_base_event_impl.ts +9 -9
- package/impl/alarms_and_conditions/ua_certificate_expiration_alarm_impl.ts +17 -0
- package/impl/alarms_and_conditions/ua_condition_impl.ts +18 -18
- package/impl/alarms_and_conditions/ua_exclusive_deviation_alarm_impl.ts +2 -2
- package/impl/alarms_and_conditions/ua_exclusive_limit_alarm_impl.ts +1 -1
- package/impl/alarms_and_conditions/ua_limit_alarm_impl.ts +16 -16
- package/impl/alarms_and_conditions/ua_non_exclusive_deviation_alarm_impl.ts +2 -2
- package/impl/alarms_and_conditions/ua_non_exclusive_limit_alarm_impl.ts +4 -4
- package/impl/alarms_and_conditions/ua_off_normal_alarm_impl.ts +1 -1
- package/impl/base_node_impl.ts +15 -0
- package/impl/base_node_private.ts +97 -146
- package/impl/data_access/ua_multistate_discrete_impl.ts +1 -1
- package/impl/data_access/ua_multistate_value_discrete_impl.ts +2 -2
- package/impl/data_access/ua_two_state_discrete_impl.ts +2 -2
- package/impl/nodeset_tools/construct_namespace_dependency.ts +5 -1
- package/impl/nodeset_tools/nodeset_to_records.ts +66 -16
- package/impl/nodeset_tools/nodeset_to_xml.ts +127 -160
- package/impl/state_machine/finite_state_machine.ts +3 -3
- package/impl/state_machine/ua_shelving_state_machine_ex.ts +20 -20
- package/impl/ua_data_type_impl.ts +42 -4
- package/impl/ua_method_impl.ts +1 -1
- package/impl/ua_object_type_impl.ts +16 -3
- package/impl/ua_variable_impl.ts +27 -10
- package/impl/ua_variable_impl_ext_obj.ts +20 -2
- package/impl/ua_variable_type_impl.ts +49 -3
- package/package.json +29 -27
- package/source_nodejs/generate_address_space.ts +7 -1
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type LocalizedText,
|
|
31
31
|
type LocalizedTextLike,
|
|
32
32
|
NodeClass,
|
|
33
|
+
type QualifiedName,
|
|
33
34
|
ResultMask
|
|
34
35
|
} from "node-opcua-data-model";
|
|
35
36
|
import type { DataValue } from "node-opcua-data-value";
|
|
@@ -516,7 +517,7 @@ export function VariableOrVariableType_toString(this: UAVariableType | UAVariabl
|
|
|
516
517
|
}
|
|
517
518
|
|
|
518
519
|
/**
|
|
519
|
-
*
|
|
520
|
+
* A subtype (or an instance) may re-declare a child that its base type already declares:
|
|
520
521
|
*
|
|
521
522
|
* MyDeriveType ------------------- -> MyBaseType --------------> TopologyElementType
|
|
522
523
|
* | | |
|
|
@@ -525,137 +526,71 @@ export function VariableOrVariableType_toString(this: UAVariableType | UAVariabl
|
|
|
525
526
|
* +- Foo1 |
|
|
526
527
|
* +- Bar
|
|
527
528
|
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
* @param newParent
|
|
531
|
-
* @param node
|
|
532
|
-
* @param copyAlsoModellingRules
|
|
533
|
-
* @param optionalFilter
|
|
534
|
-
* @param extraInfo
|
|
535
|
-
* @param browseNameMap
|
|
536
|
-
* @returns
|
|
529
|
+
* The instance gets ONE ParameterSet, cloned from the nearest declaration (Foo1), into which the
|
|
530
|
+
* base declarations are merged (Bar): see _base_declarations_of and _merge_base_declaration.
|
|
537
531
|
*/
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
) {
|
|
546
|
-
/**
|
|
547
|
-
* the type definition node of the node to clone
|
|
548
|
-
*/
|
|
549
|
-
const nodeToCloneTypeDefinition =
|
|
550
|
-
nodeToClone.nodeClass === NodeClass.ObjectType || nodeToClone.nodeClass === NodeClass.VariableType
|
|
551
|
-
? nodeToClone.subtypeOfObj
|
|
552
|
-
: null;
|
|
553
|
-
if (!nodeToCloneTypeDefinition) return;
|
|
554
|
-
|
|
555
|
-
// c8 ignore next
|
|
556
|
-
doTrace &&
|
|
557
|
-
traceLog(
|
|
558
|
-
extraInfo?.pad(),
|
|
559
|
-
chalk.gray(
|
|
560
|
-
"-------------------- now cloning children on template ",
|
|
561
|
-
node.browseName.toString(),
|
|
562
|
-
node.nodeId.toString(),
|
|
563
|
-
nodeToCloneTypeDefinition.browseName.toString()
|
|
564
|
-
)
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
const namespace = newParent.namespace;
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* the child node of the new parent that match the node to clone or enrich
|
|
571
|
-
*/
|
|
572
|
-
const newParentChild = newParent.getChildByName(node.browseName);
|
|
573
|
-
if (!newParentChild) {
|
|
574
|
-
return;
|
|
532
|
+
/**
|
|
533
|
+
* the declarations, on the supertypes of `type`, of the child that `type` declares under
|
|
534
|
+
* `browseName`, nearest supertype first; none when `type` is not a type
|
|
535
|
+
*/
|
|
536
|
+
function _base_declarations_of(type: BaseNode, browseName: QualifiedName): BaseNode[] {
|
|
537
|
+
if (type.nodeClass !== NodeClass.ObjectType && type.nodeClass !== NodeClass.VariableType) {
|
|
538
|
+
return [];
|
|
575
539
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
doTrace &&
|
|
584
|
-
traceLog(
|
|
585
|
-
extraInfo?.pad(),
|
|
586
|
-
chalk.green(
|
|
587
|
-
"-------------------- now cloning children on ",
|
|
588
|
-
newParentChild.browseName.toString(),
|
|
589
|
-
newParentChild.nodeId.toString(),
|
|
590
|
-
" (child of ",
|
|
591
|
-
node.browseName.toString(),
|
|
592
|
-
node.nodeId.toString(),
|
|
593
|
-
") from ",
|
|
594
|
-
typeDefinitionNode.browseName.toString()
|
|
595
|
-
)
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
// Find aggregates with same browseName as node. Do not search children as this includes nodes with HasSubType relation which we do
|
|
599
|
-
// not want. See issue #1326.
|
|
600
|
-
const aggregates = typeDefinitionNode.getAggregates().filter((n) => n.browseName.equals(node.browseName));
|
|
601
|
-
const typeDefinitionChild = aggregates.length > 0 ? aggregates[0] : null;
|
|
602
|
-
if (typeDefinitionChild) {
|
|
603
|
-
const references = typeDefinitionChild.findReferencesEx("Aggregates", BrowseDirection.Forward);
|
|
604
|
-
|
|
605
|
-
for (const ref of references) {
|
|
606
|
-
const grandChild = ref.node as UAVariable | UAObject | UAMethod;
|
|
607
|
-
if (grandChild.modellingRule === "MandatoryPlaceholder" || grandChild.modellingRule === "OptionalPlaceholder")
|
|
608
|
-
continue;
|
|
609
|
-
// if not already node present in new Parent => just ignore
|
|
610
|
-
const hasAlready = newParentChild.getChildByName(grandChild.browseName) !== null;
|
|
611
|
-
if (!hasAlready) {
|
|
612
|
-
if (optionalFilter && node && !optionalFilter.shouldKeep(node)) {
|
|
613
|
-
// c8 ignore next
|
|
614
|
-
doTrace &&
|
|
615
|
-
traceLog(
|
|
616
|
-
extraInfo.pad(),
|
|
617
|
-
"skipping optional ",
|
|
618
|
-
node.browseName.toString(),
|
|
619
|
-
"that doesn't appear in the filter"
|
|
620
|
-
);
|
|
621
|
-
continue; // skip this node
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const options = {
|
|
625
|
-
namespace,
|
|
626
|
-
references: [
|
|
627
|
-
new ReferenceImpl({
|
|
628
|
-
referenceType: ref.referenceType,
|
|
629
|
-
isForward: false,
|
|
630
|
-
nodeId: newParentChild.nodeId
|
|
631
|
-
})
|
|
632
|
-
],
|
|
633
|
-
copyAlsoModellingRules
|
|
634
|
-
};
|
|
635
|
-
|
|
636
|
-
const alreadyCloned = extraInfo.getCloned({
|
|
637
|
-
originalParent: nodeToClone,
|
|
638
|
-
clonedParent: newParent,
|
|
639
|
-
originalNode: grandChild
|
|
640
|
-
});
|
|
641
|
-
if (alreadyCloned) {
|
|
642
|
-
alreadyCloned.addReference({
|
|
643
|
-
referenceType: ref.referenceType,
|
|
644
|
-
isForward: false,
|
|
645
|
-
nodeId: newParentChild.nodeId
|
|
646
|
-
});
|
|
647
|
-
} else {
|
|
648
|
-
const clonedGrandChild = grandChild.clone(options, optionalFilter, extraInfo);
|
|
649
|
-
extraInfo.registerClonedObject({
|
|
650
|
-
originalNode: grandChild,
|
|
651
|
-
clonedNode: clonedGrandChild
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
540
|
+
const result: BaseNode[] = [];
|
|
541
|
+
let base = (type as UAObjectType | UAVariableType).subtypeOfObj;
|
|
542
|
+
while (base) {
|
|
543
|
+
// aggregates only: a child lookup would also follow HasSubtype (issue #1326)
|
|
544
|
+
const declaration = base.getAggregates().find((n) => n.browseName.equals(browseName));
|
|
545
|
+
if (declaration) {
|
|
546
|
+
result.push(declaration);
|
|
656
547
|
}
|
|
657
|
-
|
|
548
|
+
base = base.subtypeOfObj;
|
|
658
549
|
}
|
|
550
|
+
return result;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* record `cloned` as the clone of `original` and, by browse name, each child the clone already
|
|
555
|
+
* has as the clone of the matching child of `original`, recursively: a later reference to any
|
|
556
|
+
* of them then lands on the existing node instead of a second copy
|
|
557
|
+
*/
|
|
558
|
+
function _register_existing_children(original: BaseNode, cloned: BaseNode, extraInfo: CloneExtraInfo): void {
|
|
559
|
+
extraInfo.registerClonedObject({ originalNode: original, clonedNode: cloned });
|
|
560
|
+
for (const ref of original.findReferencesEx("Aggregates", BrowseDirection.Forward)) {
|
|
561
|
+
const child = ref.node;
|
|
562
|
+
if (!child) continue;
|
|
563
|
+
const existing = cloned.getChildByName(child.browseName);
|
|
564
|
+
if (!existing) continue;
|
|
565
|
+
_register_existing_children(child, existing, extraInfo);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* `clonedNode` was cloned from a declaration that shadows `baseDeclaration` (a subtype or an
|
|
571
|
+
* instance re-declaring a child of a base type). What the clone already has is recorded as the
|
|
572
|
+
* clone of the base declaration's same-named children; what the base declaration adds is cloned
|
|
573
|
+
* in, subject to the same filter and the same browse-name guard as the clone's own children.
|
|
574
|
+
*/
|
|
575
|
+
function _merge_base_declaration(
|
|
576
|
+
baseDeclaration: BaseNode,
|
|
577
|
+
clonedNode: BaseNode,
|
|
578
|
+
copyAlsoModellingRules: boolean,
|
|
579
|
+
optionalFilter: CloneFilter,
|
|
580
|
+
extraInfo: CloneExtraInfo,
|
|
581
|
+
browseNameMap: Set<string>
|
|
582
|
+
): void {
|
|
583
|
+
// c8 ignore next
|
|
584
|
+
doTrace && traceLog(extraInfo.pad(), "merging base declaration ", fullPath2(baseDeclaration), " into ", fullPath2(clonedNode));
|
|
585
|
+
_register_existing_children(baseDeclaration, clonedNode, extraInfo);
|
|
586
|
+
_clone_hierarchical_references(
|
|
587
|
+
baseDeclaration as UAObject | UAVariable | UAMethod,
|
|
588
|
+
clonedNode as UAObject | UAVariable | UAMethod,
|
|
589
|
+
copyAlsoModellingRules,
|
|
590
|
+
optionalFilter,
|
|
591
|
+
extraInfo,
|
|
592
|
+
browseNameMap
|
|
593
|
+
);
|
|
659
594
|
}
|
|
660
595
|
|
|
661
596
|
/*
|
|
@@ -707,27 +642,31 @@ function _clone_collection_new(
|
|
|
707
642
|
}
|
|
708
643
|
const key = `${newParent.nodeId.toString()}(${newParent.browseName.toString()})/${node.browseName.toString()}`;
|
|
709
644
|
if (browseNameMap?.has(key)) {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
645
|
+
// a same-named child was cloned already, from a nearer declaration: this one is a
|
|
646
|
+
// base declaration of it, merge what it adds
|
|
647
|
+
doTrace && traceLog(extraInfo.pad(), "node with same browseName already cloned, merging", fullPath2(node), "key=", key);
|
|
648
|
+
const existing = newParent.getChildByName(node.browseName);
|
|
649
|
+
if (existing) {
|
|
650
|
+
_merge_base_declaration(
|
|
651
|
+
node,
|
|
652
|
+
existing,
|
|
653
|
+
copyAlsoModellingRules,
|
|
654
|
+
optionalFilter.filterFor(existing as UAObject | UAVariable | UAMethod),
|
|
655
|
+
extraInfo,
|
|
656
|
+
new Set<string>()
|
|
719
657
|
);
|
|
720
|
-
|
|
658
|
+
}
|
|
721
659
|
continue; // skipping node with same browseName
|
|
722
660
|
}
|
|
723
661
|
browseNameMap?.add(key);
|
|
724
662
|
|
|
725
663
|
// assert(reference.isForward);
|
|
726
664
|
// assert(reference.referenceType instanceof NodeId, "" + reference.referenceType.toString());
|
|
727
|
-
const options = {
|
|
665
|
+
const options: CloneOptions = {
|
|
728
666
|
namespace,
|
|
729
667
|
references: [new ReferenceImpl({ referenceType: reference.referenceType, isForward: false, nodeId: newParent.nodeId })],
|
|
730
|
-
copyAlsoModellingRules
|
|
668
|
+
copyAlsoModellingRules,
|
|
669
|
+
baseDeclarations: _base_declarations_of(nodeToClone, node.browseName)
|
|
731
670
|
};
|
|
732
671
|
|
|
733
672
|
const alreadyCloned = extraInfo.getCloned({ originalParent: nodeToClone, clonedParent: newParent, originalNode: node });
|
|
@@ -746,8 +685,10 @@ function _clone_collection_new(
|
|
|
746
685
|
fullPath2(newParent)
|
|
747
686
|
);
|
|
748
687
|
|
|
749
|
-
|
|
750
|
-
|
|
688
|
+
// this parent, not any parent: a shared child may already hang under another one
|
|
689
|
+
const hasReference = alreadyCloned
|
|
690
|
+
.findReferences(reference.referenceType, false)
|
|
691
|
+
.some((r) => sameNodeId(r.nodeId, newParent.nodeId));
|
|
751
692
|
if (!hasReference) {
|
|
752
693
|
alreadyCloned.addReference({
|
|
753
694
|
referenceType: reference.referenceType,
|
|
@@ -791,10 +732,6 @@ function _clone_collection_new(
|
|
|
791
732
|
`${fullPath2(clonedNode)}`
|
|
792
733
|
);
|
|
793
734
|
|
|
794
|
-
extraInfo.level++;
|
|
795
|
-
_clone_children_on_template(nodeToClone, newParent, node, copyAlsoModellingRules, optionalFilter, extraInfo);
|
|
796
|
-
extraInfo.level--;
|
|
797
|
-
|
|
798
735
|
// also clone or instantiate interface members that may be required in the optionals
|
|
799
736
|
extraInfo.level++;
|
|
800
737
|
_cloneInterface(nodeToClone, newParent, node, optionalFilter, extraInfo, browseNameMap);
|
|
@@ -1123,6 +1060,20 @@ export function _clone<T extends UAObject | UAVariable | UAMethod, O>(
|
|
|
1123
1060
|
browseNameMap
|
|
1124
1061
|
);
|
|
1125
1062
|
|
|
1063
|
+
// what the base types declare under the same name, before the type definition is explored:
|
|
1064
|
+
// the base declaration overrides the type definition's children, so the later walk of the
|
|
1065
|
+
// type definition finds them present instead of cloning them a second time
|
|
1066
|
+
for (const baseDeclaration of options.baseDeclarations || []) {
|
|
1067
|
+
_merge_base_declaration(
|
|
1068
|
+
baseDeclaration,
|
|
1069
|
+
clonedNode,
|
|
1070
|
+
options.copyAlsoModellingRules,
|
|
1071
|
+
newFilter,
|
|
1072
|
+
extraInfo,
|
|
1073
|
+
browseNameMap
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1126
1077
|
if (originalNode.nodeClass === NodeClass.Object || originalNode.nodeClass === NodeClass.Variable) {
|
|
1127
1078
|
let typeDefinitionNode: UAVariableType | UAObjectType | null = originalNode.typeDefinitionObj;
|
|
1128
1079
|
|
|
@@ -43,7 +43,7 @@ export class UAMultiStateDiscreteImplBase<T, DT extends DataType>
|
|
|
43
43
|
* The EnumStrings property, installed as a child node by the address space rather than
|
|
44
44
|
* assigned by this constructor - hence `declare`, which emits nothing.
|
|
45
45
|
*/
|
|
46
|
-
public
|
|
46
|
+
declare public readonly enumStrings: UAProperty<LocalizedText[], DataType.LocalizedText>;
|
|
47
47
|
|
|
48
48
|
public getValue(): number {
|
|
49
49
|
// MultiStateDiscrete fixes its value to UInt32 (OPC 10000-8), whatever T says
|
|
@@ -94,8 +94,8 @@ export class UAMultiStateValueDiscreteImplBase<T extends Number, DT extends Data
|
|
|
94
94
|
* Properties installed as child nodes by the address space rather than assigned by this
|
|
95
95
|
* constructor - hence `declare`, which emits nothing.
|
|
96
96
|
*/
|
|
97
|
-
public
|
|
98
|
-
public
|
|
97
|
+
declare public readonly enumValues: UAProperty<DTEnumValue[], DataType.ExtensionObject>;
|
|
98
|
+
declare public readonly valueAsText: UAProperty<LocalizedText, DataType.LocalizedText>;
|
|
99
99
|
public setValue(value: string | number | Int64, options?: ISetStateOptions): void {
|
|
100
100
|
if (typeof value === "string") {
|
|
101
101
|
const enumValues = this.enumValues.readValue().value.value;
|
|
@@ -24,8 +24,8 @@ export class UATwoStateDiscreteImplBase extends UAVariableImplT<boolean, DataTyp
|
|
|
24
24
|
* Properties installed as child nodes by the address space rather than assigned by this
|
|
25
25
|
* constructor - hence `declare`, which emits nothing.
|
|
26
26
|
*/
|
|
27
|
-
public
|
|
28
|
-
public
|
|
27
|
+
declare public readonly falseState: UAProperty<LocalizedText, DataType.LocalizedText>;
|
|
28
|
+
declare public readonly trueState: UAProperty<LocalizedText, DataType.LocalizedText>;
|
|
29
29
|
/*
|
|
30
30
|
* @private
|
|
31
31
|
*/
|
|
@@ -367,7 +367,11 @@ export function _constructNamespaceTranslationTable(dependency: INamespace[], ex
|
|
|
367
367
|
let counter = 0;
|
|
368
368
|
translationTable.set(dependency[0].index, counter++);
|
|
369
369
|
//
|
|
370
|
-
|
|
370
|
+
// the exported namespace goes next, at 1 -- unless it *is* the UA namespace, which the line
|
|
371
|
+
// above has already placed at 0. Overwriting it there sent every ns0 id to ns=1, an index the
|
|
372
|
+
// <NamespaceUris> of such a document never declares, which is why exporting the UA namespace
|
|
373
|
+
// used to produce a document that could not be loaded back
|
|
374
|
+
if (exportedNamespace && !translationTable.has(exportedNamespace.index)) {
|
|
371
375
|
translationTable.set(exportedNamespace.index, counter++);
|
|
372
376
|
}
|
|
373
377
|
for (let i = 1; i < dependency.length; i++) {
|
|
@@ -75,7 +75,6 @@ import {
|
|
|
75
75
|
} from "./construct_namespace_dependency.js";
|
|
76
76
|
import {
|
|
77
77
|
_dumpVariantExtensionObjectValue_Body,
|
|
78
|
-
_isDefaultValue,
|
|
79
78
|
coerceInt64ToInt32,
|
|
80
79
|
initXmlWriterEx,
|
|
81
80
|
makeTypeXsd,
|
|
@@ -362,6 +361,9 @@ class RecordExporter {
|
|
|
362
361
|
browseName: this.tq(node.browseName),
|
|
363
362
|
references: this.referencesOf(node)
|
|
364
363
|
};
|
|
364
|
+
if (node.releaseStatus) {
|
|
365
|
+
record.releaseStatus = node.releaseStatus;
|
|
366
|
+
}
|
|
365
367
|
const parentNode = this.parentOf(node);
|
|
366
368
|
if (parentNode && parentNode.nodeId.namespace <= node.nodeId.namespace) {
|
|
367
369
|
record.parentNodeId = this.t(parentNode.nodeId);
|
|
@@ -380,7 +382,10 @@ class RecordExporter {
|
|
|
380
382
|
record.userAccessLevel = variable.userAccessLevel.toString();
|
|
381
383
|
}
|
|
382
384
|
}
|
|
383
|
-
|
|
385
|
+
// the declared spelling first: the loader's accessRestrictions option governs what is
|
|
386
|
+
// enforced, not what the document said, and only the latter belongs in an export
|
|
387
|
+
const accessRestrictions = node.declaredAccessRestrictions ?? node.accessRestrictions?.toString();
|
|
388
|
+
if (accessRestrictions !== undefined) record.accessRestrictions = accessRestrictions;
|
|
384
389
|
if (node.rolePermissions !== undefined && node.rolePermissions.length === 0) record.hasNoPermissions = true;
|
|
385
390
|
if (Object.hasOwn(node, "minimumSamplingInterval")) {
|
|
386
391
|
const minimumSamplingInterval = (node as UAVariable).minimumSamplingInterval;
|
|
@@ -517,6 +522,25 @@ class RecordExporter {
|
|
|
517
522
|
/** the fields as the XML loader's definition parser shapes them: an absent rank is -1, an absent type BaseDataType */
|
|
518
523
|
private definitionOf(node: UADataType): NodesetDataTypeDefinitionRecord | undefined {
|
|
519
524
|
const baseDataType = this.t(resolveNodeId(DataTypeIds.BaseDataType));
|
|
525
|
+
// an OptionSet is neither an enumeration nor a structure to node-opcua -- it is a plain
|
|
526
|
+
// UInt16 or UInt32 subtype -- so the only account of it is the one the loader kept
|
|
527
|
+
const asImpl = node as unknown as {
|
|
528
|
+
isOptionSetDataType?: boolean;
|
|
529
|
+
declaredDefinitionFields?: NodesetDefinitionField[];
|
|
530
|
+
declaredDefinitionName?: string;
|
|
531
|
+
};
|
|
532
|
+
// what the document called this definition, or -- for a DataType built here, which never
|
|
533
|
+
// had a declaration -- the browse name, which is the only name available
|
|
534
|
+
const definitionName = asImpl.declaredDefinitionName ?? this.b(node.browseName);
|
|
535
|
+
if (asImpl.isOptionSetDataType && asImpl.declaredDefinitionFields) {
|
|
536
|
+
const fields = asImpl.declaredDefinitionFields.map((f) => {
|
|
537
|
+
const field: NodesetDefinitionField = { ...f, allowSubTypes: f.allowSubTypes ?? false };
|
|
538
|
+
field.valueRank = f.valueRank ?? -1;
|
|
539
|
+
field.dataType = f.dataType ? this.t(f.dataType as unknown as NodeId) : baseDataType;
|
|
540
|
+
return field;
|
|
541
|
+
});
|
|
542
|
+
return { name: definitionName, isOptionSet: true, fields };
|
|
543
|
+
}
|
|
520
544
|
if (node.isEnumeration()) {
|
|
521
545
|
const enumDefinition = node.getEnumDefinition();
|
|
522
546
|
const fields: NodesetDefinitionField[] = (enumDefinition.fields || []).map((f) => {
|
|
@@ -527,29 +551,38 @@ class RecordExporter {
|
|
|
527
551
|
field.dataType = baseDataType;
|
|
528
552
|
return field;
|
|
529
553
|
});
|
|
530
|
-
return { name:
|
|
554
|
+
return { name: definitionName, fields };
|
|
531
555
|
}
|
|
532
556
|
if (node.isStructure()) {
|
|
533
557
|
const definition = node.getStructureDefinition();
|
|
534
|
-
|
|
558
|
+
// a structure's supertype need not be a structure: `Structure` itself subtypes BaseDataType,
|
|
559
|
+
// which has no definition at all. Asking it for one threw, and took the whole export with it
|
|
560
|
+
const baseNode = node.subtypeOfObj as UADataType | null;
|
|
561
|
+
const base = baseNode?.isStructure() ? baseNode.getStructureDefinition() : null;
|
|
535
562
|
const nbFieldsInBase = base ? base.fields?.length || 0 : 0;
|
|
563
|
+
// Part 3 8.51: in a *WithSubtypedValues structure, isOptional does not mean optional --
|
|
564
|
+
// it is where "subtyping allowed" is held. The document spells that AllowSubTypes, and
|
|
565
|
+
// says nothing about IsOptional there, so the two must be swapped back on the way out
|
|
566
|
+
const subtypedValues =
|
|
567
|
+
definition.structureType === StructureType.StructureWithSubtypedValues ||
|
|
568
|
+
definition.structureType === StructureType.UnionWithSubtypedValues;
|
|
536
569
|
const fields: NodesetDefinitionField[] = [];
|
|
537
570
|
const all = definition.fields || [];
|
|
538
571
|
for (let index = nbFieldsInBase; index < all.length; index++) {
|
|
539
572
|
const f = all[index];
|
|
540
573
|
const field: NodesetDefinitionField = {
|
|
541
574
|
name: f.name as string,
|
|
542
|
-
allowSubTypes: !!(f as unknown as { allowSubTypes?: boolean }).allowSubTypes
|
|
575
|
+
allowSubTypes: subtypedValues ? !!f.isOptional : !!(f as unknown as { allowSubTypes?: boolean }).allowSubTypes
|
|
543
576
|
};
|
|
544
577
|
if (f.description?.text?.length) field.description = { text: f.description.text } as unknown as string;
|
|
545
578
|
field.valueRank = f.valueRank === undefined || f.valueRank === -1 ? -1 : f.valueRank;
|
|
546
579
|
if (f.arrayDimensions && f.arrayDimensions.length > 0) field.arrayDimensions = f.arrayDimensions;
|
|
547
|
-
if (f.isOptional) field.isOptional = f.isOptional;
|
|
580
|
+
if (f.isOptional && !subtypedValues) field.isOptional = f.isOptional;
|
|
548
581
|
if (f.maxStringLength !== undefined && f.maxStringLength !== 0) field.maxStringLength = f.maxStringLength;
|
|
549
582
|
field.dataType = f.dataType && !f.dataType.isEmpty() ? this.t(f.dataType) : baseDataType;
|
|
550
583
|
fields.push(field);
|
|
551
584
|
}
|
|
552
|
-
const record: NodesetDataTypeDefinitionRecord = { name:
|
|
585
|
+
const record: NodesetDataTypeDefinitionRecord = { name: definitionName, fields };
|
|
553
586
|
if (definition.structureType === StructureType.Union) record.isUnion = true;
|
|
554
587
|
return record;
|
|
555
588
|
}
|
|
@@ -565,7 +598,10 @@ class RecordExporter {
|
|
|
565
598
|
this.arrayDimensionsOf(node, record);
|
|
566
599
|
if (this.addressSpace.findNode(node.dataType)) record.dataType = this.t(node.dataType);
|
|
567
600
|
const dataValue = (node as UAVariableImpl).$dataValue;
|
|
568
|
-
|
|
601
|
+
// a value the loader made up for a variable the document left unvalued is not the document's
|
|
602
|
+
// to write back; see valueWasSynthesized in nodeset_record_applier
|
|
603
|
+
const synthesized = (node as unknown as { valueWasSynthesized?: boolean }).valueWasSynthesized;
|
|
604
|
+
if (dataValue?.value && !synthesized && !dataValue.statusCode.equals(StatusCodes.BadWaitingForInitialData)) {
|
|
569
605
|
const value = this.valueOf(node, dataValue.value);
|
|
570
606
|
if (value) record.value = value;
|
|
571
607
|
}
|
|
@@ -590,8 +626,14 @@ class RecordExporter {
|
|
|
590
626
|
}
|
|
591
627
|
|
|
592
628
|
private arrayDimensionsOf(node: UAVariable | UAVariableType, record: NodesetNodeRecord): void {
|
|
593
|
-
|
|
594
|
-
|
|
629
|
+
// ArrayDimensions="0" is written only when the document declared it: node-opcua synthesizes [0]
|
|
630
|
+
// on the variables it generates, so writing it unconditionally would invent the attribute on
|
|
631
|
+
// every generated node. See arrayDimensionsWereDeclared in nodeset_record_applier
|
|
632
|
+
if (!node.arrayDimensions || node.valueRank <= 0) return;
|
|
633
|
+
// all-zero dimensions are what node-opcua synthesizes when nobody declared any, at whatever
|
|
634
|
+
// rank: [0] for a one-dimensional value, [0,0] for a matrix
|
|
635
|
+
const degenerate = node.arrayDimensions.every((d) => d === 0);
|
|
636
|
+
if (degenerate && !(node as unknown as { arrayDimensionsWereDeclared?: boolean }).arrayDimensionsWereDeclared) return;
|
|
595
637
|
record.arrayDimensions = node.arrayDimensions;
|
|
596
638
|
}
|
|
597
639
|
|
|
@@ -605,7 +647,9 @@ class RecordExporter {
|
|
|
605
647
|
this.markVisited(node);
|
|
606
648
|
this.dumpReferencedNodes(node);
|
|
607
649
|
const record = this.common(node);
|
|
608
|
-
|
|
650
|
+
// typeof, not truthiness: a node with a child called "EventNotifier" resolves the name to that
|
|
651
|
+
// child through the shared child accessors, so this is a BaseNode there, not the attribute
|
|
652
|
+
if (typeof node.eventNotifier === "number" && node.eventNotifier !== 0) record.eventNotifier = node.eventNotifier;
|
|
609
653
|
this.records.push(record);
|
|
610
654
|
this.dumpAggregates(node);
|
|
611
655
|
}
|
|
@@ -615,8 +659,10 @@ class RecordExporter {
|
|
|
615
659
|
this.markVisited(node);
|
|
616
660
|
this.dumpReferencedNodes(node);
|
|
617
661
|
const record = this.common(node);
|
|
618
|
-
|
|
619
|
-
|
|
662
|
+
// typeof, not truthiness: a node with a child called "EventNotifier" resolves the name to that
|
|
663
|
+
// child through the shared child accessors, so this is a BaseNode there, not the attribute
|
|
664
|
+
const eventNotifier = (node as unknown as { eventNotifier?: unknown }).eventNotifier;
|
|
665
|
+
if (typeof eventNotifier === "number" && eventNotifier !== 0) record.eventNotifier = eventNotifier;
|
|
620
666
|
this.records.push(record);
|
|
621
667
|
this.dumpAggregates(node);
|
|
622
668
|
this.section(`ObjectType - ${this.b(node.browseName)} }}}}`);
|
|
@@ -634,9 +680,9 @@ class RecordExporter {
|
|
|
634
680
|
private dumpView(node: BaseNode): void {
|
|
635
681
|
this.markVisited(node);
|
|
636
682
|
const record = this.common(node);
|
|
637
|
-
const view = node as unknown as { containsNoLoops?: boolean; eventNotifier?:
|
|
683
|
+
const view = node as unknown as { containsNoLoops?: boolean; eventNotifier?: unknown };
|
|
638
684
|
if (view.containsNoLoops) record.containsNoLoops = true;
|
|
639
|
-
if (view.eventNotifier) record.eventNotifier = view.eventNotifier;
|
|
685
|
+
if (typeof view.eventNotifier === "number" && view.eventNotifier !== 0) record.eventNotifier = view.eventNotifier;
|
|
640
686
|
this.records.push(record);
|
|
641
687
|
this.dumpAggregates(node);
|
|
642
688
|
}
|
|
@@ -675,8 +721,12 @@ class RecordExporter {
|
|
|
675
721
|
private valueOf(node: UAVariable | UAVariableType, variant: Variant): VariantOptions | undefined {
|
|
676
722
|
if (!(variant instanceof Variant)) return undefined;
|
|
677
723
|
if (variant.dataType === DataType.Null) return undefined;
|
|
724
|
+
// a null value is nothing to write down, whatever its declared data type
|
|
725
|
+
if (variant.value === null || variant.value === undefined) return undefined;
|
|
678
726
|
if (!this.addressSpace.findDataType(node.dataType)) return undefined;
|
|
679
|
-
|
|
727
|
+
// no _isDefaultValue test here: it dropped every zero and every empty string, which threw
|
|
728
|
+
// away the ones the document actually declared in order to suppress the ones the loader
|
|
729
|
+
// made up. valueWasSynthesized now separates the two, so a declared "" survives the trip
|
|
680
730
|
const element = (v: unknown) => this.elementOf(node, variant.dataType, v);
|
|
681
731
|
const out: VariantOptions = { dataType: variant.dataType, arrayType: variant.arrayType };
|
|
682
732
|
if (variant.arrayType === VariantArrayType.Scalar) {
|