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.
- package/dist/source/loader/ensure_datatype_extracted.js +1 -1
- package/dist/source/loader/ensure_datatype_extracted.js.map +1 -1
- package/dist/source/loader/load_nodeset2.js +9 -22
- package/dist/source/loader/load_nodeset2.js.map +1 -1
- package/dist/source/xml_writer.d.ts +5 -1
- package/dist/src/address_space.js +1 -1
- package/dist/src/address_space.js.map +1 -1
- package/dist/src/index_current.d.ts +1 -1
- package/dist/src/index_current.js +2 -3
- package/dist/src/index_current.js.map +1 -1
- package/dist/src/namespace_impl.d.ts +4 -1
- package/dist/src/namespace_impl.js +10 -2
- package/dist/src/namespace_impl.js.map +1 -1
- package/dist/src/namespace_private.d.ts +2 -1
- package/dist/src/namespace_private.js.map +1 -1
- package/dist/src/nodeset_tools/construct_namespace_dependency.d.ts +3 -1
- package/dist/src/nodeset_tools/construct_namespace_dependency.js +35 -5
- package/dist/src/nodeset_tools/construct_namespace_dependency.js.map +1 -1
- package/dist/src/nodeset_tools/nodeset_to_xml.d.ts +4 -2
- package/dist/src/nodeset_tools/nodeset_to_xml.js +44 -59
- package/dist/src/nodeset_tools/nodeset_to_xml.js.map +1 -1
- package/dist/src/ua_data_type_impl.d.ts +2 -0
- package/dist/src/ua_data_type_impl.js +1 -0
- package/dist/src/ua_data_type_impl.js.map +1 -1
- package/dist/src/ua_variable_impl.d.ts +2 -0
- package/dist/src/ua_variable_impl.js +49 -11
- package/dist/src/ua_variable_impl.js.map +1 -1
- package/package.json +39 -39
- package/source/loader/ensure_datatype_extracted.ts +1 -1
- package/source/loader/load_nodeset2.ts +22 -36
- package/source/xml_writer.ts +5 -1
- package/src/address_space.ts +1 -2
- package/src/index_current.ts +1 -1
- package/src/namespace_impl.ts +13 -3
- package/src/namespace_private.ts +7 -1
- package/src/nodeset_tools/construct_namespace_dependency.ts +42 -5
- package/src/nodeset_tools/nodeset_to_xml.ts +57 -84
- package/src/ua_data_type_impl.ts +3 -0
- package/src/ua_variable_impl.ts +74 -31
- package/test_helpers/test_fixtures/dataType_with_union.xml +90 -1
- package/test_helpers/test_fixtures/datatype_as_per_1.04.xml +38 -10
package/src/namespace_private.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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] ===
|
|
820
|
+
if (int64[0] === 0xffffffff && int64[1] === 0xffffffff) {
|
|
804
821
|
return 0xffffffff;
|
|
805
822
|
}
|
|
806
823
|
if (int64[0] !== 0) {
|
|
807
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
package/src/ua_data_type_impl.ts
CHANGED
|
@@ -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!;
|
package/src/ua_variable_impl.ts
CHANGED
|
@@ -348,23 +348,39 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
|
|
|
348
348
|
|
|
349
349
|
this.semantic_version = 0;
|
|
350
350
|
}
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
356
|
-
|
|
362
|
+
): boolean {
|
|
363
|
+
if (!context) return true;
|
|
357
364
|
assert(context instanceof SessionContext);
|
|
358
365
|
if (context.checkPermission) {
|
|
359
|
-
|
|
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
|
-
|
|
365
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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");
|