node-opcua-address-space 2.186.1 → 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.
Files changed (52) hide show
  1. package/api/loader/nodeset_record.ts +2 -0
  2. package/api/loader/nodeset_record_applier.ts +1 -0
  3. package/api/loader/nodeset_records_to_xml.ts +3 -0
  4. package/api/loader/nodeset_xml_producer.ts +1 -0
  5. package/dist/api/loader/nodeset_record.d.ts +2 -0
  6. package/dist/api/loader/nodeset_record.d.ts.map +1 -1
  7. package/dist/api/loader/nodeset_record.js.map +1 -1
  8. package/dist/api/loader/nodeset_record_applier.d.ts.map +1 -1
  9. package/dist/api/loader/nodeset_record_applier.js +1 -0
  10. package/dist/api/loader/nodeset_record_applier.js.map +1 -1
  11. package/dist/api/loader/nodeset_records_to_xml.js +3 -0
  12. package/dist/api/loader/nodeset_records_to_xml.js.map +1 -1
  13. package/dist/api/loader/nodeset_xml_producer.d.ts.map +1 -1
  14. package/dist/api/loader/nodeset_xml_producer.js +2 -0
  15. package/dist/api/loader/nodeset_xml_producer.js.map +1 -1
  16. package/dist/impl/_instantiate_helpers.d.ts.map +1 -1
  17. package/dist/impl/_instantiate_helpers.js +6 -1
  18. package/dist/impl/_instantiate_helpers.js.map +1 -1
  19. package/dist/impl/_mandatory_child_or_requested_optional_filter.d.ts +7 -1
  20. package/dist/impl/_mandatory_child_or_requested_optional_filter.d.ts.map +1 -1
  21. package/dist/impl/_mandatory_child_or_requested_optional_filter.js +14 -3
  22. package/dist/impl/_mandatory_child_or_requested_optional_filter.js.map +1 -1
  23. package/dist/impl/base_node_impl.d.ts +4 -0
  24. package/dist/impl/base_node_impl.d.ts.map +1 -1
  25. package/dist/impl/base_node_impl.js +5 -0
  26. package/dist/impl/base_node_impl.js.map +1 -1
  27. package/dist/impl/base_node_private.d.ts.map +1 -1
  28. package/dist/impl/base_node_private.js +52 -0
  29. package/dist/impl/base_node_private.js.map +1 -1
  30. package/dist/impl/nodeid_manager.d.ts +19 -0
  31. package/dist/impl/nodeid_manager.d.ts.map +1 -1
  32. package/dist/impl/nodeid_manager.js +92 -16
  33. package/dist/impl/nodeid_manager.js.map +1 -1
  34. package/dist/impl/nodeset_tools/dump_to_bsd.d.ts.map +1 -1
  35. package/dist/impl/nodeset_tools/dump_to_bsd.js +15 -0
  36. package/dist/impl/nodeset_tools/dump_to_bsd.js.map +1 -1
  37. package/dist/impl/nodeset_tools/nodeset_to_records.d.ts.map +1 -1
  38. package/dist/impl/nodeset_tools/nodeset_to_records.js +3 -0
  39. package/dist/impl/nodeset_tools/nodeset_to_records.js.map +1 -1
  40. package/dist/impl/nodeset_tools/nodeset_to_xml.d.ts.map +1 -1
  41. package/dist/impl/nodeset_tools/nodeset_to_xml.js +3 -0
  42. package/dist/impl/nodeset_tools/nodeset_to_xml.js.map +1 -1
  43. package/dist/tsconfig_base.tsbuildinfo +1 -1
  44. package/impl/_instantiate_helpers.ts +12 -1
  45. package/impl/_mandatory_child_or_requested_optional_filter.ts +20 -3
  46. package/impl/base_node_impl.ts +7 -0
  47. package/impl/base_node_private.ts +57 -0
  48. package/impl/nodeid_manager.ts +97 -16
  49. package/impl/nodeset_tools/dump_to_bsd.ts +16 -0
  50. package/impl/nodeset_tools/nodeset_to_records.ts +3 -0
  51. package/impl/nodeset_tools/nodeset_to_xml.ts +3 -0
  52. package/package.json +5 -5
@@ -61,7 +61,13 @@ function _initialize_properties_and_components<B extends UAObject | UAVariable |
61
61
  return; // nothing to do
62
62
  }
63
63
 
64
- const filter = new MandatoryChildOrRequestedOptionalFilter(instance, copyAlsoAllOptionals, optionalsMap);
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
- return false; // ignored
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
- const newFilter = new MandatoryChildOrRequestedOptionalFilter(childInstance, false, map);
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
  }
@@ -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) {
@@ -1,6 +1,6 @@
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
6
  import { setOwnProperty } from "node-opcua-utils";
@@ -86,6 +86,16 @@ function _findParentNodeId(addressSpace: AddressSpacePartial, options: Construct
86
86
  return references.length ? _filterAggregates(addressSpace, references) : null;
87
87
  }
88
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
+
89
99
  function prepareName(browseName?: QualifiedName | QualifiedNameOptions): string {
90
100
  const m = browseName?.name?.toString().replace(/[ ]/g, "").replace(/(<|>)/g, "");
91
101
  return m || "";
@@ -110,9 +120,14 @@ export interface ConstructNodeIdOptions {
110
120
  export type NodeEntry = [string, number, NodeClass];
111
121
  export type NodeEntry1 = [string, number, string /*"Object" | "Variable" etc...*/];
112
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
+
113
126
  export class NodeIdManager {
114
127
  private _cacheSymbolicName: { [key: string]: [number, NodeClass] } = {};
115
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>();
116
131
 
117
132
  private _internal_id_counter: number;
118
133
  private namespaceIndex: number;
@@ -149,9 +164,51 @@ export class NodeIdManager {
149
164
  for (const [name, value, nodeClass] of symbols2) {
150
165
  setOwnProperty(this._cacheSymbolicName, name, [value, nodeClass]);
151
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
+ }
152
198
  }
153
199
  }
154
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
+
155
212
  public getSymbols(): NodeEntry1[] {
156
213
  const line: NodeEntry1[] = [];
157
214
  for (const [key, [value, nodeClass1]] of Object.entries(this._cacheSymbolicName)) {
@@ -185,6 +242,13 @@ export class NodeIdManager {
185
242
  };
186
243
 
187
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
+ }
188
252
  const namespaceIndex = nodeId.namespace;
189
253
  let name = "";
190
254
  let n: BaseNode | null = this.addressSpace.findNode(nodeId);
@@ -204,17 +268,27 @@ export class NodeIdManager {
204
268
  fullParentName = buildUpName2(parentNodeId, suffix);
205
269
  }
206
270
  const fullName = compose(fullParentName, prepareName(options.browseName));
271
+ let name = fullName;
207
272
  const cached = this._cacheSymbolicName[fullName];
208
- if (cached && this._isCacheHitReusable(cached[0], options, parentInfo)) {
209
- return makeNodeId(cached[0], this.namespaceIndex);
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;
210
288
  }
211
289
  const nodeId = this._constructNodeId(options);
212
- if (nodeId.identifierType === NodeIdType.NUMERIC && !cached) {
213
- setOwnProperty(this._cacheSymbolicName, fullName, [
214
- nodeId.value as number,
215
- options.nodeClass || NodeClass.Unspecified
216
- ]);
217
- this._cacheSymbolicNameRev.add(nodeId.value as number);
290
+ if (nodeId.identifierType === NodeIdType.NUMERIC) {
291
+ this._registerName(name, nodeId.value as number, options.nodeClass);
218
292
  }
219
293
  return nodeId;
220
294
  }
@@ -231,12 +305,17 @@ export class NodeIdManager {
231
305
  fullParentName = buildUpName2(parentNodeId, suffix);
232
306
  }
233
307
  const fullName = compose(fullParentName, prepareName(options.browseName));
234
- if (!this._cacheSymbolicName[fullName]) {
235
- setOwnProperty(this._cacheSymbolicName, fullName, [
236
- nodeId.value as number,
237
- options.nodeClass || NodeClass.Unspecified
238
- ]);
239
- this._cacheSymbolicNameRev.add(nodeId.value as number);
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);
240
319
  }
241
320
  }
242
321
  return nodeId;
@@ -275,9 +354,11 @@ export class NodeIdManager {
275
354
  (occupant.browseName.namespaceIndex ?? 0) === (options.browseName?.namespaceIndex ?? 0);
276
355
 
277
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
278
359
  const sameParent = expectedParentNodeId
279
360
  ? !!occupant.parentNodeId && sameNodeId(occupant.parentNodeId, expectedParentNodeId)
280
- : !occupant.parentNodeId;
361
+ : !occupant.parentNodeId && _shareAnOrganizer(occupant, options);
281
362
 
282
363
  return sameBrowseName && sameParent;
283
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 || "";
@@ -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) {
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.1",
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,7 +23,7 @@
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.1",
26
+ "node-opcua-address-space-base": "2.186.2",
27
27
  "node-opcua-assert": "2.184.0",
28
28
  "node-opcua-basic-types": "2.186.1",
29
29
  "node-opcua-binary-stream": "2.186.1",
@@ -39,7 +39,7 @@
39
39
  "node-opcua-extension-object": "2.186.1",
40
40
  "node-opcua-factory": "2.186.1",
41
41
  "node-opcua-nodeid": "2.186.1",
42
- "node-opcua-nodeset-ua": "2.186.1",
42
+ "node-opcua-nodeset-ua": "2.186.2",
43
43
  "node-opcua-numeric-range": "2.186.1",
44
44
  "node-opcua-object-registry": "2.184.0",
45
45
  "node-opcua-pseudo-session": "2.186.1",
@@ -64,7 +64,7 @@
64
64
  "node-opcua-nodesets": "2.185.0",
65
65
  "node-opcua-packet-analyzer": "2.186.1",
66
66
  "node-opcua-schemas": "2.186.1",
67
- "node-opcua-service-filter": "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": "c4b15b4ca645101b2d656a75b2ea9a8a843d25b5",
86
+ "gitHead": "86af5eac683e2b11900c1e93c232d4adc06b5b77",
87
87
  "files": [
88
88
  "dist",
89
89
  "distHelpers",