node-opcua-address-space 2.80.0 → 2.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/source/loader/ensure_datatype_extracted.js +1 -1
  2. package/dist/source/loader/ensure_datatype_extracted.js.map +1 -1
  3. package/dist/source/loader/load_nodeset2.js +9 -22
  4. package/dist/source/loader/load_nodeset2.js.map +1 -1
  5. package/dist/source/xml_writer.d.ts +5 -1
  6. package/dist/src/address_space.js +1 -1
  7. package/dist/src/address_space.js.map +1 -1
  8. package/dist/src/index_current.d.ts +1 -1
  9. package/dist/src/index_current.js +2 -3
  10. package/dist/src/index_current.js.map +1 -1
  11. package/dist/src/namespace_impl.d.ts +4 -1
  12. package/dist/src/namespace_impl.js +10 -2
  13. package/dist/src/namespace_impl.js.map +1 -1
  14. package/dist/src/namespace_private.d.ts +2 -1
  15. package/dist/src/namespace_private.js.map +1 -1
  16. package/dist/src/nodeset_tools/construct_namespace_dependency.d.ts +3 -1
  17. package/dist/src/nodeset_tools/construct_namespace_dependency.js +35 -5
  18. package/dist/src/nodeset_tools/construct_namespace_dependency.js.map +1 -1
  19. package/dist/src/nodeset_tools/nodeset_to_xml.d.ts +4 -2
  20. package/dist/src/nodeset_tools/nodeset_to_xml.js +44 -59
  21. package/dist/src/nodeset_tools/nodeset_to_xml.js.map +1 -1
  22. package/dist/src/ua_data_type_impl.d.ts +2 -0
  23. package/dist/src/ua_data_type_impl.js +1 -0
  24. package/dist/src/ua_data_type_impl.js.map +1 -1
  25. package/dist/src/ua_variable_impl.d.ts +2 -0
  26. package/dist/src/ua_variable_impl.js +49 -11
  27. package/dist/src/ua_variable_impl.js.map +1 -1
  28. package/package.json +39 -39
  29. package/source/loader/ensure_datatype_extracted.ts +1 -1
  30. package/source/loader/load_nodeset2.ts +22 -36
  31. package/source/xml_writer.ts +5 -1
  32. package/src/address_space.ts +1 -2
  33. package/src/index_current.ts +1 -1
  34. package/src/namespace_impl.ts +13 -3
  35. package/src/namespace_private.ts +7 -1
  36. package/src/nodeset_tools/construct_namespace_dependency.ts +42 -5
  37. package/src/nodeset_tools/nodeset_to_xml.ts +57 -84
  38. package/src/ua_data_type_impl.ts +3 -0
  39. package/src/ua_variable_impl.ts +74 -31
  40. package/test_helpers/test_fixtures/dataType_with_union.xml +90 -1
  41. package/test_helpers/test_fixtures/datatype_as_per_1.04.xml +38 -10
@@ -10,14 +10,18 @@ import {
10
10
  CreateNodeOptions,
11
11
  ModellingRuleType,
12
12
  INamespace,
13
- UADataType
13
+ UADataType,
14
+ RequiredModel
14
15
  } from "node-opcua-address-space-base";
15
16
 
16
17
  import { AddressSpacePrivate } from "./address_space_private";
17
18
 
19
+
18
20
  export interface NamespacePrivate extends INamespace {
19
21
  addressSpace: AddressSpacePrivate;
20
22
 
23
+ setRequiredModels(requiredModels: RequiredModel[]): void;
24
+
21
25
  nodeIterator(): IterableIterator<BaseNode>;
22
26
 
23
27
  constructNodeId(options: ConstructNodeIdOptions): NodeId;
@@ -35,6 +39,8 @@ export interface NamespacePrivate extends INamespace {
35
39
  _dataTypeIterator(): IterableIterator<UADataType>;
36
40
 
37
41
  registerSymbolicNames: boolean;
42
+
43
+
38
44
  }
39
45
 
40
46
  export declare const NamespacePrivate: new (options: any) => NamespacePrivate;
@@ -8,19 +8,23 @@ function _constructNamespaceDependency(
8
8
  namespace: INamespace,
9
9
  dependency: INamespace[],
10
10
  depMap: Set<number>,
11
- _visitedDataType: Set<string>
11
+ _visitedDataType: Set<string>,
12
+ priorityTable: number[]
12
13
  ): void {
13
14
  const addressSpace = namespace.addressSpace;
14
15
  const namespace_ = namespace as NamespacePrivate;
15
16
  // navigate all namespace recursively to
16
17
 
17
18
  function consider(namespaceIndex: number) {
19
+ if (hasHigherPriorityThan(namespaceIndex,namespace.index, priorityTable)) {
20
+ return;
21
+ }
18
22
  if (!depMap.has(namespaceIndex)) {
19
23
  depMap.add(namespaceIndex);
20
24
  const namespace = addressSpace.getNamespace(namespaceIndex);
21
25
  dependency.push(namespace);
22
26
  if (namespaceIndex > 0) {
23
- _constructNamespaceDependency(namespace, dependency, depMap, _visitedDataType);
27
+ _constructNamespaceDependency(namespace, dependency, depMap, _visitedDataType, priorityTable);
24
28
  }
25
29
  }
26
30
  }
@@ -57,7 +61,7 @@ function _constructNamespaceDependency(
57
61
  exploreDataTypes(dataTypeNode);
58
62
  } else {
59
63
  // istanbul ignore next
60
- if (dataTypeNodeId.value!=0) {
64
+ if (dataTypeNodeId.value != 0) {
61
65
  console.log("Internal error: Cannot find dataType", dataTypeNodeId.toString());
62
66
  }
63
67
  }
@@ -74,8 +78,41 @@ function _constructNamespaceDependency(
74
78
  }
75
79
  }
76
80
 
77
- export function constructNamespaceDependency(namespace: INamespace): INamespace[] {
81
+
82
+ export function hasHigherPriorityThan(namespaceIndex1: number, namespaceIndex2: number, priorityTable: number[]) {
83
+ const order1 = priorityTable[namespaceIndex1];
84
+ const order2 = priorityTable[namespaceIndex2];
85
+ return order1 > order2;
86
+ }
87
+
88
+ export function constructNamespacePriorityTable(namespace: INamespace): number[] {
89
+
90
+ // Namespace 0 will always be 0
91
+ // Namespaces with no requiredModel will be considered as instance namespaces and will added at the end
92
+ // in the same order as they appear,
93
+ // Namespace with requiredModels are considered to be companion specification, so already loaded in the correct order
94
+
95
+ const addressSpace = namespace.addressSpace;
96
+ const namespaces = addressSpace.getNamespaceArray();
97
+
98
+ const namespaceWithReq = namespaces.filter((n)=> (n.getRequiredModels() !== undefined) && n.index !==0);
99
+ const namespaceWithoutReq = namespaces.filter((n)=>(n.getRequiredModels() === undefined) && n.index !==0);
100
+
101
+ const priorityList: number[] = [0];
102
+ let counter = 1;
103
+ for (let i = 0; i < namespaceWithReq.length; i++) {
104
+ priorityList[namespaceWithReq[i].index] = counter++;
105
+ }
106
+ for (let i = 0; i < namespaceWithoutReq.length; i++) {
107
+ priorityList[namespaceWithoutReq[i].index] = counter++;
108
+ }
109
+ return priorityList;
110
+ }
111
+
112
+ export function constructNamespaceDependency(namespace: INamespace, priorityTable?: number[]): INamespace[] {
78
113
  const addressSpace = namespace.addressSpace;
114
+
115
+ priorityTable = priorityTable || constructNamespacePriorityTable(namespace);
79
116
 
80
117
  const dependency: INamespace[] = [];
81
118
  const depMap = new Set<number>();
@@ -89,7 +126,7 @@ export function constructNamespaceDependency(namespace: INamespace): INamespace[
89
126
  }
90
127
  const _visitedDataType = new Set<string>();
91
128
 
92
- _constructNamespaceDependency(namespace, dependency, depMap, _visitedDataType);
129
+ _constructNamespaceDependency(namespace, dependency, depMap, _visitedDataType, priorityTable);
93
130
 
94
131
  return dependency;
95
132
  }
@@ -34,7 +34,7 @@ import {
34
34
  import { AttributeIds, Int64, minOPCUADate, StatusCode, StatusCodes } from "node-opcua-basic-types";
35
35
  import { BrowseDescription, EnumDefinition, StructureDefinition, StructureType } from "node-opcua-types";
36
36
 
37
- import { XmlWriter } from "../../source/xml_writer";
37
+ import { ITranslationTable, XmlWriter } from "../../source/xml_writer";
38
38
  import { NamespacePrivate } from "../namespace_private";
39
39
  import { ReferenceImpl } from "../reference_impl";
40
40
  import { BaseNodeImpl, getReferenceType } from "../base_node_impl";
@@ -50,7 +50,7 @@ import { SessionContext } from "../index_current";
50
50
 
51
51
  import { DefinitionMap2, TypeInfo } from "../../source/loader/make_xml_extension_object_parser";
52
52
  import { makeDefinitionMap } from "../../source/loader/decode_xml_extension_object";
53
- import { constructNamespaceDependency } from "./construct_namespace_dependency";
53
+ import { constructNamespaceDependency, constructNamespacePriorityTable, hasHigherPriorityThan } from "./construct_namespace_dependency";
54
54
 
55
55
  // tslint:disable:no-var-requires
56
56
  const XMLWriter = require("xml-writer");
@@ -98,17 +98,20 @@ function b(xw: XmlWriter, browseName: QualifiedName): string {
98
98
  return translateBrowseName(xw, browseName).toString().replace("ns=0;", "");
99
99
  }
100
100
 
101
- function _dumpReverseReferences(xw: XmlWriter, node: BaseNode) {
102
- const addressSpace = node.addressSpace;
103
- const hasSubtypeReferenceType = addressSpace.findReferenceType("HasSubtype")!;
101
+ function _hasHigherPriorityThan(xw: XmlWriter, namespaceIndex1: number, namespaceIndex2: number) {
102
+ assert(xw.priorityTable, "expecting a priorityTable");
103
+ assert(namespaceIndex1 < xw.priorityTable.length);
104
+ assert(namespaceIndex2 < xw.priorityTable.length);
105
+ return hasHigherPriorityThan(namespaceIndex1, namespaceIndex2, xw.priorityTable);
104
106
  }
107
+
105
108
  function _dumpReferences(xw: XmlWriter, node: BaseNode) {
106
109
  xw.startElement("References");
107
110
 
108
111
  const addressSpace = node.addressSpace;
109
112
 
110
113
  const aggregateReferenceType = addressSpace.findReferenceType("Aggregates")!;
111
- const hasChildReferenceType = addressSpace.findReferenceType("HasChild")!;
114
+ // const hasChildReferenceType = addressSpace.findReferenceType("HasChild")!;
112
115
  const hasSubtypeReferenceType = addressSpace.findReferenceType("HasSubtype")!;
113
116
  const hasTypeDefinitionReferenceType = addressSpace.findReferenceType("HasTypeDefinition")!;
114
117
  const nonHierarchicalReferencesType = addressSpace.findReferenceType("NonHierarchicalReferences")!;
@@ -119,6 +122,13 @@ function _dumpReferences(xw: XmlWriter, node: BaseNode) {
119
122
  function referenceToKeep(reference: UAReference): boolean {
120
123
  const referenceType = (reference as ReferenceImpl)._referenceType!;
121
124
 
125
+ const targetedNamespaceIndex = reference.nodeId.namespace;
126
+ if (_hasHigherPriorityThan(xw, targetedNamespaceIndex, node.nodeId.namespace)) {
127
+ // this reference has nothing to do here ! drop it
128
+ // because the target namespace is higher in the hierarchy
129
+ return false;
130
+ }
131
+
122
132
  // get the direct backward reference to a external namespace
123
133
  if (referenceType.isSupertypeOf(aggregateReferenceType) && !reference.isForward) {
124
134
  if (reference.nodeId.namespace !== node.nodeId.namespace) {
@@ -128,7 +138,9 @@ function _dumpReferences(xw: XmlWriter, node: BaseNode) {
128
138
  return true;
129
139
  }
130
140
  }
131
-
141
+ if (referenceType.isSupertypeOf(hasSubtypeReferenceType) && reference.isForward) {
142
+ // return false;
143
+ }
132
144
  // only keep
133
145
  if (referenceType.isSupertypeOf(aggregateReferenceType) && reference.isForward) {
134
146
  return true;
@@ -154,8 +166,6 @@ function _dumpReferences(xw: XmlWriter, node: BaseNode) {
154
166
  if (getReferenceType(reference).browseName.toString() === "HasSubtype" && reference.isForward) {
155
167
  continue;
156
168
  }
157
-
158
- // only output inverse Reference
159
169
  xw.startElement("Reference");
160
170
 
161
171
  xw.writeAttribute("ReferenceType", b(xw, getReferenceType(reference).browseName));
@@ -665,7 +675,6 @@ function _dumpArrayDimensionsAttribute(xw: XmlWriter, node: UAVariableType | UAV
665
675
  }
666
676
 
667
677
  function visitUANode(node: BaseNode, data: DumpData, forward: boolean) {
668
- assert(typeof forward === "boolean");
669
678
 
670
679
  const addressSpace = node.addressSpace;
671
680
 
@@ -772,7 +781,9 @@ function dumpCommonAttributes(xw: XmlWriter, node: BaseNode) {
772
781
 
773
782
  const parentNode = getParent(node);
774
783
  if (parentNode) {
775
- xw.writeAttribute("ParentNodeId", n(xw, parentNode.nodeId));
784
+ if (parentNode.nodeId.namespace <= node.nodeId.namespace) {
785
+ xw.writeAttribute("ParentNodeId", n(xw, parentNode.nodeId));
786
+ }
776
787
  }
777
788
  if (Object.prototype.hasOwnProperty.call(node, "symbolicName")) {
778
789
  xw.writeAttribute("SymbolicName", (node as any).symbolicName);
@@ -788,6 +799,12 @@ function dumpCommonAttributes(xw: XmlWriter, node: BaseNode) {
788
799
  xw.writeAttribute("AccessLevel", (node as UAVariable).accessLevel.toString());
789
800
  }
790
801
  }
802
+ if (Object.prototype.hasOwnProperty.call(node, "minimumSamplingInterval")) {
803
+ const minimumSamplingInterval =(node as UAVariable).minimumSamplingInterval;
804
+ if (minimumSamplingInterval > 0) {
805
+ xw.writeAttribute("MinimumSamplingInterval", minimumSamplingInterval);
806
+ }
807
+ }
791
808
  }
792
809
 
793
810
  function dumpCommonElements(xw: XmlWriter, node: BaseNode) {
@@ -800,11 +817,11 @@ function coerceInt64ToInt32(int64: Int64): number {
800
817
  if (typeof int64 === "number") {
801
818
  return int64 as number;
802
819
  }
803
- if (int64[0] === 4294967295 && int64[1] === 4294967295) {
820
+ if (int64[0] === 0xffffffff && int64[1] === 0xffffffff) {
804
821
  return 0xffffffff;
805
822
  }
806
823
  if (int64[0] !== 0) {
807
- debugLog("coerceInt64ToInt32 , loosing high word in conversion");
824
+ warningLog("coerceInt64ToInt32 , loosing high word in conversion");
808
825
  }
809
826
  return int64[1];
810
827
  }
@@ -831,8 +848,8 @@ function _dumpStructureDefinition(
831
848
  * note: baseDataType and defaultEncodingId are implicit and not stored in the XML file ??
832
849
  *
833
850
  */
834
- const baseDataType = structureDefinition.baseDataType;
835
- const defaultEncodingId = structureDefinition.defaultEncodingId;
851
+ // const baseDataType = structureDefinition.baseDataType;
852
+ // const defaultEncodingId = structureDefinition.defaultEncodingId;
836
853
 
837
854
  // do not repeat elements that are already defined in base structure in the xml ouput!
838
855
  const fields = structureDefinition.fields || [];
@@ -927,9 +944,7 @@ function dumpUADataType(xw: XmlWriter, node: UADataType) {
927
944
  xw.writeAttribute("IsAbstract", node.isAbstract ? "true" : "false");
928
945
  }
929
946
 
930
- _dumpDisplayName(xw, node);
931
-
932
- _dumpReferences(xw, node);
947
+ dumpCommonElements(xw, node);
933
948
 
934
949
  _dumpUADataTypeDefinition(xw, node);
935
950
 
@@ -1015,11 +1030,11 @@ function dumpUAVariableType(xw: XmlWriter, node: UAVariableType) {
1015
1030
  // throw new Error(" cannot find datatype " + node.dataType);
1016
1031
  console.log(
1017
1032
  " cannot find datatype " +
1018
- node.dataType +
1019
- " for node " +
1020
- node.browseName.toString() +
1021
- " id =" +
1022
- node.nodeId.toString()
1033
+ node.dataType +
1034
+ " for node " +
1035
+ node.browseName.toString() +
1036
+ " id =" +
1037
+ node.nodeId.toString()
1023
1038
  );
1024
1039
  } else {
1025
1040
  const dataTypeName = b(xw, resolveDataTypeName(addressSpace, dataTypeNode.nodeId));
@@ -1088,11 +1103,18 @@ function dumpAggregates(xw: XmlWriter, node: BaseNode) {
1088
1103
  // Xx xw.writeComment("Aggregates {{ ");
1089
1104
  const aggregates = node
1090
1105
  .getAggregates()
1091
- .sort((x: BaseNode, y: BaseNode) => (x.browseName.name!.toString() > y.browseName.name!.toString() ? 1 : -1));
1106
+ .sort(sortByBrowseName);
1107
+ // const aggregates = node.findReferencesExAsObject("Aggregates", BrowseDirection.Forward);
1108
+
1092
1109
  for (const aggregate of aggregates.sort(sortByNodeId)) {
1093
1110
  // do not export node that do not belong to our namespace
1094
1111
  if (node.nodeId.namespace !== aggregate.nodeId.namespace) {
1095
- return;
1112
+ continue;
1113
+ }
1114
+ // even further! we should export here the children
1115
+ // that have been added later by a namespace with higher index
1116
+ if (_hasHigherPriorityThan(xw, aggregate.nodeId.namespace, node.nodeId.namespace)) {
1117
+ continue;
1096
1118
  }
1097
1119
  if (!xw.visitedNode[_hash(aggregate)]) {
1098
1120
  (<BaseNodeImpl>aggregate).dumpXML(xw);
@@ -1219,18 +1241,15 @@ function writeAliases(xw: XmlWriter, aliases: Record<string, NodeIdString>) {
1219
1241
  }
1220
1242
  xw.endElement();
1221
1243
  }
1222
- interface ITranslationTable {
1223
- [key: number]: number;
1224
- }
1225
1244
 
1226
- function constructNamespaceTranslationTable(dependency: INamespace[]): ITranslationTable {
1245
+
1246
+ export function constructNamespaceTranslationTable(dependency: INamespace[]): ITranslationTable {
1227
1247
  const translationTable: ITranslationTable = {};
1228
1248
  for (let i = 0; i < dependency.length; i++) {
1229
1249
  translationTable[dependency[i].index] = i;
1230
1250
  }
1231
1251
  return translationTable;
1232
1252
  }
1233
-
1234
1253
  function dumpReferenceType(xw: XmlWriter, referenceType: UAReferenceType) {
1235
1254
  _markAsVisited(xw, referenceType);
1236
1255
 
@@ -1249,7 +1268,7 @@ function dumpReferenceType(xw: XmlWriter, referenceType: UAReferenceType) {
1249
1268
  xw.endElement();
1250
1269
  }
1251
1270
 
1252
- function sortByBrowseName(x: BaseNode, y: BaseNode): number {
1271
+ export function sortByBrowseName(x: BaseNode, y: BaseNode): number {
1253
1272
  const x_str = x.browseName.toString();
1254
1273
  const y_str = y.browseName.toString();
1255
1274
  if (x_str > y_str) {
@@ -1281,56 +1300,6 @@ export interface DumpXMLOptions {
1281
1300
  /** */
1282
1301
  }
1283
1302
 
1284
- export function dumpXml(node: BaseNode, options: DumpXMLOptions): void {
1285
- const namespace = node.namespace as NamespacePrivate;
1286
-
1287
- // make a first visit so that we determine which node to output and in which order
1288
- const data: DumpData = { aliases: {}, aliases_visited: {}, index_el: {}, elements: [] };
1289
-
1290
- const dependency = constructNamespaceDependency(namespace);
1291
- const translationTable = constructNamespaceTranslationTable(dependency);
1292
-
1293
- const xw = new XMLWriter(true);
1294
- xw.translationTable = translationTable;
1295
-
1296
- visitUANode(node, data, false);
1297
-
1298
- xw.startDocument({ encoding: "utf-8" });
1299
- xw.startElement("UANodeSet");
1300
- xw.writeAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance");
1301
- xw.writeAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
1302
- xw.writeAttribute("Version", "1.02");
1303
- xw.writeAttribute("LastModified", new Date().toISOString());
1304
- xw.writeAttribute("xmlns", "http://opcfoundation.org/UA/2011/03/UANodeSet.xsd");
1305
-
1306
- buildUpAliases(node, xw, data);
1307
- writeAliases(xw, data.aliases);
1308
-
1309
- const xmlNamspace = {
1310
- default: { namespace: "http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" }
1311
- };
1312
-
1313
- xw.startElement("Extensions");
1314
- {
1315
- xw.startElement("Extension");
1316
- {
1317
- xw.startElement("ModelInfo");
1318
- xw.writeAttribute("NodeOPCUA");
1319
- xw.endElement();
1320
- }
1321
- xw.endElement();
1322
- }
1323
- xw.endElement();
1324
-
1325
- for (const el of data.elements!) {
1326
- el.dumpXML(xw);
1327
- }
1328
-
1329
- xw.endElement();
1330
- xw.endDocument();
1331
- return xw.toString();
1332
- }
1333
-
1334
1303
  UAMethodImpl.prototype.dumpXML = function (xw) {
1335
1304
  dumpUAMethod(xw, this);
1336
1305
  };
@@ -1360,17 +1329,21 @@ function makeTypeXsd(namespaceUri: string): string {
1360
1329
 
1361
1330
  // eslint-disable-next-line max-statements
1362
1331
  NamespaceImpl.prototype.toNodeset2XML = function (this: NamespaceImpl) {
1363
- const dependency = constructNamespaceDependency(this);
1364
- const translationTable = constructNamespaceTranslationTable(dependency);
1332
+
1365
1333
 
1366
1334
  const namespaceArrayNode = this.addressSpace.findNode(VariableIds.Server_NamespaceArray);
1367
1335
  const namespaceArray: string[] = namespaceArrayNode
1368
1336
  ? namespaceArrayNode.readAttribute(null, AttributeIds.Value).value.value
1369
1337
  : [];
1370
1338
 
1371
- const xw = new XMLWriter(true);
1339
+ const xw: XmlWriter = new XMLWriter(true);
1340
+
1341
+ xw.priorityTable = constructNamespacePriorityTable(this);
1342
+ const dependency = constructNamespaceDependency(this, xw.priorityTable);
1343
+ const translationTable = constructNamespaceTranslationTable(dependency);
1372
1344
  xw.translationTable = translationTable;
1373
1345
 
1346
+
1374
1347
  xw.startDocument({ encoding: "utf-8", version: "1.0" });
1375
1348
  xw.startElement("UANodeSet");
1376
1349
 
@@ -46,6 +46,7 @@ export interface EnumerationInfo {
46
46
  }
47
47
  export interface UADataTypeOptions extends InternalBaseNodeOptions {
48
48
  partialDefinition: StructureFieldOptions[] | EnumFieldOptions[];
49
+ isUnion?: boolean;
49
50
  isAbstract?: boolean;
50
51
  symbolicName?: string;
51
52
  }
@@ -78,6 +79,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
78
79
 
79
80
  public readonly isAbstract: boolean;
80
81
 
82
+ private $isUnion?: boolean;
81
83
  private enumStrings?: any;
82
84
  private enumValues?: any;
83
85
  private $partialDefinition?: StructureFieldOptions[] | EnumFieldOptions[];
@@ -87,6 +89,7 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
87
89
  super(options);
88
90
  if (options.partialDefinition) {
89
91
  this.$partialDefinition = options.partialDefinition;
92
+ this.$isUnion = options.isUnion;
90
93
  }
91
94
  this.isAbstract = options.isAbstract === undefined || options.isAbstract === null ? false : options.isAbstract;
92
95
  this.symbolicName = options.symbolicName || this.browseName.name!;
@@ -348,23 +348,39 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
348
348
 
349
349
  this.semantic_version = 0;
350
350
  }
351
-
352
- private checkPermissionAndAccessLevelPrivate(
351
+ private checkAccessLevelPrivate(
352
+ _context: ISessionContext,
353
+ accessLevel: AccessLevelFlag): boolean {
354
+ if (this.userAccessLevel === undefined) {
355
+ return true;
356
+ }
357
+ return (this.userAccessLevel & accessLevel) === accessLevel;
358
+ }
359
+ private checkPermissionPrivate(
353
360
  context: ISessionContext,
354
361
  permission: PermissionType,
355
- accessLevel: AccessLevelFlag
356
- ) {
362
+ ): boolean {
363
+ if (!context) return true;
357
364
  assert(context instanceof SessionContext);
358
365
  if (context.checkPermission) {
359
- assert(context.checkPermission instanceof Function);
366
+ if (!(context.checkPermission instanceof Function)) {
367
+ errorLog("context checkPermission is not a function");
368
+ return false;
369
+ }
360
370
  if (!context.checkPermission(this, permission)) {
361
371
  return false;
362
372
  }
363
373
  }
364
- if (this.userAccessLevel === undefined) {
365
- return true;
374
+ return true;
375
+ }
376
+ private checkPermissionAndAccessLevelPrivate(
377
+ context: ISessionContext,
378
+ permission: PermissionType,
379
+ accessLevel: AccessLevelFlag): boolean {
380
+ if (!this.checkPermissionPrivate(context, permission)) {
381
+ return false;
366
382
  }
367
- return (this.userAccessLevel & accessLevel) === accessLevel;
383
+ return this.checkAccessLevelPrivate(context, accessLevel);
368
384
  }
369
385
 
370
386
  public isReadable(context: ISessionContext): boolean {
@@ -375,7 +391,10 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
375
391
  if (!this.isReadable(context)) {
376
392
  return false;
377
393
  }
378
- return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.Read, AccessLevelFlag.CurrentRead);
394
+ if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
395
+ return false;
396
+ }
397
+ return this.checkAccessLevelPrivate(context, AccessLevelFlag.CurrentRead);
379
398
  }
380
399
 
381
400
  public isWritable(context: ISessionContext): boolean {
@@ -451,9 +470,12 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
451
470
  if (!this.isReadable(context)) {
452
471
  return new DataValue({ statusCode: StatusCodes.BadNotReadable });
453
472
  }
454
- if (!this.isUserReadable(context)) {
473
+ if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
455
474
  return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
456
475
  }
476
+ if (!this.isUserReadable(context)) {
477
+ return new DataValue({ statusCode: StatusCodes.BadNotReadable });
478
+ }
457
479
  if (!isValidDataEncoding(dataEncoding)) {
458
480
  return new DataValue({ statusCode: StatusCodes.BadDataEncodingInvalid });
459
481
  }
@@ -484,10 +506,10 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
484
506
  ) {
485
507
  debugLog(
486
508
  chalk.red(" Warning: UAVariable#readValue ") +
487
- chalk.cyan(this.browseName.toString()) +
488
- " (" +
489
- chalk.yellow(this.nodeId.toString()) +
490
- ") exists but dataValue has not been defined"
509
+ chalk.cyan(this.browseName.toString()) +
510
+ " (" +
511
+ chalk.yellow(this.nodeId.toString()) +
512
+ ") exists but dataValue has not been defined"
491
513
  );
492
514
  }
493
515
  return dataValue;
@@ -689,9 +711,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
689
711
  if (variant.dataType === null || variant.dataType === undefined) {
690
712
  throw new Error(
691
713
  "Variant must provide a valid dataType : variant = " +
692
- variant.toString() +
693
- " this.dataType= " +
694
- this.dataType.toString()
714
+ variant.toString() +
715
+ " this.dataType= " +
716
+ this.dataType.toString()
695
717
  );
696
718
  }
697
719
  if (
@@ -700,9 +722,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
700
722
  ) {
701
723
  throw new Error(
702
724
  "Variant must provide a valid Boolean : variant = " +
703
- variant.toString() +
704
- " this.dataType= " +
705
- this.dataType.toString()
725
+ variant.toString() +
726
+ " this.dataType= " +
727
+ this.dataType.toString()
706
728
  );
707
729
  }
708
730
  if (
@@ -713,9 +735,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
713
735
  ) {
714
736
  throw new Error(
715
737
  "Variant must provide a valid LocalizedText : variant = " +
716
- variant.toString() +
717
- " this.dataType= " +
718
- this.dataType.toString()
738
+ variant.toString() +
739
+ " this.dataType= " +
740
+ this.dataType.toString()
719
741
  );
720
742
  }
721
743
  }
@@ -870,8 +892,13 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
870
892
  if (!this.isWritable(context)) {
871
893
  return callback!(null, StatusCodes.BadNotWritable);
872
894
  }
895
+ if (!this.checkPermissionPrivate(context, PermissionType.Write)) {
896
+ return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
897
+ }
898
+
899
+
873
900
  if (!this.isUserWritable(context)) {
874
- return callback!(null, StatusCodes.BadUserAccessDenied);
901
+ return callback!(null, StatusCodes.BadWriteNotSupported);
875
902
  }
876
903
 
877
904
  // adjust special case
@@ -997,9 +1024,14 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
997
1024
  if (writeValue.value!.value.dataType !== DataType.Boolean) {
998
1025
  return callback(null, StatusCodes.BadTypeMismatch);
999
1026
  }
1000
- if (!this.canUserWriteHistorizingAttribute(context)) {
1027
+ if (!this.checkPermissionPrivate(context, PermissionType.WriteHistorizing)) {
1001
1028
  return callback(null, StatusCodes.BadUserAccessDenied);
1002
1029
  }
1030
+
1031
+ if (!this.canUserWriteHistorizingAttribute(context)) {
1032
+ return callback(null, StatusCodes.BadHistoryOperationUnsupported);
1033
+ }
1034
+
1003
1035
  // if the variable has no historizing in place reject
1004
1036
  if (!this.getChildByName("HA Configuration")) {
1005
1037
  return callback(null, StatusCodes.BadNotSupported);
@@ -1227,11 +1259,16 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1227
1259
  const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
1228
1260
  innerCallback(null, dataValue);
1229
1261
  };
1230
- } else if (!this.isUserReadable(context)) {
1262
+ } else if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
1231
1263
  func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1232
1264
  const dataValue = new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
1233
1265
  innerCallback(null, dataValue);
1234
1266
  };
1267
+ } else if (!this.isUserReadable(context)) {
1268
+ func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
1269
+ const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
1270
+ innerCallback(null, dataValue);
1271
+ };
1235
1272
  } else {
1236
1273
  func = typeof this.refreshFunc === "function" ? this.asyncRefresh.bind(this, new Date()) : readImmediate;
1237
1274
  }
@@ -1560,12 +1597,19 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1560
1597
  continuationData: ContinuationData,
1561
1598
  callback: CallbackT<HistoryReadResult>
1562
1599
  ): void {
1563
- if (!this.canUserReadHistory(context)) {
1600
+
1601
+ if (!this.checkPermissionPrivate(context, PermissionType.ReadHistory)) {
1564
1602
  const result = new HistoryReadResult({
1565
1603
  statusCode: StatusCodes.BadUserAccessDenied
1566
1604
  });
1567
1605
  callback(null, result);
1568
1606
  }
1607
+ if (!this.canUserReadHistory(context)) {
1608
+ const result = new HistoryReadResult({
1609
+ statusCode: StatusCodes.BadHistoryOperationUnsupported
1610
+ });
1611
+ callback(null, result);
1612
+ }
1569
1613
  const result = new HistoryReadResult({
1570
1614
  statusCode: StatusCodes.BadHistoryOperationUnsupported
1571
1615
  });
@@ -1634,8 +1678,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
1634
1678
  const nbElements = dataValue.value.dimensions.reduce((acc, x) => acc * x, 1);
1635
1679
  if (dataValue.value.value.length !== 0 && dataValue.value.value.length !== nbElements) {
1636
1680
  throw new Error(
1637
- `Internal Error: matrix dimension doesn't match the number of element in the array : ${dataValue.toString()} "\n expecting ${nbElements} elements but got ${
1638
- dataValue.value.value.length
1681
+ `Internal Error: matrix dimension doesn't match the number of element in the array : ${dataValue.toString()} "\n expecting ${nbElements} elements but got ${dataValue.value.value.length
1639
1682
  }`
1640
1683
  );
1641
1684
  }
@@ -1984,7 +2027,7 @@ function _Variable_bind_with_timestamped_get(
1984
2027
  errorLog(
1985
2028
  chalk.red(" Bind variable error: "),
1986
2029
  " the timestamped_get function must return a DataValue or a Promise<DataValue>" +
1987
- "\n value_check.constructor.name ",
2030
+ "\n value_check.constructor.name ",
1988
2031
  dataValue_verify ? dataValue_verify.constructor.name : "null"
1989
2032
  );
1990
2033
 
@@ -2179,7 +2222,7 @@ export interface UAVariableImplT<T, DT extends DataType> extends UAVariableImpl,
2179
2222
  writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, callback: StatusCodeCallback): void;
2180
2223
  writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, indexRange?: NumericRange | null): Promise<StatusCode>;
2181
2224
  }
2182
- export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl {}
2225
+ export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl { }
2183
2226
  // x TO DO
2184
2227
  // require("./data_access/ua_variable_data_access");
2185
2228
  // require("./historical_access/ua_variable_history");