node-opcua-address-space 2.186.0 → 2.186.2
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/loader/make_xml_extension_object_parser.ts +6 -6
- package/api/loader/nodeset_image_codec.ts +3 -2
- package/api/loader/nodeset_record.ts +2 -0
- package/api/loader/nodeset_record_applier.ts +1 -0
- package/api/loader/nodeset_records_to_xml.ts +3 -0
- package/api/loader/nodeset_xml_producer.ts +1 -0
- package/dist/api/loader/make_xml_extension_object_parser.js +6 -6
- package/dist/api/loader/make_xml_extension_object_parser.js.map +1 -1
- package/dist/api/loader/nodeset_image_codec.d.ts.map +1 -1
- package/dist/api/loader/nodeset_image_codec.js +3 -2
- package/dist/api/loader/nodeset_image_codec.js.map +1 -1
- package/dist/api/loader/nodeset_record.d.ts +2 -0
- package/dist/api/loader/nodeset_record.d.ts.map +1 -1
- package/dist/api/loader/nodeset_record.js.map +1 -1
- package/dist/api/loader/nodeset_record_applier.d.ts.map +1 -1
- package/dist/api/loader/nodeset_record_applier.js +1 -0
- package/dist/api/loader/nodeset_record_applier.js.map +1 -1
- package/dist/api/loader/nodeset_records_to_xml.js +3 -0
- package/dist/api/loader/nodeset_records_to_xml.js.map +1 -1
- package/dist/api/loader/nodeset_xml_producer.d.ts.map +1 -1
- package/dist/api/loader/nodeset_xml_producer.js +2 -0
- package/dist/api/loader/nodeset_xml_producer.js.map +1 -1
- package/dist/impl/_instantiate_helpers.d.ts.map +1 -1
- package/dist/impl/_instantiate_helpers.js +6 -1
- package/dist/impl/_instantiate_helpers.js.map +1 -1
- package/dist/impl/_mandatory_child_or_requested_optional_filter.d.ts +7 -1
- package/dist/impl/_mandatory_child_or_requested_optional_filter.d.ts.map +1 -1
- package/dist/impl/_mandatory_child_or_requested_optional_filter.js +14 -3
- package/dist/impl/_mandatory_child_or_requested_optional_filter.js.map +1 -1
- package/dist/impl/base_node_impl.d.ts +4 -0
- package/dist/impl/base_node_impl.d.ts.map +1 -1
- package/dist/impl/base_node_impl.js +5 -0
- package/dist/impl/base_node_impl.js.map +1 -1
- package/dist/impl/base_node_private.d.ts.map +1 -1
- package/dist/impl/base_node_private.js +52 -0
- package/dist/impl/base_node_private.js.map +1 -1
- package/dist/impl/event_data.js +2 -2
- package/dist/impl/event_data.js.map +1 -1
- package/dist/impl/nodeid_manager.d.ts +19 -0
- package/dist/impl/nodeid_manager.d.ts.map +1 -1
- package/dist/impl/nodeid_manager.js +94 -11
- package/dist/impl/nodeid_manager.js.map +1 -1
- package/dist/impl/nodeset_tools/dump_to_bsd.d.ts.map +1 -1
- package/dist/impl/nodeset_tools/dump_to_bsd.js +15 -0
- package/dist/impl/nodeset_tools/dump_to_bsd.js.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_records.d.ts.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_records.js +6 -3
- package/dist/impl/nodeset_tools/nodeset_to_records.js.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_xml.d.ts.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_xml.js +3 -0
- package/dist/impl/nodeset_tools/nodeset_to_xml.js.map +1 -1
- package/dist/impl/ua_data_type_impl.d.ts.map +1 -1
- package/dist/impl/ua_data_type_impl.js +2 -1
- package/dist/impl/ua_data_type_impl.js.map +1 -1
- package/dist/tsconfig_base.tsbuildinfo +1 -1
- package/impl/_instantiate_helpers.ts +12 -1
- package/impl/_mandatory_child_or_requested_optional_filter.ts +20 -3
- package/impl/base_node_impl.ts +7 -0
- package/impl/base_node_private.ts +57 -0
- package/impl/event_data.ts +2 -2
- package/impl/nodeid_manager.ts +99 -11
- package/impl/nodeset_tools/dump_to_bsd.ts +16 -0
- package/impl/nodeset_tools/nodeset_to_records.ts +6 -3
- package/impl/nodeset_tools/nodeset_to_xml.ts +3 -0
- package/impl/ua_data_type_impl.ts +2 -1
- package/package.json +31 -31
|
@@ -61,7 +61,13 @@ function _initialize_properties_and_components<B extends UAObject | UAVariable |
|
|
|
61
61
|
return; // nothing to do
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
// an instance declaration of a type (copyAlsoModellingRules) takes its type's placeholders too
|
|
65
|
+
const filter = new MandatoryChildOrRequestedOptionalFilter(
|
|
66
|
+
instance,
|
|
67
|
+
copyAlsoAllOptionals,
|
|
68
|
+
optionalsMap,
|
|
69
|
+
copyAlsoModellingRules
|
|
70
|
+
);
|
|
65
71
|
|
|
66
72
|
doTrace &&
|
|
67
73
|
traceLog(
|
|
@@ -115,6 +121,8 @@ export function initialize_properties_and_components<
|
|
|
115
121
|
optionals?: string[]
|
|
116
122
|
): void {
|
|
117
123
|
const extraInfo = new CloneHelper();
|
|
124
|
+
// flushed below, once the whole tree is cloned
|
|
125
|
+
extraInfo.canDefer = true;
|
|
118
126
|
|
|
119
127
|
extraInfo.pushContext({
|
|
120
128
|
clonedParent: instance,
|
|
@@ -151,6 +159,9 @@ export function initialize_properties_and_components<
|
|
|
151
159
|
browseNameMap
|
|
152
160
|
);
|
|
153
161
|
|
|
162
|
+
// the members a node only organizes, now that their owners were cloned
|
|
163
|
+
extraInfo.flushDeferred();
|
|
164
|
+
|
|
154
165
|
reconstructFunctionalGroupType(extraInfo);
|
|
155
166
|
|
|
156
167
|
reconstructNonHierarchicalReferences(extraInfo);
|
|
@@ -25,9 +25,16 @@ export class MandatoryChildOrRequestedOptionalFilter implements CloneFilter {
|
|
|
25
25
|
private readonly optionalsMap: OptionalMap;
|
|
26
26
|
private readonly references: UAReference[];
|
|
27
27
|
private readonly copyAlsoAllOptionals: boolean = false;
|
|
28
|
+
/**
|
|
29
|
+
* the clone is an instance declaration of a type (instantiate() inside a type): the
|
|
30
|
+
* placeholders its type declares are part of it, as the ModelCompiler emits them
|
|
31
|
+
* (OPC 10000-100 DI DeviceType/DeviceTypeImage/<ImageIdentifier>). An instance never takes one.
|
|
32
|
+
*/
|
|
33
|
+
private readonly keepPlaceholders: boolean = false;
|
|
28
34
|
|
|
29
|
-
constructor(instance: BaseNode, copyAlsoAllOptionals: boolean, optionalsMap: OptionalMap) {
|
|
35
|
+
constructor(instance: BaseNode, copyAlsoAllOptionals: boolean, optionalsMap: OptionalMap, keepPlaceholders = false) {
|
|
30
36
|
this.copyAlsoAllOptionals = copyAlsoAllOptionals;
|
|
37
|
+
this.keepPlaceholders = keepPlaceholders;
|
|
31
38
|
// should we clone the node to be a component or propertyOf of a instance
|
|
32
39
|
assert(null !== instance);
|
|
33
40
|
this.optionalsMap = optionalsMap;
|
|
@@ -78,7 +85,8 @@ export class MandatoryChildOrRequestedOptionalFilter implements CloneFilter {
|
|
|
78
85
|
// only if in requested optionals
|
|
79
86
|
return this.copyAlsoAllOptionals || (node.browseName?.name || "") in this.optionalsMap;
|
|
80
87
|
case "OptionalPlaceholder":
|
|
81
|
-
|
|
88
|
+
case "MandatoryPlaceholder":
|
|
89
|
+
return this.keepPlaceholders;
|
|
82
90
|
default:
|
|
83
91
|
return false; // ignored
|
|
84
92
|
}
|
|
@@ -94,7 +102,16 @@ export class MandatoryChildOrRequestedOptionalFilter implements CloneFilter {
|
|
|
94
102
|
}
|
|
95
103
|
/* c8 ignore next */
|
|
96
104
|
doTrace && traceLog("filterFor ", browseName, map);
|
|
97
|
-
|
|
105
|
+
// a placeholder's copy takes its own members, not the placeholders of its type: the walk
|
|
106
|
+
// would never end on a type whose placeholder is typed by a type holding one in turn
|
|
107
|
+
const isPlaceholder =
|
|
108
|
+
childInstance.modellingRule === "OptionalPlaceholder" || childInstance.modellingRule === "MandatoryPlaceholder";
|
|
109
|
+
const newFilter = new MandatoryChildOrRequestedOptionalFilter(
|
|
110
|
+
childInstance,
|
|
111
|
+
false,
|
|
112
|
+
map,
|
|
113
|
+
this.keepPlaceholders && !isPlaceholder
|
|
114
|
+
);
|
|
98
115
|
return newFilter;
|
|
99
116
|
}
|
|
100
117
|
}
|
package/impl/base_node_impl.ts
CHANGED
|
@@ -184,6 +184,8 @@ export interface InternalBaseNodeOptions {
|
|
|
184
184
|
nodesetDocumentation?: string;
|
|
185
185
|
/** the `<Category>` elements as the nodeset declared them; see BaseNode.nodesetCategory */
|
|
186
186
|
nodesetCategory?: string[];
|
|
187
|
+
/** `DesignToolOnly="true"` as the nodeset declared it; see BaseNode.designToolOnly */
|
|
188
|
+
designToolOnly?: boolean;
|
|
187
189
|
/** the AccessRestrictions as declared, whatever this loader applies; see BaseNode */
|
|
188
190
|
declaredAccessRestrictions?: string;
|
|
189
191
|
}
|
|
@@ -233,6 +235,8 @@ export abstract class BaseNodeImpl<T extends BaseNodeEvents & ListenerSignature<
|
|
|
233
235
|
public nodesetDocumentation?: string;
|
|
234
236
|
/** see BaseNode.nodesetCategory; only set when the nodeset declared at least one */
|
|
235
237
|
public nodesetCategory?: string[];
|
|
238
|
+
/** see BaseNode.designToolOnly; only set when the nodeset declared it */
|
|
239
|
+
public designToolOnly?: boolean;
|
|
236
240
|
/** see BaseNode.declaredAccessRestrictions */
|
|
237
241
|
public declaredAccessRestrictions?: string;
|
|
238
242
|
private _accessRestrictions?: AccessRestrictionsFlag;
|
|
@@ -459,6 +463,9 @@ export abstract class BaseNodeImpl<T extends BaseNodeEvents & ListenerSignature<
|
|
|
459
463
|
if (options.nodesetCategory && options.nodesetCategory.length > 0) {
|
|
460
464
|
this.nodesetCategory = [...options.nodesetCategory];
|
|
461
465
|
}
|
|
466
|
+
if (options.designToolOnly) {
|
|
467
|
+
this.designToolOnly = true;
|
|
468
|
+
}
|
|
462
469
|
if (options.parentNodeId) {
|
|
463
470
|
_private._declaredParentNodeId =
|
|
464
471
|
options.parentNodeId instanceof NodeId
|
|
@@ -595,6 +595,39 @@ function _merge_base_declaration(
|
|
|
595
595
|
);
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
+
/**
|
|
599
|
+
* whether `declaringNode` is the declared parent (ParentNodeId) of `node` while `reference` links the
|
|
600
|
+
* two without aggregating it: the parent then names the node, the reference does not
|
|
601
|
+
*/
|
|
602
|
+
function _isDeclaredNonAggregatedMemberOf(node: BaseNode, declaringNode: BaseNode, reference: UAReference): boolean {
|
|
603
|
+
const declaredParent = node.parentNodeId;
|
|
604
|
+
if (!declaredParent || !sameNodeId(declaredParent, declaringNode.nodeId)) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
const addressSpace = node.addressSpace;
|
|
608
|
+
const referenceType = addressSpace.findReferenceType(reference.referenceType);
|
|
609
|
+
const aggregates = addressSpace.findReferenceType("Aggregates");
|
|
610
|
+
return !!referenceType && !!aggregates && !referenceType.isSubtypeOf(aggregates);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* whether `organizer` reaches `node` through a reference that does not aggregate it, while the
|
|
615
|
+
* node's declared parent (ParentNodeId) is another node that aggregates it: its owner
|
|
616
|
+
*/
|
|
617
|
+
function _isOrganizedWhileOwnedByAnother(node: BaseNode, organizer: BaseNode, reference: UAReference): boolean {
|
|
618
|
+
const owner = node.parentNodeId;
|
|
619
|
+
if (!owner || sameNodeId(owner, organizer.nodeId)) {
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
const addressSpace = node.addressSpace;
|
|
623
|
+
const referenceType = addressSpace.findReferenceType(reference.referenceType);
|
|
624
|
+
const aggregates = addressSpace.findReferenceType("Aggregates");
|
|
625
|
+
if (!referenceType || !aggregates || referenceType.isSubtypeOf(aggregates)) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
return node.findReferencesEx("Aggregates", BrowseDirection.Inverse).some((r) => sameNodeId(r.nodeId, owner));
|
|
629
|
+
}
|
|
630
|
+
|
|
598
631
|
/*
|
|
599
632
|
* clone properties and methods
|
|
600
633
|
* @private
|
|
@@ -662,6 +695,23 @@ function _clone_collection_new(
|
|
|
662
695
|
}
|
|
663
696
|
browseNameMap?.add(key);
|
|
664
697
|
|
|
698
|
+
// a node this one only organizes, while another node of the template owns it (aggregates
|
|
699
|
+
// it, as its ParentNodeId), is left to its owner: cloned through this reference first, it
|
|
700
|
+
// was parentless and named after its browse name alone (OPC 30100 IO-Link:
|
|
701
|
+
// IOLinkDeviceType/General organizes MethodSet/ApplicationReset). Once the whole tree is
|
|
702
|
+
// cloned, the owner's clone gets this reference too; a node no owner cloned is cloned here.
|
|
703
|
+
if (
|
|
704
|
+
extraInfo instanceof CloneHelper &&
|
|
705
|
+
extraInfo.canDefer &&
|
|
706
|
+
_isOrganizedWhileOwnedByAnother(node, nodeToClone, reference)
|
|
707
|
+
) {
|
|
708
|
+
extraInfo.deferInCurrentContext(() => attach(reference, node));
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
attach(reference, node);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function attach(reference: UAReference, node: UAVariable | UAObject | UAMethod): void {
|
|
665
715
|
// assert(reference.isForward);
|
|
666
716
|
// assert(reference.referenceType instanceof NodeId, "" + reference.referenceType.toString());
|
|
667
717
|
const options: CloneOptions = {
|
|
@@ -670,6 +720,13 @@ function _clone_collection_new(
|
|
|
670
720
|
copyAlsoModellingRules,
|
|
671
721
|
baseDeclarations: _base_declarations_of(nodeToClone, node.browseName)
|
|
672
722
|
};
|
|
723
|
+
// a member that the template declares under the node being cloned (its ParentNodeId), but links
|
|
724
|
+
// through a reference that does not aggregate it (Organizes), is named after that parent on the
|
|
725
|
+
// type (`DirectoryType_CertificateGroups`): the clone declares its own parent likewise, or it is
|
|
726
|
+
// named after its browse name alone and misses its id (`Directory_CertificateGroups`)
|
|
727
|
+
if (_isDeclaredNonAggregatedMemberOf(node, nodeToClone, reference)) {
|
|
728
|
+
options.parentNodeId = newParent.nodeId;
|
|
729
|
+
}
|
|
673
730
|
|
|
674
731
|
const alreadyCloned = extraInfo.getCloned({ originalParent: nodeToClone, clonedParent: newParent, originalNode: node });
|
|
675
732
|
if (alreadyCloned) {
|
package/impl/event_data.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { assert } from "node-opcua-assert";
|
|
|
7
7
|
import { coerceExpandedNodeId, NodeId, sameNodeId } from "node-opcua-nodeid";
|
|
8
8
|
import { StatusCodes } from "node-opcua-status-code";
|
|
9
9
|
import { type BrowsePath, BrowsePathResult } from "node-opcua-types";
|
|
10
|
-
import { lowerFirstLetter } from "node-opcua-utils";
|
|
10
|
+
import { lowerFirstLetter, setOwnProperty } from "node-opcua-utils";
|
|
11
11
|
import { Variant, type VariantLike } from "node-opcua-variant";
|
|
12
12
|
import type { EventField } from "./event_layout.js";
|
|
13
13
|
|
|
@@ -49,7 +49,7 @@ export class EventData implements IEventData {
|
|
|
49
49
|
/** the value of one field of the layout */
|
|
50
50
|
public _setField(field: EventField, variant: VariantLike): void {
|
|
51
51
|
const value = Variant.coerce(variant);
|
|
52
|
-
(this as Record<string, unknown
|
|
52
|
+
setOwnProperty(this as Record<string, unknown>, field.lowerName, value);
|
|
53
53
|
if (!this.#sharedPaths) {
|
|
54
54
|
this.#pathToNodeId.set(field.fullBrowsePath, field.node.nodeId);
|
|
55
55
|
}
|
package/impl/nodeid_manager.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { BaseNode, UAReference, UAReferenceType } from "node-opcua-address-space-base";
|
|
2
2
|
import { assert } from "node-opcua-assert";
|
|
3
|
-
import { NodeClass, type QualifiedName, type QualifiedNameOptions } from "node-opcua-data-model";
|
|
3
|
+
import { BrowseDirection, NodeClass, type QualifiedName, type QualifiedNameOptions } from "node-opcua-data-model";
|
|
4
4
|
import { make_debugLog, make_warningLog } from "node-opcua-debug";
|
|
5
5
|
import { makeNodeId, NodeId, type NodeIdLike, NodeIdType, resolveNodeId, sameNodeId } from "node-opcua-nodeid";
|
|
6
|
+
import { setOwnProperty } from "node-opcua-utils";
|
|
6
7
|
import { BaseNodeImpl } from "./base_node_impl.js";
|
|
7
8
|
import { type ReferenceImpl, resolveReferenceType } from "./reference_impl.js";
|
|
8
9
|
|
|
@@ -85,6 +86,16 @@ function _findParentNodeId(addressSpace: AddressSpacePartial, options: Construct
|
|
|
85
86
|
return references.length ? _filterAggregates(addressSpace, references) : null;
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
/** whether the node being built hangs, through an inverse reference, from a node `occupant` hangs from */
|
|
90
|
+
function _shareAnOrganizer(occupant: BaseNode, options: ConstructNodeIdOptions): boolean {
|
|
91
|
+
const references = (options.references ?? []).filter((r) => !r.isForward);
|
|
92
|
+
if (references.length === 0) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const parents = occupant.findReferencesEx("HierarchicalReferences", BrowseDirection.Inverse).map((r) => r.nodeId);
|
|
96
|
+
return references.some((r) => parents.some((p) => sameNodeId(p, r.nodeId)));
|
|
97
|
+
}
|
|
98
|
+
|
|
88
99
|
function prepareName(browseName?: QualifiedName | QualifiedNameOptions): string {
|
|
89
100
|
const m = browseName?.name?.toString().replace(/[ ]/g, "").replace(/(<|>)/g, "");
|
|
90
101
|
return m || "";
|
|
@@ -109,9 +120,14 @@ export interface ConstructNodeIdOptions {
|
|
|
109
120
|
export type NodeEntry = [string, number, NodeClass];
|
|
110
121
|
export type NodeEntry1 = [string, number, string /*"Object" | "Variable" etc...*/];
|
|
111
122
|
|
|
123
|
+
/** the separator of a disambiguated symbolic name (`X__2`): prepareName never produces it from a browse name */
|
|
124
|
+
export const SYMBOLIC_NAME_OCCURRENCE_SEPARATOR = "__";
|
|
125
|
+
|
|
112
126
|
export class NodeIdManager {
|
|
113
127
|
private _cacheSymbolicName: { [key: string]: [number, NodeClass] } = {};
|
|
114
128
|
private _cacheSymbolicNameRev: Set<number> = new Set<number>();
|
|
129
|
+
/** the symbolic name each id was registered under, so that a node's members are named after it */
|
|
130
|
+
private _nameOfId: Map<number, string> = new Map<number, string>();
|
|
115
131
|
|
|
116
132
|
private _internal_id_counter: number;
|
|
117
133
|
private namespaceIndex: number;
|
|
@@ -146,11 +162,53 @@ export class NodeIdManager {
|
|
|
146
162
|
NodeClass
|
|
147
163
|
][];
|
|
148
164
|
for (const [name, value, nodeClass] of symbols2) {
|
|
149
|
-
this._cacheSymbolicName
|
|
165
|
+
setOwnProperty(this._cacheSymbolicName, name, [value, nodeClass]);
|
|
150
166
|
this._cacheSymbolicNameRev.add(value);
|
|
167
|
+
if (!this._nameOfId.has(value)) {
|
|
168
|
+
this._nameOfId.set(value, name);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private _registerName(name: string, value: number, nodeClass: NodeClass | undefined): void {
|
|
174
|
+
setOwnProperty(this._cacheSymbolicName, name, [value, nodeClass || NodeClass.Unspecified]);
|
|
175
|
+
this._cacheSymbolicNameRev.add(value);
|
|
176
|
+
this._nameOfId.set(value, name);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The first `name__n` (n >= 2) a node may take: one the table does not list, or, for a node
|
|
181
|
+
* whose id is not fixed yet, one listed with an id that no node holds (a preset). A node whose
|
|
182
|
+
* id is fixed (`nodeId`) takes the name listed with that id, or a new one.
|
|
183
|
+
*/
|
|
184
|
+
private _nextFreeName(fullName: string, nodeId?: number): { name: string; value?: number } {
|
|
185
|
+
for (let n = 2; ; n++) {
|
|
186
|
+
const name = `${fullName}${SYMBOLIC_NAME_OCCURRENCE_SEPARATOR}${n}`;
|
|
187
|
+
const cached = this._cacheSymbolicName[name];
|
|
188
|
+
if (!cached) {
|
|
189
|
+
return { name };
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
nodeId === undefined
|
|
193
|
+
? !this.addressSpace.findNode(makeNodeId(cached[0], this.namespaceIndex))
|
|
194
|
+
: cached[0] === nodeId
|
|
195
|
+
) {
|
|
196
|
+
return { name, value: cached[0] };
|
|
197
|
+
}
|
|
151
198
|
}
|
|
152
199
|
}
|
|
153
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Whether a node whose symbolic name another node holds takes the next free `name__n`: a
|
|
203
|
+
* parentless node (its name is its browse name alone, which distinct nodes share), and a
|
|
204
|
+
* child whose browse name differs from its sibling's by the namespace only (`1:Speed` next to
|
|
205
|
+
* `0:Speed`), which the name does not spell. A child of a string NodeId is not named at all:
|
|
206
|
+
* its id is built from its parent's.
|
|
207
|
+
*/
|
|
208
|
+
private _mayTakeAnotherName(parentInfo: [NodeId, Suffix] | null): boolean {
|
|
209
|
+
return parentInfo === null || parentInfo[0].identifierType === NodeIdType.NUMERIC;
|
|
210
|
+
}
|
|
211
|
+
|
|
154
212
|
public getSymbols(): NodeEntry1[] {
|
|
155
213
|
const line: NodeEntry1[] = [];
|
|
156
214
|
for (const [key, [value, nodeClass1]] of Object.entries(this._cacheSymbolicName)) {
|
|
@@ -184,6 +242,13 @@ export class NodeIdManager {
|
|
|
184
242
|
};
|
|
185
243
|
|
|
186
244
|
const buildUpName2 = (nodeId: NodeId, suffix: string) => {
|
|
245
|
+
// a parent registered under a name of its own (`X__2`) passes that name on to its members
|
|
246
|
+
if (nodeId.namespace === this.namespaceIndex && nodeId.identifierType === NodeIdType.NUMERIC) {
|
|
247
|
+
const registered = this._nameOfId.get(nodeId.value as number);
|
|
248
|
+
if (registered !== undefined) {
|
|
249
|
+
return registered + suffix;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
187
252
|
const namespaceIndex = nodeId.namespace;
|
|
188
253
|
let name = "";
|
|
189
254
|
let n: BaseNode | null = this.addressSpace.findNode(nodeId);
|
|
@@ -203,14 +268,27 @@ export class NodeIdManager {
|
|
|
203
268
|
fullParentName = buildUpName2(parentNodeId, suffix);
|
|
204
269
|
}
|
|
205
270
|
const fullName = compose(fullParentName, prepareName(options.browseName));
|
|
271
|
+
let name = fullName;
|
|
206
272
|
const cached = this._cacheSymbolicName[fullName];
|
|
207
|
-
if (cached
|
|
208
|
-
|
|
273
|
+
if (cached) {
|
|
274
|
+
if (this._isCacheHitReusable(cached[0], options, parentInfo)) {
|
|
275
|
+
return makeNodeId(cached[0], this.namespaceIndex);
|
|
276
|
+
}
|
|
277
|
+
if (!this._mayTakeAnotherName(parentInfo)) {
|
|
278
|
+
// a fresh id, with no name
|
|
279
|
+
return this._constructNodeId(options);
|
|
280
|
+
}
|
|
281
|
+
// another node holds the name: this one takes the next free `name__n`
|
|
282
|
+
const free = this._nextFreeName(fullName);
|
|
283
|
+
if (free.value !== undefined) {
|
|
284
|
+
this._nameOfId.set(free.value, free.name);
|
|
285
|
+
return makeNodeId(free.value, this.namespaceIndex);
|
|
286
|
+
}
|
|
287
|
+
name = free.name;
|
|
209
288
|
}
|
|
210
289
|
const nodeId = this._constructNodeId(options);
|
|
211
|
-
if (nodeId.identifierType === NodeIdType.NUMERIC
|
|
212
|
-
this.
|
|
213
|
-
this._cacheSymbolicNameRev.add(nodeId.value as number);
|
|
290
|
+
if (nodeId.identifierType === NodeIdType.NUMERIC) {
|
|
291
|
+
this._registerName(name, nodeId.value as number, options.nodeClass);
|
|
214
292
|
}
|
|
215
293
|
return nodeId;
|
|
216
294
|
}
|
|
@@ -227,9 +305,17 @@ export class NodeIdManager {
|
|
|
227
305
|
fullParentName = buildUpName2(parentNodeId, suffix);
|
|
228
306
|
}
|
|
229
307
|
const fullName = compose(fullParentName, prepareName(options.browseName));
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
308
|
+
const value = nodeId.value as number;
|
|
309
|
+
const cached = this._cacheSymbolicName[fullName];
|
|
310
|
+
if (!cached) {
|
|
311
|
+
this._registerName(fullName, value, options.nodeClass);
|
|
312
|
+
} else if (cached[0] === value) {
|
|
313
|
+
this._nameOfId.set(value, fullName);
|
|
314
|
+
} else if (this._mayTakeAnotherName(parentInfo)) {
|
|
315
|
+
// another node holds the name (OPC 40502 CNC's three parentless X axes): this
|
|
316
|
+
// one is listed under the next free `name__n`, as it would be named when built
|
|
317
|
+
const free = this._nextFreeName(fullName, value);
|
|
318
|
+
this._registerName(free.name, value, options.nodeClass);
|
|
233
319
|
}
|
|
234
320
|
}
|
|
235
321
|
return nodeId;
|
|
@@ -268,9 +354,11 @@ export class NodeIdManager {
|
|
|
268
354
|
(occupant.browseName.namespaceIndex ?? 0) === (options.browseName?.namespaceIndex ?? 0);
|
|
269
355
|
|
|
270
356
|
const expectedParentNodeId = parentInfo ? parentInfo[0] : null;
|
|
357
|
+
// two parentless nodes are the same node only when one node organizes both (the same
|
|
358
|
+
// node declared twice in a folder); otherwise they merely share a browse name
|
|
271
359
|
const sameParent = expectedParentNodeId
|
|
272
360
|
? !!occupant.parentNodeId && sameNodeId(occupant.parentNodeId, expectedParentNodeId)
|
|
273
|
-
: !occupant.parentNodeId;
|
|
361
|
+
: !occupant.parentNodeId && _shareAnOrganizer(occupant, options);
|
|
274
362
|
|
|
275
363
|
return sameBrowseName && sameParent;
|
|
276
364
|
}
|
|
@@ -132,7 +132,23 @@ function dumpDataTypeStructure(
|
|
|
132
132
|
xw.endElement();
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* whether the 1.03 dictionary describes `dataType`: it lists Structures and Enumerations only. A
|
|
137
|
+
* basic type's subtype (OPC 30010 AutoID's CodeTypeDataType, a String) has no definition at all,
|
|
138
|
+
* and a bit-mask OptionSet's lists bits, not values: asking for them threw.
|
|
139
|
+
*/
|
|
140
|
+
function isDescribedInBSD(dataType: UADataType): boolean {
|
|
141
|
+
if (dataType.isStructure()) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
const isOptionSet = !!(dataType as UADataType & { isOptionSetDataType?: boolean }).isOptionSetDataType;
|
|
145
|
+
return !isOptionSet && dataType.isEnumeration();
|
|
146
|
+
}
|
|
147
|
+
|
|
135
148
|
function dumpDataTypeToBSD(xw: XmlWriter, dataType: UADataType, map: Map<number, string>) {
|
|
149
|
+
if (!isDescribedInBSD(dataType)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
136
152
|
const addressSpace = dataType.addressSpace;
|
|
137
153
|
|
|
138
154
|
const name: string = dataType.browseName.name || "";
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
type StructureDefinition,
|
|
46
46
|
StructureType
|
|
47
47
|
} from "node-opcua-types";
|
|
48
|
-
import { isNullOrUndefined } from "node-opcua-utils";
|
|
48
|
+
import { isNullOrUndefined, setOwnProperty } from "node-opcua-utils";
|
|
49
49
|
import { DataType, Variant, VariantArrayType, type VariantOptions } from "node-opcua-variant";
|
|
50
50
|
import XMLWriter from "xml-writer";
|
|
51
51
|
import { makeDefinitionMap } from "../../api/loader/decode_xml_extension_object.js";
|
|
@@ -274,14 +274,14 @@ class RecordExporter {
|
|
|
274
274
|
if (nodeV.dataType && nodeV.dataType.value !== 0) {
|
|
275
275
|
const dataTypeName = this.b(this.resolveDataTypeName(nodeV.dataType));
|
|
276
276
|
if (dataTypeName && !aliases[dataTypeName]) {
|
|
277
|
-
aliases
|
|
277
|
+
setOwnProperty(aliases, dataTypeName, this.t(nodeV.dataType));
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
for (const reference of node.allReferences()) {
|
|
282
282
|
const key = this.b(getReferenceType(reference).browseName);
|
|
283
283
|
// the XML export overwrites a reference-type alias each time it meets it; same value every time
|
|
284
|
-
aliases
|
|
284
|
+
setOwnProperty(aliases, key, this.t(reference.referenceType));
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
@@ -366,6 +366,9 @@ class RecordExporter {
|
|
|
366
366
|
if (node.releaseStatus) {
|
|
367
367
|
record.releaseStatus = node.releaseStatus;
|
|
368
368
|
}
|
|
369
|
+
if (node.designToolOnly) {
|
|
370
|
+
record.designToolOnly = true;
|
|
371
|
+
}
|
|
369
372
|
if (node.nodesetCategory && node.nodesetCategory.length > 0) {
|
|
370
373
|
record.category = [...node.nodesetCategory];
|
|
371
374
|
}
|
|
@@ -685,6 +685,9 @@ function dumpCommonAttributes(xw: XmlWriter, node: BaseNode) {
|
|
|
685
685
|
if (node.releaseStatus) {
|
|
686
686
|
xw.writeAttribute("ReleaseStatus", node.releaseStatus);
|
|
687
687
|
}
|
|
688
|
+
if (node.designToolOnly) {
|
|
689
|
+
xw.writeAttribute("DesignToolOnly", "true");
|
|
690
|
+
}
|
|
688
691
|
if (Object.hasOwn(node, "isAbstract")) {
|
|
689
692
|
const isAbstract = (node as unknown as { isAbstract: boolean }).isAbstract;
|
|
690
693
|
if (isAbstract) {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type StructureFieldOptions,
|
|
22
22
|
StructureType
|
|
23
23
|
} from "node-opcua-types";
|
|
24
|
+
import { setOwnProperty } from "node-opcua-utils";
|
|
24
25
|
import { DataType } from "node-opcua-variant";
|
|
25
26
|
import type { ExtensionObjectConstructorFuncWithSchema } from "../api/interfaces/extension_object_constructor.js";
|
|
26
27
|
import { SessionContext } from "../api/session_context.js";
|
|
@@ -279,7 +280,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
279
280
|
valueIndex: {}
|
|
280
281
|
};
|
|
281
282
|
for (const e of definition) {
|
|
282
|
-
indexes.nameIndex
|
|
283
|
+
setOwnProperty(indexes.nameIndex, e.name, e);
|
|
283
284
|
indexes.valueIndex[e.value] = e;
|
|
284
285
|
}
|
|
285
286
|
return indexes;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-address-space",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.186.
|
|
4
|
+
"version": "2.186.2",
|
|
5
5
|
"description": "pure nodejs OPCUA SDK - module address-space",
|
|
6
6
|
"main": "./dist/api/index.js",
|
|
7
7
|
"types": "./dist/api/index.d.ts",
|
|
@@ -23,48 +23,48 @@
|
|
|
23
23
|
"@types/semver": "7.8.0",
|
|
24
24
|
"chalk": "4.1.2",
|
|
25
25
|
"dequeue": "^1.0.5",
|
|
26
|
-
"node-opcua-address-space-base": "2.186.
|
|
26
|
+
"node-opcua-address-space-base": "2.186.2",
|
|
27
27
|
"node-opcua-assert": "2.184.0",
|
|
28
|
-
"node-opcua-basic-types": "2.
|
|
29
|
-
"node-opcua-binary-stream": "2.
|
|
30
|
-
"node-opcua-client-dynamic-extension-object": "2.
|
|
28
|
+
"node-opcua-basic-types": "2.186.1",
|
|
29
|
+
"node-opcua-binary-stream": "2.186.1",
|
|
30
|
+
"node-opcua-client-dynamic-extension-object": "2.186.1",
|
|
31
31
|
"node-opcua-constants": "2.185.0",
|
|
32
32
|
"node-opcua-crypto": "6.0.0",
|
|
33
|
-
"node-opcua-data-access": "2.
|
|
34
|
-
"node-opcua-data-model": "2.
|
|
35
|
-
"node-opcua-data-value": "2.
|
|
36
|
-
"node-opcua-date-time": "2.
|
|
33
|
+
"node-opcua-data-access": "2.186.1",
|
|
34
|
+
"node-opcua-data-model": "2.186.1",
|
|
35
|
+
"node-opcua-data-value": "2.186.1",
|
|
36
|
+
"node-opcua-date-time": "2.186.1",
|
|
37
37
|
"node-opcua-debug": "2.184.0",
|
|
38
|
-
"node-opcua-enum": "2.
|
|
39
|
-
"node-opcua-extension-object": "2.
|
|
40
|
-
"node-opcua-factory": "2.
|
|
41
|
-
"node-opcua-nodeid": "2.
|
|
42
|
-
"node-opcua-nodeset-ua": "2.186.
|
|
43
|
-
"node-opcua-numeric-range": "2.
|
|
38
|
+
"node-opcua-enum": "2.186.1",
|
|
39
|
+
"node-opcua-extension-object": "2.186.1",
|
|
40
|
+
"node-opcua-factory": "2.186.1",
|
|
41
|
+
"node-opcua-nodeid": "2.186.1",
|
|
42
|
+
"node-opcua-nodeset-ua": "2.186.2",
|
|
43
|
+
"node-opcua-numeric-range": "2.186.1",
|
|
44
44
|
"node-opcua-object-registry": "2.184.0",
|
|
45
|
-
"node-opcua-pseudo-session": "2.
|
|
46
|
-
"node-opcua-service-browse": "2.
|
|
47
|
-
"node-opcua-service-call": "2.
|
|
48
|
-
"node-opcua-service-history": "2.
|
|
49
|
-
"node-opcua-service-translate-browse-path": "2.
|
|
50
|
-
"node-opcua-service-write": "2.
|
|
51
|
-
"node-opcua-status-code": "2.
|
|
52
|
-
"node-opcua-types": "2.
|
|
53
|
-
"node-opcua-utils": "2.
|
|
54
|
-
"node-opcua-variant": "2.
|
|
55
|
-
"node-opcua-xml2json": "2.
|
|
45
|
+
"node-opcua-pseudo-session": "2.186.1",
|
|
46
|
+
"node-opcua-service-browse": "2.186.1",
|
|
47
|
+
"node-opcua-service-call": "2.186.1",
|
|
48
|
+
"node-opcua-service-history": "2.186.1",
|
|
49
|
+
"node-opcua-service-translate-browse-path": "2.186.1",
|
|
50
|
+
"node-opcua-service-write": "2.186.1",
|
|
51
|
+
"node-opcua-status-code": "2.186.1",
|
|
52
|
+
"node-opcua-types": "2.186.1",
|
|
53
|
+
"node-opcua-utils": "2.186.1",
|
|
54
|
+
"node-opcua-variant": "2.186.1",
|
|
55
|
+
"node-opcua-xml2json": "2.186.1",
|
|
56
56
|
"semver": "^7.8.5",
|
|
57
57
|
"thenify-ex": "4.4.0",
|
|
58
58
|
"xml-writer": "^1.7.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"humanize": "0.0.9",
|
|
62
|
-
"node-opcua-benchmarker": "2.
|
|
62
|
+
"node-opcua-benchmarker": "2.186.1",
|
|
63
63
|
"node-opcua-leak-detector": "2.184.1",
|
|
64
64
|
"node-opcua-nodesets": "2.185.0",
|
|
65
|
-
"node-opcua-packet-analyzer": "2.
|
|
66
|
-
"node-opcua-schemas": "2.
|
|
67
|
-
"node-opcua-service-filter": "2.186.
|
|
65
|
+
"node-opcua-packet-analyzer": "2.186.1",
|
|
66
|
+
"node-opcua-schemas": "2.186.1",
|
|
67
|
+
"node-opcua-service-filter": "2.186.2",
|
|
68
68
|
"node-opcua-test-fixtures": "2.184.0",
|
|
69
69
|
"node-opcua-test-helpers": "2.184.0"
|
|
70
70
|
},
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"internet of things"
|
|
84
84
|
],
|
|
85
85
|
"homepage": "http://node-opcua.github.io/",
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "86af5eac683e2b11900c1e93c232d4adc06b5b77",
|
|
87
87
|
"files": [
|
|
88
88
|
"dist",
|
|
89
89
|
"distHelpers",
|